uricontrol-js

uricontrol-js is a small URI-native control core for browsers and Node.js. It is not an agent framework. It is a deterministic layer:

URI -> manifest -> policy -> handler -> event -> result/projection

The same envelope can be used in a browser page, in a Node.js service, and across backends implemented in Python, Go, PHP, TypeScript, etc.

Why

Traditional APIs often spread one operation across several representations:

uricontrol makes the URI the shared semantic address:

page://state/counter/command/increment
node://counter/command/increment
py://math/add
printer://epson/command/nozzle-check
systemd://unit/nginx.service/command/restart

Install / test

cd uricontrol-js
npm test

No build step is required. Browser examples import the source through native ES modules:

<script type="module" src="./app.js"></script>

Core API

import {
  CapabilityRegistry,
  MemoryEventStore,
  PolicyGate,
  UriControlRuntime,
} from '@uricontrol/js';

const manifest = {
  id: 'counter-pack',
  version: 1,
  scheme: 'counter',
  uri_patterns: [
    {
      pattern: 'counter://main/command/increment',
      kind: 'command',
      operation: 'increment',
      handler: 'increment',
      side_effects: true,
      approval: 'required',
    },
  ],
};

let value = 0;
const registry = new CapabilityRegistry().loadManifest(manifest, {
  increment(payload) {
    value += Number(payload.step ?? 1);
    return { value };
  },
});

const runtime = new UriControlRuntime({
  registry,
  eventStore: new MemoryEventStore(),
  policy: new PolicyGate(),
});

const result = await runtime.call(
  'counter://main/command/increment',
  { step: 2 },
  { approved: true },
);

Browser page demo

Run a Node server that serves the browser demo and exposes /uri/call:

node examples/node-server/server.mjs --static examples/browser-page --port 8080

Open:

http://127.0.0.1:8080/

The page has its own uricontrol-js runtime and can call:

page://state/counter/command/increment
page://dom/title/command/set

It can also call the Node service:

node://counter/command/increment

And it can call another browser tab/page through BroadcastChannel.

Fullstack demo: browser + Node + Python

Terminal 1:

python examples/python-backend/server.py

Terminal 2:

node examples/node-server/server.mjs --static examples/browser-page --port 8080

Open:

http://127.0.0.1:8080/

Flow:

browser page runtime
  page://state/counter/command/increment

browser -> Node HTTP
  node://counter/command/increment

browser -> Node HTTP -> Python HTTP
  node://python/math/add -> py://math/add

Protocol adapters

The runtime has one internal contract:

runtime.call(uri, payload, context)

Adapters expose that same contract through protocols:

Protocol Role Files
HTTP request/response calls src/server/http-server.js, src/transports/http-client.js
SSE server -> browser event stream src/server/http-server.js, src/transports/sse-client.js
WebSocket bidirectional calls and events src/server/ws-server.js, src/transports/ws-client.js
BroadcastChannel same-origin page/tab bridge src/transports/broadcast-channel.js
postMessage iframe/window bridge src/transports/post-message.js
gRPC service-to-service binary RPC skeleton src/server/grpc-adapter.js, contracts/proto/uricontrol/v1/envelope.proto

Suggested split with Python uricontrol

uricontrol/       # Python core
uricontrol-js/    # browser + Node core
urisys/           # real system using both

urisys can import both runtimes and use the same envelope:

{
  "uri": "py://math/add",
  "payload": { "a": 40, "b": 2 },
  "context": { "approved": true, "caller": "node-proxy" }
}

Response:

{
  "ok": true,
  "uri": "py://math/add",
  "result": { "sum": 42, "backend": "python" },
  "event": { "event_type": "py.math.add.completed" }
}

Framework integration examples

Design rule

Pack knows technology.
Core knows URI, manifest, policy, dispatch and events.
Transport only carries envelopes.

License

Licensed under Apache-2.0.