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.
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
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>
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 },
);
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.
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
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 |
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" }
}
HttpUriClient in a service/store/composable.runtime.call().POST /uri/call to runtime.call().uricontrol runtime behind the same endpoint shape.runtime.call() in a provider/service and expose HTTP/WS gateways.page://... for UI runtime and native://... for desktop/backend runtime.Pack knows technology.
Core knows URI, manifest, policy, dispatch and events.
Transport only carries envelopes.
Licensed under Apache-2.0.