@hediet/linkrpc 0.0.3-next.20260925.3 → 0.0.3-next.20260926.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/chunks/{hub.interfaces-CXYE_h39.js → hub.interfaces-I-GtB1Ah.js} +2 -2
- package/dist/chunks/{hub.interfaces-CXYE_h39.js.map → hub.interfaces-I-GtB1Ah.js.map} +1 -1
- package/dist/chunks/{hubAccess-BHfWpT7f.d.ts → hubAccess-Ci7MB_My.d.ts} +2 -2
- package/dist/chunks/{hubAccess-BHfWpT7f.d.ts.map → hubAccess-Ci7MB_My.d.ts.map} +1 -1
- package/dist/chunks/{hubFacade-CxRmoKcR.js → hubFacade-ClN2ausU.js} +2 -2
- package/dist/chunks/{hubFacade-CxRmoKcR.js.map → hubFacade-ClN2ausU.js.map} +1 -1
- package/dist/chunks/{hubFacade-CmacIwKr.d.ts → hubFacade-xR-h9dSH.d.ts} +2 -2
- package/dist/chunks/{hubFacade-CmacIwKr.d.ts.map → hubFacade-xR-h9dSH.d.ts.map} +1 -1
- package/dist/chunks/{inspectionHost-pSFBRJNJ.js → inspectionHost-BA6ZmC_P.js} +37 -5
- package/dist/chunks/inspectionHost-BA6ZmC_P.js.map +1 -0
- package/dist/chunks/{linkRpcConnection-CzF2DvFS.d.ts → linkRpcConnection-BnMwoBYd.d.ts} +5 -2
- package/dist/chunks/linkRpcConnection-BnMwoBYd.d.ts.map +1 -0
- package/dist/chunks/{src-mMza2QeQ.js → src-D2FV27f4.js} +8 -21
- package/dist/chunks/src-D2FV27f4.js.map +1 -0
- package/dist/chunks/webSocketTransport-DoGQGWWz.js +225 -0
- package/dist/chunks/webSocketTransport-DoGQGWWz.js.map +1 -0
- package/dist/chunks/webSocketTransport-DyCG1UQU.d.ts +85 -0
- package/dist/chunks/webSocketTransport-DyCG1UQU.d.ts.map +1 -0
- package/dist/hub/client/index.d.ts +3 -3
- package/dist/hub/client/index.js +3 -3
- package/dist/hub/common/index.d.ts +2 -2
- package/dist/hub/common/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/inspection/index.d.ts +1 -1
- package/dist/inspection/index.js +1 -1
- package/dist/node.d.ts +7 -109
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +10 -188
- package/dist/node.js.map +1 -1
- package/dist/web.d.ts +3 -2
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +4 -3
- package/dist/web.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunks/inspectionHost-pSFBRJNJ.js.map +0 -1
- package/dist/chunks/linkRpcConnection-CzF2DvFS.d.ts.map +0 -1
- package/dist/chunks/src-mMza2QeQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,6 +7,43 @@ message on the wire is a valid JSON-RPC message, and linkrpc adds just enough on
|
|
|
7
7
|
strongly-typed services share one connection — an addressing grammar, built-in reflection,
|
|
8
8
|
content-addressed interface identity, and optional layers for signing and capabilities.
|
|
9
9
|
|
|
10
|
+
## Direct browser hub connections
|
|
11
|
+
|
|
12
|
+
The `@hediet/linkrpc/web` entry exports the native `WebSocketTransport`,
|
|
13
|
+
`openWebSocket`, and `connectWebSocketTransport` helpers without Node imports.
|
|
14
|
+
The last helper completes the same `runInitializeHandshake` used by Node
|
|
15
|
+
transports before exposing the connection:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {
|
|
19
|
+
connectWebSocketTransport, JsonRpcChannel, SigningSender, LinkRpcConnection,
|
|
20
|
+
} from '@hediet/linkrpc/web';
|
|
21
|
+
|
|
22
|
+
const abort = new AbortController();
|
|
23
|
+
const transport = await connectWebSocketTransport(endpoint, {
|
|
24
|
+
token: userSuppliedToken, // in-band initialization, not a URL parameter
|
|
25
|
+
signal: abort.signal,
|
|
26
|
+
timeoutMs: 10_000,
|
|
27
|
+
onClose: () => { /* clear connection-dependent state */ },
|
|
28
|
+
});
|
|
29
|
+
const connection = new LinkRpcConnection(SigningSender.wrapChannel(
|
|
30
|
+
JsonRpcChannel.create(transport), { principal },
|
|
31
|
+
));
|
|
32
|
+
// On disposal, close RPC state and the socket.
|
|
33
|
+
connection.close();
|
|
34
|
+
abort.abort();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`principal` is a caller-owned `Principal`; restore a browser identity with
|
|
38
|
+
`KeypairSigningIdentity.fromJson` or generate one with `generateNew`, and pair
|
|
39
|
+
it with a `CapBag`. Storage and explicit permission consent remain the app's
|
|
40
|
+
responsibility. A connection token does not authorize a signing identity.
|
|
41
|
+
These helpers never negotiate additional permissions or persist credentials.
|
|
42
|
+
The browser must trust the endpoint's TLS certificate, and its Origin must be
|
|
43
|
+
accepted by the server. `openWebSocket` accepts only standard browser
|
|
44
|
+
subprotocols, cancellation, and timeout options; Node-only upgrade headers
|
|
45
|
+
remain available through `@hediet/linkrpc/node`.
|
|
46
|
+
|
|
10
47
|
## Interface templates
|
|
11
48
|
|
|
12
49
|
This bounded v1 adds TypeScript authoring groups over ordinary concrete RPC methods.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { mt as requestType, z as defineInterface } from "./inspectionHost-
|
|
5
|
+
import { mt as requestType, z as defineInterface } from "./inspectionHost-BA6ZmC_P.js";
|
|
6
6
|
import { array, boolean, discriminatedUnion, enum as enum$1, extend, literal, minLength, number, object, optional, record, string, union, unknown } from "zod/mini";
|
|
7
7
|
//#region src/hub/common/hub.interfaces.ts
|
|
8
8
|
const zMemberPattern = union([object({ exact: string() }), object({ prefix: string() })]);
|
|
@@ -523,4 +523,4 @@ const hubAccessManifestInterface = defineInterface({
|
|
|
523
523
|
//#endregion
|
|
524
524
|
export { hubServiceIdRegistryInterface as i, hubAccessManifestInterface as n, hubGrantedServiceIdInterface as r, hubAccessInterface as t };
|
|
525
525
|
|
|
526
|
-
//# sourceMappingURL=hub.interfaces-
|
|
526
|
+
//# sourceMappingURL=hub.interfaces-I-GtB1Ah.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub.interfaces-CXYE_h39.js","names":["zEnum"],"sources":["../../src/hub/common/hub.interfaces.ts"],"sourcesContent":["import { array, boolean, discriminatedUnion, enum as zEnum, extend, literal, minLength, number, object, optional, record, string, union, unknown } from 'zod/mini';\nimport { defineInterface, requestType, type InterfaceClient } from '../../index';\n\nconst zMemberPattern = union([\n object({ exact: string() }),\n object({ prefix: string() }),\n]);\n\n/**\n * Optional per-permission invocation preview the requester suggests for\n * \"Allow once\" consent UX. When present, the user can bind the resulting\n * capability to this exact (method, params, interfaceHash) tuple.\n *\n * `nonce` and `signedAtMs` are consumer-chosen and identify the exact call\n * attempt the cap will authorise. The host hashes the same canonical\n * bytes the consumer will sign; the gate re-derives the hash from the\n * inbound call and compares. The consumer MUST use the same\n * nonce+signedAtMs when it actually issues the call (`@hediet/linkrpc`'s\n * {@link JsonRpcChannel.setCapProvider} hook does this automatically).\n *\n * Everything in `params` is shown to the user verbatim.\n */\nconst zCallIntent = object({\n /** Fully-qualified method name the requester intends to call. */\n method: string(),\n /** Exact params the requester intends to send. */\n params: optional(unknown()),\n /** Optional schema hash assertion the call carries inside the signed payload. */\n interfaceHash: optional(string()),\n /** base64url-encoded nonce bytes the consumer will sign with. */\n nonce: string(),\n /** Unix milliseconds the consumer will sign with. */\n signedAtMs: number(),\n /** Optional one-line summary the shell displays next to params. */\n summary: optional(string()),\n /** Requester's preferred default action (`once` if omitted). */\n suggestion: optional(zEnum(['once', 'shortLived', 'longLived', 'persistent'])),\n});\n\nconst zParamMatcher = union([\n object({ exact: unknown() }),\n object({ enum: array(unknown()) }),\n object({ prefix: string() }),\n object({ subsetOf: array(string()) }),\n object({ any: literal(true) }),\n]);\n\nconst zCallBindHash = object({\n alg: literal('sha256'),\n payloadHash: string(),\n});\n\n/** Wire shape of `TargetPattern` from `@hediet/linkrpc`. */\nconst zTargetPattern = object({\n serviceId: zMemberPattern,\n interfaceId: zMemberPattern,\n interfaceHash: optional(string()),\n members: array(zMemberPattern),\n});\n\n/**\n * Wire shape of `Permission` from `@hediet/linkrpc/identity/capability`.\n * Used by `hubAccess::requestAccess` so the consumer can describe exactly\n * what authority it wants — including wildcard service ids\n * (`target.serviceId: { prefix: \"\" }`). `canInvoke`/`canDelegate` default\n * to `false` (fail closed).\n */\nconst zPermission = object({\n target: zTargetPattern,\n canInvoke: optional(boolean()),\n canDelegate: optional(boolean()),\n params: optional(record(string(), zParamMatcher)),\n callBind: optional(zCallBindHash),\n});\n\n/**\n * A `requestAccess` permission carrying the consent-only {@link zCallIntent}\n * preview alongside the authority it requests. The host strips `callIntent`\n * before minting the signed capability.\n */\nconst zRequestPermission = extend(zPermission, {\n callIntent: optional(zCallIntent),\n});\n\n/**\n * Who is asking, plus the `principal` that becomes the minted capability's\n * `audience`/target. The hub uses `principal` directly (it is no longer derived\n * from the call's verified signer): a cap minted for a `principal` is only usable\n * by the holder of that principal's key, enforced at call time\n * (`permits` checks `audience === call.signer`).\n */\nconst zConsumer = object({\n name: string(),\n /** Capability audience/target — the consumer's own PrincipalId. */\n principal: string(),\n origin: optional(string()),\n purpose: optional(string()),\n});\n\n/**\n * Wire shape of `SignedCapability` from `@hediet/linkrpc`. Mirrors the\n * structural type without pulling zod into the identity package. Keep in\n * sync with `Capability` / `SignedCapability` there.\n *\n * A capability is now a flat object (its fields) plus a `$hubrpcSignature`\n * map carrying the issuer's `capability` signature. Delegation links to a\n * single `parentHash` (the parent's `signedHash(\"capability\", ...)`).\n */\nconst zSignedCapability = object({\n issuer: string(),\n audience: string(),\n permissions: array(zPermission),\n expiresAtMs: optional(number()),\n parentHash: optional(string()),\n nonce: string(),\n $hubrpcSignature: object({\n capability: optional(object({ keyId: string(), sig: string() })),\n call: optional(object({ keyId: string(), sig: string() })),\n }),\n});\n\n/** Lifetime hint for a minted capability. Shared across hubAccess + the manifest. */\nconst zDuration = zEnum(['once', 'shortLived', 'longLived', 'persistent']);\n\n// ---- discovery slot shapes (shared by hubAccess.request and hubAccessManifest) ----\n\n/** One interface a slot's chosen service must implement. */\nconst zInterfaceRef = object({\n id: string(),\n hash: optional(string()),\n /** Default true. */\n required: optional(boolean()),\n});\n\n/** One member (method) the consumer intends to call on a slot's interface. */\nconst zMemberRequest = object({\n interfaceId: string(),\n member: zMemberPattern,\n /** Default true. */\n required: optional(boolean()),\n});\n\n/**\n * A consumer's need for ONE service: the interfaces it must speak and the\n * members it intends to call. The discovery side of both `hubAccess::request`\n * (sent as a call param) and `hubAccessManifest` (published as state).\n */\nconst zSlotRequest = object({\n interfaces: array(zInterfaceRef),\n members: optional(array(zMemberRequest)),\n});\n\n/** The resolution of one slot: exactly one chosen service. */\nconst zResolvedSlot = object({\n serviceId: string(),\n satisfiedInterfaces: array(string()),\n});\n\n/**\n * Minimal JSON-document patch op, addressed by an RFC 6901 JSON Pointer.\n * `path: \"\"` targets the whole document; a pointer like `/granted/secretStore`\n * targets one entry. Used by `hubAccessManifest::setCurrent` to replace the\n * entire granted document or a single entry.\n */\nconst zManifestPatch = discriminatedUnion('op', [\n object({ op: literal('set'), path: string(), value: unknown() }),\n object({ op: literal('remove'), path: string() }),\n]);\n\n/**\n * `hubServiceIdRegistry::registerServiceId` — a participant claims a prefix\n * **outside** its provenance-granted namespace.\n *\n * Reached under the hub's own service id (form-3\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId`) and gated by an\n * admin-rooted capability. For claims **within** the connection's granted\n * namespace, use the cheaper, capability-free `hubGrantedServiceId::register`\n * instead.\n */\nexport const hubServiceIdRegistryInterface = defineInterface(\n {\n id: 'hubServiceIdRegistry',\n description: 'Claim a serviceId prefix on the hub.',\n },\n {\n registerServiceId: requestType(\n object({\n requestedPrefix: string().check(minLength(1)),\n }),\n object({}),\n ),\n },\n);\n\n\n/**\n * `hubGrantedServiceId` — the ungated connection surface, served at the\n * connection root on each participant's overlay (so it is reachable directly,\n * never forwarded, and needs no capability of its own). Two jobs:\n *\n * - `get` (unsigned) reports the topology facts the hub decided for this\n * connection — where it is and what it may claim.\n * - `register` (provenance-gated) claims a prefix **within**\n * this connection's granted namespace — the capability-free claim path.\n * Claims outside the namespace go through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n * - `getHubServiceId` (unsigned) reports the serviceId prefix the hub mounts\n * its own global services under — the prefix to address the admin-gated\n * registry and reflection endpoints through. Served at the connection root\n * so a participant can discover it **before** it knows where the hub lives.\n *\n * The hub's consent front door (`hubAccess::*`) is served at the connection\n * root too, so it needs no bootstrap capability and is reached directly.\n */\nexport const hubGrantedServiceIdInterface = defineInterface(\n {\n id: 'hubGrantedServiceId',\n description: 'Connection facts and capability-free serviceId claims.',\n },\n {\n /**\n * Connection facts (unsigned). The participant pulls, on each\n * (re)connect, the topology facts the hub has decided for this\n * connection:\n *\n * - `grantedServiceIdNamespace` — the absolute serviceId region this\n * connection's provenance may claim (anything at/under it). May be\n * the empty string, meaning \"claim nothing freely\".\n */\n get: requestType(\n object({}),\n object({\n grantedServiceIdNamespace: string(),\n }),\n ),\n\n /**\n * The serviceId prefix the hub mounts its own global services under\n * (default `'hub'`). Use it to address the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door and the\n * hub's reflection endpoints. Served at the connection root (unsigned),\n * so a participant can learn it without first knowing where the hub\n * lives.\n */\n getHubServiceId: requestType(\n object({}),\n object({\n hubServiceId: string(),\n }),\n ),\n\n /**\n * Claim a serviceId prefix **within** this connection's provenance-\n * granted namespace (see `get().grantedServiceIdNamespace`). No\n * capability needed — the grant already happened out-of-band at attach\n * time. The requested `serviceId` must equal the granted namespace or be\n * nested beneath it; anything else is rejected (claim it through the\n * admin-gated `<hubServiceId>::hubServiceIdRegistry::registerServiceId`\n * door instead).\n */\n register: requestType(\n object({ serviceId: string().check(minLength(1)) }),\n object({}),\n ),\n },\n);\n\n/**\n * `hubAccess::request` — a consumer (e.g. a sandboxed web editor) asks the\n * hub for scoped access to one or more services.\n *\n * Consumers describe their needs as named *dependencies* (\"slots\"): each\n * slot lists the interfaces the chosen service must implement and the\n * methods the consumer wants to call. The hub resolves candidate services\n * from its participant directory, then forwards the bundle to a host-\n * supplied handler (see `Hub` options) that shows the user a single\n * prompt. The handler picks a concrete service per slot and approves a\n * subset of the requested methods.\n *\n * In v1 the response carries only the resolution (which serviceId was\n * chosen per slot). Once the hub holds a signing identity it will also\n * return a `SignedCapability` the consumer attaches to subsequent calls.\n * Until then, dispatch is not gated on the grant — see plan-access.md.\n */\nexport const hubAccessInterface = defineInterface(\n {\n id: 'hubAccess',\n description: 'Consumer requests scoped access to services on the hub.',\n },\n {\n request: requestType(\n object({\n consumer: zConsumer,\n dependencies: record(string(), zSlotRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n slots: record(string(), zResolvedSlot),\n /**\n * One or more `SignedCapability`s issued by the hub\n * (audience = consumer NodeId). Consumers attach them\n * via `$hubrpc.capabilities` on subsequent calls. Empty\n * array is legal (hub with no signing identity / tests).\n *\n * Plural so a handler can return per-slot caps with\n * different `exp`/caveats; typical handlers return a\n * single cap covering every granted member.\n */\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n object({\n status: literal('noCandidates'),\n /** Slot ids that have zero matching candidates. */\n slots: array(string()),\n }),\n ]),\n ),\n\n /**\n * Service-pinned widening of an existing grant. The consumer asks\n * the hub for additional members on a `serviceId` they already\n * deal with — same audience (their NodeId) and (intended) same\n * `rootIssuer` as the prior grant. The hub never picks the service\n * for the consumer here: `serviceId` is an input, not a result.\n *\n * On grant the response carries a fresh `SignedCapability` whose\n * attenuations cover **only** the granted delta. Bag-compatible\n * with the prior cap; combine via `merge` (when it exists) or\n * just keep both in `$hubrpc.capabilities`.\n *\n * Distinguished from `request` so the consent UI can render a\n * different affordance (\"X already has read access on `github`,\n * grant `update` as well?\" instead of from-zero selection).\n *\n * TODO(hub-ledger): enforce \"consumer has prior history on this\n * service\" — see `hub.ts:_handleAccessExtend`. v1 forwards\n * directly to the host's `onAccessExtend` without consulting the\n * `_grants` ledger; this is by design while we settle on the\n * persistence model.\n */\n extend: requestType(\n object({\n consumer: zConsumer,\n /**\n * Service to widen the grant on. MUST equal the\n * `serviceId` of a previously-issued attenuation for this\n * consumer NodeId. v1 does not validate this.\n */\n serviceId: string(),\n /**\n * The delta. Same shape as `request`'s slot.members. Each\n * entry MUST refer to an interface the consumer has been\n * introduced to on `serviceId` via a prior `request`.\n */\n added: array(zMemberRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n serviceId: string(),\n /** Members actually granted (handler may approve a subset). */\n granted: array(\n object({\n interfaceId: string(),\n member: zMemberPattern,\n }),\n ),\n /**\n * Capability covering only the granted delta. Audience\n * = consumer NodeId. Bag-compatible with the prior\n * cap on this service.\n */\n capabilities: optional(array(zSignedCapability)),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n // TODO(hub-ledger): additional variants once the host's\n // grant ledger is persisted:\n // - { status: \"unknownService\", serviceId }\n // - { status: \"unknownInterface\", serviceId, interfaceIds }\n // Reject without ever prompting the user when the consumer\n // is asking to widen a service it has no prior grant on.\n ]),\n ),\n\n /**\n * `hubAccess::requestAccess` — direct capability request. The\n * consumer specifies the exact attenuations it wants. No\n * service-discovery, no candidate resolution: the consumer\n * already knows which `(serviceId, interfaceId, members)` it\n * needs, including wildcards (e.g. `serviceId: { prefix: \"\" }`\n * to ask for an interface anywhere).\n *\n * Compared to `request`:\n * - `request` does directory-based discovery, picks one service\n * per slot, and returns a cap pinned to that service. Use\n * when the consumer says \"give me SOME service that does X\".\n * - `requestAccess` is verbatim. Use when the consumer says\n * \"give me exactly these attenuations\". Especially useful for\n * reflection (`hubrpc.directory::list` on any service) and\n * for on-demand per-method grants from an explorer-style UI.\n *\n * The user prompt shows the exact `Capability` the hub will sign\n * on Allow, same byte-equality guarantee as `request`/`extend`.\n */\n requestAccess: requestType(\n object({\n consumer: zConsumer,\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n ]),\n ),\n },\n);\n\n// ════════════════════════════════════════════════════════════════════════\n// hubAccessManifest — the declarative twin of `hubAccess`.\n// ════════════════════════════════════════════════════════════════════════\n\n/**\n * The root issuer(s) whose signature the participant will accept on the minted\n * capability. A capability is only usable by its holder if it chains to a root\n * that the *target* service trusts — and not every service trusts every root.\n * So the participant declares, per entry, which minting root(s) would produce a\n * cap it can actually use; the admin must mint with one of them (or decline).\n *\n * Tri-state, by design:\n * - **omitted** (`undefined`) — unspecified. The participant doesn't know /\n * doesn't constrain the root; the admin picks. (The common, lenient default.)\n * - **non-empty** (`[\"id:abc\", …]`) — mint with one of *these* roots; a cap\n * rooted elsewhere is useless to the participant.\n * - **empty** (`[]`) — \"no root is acceptable\", i.e. nothing can satisfy this.\n * Almost certainly a bug (it can never be granted); kept representable so a\n * tool can detect and flag it rather than silently treating it as \"any\".\n */\nconst zAcceptableRootIds = array(string());\n\n/**\n * A desired access entry the participant publishes. Two kinds, mirroring the\n * two discovery-vs-verbatim halves of `hubAccess`:\n *\n * - `discover` — \"find me a service implementing X\" (same shape as a\n * `hubAccess::request` slot). The admin resolves one concrete service.\n * - `direct` — \"grant exactly these permissions\" (same shape as\n * `hubAccess::requestAccess`). The serviceId is already in each permission's\n * target; no discovery.\n */\nconst zManifestRequest = discriminatedUnion('kind', [\n extend(zSlotRequest, {\n kind: literal('discover'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n /** Human rationale shown to the admin (\"seal tokens at rest\"). */\n reason: optional(string()),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /**\n * Host-authored provenance bag, stamped at `registerHubAccessAtRoot`\n * (NOT taken from the consumer's request params — so it cannot be\n * spoofed). An open `record<string, unknown>` so a host can attach\n * whatever origin info it observed about the requesting transport (e.g.\n * `{ sourceTransportId }` to route a local consent UI). Meaningful only\n * to a consumer that KNOWS this manifest is local; an aggregating\n * manifest MUST re-scope or omit it.\n */\n origin: optional(record(string(), unknown())),\n }),\n object({\n kind: literal('direct'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n reason: optional(string()),\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /** Host-authored provenance bag. See the `discover` variant. */\n origin: optional(record(string(), unknown())),\n }),\n]);\n\n/**\n * The current (granted) value of one entry. For `discover` it carries the single\n * chosen service (mirroring `hubAccess::request`'s resolved slot) plus the caps;\n * for `direct` just the caps (the serviceId is in each permission's target).\n */\nconst zGrantedEntry = discriminatedUnion('kind', [\n extend(zResolvedSlot, {\n kind: literal('discover'),\n capabilities: array(zSignedCapability),\n }),\n object({\n kind: literal('direct'),\n capabilities: array(zSignedCapability),\n }),\n]);\n\n/**\n * The current value of one entry: `granted` (caps, plus the chosen service for\n * `discover`) or `denied` (with an optional reason). An entry *absent* from the\n * current document is **undecided** — distinct from a terminal `denied`, which\n * lets an approver say \"no\" (and why) instead of silently withholding.\n */\nconst zCurrentEntry = discriminatedUnion('status', [\n object({ status: literal('granted'), granted: zGrantedEntry }),\n object({ status: literal('denied'), reason: optional(string()) }),\n]);\n\n/**\n * `hubAccessManifest` — the declarative twin of {@link hubAccessInterface}.\n *\n * Where `hubAccess` is the imperative, just-in-time door a consumer *calls* (and\n * the hub serves at the connection root), `hubAccessManifest` is **served by the\n * participant** under its own serviceId, so it appears in `hubrpc.directory::list`\n * — its very presence is the request. A participant publishes its DESIRED access\n * entries (keyed by id, like `request`'s `dependencies`); an admin discovers\n * them via the directory, mints capabilities **with its own identity** (a\n * configured hub capability root), and writes the CURRENT/granted state back via\n * patches. The participant reads the granted entries and attaches the caps to\n * its later calls.\n *\n * Reconcile model (desired vs. current), aligned with the hub's other surfaces:\n * - `getDesired` / `watchDesired` — the participant's declared needs.\n * - `getCurrent` / `setCurrent` / `watchCurrent` — the admin-written grants.\n *\n * `watch*` follows the coarse empty-tick convention of `hubrpc.directory::watch`:\n * a tick means \"re-`get` now\", keeping the server stateless (no per-item deltas).\n */\nexport const hubAccessManifestInterface = defineInterface(\n {\n id: 'hubAccessManifest',\n description:\n 'Participant-served declarative access: publish DESIRED access entries '\n + '(discover | direct), an admin mints capabilities and writes the CURRENT '\n + 'granted state back. The reconcile twin of hubAccess::{request,requestAccess}.',\n },\n {\n // ---- desired (participant-authored; the admin reads) ----------------\n /** The full desired document: who is asking and the entries it wants. */\n getDesired: requestType(\n object({}),\n object({\n /**\n * entryId → desired entry, each carrying its own `consumer`. A\n * per-participant manifest's entries all share one consumer; a hub\n * broker aggregates entries from many consumers in one flat map.\n */\n requested: record(string(), zManifestRequest),\n /** Bumped whenever `requested` changes; lets watchers dedupe ticks. */\n revision: number(),\n }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the desired document. Emits an empty tick when\n * `requested` may have changed; the caller re-`getDesired`. Resolves when\n * the caller cancels. Mirrors `hubrpc.directory::watch`.\n */\n watchDesired: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n\n // ---- current / granted (admin-authored; the participant reads) ------\n /** The full current document: entryId → granted/denied (absent ⇒ undecided). */\n getCurrent: requestType(\n object({}),\n object({\n /** entryId → current value (granted | denied). */\n current: record(string(), zCurrentEntry),\n /** Bumped on every successful `setCurrent`. */\n revision: number(),\n }),\n ),\n /**\n * Apply patches to the current document. Replace the whole document with\n * `{ op: 'set', path: '', value }`, or a single entry with\n * `{ op: 'set', path: '/current/<entryId>', value }` (a `zCurrentEntry`:\n * granted or denied). Admin-only in practice (gated by a capability\n * rooted at an accepted issuer).\n */\n setCurrent: requestType(\n object({ patches: array(zManifestPatch) }),\n object({ revision: number() }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the current document. Emits an empty tick when\n * `current` may have changed; the participant re-`getCurrent` and applies\n * any new capabilities. Resolves when the caller cancels.\n */\n watchCurrent: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n },\n);\n\n/**\n * The typed client shape of {@link hubAccessManifestInterface} — the exact\n * object `connection.get(hubAccessManifestInterface)` (or\n * `connection.service(id).get(...)`) returns. Approvers depend on this contract,\n * not on a concrete connection: the same approver runs against a local in-memory\n * host (loopback connection), a remote hub's served manifest, or a future\n * aggregating implementation with no code change.\n */\nexport type IHubAccessManifest = InterfaceClient<typeof hubAccessManifestInterface>;\n\n/** One typed entry from `hubAccessManifest::getDesired().requested`. */\nexport type HubAccessManifestRequest =\n Awaited<ReturnType<IHubAccessManifest['getDesired']>>['requested'][string];\n\n/** One typed value accepted at `/current/<entryId>` by `setCurrent`. */\nexport type HubAccessManifestDecision =\n Awaited<ReturnType<IHubAccessManifest['getCurrent']>>['current'][string];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hub.interfaces-I-GtB1Ah.js","names":["zEnum"],"sources":["../../src/hub/common/hub.interfaces.ts"],"sourcesContent":["import { array, boolean, discriminatedUnion, enum as zEnum, extend, literal, minLength, number, object, optional, record, string, union, unknown } from 'zod/mini';\nimport { defineInterface, requestType, type InterfaceClient } from '../../index';\n\nconst zMemberPattern = union([\n object({ exact: string() }),\n object({ prefix: string() }),\n]);\n\n/**\n * Optional per-permission invocation preview the requester suggests for\n * \"Allow once\" consent UX. When present, the user can bind the resulting\n * capability to this exact (method, params, interfaceHash) tuple.\n *\n * `nonce` and `signedAtMs` are consumer-chosen and identify the exact call\n * attempt the cap will authorise. The host hashes the same canonical\n * bytes the consumer will sign; the gate re-derives the hash from the\n * inbound call and compares. The consumer MUST use the same\n * nonce+signedAtMs when it actually issues the call (`@hediet/linkrpc`'s\n * {@link JsonRpcChannel.setCapProvider} hook does this automatically).\n *\n * Everything in `params` is shown to the user verbatim.\n */\nconst zCallIntent = object({\n /** Fully-qualified method name the requester intends to call. */\n method: string(),\n /** Exact params the requester intends to send. */\n params: optional(unknown()),\n /** Optional schema hash assertion the call carries inside the signed payload. */\n interfaceHash: optional(string()),\n /** base64url-encoded nonce bytes the consumer will sign with. */\n nonce: string(),\n /** Unix milliseconds the consumer will sign with. */\n signedAtMs: number(),\n /** Optional one-line summary the shell displays next to params. */\n summary: optional(string()),\n /** Requester's preferred default action (`once` if omitted). */\n suggestion: optional(zEnum(['once', 'shortLived', 'longLived', 'persistent'])),\n});\n\nconst zParamMatcher = union([\n object({ exact: unknown() }),\n object({ enum: array(unknown()) }),\n object({ prefix: string() }),\n object({ subsetOf: array(string()) }),\n object({ any: literal(true) }),\n]);\n\nconst zCallBindHash = object({\n alg: literal('sha256'),\n payloadHash: string(),\n});\n\n/** Wire shape of `TargetPattern` from `@hediet/linkrpc`. */\nconst zTargetPattern = object({\n serviceId: zMemberPattern,\n interfaceId: zMemberPattern,\n interfaceHash: optional(string()),\n members: array(zMemberPattern),\n});\n\n/**\n * Wire shape of `Permission` from `@hediet/linkrpc/identity/capability`.\n * Used by `hubAccess::requestAccess` so the consumer can describe exactly\n * what authority it wants — including wildcard service ids\n * (`target.serviceId: { prefix: \"\" }`). `canInvoke`/`canDelegate` default\n * to `false` (fail closed).\n */\nconst zPermission = object({\n target: zTargetPattern,\n canInvoke: optional(boolean()),\n canDelegate: optional(boolean()),\n params: optional(record(string(), zParamMatcher)),\n callBind: optional(zCallBindHash),\n});\n\n/**\n * A `requestAccess` permission carrying the consent-only {@link zCallIntent}\n * preview alongside the authority it requests. The host strips `callIntent`\n * before minting the signed capability.\n */\nconst zRequestPermission = extend(zPermission, {\n callIntent: optional(zCallIntent),\n});\n\n/**\n * Who is asking, plus the `principal` that becomes the minted capability's\n * `audience`/target. The hub uses `principal` directly (it is no longer derived\n * from the call's verified signer): a cap minted for a `principal` is only usable\n * by the holder of that principal's key, enforced at call time\n * (`permits` checks `audience === call.signer`).\n */\nconst zConsumer = object({\n name: string(),\n /** Capability audience/target — the consumer's own PrincipalId. */\n principal: string(),\n origin: optional(string()),\n purpose: optional(string()),\n});\n\n/**\n * Wire shape of `SignedCapability` from `@hediet/linkrpc`. Mirrors the\n * structural type without pulling zod into the identity package. Keep in\n * sync with `Capability` / `SignedCapability` there.\n *\n * A capability is now a flat object (its fields) plus a `$hubrpcSignature`\n * map carrying the issuer's `capability` signature. Delegation links to a\n * single `parentHash` (the parent's `signedHash(\"capability\", ...)`).\n */\nconst zSignedCapability = object({\n issuer: string(),\n audience: string(),\n permissions: array(zPermission),\n expiresAtMs: optional(number()),\n parentHash: optional(string()),\n nonce: string(),\n $hubrpcSignature: object({\n capability: optional(object({ keyId: string(), sig: string() })),\n call: optional(object({ keyId: string(), sig: string() })),\n }),\n});\n\n/** Lifetime hint for a minted capability. Shared across hubAccess + the manifest. */\nconst zDuration = zEnum(['once', 'shortLived', 'longLived', 'persistent']);\n\n// ---- discovery slot shapes (shared by hubAccess.request and hubAccessManifest) ----\n\n/** One interface a slot's chosen service must implement. */\nconst zInterfaceRef = object({\n id: string(),\n hash: optional(string()),\n /** Default true. */\n required: optional(boolean()),\n});\n\n/** One member (method) the consumer intends to call on a slot's interface. */\nconst zMemberRequest = object({\n interfaceId: string(),\n member: zMemberPattern,\n /** Default true. */\n required: optional(boolean()),\n});\n\n/**\n * A consumer's need for ONE service: the interfaces it must speak and the\n * members it intends to call. The discovery side of both `hubAccess::request`\n * (sent as a call param) and `hubAccessManifest` (published as state).\n */\nconst zSlotRequest = object({\n interfaces: array(zInterfaceRef),\n members: optional(array(zMemberRequest)),\n});\n\n/** The resolution of one slot: exactly one chosen service. */\nconst zResolvedSlot = object({\n serviceId: string(),\n satisfiedInterfaces: array(string()),\n});\n\n/**\n * Minimal JSON-document patch op, addressed by an RFC 6901 JSON Pointer.\n * `path: \"\"` targets the whole document; a pointer like `/granted/secretStore`\n * targets one entry. Used by `hubAccessManifest::setCurrent` to replace the\n * entire granted document or a single entry.\n */\nconst zManifestPatch = discriminatedUnion('op', [\n object({ op: literal('set'), path: string(), value: unknown() }),\n object({ op: literal('remove'), path: string() }),\n]);\n\n/**\n * `hubServiceIdRegistry::registerServiceId` — a participant claims a prefix\n * **outside** its provenance-granted namespace.\n *\n * Reached under the hub's own service id (form-3\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId`) and gated by an\n * admin-rooted capability. For claims **within** the connection's granted\n * namespace, use the cheaper, capability-free `hubGrantedServiceId::register`\n * instead.\n */\nexport const hubServiceIdRegistryInterface = defineInterface(\n {\n id: 'hubServiceIdRegistry',\n description: 'Claim a serviceId prefix on the hub.',\n },\n {\n registerServiceId: requestType(\n object({\n requestedPrefix: string().check(minLength(1)),\n }),\n object({}),\n ),\n },\n);\n\n\n/**\n * `hubGrantedServiceId` — the ungated connection surface, served at the\n * connection root on each participant's overlay (so it is reachable directly,\n * never forwarded, and needs no capability of its own). Two jobs:\n *\n * - `get` (unsigned) reports the topology facts the hub decided for this\n * connection — where it is and what it may claim.\n * - `register` (provenance-gated) claims a prefix **within**\n * this connection's granted namespace — the capability-free claim path.\n * Claims outside the namespace go through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n * - `getHubServiceId` (unsigned) reports the serviceId prefix the hub mounts\n * its own global services under — the prefix to address the admin-gated\n * registry and reflection endpoints through. Served at the connection root\n * so a participant can discover it **before** it knows where the hub lives.\n *\n * The hub's consent front door (`hubAccess::*`) is served at the connection\n * root too, so it needs no bootstrap capability and is reached directly.\n */\nexport const hubGrantedServiceIdInterface = defineInterface(\n {\n id: 'hubGrantedServiceId',\n description: 'Connection facts and capability-free serviceId claims.',\n },\n {\n /**\n * Connection facts (unsigned). The participant pulls, on each\n * (re)connect, the topology facts the hub has decided for this\n * connection:\n *\n * - `grantedServiceIdNamespace` — the absolute serviceId region this\n * connection's provenance may claim (anything at/under it). May be\n * the empty string, meaning \"claim nothing freely\".\n */\n get: requestType(\n object({}),\n object({\n grantedServiceIdNamespace: string(),\n }),\n ),\n\n /**\n * The serviceId prefix the hub mounts its own global services under\n * (default `'hub'`). Use it to address the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door and the\n * hub's reflection endpoints. Served at the connection root (unsigned),\n * so a participant can learn it without first knowing where the hub\n * lives.\n */\n getHubServiceId: requestType(\n object({}),\n object({\n hubServiceId: string(),\n }),\n ),\n\n /**\n * Claim a serviceId prefix **within** this connection's provenance-\n * granted namespace (see `get().grantedServiceIdNamespace`). No\n * capability needed — the grant already happened out-of-band at attach\n * time. The requested `serviceId` must equal the granted namespace or be\n * nested beneath it; anything else is rejected (claim it through the\n * admin-gated `<hubServiceId>::hubServiceIdRegistry::registerServiceId`\n * door instead).\n */\n register: requestType(\n object({ serviceId: string().check(minLength(1)) }),\n object({}),\n ),\n },\n);\n\n/**\n * `hubAccess::request` — a consumer (e.g. a sandboxed web editor) asks the\n * hub for scoped access to one or more services.\n *\n * Consumers describe their needs as named *dependencies* (\"slots\"): each\n * slot lists the interfaces the chosen service must implement and the\n * methods the consumer wants to call. The hub resolves candidate services\n * from its participant directory, then forwards the bundle to a host-\n * supplied handler (see `Hub` options) that shows the user a single\n * prompt. The handler picks a concrete service per slot and approves a\n * subset of the requested methods.\n *\n * In v1 the response carries only the resolution (which serviceId was\n * chosen per slot). Once the hub holds a signing identity it will also\n * return a `SignedCapability` the consumer attaches to subsequent calls.\n * Until then, dispatch is not gated on the grant — see plan-access.md.\n */\nexport const hubAccessInterface = defineInterface(\n {\n id: 'hubAccess',\n description: 'Consumer requests scoped access to services on the hub.',\n },\n {\n request: requestType(\n object({\n consumer: zConsumer,\n dependencies: record(string(), zSlotRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n slots: record(string(), zResolvedSlot),\n /**\n * One or more `SignedCapability`s issued by the hub\n * (audience = consumer NodeId). Consumers attach them\n * via `$hubrpc.capabilities` on subsequent calls. Empty\n * array is legal (hub with no signing identity / tests).\n *\n * Plural so a handler can return per-slot caps with\n * different `exp`/caveats; typical handlers return a\n * single cap covering every granted member.\n */\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n object({\n status: literal('noCandidates'),\n /** Slot ids that have zero matching candidates. */\n slots: array(string()),\n }),\n ]),\n ),\n\n /**\n * Service-pinned widening of an existing grant. The consumer asks\n * the hub for additional members on a `serviceId` they already\n * deal with — same audience (their NodeId) and (intended) same\n * `rootIssuer` as the prior grant. The hub never picks the service\n * for the consumer here: `serviceId` is an input, not a result.\n *\n * On grant the response carries a fresh `SignedCapability` whose\n * attenuations cover **only** the granted delta. Bag-compatible\n * with the prior cap; combine via `merge` (when it exists) or\n * just keep both in `$hubrpc.capabilities`.\n *\n * Distinguished from `request` so the consent UI can render a\n * different affordance (\"X already has read access on `github`,\n * grant `update` as well?\" instead of from-zero selection).\n *\n * TODO(hub-ledger): enforce \"consumer has prior history on this\n * service\" — see `hub.ts:_handleAccessExtend`. v1 forwards\n * directly to the host's `onAccessExtend` without consulting the\n * `_grants` ledger; this is by design while we settle on the\n * persistence model.\n */\n extend: requestType(\n object({\n consumer: zConsumer,\n /**\n * Service to widen the grant on. MUST equal the\n * `serviceId` of a previously-issued attenuation for this\n * consumer NodeId. v1 does not validate this.\n */\n serviceId: string(),\n /**\n * The delta. Same shape as `request`'s slot.members. Each\n * entry MUST refer to an interface the consumer has been\n * introduced to on `serviceId` via a prior `request`.\n */\n added: array(zMemberRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n serviceId: string(),\n /** Members actually granted (handler may approve a subset). */\n granted: array(\n object({\n interfaceId: string(),\n member: zMemberPattern,\n }),\n ),\n /**\n * Capability covering only the granted delta. Audience\n * = consumer NodeId. Bag-compatible with the prior\n * cap on this service.\n */\n capabilities: optional(array(zSignedCapability)),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n // TODO(hub-ledger): additional variants once the host's\n // grant ledger is persisted:\n // - { status: \"unknownService\", serviceId }\n // - { status: \"unknownInterface\", serviceId, interfaceIds }\n // Reject without ever prompting the user when the consumer\n // is asking to widen a service it has no prior grant on.\n ]),\n ),\n\n /**\n * `hubAccess::requestAccess` — direct capability request. The\n * consumer specifies the exact attenuations it wants. No\n * service-discovery, no candidate resolution: the consumer\n * already knows which `(serviceId, interfaceId, members)` it\n * needs, including wildcards (e.g. `serviceId: { prefix: \"\" }`\n * to ask for an interface anywhere).\n *\n * Compared to `request`:\n * - `request` does directory-based discovery, picks one service\n * per slot, and returns a cap pinned to that service. Use\n * when the consumer says \"give me SOME service that does X\".\n * - `requestAccess` is verbatim. Use when the consumer says\n * \"give me exactly these attenuations\". Especially useful for\n * reflection (`hubrpc.directory::list` on any service) and\n * for on-demand per-method grants from an explorer-style UI.\n *\n * The user prompt shows the exact `Capability` the hub will sign\n * on Allow, same byte-equality guarantee as `request`/`extend`.\n */\n requestAccess: requestType(\n object({\n consumer: zConsumer,\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n ]),\n ),\n },\n);\n\n// ════════════════════════════════════════════════════════════════════════\n// hubAccessManifest — the declarative twin of `hubAccess`.\n// ════════════════════════════════════════════════════════════════════════\n\n/**\n * The root issuer(s) whose signature the participant will accept on the minted\n * capability. A capability is only usable by its holder if it chains to a root\n * that the *target* service trusts — and not every service trusts every root.\n * So the participant declares, per entry, which minting root(s) would produce a\n * cap it can actually use; the admin must mint with one of them (or decline).\n *\n * Tri-state, by design:\n * - **omitted** (`undefined`) — unspecified. The participant doesn't know /\n * doesn't constrain the root; the admin picks. (The common, lenient default.)\n * - **non-empty** (`[\"id:abc\", …]`) — mint with one of *these* roots; a cap\n * rooted elsewhere is useless to the participant.\n * - **empty** (`[]`) — \"no root is acceptable\", i.e. nothing can satisfy this.\n * Almost certainly a bug (it can never be granted); kept representable so a\n * tool can detect and flag it rather than silently treating it as \"any\".\n */\nconst zAcceptableRootIds = array(string());\n\n/**\n * A desired access entry the participant publishes. Two kinds, mirroring the\n * two discovery-vs-verbatim halves of `hubAccess`:\n *\n * - `discover` — \"find me a service implementing X\" (same shape as a\n * `hubAccess::request` slot). The admin resolves one concrete service.\n * - `direct` — \"grant exactly these permissions\" (same shape as\n * `hubAccess::requestAccess`). The serviceId is already in each permission's\n * target; no discovery.\n */\nconst zManifestRequest = discriminatedUnion('kind', [\n extend(zSlotRequest, {\n kind: literal('discover'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n /** Human rationale shown to the admin (\"seal tokens at rest\"). */\n reason: optional(string()),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /**\n * Host-authored provenance bag, stamped at `registerHubAccessAtRoot`\n * (NOT taken from the consumer's request params — so it cannot be\n * spoofed). An open `record<string, unknown>` so a host can attach\n * whatever origin info it observed about the requesting transport (e.g.\n * `{ sourceTransportId }` to route a local consent UI). Meaningful only\n * to a consumer that KNOWS this manifest is local; an aggregating\n * manifest MUST re-scope or omit it.\n */\n origin: optional(record(string(), unknown())),\n }),\n object({\n kind: literal('direct'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n reason: optional(string()),\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /** Host-authored provenance bag. See the `discover` variant. */\n origin: optional(record(string(), unknown())),\n }),\n]);\n\n/**\n * The current (granted) value of one entry. For `discover` it carries the single\n * chosen service (mirroring `hubAccess::request`'s resolved slot) plus the caps;\n * for `direct` just the caps (the serviceId is in each permission's target).\n */\nconst zGrantedEntry = discriminatedUnion('kind', [\n extend(zResolvedSlot, {\n kind: literal('discover'),\n capabilities: array(zSignedCapability),\n }),\n object({\n kind: literal('direct'),\n capabilities: array(zSignedCapability),\n }),\n]);\n\n/**\n * The current value of one entry: `granted` (caps, plus the chosen service for\n * `discover`) or `denied` (with an optional reason). An entry *absent* from the\n * current document is **undecided** — distinct from a terminal `denied`, which\n * lets an approver say \"no\" (and why) instead of silently withholding.\n */\nconst zCurrentEntry = discriminatedUnion('status', [\n object({ status: literal('granted'), granted: zGrantedEntry }),\n object({ status: literal('denied'), reason: optional(string()) }),\n]);\n\n/**\n * `hubAccessManifest` — the declarative twin of {@link hubAccessInterface}.\n *\n * Where `hubAccess` is the imperative, just-in-time door a consumer *calls* (and\n * the hub serves at the connection root), `hubAccessManifest` is **served by the\n * participant** under its own serviceId, so it appears in `hubrpc.directory::list`\n * — its very presence is the request. A participant publishes its DESIRED access\n * entries (keyed by id, like `request`'s `dependencies`); an admin discovers\n * them via the directory, mints capabilities **with its own identity** (a\n * configured hub capability root), and writes the CURRENT/granted state back via\n * patches. The participant reads the granted entries and attaches the caps to\n * its later calls.\n *\n * Reconcile model (desired vs. current), aligned with the hub's other surfaces:\n * - `getDesired` / `watchDesired` — the participant's declared needs.\n * - `getCurrent` / `setCurrent` / `watchCurrent` — the admin-written grants.\n *\n * `watch*` follows the coarse empty-tick convention of `hubrpc.directory::watch`:\n * a tick means \"re-`get` now\", keeping the server stateless (no per-item deltas).\n */\nexport const hubAccessManifestInterface = defineInterface(\n {\n id: 'hubAccessManifest',\n description:\n 'Participant-served declarative access: publish DESIRED access entries '\n + '(discover | direct), an admin mints capabilities and writes the CURRENT '\n + 'granted state back. The reconcile twin of hubAccess::{request,requestAccess}.',\n },\n {\n // ---- desired (participant-authored; the admin reads) ----------------\n /** The full desired document: who is asking and the entries it wants. */\n getDesired: requestType(\n object({}),\n object({\n /**\n * entryId → desired entry, each carrying its own `consumer`. A\n * per-participant manifest's entries all share one consumer; a hub\n * broker aggregates entries from many consumers in one flat map.\n */\n requested: record(string(), zManifestRequest),\n /** Bumped whenever `requested` changes; lets watchers dedupe ticks. */\n revision: number(),\n }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the desired document. Emits an empty tick when\n * `requested` may have changed; the caller re-`getDesired`. Resolves when\n * the caller cancels. Mirrors `hubrpc.directory::watch`.\n */\n watchDesired: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n\n // ---- current / granted (admin-authored; the participant reads) ------\n /** The full current document: entryId → granted/denied (absent ⇒ undecided). */\n getCurrent: requestType(\n object({}),\n object({\n /** entryId → current value (granted | denied). */\n current: record(string(), zCurrentEntry),\n /** Bumped on every successful `setCurrent`. */\n revision: number(),\n }),\n ),\n /**\n * Apply patches to the current document. Replace the whole document with\n * `{ op: 'set', path: '', value }`, or a single entry with\n * `{ op: 'set', path: '/current/<entryId>', value }` (a `zCurrentEntry`:\n * granted or denied). Admin-only in practice (gated by a capability\n * rooted at an accepted issuer).\n */\n setCurrent: requestType(\n object({ patches: array(zManifestPatch) }),\n object({ revision: number() }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the current document. Emits an empty tick when\n * `current` may have changed; the participant re-`getCurrent` and applies\n * any new capabilities. Resolves when the caller cancels.\n */\n watchCurrent: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n },\n);\n\n/**\n * The typed client shape of {@link hubAccessManifestInterface} — the exact\n * object `connection.get(hubAccessManifestInterface)` (or\n * `connection.service(id).get(...)`) returns. Approvers depend on this contract,\n * not on a concrete connection: the same approver runs against a local in-memory\n * host (loopback connection), a remote hub's served manifest, or a future\n * aggregating implementation with no code change.\n */\nexport type IHubAccessManifest = InterfaceClient<typeof hubAccessManifestInterface>;\n\n/** One typed entry from `hubAccessManifest::getDesired().requested`. */\nexport type HubAccessManifestRequest =\n Awaited<ReturnType<IHubAccessManifest['getDesired']>>['requested'][string];\n\n/** One typed value accepted at `/current/<entryId>` by `setCurrent`. */\nexport type HubAccessManifestDecision =\n Awaited<ReturnType<IHubAccessManifest['getCurrent']>>['current'][string];\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { Fr as SignedCapability } from "./linkRpcConnection-
|
|
5
|
+
import { Fr as SignedCapability } from "./linkRpcConnection-BnMwoBYd.js";
|
|
6
6
|
//#region src/hub/common/hubAccess.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* The client-facing wire shapes of the hub's capability-negotiation protocol
|
|
@@ -76,4 +76,4 @@ declare function findCoveringCapabilities(capabilities: readonly SignedCapabilit
|
|
|
76
76
|
}): readonly SignedCapability[] | undefined;
|
|
77
77
|
//#endregion
|
|
78
78
|
export { HubAccessResult as a, HubAccessRequest as i, HubAccessPattern as n, findCoveringCapabilities as o, HubAccessPermission as r, HubAccessDuration as t };
|
|
79
|
-
//# sourceMappingURL=hubAccess-
|
|
79
|
+
//# sourceMappingURL=hubAccess-Ci7MB_My.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hubAccess-
|
|
1
|
+
{"version":3,"file":"hubAccess-Ci7MB_My.d.ts","names":[],"sources":["../../src/hub/common/hubAccess.ts"],"mappings":";;;;;;;;;;;;;KAqBY;;KAGA;WAA8B;;WAA8B;;;UAGvD;WACJ;aACI,WAAW;aACX,aAAa;aACb;aACA,kBAAkB;;;WAGtB;;WAEA;WACA,SAAS;;;UAIL;WACJ;aAAqB;aAAuB;aAA0B;;;WAEtE,sBAAsB;;WAEtB,WAAW;;;;;;;KAQZ;WAEK;;WAEA,uBAAuB;;WAEvB;;WAEA;WAA2B;;WAC3B;WAAyB;;;;;;;;;;iBAU1B,yBACZ,uBAAuB,oBACvB,oBAAoB,uBACpB;WACa;WACA;aAEL"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { r as hubGrantedServiceIdInterface } from "./hub.interfaces-
|
|
5
|
+
import { r as hubGrantedServiceIdInterface } from "./hub.interfaces-I-GtB1Ah.js";
|
|
6
6
|
//#region src/hub/client/hubFacade.ts
|
|
7
7
|
/**
|
|
8
8
|
* Thin façade over the hub's own serviceId-routed services
|
|
@@ -82,4 +82,4 @@ function hubFromConnection(connection) {
|
|
|
82
82
|
//#endregion
|
|
83
83
|
export { hubFromConnection as n, Hub as t };
|
|
84
84
|
|
|
85
|
-
//# sourceMappingURL=hubFacade-
|
|
85
|
+
//# sourceMappingURL=hubFacade-ClN2ausU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hubFacade-
|
|
1
|
+
{"version":3,"file":"hubFacade-ClN2ausU.js","names":[],"sources":["../../src/hub/client/hubFacade.ts"],"sourcesContent":["import type { LinkRpcConnection } from '../../connection/linkRpcConnection';\nimport { hubGrantedServiceIdInterface } from '../common/hub.interfaces';\n\n/**\n * Description of the services a consumer will need, presented to the user\n * up-front so access can be approved before the first call. Each named\n * *dependency* (\"slot\") lists the interfaces the chosen service must\n * implement and the members the consumer intends to call.\n */\nexport interface SlotPrerequest {\n readonly consumer: { readonly name: string; readonly purpose: string; };\n readonly dependencies: Readonly<\n Record<string, {\n readonly interfaces: readonly { readonly id: string; }[];\n readonly members: readonly {\n readonly interfaceId: string;\n readonly member: { readonly exact: string; };\n }[];\n }>\n >;\n}\n\n/** Handle for a set of slots reserved via {@link Hub.prerequestSlots}. */\nexport interface SlotReservation {\n /** Resolve the reserved slots into connected service clients. */\n resolve(): Promise<never>;\n}\n\n/**\n * Thin façade over the hub's own serviceId-routed services\n * (`hubGrantedServiceId`, `hubAccess`, …). The hub is not a single object on\n * the wire — it is a set of services the connection can `get`. This groups the\n * common connection calls so callers don't hand-route interface clients.\n */\nexport class Hub {\n constructor(private readonly _connection: LinkRpcConnection) { }\n\n /** `hubGrantedServiceId::get` — where am I and which serviceId namespace may I claim. */\n public getConnectionInfo() {\n return this._connection.get(hubGrantedServiceIdInterface).get({});\n }\n\n /**\n * Reserve the slots a consumer will need so the user can approve them\n * ahead of the first call. NOT YET IMPLEMENTED by the hub — the\n * returned reservation throws on {@link SlotReservation.resolve}.\n */\n public prerequestSlots(_request: SlotPrerequest): SlotReservation {\n return {\n resolve: async (): Promise<never> => {\n throw new Error('not implemented: hub.prerequestSlots / slot resolution');\n },\n };\n }\n\n /**\n * Claim a serviceId namespace for this connection so it may register\n * services under it, via `hubGrantedServiceId::register`.\n *\n * The claim is auto-granted from the connection's provenance when\n * `serviceIdNamespace` is at/under the connection's\n * `grantedServiceIdNamespace` (see {@link getConnectionInfo}) — no\n * signature or capability needed. Claims **outside** the granted namespace\n * are rejected here; route those through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n *\n * Resolves once the namespace is owned by this connection (idempotent for\n * a namespace this connection already owns). Rejects when the hub denies\n * the claim — e.g. the namespace is owned by another identity, or the\n * caller lacks admin authority outside its granted namespace.\n *\n * `requestPermissionIfDenied` is a hint for the future interactive\n * admin-approval flow; the hub does not yet expose one, so denial always\n * surfaces as a rejection regardless of the flag.\n */\n public async registerServiceIdNamespace(\n serviceIdNamespace: string,\n _opts?: { readonly requestPermissionIfDenied?: boolean; },\n ): Promise<void> {\n await this._connection\n .get(hubGrantedServiceIdInterface)\n .register({ serviceId: serviceIdNamespace });\n }\n\n /**\n * Claim this connection's provenance-granted serviceId namespace in one\n * step: read `grantedServiceIdNamespace` via {@link getConnectionInfo},\n * then {@link registerServiceIdNamespace} it, returning the claimed\n * `serviceId` so the caller can mount services under it.\n *\n * This is the batteries-included version of the\n * `hubGrantedServiceId::get` → `hubGrantedServiceId::register` dance a\n * service-exposing participant performs right after connecting.\n *\n * Throws when the hub granted this connection no namespace (an empty\n * `grantedServiceIdNamespace`, meaning \"claim nothing freely\") — there is\n * nothing to register, and any service prefix would have to be claimed\n * through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n */\n public async claimGrantedServiceIdNamespace(): Promise<{ readonly serviceId: string; }> {\n const { grantedServiceIdNamespace } = await this.getConnectionInfo();\n if (!grantedServiceIdNamespace) {\n throw new Error(\n 'hubGrantedServiceId::get returned an empty granted namespace; ' +\n 'nothing to claim (register a prefix through the admin-gated ' +\n 'hubServiceIdRegistry::registerServiceId door instead)',\n );\n }\n await this.registerServiceIdNamespace(grantedServiceIdNamespace);\n return { serviceId: grantedServiceIdNamespace };\n }\n}\n\n/** Wrap a connection in a {@link Hub} façade. */\nexport function hubFromConnection(connection: LinkRpcConnection): Hub {\n return new Hub(connection);\n}\n"],"mappings":";;;;;;;;;;;;AAkCA,IAAa,MAAb,MAAiB;CACgB;CAA7B,YAAY,aAAiD;EAAhC,KAAA,cAAA;CAAkC;;CAG/D,oBAA2B;EACvB,OAAO,KAAK,YAAY,IAAI,4BAA4B,CAAC,CAAC,IAAI,CAAC,CAAC;CACpE;;;;;;CAOA,gBAAuB,UAA2C;EAC9D,OAAO,EACH,SAAS,YAA4B;GACjC,MAAM,IAAI,MAAM,wDAAwD;EAC5E,EACJ;CACJ;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAa,2BACT,oBACA,OACa;EACb,MAAM,KAAK,YACN,IAAI,4BAA4B,CAAC,CACjC,SAAS,EAAE,WAAW,mBAAmB,CAAC;CACnD;;;;;;;;;;;;;;;;;CAkBA,MAAa,iCAA2E;EACpF,MAAM,EAAE,8BAA8B,MAAM,KAAK,kBAAkB;EACnE,IAAI,CAAC,2BACD,MAAM,IAAI,MACN,iLAGJ;EAEJ,MAAM,KAAK,2BAA2B,yBAAyB;EAC/D,OAAO,EAAE,WAAW,0BAA0B;CAClD;AACJ;;AAGA,SAAgB,kBAAkB,YAAoC;CAClE,OAAO,IAAI,IAAI,UAAU;AAC7B"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { a as LinkRpcConnection } from "./linkRpcConnection-
|
|
5
|
+
import { a as LinkRpcConnection } from "./linkRpcConnection-BnMwoBYd.js";
|
|
6
6
|
//#region src/hub/client/hubFacade.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Description of the services a consumer will need, presented to the user
|
|
@@ -98,4 +98,4 @@ declare class Hub {
|
|
|
98
98
|
declare function hubFromConnection(connection: LinkRpcConnection): Hub;
|
|
99
99
|
//#endregion
|
|
100
100
|
export { hubFromConnection as i, SlotPrerequest as n, SlotReservation as r, Hub as t };
|
|
101
|
-
//# sourceMappingURL=hubFacade-
|
|
101
|
+
//# sourceMappingURL=hubFacade-xR-h9dSH.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hubFacade-
|
|
1
|
+
{"version":3,"file":"hubFacade-xR-h9dSH.d.ts","names":[],"sources":["../../src/hub/client/hubFacade.ts"],"mappings":";;;;;;;;;;;;UASiB;WACJ;aAAqB;aAAuB;;WAC5C,cAAc,SACnB;aACa;eAAgC;;aAChC;eACI;eACA;iBAAmB;;;;;;UAO3B;;EAEb,WAAW;;;;;;;;cASF;mBACoB;EAAA,YAAA,aAAa;;EAGnC,qBAAiB;;;;;;;;EASjB,gBAAgB,UAAU,iBAAiB;;;;;;;;;;;;;;;;;;;;;EA4BrC,2BACT,4BACA;aAAmB;MACpB;;;;;;;;;;;;;;;;;EAsBU,kCAAkC;aAAmB;;;;iBAetD,kBAAkB,YAAY,oBAAoB"}
|
|
@@ -42,6 +42,7 @@ const KEPT_KEYS = /* @__PURE__ */ new Set([
|
|
|
42
42
|
* - `{"not":{}}` ⇒ `false` (bottom); any other `not` is stripped
|
|
43
43
|
* - `type:"object"` without `additionalProperties` ⇒ closed (`false`),
|
|
44
44
|
* matching linkrpc's stricter contract
|
|
45
|
+
* - producer `type` arrays ⇒ `anyOf` branches, preserving their order
|
|
45
46
|
*
|
|
46
47
|
* The output is canonical: structurally equal inputs produce structurally
|
|
47
48
|
* equal outputs, which is what `computeInterfaceHash` relies on.
|
|
@@ -52,6 +53,40 @@ function normalizeJsonSchema(raw) {
|
|
|
52
53
|
if (Array.isArray(raw)) throw new Error("normalizeJsonSchema: expected object, got array");
|
|
53
54
|
const r = raw;
|
|
54
55
|
if ("not" in r && isEmptyObject(r["not"])) return false;
|
|
56
|
+
if (Array.isArray(r["type"])) {
|
|
57
|
+
const types = r["type"];
|
|
58
|
+
if (types.length === 0 || new Set(types).size !== types.length || types.some((type) => ![
|
|
59
|
+
"null",
|
|
60
|
+
"boolean",
|
|
61
|
+
"number",
|
|
62
|
+
"integer",
|
|
63
|
+
"string",
|
|
64
|
+
"array",
|
|
65
|
+
"object"
|
|
66
|
+
].includes(type))) throw new Error("normalizeJsonSchema: type array must contain distinct JSON Schema type names");
|
|
67
|
+
const { title, description, ...branch } = r;
|
|
68
|
+
return normalizeJsonSchema({
|
|
69
|
+
title,
|
|
70
|
+
description,
|
|
71
|
+
anyOf: types.map((type) => {
|
|
72
|
+
const schema = {
|
|
73
|
+
...branch,
|
|
74
|
+
type
|
|
75
|
+
};
|
|
76
|
+
if (type !== "object") {
|
|
77
|
+
delete schema.properties;
|
|
78
|
+
delete schema.required;
|
|
79
|
+
delete schema.additionalProperties;
|
|
80
|
+
}
|
|
81
|
+
if (type !== "array") {
|
|
82
|
+
delete schema.items;
|
|
83
|
+
delete schema.prefixItems;
|
|
84
|
+
}
|
|
85
|
+
if (type !== "string" && type !== "number" && type !== "integer") delete schema.format;
|
|
86
|
+
return schema;
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
}
|
|
55
90
|
const out = {};
|
|
56
91
|
for (const [k, v] of Object.entries(r)) {
|
|
57
92
|
if (!KEPT_KEYS.has(k)) continue;
|
|
@@ -1382,10 +1417,6 @@ function createSchemaToZod(components = {}) {
|
|
|
1382
1417
|
if (schema === true) return z.unknown();
|
|
1383
1418
|
if (schema === false) return z.never();
|
|
1384
1419
|
const raw = schema;
|
|
1385
|
-
if (Array.isArray(raw.type)) return union$1(raw.type.map((type) => compile({
|
|
1386
|
-
...raw,
|
|
1387
|
-
type
|
|
1388
|
-
})));
|
|
1389
1420
|
let base;
|
|
1390
1421
|
if ("$ref" in schema) base = component(componentSchemaName(schema.$ref));
|
|
1391
1422
|
else if ("const" in schema) base = valueSchema(schema.const);
|
|
@@ -1435,6 +1466,7 @@ function createSchemaToZod(components = {}) {
|
|
|
1435
1466
|
});
|
|
1436
1467
|
break;
|
|
1437
1468
|
}
|
|
1469
|
+
default: throw new Error("schemaToZod: unsupported schema type; normalize producer schemas before use");
|
|
1438
1470
|
}
|
|
1439
1471
|
return base;
|
|
1440
1472
|
};
|
|
@@ -2929,4 +2961,4 @@ function createPortId() {
|
|
|
2929
2961
|
//#endregion
|
|
2930
2962
|
export { validateInterfaceTemplate as $, PRINCIPAL_PREFIX as A, interfaceFromSchema as B, zTopologyTransportInfo as C, isRequest as Ct, zTrafficTransitEvent as D, normalizeJsonSchema as Dt, zTrafficTransitEndpoint as E, isRpcFailure as Et, principalForPublicKey as F, schemaToZod as G, interfaceTemplateGroup as H, publicKeyForKeyId as I, getInterfaceTags as J, INTERFACE_TEMPLATES_EXTENSION as K, resolveSigningKey as L, bytesToBase64Url as M, keyIdForPrincipal as N, zTrafficWatchResult as O, keyIdForPublicKey as P, preferRicherInterfaceSchema as Q, InterfaceDefinition as R, zTopologyTransportEndpoint as S, isNotification as St, zTrafficOverflowEvent as T, RpcFailure as Tt, validateInterfaceErrors as U, toMethodSchema as V, createSchemaToZod as W, interfaceTemplateMemberName as X, interfaceTemplateArgumentsEquivalent as Y, interfaceTemplatesEqual as Z, zTopologyGraph as _, jsonIssues as _t, TrafficWatchFlowTracker as a, jcsCanonicalizeBytes as at, zTopologyNode as b, brandApplicationError as bt, StreamControlType as c, componentSchemaRef as ct, nodeInterface as d, applicationError as dt, validateInterfaceTemplates as et, topologyInterface as f, isApplicationErrorValue as ft, zRouteClaim as g, zodToSvcJsonSchema as gt, zParticipantDescriptorSource as h, rpcError as ht, TrafficFlowFilter as i, jcsCanonicalize as it, base64UrlToBytes as j, KEY_ID_PREFIX as k, StreamDir as l, NotificationType as lt, zParticipantDescriptor as m, requestType as mt, ConnectionInspectionSource as n, computeInterfaceHash as nt, STREAM_METHOD as o, assertSchemaReferences as ot, trafficInterface as p, notificationType as pt, bindTemplateArguments as q, BoundedTrafficSubscription as r, sha256 as rt, StreamControlReason as s, componentSchemaName as st, InspectionHost as t, EXTENSION_PREFIX as tt, streamInterface as u, RequestType as ut, zTopologyLink as v, parseDeclaredErrorBody as vt, zTrafficEvent as w, isResponse as wt, zTopologyPort as x, ErrorCode as xt, zTopologyLinkEndpoint as y, jsonPointerSegment as yt, defineInterface as z };
|
|
2931
2963
|
|
|
2932
|
-
//# sourceMappingURL=inspectionHost-
|
|
2964
|
+
//# sourceMappingURL=inspectionHost-BA6ZmC_P.js.map
|