Sitelet https://github.com/Accenture/mercury-nodejs
Skip to content

Latest commit

 

History

578 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mercury Composable — polyglot functions for Node.js

Write decoupled functions in JavaScript/TypeScript and let Mercury Composable engines (Java, and the official Rust port) orchestrate them from Event Script flows and MiniGraph knowledge graphs — with no orchestration code in Node.js at all.

This package is a deliberately lightweight wrapper of the Event-over-HTTP protocol:

  • an Event API host (POST /api/event) that dispatches incoming event envelopes to your registered functions,
  • a thin client (PostOffice) to call functions on peer applications the same way,
  • the standard event envelope wire format codec (language-neutral MsgPack), and
  • a primitive in-process event bus — the single dispatch pipeline: one FIFO mailbox per route consumed by instances worker loops, and
  • the minimalist utilities shared with the engines for consistency: configuration management, logging in the engines' presentation format, and distributed-trace context, and
  • the OpenTelemetry forwarder (opt-in, otel.forwarding=true): the host's trace spans exported over OTLP/HTTP to Dynatrace, Splunk or a collector — no SDK, no new dependency, and
  • the LLM helper app (examples/llm-helper): llm.chat, llm.stream and llm.health on the official Anthropic SDK — the graph decides, the model advises; llm.stream relays the model's token batches as they are produced.

Orchestration deliberately stays in the engines. Functions written here are addressed by route name through the engines' declarative yaml.event.over.http map, so a flow or a graph task calls a Node.js function exactly as if it were local.

Documentation: https://accenture.github.io/mercury-nodejs/ — including the AI Agent Guide for deterministic function generation.

Published on npm. mercury-composable v4.12.15 is live — npm install mercury-composable. This repository was repurposed in August 2026 for the polyglot initiative: instead of re-porting the full composable foundation to Node.js, the fresh start rides the engines' Event-over-HTTP protocol — light by design. The previous Node.js port (up to v4.3.28) remains available in the git history only — it never occupied this name on the public registry.

Quick start

// app.mjs
import { AppException, platform, preload } from 'mercury-composable';

preload('hello.node', { instances: 10 }, async (headers, body) => {
  if (typeof body !== 'object' || body === null || !('text' in body)) {
    throw new AppException(400, "missing 'text'");
  }
  return { text: String(body.text).toUpperCase(), language: 'node.js' };
});

await platform.run(); // port from rest.server.port (default 8085)

Run it:

npm install mercury-composable
mercury-serve app.mjs -Drest.server.port=8087

Call it from a Mercury engine application with two configuration entries and no code — application.properties:

yaml.event.over.http=classpath:/event-over-http.yaml

event-over-http.yaml:

event.http:
  - route: 'hello.node'
    target: 'http://127.0.0.1:8087/api/event'

Any Event Script task or MiniGraph graph.task node that names the route hello.node now executes the Node.js function, with trace context carried end to end.

The function contract

A handler receives the same two-part input as an engine TypedLambdaFunction — (headers, body) — and returns the reply body (or an EventEnvelope for full control of status and reply headers). Node.js is non-blocking by nature; handlers may be async.

  • Throw AppException(status, message) for intentional errors — it becomes the portable error contract on the wire (envelope status + message), handled by the calling flow's exception handler or the graph's error.* contract.
  • getTrace() exposes traceId / tracePath / cid; annotateTrace(k, v) sends an annotation back on the reply envelope.
  • Functions must be stateless; anything you must keep belongs to the caller's flow model or state machine.

Local function calls

PostOffice without an endpoint delivers through this application's own event bus — the engines' semantics for an in-app po call:

  • isPrivate: true means exactly what it means in the engines: callable in-app only. Local calls reach private and public routes alike; the HTTP host keeps answering 403 for private targets from the wire.
  • instances is faithful: each route has one FIFO mailbox consumed by that many worker loops. RPC waits are bounded by timeoutMs (the standard 408 envelope on breach), and a queued call whose caller already timed out is skipped, never wastefully executed.
  • There is no spill tier and no queue cap by design: back-pressure belongs to the tier that owns recovery — the engines' flows and graphs. A leaf host fails fast by deadline instead of hoarding work.

Local eventing is for simple leaf-side composition. Workflow processing belongs in Event Script and Knowledge Graph on the engines — that boundary is the architecture.

Configuration, logging, telemetry

The same conventions as the engines, so a polyglot installation stays uniform:

Key Meaning Default
application.name application identity in logs application
rest.server.port Event API port 8085
log.format text, json (pretty-printed) or compact (single-line JSONL) text
log.level log level (LOG_LEVEL env var wins) INFO

Configuration lives in the resources folder, mirroring the engines: resources/application.yml (or .yaml / .properties) in the working directory or next to the application file, or an explicit --config path — see examples/demo-app/resources/application.yml for a worked sample. Values support ${ENV_VAR:default} substitution. Runtime parameter overrides use the same -D syntax as the Java engine and the Rust port — checked first on every read (appConfig().set(key, value) does the same programmatically, the f:setConfig analog):

mercury-serve app.mjs -Drest.server.port=8087 -Dlog.format=json

Log lines follow the Java reference engine's pattern for one-aggregation consistency:

2026-08-22 10:15:30.123 INFO  app:12 - Loaded PUBLIC hello.node, instances=10

Actuator endpoints

The host serves the engines' operational endpoints on the same port as /api/event, so Kubernetes probes and dashboards treat a Node.js app exactly like a Java or Rust engine app:

Endpoint Purpose
GET / minimal index page linking the endpoints below
GET /info app identity, runtime, origin id, start time, uptime
GET /info/routes registered routes split by visibility, with instance counts
GET /env selected environment variables and configuration parameters
GET /health dependency health checks — UP (HTTP 200) or DOWN (HTTP 400)
GET /livenessprobe OK while the last health outcome was good, else HTTP 400

Configuration keys carry the engines' names: info.app.version, info.app.description, show.env.variables and show.application.properties (opt-in lists — secrets are never dumped wholesale), and mandatory.health.dependencies / optional.health.dependencies (routes of health check functions; optional ones never change the overall status). A health check function is a normal registered function — usually private — speaking the engines' interface contract, called through the event bus:

preload('demo.health', { isPrivate: true }, async (headers, _body) => {
  if (headers.type === 'info') {
    return { service: 'demo.service', href: 'http://127.0.0.1' };
  }
  return 'demo.service is running fine';   // a non-200 reply marks it down
});

JSON responses are pretty-printed — the engines' default-serializer presentation — and unknown paths answer the engines' error shape ({"status": 404, "message": "Resource not found", "type": "error"}).

Kubernetes wiring: point livenessProbe at /livenessprobe and readinessProbe at /health.

Wire compatibility

The codec implements the Event Envelope Wire Format (standard format) and is verified against the golden conformance vectors shared by the Java and Rust engines (test/vectors/vectors.json). The classic compact format is detected and rejected with a teaching error — engines default to the standard format for Event over HTTP.

Serialization notes: 64-bit integers beyond Number.MAX_SAFE_INTEGER decode as BigInt (exact), smaller ones as number; timestamps travel as ISO-8601 UTC strings with millisecond precision; binary payloads are Uint8Array.

Scope

This package intentionally contains no orchestration: no flows, no graphs, no persistence, no pub/sub broadcast — those live in the engines. What it does carry is deliberately minimal: functions, a primitive in-process event bus (route mailboxes + workers, RPC and drop-n-forget — nothing more), and the minimalist foundation utilities, keeping Node.js fast to prototype with while the composable core guarantees the architecture.

License

Apache 2.0 — see LICENSE.

About

Node language pack for Intent-Driven Development, Composability and Active Knowledge Graph

Topics

Resources

Code of conduct

Contributing

Stars

7 stars

Watchers

7 watching

Forks

Releases

Packages

Used by

Contributors

Languages