@hediet/linkrpc 0.0.1
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 +383 -0
- package/dist/chunks/_empty-crypto-Bi0tGx5K.js +8 -0
- package/dist/chunks/boundedTrafficSubscription-1L592xc7.js +1126 -0
- package/dist/chunks/boundedTrafficSubscription-1L592xc7.js.map +1 -0
- package/dist/chunks/hub.interfaces-BzWfsVT2.js +526 -0
- package/dist/chunks/hub.interfaces-BzWfsVT2.js.map +1 -0
- package/dist/chunks/hubAccess-DwTZPiI8.d.ts +79 -0
- package/dist/chunks/hubAccess-DwTZPiI8.d.ts.map +1 -0
- package/dist/chunks/hubFacade-CQflVkVC.js +85 -0
- package/dist/chunks/hubFacade-CQflVkVC.js.map +1 -0
- package/dist/chunks/hubFacade-Dkgw2pTi.d.ts +101 -0
- package/dist/chunks/hubFacade-Dkgw2pTi.d.ts.map +1 -0
- package/dist/chunks/linkRpcConnection-CtlQmetO.d.ts +3623 -0
- package/dist/chunks/linkRpcConnection-CtlQmetO.d.ts.map +1 -0
- package/dist/chunks/rolldown-runtime-4LSo1kEK.js +17 -0
- package/dist/chunks/src-D3NUIwyo.js +7795 -0
- package/dist/chunks/src-D3NUIwyo.js.map +1 -0
- package/dist/hub/client/index.d.ts +50 -0
- package/dist/hub/client/index.d.ts.map +1 -0
- package/dist/hub/client/index.js +97 -0
- package/dist/hub/client/index.js.map +1 -0
- package/dist/hub/common/index.d.ts +1189 -0
- package/dist/hub/common/index.d.ts.map +1 -0
- package/dist/hub/common/index.js +267 -0
- package/dist/hub/common/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/inspection/index.d.ts +66 -0
- package/dist/inspection/index.d.ts.map +1 -0
- package/dist/inspection/index.js +6 -0
- package/dist/node.d.ts +548 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +1087 -0
- package/dist/node.js.map +1 -0
- package/dist/web.d.ts +44 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +58 -0
- package/dist/web.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,3623 @@
|
|
|
1
|
+
import { $ZodType, output } from "zod/v4/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/protocol/jsonValue.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The JSON value lattice. Anything the wire can carry.
|
|
6
|
+
*
|
|
7
|
+
* Object values are `JsonValue | undefined` so `undefined`-valued fields
|
|
8
|
+
* may be present in source and are simply omitted at canonicalisation
|
|
9
|
+
* time (rather than serialised as `null`).
|
|
10
|
+
*/
|
|
11
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
12
|
+
[key: string]: JsonValue | undefined;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/protocol/jsonRpc.d.ts
|
|
16
|
+
type RequestId = number | string;
|
|
17
|
+
interface JsonRpcRequest<TParams = JsonValue> {
|
|
18
|
+
jsonrpc: "2.0";
|
|
19
|
+
id: RequestId;
|
|
20
|
+
method: string;
|
|
21
|
+
params?: TParams;
|
|
22
|
+
}
|
|
23
|
+
interface JsonRpcNotification<TParams = JsonValue> {
|
|
24
|
+
jsonrpc: "2.0";
|
|
25
|
+
method: string;
|
|
26
|
+
params?: TParams;
|
|
27
|
+
}
|
|
28
|
+
interface JsonRpcSuccess {
|
|
29
|
+
jsonrpc: "2.0";
|
|
30
|
+
id: RequestId | null;
|
|
31
|
+
result: JsonValue;
|
|
32
|
+
}
|
|
33
|
+
interface JsonRpcError {
|
|
34
|
+
jsonrpc: "2.0";
|
|
35
|
+
id: RequestId | null;
|
|
36
|
+
error: {
|
|
37
|
+
code: number;
|
|
38
|
+
message: string;
|
|
39
|
+
data?: JsonValue;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
type JsonRpcResponse = JsonRpcSuccess | JsonRpcError;
|
|
43
|
+
type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
|
|
44
|
+
declare const ErrorCode: {
|
|
45
|
+
readonly parseError: -32700;
|
|
46
|
+
readonly invalidRequest: -32600;
|
|
47
|
+
readonly methodNotFound: -32601;
|
|
48
|
+
readonly invalidParams: -32602;
|
|
49
|
+
readonly internalError: -32603;
|
|
50
|
+
/** Caller is authenticated but lacks a capability covering the call. */
|
|
51
|
+
readonly permissionRequired: -32401;
|
|
52
|
+
/** The peer a request was routed to detached before it could respond. */
|
|
53
|
+
readonly peerDisconnected: -32402;
|
|
54
|
+
/** The request exceeded the hub's idle timeout with no stream activity. */
|
|
55
|
+
readonly requestTimeout: -32403;
|
|
56
|
+
/** The request was cancelled (by the caller, or by the hub on disconnect). */
|
|
57
|
+
readonly cancelled: -32800;
|
|
58
|
+
};
|
|
59
|
+
declare function isRequest(m: JsonRpcMessage): m is JsonRpcRequest;
|
|
60
|
+
declare function isNotification(m: JsonRpcMessage): m is JsonRpcNotification;
|
|
61
|
+
declare function isResponse(m: JsonRpcMessage): m is JsonRpcResponse;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/schema/linkRpcJsonSchema.d.ts
|
|
64
|
+
type LinkRpcJsonSchema = true | false | NullSchema | BooleanSchema | NumberSchema | IntegerSchema | StringSchema | ConstSchema | EnumSchema | ArraySchema | TupleSchema | ObjectSchema | UnionSchema | OneOfSchema | RefSchema;
|
|
65
|
+
interface SchemaBase {
|
|
66
|
+
[key: `x-${string}`]: unknown;
|
|
67
|
+
title?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Additional draft 2020-12 constraints that refine (never replace) this
|
|
71
|
+
* node. This extension is non-normative for LinkRPC identity; use
|
|
72
|
+
* {@link materializeJsonSchema} when exporting to a general validator.
|
|
73
|
+
*/
|
|
74
|
+
"x-json-schema"?: boolean | Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
interface NullSchema extends SchemaBase {
|
|
77
|
+
type: "null";
|
|
78
|
+
}
|
|
79
|
+
interface BooleanSchema extends SchemaBase {
|
|
80
|
+
type: "boolean";
|
|
81
|
+
}
|
|
82
|
+
interface NumberSchema extends SchemaBase {
|
|
83
|
+
type: "number";
|
|
84
|
+
/** Opaque well-known refinement tag, e.g. "float32", "percentage". */
|
|
85
|
+
format?: string;
|
|
86
|
+
}
|
|
87
|
+
interface IntegerSchema extends SchemaBase {
|
|
88
|
+
type: "integer";
|
|
89
|
+
/** Opaque well-known refinement tag, e.g. "int32", "uint8", "unix-time". */
|
|
90
|
+
format?: string;
|
|
91
|
+
}
|
|
92
|
+
interface StringSchema extends SchemaBase {
|
|
93
|
+
type: "string";
|
|
94
|
+
/** Opaque well-known refinement tag, e.g. "email", "uri", "uuid", "date-time". */
|
|
95
|
+
format?: string;
|
|
96
|
+
}
|
|
97
|
+
/** Single literal value. */
|
|
98
|
+
interface ConstSchema extends SchemaBase {
|
|
99
|
+
const: JsonValue;
|
|
100
|
+
}
|
|
101
|
+
/** Finite set of literal values. */
|
|
102
|
+
interface EnumSchema extends SchemaBase {
|
|
103
|
+
enum: JsonValue[];
|
|
104
|
+
}
|
|
105
|
+
/** Homogeneous array. */
|
|
106
|
+
interface ArraySchema extends SchemaBase {
|
|
107
|
+
type: "array";
|
|
108
|
+
items: LinkRpcJsonSchema;
|
|
109
|
+
}
|
|
110
|
+
/** Fixed-length head plus optional rest element type. */
|
|
111
|
+
interface TupleSchema extends SchemaBase {
|
|
112
|
+
type: "array";
|
|
113
|
+
prefixItems: LinkRpcJsonSchema[];
|
|
114
|
+
/** `false` => exact length; schema => typed rest; omitted => exact length. */
|
|
115
|
+
items?: LinkRpcJsonSchema | false;
|
|
116
|
+
}
|
|
117
|
+
interface ObjectSchema extends SchemaBase {
|
|
118
|
+
type: "object";
|
|
119
|
+
properties: Record<string, LinkRpcJsonSchema>;
|
|
120
|
+
/** Names of required properties. Must all be keys of `properties`. */
|
|
121
|
+
required?: string[];
|
|
122
|
+
/** `false` => closed; schema => value type for unknown keys. */
|
|
123
|
+
additionalProperties: LinkRpcJsonSchema | false;
|
|
124
|
+
}
|
|
125
|
+
/** Untagged union. Assignability distributes over branches. */
|
|
126
|
+
interface UnionSchema extends SchemaBase {
|
|
127
|
+
anyOf: LinkRpcJsonSchema[];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Tagged-or-disjoint union (`oneOf`). Structurally treated the same as
|
|
131
|
+
* `UnionSchema` for assignability; the distinction is preserved so the
|
|
132
|
+
* wire schema faithfully reflects whether the source was a
|
|
133
|
+
* discriminated union (e.g. Zod's `z.discriminatedUnion`) or a plain
|
|
134
|
+
* union.
|
|
135
|
+
*
|
|
136
|
+
* `discriminator`, when present, is a pure consumer hint: it names the
|
|
137
|
+
* property that branches dispatch on. Every branch SHOULD be an object
|
|
138
|
+
* with that property set to a distinct `const` value, but the subset
|
|
139
|
+
* does not enforce this — broken discriminators are tolerated.
|
|
140
|
+
*/
|
|
141
|
+
interface OneOfSchema extends SchemaBase {
|
|
142
|
+
oneOf: LinkRpcJsonSchema[];
|
|
143
|
+
discriminator?: DiscriminatorSchema;
|
|
144
|
+
}
|
|
145
|
+
interface DiscriminatorSchema {
|
|
146
|
+
propertyName: string;
|
|
147
|
+
}
|
|
148
|
+
/** Reference to a named entry in `SvcInterfaceSchema.components.schemas`. */
|
|
149
|
+
interface RefSchema extends SchemaBase {
|
|
150
|
+
/** JSON Pointer, restricted to "#/components/schemas/<name>". */
|
|
151
|
+
$ref: string;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/schema/linkRpcInterfaceSchema.d.ts
|
|
155
|
+
/**
|
|
156
|
+
* Minimal interface schema for linkrpc. A strict subset of OpenRPC 1.x:
|
|
157
|
+
* identity & addressing live in linkrpc, so this format only describes the
|
|
158
|
+
* contract of a single interface (methods + reusable JSON Schemas).
|
|
159
|
+
*/
|
|
160
|
+
interface LinkRpcInterfaceSchema {
|
|
161
|
+
[key: `x-${string}`]: unknown;
|
|
162
|
+
/** Stable interface id, e.g. "de.hediet.notification-target". */
|
|
163
|
+
id: string;
|
|
164
|
+
/**
|
|
165
|
+
* Content hash of the normalized schema. Pairs with `id` to form `id@hash`.
|
|
166
|
+
* Normalization: serialize this object with `hash` omitted, object keys
|
|
167
|
+
* sorted recursively, and no insignificant whitespace; hash the bytes.
|
|
168
|
+
*/
|
|
169
|
+
hash: string;
|
|
170
|
+
/**
|
|
171
|
+
* Normative human description (GitHub-flavored markdown). Part of the
|
|
172
|
+
* interface hash — changing `description` is a contract change. Use it
|
|
173
|
+
* for the canonical purpose / semantics of the interface.
|
|
174
|
+
*/
|
|
175
|
+
description?: string;
|
|
176
|
+
/**
|
|
177
|
+
* Non-normative implementation notes (markdown). Stripped from the hash,
|
|
178
|
+
* so editing `comment` never changes the interface identity. Use it for
|
|
179
|
+
* rationale, changelog notes, examples, etc.
|
|
180
|
+
*/
|
|
181
|
+
comment?: string;
|
|
182
|
+
/** Methods keyed by local member name. */
|
|
183
|
+
methods: Record<string, MethodSchema>;
|
|
184
|
+
/** Reusable schema definitions, referenced via `#/components/schemas/<name>`. */
|
|
185
|
+
components?: {
|
|
186
|
+
schemas?: Record<string, LinkRpcJsonSchema>;
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
interface MethodSchema {
|
|
190
|
+
[key: `x-${string}`]: unknown;
|
|
191
|
+
/** Schema for the user params object. */
|
|
192
|
+
params: LinkRpcJsonSchema;
|
|
193
|
+
/** Result schema. Omit to declare a notification-only method. */
|
|
194
|
+
result?: LinkRpcJsonSchema;
|
|
195
|
+
/**
|
|
196
|
+
* Schema for client-emitted stream messages (`$stream::send` from
|
|
197
|
+
* caller to callee) on an in-flight call. Absent means the client
|
|
198
|
+
* may not stream on this method.
|
|
199
|
+
*/
|
|
200
|
+
clientStream?: LinkRpcJsonSchema;
|
|
201
|
+
/**
|
|
202
|
+
* Schema for server-emitted stream messages (`$stream::send` from
|
|
203
|
+
* callee to caller) on an in-flight call. Absent means the server
|
|
204
|
+
* may not stream on this method.
|
|
205
|
+
*/
|
|
206
|
+
serverStream?: LinkRpcJsonSchema;
|
|
207
|
+
/** Application-level errors. Codes MUST be unique. */
|
|
208
|
+
errors?: ErrorSchema[];
|
|
209
|
+
summary?: string;
|
|
210
|
+
/**
|
|
211
|
+
* Normative description of this method's contract (markdown). Part of
|
|
212
|
+
* the interface hash — changing it is a contract change.
|
|
213
|
+
*/
|
|
214
|
+
description?: string;
|
|
215
|
+
/** Non-normative implementation notes. Stripped from the hash. */
|
|
216
|
+
comment?: string;
|
|
217
|
+
deprecated?: boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Behavioral claims about this method. All flags default to `false`;
|
|
220
|
+
* setting one is always a positive refinement of the contract.
|
|
221
|
+
* Part of the interface hash — these are normative claims callers
|
|
222
|
+
* may rely on, so changing them is a contract change.
|
|
223
|
+
*/
|
|
224
|
+
annotations?: MemberAnnotations;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Behavioral claims about a method. Every flag is a positive assertion
|
|
228
|
+
* (default `false` ≡ "no claim"); setting one strengthens the contract
|
|
229
|
+
* the caller may rely on.
|
|
230
|
+
*
|
|
231
|
+
* These are normative — they are included in the interface hash.
|
|
232
|
+
*/
|
|
233
|
+
interface MemberAnnotations {
|
|
234
|
+
/**
|
|
235
|
+
* The method does not modify any observable state on the callee
|
|
236
|
+
* (or anywhere reachable from it). Pure query.
|
|
237
|
+
*
|
|
238
|
+
* Implies `idempotent` and `reversible` (a no-op has nothing to
|
|
239
|
+
* undo and repeats trivially).
|
|
240
|
+
*/
|
|
241
|
+
readOnly?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Calling N times with the same params has the same observable effect
|
|
244
|
+
* as calling once. Safe to retry on transport failure.
|
|
245
|
+
*/
|
|
246
|
+
idempotent?: boolean;
|
|
247
|
+
/**
|
|
248
|
+
* Effects of this method are reversible — the caller (or operator)
|
|
249
|
+
* can undo them with a follow-up call. Implies the method is not
|
|
250
|
+
* `dangerous`.
|
|
251
|
+
*/
|
|
252
|
+
reversible?: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Calling this method is expensive (slow, costly, or rate-limited).
|
|
255
|
+
* Callers should avoid unnecessary invocations and may want to
|
|
256
|
+
* confirm / batch.
|
|
257
|
+
*/
|
|
258
|
+
expensive?: boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Method has irreversible or destructive effects (data loss, money
|
|
261
|
+
* spent, message sent, etc.). UIs should require confirmation.
|
|
262
|
+
*/
|
|
263
|
+
dangerous?: boolean;
|
|
264
|
+
}
|
|
265
|
+
interface ErrorSchema {
|
|
266
|
+
/** JSON-RPC error code. -32768..-32000 are reserved. */
|
|
267
|
+
code: number;
|
|
268
|
+
message: string;
|
|
269
|
+
/** Optional schema describing the shape of `error.data`. */
|
|
270
|
+
data?: LinkRpcJsonSchema;
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/schema/memberTypes.d.ts
|
|
274
|
+
/**
|
|
275
|
+
* The schema type linkrpc accepts everywhere: the zod *core* base shared by
|
|
276
|
+
* both classic `zod` and `zod/mini`. Typing against it (instead of classic
|
|
277
|
+
* `z.ZodType`) lets callers author interfaces with either flavour — classic
|
|
278
|
+
* for ergonomics, mini for minimal bundle size — while linkrpc's own internal
|
|
279
|
+
* interfaces use mini. `O` is the validated output type.
|
|
280
|
+
*/
|
|
281
|
+
type Schema<O = unknown> = $ZodType<O>;
|
|
282
|
+
/**
|
|
283
|
+
* Marker base for a typed method member. Carries enough metadata to:
|
|
284
|
+
* - validate / parse params at runtime (via the zod schemas),
|
|
285
|
+
* - emit a JSON schema for reflection,
|
|
286
|
+
* - propagate TS types up to the interface definition.
|
|
287
|
+
*/
|
|
288
|
+
type MemberType = RequestType<any, any, any, any, any> | NotificationType<any>;
|
|
289
|
+
/**
|
|
290
|
+
* Optional documentation for a method member.
|
|
291
|
+
*
|
|
292
|
+
* - `description` is the **normative** contract of the method (included in the
|
|
293
|
+
* interface hash — changing it changes `schemaHash`).
|
|
294
|
+
* - `comment` is non-normative implementation notes (stripped from the hash).
|
|
295
|
+
* - `annotations` are normative behavioral claims (e.g. `readOnly`,
|
|
296
|
+
* `idempotent`); included in the hash.
|
|
297
|
+
*/
|
|
298
|
+
interface MemberDocs {
|
|
299
|
+
description?: string;
|
|
300
|
+
comment?: string;
|
|
301
|
+
annotations?: MemberAnnotations;
|
|
302
|
+
}
|
|
303
|
+
declare class RequestType<TParams = unknown, TResult = void, TError = void, TClientStream = never, TServerStream = never> {
|
|
304
|
+
readonly paramsSchema: Schema<TParams>;
|
|
305
|
+
readonly resultSchema: Schema<TResult>;
|
|
306
|
+
readonly errorSchema: Schema<TError>;
|
|
307
|
+
readonly docs: MemberDocs;
|
|
308
|
+
/**
|
|
309
|
+
* Schema for stream messages the **client** may emit on an
|
|
310
|
+
* in-flight call (e.g. cancellation, input). `undefined` means
|
|
311
|
+
* the client may not stream on this method.
|
|
312
|
+
*/
|
|
313
|
+
readonly clientStreamSchema?: Schema<TClientStream> | undefined;
|
|
314
|
+
/**
|
|
315
|
+
* Schema for stream messages the **server** may emit while
|
|
316
|
+
* handling an in-flight call (e.g. progress, partial results).
|
|
317
|
+
* `undefined` means the server may not stream on this method.
|
|
318
|
+
*/
|
|
319
|
+
readonly serverStreamSchema?: Schema<TServerStream> | undefined;
|
|
320
|
+
readonly kind: "request";
|
|
321
|
+
constructor(paramsSchema: Schema<TParams>, resultSchema: Schema<TResult>, errorSchema: Schema<TError>, docs?: MemberDocs,
|
|
322
|
+
/**
|
|
323
|
+
* Schema for stream messages the **client** may emit on an
|
|
324
|
+
* in-flight call (e.g. cancellation, input). `undefined` means
|
|
325
|
+
* the client may not stream on this method.
|
|
326
|
+
*/
|
|
327
|
+
clientStreamSchema?: Schema<TClientStream> | undefined,
|
|
328
|
+
/**
|
|
329
|
+
* Schema for stream messages the **server** may emit while
|
|
330
|
+
* handling an in-flight call (e.g. progress, partial results).
|
|
331
|
+
* `undefined` means the server may not stream on this method.
|
|
332
|
+
*/
|
|
333
|
+
serverStreamSchema?: Schema<TServerStream> | undefined);
|
|
334
|
+
/** Phantom field — typed-only, do not access at runtime. */
|
|
335
|
+
readonly _params: TParams;
|
|
336
|
+
readonly _result: TResult;
|
|
337
|
+
readonly _error: TError;
|
|
338
|
+
readonly _clientStream: TClientStream;
|
|
339
|
+
readonly _serverStream: TServerStream;
|
|
340
|
+
/**
|
|
341
|
+
* Return a copy of this request type with stream payload schemas
|
|
342
|
+
* attached. Pass `undefined` for either direction to leave it
|
|
343
|
+
* closed.
|
|
344
|
+
*/
|
|
345
|
+
withStream<TClient = TClientStream, TServer = TServerStream>(opts: {
|
|
346
|
+
client?: Schema<TClient>;
|
|
347
|
+
server?: Schema<TServer>;
|
|
348
|
+
}): RequestType<TParams, TResult, TError, TClient, TServer>;
|
|
349
|
+
}
|
|
350
|
+
declare class NotificationType<TParams = unknown> {
|
|
351
|
+
readonly paramsSchema: Schema<TParams>;
|
|
352
|
+
readonly docs: MemberDocs;
|
|
353
|
+
readonly kind: "notification";
|
|
354
|
+
constructor(paramsSchema: Schema<TParams>, docs?: MemberDocs);
|
|
355
|
+
readonly _params: TParams;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Define a request method.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* bar: requestType(z.object({ to: z.string() }), z.string(), {
|
|
362
|
+
* description: "MUST resolve before the next call from the same caller.",
|
|
363
|
+
* })
|
|
364
|
+
*/
|
|
365
|
+
declare function requestType<TParams, TResult = void, TError = void>(params: Schema<TParams>, result?: Schema<TResult>, docsOrError?: MemberDocs | Schema<TError>, maybeDocs?: MemberDocs): RequestType<TParams, TResult, TError>;
|
|
366
|
+
/**
|
|
367
|
+
* Define a notification method.
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* foo: notificationType(z.object({ message: z.string() }))
|
|
371
|
+
*/
|
|
372
|
+
declare function notificationType<TParams>(params: Schema<TParams>, docs?: MemberDocs): NotificationType<TParams>;
|
|
373
|
+
interface ZodToSvcJsonSchemaOptions {
|
|
374
|
+
readonly methodName: string;
|
|
375
|
+
readonly schemaPosition: string;
|
|
376
|
+
readonly components: Record<string, LinkRpcJsonSchema>;
|
|
377
|
+
}
|
|
378
|
+
/** Convert a zod schema to our restricted SvcJsonSchema subset. */
|
|
379
|
+
declare function zodToSvcJsonSchema(schema: Schema, options?: ZodToSvcJsonSchemaOptions): LinkRpcJsonSchema;
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/connection/interfaceDefinition.d.ts
|
|
382
|
+
/**
|
|
383
|
+
* Per-call stream API handed to a request handler as its third argument.
|
|
384
|
+
* Allows the handler to consume {@link STREAM_METHOD} notifications
|
|
385
|
+
* emitted by the caller (`onMessage`) and emit stream notifications to
|
|
386
|
+
* the caller (`send`) while the request is in flight.
|
|
387
|
+
*
|
|
388
|
+
* Members are typed by the originating method's
|
|
389
|
+
* {@link RequestType.clientStreamSchema} / `serverStreamSchema`.
|
|
390
|
+
* Streams whose schema is `undefined` make the corresponding member
|
|
391
|
+
* effectively unusable: `send` is typed `(payload: never) => void` and
|
|
392
|
+
* the registered `onMessage` listener can never fire because the
|
|
393
|
+
* receiver-side validator rejects the wire payload.
|
|
394
|
+
*/
|
|
395
|
+
interface StreamApi<TClient = unknown, TServer = unknown> {
|
|
396
|
+
/** Emit a server→client stream message. */
|
|
397
|
+
send(payload: TServer): Promise<void>;
|
|
398
|
+
/**
|
|
399
|
+
* Register a listener for client→server stream messages on this
|
|
400
|
+
* request. Calling twice replaces the previous listener.
|
|
401
|
+
*/
|
|
402
|
+
onMessage(listener: (payload: TClient) => void): void;
|
|
403
|
+
/**
|
|
404
|
+
* Liveness probe toward the caller: resolves once the caller
|
|
405
|
+
* acknowledges with a matching pong, rejects if the call settles
|
|
406
|
+
* first. Independent of the channel's automatic keepalive ping.
|
|
407
|
+
*/
|
|
408
|
+
ping(): Promise<void>;
|
|
409
|
+
/**
|
|
410
|
+
* Aborts when the caller cancels this in-flight request, or when the
|
|
411
|
+
* hub cancels it on the caller's behalf (caller disconnect / idle
|
|
412
|
+
* timeout). Observe it to stop work early and settle the request
|
|
413
|
+
* (e.g. `signal.throwIfAborted()`), which surfaces as a `cancelled`
|
|
414
|
+
* error to the caller.
|
|
415
|
+
*/
|
|
416
|
+
readonly signal: AbortSignal;
|
|
417
|
+
}
|
|
418
|
+
interface InterfaceInfo {
|
|
419
|
+
id: string;
|
|
420
|
+
/**
|
|
421
|
+
* Normative description of the interface (markdown). Part of the
|
|
422
|
+
* interface hash — changing it is a contract change.
|
|
423
|
+
*/
|
|
424
|
+
description?: string;
|
|
425
|
+
/** Non-normative implementation notes. Stripped from the hash. */
|
|
426
|
+
comment?: string;
|
|
427
|
+
/**
|
|
428
|
+
* Optional expected content hash (see `computeInterfaceHash`). When
|
|
429
|
+
* set, {@link InterfaceDefinition}'s constructor verifies that the
|
|
430
|
+
* computed {@link InterfaceDefinition.schemaHash} matches and throws
|
|
431
|
+
* otherwise — a guard against silent contract drift when the wire
|
|
432
|
+
* shape changes but a pinned hash was not updated.
|
|
433
|
+
*/
|
|
434
|
+
hash?: string;
|
|
435
|
+
}
|
|
436
|
+
type MemberMap = Record<string, MemberType>;
|
|
437
|
+
/** Stable wire identity of one member in an interface definition. */
|
|
438
|
+
interface InterfaceMemberRef<TName extends string = string> {
|
|
439
|
+
readonly interfaceId: string;
|
|
440
|
+
readonly interfaceHash: string;
|
|
441
|
+
readonly member: TName;
|
|
442
|
+
}
|
|
443
|
+
type InterfaceMemberRefMap<TMembers extends MemberMap> = { readonly [K in keyof TMembers & string]: InterfaceMemberRef<K>; };
|
|
444
|
+
/**
|
|
445
|
+
* Per-call options accepted by a streaming-enabled client method. Currently
|
|
446
|
+
* only `onMessage` for consuming server→client stream notifications; the
|
|
447
|
+
* caller addresses client→server stream messages via the returned
|
|
448
|
+
* {@link StreamingCall}.
|
|
449
|
+
*/
|
|
450
|
+
interface StreamCallOptions<TServer> {
|
|
451
|
+
/** Listener for server→client stream messages on this in-flight call. */
|
|
452
|
+
onMessage?: (payload: TServer) => void;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Return shape of a streaming-enabled client method. Behaves as a
|
|
456
|
+
* `Promise<TResult>` for the final response, and exposes `send` for
|
|
457
|
+
* emitting client→server stream messages on the in-flight call. The
|
|
458
|
+
* wire request id is also exposed (resolved once the request has
|
|
459
|
+
* been allocated).
|
|
460
|
+
*
|
|
461
|
+
* For methods whose interface schema declares no client stream
|
|
462
|
+
* (`TClient = never`), `send` is typed `(payload: never) => Promise<void>`
|
|
463
|
+
* and is effectively uncallable — matching the runtime behavior, where
|
|
464
|
+
* the receiver would drop client-emitted stream messages anyway.
|
|
465
|
+
*/
|
|
466
|
+
interface StreamingCall<TResult, TClient> extends Promise<TResult> {
|
|
467
|
+
/** Resolves to the wire request id once the call has been sent. */
|
|
468
|
+
readonly requestId: Promise<RequestId>;
|
|
469
|
+
/** Emit a client→server stream message on this in-flight call. */
|
|
470
|
+
send(payload: TClient): Promise<void>;
|
|
471
|
+
/**
|
|
472
|
+
* Ask the callee to abort this in-flight call. Advisory: the call
|
|
473
|
+
* settles via its normal response (typically a `cancelled` error).
|
|
474
|
+
* `reason` is an open-set diagnostic string (see `StreamControlReason`).
|
|
475
|
+
*/
|
|
476
|
+
cancel(reason?: string): Promise<void>;
|
|
477
|
+
/**
|
|
478
|
+
* Stop tracking this call locally. This does not notify the callee; call
|
|
479
|
+
* {@link cancel} first when remote work should also be cancelled.
|
|
480
|
+
*/
|
|
481
|
+
dispose?(reason?: string): void;
|
|
482
|
+
/**
|
|
483
|
+
* Liveness probe toward the callee: resolves once the callee
|
|
484
|
+
* acknowledges with a matching pong, rejects if the call settles
|
|
485
|
+
* first. Independent of the channel's automatic keepalive ping.
|
|
486
|
+
*/
|
|
487
|
+
ping(): Promise<void>;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* `true` iff at least one stream direction is declared (either client or
|
|
491
|
+
* server stream schema present). Used by {@link InterfaceClient} to pick
|
|
492
|
+
* between the plain `Promise<R>` shape and the {@link StreamingCall}
|
|
493
|
+
* shape per method.
|
|
494
|
+
*/
|
|
495
|
+
type _HasStream<TClient, TServer> = [TClient] extends [never] ? ([TServer] extends [never] ? false : true) : true;
|
|
496
|
+
/**
|
|
497
|
+
* Compile-time TypeScript shape of an interface — useful for typed
|
|
498
|
+
* client/server implementations on top of the runtime definition.
|
|
499
|
+
*
|
|
500
|
+
* Methods whose `RequestType` declares stream payloads via
|
|
501
|
+
* `.withStream({ client, server })` return a {@link StreamingCall}
|
|
502
|
+
* augmented with `send`; all others return a plain `Promise<TResult>`.
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* type Client = InterfaceClient<typeof myInterface>;
|
|
506
|
+
* // => { bar(p: {...}): Promise<string>; foo(p: {...}): void; }
|
|
507
|
+
*/
|
|
508
|
+
type InterfaceClientMember<TMember> = TMember extends RequestType<infer P, infer R, any, infer TC, infer TS> ? (_HasStream<TC, TS> extends true ? (params: P, opts?: StreamCallOptions<TS>) => StreamingCall<R, TC> : (params: P) => Promise<R>) : TMember extends NotificationType<infer P> ? (params: P) => void : never;
|
|
509
|
+
type InterfaceClient<TDef extends InterfaceDefinition<any>> = { [K in keyof TDef['members']]: InterfaceClientMember<TDef['members'][K]>; };
|
|
510
|
+
/**
|
|
511
|
+
* Compile-time shape of a server implementation for an interface — used by
|
|
512
|
+
* `LinkRpcConnection.register`. Request handlers may return synchronously or
|
|
513
|
+
* via a promise; notification handlers return void.
|
|
514
|
+
*
|
|
515
|
+
* `TCtx` is the call-context type carried by the hosting connection — see
|
|
516
|
+
* `LinkRpcConnection<TCtx>`. For the default connection (`TCtx = undefined`)
|
|
517
|
+
* handlers may take a single params arg; for ctx-aware connections (e.g.
|
|
518
|
+
* the hub's self connection) they may take a second `ctx` arg of the
|
|
519
|
+
* concrete type. A 1-arg handler remains assignable where a 2-arg handler
|
|
520
|
+
* is expected, so existing handlers compile unchanged.
|
|
521
|
+
*/
|
|
522
|
+
type InterfaceHandler<TMember, TCtx> = TMember extends RequestType<infer P, infer R, any, infer TC, infer TS> ? (params: P, ctx: TCtx, stream: StreamApi<TC, TS>) => R | Promise<R> : TMember extends NotificationType<infer P> ? (params: P, ctx: TCtx) => void | Promise<void> : never;
|
|
523
|
+
type InterfaceHandlers<TDef extends InterfaceDefinition<any>, TCtx = undefined> = { [K in keyof TDef['members']]: InterfaceHandler<TDef['members'][K], TCtx>; };
|
|
524
|
+
/**
|
|
525
|
+
* Options accepted by {@link InterfaceDefinition}'s constructor.
|
|
526
|
+
*
|
|
527
|
+
* `frozenSchema` lets callers supply an externally-authored
|
|
528
|
+
* `LinkRpcInterfaceSchema` (e.g. produced by codegen or, in the faker, by an
|
|
529
|
+
* LLM at runtime) verbatim. When set, `toSchema()` returns that document
|
|
530
|
+
* (with `hash` overlaid) and `schemaHash` is computed from it — the
|
|
531
|
+
* `members` map is used only for runtime dispatch (param/result
|
|
532
|
+
* validation, stream routing) and is no longer the source of truth for
|
|
533
|
+
* the wire shape.
|
|
534
|
+
*/
|
|
535
|
+
interface InterfaceDefinitionOpts {
|
|
536
|
+
frozenSchema?: LinkRpcInterfaceSchema;
|
|
537
|
+
}
|
|
538
|
+
declare class InterfaceDefinition<TMembers extends MemberMap> {
|
|
539
|
+
readonly info: InterfaceInfo;
|
|
540
|
+
readonly members: TMembers;
|
|
541
|
+
/** Typed wire references for capability and access-request construction. */
|
|
542
|
+
readonly ref: InterfaceMemberRefMap<TMembers>;
|
|
543
|
+
constructor(info: InterfaceInfo, members: TMembers, opts?: InterfaceDefinitionOpts);
|
|
544
|
+
/** Content hash of this interface (see `computeInterfaceHash`). */
|
|
545
|
+
get schemaHash(): string;
|
|
546
|
+
/** Lower the definition to a wire-format `LinkRpcInterfaceSchema`, hash filled in. */
|
|
547
|
+
toSchema(): LinkRpcInterfaceSchema;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Convenient builder for an interface definition. Tracks TypeScript types
|
|
551
|
+
* through `requestType` / `notificationType` so client and server code can
|
|
552
|
+
* derive their shapes from the definition.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* const myInterface = defineInterface(
|
|
556
|
+
* { id: "de.hediet.notification-target" },
|
|
557
|
+
* {
|
|
558
|
+
* send: requestType(z.object({ to: z.string() }), z.string()),
|
|
559
|
+
* notify: notificationType(z.object({ message: z.string() })),
|
|
560
|
+
* },
|
|
561
|
+
* );
|
|
562
|
+
*/
|
|
563
|
+
declare function defineInterface<TMembers extends MemberMap>(info: InterfaceInfo, members: TMembers): InterfaceDefinition<TMembers>;
|
|
564
|
+
/**
|
|
565
|
+
* Build a runtime {@link InterfaceDefinition} from a previously-published
|
|
566
|
+
* {@link LinkRpcInterfaceSchema} — typically one received over the wire (e.g.
|
|
567
|
+
* from `hubrpc.schemas::get`) or generated at runtime by tooling that does
|
|
568
|
+
* not have the original zod sources at hand (codegen, faker / mock
|
|
569
|
+
* services, dynamic gateways).
|
|
570
|
+
*
|
|
571
|
+
* The original schema is kept verbatim: `toSchema()` returns it (with
|
|
572
|
+
* `hash` overlaid) and `schemaHash` is computed from it, so reflection
|
|
573
|
+
* consumers see the real wire contract. Member-level params / results /
|
|
574
|
+
* streams use `z.any()` because no zod source is available — call-site
|
|
575
|
+
* validation is therefore a no-op and the caller is responsible for
|
|
576
|
+
* shape-checking inputs and outputs.
|
|
577
|
+
*
|
|
578
|
+
* Methods with no `result` descriptor become notifications; methods with
|
|
579
|
+
* `clientStream` / `serverStream` get pass-through stream payload
|
|
580
|
+
* schemas attached.
|
|
581
|
+
*/
|
|
582
|
+
declare function interfaceFromSchema(schema: LinkRpcInterfaceSchema): InterfaceDefinition<MemberMap>;
|
|
583
|
+
//#endregion
|
|
584
|
+
//#region src/schema/hash.d.ts
|
|
585
|
+
/**
|
|
586
|
+
* Compute the interface hash: SHA-256 of the canonicalized schema, truncated
|
|
587
|
+
* to 16 hex chars (64 bits of collision budget per id).
|
|
588
|
+
*
|
|
589
|
+
* Canonicalization strips the schema down to its **normative wire-contract
|
|
590
|
+
* projection** before hashing, so a single interface document can carry richer
|
|
591
|
+
* non-normative material (codegen hints, safety expressions, notes) without
|
|
592
|
+
* changing identity:
|
|
593
|
+
*
|
|
594
|
+
* 1. Normalize every JSON-Schema position (`params`, `result`, streams, and
|
|
595
|
+
* `components.schemas`) onto the decidable linkrpc subset.
|
|
596
|
+
* 2. Strip every `comment` field (non-normative — must not affect identity).
|
|
597
|
+
* `description` is NORMATIVE and kept in the hash.
|
|
598
|
+
* 3. Strip every **specification-extension** field — any object key whose name
|
|
599
|
+
* begins with `x-` — at every level of the document (see
|
|
600
|
+
* {@link EXTENSION_PREFIX}). This is the minimal, explicit "one document,
|
|
601
|
+
* two views" mechanism: the stored document keeps the rich `x-…`
|
|
602
|
+
* expressions; identity hashes only the simple contract. It mirrors
|
|
603
|
+
* OpenRPC/OpenAPI specification extensions (this schema format is a subset
|
|
604
|
+
* of OpenRPC 1.x). Editing an `x-…` value never changes the hash; changing
|
|
605
|
+
* a wire field (`params`, `result`, member names, `type`, `required`,
|
|
606
|
+
* `description`, `annotations`, …) does.
|
|
607
|
+
* 4. Omit the top-level `hash` field itself.
|
|
608
|
+
* 5. RFC 8785 JCS encode (recursive key sort, no whitespace) via {@link jcsCanonicalize}.
|
|
609
|
+
*
|
|
610
|
+
* Because no interface schema uses `x-…` keys today, this preserves every
|
|
611
|
+
* existing hash: stripping a set of keys that are always absent is a no-op.
|
|
612
|
+
*
|
|
613
|
+
* > Reservation. `x-…` is reserved for non-normative extensions at every level.
|
|
614
|
+
* > Member names cannot collide (they are alphanumeric per chapter 01 §2), and
|
|
615
|
+
* > object property names in the JSON Schema subset MUST NOT begin with `x-`.
|
|
616
|
+
*/
|
|
617
|
+
declare function computeInterfaceHash(schema: LinkRpcInterfaceSchema): string;
|
|
618
|
+
/**
|
|
619
|
+
* Prefix marking a non-normative **specification-extension** key. Any object
|
|
620
|
+
* key beginning with this prefix is stripped before hashing (at every level),
|
|
621
|
+
* exactly like `comment`. Reserved for rich, identity-neutral material such as
|
|
622
|
+
* codegen directives, richer validation/safety expressions, or tooling hints.
|
|
623
|
+
*/
|
|
624
|
+
declare const EXTENSION_PREFIX = "x-";
|
|
625
|
+
//#endregion
|
|
626
|
+
//#region src/schema/normalize.d.ts
|
|
627
|
+
/**
|
|
628
|
+
* Normalize raw JSON Schema output (e.g. from `z.toJSONSchema`) into the
|
|
629
|
+
* SvcJsonSchema subset:
|
|
630
|
+
*
|
|
631
|
+
* - drop annotation keys (`examples`, `default`, `$comment`, `readOnly`, ...)
|
|
632
|
+
* - drop out-of-subset refinements (`pattern`, `minimum`, `multipleOf`,
|
|
633
|
+
* `allOf`, `oneOf`, `if/then/else`, `patternProperties`, ...)
|
|
634
|
+
* - empty schema `{}` ⇒ `true` (top)
|
|
635
|
+
* - `{"not":{}}` ⇒ `false` (bottom); any other `not` is stripped
|
|
636
|
+
* - `type:"object"` without `additionalProperties` ⇒ closed (`false`),
|
|
637
|
+
* matching linkrpc's stricter contract
|
|
638
|
+
*
|
|
639
|
+
* The output is canonical: structurally equal inputs produce structurally
|
|
640
|
+
* equal outputs, which is what `computeInterfaceHash` relies on.
|
|
641
|
+
*/
|
|
642
|
+
declare function normalizeJsonSchema(raw: unknown): LinkRpcJsonSchema;
|
|
643
|
+
//#endregion
|
|
644
|
+
//#region src/schema/assignability.d.ts
|
|
645
|
+
/**
|
|
646
|
+
* Structural subtype check: returns `true` iff every JSON value matching
|
|
647
|
+
* `sub` also matches `sup`, over the SvcJsonSchema subset.
|
|
648
|
+
*
|
|
649
|
+
* Cycles introduced by `$ref` are broken by assuming the recursive case
|
|
650
|
+
* holds (standard coinductive subtyping). This is sound for productive
|
|
651
|
+
* (non-degenerate) schemas; pathological inputs are out of scope.
|
|
652
|
+
*/
|
|
653
|
+
declare function isAssignable(sub: LinkRpcJsonSchema, sup: LinkRpcJsonSchema, components?: Components): boolean;
|
|
654
|
+
interface Components {
|
|
655
|
+
schemas?: Record<string, LinkRpcJsonSchema>;
|
|
656
|
+
}
|
|
657
|
+
//#endregion
|
|
658
|
+
//#region src/schema/assertSchemaReferences.d.ts
|
|
659
|
+
declare function componentSchemaRef(name: string): string;
|
|
660
|
+
declare function componentSchemaName(ref: string): string;
|
|
661
|
+
/**
|
|
662
|
+
* Check the finite schema graph without interpreting JSON literal payloads.
|
|
663
|
+
* A reference/union-only cycle is invalid; descending into an object member
|
|
664
|
+
* or an array item guards a cycle, but does not imply the schema is nonempty.
|
|
665
|
+
*/
|
|
666
|
+
declare function assertSchemaReferences(roots: Iterable<LinkRpcJsonSchema>, components?: Record<string, LinkRpcJsonSchema>): void;
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/schema/materializeJsonSchema.d.ts
|
|
669
|
+
interface MaterializedJsonSchema {
|
|
670
|
+
$schema: "https://json-schema.org/draft/2020-12/schema";
|
|
671
|
+
$defs?: Record<string, unknown>;
|
|
672
|
+
[key: string]: unknown;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Export a LinkRPC schema and its components as standalone JSON Schema
|
|
676
|
+
* draft 2020-12. Node-local `x-json-schema` constraints are conjoined via
|
|
677
|
+
* `allOf`, so a refinement cannot accidentally override the wire contract.
|
|
678
|
+
*
|
|
679
|
+
* References are rewritten rather than dereferenced. Consequently recursive
|
|
680
|
+
* and mutually-recursive component graphs are materialized in finite time.
|
|
681
|
+
*/
|
|
682
|
+
declare function materializeJsonSchema(root: LinkRpcJsonSchema, components?: Record<string, LinkRpcJsonSchema>): MaterializedJsonSchema;
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/schema/schemaToZod.d.ts
|
|
685
|
+
interface SchemaToZodContext {
|
|
686
|
+
readonly toZod: (schema: LinkRpcJsonSchema) => z.ZodType<unknown>;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Create a recursive LinkRPC-schema validator materializer. Component
|
|
690
|
+
* references are lazy, so self and mutual recursion remain guarded.
|
|
691
|
+
*/
|
|
692
|
+
declare function createSchemaToZod(components?: Record<string, LinkRpcJsonSchema>): SchemaToZodContext;
|
|
693
|
+
declare function schemaToZod(schema: LinkRpcJsonSchema, components?: Record<string, LinkRpcJsonSchema>): z.ZodType<unknown>;
|
|
694
|
+
//#endregion
|
|
695
|
+
//#region src/schema/codegen/generateTsInterface.d.ts
|
|
696
|
+
interface GenerateInterfaceOptions {
|
|
697
|
+
/**
|
|
698
|
+
* Module specifier from which `defineInterface`, `requestType`,
|
|
699
|
+
* `notificationType` are imported. Defaults to `@hediet/linkrpc`.
|
|
700
|
+
*/
|
|
701
|
+
linkRpcImport?: string;
|
|
702
|
+
/**
|
|
703
|
+
* Identifier for the exported `InterfaceDefinition` const. Defaults to
|
|
704
|
+
* a sanitized form of the interface id with an `Interface` suffix
|
|
705
|
+
* (e.g. `hubrpc.directory` → `linkRpcDirectoryInterface`).
|
|
706
|
+
*/
|
|
707
|
+
exportName?: string;
|
|
708
|
+
/**
|
|
709
|
+
* Preserve the supplied wire schema verbatim instead of reconstructing it
|
|
710
|
+
* from the generated Zod schemas. This permits type-safe generation for
|
|
711
|
+
* wire shapes that Zod represents differently, such as untagged `oneOf`.
|
|
712
|
+
*/
|
|
713
|
+
preserveWireSchema?: boolean;
|
|
714
|
+
/**
|
|
715
|
+
* Also export a metadata-free target for `connection.get(target)`.
|
|
716
|
+
* The interface definition remains a separate export.
|
|
717
|
+
*/
|
|
718
|
+
bareTarget?: {
|
|
719
|
+
exportName: string;
|
|
720
|
+
prefix: string;
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Render a {@link LinkRpcInterfaceSchema} as a stand-alone TypeScript source
|
|
725
|
+
* file that, when evaluated, reproduces the same canonical schema (and
|
|
726
|
+
* therefore the same `schemaHash`).
|
|
727
|
+
*
|
|
728
|
+
* Components in `components.schemas` are emitted as named `const`
|
|
729
|
+
* declarations referenced from the bodies via `$ref`. Only recursive
|
|
730
|
+
* components receive explicit payload types; other types remain Zod-inferred.
|
|
731
|
+
*/
|
|
732
|
+
declare function generateTsInterface(schema: LinkRpcInterfaceSchema, options?: GenerateInterfaceOptions): string;
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region src/inspection/inspection.interfaces.d.ts
|
|
735
|
+
declare const zTopologyPort: import("zod/mini").ZodMiniObject<{
|
|
736
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
737
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
738
|
+
}, import("zod/v4/core").$strip>;
|
|
739
|
+
declare const zParticipantDescriptor: import("zod/mini").ZodMiniObject<{
|
|
740
|
+
type: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
741
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
742
|
+
processId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
743
|
+
processType: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
744
|
+
nodeStatusServiceId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
745
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
746
|
+
}, import("zod/v4/core").$strip>;
|
|
747
|
+
declare const zParticipantDescriptorSource: import("zod/mini").ZodMiniObject<{
|
|
748
|
+
source: import("zod/mini").ZodMiniEnum<{
|
|
749
|
+
self: "self";
|
|
750
|
+
attacher: "attacher";
|
|
751
|
+
}>;
|
|
752
|
+
descriptor: import("zod/mini").ZodMiniObject<{
|
|
753
|
+
type: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
754
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
755
|
+
processId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
756
|
+
processType: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
757
|
+
nodeStatusServiceId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
758
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
759
|
+
}, import("zod/v4/core").$strip>;
|
|
760
|
+
}, import("zod/v4/core").$strip>;
|
|
761
|
+
declare const zTopologyNode: import("zod/mini").ZodMiniObject<{
|
|
762
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
763
|
+
kind: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniEnum<{
|
|
764
|
+
endpoint: "endpoint";
|
|
765
|
+
hub: "hub";
|
|
766
|
+
}>>;
|
|
767
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
768
|
+
descriptors: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
769
|
+
source: import("zod/mini").ZodMiniEnum<{
|
|
770
|
+
self: "self";
|
|
771
|
+
attacher: "attacher";
|
|
772
|
+
}>;
|
|
773
|
+
descriptor: import("zod/mini").ZodMiniObject<{
|
|
774
|
+
type: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
775
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
776
|
+
processId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
777
|
+
processType: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
778
|
+
nodeStatusServiceId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
779
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
780
|
+
}, import("zod/v4/core").$strip>;
|
|
781
|
+
}, import("zod/v4/core").$strip>>>;
|
|
782
|
+
ports: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
783
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
784
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
785
|
+
}, import("zod/v4/core").$strip>>;
|
|
786
|
+
}, import("zod/v4/core").$strip>;
|
|
787
|
+
declare const zTopologyLinkEndpoint: import("zod/mini").ZodMiniObject<{
|
|
788
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
789
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
790
|
+
}, import("zod/v4/core").$strip>;
|
|
791
|
+
declare const zTopologyTransportEndpoint: import("zod/mini").ZodMiniObject<{
|
|
792
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
793
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
794
|
+
}, import("zod/v4/core").$strip>;
|
|
795
|
+
/**
|
|
796
|
+
* Transport details as observed at a topology link. `local` corresponds to the
|
|
797
|
+
* link's `from` endpoint and `remote` to its `to` endpoint.
|
|
798
|
+
*/
|
|
799
|
+
declare const zTopologyTransportInfo: import("zod/mini").ZodMiniObject<{
|
|
800
|
+
type: import("zod/mini").ZodMiniString<string>;
|
|
801
|
+
local: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
802
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
803
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
804
|
+
}, import("zod/v4/core").$strip>>;
|
|
805
|
+
remote: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
806
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
807
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
808
|
+
}, import("zod/v4/core").$strip>>;
|
|
809
|
+
path: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
810
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
811
|
+
}, import("zod/v4/core").$strip>;
|
|
812
|
+
declare const zTopologyLink: import("zod/mini").ZodMiniObject<{
|
|
813
|
+
from: import("zod/mini").ZodMiniObject<{
|
|
814
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
815
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
816
|
+
}, import("zod/v4/core").$strip>;
|
|
817
|
+
to: import("zod/mini").ZodMiniObject<{
|
|
818
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
819
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
820
|
+
}, import("zod/v4/core").$strip>;
|
|
821
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
822
|
+
peerState: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniEnum<{
|
|
823
|
+
identified: "identified";
|
|
824
|
+
pending: "pending";
|
|
825
|
+
unsupported: "unsupported";
|
|
826
|
+
error: "error";
|
|
827
|
+
}>>;
|
|
828
|
+
transport: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
829
|
+
type: import("zod/mini").ZodMiniString<string>;
|
|
830
|
+
local: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
831
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
832
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
833
|
+
}, import("zod/v4/core").$strip>>;
|
|
834
|
+
remote: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
835
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
836
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
837
|
+
}, import("zod/v4/core").$strip>>;
|
|
838
|
+
path: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
839
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
840
|
+
}, import("zod/v4/core").$strip>>;
|
|
841
|
+
}, import("zod/v4/core").$strip>;
|
|
842
|
+
declare const zRouteClaim: import("zod/mini").ZodMiniObject<{
|
|
843
|
+
serviceId: import("zod/mini").ZodMiniString<string>;
|
|
844
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
845
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
846
|
+
match: import("zod/mini").ZodMiniEnum<{
|
|
847
|
+
exact: "exact";
|
|
848
|
+
prefix: "prefix";
|
|
849
|
+
}>;
|
|
850
|
+
}, import("zod/v4/core").$strip>;
|
|
851
|
+
declare const zTopologyGraph: import("zod/mini").ZodMiniObject<{
|
|
852
|
+
observerServiceId: import("zod/mini").ZodMiniString<string>;
|
|
853
|
+
entryNodeId: import("zod/mini").ZodMiniString<string>;
|
|
854
|
+
nodes: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
855
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
856
|
+
kind: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniEnum<{
|
|
857
|
+
endpoint: "endpoint";
|
|
858
|
+
hub: "hub";
|
|
859
|
+
}>>;
|
|
860
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
861
|
+
descriptors: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
862
|
+
source: import("zod/mini").ZodMiniEnum<{
|
|
863
|
+
self: "self";
|
|
864
|
+
attacher: "attacher";
|
|
865
|
+
}>;
|
|
866
|
+
descriptor: import("zod/mini").ZodMiniObject<{
|
|
867
|
+
type: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
868
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
869
|
+
processId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
870
|
+
processType: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
871
|
+
nodeStatusServiceId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
872
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
873
|
+
}, import("zod/v4/core").$strip>;
|
|
874
|
+
}, import("zod/v4/core").$strip>>>;
|
|
875
|
+
ports: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
876
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
877
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
878
|
+
}, import("zod/v4/core").$strip>>;
|
|
879
|
+
}, import("zod/v4/core").$strip>>;
|
|
880
|
+
links: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
881
|
+
from: import("zod/mini").ZodMiniObject<{
|
|
882
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
883
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
884
|
+
}, import("zod/v4/core").$strip>;
|
|
885
|
+
to: import("zod/mini").ZodMiniObject<{
|
|
886
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
887
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
888
|
+
}, import("zod/v4/core").$strip>;
|
|
889
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
890
|
+
peerState: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniEnum<{
|
|
891
|
+
identified: "identified";
|
|
892
|
+
pending: "pending";
|
|
893
|
+
unsupported: "unsupported";
|
|
894
|
+
error: "error";
|
|
895
|
+
}>>;
|
|
896
|
+
transport: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
897
|
+
type: import("zod/mini").ZodMiniString<string>;
|
|
898
|
+
local: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
899
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
900
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
901
|
+
}, import("zod/v4/core").$strip>>;
|
|
902
|
+
remote: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
903
|
+
address: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
904
|
+
port: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
905
|
+
}, import("zod/v4/core").$strip>>;
|
|
906
|
+
path: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
907
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
908
|
+
}, import("zod/v4/core").$strip>>;
|
|
909
|
+
}, import("zod/v4/core").$strip>>;
|
|
910
|
+
routes: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
911
|
+
serviceId: import("zod/mini").ZodMiniString<string>;
|
|
912
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
913
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
914
|
+
match: import("zod/mini").ZodMiniEnum<{
|
|
915
|
+
exact: "exact";
|
|
916
|
+
prefix: "prefix";
|
|
917
|
+
}>;
|
|
918
|
+
}, import("zod/v4/core").$strip>>;
|
|
919
|
+
}, import("zod/v4/core").$strip>;
|
|
920
|
+
type TopologyPort = output<typeof zTopologyPort>;
|
|
921
|
+
type ParticipantDescriptor = output<typeof zParticipantDescriptor>;
|
|
922
|
+
type ParticipantDescriptorSource = output<typeof zParticipantDescriptorSource>;
|
|
923
|
+
type TopologyNode = output<typeof zTopologyNode>;
|
|
924
|
+
type TopologyLinkEndpoint = output<typeof zTopologyLinkEndpoint>;
|
|
925
|
+
type TopologyTransportEndpoint = output<typeof zTopologyTransportEndpoint>;
|
|
926
|
+
type TopologyTransportInfo = output<typeof zTopologyTransportInfo>;
|
|
927
|
+
type TopologyLink = output<typeof zTopologyLink>;
|
|
928
|
+
type RouteClaim = output<typeof zRouteClaim>;
|
|
929
|
+
type TopologyGraph = output<typeof zTopologyGraph>;
|
|
930
|
+
declare const topologyInterface: InterfaceDefinition<{
|
|
931
|
+
getGraph: RequestType<Record<string, never>, {
|
|
932
|
+
observerServiceId: string;
|
|
933
|
+
entryNodeId: string;
|
|
934
|
+
nodes: {
|
|
935
|
+
nodeId: string;
|
|
936
|
+
ports: {
|
|
937
|
+
portId: string;
|
|
938
|
+
label?: string | undefined;
|
|
939
|
+
}[];
|
|
940
|
+
kind?: "endpoint" | "hub" | undefined;
|
|
941
|
+
label?: string | undefined;
|
|
942
|
+
descriptors?: {
|
|
943
|
+
source: "self" | "attacher";
|
|
944
|
+
descriptor: {
|
|
945
|
+
type?: string | undefined;
|
|
946
|
+
label?: string | undefined;
|
|
947
|
+
processId?: number | undefined;
|
|
948
|
+
processType?: string | undefined;
|
|
949
|
+
nodeStatusServiceId?: string | undefined;
|
|
950
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
951
|
+
};
|
|
952
|
+
}[] | undefined;
|
|
953
|
+
}[];
|
|
954
|
+
links: {
|
|
955
|
+
from: {
|
|
956
|
+
nodeId: string;
|
|
957
|
+
portId: string;
|
|
958
|
+
};
|
|
959
|
+
to: {
|
|
960
|
+
nodeId: string;
|
|
961
|
+
portId: string;
|
|
962
|
+
};
|
|
963
|
+
label?: string | undefined;
|
|
964
|
+
peerState?: "identified" | "pending" | "unsupported" | "error" | undefined;
|
|
965
|
+
transport?: {
|
|
966
|
+
type: string;
|
|
967
|
+
local?: {
|
|
968
|
+
address?: string | undefined;
|
|
969
|
+
port?: number | undefined;
|
|
970
|
+
} | undefined;
|
|
971
|
+
remote?: {
|
|
972
|
+
address?: string | undefined;
|
|
973
|
+
port?: number | undefined;
|
|
974
|
+
} | undefined;
|
|
975
|
+
path?: string | undefined;
|
|
976
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
977
|
+
} | undefined;
|
|
978
|
+
}[];
|
|
979
|
+
routes: {
|
|
980
|
+
serviceId: string;
|
|
981
|
+
nodeId: string;
|
|
982
|
+
portId: string;
|
|
983
|
+
match: "exact" | "prefix";
|
|
984
|
+
}[];
|
|
985
|
+
}, void, Record<string, never>, any>;
|
|
986
|
+
watchGraph: RequestType<Record<string, never>, Record<string, never>, void, any, Record<string, never>>;
|
|
987
|
+
}>;
|
|
988
|
+
declare const zTrafficTransitEndpoint: import("zod/mini").ZodMiniObject<{
|
|
989
|
+
edgeId: import("zod/mini").ZodMiniString<string>;
|
|
990
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
991
|
+
requestId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniString<string>]>>;
|
|
992
|
+
}, import("zod/v4/core").$strip>;
|
|
993
|
+
declare const zTrafficTransitEvent: import("zod/mini").ZodMiniObject<{
|
|
994
|
+
type: import("zod/mini").ZodMiniLiteral<"transit">;
|
|
995
|
+
timeMs: import("zod/mini").ZodMiniNumber<number>;
|
|
996
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
997
|
+
in: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
998
|
+
edgeId: import("zod/mini").ZodMiniString<string>;
|
|
999
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
1000
|
+
requestId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniString<string>]>>;
|
|
1001
|
+
}, import("zod/v4/core").$strip>>;
|
|
1002
|
+
out: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
1003
|
+
edgeId: import("zod/mini").ZodMiniString<string>;
|
|
1004
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
1005
|
+
requestId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniString<string>]>>;
|
|
1006
|
+
}, import("zod/v4/core").$strip>>;
|
|
1007
|
+
disposition: import("zod/mini").ZodMiniEnum<{
|
|
1008
|
+
forwarded: "forwarded";
|
|
1009
|
+
consumed: "consumed";
|
|
1010
|
+
dropped: "dropped";
|
|
1011
|
+
unroutable: "unroutable";
|
|
1012
|
+
}>;
|
|
1013
|
+
kind: import("zod/mini").ZodMiniEnum<{
|
|
1014
|
+
request: "request";
|
|
1015
|
+
notification: "notification";
|
|
1016
|
+
response: "response";
|
|
1017
|
+
stream: "stream";
|
|
1018
|
+
}>;
|
|
1019
|
+
method: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
1020
|
+
params: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnknown>;
|
|
1021
|
+
result: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnknown>;
|
|
1022
|
+
error: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
1023
|
+
code: import("zod/mini").ZodMiniNumber<number>;
|
|
1024
|
+
message: import("zod/mini").ZodMiniString<string>;
|
|
1025
|
+
data: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnknown>;
|
|
1026
|
+
}, import("zod/v4/core").$strip>>;
|
|
1027
|
+
}, import("zod/v4/core").$strip>;
|
|
1028
|
+
declare const zTrafficOverflowEvent: import("zod/mini").ZodMiniObject<{
|
|
1029
|
+
type: import("zod/mini").ZodMiniLiteral<"overflow">;
|
|
1030
|
+
dropped: import("zod/mini").ZodMiniNumber<number>;
|
|
1031
|
+
}, import("zod/v4/core").$strip>;
|
|
1032
|
+
declare const zTrafficEvent: import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniObject<{
|
|
1033
|
+
type: import("zod/mini").ZodMiniLiteral<"transit">;
|
|
1034
|
+
timeMs: import("zod/mini").ZodMiniNumber<number>;
|
|
1035
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
1036
|
+
in: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
1037
|
+
edgeId: import("zod/mini").ZodMiniString<string>;
|
|
1038
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
1039
|
+
requestId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniString<string>]>>;
|
|
1040
|
+
}, import("zod/v4/core").$strip>>;
|
|
1041
|
+
out: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
1042
|
+
edgeId: import("zod/mini").ZodMiniString<string>;
|
|
1043
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
1044
|
+
requestId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniString<string>]>>;
|
|
1045
|
+
}, import("zod/v4/core").$strip>>;
|
|
1046
|
+
disposition: import("zod/mini").ZodMiniEnum<{
|
|
1047
|
+
forwarded: "forwarded";
|
|
1048
|
+
consumed: "consumed";
|
|
1049
|
+
dropped: "dropped";
|
|
1050
|
+
unroutable: "unroutable";
|
|
1051
|
+
}>;
|
|
1052
|
+
kind: import("zod/mini").ZodMiniEnum<{
|
|
1053
|
+
request: "request";
|
|
1054
|
+
notification: "notification";
|
|
1055
|
+
response: "response";
|
|
1056
|
+
stream: "stream";
|
|
1057
|
+
}>;
|
|
1058
|
+
method: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
1059
|
+
params: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnknown>;
|
|
1060
|
+
result: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnknown>;
|
|
1061
|
+
error: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniObject<{
|
|
1062
|
+
code: import("zod/mini").ZodMiniNumber<number>;
|
|
1063
|
+
message: import("zod/mini").ZodMiniString<string>;
|
|
1064
|
+
data: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniUnknown>;
|
|
1065
|
+
}, import("zod/v4/core").$strip>>;
|
|
1066
|
+
}, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniObject<{
|
|
1067
|
+
type: import("zod/mini").ZodMiniLiteral<"overflow">;
|
|
1068
|
+
dropped: import("zod/mini").ZodMiniNumber<number>;
|
|
1069
|
+
}, import("zod/v4/core").$strip>]>;
|
|
1070
|
+
type TrafficTransitEndpoint = output<typeof zTrafficTransitEndpoint>;
|
|
1071
|
+
type TrafficTransitEvent = output<typeof zTrafficTransitEvent>;
|
|
1072
|
+
type TrafficOverflowEvent = output<typeof zTrafficOverflowEvent>;
|
|
1073
|
+
type TrafficEvent = output<typeof zTrafficEvent>;
|
|
1074
|
+
declare const zTrafficWatchResult: import("zod/mini").ZodMiniObject<{
|
|
1075
|
+
delivered: import("zod/mini").ZodMiniNumber<number>;
|
|
1076
|
+
dropped: import("zod/mini").ZodMiniNumber<number>;
|
|
1077
|
+
}, import("zod/v4/core").$strip>;
|
|
1078
|
+
type TrafficWatchResult = output<typeof zTrafficWatchResult>;
|
|
1079
|
+
/**
|
|
1080
|
+
* Observe raw message transits at the node hosting the addressed service.
|
|
1081
|
+
* Consumers may correlate adjacent transits by shared `(portId, requestId)`.
|
|
1082
|
+
*/
|
|
1083
|
+
declare const trafficInterface: InterfaceDefinition<{
|
|
1084
|
+
watch: RequestType<{
|
|
1085
|
+
methodPrefix?: string | undefined;
|
|
1086
|
+
trafficIgnoreKey?: string | undefined;
|
|
1087
|
+
focusRequest?: {
|
|
1088
|
+
portId: string;
|
|
1089
|
+
requestId: string | number;
|
|
1090
|
+
} | undefined;
|
|
1091
|
+
}, {
|
|
1092
|
+
delivered: number;
|
|
1093
|
+
dropped: number;
|
|
1094
|
+
}, void, any, {
|
|
1095
|
+
type: "transit";
|
|
1096
|
+
timeMs: number;
|
|
1097
|
+
nodeId: string;
|
|
1098
|
+
disposition: "forwarded" | "consumed" | "dropped" | "unroutable";
|
|
1099
|
+
kind: "request" | "notification" | "response" | "stream";
|
|
1100
|
+
in?: {
|
|
1101
|
+
edgeId: string;
|
|
1102
|
+
portId: string;
|
|
1103
|
+
requestId?: string | number | undefined;
|
|
1104
|
+
} | undefined;
|
|
1105
|
+
out?: {
|
|
1106
|
+
edgeId: string;
|
|
1107
|
+
portId: string;
|
|
1108
|
+
requestId?: string | number | undefined;
|
|
1109
|
+
} | undefined;
|
|
1110
|
+
method?: string | undefined;
|
|
1111
|
+
params?: unknown;
|
|
1112
|
+
result?: unknown;
|
|
1113
|
+
error?: {
|
|
1114
|
+
code: number;
|
|
1115
|
+
message: string;
|
|
1116
|
+
data?: unknown;
|
|
1117
|
+
} | undefined;
|
|
1118
|
+
} | {
|
|
1119
|
+
type: "overflow";
|
|
1120
|
+
dropped: number;
|
|
1121
|
+
}>;
|
|
1122
|
+
watchWithPayloads: RequestType<{
|
|
1123
|
+
maxPayloadBytes: number;
|
|
1124
|
+
methodPrefix?: string | undefined;
|
|
1125
|
+
trafficIgnoreKey?: string | undefined;
|
|
1126
|
+
focusRequest?: {
|
|
1127
|
+
portId: string;
|
|
1128
|
+
requestId: string | number;
|
|
1129
|
+
} | undefined;
|
|
1130
|
+
}, {
|
|
1131
|
+
delivered: number;
|
|
1132
|
+
dropped: number;
|
|
1133
|
+
}, void, any, {
|
|
1134
|
+
type: "transit";
|
|
1135
|
+
timeMs: number;
|
|
1136
|
+
nodeId: string;
|
|
1137
|
+
disposition: "forwarded" | "consumed" | "dropped" | "unroutable";
|
|
1138
|
+
kind: "request" | "notification" | "response" | "stream";
|
|
1139
|
+
in?: {
|
|
1140
|
+
edgeId: string;
|
|
1141
|
+
portId: string;
|
|
1142
|
+
requestId?: string | number | undefined;
|
|
1143
|
+
} | undefined;
|
|
1144
|
+
out?: {
|
|
1145
|
+
edgeId: string;
|
|
1146
|
+
portId: string;
|
|
1147
|
+
requestId?: string | number | undefined;
|
|
1148
|
+
} | undefined;
|
|
1149
|
+
method?: string | undefined;
|
|
1150
|
+
params?: unknown;
|
|
1151
|
+
result?: unknown;
|
|
1152
|
+
error?: {
|
|
1153
|
+
code: number;
|
|
1154
|
+
message: string;
|
|
1155
|
+
data?: unknown;
|
|
1156
|
+
} | undefined;
|
|
1157
|
+
} | {
|
|
1158
|
+
type: "overflow";
|
|
1159
|
+
dropped: number;
|
|
1160
|
+
}>;
|
|
1161
|
+
}>;
|
|
1162
|
+
//#endregion
|
|
1163
|
+
//#region src/inspection/node.interfaces.d.ts
|
|
1164
|
+
declare const zNodeInfo: import("zod/mini").ZodMiniObject<{
|
|
1165
|
+
nodeId: import("zod/mini").ZodMiniString<string>;
|
|
1166
|
+
portId: import("zod/mini").ZodMiniString<string>;
|
|
1167
|
+
descriptors: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
1168
|
+
source: import("zod/mini").ZodMiniEnum<{
|
|
1169
|
+
self: "self";
|
|
1170
|
+
attacher: "attacher";
|
|
1171
|
+
}>;
|
|
1172
|
+
descriptor: import("zod/mini").ZodMiniObject<{
|
|
1173
|
+
type: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
1174
|
+
label: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
1175
|
+
processId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniNumber<number>>;
|
|
1176
|
+
processType: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
1177
|
+
nodeStatusServiceId: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniString<string>>;
|
|
1178
|
+
metadata: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniNumber<number>, import("zod/mini").ZodMiniBoolean<boolean>]>>>;
|
|
1179
|
+
}, import("zod/v4/core").$strip>;
|
|
1180
|
+
}, import("zod/v4/core").$strip>>>;
|
|
1181
|
+
}, import("zod/v4/core").$strip>;
|
|
1182
|
+
type NodeInfo = output<typeof zNodeInfo>;
|
|
1183
|
+
/**
|
|
1184
|
+
* Generates unique topology-correlation labels, not security identities.
|
|
1185
|
+
* Custom generators must avoid collisions across nodes and ports. Tests can
|
|
1186
|
+
* supply an independent deterministic sequence for each participant.
|
|
1187
|
+
*/
|
|
1188
|
+
type TopologyIdGenerator = (kind: 'node' | 'port') => string;
|
|
1189
|
+
/**
|
|
1190
|
+
* Minimal root service for aligning the independently observed topologies at
|
|
1191
|
+
* both ends of a connection.
|
|
1192
|
+
*/
|
|
1193
|
+
declare const nodeInterface: InterfaceDefinition<{
|
|
1194
|
+
getNodeId: RequestType<Record<string, never>, {
|
|
1195
|
+
nodeId: string;
|
|
1196
|
+
portId: string;
|
|
1197
|
+
descriptors?: {
|
|
1198
|
+
source: "self" | "attacher";
|
|
1199
|
+
descriptor: {
|
|
1200
|
+
type?: string | undefined;
|
|
1201
|
+
label?: string | undefined;
|
|
1202
|
+
processId?: number | undefined;
|
|
1203
|
+
processType?: string | undefined;
|
|
1204
|
+
nodeStatusServiceId?: string | undefined;
|
|
1205
|
+
metadata?: Record<string, string | number | boolean> | undefined;
|
|
1206
|
+
};
|
|
1207
|
+
}[] | undefined;
|
|
1208
|
+
}, any, never, never>;
|
|
1209
|
+
}>;
|
|
1210
|
+
//#endregion
|
|
1211
|
+
//#region src/connection/streaming.d.ts
|
|
1212
|
+
/**
|
|
1213
|
+
* Direction a {@link STREAM_METHOD} message travels, relative to the
|
|
1214
|
+
* request it is correlated with. Made explicit on the wire so that a
|
|
1215
|
+
* middlebox (the hub) can *author* a stream message — e.g. inject a
|
|
1216
|
+
* cancel when a caller disconnects — without an inbound message whose
|
|
1217
|
+
* arrival link it could infer the direction from.
|
|
1218
|
+
*/
|
|
1219
|
+
declare const StreamDir: {
|
|
1220
|
+
/** callee → caller: progress, partial results. Routes like a response. */
|
|
1221
|
+
readonly toCaller: "toCaller";
|
|
1222
|
+
/** caller → callee: input, cancellation, keepalive ping. */
|
|
1223
|
+
readonly toCallee: "toCallee";
|
|
1224
|
+
};
|
|
1225
|
+
type StreamDir = (typeof StreamDir)[keyof typeof StreamDir];
|
|
1226
|
+
/**
|
|
1227
|
+
* Reserved control verbs. A control message carries no app `payload`; it
|
|
1228
|
+
* is interpreted by the runtime / hub itself, independently of the
|
|
1229
|
+
* originating method's stream schemas, so *any* request is cancellable
|
|
1230
|
+
* and keep-alive-able even when it declares no app stream.
|
|
1231
|
+
*/
|
|
1232
|
+
declare const StreamControlType: {
|
|
1233
|
+
/**
|
|
1234
|
+
* Ask the callee to abort the in-flight request. Always {@link
|
|
1235
|
+
* StreamDir.toCallee}. The callee surfaces this as an `AbortSignal`
|
|
1236
|
+
* and is expected to settle the request (typically with a
|
|
1237
|
+
* `cancelled` error response).
|
|
1238
|
+
*/
|
|
1239
|
+
readonly cancel: "cancel";
|
|
1240
|
+
/**
|
|
1241
|
+
* Keepalive / liveness probe. Resets the hub's per-request idle timer
|
|
1242
|
+
* so a long-running call is not reaped, and lets either side actively
|
|
1243
|
+
* probe the peer. Carries a `nonce` the peer echoes in its {@link
|
|
1244
|
+
* StreamControlType.pong}. May travel in either direction. Emitted
|
|
1245
|
+
* automatically (without awaiting a pong) by the channel for streaming
|
|
1246
|
+
* calls; see {@link STREAM_METHOD}.
|
|
1247
|
+
*/
|
|
1248
|
+
readonly ping: "ping";
|
|
1249
|
+
/**
|
|
1250
|
+
* Reply to a {@link StreamControlType.ping}, echoing the ping's
|
|
1251
|
+
* `nonce` so the prober can correlate it. Travels opposite the ping.
|
|
1252
|
+
* Carries no app effect — purely a liveness acknowledgement.
|
|
1253
|
+
*/
|
|
1254
|
+
readonly pong: "pong";
|
|
1255
|
+
};
|
|
1256
|
+
type StreamControlType = (typeof StreamControlType)[keyof typeof StreamControlType];
|
|
1257
|
+
/**
|
|
1258
|
+
* Well-known `control.reason` strings. The set is **open** — any string
|
|
1259
|
+
* is valid on the wire; these are the reasons linkrpc itself emits.
|
|
1260
|
+
*/
|
|
1261
|
+
declare const StreamControlReason: {
|
|
1262
|
+
/**
|
|
1263
|
+
* The caller's transport dropped while the request was in flight; the
|
|
1264
|
+
* hub cancels the callee's now-orphaned work.
|
|
1265
|
+
*/
|
|
1266
|
+
readonly clientDisconnected: "clientDisconnected";
|
|
1267
|
+
/**
|
|
1268
|
+
* The request exceeded the hub's idle timeout with no stream activity
|
|
1269
|
+
* (no ping, no stream message). See {@link STREAM_METHOD} docs.
|
|
1270
|
+
*/
|
|
1271
|
+
readonly idleTimeout: "idleTimeout";
|
|
1272
|
+
};
|
|
1273
|
+
type StreamControlReason = (typeof StreamControlReason)[keyof typeof StreamControlReason];
|
|
1274
|
+
/**
|
|
1275
|
+
* Streaming sub-protocol. Long-running requests can emit additional
|
|
1276
|
+
* notifications correlated with the original call in either direction
|
|
1277
|
+
* (callee → caller progress, caller → callee input / cancellation /
|
|
1278
|
+
* keepalive).
|
|
1279
|
+
*
|
|
1280
|
+
* Wire form: a JSON-RPC notification whose method is
|
|
1281
|
+
* `${streamInterface.info.id}::send` and whose params match
|
|
1282
|
+
* `streamInterface.members.send.paramsSchema`. The hub routes purely on
|
|
1283
|
+
* `requestId` (following the originating request's established path, like
|
|
1284
|
+
* a response); the originating request's capability authorization covers
|
|
1285
|
+
* its full stream lifetime, so stream notifications carry no method /
|
|
1286
|
+
* interface namespace of their own. App `payload` contents are typed per
|
|
1287
|
+
* request via the originating method's
|
|
1288
|
+
* `RequestType.clientStreamSchema` / `serverStreamSchema`; reserved
|
|
1289
|
+
* `control` messages are schema-independent.
|
|
1290
|
+
*
|
|
1291
|
+
* Idle timeout: a request that never streams (no `control` ping, no
|
|
1292
|
+
* stream message) is subject to the hub's per-request idle timeout
|
|
1293
|
+
* (default 30 minutes) — at which point it is cancelled and its caller
|
|
1294
|
+
* gets a `requestTimeout` error. This bounds the hub's pending-request
|
|
1295
|
+
* table, which is also what protects it from a slow-loris / DDoS that
|
|
1296
|
+
* opens requests and never completes them. Streaming-enabled calls keep
|
|
1297
|
+
* themselves alive by emitting a periodic {@link StreamControlType.ping}.
|
|
1298
|
+
*
|
|
1299
|
+
* The interface is declarative: nobody calls
|
|
1300
|
+
* `LinkRpcConnection.register(streamInterface, ...)`. The channel and the
|
|
1301
|
+
* hub intercept `$stream::send` directly. The definition exists so
|
|
1302
|
+
* reflection / consent UIs can describe the wire shape and so all wire
|
|
1303
|
+
* constants derive from one source.
|
|
1304
|
+
*/
|
|
1305
|
+
declare const streamInterface: InterfaceDefinition<{
|
|
1306
|
+
send: NotificationType<{
|
|
1307
|
+
requestId: string | number;
|
|
1308
|
+
dir: "toCaller" | "toCallee";
|
|
1309
|
+
control?: {
|
|
1310
|
+
type: "cancel" | "ping" | "pong";
|
|
1311
|
+
reason?: string | undefined;
|
|
1312
|
+
nonce?: string | undefined;
|
|
1313
|
+
} | undefined;
|
|
1314
|
+
payload?: unknown;
|
|
1315
|
+
}>;
|
|
1316
|
+
}>;
|
|
1317
|
+
/**
|
|
1318
|
+
* Full wire method name for stream notifications. Pulled from the
|
|
1319
|
+
* interface object so we have a single source of truth.
|
|
1320
|
+
*/
|
|
1321
|
+
declare const STREAM_METHOD: `${string}::send`;
|
|
1322
|
+
/** Wire-level shape carried on `params` of {@link STREAM_METHOD}. */
|
|
1323
|
+
type StreamSendParams = output<typeof streamInterface.members.send.paramsSchema>;
|
|
1324
|
+
//#endregion
|
|
1325
|
+
//#region src/protocol/jcs.d.ts
|
|
1326
|
+
/**
|
|
1327
|
+
* RFC 8785 JSON Canonicalization Scheme (JCS).
|
|
1328
|
+
*
|
|
1329
|
+
* Used as the byte-deterministic encoding under every linkrpc signature
|
|
1330
|
+
* (RPC calls, capabilities, hub-signed previews). Both signer and verifier
|
|
1331
|
+
* canonicalize the same value object and obtain byte-identical UTF-8 bytes.
|
|
1332
|
+
*
|
|
1333
|
+
* Implementation inlined from the `canonicalize` npm package (Apache-2.0,
|
|
1334
|
+
* https://github.com/erdtman/canonicalize) so this module stays
|
|
1335
|
+
* zero-dependency for browser bundlers that don't resolve transitive
|
|
1336
|
+
* npm specifiers (e.g. the in-house app-bundler).
|
|
1337
|
+
*/
|
|
1338
|
+
/** RFC 8785 canonical JSON string for `value`. */
|
|
1339
|
+
declare function jcsCanonicalize(value: unknown): string;
|
|
1340
|
+
/** UTF-8 bytes of the RFC 8785 canonical JSON for `value`. The thing actually signed / hashed. */
|
|
1341
|
+
declare function jcsCanonicalizeBytes(value: unknown): Uint8Array;
|
|
1342
|
+
//#endregion
|
|
1343
|
+
//#region src/crypto/cryptoProvider.d.ts
|
|
1344
|
+
/**
|
|
1345
|
+
* Identity primitives. linkrpc itself is identity-agnostic; this module
|
|
1346
|
+
* defines the shared types. No crypto code lives here — the crypto API is
|
|
1347
|
+
* `./crypto` (backed by the Web Crypto implementation in
|
|
1348
|
+
* `./ed25519CryptoProvider`).
|
|
1349
|
+
*/
|
|
1350
|
+
/** Raw Ed25519 public key (32 bytes). */
|
|
1351
|
+
type PublicKey = Uint8Array;
|
|
1352
|
+
/** Raw Ed25519 private key (32 bytes — seed form). */
|
|
1353
|
+
type PrivateKey = Uint8Array;
|
|
1354
|
+
/** Raw Ed25519 signature (64 bytes). */
|
|
1355
|
+
type Signature = Uint8Array;
|
|
1356
|
+
/**
|
|
1357
|
+
* A **principal**: an identity's stable, opaque name. In Phase 1 every
|
|
1358
|
+
* principal is *perpetual* and self-describing — its genesis signing key is
|
|
1359
|
+
* embedded verbatim:
|
|
1360
|
+
*
|
|
1361
|
+
* principal = "id:" + keyId (e.g. "id:key:<b64url(pubKey)>")
|
|
1362
|
+
*
|
|
1363
|
+
* So the genesis check is pure string equality (`"id:" + keyId === principal`)
|
|
1364
|
+
* and no key-binding records are ever needed. Treat as opaque; construct via
|
|
1365
|
+
* {@link principalForPublicKey} and resolve a verifying key via
|
|
1366
|
+
* {@link resolveSigningKey}.
|
|
1367
|
+
*/
|
|
1368
|
+
type PrincipalId = string;
|
|
1369
|
+
/**
|
|
1370
|
+
* A **keyId**: names the signing key that produced a signature. In Phase 1 the
|
|
1371
|
+
* only form is an *inline* key — the public key encoded verbatim:
|
|
1372
|
+
*
|
|
1373
|
+
* keyId = "key:" + b64url(pubKey)
|
|
1374
|
+
*
|
|
1375
|
+
* (Future forms, e.g. `"keydoc:" + b64url(sha256(jcs(doc)))`, are resolved
|
|
1376
|
+
* through bindings — see the design doc — but Phase 1 needs none.)
|
|
1377
|
+
*/
|
|
1378
|
+
type KeyId = string;
|
|
1379
|
+
interface Keypair {
|
|
1380
|
+
readonly publicKey: PublicKey;
|
|
1381
|
+
readonly privateKey: PrivateKey;
|
|
1382
|
+
}
|
|
1383
|
+
/** Raw X25519 keypair. Both halves are 32 bytes. */
|
|
1384
|
+
interface X25519Keypair {
|
|
1385
|
+
readonly publicKey: Uint8Array;
|
|
1386
|
+
readonly privateKey: Uint8Array;
|
|
1387
|
+
}
|
|
1388
|
+
declare function bytesToBase64Url(bytes: Uint8Array): string;
|
|
1389
|
+
declare function base64UrlToBytes(s: string): Uint8Array;
|
|
1390
|
+
/** Role-tag prefix on a {@link KeyId}'s inline-key form. */
|
|
1391
|
+
declare const KEY_ID_PREFIX = "key:";
|
|
1392
|
+
/** Role-tag prefix that wraps a genesis {@link KeyId} into a {@link PrincipalId}. */
|
|
1393
|
+
declare const PRINCIPAL_PREFIX = "id:";
|
|
1394
|
+
/** The inline {@link KeyId} for a raw public key: `"key:" + b64url(pk)`. */
|
|
1395
|
+
declare function keyIdForPublicKey(pk: PublicKey): KeyId;
|
|
1396
|
+
/** The perpetual {@link PrincipalId} for a raw public key: `"id:key:" + b64url(pk)`. */
|
|
1397
|
+
declare function principalForPublicKey(pk: PublicKey): PrincipalId;
|
|
1398
|
+
/**
|
|
1399
|
+
* The genesis {@link KeyId} embedded in a perpetual {@link PrincipalId}
|
|
1400
|
+
* (`"id:" + keyId`). Throws if `principal` is not a well-formed `id:` name.
|
|
1401
|
+
*/
|
|
1402
|
+
declare function keyIdForPrincipal(principal: PrincipalId): KeyId;
|
|
1403
|
+
/**
|
|
1404
|
+
* The raw public key carried inline by a `"key:"` {@link KeyId}. Throws on a
|
|
1405
|
+
* missing prefix or malformed base64url.
|
|
1406
|
+
*/
|
|
1407
|
+
declare function publicKeyForKeyId(keyId: KeyId): PublicKey;
|
|
1408
|
+
/** Arguments to {@link resolveSigningKey}. */
|
|
1409
|
+
interface ResolveSigningKeyArgs {
|
|
1410
|
+
/** The identity the signature claims to be from. */
|
|
1411
|
+
readonly principal: PrincipalId;
|
|
1412
|
+
/** Which key (per `$hubrpcSignature[domain].keyId`) is claimed to have signed. */
|
|
1413
|
+
readonly keyId: KeyId;
|
|
1414
|
+
/** Presented key-binding records. Unused in Phase 1 (perpetual ids need none). */
|
|
1415
|
+
readonly bindings?: Record<KeyId, unknown>;
|
|
1416
|
+
/** Time the signature was made (e.g. `signedAtMs`). Unused in Phase 1. */
|
|
1417
|
+
readonly timeMs?: number;
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Resolve the verifying public key for `(principal, keyId)`, or `undefined`
|
|
1421
|
+
* to **reject** (fail closed). This is the single seam every verifier goes
|
|
1422
|
+
* through, replacing the old "the id *is* the key" decode.
|
|
1423
|
+
*
|
|
1424
|
+
* Phase 1 supports only inline genesis keys: the `keyId` must be a `"key:"`
|
|
1425
|
+
* form and the principal must be exactly `"id:" + keyId` (genesis
|
|
1426
|
+
* self-certification by string equality). `keydoc:`/rotation forms resolve
|
|
1427
|
+
* through `bindings`/`timeMs` later and are intentionally not handled here.
|
|
1428
|
+
*/
|
|
1429
|
+
declare function resolveSigningKey(args: ResolveSigningKeyArgs): {
|
|
1430
|
+
publicKey: PublicKey;
|
|
1431
|
+
} | undefined;
|
|
1432
|
+
//#endregion
|
|
1433
|
+
//#region src/protocol/signedObject.d.ts
|
|
1434
|
+
/** An open signature domain. Each gets a distinct {@link signingDomainValue}. */
|
|
1435
|
+
type SignDomain = string;
|
|
1436
|
+
/** Reserved wire-key: the signature map `{ [domain]: SignatureEnvelope }`. */
|
|
1437
|
+
declare const HUBRPC_SIGNATURE_KEY = "$hubrpcSignature";
|
|
1438
|
+
/** Reserved wire-key: extrinsic unsigned attachments (e.g. the capability bag). */
|
|
1439
|
+
declare const HUBRPC_UNSIGNED_KEY = "$hubrpcUnsigned";
|
|
1440
|
+
/** Reserved wire-key: signed call meta, present only on calls (dodges the JSON-RPC param namespace). */
|
|
1441
|
+
declare const HUBRPC_META_KEY = "$hubrpc";
|
|
1442
|
+
/** @deprecated Use {@link HUBRPC_SIGNATURE_KEY}. */
|
|
1443
|
+
declare const LINKRPC_SIGNATURE_KEY = "$hubrpcSignature";
|
|
1444
|
+
/** @deprecated Use {@link HUBRPC_UNSIGNED_KEY}. */
|
|
1445
|
+
declare const LINKRPC_UNSIGNED_KEY = "$hubrpcUnsigned";
|
|
1446
|
+
/** @deprecated Use {@link HUBRPC_META_KEY}. */
|
|
1447
|
+
declare const LINKRPC_META_KEY = "$hubrpc";
|
|
1448
|
+
/**
|
|
1449
|
+
* One domain's signature on the wire: which key signed ({@link KeyId}, an
|
|
1450
|
+
* **unsigned** routing hint) plus the raw `base64url(sig)`. Lying about
|
|
1451
|
+
* `keyId` only makes verification fail (the resolver rejects or the key
|
|
1452
|
+
* mismatches), so it carries no integrity claim and is excluded from the
|
|
1453
|
+
* signed bytes along with the rest of `$hubrpcSignature`.
|
|
1454
|
+
*/
|
|
1455
|
+
interface SignatureEnvelope {
|
|
1456
|
+
/** The key the signer used (`"key:..."`). Resolved against the principal. */
|
|
1457
|
+
readonly keyId: KeyId;
|
|
1458
|
+
/** `base64url` of the raw signature over {@link signingInput}. */
|
|
1459
|
+
readonly sig: string;
|
|
1460
|
+
}
|
|
1461
|
+
/** Per-domain signature map carried under {@link HUBRPC_SIGNATURE_KEY}. */
|
|
1462
|
+
type Signatures = { readonly [D in SignDomain]?: SignatureEnvelope; };
|
|
1463
|
+
/** Documentary brand for `base64url(sha256(signingInput(domain, T)))`. */
|
|
1464
|
+
type Base64Sha256<T = unknown> = string & {
|
|
1465
|
+
readonly __sha256Of?: T;
|
|
1466
|
+
};
|
|
1467
|
+
/**
|
|
1468
|
+
* The domain-separation key. It includes the frozen signature-suite version
|
|
1469
|
+
* and becomes the sole key of the object passed to JCS.
|
|
1470
|
+
*/
|
|
1471
|
+
declare function signingDomainValue(domain: SignDomain): string;
|
|
1472
|
+
/**
|
|
1473
|
+
* THE bytes a signature commits to (and a content hash hashes) for `obj` in
|
|
1474
|
+
* `domain`: `jcs({ [domainValue(domain)]: obj minus the two reserved keys })`.
|
|
1475
|
+
*/
|
|
1476
|
+
declare function signingInput(domain: SignDomain, obj: object): Uint8Array;
|
|
1477
|
+
/**
|
|
1478
|
+
* Content identity of a signed object: `base64url(sha256(signingInput(...)))`.
|
|
1479
|
+
* The single operation behind both a capability's `callBind.payloadHash`
|
|
1480
|
+
* (domain `"call"`) and a child capability's `parentHash` (domain
|
|
1481
|
+
* `"capability"`).
|
|
1482
|
+
*/
|
|
1483
|
+
declare function signedHash<T extends object>(domain: SignDomain, obj: T): Base64Sha256<T>;
|
|
1484
|
+
/** Read the signature envelope for `domain` off an object's `$hubrpcSignature` map. */
|
|
1485
|
+
declare function readSignature(obj: object, domain: SignDomain): SignatureEnvelope | undefined;
|
|
1486
|
+
/** The {@link KeyId} the signer used for `domain` (the unsigned routing hint), if any. */
|
|
1487
|
+
declare function getKeyId(obj: object, domain: SignDomain): KeyId | undefined;
|
|
1488
|
+
/**
|
|
1489
|
+
* Return a copy of `obj` with `$hubrpcSignature[domain]` set to `envelope`,
|
|
1490
|
+
* preserving any sibling-domain signatures already present.
|
|
1491
|
+
*/
|
|
1492
|
+
declare function withSignature<T extends object>(obj: T, domain: SignDomain, envelope: SignatureEnvelope): T & {
|
|
1493
|
+
$hubrpcSignature: Record<string, SignatureEnvelope>;
|
|
1494
|
+
};
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#region src/protocol/capability.d.ts
|
|
1497
|
+
/**
|
|
1498
|
+
* A single axis matcher. Empty prefix matches anything. Non-empty prefix `p`
|
|
1499
|
+
* matches `value` iff:
|
|
1500
|
+
* - `value === p`, OR
|
|
1501
|
+
* - `value` starts with `p + delimiter` (delimiter depends on the axis).
|
|
1502
|
+
*
|
|
1503
|
+
* For axes without a delimiter (`member`), prefix degrades to `startsWith`.
|
|
1504
|
+
*/
|
|
1505
|
+
type Pattern = {
|
|
1506
|
+
exact: string;
|
|
1507
|
+
} | {
|
|
1508
|
+
prefix: string;
|
|
1509
|
+
};
|
|
1510
|
+
/**
|
|
1511
|
+
* Per-field matcher used by {@link Permission.params}. The set of declared
|
|
1512
|
+
* keys is a strict allowlist — a call whose params carry an undeclared key
|
|
1513
|
+
* is rejected, so the only way to permit a free value is to say
|
|
1514
|
+
* `{ any: true }` explicitly. Values are compared via canonical-JSON
|
|
1515
|
+
* equality, so anything JSON-serializable is acceptable.
|
|
1516
|
+
*
|
|
1517
|
+
* - `exact` — canonical-JSON equality against a single value.
|
|
1518
|
+
* - `enum` — canonical-JSON equality against any value in the list.
|
|
1519
|
+
* - `prefix` — value MUST be a string and `startsWith(prefix)`. Used to
|
|
1520
|
+
* pin e.g. a URL to a base-path subtree.
|
|
1521
|
+
* - `subsetOf` — value MUST be a `string[]` whose every element is in this
|
|
1522
|
+
* set. Order-independent; the empty array is a valid subset.
|
|
1523
|
+
* Used to bound a requested scope set.
|
|
1524
|
+
* - `any` — matches anything (the explicit wildcard).
|
|
1525
|
+
*/
|
|
1526
|
+
type ParamMatcher = {
|
|
1527
|
+
exact: unknown;
|
|
1528
|
+
} | {
|
|
1529
|
+
enum: unknown[];
|
|
1530
|
+
} | {
|
|
1531
|
+
prefix: string;
|
|
1532
|
+
} | {
|
|
1533
|
+
subsetOf: string[];
|
|
1534
|
+
} | {
|
|
1535
|
+
any: true;
|
|
1536
|
+
};
|
|
1537
|
+
/**
|
|
1538
|
+
* The exact, point-precision narrowing of a {@link Permission}: it admits
|
|
1539
|
+
* **one** signed RPC call — the one whose call content-hash
|
|
1540
|
+
* ({@link import("./signedObject").signedHash}`("call", signedCall)`, i.e.
|
|
1541
|
+
* `base64url(sha256(jcs({ "hubrpc-sig/v1/call": userParams + $hubrpc })))`)
|
|
1542
|
+
* equals `payloadHash`. No field-by-field comparison; pure hash equality.
|
|
1543
|
+
* This is the *same bytes the call signature commits to*, so the host can
|
|
1544
|
+
* pre-compute it at consent time exactly as the consumer will sign.
|
|
1545
|
+
*
|
|
1546
|
+
* Because it binds the entire signed payload, `callBind` pins **every**
|
|
1547
|
+
* signed field at once — method, user params, `nonce`, `signedAtMs`,
|
|
1548
|
+
* `signer`, and `interfaceHash?`. Since the bound bytes include the
|
|
1549
|
+
* per-request `nonce`, a `callBind` grant is intrinsically single-use:
|
|
1550
|
+
* the request-nonce ledger rejects any replay of the one call it names.
|
|
1551
|
+
*
|
|
1552
|
+
* Used for "Allow once" flows where the user approved a single concrete call.
|
|
1553
|
+
*/
|
|
1554
|
+
interface CallBind {
|
|
1555
|
+
/** Hash algorithm. Currently always "sha256". */
|
|
1556
|
+
alg: "sha256";
|
|
1557
|
+
/**
|
|
1558
|
+
* `signedHash("call", signedCall)` — the SHA-256 of exactly the bytes the
|
|
1559
|
+
* RPC signature commits to, for the one call this permission is bound to.
|
|
1560
|
+
*/
|
|
1561
|
+
payloadHash: Base64Sha256;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* The address of a class of calls, expressed as patterns. A
|
|
1565
|
+
* {@link CallTarget} is admitted iff every axis pattern admits it.
|
|
1566
|
+
*/
|
|
1567
|
+
interface TargetPattern {
|
|
1568
|
+
/** Delimiter `/`. `"github"` matches `"github/repos"` but not `"githubclone"`. */
|
|
1569
|
+
serviceId: Pattern;
|
|
1570
|
+
/** Delimiter `.`. `"linkrpc"` matches `"hubrpc.directory"` but not `"linkrpcx.foo"`. */
|
|
1571
|
+
interfaceId: Pattern;
|
|
1572
|
+
/**
|
|
1573
|
+
* Optional schema-version pin. When set, the call's asserted
|
|
1574
|
+
* `interfaceHash` (carried in `$hubrpc.interfaceHash`) must equal this
|
|
1575
|
+
* string. Omit to accept any version.
|
|
1576
|
+
*/
|
|
1577
|
+
interfaceHash?: string;
|
|
1578
|
+
/**
|
|
1579
|
+
* Any-of: the call's member must match at least one pattern. Empty
|
|
1580
|
+
* list matches nothing. `[{ prefix: "" }]` is the universal wildcard.
|
|
1581
|
+
*/
|
|
1582
|
+
members: Pattern[];
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* One grant clause of a {@link Capability}. Denotes a set of calls the
|
|
1586
|
+
* holder may make, described at up to three zoom levels (coarse → exact):
|
|
1587
|
+
*
|
|
1588
|
+
* - `target` — *which endpoint* (always present);
|
|
1589
|
+
* - `params` — *which argument values* (optional value allowlist);
|
|
1590
|
+
* - `callBind` — *which exact call* (optional collapse to a single call).
|
|
1591
|
+
*
|
|
1592
|
+
* The {@link canInvoke}/{@link canDelegate} flags say what the holder may
|
|
1593
|
+
* *do* with that set: invoke calls in it, and/or delegate (re-issue a
|
|
1594
|
+
* narrowed grant) onward. Both default to `false` (fail closed) — a
|
|
1595
|
+
* permission that grants neither admits nothing.
|
|
1596
|
+
*/
|
|
1597
|
+
interface Permission {
|
|
1598
|
+
/** Which endpoints this clause talks about. */
|
|
1599
|
+
target: TargetPattern;
|
|
1600
|
+
/** The holder may invoke calls in the set. Default `false`. */
|
|
1601
|
+
canInvoke?: boolean;
|
|
1602
|
+
/** The holder may delegate (re-issue a narrowed grant) of the set. Default `false`. */
|
|
1603
|
+
canDelegate?: boolean;
|
|
1604
|
+
/**
|
|
1605
|
+
* Optional value-level narrowing. Strict allowlist by top-level param
|
|
1606
|
+
* key: the call's param key-set must equal the declared set; each value
|
|
1607
|
+
* must satisfy its matcher. Omit to accept any params.
|
|
1608
|
+
*/
|
|
1609
|
+
params?: Record<string, ParamMatcher>;
|
|
1610
|
+
/** Optional collapse to a single exact signed call. See {@link CallBind}. */
|
|
1611
|
+
callBind?: CallBind;
|
|
1612
|
+
}
|
|
1613
|
+
interface Capability {
|
|
1614
|
+
/** Issuer principal (signer of this cap). */
|
|
1615
|
+
issuer: PrincipalId;
|
|
1616
|
+
/** Audience principal (holder allowed to wield it). */
|
|
1617
|
+
audience: PrincipalId;
|
|
1618
|
+
/** What the holder may do. A call is permitted by the cap iff it is permitted by **any** permission. */
|
|
1619
|
+
permissions: Permission[];
|
|
1620
|
+
/** Unix milliseconds. Absent = never expires. */
|
|
1621
|
+
expiresAtMs?: number;
|
|
1622
|
+
/**
|
|
1623
|
+
* The single parent this cap delegates from, referenced by its content
|
|
1624
|
+
* hash (`signedHash("capability", parent)`). Linearized — at most one
|
|
1625
|
+
* parent. The parent itself travels out-of-band in the call's
|
|
1626
|
+
* `$hubrpcUnsigned.capabilities` bag and is resolved by this hash.
|
|
1627
|
+
* Effective authority is the intersection over the chain: a call must be
|
|
1628
|
+
* permitted by every link. Absent = this cap is a root.
|
|
1629
|
+
*/
|
|
1630
|
+
parentHash?: Base64Sha256<Capability>;
|
|
1631
|
+
/** Per-cap distinguisher (base64url). Identifies the capability for audit/logging. */
|
|
1632
|
+
nonce: string;
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* A capability with its issuer's signature attached under
|
|
1636
|
+
* `$hubrpcSignature.capability`. It IS a {@link Capability} (the fields are
|
|
1637
|
+
* top-level) plus the signature map — no separate wrapper object. The
|
|
1638
|
+
* signature commits to `signingInput("capability", cap)`, i.e. the cap with
|
|
1639
|
+
* `$hubrpcSignature`/`$hubrpcUnsigned` stripped.
|
|
1640
|
+
*/
|
|
1641
|
+
type SignedCapability = Capability & {
|
|
1642
|
+
readonly [HUBRPC_SIGNATURE_KEY]: Signatures;
|
|
1643
|
+
};
|
|
1644
|
+
/**
|
|
1645
|
+
* Structural wire-shape check for an untrusted signed capability. This does
|
|
1646
|
+
* not establish authenticity; callers must still verify its signature and
|
|
1647
|
+
* delegation chain.
|
|
1648
|
+
*/
|
|
1649
|
+
declare function hasSignedCapabilityShape(value: unknown): value is SignedCapability;
|
|
1650
|
+
/**
|
|
1651
|
+
* The address of a concrete call — *which endpoint*, with no arguments.
|
|
1652
|
+
* This is what {@link TargetPattern} matches against. The full concrete
|
|
1653
|
+
* call (with params/nonce/signer/bytes) is `Call` in `identity/capability`.
|
|
1654
|
+
*/
|
|
1655
|
+
interface CallTarget {
|
|
1656
|
+
serviceId: string;
|
|
1657
|
+
interfaceId: string;
|
|
1658
|
+
member: string;
|
|
1659
|
+
/**
|
|
1660
|
+
* Schema-version assertion the caller stamps from its compile-time
|
|
1661
|
+
* knowledge of the interface (typed clients use `iface.schemaHash`).
|
|
1662
|
+
* Compared against `TargetPattern.interfaceHash` by the matcher. Absent
|
|
1663
|
+
* means the caller didn't pin a version — the matcher then requires
|
|
1664
|
+
* `target.interfaceHash` to also be absent.
|
|
1665
|
+
*/
|
|
1666
|
+
interfaceHash?: string;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Addressing match for a single permission: do its
|
|
1670
|
+
* `serviceId`/`interfaceId`/`interfaceHash`/`members` patterns admit
|
|
1671
|
+
* `target`?
|
|
1672
|
+
*/
|
|
1673
|
+
declare function permissionMatchesTarget(target: CallTarget, perm: Permission): boolean;
|
|
1674
|
+
/**
|
|
1675
|
+
* Strict allowlist match for {@link Permission.params}. The set of
|
|
1676
|
+
* top-level keys in the call MUST equal the declared set; values must
|
|
1677
|
+
* satisfy their matchers. Nested objects/arrays are compared as opaque
|
|
1678
|
+
* JSON values via canonical-JSON equality. Pure (no crypto).
|
|
1679
|
+
*/
|
|
1680
|
+
declare function matchParams(declared: Record<string, ParamMatcher>, actualParams: unknown): {
|
|
1681
|
+
ok: true;
|
|
1682
|
+
} | {
|
|
1683
|
+
ok: false;
|
|
1684
|
+
reason: string;
|
|
1685
|
+
};
|
|
1686
|
+
/** What a holder may do with a permission's call-set. */
|
|
1687
|
+
type Ability = "invoke" | "delegate";
|
|
1688
|
+
/**
|
|
1689
|
+
* A concrete, authenticated RPC call — the thing {@link permits} judges.
|
|
1690
|
+
* It is the call's {@link CallTarget} (which endpoint) plus the arguments
|
|
1691
|
+
* and the authenticated request metadata. Built by `verifyCall` from a
|
|
1692
|
+
* signed wire envelope.
|
|
1693
|
+
*/
|
|
1694
|
+
interface Call {
|
|
1695
|
+
/** Which endpoint is being called. */
|
|
1696
|
+
target: CallTarget;
|
|
1697
|
+
/** User params with identity envelopes stripped. */
|
|
1698
|
+
params: unknown;
|
|
1699
|
+
/** Per-request replay nonce (the unit the gate dedups on). */
|
|
1700
|
+
nonce: string;
|
|
1701
|
+
/** Unix milliseconds the call was signed. */
|
|
1702
|
+
signedAtMs: number;
|
|
1703
|
+
/** The authenticated signer — must equal the leaf capability's audience. */
|
|
1704
|
+
signer: PrincipalId;
|
|
1705
|
+
/** The call's content hash (`signedHash("call", signedCall)`) — what `callBind` compares against. */
|
|
1706
|
+
callHash: Base64Sha256;
|
|
1707
|
+
}
|
|
1708
|
+
type Verdict = {
|
|
1709
|
+
ok: true;
|
|
1710
|
+
} | {
|
|
1711
|
+
ok: false;
|
|
1712
|
+
reason: string;
|
|
1713
|
+
};
|
|
1714
|
+
/**
|
|
1715
|
+
* Does `permission` admit `call` for the given `ability`? Checks, in
|
|
1716
|
+
* order: the ability flag (`canInvoke`/`canDelegate`, both default
|
|
1717
|
+
* `false`), the target address, the optional `params` allowlist, and the
|
|
1718
|
+
* optional `callBind` hash-binding. Pure.
|
|
1719
|
+
*/
|
|
1720
|
+
declare function permissionPermits(call: Call, permission: Permission, ability: Ability): Verdict;
|
|
1721
|
+
/**
|
|
1722
|
+
* Does `cap` admit `call` for the given `ability`? True iff **any** of its
|
|
1723
|
+
* permissions does (the union over permissions). This is one *link's*
|
|
1724
|
+
* judgment; chain intersection is enforced by {@link verifyChain}.
|
|
1725
|
+
*/
|
|
1726
|
+
declare function capabilityPermits(call: Call, cap: Capability, ability: Ability): Verdict;
|
|
1727
|
+
//#endregion
|
|
1728
|
+
//#region src/protocol/methodName.d.ts
|
|
1729
|
+
/**
|
|
1730
|
+
* Parsed JSON-RPC method string. The linkrpc dialect uses `::` to
|
|
1731
|
+
* separate up to three segments:
|
|
1732
|
+
*
|
|
1733
|
+
* - `"member"` — bare (preset-bound dispatch)
|
|
1734
|
+
* - `"interfaceId::member"` — interface form (root service)
|
|
1735
|
+
* - `"::interfaceId::member"` — explicit root form
|
|
1736
|
+
* - `"serviceId::interfaceId::member"` — fully-qualified form
|
|
1737
|
+
*
|
|
1738
|
+
* The hub does not accept the `bare` form (it has no interface context
|
|
1739
|
+
* to route on). Connection-level dispatch accepts all three.
|
|
1740
|
+
*/
|
|
1741
|
+
type ParsedMethodName = {
|
|
1742
|
+
kind: "bare";
|
|
1743
|
+
member: string;
|
|
1744
|
+
} | {
|
|
1745
|
+
kind: "interface";
|
|
1746
|
+
interfaceId: string;
|
|
1747
|
+
member: string;
|
|
1748
|
+
} | {
|
|
1749
|
+
kind: "full";
|
|
1750
|
+
serviceId: string;
|
|
1751
|
+
interfaceId: string;
|
|
1752
|
+
member: string;
|
|
1753
|
+
};
|
|
1754
|
+
/**
|
|
1755
|
+
* Parse a JSON-RPC method string. Returns `undefined` if any segment is
|
|
1756
|
+
* empty or the segment count is out of range.
|
|
1757
|
+
*/
|
|
1758
|
+
declare function parseMethodName(method: string): ParsedMethodName | undefined;
|
|
1759
|
+
/**
|
|
1760
|
+
* Convert a parsed method string to a {@link CallTarget} for capability
|
|
1761
|
+
* matching. Interface-form calls are addressed to the root service —
|
|
1762
|
+
* represented as an empty `serviceId`. Throws on `bare` form (no
|
|
1763
|
+
* interface context) and on malformed input.
|
|
1764
|
+
*/
|
|
1765
|
+
declare function methodNameToTarget(method: string): CallTarget;
|
|
1766
|
+
//#endregion
|
|
1767
|
+
//#region src/protocol/linkRpcEnvelope.d.ts
|
|
1768
|
+
/**
|
|
1769
|
+
* Signed call meta. Lives under `params.$hubrpc` so it can't collide with
|
|
1770
|
+
* the user's top-level param keys. Present on signed AND unsigned calls
|
|
1771
|
+
* (the latter omit {@link CallMeta.principal} and carry no `$hubrpcSignature`).
|
|
1772
|
+
*
|
|
1773
|
+
* Included verbatim in the bytes a call signature commits to — the
|
|
1774
|
+
* signature covers `signingInput("call", { ...userParams, $hubrpc })`.
|
|
1775
|
+
*/
|
|
1776
|
+
interface CallMeta {
|
|
1777
|
+
/** Fully-qualified wire method. The verifier asserts it equals the JSON-RPC `method`. */
|
|
1778
|
+
readonly method: string;
|
|
1779
|
+
/** Replay-protection nonce (base64url). The gate dedups on this. */
|
|
1780
|
+
readonly nonce: string;
|
|
1781
|
+
/** Unix milliseconds. Skew window enforced by the verifier. */
|
|
1782
|
+
readonly signedAtMs: number;
|
|
1783
|
+
/** Identity wielding the call. Present iff signed; equals the audience of every presented cap. */
|
|
1784
|
+
readonly principal?: PrincipalId;
|
|
1785
|
+
/** Optional schema hash assertion. Matched against `TargetPattern.interfaceHash`. */
|
|
1786
|
+
readonly interfaceHash?: string;
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Extrinsic, unsigned attachments carried under `params.$hubrpcUnsigned`.
|
|
1790
|
+
* Not covered by the call signature (they aren't authored by the signer).
|
|
1791
|
+
*/
|
|
1792
|
+
interface LinkRpcUnsigned {
|
|
1793
|
+
/** Caller's presented capability bag. Parents are resolved out of this by hash. */
|
|
1794
|
+
readonly capabilities?: readonly SignedCapability[];
|
|
1795
|
+
}
|
|
1796
|
+
/** Reserved wire-key meta carried on a linkrpc call's params object. */
|
|
1797
|
+
interface LinkRpcWireMeta {
|
|
1798
|
+
readonly [HUBRPC_META_KEY]?: CallMeta;
|
|
1799
|
+
readonly [HUBRPC_SIGNATURE_KEY]?: Signatures;
|
|
1800
|
+
readonly [HUBRPC_UNSIGNED_KEY]?: LinkRpcUnsigned;
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Wire shape of `params` on a linkrpc JSON-RPC call.
|
|
1804
|
+
*
|
|
1805
|
+
* The user's params object (if any) is spread as-is, with the
|
|
1806
|
+
* {@link LinkRpcWireMeta} keys attached on top. `TUserParams` carries the
|
|
1807
|
+
* user-visible param shape; defaults to an open object. User params MUST be
|
|
1808
|
+
* a plain object — enforced at signing/verification via
|
|
1809
|
+
* {@link requireObjectParams}.
|
|
1810
|
+
*/
|
|
1811
|
+
type LinkRpcWireParams<TUserParams extends {
|
|
1812
|
+
[key: string]: JsonValue | undefined;
|
|
1813
|
+
} = {
|
|
1814
|
+
[key: string]: JsonValue | undefined;
|
|
1815
|
+
}> = TUserParams & LinkRpcWireMeta;
|
|
1816
|
+
/** JSON-RPC request whose `params` carry the linkrpc wire meta. */
|
|
1817
|
+
type LinkRpcJsonRpcRequest<TUserParams extends {
|
|
1818
|
+
[key: string]: JsonValue | undefined;
|
|
1819
|
+
} = {
|
|
1820
|
+
[key: string]: JsonValue | undefined;
|
|
1821
|
+
}> = JsonRpcRequest<LinkRpcWireParams<TUserParams>>;
|
|
1822
|
+
/** JSON-RPC notification whose `params` carry the linkrpc wire meta. */
|
|
1823
|
+
type LinkRpcJsonRpcNotification<TUserParams extends {
|
|
1824
|
+
[key: string]: JsonValue | undefined;
|
|
1825
|
+
} = {
|
|
1826
|
+
[key: string]: JsonValue | undefined;
|
|
1827
|
+
}> = JsonRpcNotification<LinkRpcWireParams<TUserParams>>;
|
|
1828
|
+
/** Either of the two linkrpc-bearing JSON-RPC message shapes. */
|
|
1829
|
+
type LinkRpcJsonRpcMessage<TUserParams extends {
|
|
1830
|
+
[key: string]: JsonValue | undefined;
|
|
1831
|
+
} = {
|
|
1832
|
+
[key: string]: JsonValue | undefined;
|
|
1833
|
+
}> = LinkRpcJsonRpcRequest<TUserParams> | LinkRpcJsonRpcNotification<TUserParams>;
|
|
1834
|
+
/**
|
|
1835
|
+
* The linkrpc signing/cap system accepts only plain-object user params (or
|
|
1836
|
+
* none). Reject arrays/primitives at the boundary so "strip `$hubrpc*`, the
|
|
1837
|
+
* rest is the signed user params" stays unambiguous.
|
|
1838
|
+
*/
|
|
1839
|
+
declare function requireObjectParams(userParams: JsonValue | undefined): Record<string, JsonValue | undefined>;
|
|
1840
|
+
/**
|
|
1841
|
+
* Return the application-authored portion of a LinkRPC params value.
|
|
1842
|
+
*
|
|
1843
|
+
* Routing and gate layers must continue forwarding the original wire params so
|
|
1844
|
+
* every chained hub can independently verify the envelope. Terminal dispatch
|
|
1845
|
+
* may use this derived value for application-schema validation without mutating
|
|
1846
|
+
* or replacing the wire message.
|
|
1847
|
+
*/
|
|
1848
|
+
declare function stripLinkRpcWireMeta(wireParams: unknown): JsonValue | undefined;
|
|
1849
|
+
//#endregion
|
|
1850
|
+
//#region src/disposable.d.ts
|
|
1851
|
+
/** Minimal disposable handle; calling `dispose` releases the resource. */
|
|
1852
|
+
interface IDisposable {
|
|
1853
|
+
dispose(): void;
|
|
1854
|
+
}
|
|
1855
|
+
//#endregion
|
|
1856
|
+
//#region src/transport/messageTransport.d.ts
|
|
1857
|
+
interface IMessageTransport<TIncoming = JsonRpcMessage, TOutgoing = JsonRpcMessage> {
|
|
1858
|
+
send(message: TOutgoing): void | Promise<void>;
|
|
1859
|
+
/**
|
|
1860
|
+
* Sets the listener for incoming messages. Setting `undefined` detaches.
|
|
1861
|
+
* The transport buffers messages received before a listener is attached
|
|
1862
|
+
* and delivers them the next tick when one is set.
|
|
1863
|
+
* Forgetting to set a listener will cause the queue to leak.
|
|
1864
|
+
*/
|
|
1865
|
+
setListener(listener: ((message: TIncoming) => void) | undefined): void;
|
|
1866
|
+
dispose(): void;
|
|
1867
|
+
}
|
|
1868
|
+
type MessageWithContext<TCtx> = JsonRpcMessage & {
|
|
1869
|
+
context: TCtx;
|
|
1870
|
+
};
|
|
1871
|
+
type MessageTransportWithContext<TCtx> = IMessageTransport<JsonRpcMessage, MessageWithContext<TCtx>>;
|
|
1872
|
+
type MessageTransportDirection = 'send' | 'receive';
|
|
1873
|
+
type MessageTransportTrace = (direction: MessageTransportDirection, message: JsonRpcMessage) => void;
|
|
1874
|
+
/**
|
|
1875
|
+
* Observe every message crossing a transport without changing its buffering or
|
|
1876
|
+
* lifecycle behavior.
|
|
1877
|
+
*/
|
|
1878
|
+
declare function traceMessageTransport(transport: IMessageTransport, trace: MessageTransportTrace): IMessageTransport;
|
|
1879
|
+
/**
|
|
1880
|
+
* Pipe two transports together: every message one receives is forwarded to
|
|
1881
|
+
* the other's `send`. Returns a disposable that detaches both listeners.
|
|
1882
|
+
*
|
|
1883
|
+
* Neither transport is disposed — the caller owns their lifecycle. This is
|
|
1884
|
+
* the building block for relays (e.g. bridging a `WindowMessageTransport`
|
|
1885
|
+
* for an iframe to a multiplexer channel).
|
|
1886
|
+
*/
|
|
1887
|
+
declare function connectTransports<T extends JsonRpcMessage>(a: IMessageTransport<T>, b: IMessageTransport<T>): IDisposable;
|
|
1888
|
+
/**
|
|
1889
|
+
* Two in-memory transports wired back-to-back. Useful for tests and for
|
|
1890
|
+
* same-process bridges.
|
|
1891
|
+
*
|
|
1892
|
+
* Generic in the per-direction payload type so asymmetric pairs (e.g. one
|
|
1893
|
+
* side sends plain `JsonRpcMessage`, the other side sends
|
|
1894
|
+
* `JsonRpcMessage & { context: Participant }`) are expressible at the type
|
|
1895
|
+
* level. At runtime both halves just hand objects through by reference.
|
|
1896
|
+
*/
|
|
1897
|
+
declare class TransportPair<TFromA = JsonRpcMessage, TFromB = JsonRpcMessage> {
|
|
1898
|
+
readonly a: IMessageTransport<TFromB, TFromA>;
|
|
1899
|
+
readonly b: IMessageTransport<TFromA, TFromB>;
|
|
1900
|
+
constructor();
|
|
1901
|
+
}
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/transport/multiplexedTransport.d.ts
|
|
1904
|
+
interface MuxEnvelope {
|
|
1905
|
+
readonly $mux: "v1";
|
|
1906
|
+
readonly ch: string;
|
|
1907
|
+
readonly m: JsonRpcMessage;
|
|
1908
|
+
}
|
|
1909
|
+
declare class MultiplexedTransport<TChannels extends Record<string, string>> implements IDisposable {
|
|
1910
|
+
static create<TChannels extends Record<string, string>>(base: IMessageTransport<MuxEnvelope, MuxEnvelope>, channels: TChannels): MultiplexedTransport<TChannels>;
|
|
1911
|
+
/** Logical transports keyed by the friendly channel names. */
|
|
1912
|
+
readonly transports: { readonly [K in keyof TChannels]: IMessageTransport; };
|
|
1913
|
+
private readonly _base;
|
|
1914
|
+
private readonly _byId;
|
|
1915
|
+
private readonly _usedIds;
|
|
1916
|
+
private _disposed;
|
|
1917
|
+
constructor(base: IMessageTransport<MuxEnvelope, MuxEnvelope>, channels: TChannels);
|
|
1918
|
+
/**
|
|
1919
|
+
* Add a logical channel after the multiplexer has started.
|
|
1920
|
+
*
|
|
1921
|
+
* Channel ids are permanently retired when disposed. This prevents a late
|
|
1922
|
+
* envelope for an old iframe from being delivered to a replacement iframe.
|
|
1923
|
+
*/
|
|
1924
|
+
addChannel(id: string): IMessageTransport;
|
|
1925
|
+
dispose(): void;
|
|
1926
|
+
private _createChannel;
|
|
1927
|
+
}
|
|
1928
|
+
//#endregion
|
|
1929
|
+
//#region src/connection/channel.d.ts
|
|
1930
|
+
declare class Channel<TInCtx = undefined, TOutCtx = undefined> {
|
|
1931
|
+
readonly sender: IRequestSender<TOutCtx>;
|
|
1932
|
+
private readonly _setHandler;
|
|
1933
|
+
private readonly _setWireObserver?;
|
|
1934
|
+
constructor(sender: IRequestSender<TOutCtx>, _setHandler: (h: IRequestHandler<TInCtx> | undefined) => void, _setWireObserver?: ((observer: WireMessageObserver | undefined) => void) | undefined);
|
|
1935
|
+
/** Bind the inbound request/notification handler. May be called before or after construction of {@link LinkRpcConnection}. */
|
|
1936
|
+
setRequestHandler(handler: IRequestHandler<TInCtx> | undefined): void;
|
|
1937
|
+
/**
|
|
1938
|
+
* Install the endpoint inspection observer at the JSON-RPC wire boundary.
|
|
1939
|
+
* Intended for {@link LinkRpcConnection}; ordinary consumers should use the
|
|
1940
|
+
* public `hubrpc.traffic` service instead.
|
|
1941
|
+
*/
|
|
1942
|
+
setWireMessageObserver(observer: WireMessageObserver | undefined): void;
|
|
1943
|
+
/**
|
|
1944
|
+
* Compose this channel with a sender-side decorator (typically the
|
|
1945
|
+
* signing layer). The decorator wraps {@link sender} only; the
|
|
1946
|
+
* {@link setRequestHandler} binding is shared with the original channel
|
|
1947
|
+
* so the receive side is bound exactly once regardless of decoration depth.
|
|
1948
|
+
*/
|
|
1949
|
+
withSender<TNewOutCtx>(decorate: (raw: IRequestSender<TOutCtx>) => IRequestSender<TNewOutCtx>): Channel<TInCtx, TNewOutCtx>;
|
|
1950
|
+
}
|
|
1951
|
+
type WireMessageDirection = 'inbound' | 'outbound';
|
|
1952
|
+
type WireMessageObserver = (direction: WireMessageDirection, message: JsonRpcMessage) => void;
|
|
1953
|
+
interface IRequestHandler<TInCtx = undefined> {
|
|
1954
|
+
handleRequest(call: IncomingCall<TInCtx>): Promise<Result>;
|
|
1955
|
+
handleNotification(call: IncomingCall<TInCtx>): void;
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* Inbound call observed by an {@link IRequestHandler}. `TInCtx` is the
|
|
1959
|
+
* per-call out-of-band context the transport attached on the receiving
|
|
1960
|
+
* side (e.g. the hub stamps `Participant` on overlay-bound messages).
|
|
1961
|
+
* For wire transports it's `undefined`.
|
|
1962
|
+
*/
|
|
1963
|
+
interface IncomingCall<TInCtx = undefined> {
|
|
1964
|
+
method: string;
|
|
1965
|
+
params: JsonValue | undefined;
|
|
1966
|
+
/** Out-of-band context attached by the transport, if any. */
|
|
1967
|
+
context: TInCtx;
|
|
1968
|
+
/**
|
|
1969
|
+
* Per-call streaming handle. Both directions live on this object,
|
|
1970
|
+
* scoped to the request's lifetime. The inbound listener is auto-
|
|
1971
|
+
* detached when the handler's response settles. For notifications
|
|
1972
|
+
* (no request id) the handle is a no-op stub.
|
|
1973
|
+
*/
|
|
1974
|
+
stream: IncomingStream;
|
|
1975
|
+
/**
|
|
1976
|
+
* Aborts when the caller cancels this in-flight request (a
|
|
1977
|
+
* `toCallee` `cancel` control), or when the hub cancels it on the
|
|
1978
|
+
* caller's behalf (caller disconnect / idle timeout). The handler
|
|
1979
|
+
* should observe this and settle promptly — typically by throwing,
|
|
1980
|
+
* which surfaces as a `cancelled` error response. For notifications
|
|
1981
|
+
* this never aborts.
|
|
1982
|
+
*/
|
|
1983
|
+
signal: AbortSignal;
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Per-call streaming handle attached to {@link IncomingCall.stream}.
|
|
1987
|
+
* Scoped to a single in-flight request; the channel automatically
|
|
1988
|
+
* detaches the inbound listener when the handler's response settles.
|
|
1989
|
+
*
|
|
1990
|
+
* For notifications (which have no request id), `requestId` is
|
|
1991
|
+
* `undefined`, `send` is a no-op, and `onMessage` is a no-op.
|
|
1992
|
+
*/
|
|
1993
|
+
interface IncomingStream {
|
|
1994
|
+
/** Emit a server→client stream notification correlated with this call. */
|
|
1995
|
+
send(payload: JsonValue): Promise<void>;
|
|
1996
|
+
/**
|
|
1997
|
+
* Register a listener for client→server stream messages correlated
|
|
1998
|
+
* with this call. Pass `undefined` to detach. Replaces any prior
|
|
1999
|
+
* listener. The channel detaches the listener automatically when
|
|
2000
|
+
* the handler's response settles.
|
|
2001
|
+
*/
|
|
2002
|
+
onMessage(listener: ((payload: JsonValue) => void) | undefined): void;
|
|
2003
|
+
/**
|
|
2004
|
+
* Liveness probe toward the caller: emit a `toCaller` ping and
|
|
2005
|
+
* resolve once the caller's `pong` (matching nonce) returns. Rejects
|
|
2006
|
+
* if the request settles first. Independent of the channel's
|
|
2007
|
+
* automatic keepalive ping.
|
|
2008
|
+
*/
|
|
2009
|
+
ping(): Promise<void>;
|
|
2010
|
+
}
|
|
2011
|
+
type Result = {
|
|
2012
|
+
result: JsonValue;
|
|
2013
|
+
} | {
|
|
2014
|
+
error: {
|
|
2015
|
+
code: number;
|
|
2016
|
+
message: string;
|
|
2017
|
+
data?: JsonValue;
|
|
2018
|
+
};
|
|
2019
|
+
};
|
|
2020
|
+
/**
|
|
2021
|
+
* What callers use to send. The {@link Channel} factory binds an
|
|
2022
|
+
* {@link IRequestHandler} at construction (immutable for the channel's
|
|
2023
|
+
* lifetime) and hands the caller back an `IRequestSender`.
|
|
2024
|
+
*
|
|
2025
|
+
* `TOutCtx` is the per-call context bag this sender understands. For
|
|
2026
|
+
* the base {@link JsonRpcChannel} it's `undefined` (the channel
|
|
2027
|
+
* doesn't read any overrides). Decorators like `SigningSender`
|
|
2028
|
+
* parametrise it on their own ctx shape.
|
|
2029
|
+
*/
|
|
2030
|
+
interface IRequestSender<TOutCtx = undefined> {
|
|
2031
|
+
sendRequest(method: string, params: JsonValue | undefined, opts?: SendOpts<TOutCtx>): Promise<JsonValue>;
|
|
2032
|
+
sendNotification(method: string, params: JsonValue | undefined, opts?: SendOpts<TOutCtx>): Promise<void>;
|
|
2033
|
+
/**
|
|
2034
|
+
* Issue a request that participates in the streaming sub-protocol.
|
|
2035
|
+
* Returns synchronously with the wire request id, the result
|
|
2036
|
+
* promise, and a `send` for emitting client→server stream messages.
|
|
2037
|
+
*/
|
|
2038
|
+
sendRequestWithStream(method: string, params: JsonValue | undefined, opts?: StreamSendOpts<TOutCtx>): RawStreamingCall;
|
|
2039
|
+
close(): void;
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Handle for a request issued via
|
|
2043
|
+
* {@link IRequestSender.sendRequestWithStream}. `send` emits
|
|
2044
|
+
* client→server stream messages correlated with this call. `result`
|
|
2045
|
+
* resolves with the call's response.
|
|
2046
|
+
*/
|
|
2047
|
+
interface RawStreamingCall {
|
|
2048
|
+
readonly result: Promise<JsonValue>;
|
|
2049
|
+
/** Emit a client→server (`toCallee`) stream message correlated with this call. */
|
|
2050
|
+
send(payload: JsonValue): void;
|
|
2051
|
+
/**
|
|
2052
|
+
* Ask the callee to abort this in-flight request (a `toCallee`
|
|
2053
|
+
* `cancel` control). Advisory: the request settles via its normal
|
|
2054
|
+
* response (typically a `cancelled` error). `reason` is an open-set
|
|
2055
|
+
* diagnostic string (see `StreamControlReason`).
|
|
2056
|
+
*/
|
|
2057
|
+
cancel(reason?: string): void;
|
|
2058
|
+
/**
|
|
2059
|
+
* Stop tracking this request locally and reject {@link result}. This does
|
|
2060
|
+
* not notify the callee; call {@link cancel} first when remote work should
|
|
2061
|
+
* also be cancelled. Safe to call after the request has already settled.
|
|
2062
|
+
*/
|
|
2063
|
+
dispose?(reason?: string): void;
|
|
2064
|
+
/**
|
|
2065
|
+
* Liveness probe toward the callee: emit a `toCallee` ping and
|
|
2066
|
+
* resolve once the callee's `pong` (matching nonce) returns. Rejects
|
|
2067
|
+
* if the request settles first. Independent of the channel's
|
|
2068
|
+
* automatic keepalive ping.
|
|
2069
|
+
*/
|
|
2070
|
+
ping(): Promise<void>;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Per-send options.
|
|
2074
|
+
*
|
|
2075
|
+
* `interfaceHash` is interface-level call metadata: the schema hash of
|
|
2076
|
+
* the interface a typed proxy is calling. It is independent of
|
|
2077
|
+
* `TOutCtx` — the connection stamps it from the interface definition,
|
|
2078
|
+
* the base {@link JsonRpcChannel} ignores it, and signing decorators
|
|
2079
|
+
* bake it into the `$hubrpc` envelope.
|
|
2080
|
+
*
|
|
2081
|
+
* `ctx` is the sender's `TOutCtx`-typed override / extension bag for
|
|
2082
|
+
* per-call decorator overrides (e.g. `signerOverride`, `capsOverride`).
|
|
2083
|
+
* The base {@link JsonRpcChannel} ignores `ctx` entirely; decorators
|
|
2084
|
+
* like `SigningSender` interpret it.
|
|
2085
|
+
*/
|
|
2086
|
+
interface SendOpts<TOutCtx = undefined> {
|
|
2087
|
+
readonly ctx?: TOutCtx;
|
|
2088
|
+
/** Interface schema hash for this call (interface-level metadata). */
|
|
2089
|
+
readonly interfaceHash?: string;
|
|
2090
|
+
}
|
|
2091
|
+
interface StreamSendOpts<TOutCtx = undefined> extends SendOpts<TOutCtx> {
|
|
2092
|
+
readonly onStreamMessage?: (payload: JsonValue) => void;
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Convenience: the transport type a {@link JsonRpcChannel} expects.
|
|
2096
|
+
* Outbound is plain `JsonRpcMessage` — the base channel does not
|
|
2097
|
+
* attach context to outgoing wire messages.
|
|
2098
|
+
*/
|
|
2099
|
+
type ChannelTransport<TInCtx = undefined> = IMessageTransport<MessageWithCtx<TInCtx>>;
|
|
2100
|
+
/**
|
|
2101
|
+
* Type of incoming messages on the channel's transport. With
|
|
2102
|
+
* `TInCtx = undefined` (default) this is just `JsonRpcMessage`. With a
|
|
2103
|
+
* concrete `TInCtx`, the transport carries a `context` field alongside
|
|
2104
|
+
* the message — out-of-band, never sent over a wire, set by the
|
|
2105
|
+
* in-process producer.
|
|
2106
|
+
*/
|
|
2107
|
+
type MessageWithCtx<TInCtx> = [TInCtx] extends [undefined] ? JsonRpcMessage : JsonRpcMessage & {
|
|
2108
|
+
context: TInCtx;
|
|
2109
|
+
};
|
|
2110
|
+
declare class RpcError extends Error {
|
|
2111
|
+
readonly code: number;
|
|
2112
|
+
readonly data?: JsonValue | undefined;
|
|
2113
|
+
constructor(message: string, code: number, data?: JsonValue | undefined);
|
|
2114
|
+
}
|
|
2115
|
+
//#endregion
|
|
2116
|
+
//#region src/connection/bareInterfaceTarget.d.ts
|
|
2117
|
+
/** A metadata-free foreign-protocol interface and its wire-method prefix. */
|
|
2118
|
+
interface BareInterfaceTarget<TDef extends InterfaceDefinition<any>> {
|
|
2119
|
+
readonly mode: 'bare';
|
|
2120
|
+
readonly interface: TDef;
|
|
2121
|
+
readonly prefix: string;
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* Bundle an interface definition with metadata-free foreign-protocol
|
|
2125
|
+
* addressing for use with `connection.get(target)`.
|
|
2126
|
+
*/
|
|
2127
|
+
declare function bareInterfaceTarget<TDef extends InterfaceDefinition<any>>(iface: TDef, options?: {
|
|
2128
|
+
readonly prefix?: string;
|
|
2129
|
+
}): BareInterfaceTarget<TDef>;
|
|
2130
|
+
//#endregion
|
|
2131
|
+
//#region src/connection/channelConnector.d.ts
|
|
2132
|
+
/**
|
|
2133
|
+
* A raw channel that signals when it closes (and, when possible, can be torn
|
|
2134
|
+
* down). Both {@link openHubChannel}'s `HubChannel` and `openStdioChannel`'s
|
|
2135
|
+
* `StdioChannel` satisfy this shape.
|
|
2136
|
+
*/
|
|
2137
|
+
type ConnectableChannel = Channel<undefined, unknown> & {
|
|
2138
|
+
/**
|
|
2139
|
+
* Fires once when the channel closes. Returns a disposable to unsubscribe.
|
|
2140
|
+
*
|
|
2141
|
+
* Ordering contract: when a close causes a sending method (e.g. a request
|
|
2142
|
+
* or notification) to reject/throw, {@link onClose} must fire *before* that
|
|
2143
|
+
* rejection surfaces to the caller. This lets consumers reliably tell a
|
|
2144
|
+
* close-induced failure (the channel is already observably closed) apart
|
|
2145
|
+
* from a genuine error (the channel is still open). See
|
|
2146
|
+
* {@link ChannelConnector.keepConnected}.
|
|
2147
|
+
*/
|
|
2148
|
+
onClose(listener: () => void): IDisposable;
|
|
2149
|
+
/** Tear the channel down. Optional — stdio channels have no explicit close. */
|
|
2150
|
+
close?(): void;
|
|
2151
|
+
};
|
|
2152
|
+
/** Handle returned by {@link ChannelConnector.keepConnected}. */
|
|
2153
|
+
interface KeepConnectedHandle {
|
|
2154
|
+
/**
|
|
2155
|
+
* Resolves when the loop exits: the channel closed and the connector does
|
|
2156
|
+
* not redial, or {@link stop} (or the supplied signal) fired.
|
|
2157
|
+
*/
|
|
2158
|
+
readonly done: Promise<void>;
|
|
2159
|
+
/** Stop redialing and close the current channel (if any). */
|
|
2160
|
+
stop(): void;
|
|
2161
|
+
}
|
|
2162
|
+
/** Callback run on every (re)connect. The channel is the raw, unsigned channel. */
|
|
2163
|
+
type OnChannelConnect<T extends ConnectableChannel> = (ctx: {
|
|
2164
|
+
channel: T;
|
|
2165
|
+
}) => void | Promise<void>;
|
|
2166
|
+
interface ExpBackoffOptions {
|
|
2167
|
+
readonly initialBackoffMs?: number;
|
|
2168
|
+
readonly maxBackoffMs?: number;
|
|
2169
|
+
}
|
|
2170
|
+
/**
|
|
2171
|
+
* Drives a connect / (re)connect loop over a raw {@link ConnectableChannel}.
|
|
2172
|
+
*
|
|
2173
|
+
* Unlike connection-level helpers, this works at the channel layer: the
|
|
2174
|
+
* caller composes identity / signing on top of the channel handed to
|
|
2175
|
+
* {@link keepConnected} (e.g. `SigningSender.wrapChannel(channel, { principal })`).
|
|
2176
|
+
*
|
|
2177
|
+
* Construct via {@link ChannelConnector.once} (a single, already-open channel
|
|
2178
|
+
* that never redials) or {@link ChannelConnector.expBackoff} (re-open via a
|
|
2179
|
+
* factory with exponential backoff after each close).
|
|
2180
|
+
*/
|
|
2181
|
+
declare class ChannelConnector<T extends ConnectableChannel> {
|
|
2182
|
+
private readonly _open;
|
|
2183
|
+
private readonly _redial;
|
|
2184
|
+
private readonly _initialBackoffMs;
|
|
2185
|
+
private readonly _maxBackoffMs;
|
|
2186
|
+
private constructor();
|
|
2187
|
+
/**
|
|
2188
|
+
* A connector over a single channel (or a promise of one). Never redials;
|
|
2189
|
+
* the loop ends when the channel closes or {@link KeepConnectedHandle.stop}
|
|
2190
|
+
* fires.
|
|
2191
|
+
*/
|
|
2192
|
+
static once<T extends ConnectableChannel>(channel: T | Promise<T>): ChannelConnector<T>;
|
|
2193
|
+
/**
|
|
2194
|
+
* A redialing connector: `open` is called once per attempt, and the loop
|
|
2195
|
+
* reconnects with exponential backoff after each close (or failed open).
|
|
2196
|
+
*/
|
|
2197
|
+
static expBackoff<T extends ConnectableChannel>(open: () => Promise<T>, opts?: ExpBackoffOptions): ChannelConnector<T>;
|
|
2198
|
+
/**
|
|
2199
|
+
* Run `onConnect` on every (re)connect with the freshly opened channel.
|
|
2200
|
+
* The callback typically wraps the channel with signing and registers
|
|
2201
|
+
* handlers. Returns once the loop exits (see {@link KeepConnectedHandle}).
|
|
2202
|
+
*/
|
|
2203
|
+
keepConnected(onConnect: OnChannelConnect<T>, opts?: {
|
|
2204
|
+
signal?: AbortSignal;
|
|
2205
|
+
}): KeepConnectedHandle;
|
|
2206
|
+
}
|
|
2207
|
+
//#endregion
|
|
2208
|
+
//#region src/connection/jsonRpcChannel.d.ts
|
|
2209
|
+
/**
|
|
2210
|
+
* Minimal JSON-RPC 2.0 channel: correlates requests with responses,
|
|
2211
|
+
* dispatches incoming requests/notifications to a handler.
|
|
2212
|
+
*
|
|
2213
|
+
* Construct via {@link JsonRpcChannel.create}: it returns a
|
|
2214
|
+
* {@link Channel} factory which materialises the live channel once a
|
|
2215
|
+
* handler is supplied via `.connect(handler)`. The handler is fixed for
|
|
2216
|
+
* the channel's lifetime — no setter, no mid-flight swap.
|
|
2217
|
+
*
|
|
2218
|
+
* The outbound `TOutCtx` of the produced sender is `unknown` — this
|
|
2219
|
+
* channel ignores per-call ctx entirely. Decorators (e.g.
|
|
2220
|
+
* `SigningSender`) lift it to a concrete shape.
|
|
2221
|
+
*/
|
|
2222
|
+
declare class JsonRpcChannel<TInCtx = undefined> implements IRequestSender<unknown> {
|
|
2223
|
+
private readonly _transport;
|
|
2224
|
+
/**
|
|
2225
|
+
* Wrap a transport in a {@link Channel}. The channel's sender is live
|
|
2226
|
+
* immediately; call {@link Channel.setRequestHandler} (or pass the channel
|
|
2227
|
+
* to {@link LinkRpcConnection}) to bind the inbound handler.
|
|
2228
|
+
*/
|
|
2229
|
+
static create<TInCtx = undefined>(transport: ChannelTransport<TInCtx>): Channel<TInCtx, unknown>;
|
|
2230
|
+
/** Wrap a transport and retain an explicit lifecycle hook for its owner. */
|
|
2231
|
+
static createWithClose<TInCtx = undefined>(transport: ChannelTransport<TInCtx>): {
|
|
2232
|
+
channel: Channel<TInCtx, unknown>;
|
|
2233
|
+
close: () => void;
|
|
2234
|
+
};
|
|
2235
|
+
private _nextId;
|
|
2236
|
+
private readonly _pending;
|
|
2237
|
+
/**
|
|
2238
|
+
* Per-request callbacks invoked when a {@link STREAM_METHOD}
|
|
2239
|
+
* notification arrives with a matching `requestId`. Used in both
|
|
2240
|
+
* directions: outgoing-request callers register here keyed by the
|
|
2241
|
+
* id they sent; per-call incoming-request stream handles register
|
|
2242
|
+
* keyed by the id they observed. Entries are removed when the
|
|
2243
|
+
* corresponding request completes (response received for outgoing,
|
|
2244
|
+
* response sent for incoming).
|
|
2245
|
+
*/
|
|
2246
|
+
private readonly _streamListeners;
|
|
2247
|
+
/**
|
|
2248
|
+
* Per-incoming-request handlers for reserved `control` messages
|
|
2249
|
+
* (cancel / ping / pong) arriving as stream notifications. Keyed
|
|
2250
|
+
* by the id observed; removed when the request settles.
|
|
2251
|
+
*/
|
|
2252
|
+
private readonly _streamControl;
|
|
2253
|
+
private readonly _incomingAborts;
|
|
2254
|
+
private _handler;
|
|
2255
|
+
private _wireObserver;
|
|
2256
|
+
private _closeError;
|
|
2257
|
+
setRequestHandler(handler: IRequestHandler<TInCtx> | undefined): void;
|
|
2258
|
+
setWireMessageObserver(observer: WireMessageObserver | undefined): void;
|
|
2259
|
+
private constructor();
|
|
2260
|
+
sendRequest(method: string, params: JsonValue | undefined, opts?: SendOpts<unknown>): Promise<JsonValue>;
|
|
2261
|
+
sendNotification(method: string, params: JsonValue | undefined, opts?: SendOpts<unknown>): Promise<void>;
|
|
2262
|
+
sendRequestWithStream(method: string, params: JsonValue | undefined, opts?: StreamSendOpts<unknown>): RawStreamingCall;
|
|
2263
|
+
/**
|
|
2264
|
+
* Build the symmetric ping/pong machinery for one in-flight request.
|
|
2265
|
+
*
|
|
2266
|
+
* `outboundDir` is the direction *this* side emits controls in
|
|
2267
|
+
* (`toCallee` for the caller, `toCaller` for the callee). A received
|
|
2268
|
+
* ping is answered with a pong in that same direction, echoing the
|
|
2269
|
+
* ping's nonce; a received pong resolves the matching outstanding
|
|
2270
|
+
* {@link ping} probe.
|
|
2271
|
+
*/
|
|
2272
|
+
private _makePinger;
|
|
2273
|
+
/**
|
|
2274
|
+
* Emit a stream notification ({@link STREAM_METHOD}) associated with
|
|
2275
|
+
* an in-flight request. Internal: outgoing-side callers reach this
|
|
2276
|
+
* via {@link RawStreamingCall.send} / `cancel`; incoming-side handlers
|
|
2277
|
+
* reach it via {@link IncomingStream.send}.
|
|
2278
|
+
*/
|
|
2279
|
+
private _sendStream;
|
|
2280
|
+
close(): void;
|
|
2281
|
+
private _throwIfClosed;
|
|
2282
|
+
private _onMessage;
|
|
2283
|
+
private _handleStreamNotification;
|
|
2284
|
+
private _handleRequest;
|
|
2285
|
+
private _handleNotification;
|
|
2286
|
+
private _observeInbound;
|
|
2287
|
+
private _observeOutbound;
|
|
2288
|
+
private _observeSendFailure;
|
|
2289
|
+
}
|
|
2290
|
+
//#endregion
|
|
2291
|
+
//#region src/connection/requestTimeout.d.ts
|
|
2292
|
+
declare const DEFAULT_RPC_TIMEOUT_MS = 5000;
|
|
2293
|
+
interface CancellableRequest<T> extends Promise<T> {
|
|
2294
|
+
cancel(reason?: string): void | Promise<void>;
|
|
2295
|
+
dispose?(reason?: string): void;
|
|
2296
|
+
}
|
|
2297
|
+
declare function withRpcTimeout<T>(request: CancellableRequest<T>, target: string, timeoutMs?: number): Promise<T>;
|
|
2298
|
+
//#endregion
|
|
2299
|
+
//#region src/connection/endpointUri.d.ts
|
|
2300
|
+
/**
|
|
2301
|
+
* A strict, RFC-3986 URI vocabulary for "where the linkrpc server lives, and
|
|
2302
|
+
* how to reach (or start) it". Every endpoint round-trips through
|
|
2303
|
+
* {@link parseEndpointUri} / {@link formatEndpointUri} and is a valid `new URL()`
|
|
2304
|
+
* — safe to put in env vars, logs, and config.
|
|
2305
|
+
*
|
|
2306
|
+
* Supported schemes:
|
|
2307
|
+
* - `unix:/path/to.sock?token=…` → {@link SocketEndpoint}
|
|
2308
|
+
* - `npipe://./pipe/name?token=…` → {@link SocketEndpoint} (Windows)
|
|
2309
|
+
* - `ws://host:port?token=…` / `wss:…` → {@link WsEndpoint} (LinkRPC handshake)
|
|
2310
|
+
* - `ws-no-init://host:port?…` → {@link WsNoInitEndpoint}
|
|
2311
|
+
* - `cmd-stdio:?command=…` / `…?argv=…` → {@link CmdStdioEndpoint}
|
|
2312
|
+
* - `cmd:?command=…` / `…?argv=…` → {@link CmdEnvEndpoint}
|
|
2313
|
+
*
|
|
2314
|
+
* The command payload is `{ command: string } | { argv: string[] }`:
|
|
2315
|
+
* - `?command=node%20server.js` — one verbatim string, split by the OS shell.
|
|
2316
|
+
* - `?argv=node&argv=server.js` — repeated `argv` params, structure-preserving.
|
|
2317
|
+
*
|
|
2318
|
+
* A bare string with no scheme (legacy `LINKRPC_ENDPOINT`) is auto-detected: a
|
|
2319
|
+
* `ws://`/`wss://` URL stays WebSocket, anything else is a socket path.
|
|
2320
|
+
*/
|
|
2321
|
+
/** Verbatim command line (`{ command }`) or pre-split argv (`{ argv }`). */
|
|
2322
|
+
type EndpointCommand = {
|
|
2323
|
+
readonly command: string;
|
|
2324
|
+
} | {
|
|
2325
|
+
readonly argv: readonly string[];
|
|
2326
|
+
};
|
|
2327
|
+
/** Named pipe / unix-domain-socket the server already listens on. */
|
|
2328
|
+
interface SocketEndpoint {
|
|
2329
|
+
readonly kind: 'socket';
|
|
2330
|
+
readonly path: string;
|
|
2331
|
+
readonly token?: string;
|
|
2332
|
+
/**
|
|
2333
|
+
* Present on sockets created by `hub connect`. Local framing still uses the
|
|
2334
|
+
* LinkRPC transport handshake; this describes whether forwarded application
|
|
2335
|
+
* calls target a LinkRPC or plain JSON-RPC peer.
|
|
2336
|
+
*/
|
|
2337
|
+
readonly brokerMode?: 'linkrpc' | 'raw';
|
|
2338
|
+
}
|
|
2339
|
+
/** A running WebSocket hub; `token` rides in the `hubrpc::initialize` handshake. */
|
|
2340
|
+
interface WsEndpoint {
|
|
2341
|
+
readonly kind: 'ws';
|
|
2342
|
+
readonly url: string;
|
|
2343
|
+
readonly token?: string;
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* A running plain-JSON-RPC WebSocket endpoint. Unlike {@link WsEndpoint}, CLI
|
|
2347
|
+
* consumers use it without the `hubrpc::initialize` handshake or LinkRPC signing.
|
|
2348
|
+
* Query parameters are preserved verbatim for protocols that authenticate
|
|
2349
|
+
* during the WebSocket upgrade (for example AHP's `tkn` parameter).
|
|
2350
|
+
*/
|
|
2351
|
+
interface WsNoInitEndpoint {
|
|
2352
|
+
readonly kind: 'ws-no-init';
|
|
2353
|
+
/** Actual `ws://` URL passed to the WebSocket constructor. */
|
|
2354
|
+
readonly url: string;
|
|
2355
|
+
}
|
|
2356
|
+
/** Spawn a child and talk linkrpc over its stdin/stdout. */
|
|
2357
|
+
interface CmdStdioEndpoint {
|
|
2358
|
+
readonly kind: 'cmd-stdio';
|
|
2359
|
+
readonly command: EndpointCommand;
|
|
2360
|
+
/** Extra environment variables injected into the spawned child. */
|
|
2361
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
2362
|
+
/** Working directory for the spawned child. */
|
|
2363
|
+
readonly cwd?: string;
|
|
2364
|
+
}
|
|
2365
|
+
/**
|
|
2366
|
+
* Spawn a child against a freshly-started *local hub*: the parent listens on a
|
|
2367
|
+
* private socket, hands the child its address + token via `LINKRPC_ENDPOINT` /
|
|
2368
|
+
* `LINKRPC_TOKEN`, and the child dials in as a hub participant (registering its
|
|
2369
|
+
* services), exactly as it would against a remote hub.
|
|
2370
|
+
*/
|
|
2371
|
+
interface CmdEnvEndpoint {
|
|
2372
|
+
readonly kind: 'cmd-env';
|
|
2373
|
+
readonly command: EndpointCommand;
|
|
2374
|
+
/**
|
|
2375
|
+
* When set, the local hub provisions (or reuses) a *persistent* managed
|
|
2376
|
+
* identity under this slot id, so the child's HPKE wrap/unwrap keys survive
|
|
2377
|
+
* across runs (sealed archives re-open).
|
|
2378
|
+
*/
|
|
2379
|
+
readonly provisionSlot?: string;
|
|
2380
|
+
/** Extra environment variables injected into the spawned child. */
|
|
2381
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
2382
|
+
/** Working directory for the spawned child. */
|
|
2383
|
+
readonly cwd?: string;
|
|
2384
|
+
}
|
|
2385
|
+
type ResolvedEndpoint = SocketEndpoint | WsEndpoint | WsNoInitEndpoint | CmdStdioEndpoint | CmdEnvEndpoint;
|
|
2386
|
+
/**
|
|
2387
|
+
* Parse a strict endpoint URI into an {@link ResolvedEndpoint}. Throws on an
|
|
2388
|
+
* unknown scheme or a malformed command endpoint. A bare (scheme-less) string
|
|
2389
|
+
* is auto-detected as `ws`/`wss` URL or a socket path.
|
|
2390
|
+
*/
|
|
2391
|
+
declare function parseEndpointUri(uri: string): ResolvedEndpoint;
|
|
2392
|
+
interface FormatEndpointOptions {
|
|
2393
|
+
/** Emit the real token instead of redacting it. Default: redact. */
|
|
2394
|
+
readonly revealToken?: boolean;
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Render an {@link ResolvedEndpoint} back to a canonical strict URI. The token is
|
|
2398
|
+
* redacted (`***`) unless `revealToken` is set, so the result is paste-safe for
|
|
2399
|
+
* logs. Round-trips with {@link parseEndpointUri} when `revealToken` is true.
|
|
2400
|
+
*/
|
|
2401
|
+
declare function formatEndpointUri(spec: ResolvedEndpoint, options?: FormatEndpointOptions): string;
|
|
2402
|
+
/** True for endpoints that connect to existing, possibly-remote infrastructure. */
|
|
2403
|
+
declare function isHubEndpoint(spec: ResolvedEndpoint): spec is SocketEndpoint | WsEndpoint;
|
|
2404
|
+
//#endregion
|
|
2405
|
+
//#region src/identity/identity.d.ts
|
|
2406
|
+
/** JSON-serializable form of a {@link KeypairSigningIdentity} (base64url keys). */
|
|
2407
|
+
interface SerializedKeypairSigningIdentity {
|
|
2408
|
+
readonly privateKey: string;
|
|
2409
|
+
readonly publicKey: string;
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* The public face of a signing identity: its stable {@link PrincipalId} name
|
|
2413
|
+
* and the {@link KeyId} its signatures are stamped with. In Phase 1 (perpetual
|
|
2414
|
+
* identities) the keyId is the genesis key embedded in the principal, so it is
|
|
2415
|
+
* derived by stripping the `id:` prefix.
|
|
2416
|
+
*/
|
|
2417
|
+
declare class PublicSigningIdentity {
|
|
2418
|
+
readonly principal: PrincipalId;
|
|
2419
|
+
constructor(principal: PrincipalId);
|
|
2420
|
+
/** The {@link KeyId} this identity signs with (its genesis key, in Phase 1). */
|
|
2421
|
+
get keyId(): KeyId;
|
|
2422
|
+
}
|
|
2423
|
+
interface SigningIdentity {
|
|
2424
|
+
readonly publicSigningIdentity: PublicSigningIdentity;
|
|
2425
|
+
sign(bytes: Uint8Array): Promise<Uint8Array>;
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* {@link SigningIdentity} backed by an in-process keypair. For the common
|
|
2429
|
+
* case where the signing material lives in memory. For external HSM-style
|
|
2430
|
+
* backends, implement {@link SigningIdentity} directly.
|
|
2431
|
+
*/
|
|
2432
|
+
declare class KeypairSigningIdentity implements SigningIdentity {
|
|
2433
|
+
private readonly _privateKey;
|
|
2434
|
+
readonly publicSigningIdentity: PublicSigningIdentity;
|
|
2435
|
+
constructor(principal: PrincipalId, _privateKey: PrivateKey);
|
|
2436
|
+
sign(message: Uint8Array): Promise<Signature>;
|
|
2437
|
+
static fromKeypair(keypair: Keypair): KeypairSigningIdentity;
|
|
2438
|
+
/** Generate a fresh signing identity (Ed25519 keypair). */
|
|
2439
|
+
static generateNew(): Promise<KeypairSigningIdentity>;
|
|
2440
|
+
/** Restore an identity from its {@link toJson} form. */
|
|
2441
|
+
static fromJson(json: SerializedKeypairSigningIdentity): KeypairSigningIdentity;
|
|
2442
|
+
/** Serialize the keypair to a JSON-friendly form (base64url keys). */
|
|
2443
|
+
toJson(): SerializedKeypairSigningIdentity;
|
|
2444
|
+
}
|
|
2445
|
+
declare class PublicWrappingIdentity {
|
|
2446
|
+
readonly wrapPublicKey: Uint8Array;
|
|
2447
|
+
constructor(wrapPublicKey: Uint8Array);
|
|
2448
|
+
}
|
|
2449
|
+
interface WrappingIdentity {
|
|
2450
|
+
readonly publicWrappingIdentity: PublicWrappingIdentity;
|
|
2451
|
+
wrap(domain: string, bytes: Uint8Array): Promise<Uint8Array>;
|
|
2452
|
+
unwrap(domain: string, blob: Uint8Array): Promise<Uint8Array>;
|
|
2453
|
+
}
|
|
2454
|
+
interface Identity extends SigningIdentity, WrappingIdentity {
|
|
2455
|
+
/** The identity's stable {@link PrincipalId} name. */
|
|
2456
|
+
readonly principal: PrincipalId;
|
|
2457
|
+
/** @deprecated */
|
|
2458
|
+
readonly wrapPublicKey: Uint8Array;
|
|
2459
|
+
}
|
|
2460
|
+
/**
|
|
2461
|
+
* Full {@link Identity} backed by in-process Ed25519 (signing) and X25519
|
|
2462
|
+
* (wrapping) keypairs. The local counterpart to a keystore/executor-backed
|
|
2463
|
+
* identity — used by self-managed principals that hold their own key
|
|
2464
|
+
* material (e.g. loaded from disk). For external HSM-style backends,
|
|
2465
|
+
* implement {@link Identity} directly.
|
|
2466
|
+
*/
|
|
2467
|
+
declare class KeypairIdentity implements Identity {
|
|
2468
|
+
private readonly _privateKey;
|
|
2469
|
+
private readonly _wrap;
|
|
2470
|
+
readonly principal: PrincipalId;
|
|
2471
|
+
readonly wrapPublicKey: Uint8Array;
|
|
2472
|
+
readonly publicSigningIdentity: PublicSigningIdentity;
|
|
2473
|
+
readonly publicWrappingIdentity: PublicWrappingIdentity;
|
|
2474
|
+
constructor(principal: PrincipalId, _privateKey: PrivateKey, _wrap: X25519Keypair);
|
|
2475
|
+
sign(message: Uint8Array): Promise<Signature>;
|
|
2476
|
+
wrap(domain: string, bytes: Uint8Array): Promise<Uint8Array>;
|
|
2477
|
+
unwrap(domain: string, blob: Uint8Array): Promise<Uint8Array>;
|
|
2478
|
+
/** Generate a fresh identity (Ed25519 + X25519 keypairs). */
|
|
2479
|
+
static generate(): Promise<KeypairIdentity>;
|
|
2480
|
+
}
|
|
2481
|
+
//#endregion
|
|
2482
|
+
//#region src/identity/capability.d.ts
|
|
2483
|
+
/**
|
|
2484
|
+
* Pure freshness check for a single capability link: `true` when the cap never
|
|
2485
|
+
* expires, or its `expiresAtMs` is still in the future at `now + marginMs`.
|
|
2486
|
+
*
|
|
2487
|
+
* Mirrors the gate's expiry rule in {@link verifyChain}'s link check
|
|
2488
|
+
* (`expiresAtMs < nowMs` ⇒ expired) so a producer can decide, *before signing*,
|
|
2489
|
+
* whether to re-acquire a cap rather than attach one the gate will reject as
|
|
2490
|
+
* `expired`. The `marginMs` safety window absorbs in-flight transit time and
|
|
2491
|
+
* client/hub clock skew — pass the call's `signedAtMs` as `now` so the check
|
|
2492
|
+
* evaluates expiry against the same instant baked into the envelope.
|
|
2493
|
+
*
|
|
2494
|
+
* This checks ONE link. Effective authority requires every link in a
|
|
2495
|
+
* delegation chain to be unexpired, so a producer attaching a bag should apply
|
|
2496
|
+
* this to every cap it would attach (see {@link capBagFreshAt}).
|
|
2497
|
+
*/
|
|
2498
|
+
declare function capabilityFreshAt(cap: Capability, now: number, marginMs?: number): boolean;
|
|
2499
|
+
/**
|
|
2500
|
+
* Freshness over a whole presented bag: `true` only when every cap is fresh at
|
|
2501
|
+
* `now + marginMs` (see {@link capabilityFreshAt}). An empty bag is trivially
|
|
2502
|
+
* fresh. Use this at the cap-production point to decide whether to re-acquire.
|
|
2503
|
+
*/
|
|
2504
|
+
declare function capBagFreshAt(caps: readonly Capability[], now: number, marginMs?: number): boolean;
|
|
2505
|
+
type PermitResult = {
|
|
2506
|
+
ok: true;
|
|
2507
|
+
capabilityNonce: string;
|
|
2508
|
+
rootIssuer: PrincipalId;
|
|
2509
|
+
} | {
|
|
2510
|
+
ok: false;
|
|
2511
|
+
reason: string;
|
|
2512
|
+
};
|
|
2513
|
+
/**
|
|
2514
|
+
* A trust anchor accepted as a capability-chain root, plus whether it may be
|
|
2515
|
+
* named in authorization error messages.
|
|
2516
|
+
*/
|
|
2517
|
+
interface AcceptedRootIssuer {
|
|
2518
|
+
/** The principal accepted as a chain root for the queried service. */
|
|
2519
|
+
principal: PrincipalId;
|
|
2520
|
+
/**
|
|
2521
|
+
* Whether this issuer may be disclosed in `permits` rejection reasons.
|
|
2522
|
+
* Public anchors (e.g. a hub's well-known admin identity) are surfaced to
|
|
2523
|
+
* help diagnose "wrong root" failures; private ones are withheld so the
|
|
2524
|
+
* error never leaks the set of trusted issuers.
|
|
2525
|
+
*/
|
|
2526
|
+
isPublic: boolean;
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* **The** authorization predicate. A call is permitted iff some presented
|
|
2530
|
+
* capability (1) addresses the call, (2) has a genuine, well-delegated
|
|
2531
|
+
* chain whose leaf audience is the caller (`call.signer`), and (3) roots
|
|
2532
|
+
* at an issuer the verifier accepts **for the call's service**.
|
|
2533
|
+
*
|
|
2534
|
+
* Pure: it reports the verdict (and the `capabilityNonce` / `rootIssuer`
|
|
2535
|
+
* for audit) but performs no consumption. Replay is the caller's job — the
|
|
2536
|
+
* gate dedups `call.nonce`, so `callBind` grants are single-use for free.
|
|
2537
|
+
*
|
|
2538
|
+
* `acceptedRootIssuers` is consulted with the call's `serviceId` and returns
|
|
2539
|
+
* the accepted {@link AcceptedRootIssuer} anchors; an empty result rejects
|
|
2540
|
+
* every capability (fail closed) — trust is the verifier's, never the
|
|
2541
|
+
* token's. Anchors flagged `isPublic` are named in the rejection reason when
|
|
2542
|
+
* a chain roots at an unaccepted issuer.
|
|
2543
|
+
*/
|
|
2544
|
+
declare function permits(call: Call, capabilities: readonly SignedCapability[], acceptedRootIssuers: (serviceId: string) => readonly AcceptedRootIssuer[], nowMs: number, opts?: {
|
|
2545
|
+
maxDepth?: number;
|
|
2546
|
+
}): Promise<PermitResult>;
|
|
2547
|
+
/**
|
|
2548
|
+
* Sign a `Capability` with a {@link SigningIdentity}. The private key never
|
|
2549
|
+
* leaves the identity. The result verifies via {@link verifyChain}.
|
|
2550
|
+
*/
|
|
2551
|
+
declare function signCapability(capability: Capability, issuer: SigningIdentity): Promise<SignedCapability>;
|
|
2552
|
+
interface IssueCapabilityOptions {
|
|
2553
|
+
/** Identity allowed to present the capability. */
|
|
2554
|
+
readonly audience: PublicSigningIdentity;
|
|
2555
|
+
/** Authority granted to the audience. */
|
|
2556
|
+
readonly permissions: readonly Permission[];
|
|
2557
|
+
/** Optional expiration time as Unix milliseconds. */
|
|
2558
|
+
readonly expiresAtMs?: number;
|
|
2559
|
+
/** Optional parent whose authority this capability narrows. */
|
|
2560
|
+
readonly parent?: SignedCapability;
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Issue and sign a capability, deriving its issuer, nonce, and optional parent
|
|
2564
|
+
* hash. The result is plain JSON data and survives a JSON stringify/parse
|
|
2565
|
+
* round-trip without reconstruction.
|
|
2566
|
+
*/
|
|
2567
|
+
declare function issueCapability(issuer: SigningIdentity, options: IssueCapabilityOptions): Promise<SignedCapability>;
|
|
2568
|
+
type ParamMatcherFor<T> = {
|
|
2569
|
+
exact: T;
|
|
2570
|
+
} | {
|
|
2571
|
+
enum: T[];
|
|
2572
|
+
} | {
|
|
2573
|
+
any: true;
|
|
2574
|
+
} | (T extends string ? {
|
|
2575
|
+
prefix: string;
|
|
2576
|
+
} : never) | (T extends readonly string[] ? {
|
|
2577
|
+
subsetOf: string[];
|
|
2578
|
+
} : never);
|
|
2579
|
+
/** Type-safe top-level parameter matchers for one interface member. */
|
|
2580
|
+
type ParamMatchers<T> = T extends Record<string, unknown> ? { [K in keyof T]: ParamMatcherFor<Exclude<T[K], undefined>>; } : never;
|
|
2581
|
+
type RequestMemberName<TMembers extends MemberMap> = { [K in keyof TMembers]: TMembers[K] extends RequestType<any, any, any, any, any> ? K : never; }[keyof TMembers] & string;
|
|
2582
|
+
type RequestParams<TMember> = TMember extends RequestType<infer TParams, any, any, any, any> ? TParams : never;
|
|
2583
|
+
/** Match a string parameter by prefix. */
|
|
2584
|
+
declare function prefix(value: string): {
|
|
2585
|
+
prefix: string;
|
|
2586
|
+
};
|
|
2587
|
+
/**
|
|
2588
|
+
* Build an invoke permission from a typed interface member. The helper derives
|
|
2589
|
+
* the interface id and schema hash and type-checks parameter matcher names and
|
|
2590
|
+
* values against the member's params.
|
|
2591
|
+
*/
|
|
2592
|
+
declare function invoke<TMembers extends MemberMap, TMember extends RequestMemberName<TMembers>>(serviceId: string, iface: InterfaceDefinition<TMembers>, member: TMember, params?: ParamMatchers<RequestParams<TMembers[TMember]>>): Permission;
|
|
2593
|
+
//#endregion
|
|
2594
|
+
//#region src/identity/managedIdentity.d.ts
|
|
2595
|
+
/**
|
|
2596
|
+
* Executor-side backing for `identity.storage::*`. Lifecycle tied to a
|
|
2597
|
+
* single managed-identity slot; the executor decides where bytes are
|
|
2598
|
+
* stored.
|
|
2599
|
+
*/
|
|
2600
|
+
interface ManagedIdentityStorageBackend {
|
|
2601
|
+
get(key: string): Promise<unknown | undefined>;
|
|
2602
|
+
set(key: string, value: unknown): Promise<void>;
|
|
2603
|
+
/** Returns `true` iff the key existed before this call. */
|
|
2604
|
+
delete(key: string): Promise<boolean>;
|
|
2605
|
+
list(prefix?: string): Promise<string[]>;
|
|
2606
|
+
}
|
|
2607
|
+
/**
|
|
2608
|
+
* In-process storage backend. Used by tests and by any executor that
|
|
2609
|
+
* doesn't need at-rest persistence.
|
|
2610
|
+
*/
|
|
2611
|
+
declare class InMemoryManagedIdentityStorage implements ManagedIdentityStorageBackend {
|
|
2612
|
+
private readonly _data;
|
|
2613
|
+
get(key: string): Promise<unknown | undefined>;
|
|
2614
|
+
set(key: string, value: unknown): Promise<void>;
|
|
2615
|
+
delete(key: string): Promise<boolean>;
|
|
2616
|
+
list(prefix?: string): Promise<string[]>;
|
|
2617
|
+
}
|
|
2618
|
+
/**
|
|
2619
|
+
* In-process `ManagedIdentity` that holds the private key in memory and
|
|
2620
|
+
* uses the package crypto API. Useful for tests and for hub-side features
|
|
2621
|
+
* that want a short-lived identity without touching disk.
|
|
2622
|
+
*/
|
|
2623
|
+
declare class InMemoryManagedIdentity implements Identity {
|
|
2624
|
+
private readonly _ed;
|
|
2625
|
+
private readonly _wrap;
|
|
2626
|
+
readonly principal: PrincipalId;
|
|
2627
|
+
readonly wrapPublicKey: Uint8Array;
|
|
2628
|
+
constructor(_ed: Keypair, _wrap: X25519Keypair);
|
|
2629
|
+
get publicSigningIdentity(): PublicSigningIdentity;
|
|
2630
|
+
get publicWrappingIdentity(): PublicWrappingIdentity;
|
|
2631
|
+
sign(bytes: Uint8Array): Promise<Signature>;
|
|
2632
|
+
wrap(domain: string, bytes: Uint8Array): Promise<Uint8Array>;
|
|
2633
|
+
unwrap(domain: string, blob: Uint8Array): Promise<Uint8Array>;
|
|
2634
|
+
/** Generate a fresh in-memory identity (Ed25519 + X25519 keypairs). */
|
|
2635
|
+
static generate(): Promise<InMemoryManagedIdentity>;
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* Register the `identity` interface on `overlay` so the bound participant
|
|
2639
|
+
* can call `identity::sign` etc. on its root overlay. The overlay is
|
|
2640
|
+
* private to one participant — no other peer can reach these methods.
|
|
2641
|
+
*
|
|
2642
|
+
* When `storage` is supplied, the `identity.storage` interface is
|
|
2643
|
+
* registered alongside `identity::*`. Lifetime/scope of the backend is
|
|
2644
|
+
* the executor's responsibility — for keystore-backed identities the
|
|
2645
|
+
* backend lives as long as the identity slot does.
|
|
2646
|
+
*
|
|
2647
|
+
* Typical caller: the executor (e.g. the VS Code extension), right after
|
|
2648
|
+
* `Hub.attachParticipant`, on the `rootOverlay` returned from the attach
|
|
2649
|
+
* handle.
|
|
2650
|
+
*/
|
|
2651
|
+
declare function registerIdentityOnOverlay(overlay: LinkRpcConnection, identity: Identity, storage?: ManagedIdentityStorageBackend): void;
|
|
2652
|
+
/**
|
|
2653
|
+
* Like {@link registerIdentityOnOverlay}, but resolves the identity lazily on
|
|
2654
|
+
* first use via `resolveIdentity`. The identity is materialized only when the
|
|
2655
|
+
* bound participant actually calls `identity::*` — registering the overlay
|
|
2656
|
+
* does NOT create or load any identity.
|
|
2657
|
+
*
|
|
2658
|
+
* This is essential for the consent model: a host can serve `identity::*` on a
|
|
2659
|
+
* keystore slot without making the slot privileged (`hasState`) until the app
|
|
2660
|
+
* genuinely opts into an identity. `resolveIdentity` is expected to be cheap on
|
|
2661
|
+
* repeat calls (the keystore caches in memory), and to reflect lifecycle
|
|
2662
|
+
* changes — e.g. after a slot is wiped and re-created ("Reset Identity"), the
|
|
2663
|
+
* next call resolves the fresh identity.
|
|
2664
|
+
*
|
|
2665
|
+
* `storage` is registered eagerly because reading/writing storage is itself
|
|
2666
|
+
* the privileged act the app must perform to gain state — exposing the
|
|
2667
|
+
* interface costs nothing until used.
|
|
2668
|
+
*/
|
|
2669
|
+
declare function registerLazyIdentityOnOverlay(overlay: LinkRpcConnection<unknown>, resolveIdentity: () => Promise<Identity>, storage?: ManagedIdentityStorageBackend): void;
|
|
2670
|
+
/**
|
|
2671
|
+
* Client-side proxy for `identity.storage::*`. Methods round-trip through
|
|
2672
|
+
* the participant's root overlay; the executor enforces key-shape rules
|
|
2673
|
+
* server-side.
|
|
2674
|
+
*
|
|
2675
|
+
* Calls are unsigned (same recursion-guard rationale as
|
|
2676
|
+
* {@link createManagedIdentity} — the overlay is private to one
|
|
2677
|
+
* participant, so transport-level routing already authenticates).
|
|
2678
|
+
*/
|
|
2679
|
+
interface ManagedIdentityStorage {
|
|
2680
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
2681
|
+
set(key: string, value: unknown): Promise<void>;
|
|
2682
|
+
delete(key: string): Promise<boolean>;
|
|
2683
|
+
list(prefix?: string): Promise<string[]>;
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* A managed identity resolved from an executor's `identity::*` overlay: a
|
|
2687
|
+
* full {@link Identity} (signing + HPKE wrap/unwrap, all round-tripping
|
|
2688
|
+
* through the executor — private keys never leave it) plus the
|
|
2689
|
+
* per-identity {@link ManagedIdentityStorage}.
|
|
2690
|
+
*/
|
|
2691
|
+
interface ManagedIdentity extends Identity {
|
|
2692
|
+
/**
|
|
2693
|
+
* Per-identity persistent key/value store. Backed by the executor's
|
|
2694
|
+
* `identity.storage::*` overlay; calls throw when the executor did not
|
|
2695
|
+
* register storage for this slot (e.g. an in-process identity for tests).
|
|
2696
|
+
*/
|
|
2697
|
+
readonly storage: ManagedIdentityStorage;
|
|
2698
|
+
}
|
|
2699
|
+
/**
|
|
2700
|
+
* Bootstrap a {@link ManagedIdentity} from a sender (typically the raw
|
|
2701
|
+
* unsigned sender obtained via `channel.sender` before wrapping with
|
|
2702
|
+
* {@link SigningSender}). Makes `identity::*` calls unsigned — the
|
|
2703
|
+
* executor's overlay is private to one participant so routing already
|
|
2704
|
+
* identifies the caller, preventing signing recursion.
|
|
2705
|
+
*
|
|
2706
|
+
* The returned identity signs / wraps / unwraps by round-tripping through
|
|
2707
|
+
* the executor's `identity::*` overlay; the caller wires it into their
|
|
2708
|
+
* {@link SigningSender} configuration (e.g. as a {@link Principal}). No
|
|
2709
|
+
* side effects on any existing connection or holder.
|
|
2710
|
+
*/
|
|
2711
|
+
declare function createManagedIdentity(sender: IRequestSender<unknown>): Promise<ManagedIdentity>;
|
|
2712
|
+
//#endregion
|
|
2713
|
+
//#region src/identity/capBag.d.ts
|
|
2714
|
+
interface CapBagOptions {
|
|
2715
|
+
/**
|
|
2716
|
+
* Per-identity persistent storage (e.g.
|
|
2717
|
+
* {@link ManagedIdentityHandle.storage}). Omit for a memory-only bag
|
|
2718
|
+
* that forgets its caps when the process exits.
|
|
2719
|
+
*/
|
|
2720
|
+
readonly storage?: ManagedIdentityStorage;
|
|
2721
|
+
/** Storage key. Defaults to `hubrpc.caps.v1`. */
|
|
2722
|
+
readonly storageKey?: string;
|
|
2723
|
+
/** Optional sink for non-fatal storage warnings. */
|
|
2724
|
+
readonly onWarn?: (message: string) => void;
|
|
2725
|
+
}
|
|
2726
|
+
/**
|
|
2727
|
+
* A process-lifetime bag of hub-issued capabilities. Plug
|
|
2728
|
+
* {@link CapBag.provider} straight into a connection's cap provider so every
|
|
2729
|
+
* outbound signed call carries whatever caps have been granted so far. The
|
|
2730
|
+
* hub picks whichever cap matches the call per dispatch.
|
|
2731
|
+
*
|
|
2732
|
+
* When constructed with {@link CapBagOptions.storage} the bag hydrates from
|
|
2733
|
+
* (and persists to) storage, so the next process spawn for the same identity
|
|
2734
|
+
* reuses its grants without re-prompting.
|
|
2735
|
+
*/
|
|
2736
|
+
declare class CapBag {
|
|
2737
|
+
/** Create a bag, hydrating from storage when one was supplied. */
|
|
2738
|
+
static load(options?: CapBagOptions): Promise<CapBag>;
|
|
2739
|
+
private readonly _caps;
|
|
2740
|
+
private readonly _storage?;
|
|
2741
|
+
private readonly _storageKey;
|
|
2742
|
+
private readonly _onWarn;
|
|
2743
|
+
private constructor();
|
|
2744
|
+
get capabilities(): readonly SignedCapability[];
|
|
2745
|
+
/**
|
|
2746
|
+
* A {@link CapProvider} reflecting the bag at call-time. Assign it to
|
|
2747
|
+
* `HubClientHandle.signing.capProvider` or pass it through
|
|
2748
|
+
* `SigningSenderConfig.capProvider`.
|
|
2749
|
+
*/
|
|
2750
|
+
readonly provider: CapProvider;
|
|
2751
|
+
/** Append capabilities and persist (when backed by storage). */
|
|
2752
|
+
add(...caps: readonly SignedCapability[]): Promise<void>;
|
|
2753
|
+
/** Drop all capabilities and persist the empty bag. */
|
|
2754
|
+
clear(): Promise<void>;
|
|
2755
|
+
private _hydrate;
|
|
2756
|
+
private _persist;
|
|
2757
|
+
}
|
|
2758
|
+
//#endregion
|
|
2759
|
+
//#region src/identity/principal.d.ts
|
|
2760
|
+
/**
|
|
2761
|
+
* A signing identity bundled with its durable capability set. The
|
|
2762
|
+
* {@link SigningIdentity} is fixed for the life of the principal; the
|
|
2763
|
+
* {@link CapBag} is mutable and extensible — caps accumulate as the peer grants
|
|
2764
|
+
* them.
|
|
2765
|
+
*
|
|
2766
|
+
* Transient, per-call (one-shot) grants are deliberately NOT part of a
|
|
2767
|
+
* principal: that is a separate policy, see `OneShotCapStaging`.
|
|
2768
|
+
*
|
|
2769
|
+
* Lives in its own module (rather than alongside `SigningSender`) so the
|
|
2770
|
+
* `SigningSender` ⇄ `createManagedPrincipal` factory cycle does not run through
|
|
2771
|
+
* a top-level `class … extends Principal`: such a cycle would hit a TDZ
|
|
2772
|
+
* ("Class extends value undefined") depending on module evaluation order.
|
|
2773
|
+
*/
|
|
2774
|
+
declare class Principal {
|
|
2775
|
+
readonly identity: SigningIdentity;
|
|
2776
|
+
readonly capBag: CapBag;
|
|
2777
|
+
static create(identity: SigningIdentity, capabilities?: readonly SignedCapability[]): Promise<Principal>;
|
|
2778
|
+
constructor(identity: SigningIdentity, capBag: CapBag);
|
|
2779
|
+
get id(): PrincipalId;
|
|
2780
|
+
}
|
|
2781
|
+
//#endregion
|
|
2782
|
+
//#region src/identity/managedPrincipal.d.ts
|
|
2783
|
+
/**
|
|
2784
|
+
* A {@link Principal} that also exposes the executor-backed per-identity
|
|
2785
|
+
* {@link ManagedIdentityStorage}. Returned by {@link createManagedPrincipal}
|
|
2786
|
+
* so callers can persist their own app state (e.g. a resolved serviceId)
|
|
2787
|
+
* alongside the granted capabilities, using the very same durable store the
|
|
2788
|
+
* {@link CapBag} hydrates from — no second storage wiring required.
|
|
2789
|
+
*/
|
|
2790
|
+
declare class PrincipalWithStore extends Principal {
|
|
2791
|
+
readonly store: ManagedIdentityStorage;
|
|
2792
|
+
readonly identity: Identity;
|
|
2793
|
+
constructor(identity: Identity, capBag: CapBag, store: ManagedIdentityStorage);
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Executor-managed principal: the peer signs for us through its
|
|
2797
|
+
* `identity::sign` overlay (we never hold a private key). Bootstrapped purely
|
|
2798
|
+
* from the outbound `sender` — no side effects on any connection — and caps
|
|
2799
|
+
* persist in the executor-backed per-identity storage. Works over any
|
|
2800
|
+
* transport whose peer serves the identity overlay (hub *or* stdio).
|
|
2801
|
+
*
|
|
2802
|
+
* Call this exactly once per channel: it performs the `identity::*` handshake
|
|
2803
|
+
* and hydrates the {@link CapBag} from storage. The returned
|
|
2804
|
+
* {@link PrincipalWithStore} additionally surfaces that same durable
|
|
2805
|
+
* {@link ManagedIdentityStorage} as `store`.
|
|
2806
|
+
*/
|
|
2807
|
+
declare function createManagedPrincipal(sender: IRequestSender<unknown>): Promise<PrincipalWithStore>;
|
|
2808
|
+
//#endregion
|
|
2809
|
+
//#region src/identity/signingSender.d.ts
|
|
2810
|
+
/**
|
|
2811
|
+
* Per-outbound-call hook. Sees every signed call this sender makes,
|
|
2812
|
+
* and gets a chance to:
|
|
2813
|
+
* - attach capabilities (the common case),
|
|
2814
|
+
* - override `method` / `params` / `interfaceHash` (rewriting,
|
|
2815
|
+
* auditing, schema-pinning),
|
|
2816
|
+
* - advance `signedAtMs` (e.g. after a slow consent prompt that needs
|
|
2817
|
+
* to issue a cap bound to a fresher timestamp than the sender's
|
|
2818
|
+
* initial wall-clock).
|
|
2819
|
+
*
|
|
2820
|
+
* `signer` and `nonce` are NOT overridable. `signer` is the call's
|
|
2821
|
+
* identity; `nonce` ties any cap to this specific call attempt and must
|
|
2822
|
+
* not drift between the cap-issuance request and the call itself.
|
|
2823
|
+
*/
|
|
2824
|
+
type CapProvider = (req: {
|
|
2825
|
+
readonly method: string;
|
|
2826
|
+
readonly params: JsonValue | undefined;
|
|
2827
|
+
readonly signer: PrincipalId;
|
|
2828
|
+
readonly nonce: string;
|
|
2829
|
+
readonly signedAtMs: number;
|
|
2830
|
+
readonly interfaceHash?: string;
|
|
2831
|
+
}) => Promise<CapProviderResult>;
|
|
2832
|
+
interface CapProviderResult {
|
|
2833
|
+
readonly capabilities?: readonly SignedCapability[];
|
|
2834
|
+
/** Override the wire method this sender signs. Default: the original. */
|
|
2835
|
+
readonly method?: string;
|
|
2836
|
+
/** Override the params this sender signs. Default: the original. */
|
|
2837
|
+
readonly params?: JsonValue;
|
|
2838
|
+
/** Override the interfaceHash baked into the signed envelope. */
|
|
2839
|
+
readonly interfaceHash?: string;
|
|
2840
|
+
/** Advance the Unix-ms timestamp baked into the signed envelope. */
|
|
2841
|
+
readonly signedAtMs?: number;
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Per-call ctx the {@link SigningSender} consumes. All fields are
|
|
2845
|
+
* optional; the sender's persistent {@link SigningSenderConfig.principal} is
|
|
2846
|
+
* the default.
|
|
2847
|
+
*
|
|
2848
|
+
* To install a signer after the sender is already constructed (e.g. a
|
|
2849
|
+
* managed-identity bootstrap), mutate the caller-owned config object — it is
|
|
2850
|
+
* read fresh on every call. No setter on the sender itself.
|
|
2851
|
+
*/
|
|
2852
|
+
interface SigningCallCtx {
|
|
2853
|
+
/**
|
|
2854
|
+
* Override the signer for this call only. Pass `null` to skip
|
|
2855
|
+
* signing entirely (plain unsigned JSON-RPC). Bootstrap flows that
|
|
2856
|
+
* must talk to `identity::*` before a managed signer is available
|
|
2857
|
+
* use `signerOverride: null` for those calls.
|
|
2858
|
+
*/
|
|
2859
|
+
readonly signerOverride?: SigningIdentity | null;
|
|
2860
|
+
/**
|
|
2861
|
+
* Replace whatever caps the persistent cap provider would attach.
|
|
2862
|
+
* Skips the persistent provider for this call.
|
|
2863
|
+
*/
|
|
2864
|
+
readonly capsOverride?: readonly SignedCapability[];
|
|
2865
|
+
/**
|
|
2866
|
+
* Reserved for the perm-denied / consent retry path. Currently a
|
|
2867
|
+
* passthrough — sender does not act on it yet.
|
|
2868
|
+
*/
|
|
2869
|
+
readonly requestPermissionWhenDenied?: boolean;
|
|
2870
|
+
}
|
|
2871
|
+
/**
|
|
2872
|
+
* Persistent config for a {@link SigningSender}.
|
|
2873
|
+
*
|
|
2874
|
+
* `principal` is the call identity plus its durable, extensible capability
|
|
2875
|
+
* set. `oneShotCaps` is an optional side-policy that may stage a single-use
|
|
2876
|
+
* capability onto the very next call without touching the principal. Both
|
|
2877
|
+
* fields are read fresh on every call, so callers may install or swap them
|
|
2878
|
+
* after the sender has been constructed.
|
|
2879
|
+
*
|
|
2880
|
+
* A missing `principal` means "no signing / no caps" for that call, unless
|
|
2881
|
+
* overridden via {@link SigningCallCtx}.
|
|
2882
|
+
*/
|
|
2883
|
+
interface SigningSenderConfig {
|
|
2884
|
+
/** Identity + persistent (extensible) capabilities. */
|
|
2885
|
+
readonly principal?: Principal;
|
|
2886
|
+
/**
|
|
2887
|
+
* Optional per-call capability provider. Runs at sign-time with the
|
|
2888
|
+
* concrete method/params/nonce/timestamp and may return caps and/or
|
|
2889
|
+
* signing overrides.
|
|
2890
|
+
*/
|
|
2891
|
+
readonly capProvider?: CapProvider;
|
|
2892
|
+
/**
|
|
2893
|
+
* Optional one-shot capability staging policy. Independent of the
|
|
2894
|
+
* principal; drained per outbound signed call.
|
|
2895
|
+
*/
|
|
2896
|
+
readonly oneShotCaps?: OneShotCapStaging;
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* Side-policy for staging *one-shot* capabilities onto the next outbound
|
|
2900
|
+
* signed call, independent of any {@link Principal}. The {@link SigningSender}
|
|
2901
|
+
* drains the staged caps after attaching them once, so a spent cap never
|
|
2902
|
+
* leaks into a later call.
|
|
2903
|
+
*
|
|
2904
|
+
* Requesting the one-shot grant (prompting the user / hub) lives next to —
|
|
2905
|
+
* not inside — the principal: the principal owns identity and durable
|
|
2906
|
+
* capabilities; this owns the transient per-call grant.
|
|
2907
|
+
*/
|
|
2908
|
+
declare class OneShotCapStaging {
|
|
2909
|
+
private _staged;
|
|
2910
|
+
/** Stage caps for the next signed call. Replaces any still-pending caps. */
|
|
2911
|
+
stage(caps: readonly SignedCapability[]): void;
|
|
2912
|
+
/**
|
|
2913
|
+
* Take (and clear) the staged caps. Called by {@link SigningSender} once
|
|
2914
|
+
* per outbound signed call.
|
|
2915
|
+
*/
|
|
2916
|
+
take(): readonly SignedCapability[];
|
|
2917
|
+
}
|
|
2918
|
+
/**
|
|
2919
|
+
* Outbound sender decorator that wraps every call in a `$hubrpc` signed
|
|
2920
|
+
* envelope and (optionally) attaches capabilities. Stateless w.r.t. signing
|
|
2921
|
+
* config — the {@link Principal} and {@link OneShotCapStaging} are resolved
|
|
2922
|
+
* per-call from {@link SigningSenderConfig}, so the caller can install or
|
|
2923
|
+
* swap them after construction by mutating that object.
|
|
2924
|
+
*/
|
|
2925
|
+
/**
|
|
2926
|
+
* The result of {@link SigningSender.fromChannelWithManagedPrincipal} /
|
|
2927
|
+
* {@link SigningSender.fromTransportWithManagedPrincipal}: a signing
|
|
2928
|
+
* {@link Channel} ready for `LinkRpcConnection`, plus the executor-managed
|
|
2929
|
+
* {@link PrincipalWithStore} that drives it (caps, per-identity storage, nodeId).
|
|
2930
|
+
*/
|
|
2931
|
+
interface ManagedSigningChannel<TInCtx = undefined> {
|
|
2932
|
+
readonly channel: Channel<TInCtx, SigningCallCtx>;
|
|
2933
|
+
readonly principal: PrincipalWithStore;
|
|
2934
|
+
}
|
|
2935
|
+
declare class SigningSender<TInCtx = unknown> implements IRequestSender<SigningCallCtx> {
|
|
2936
|
+
private readonly _inner;
|
|
2937
|
+
private readonly _config;
|
|
2938
|
+
/**
|
|
2939
|
+
* Wrap an inner channel with signing. Composes via {@link Channel.withSender}
|
|
2940
|
+
* so the resulting `Channel<TInCtx, SigningCallCtx>` plugs into
|
|
2941
|
+
* `LinkRpcConnection`.
|
|
2942
|
+
*/
|
|
2943
|
+
static wrapChannel<TInCtx>(inner: Channel<TInCtx, unknown>, config: SigningSenderConfig): Channel<TInCtx, SigningCallCtx>;
|
|
2944
|
+
/**
|
|
2945
|
+
* Bootstrap an executor-managed {@link Principal} over the **unsigned**
|
|
2946
|
+
* `channel`, then wrap that same channel with signing driven by it.
|
|
2947
|
+
*
|
|
2948
|
+
* Crucially, the `identity::*` handshake that mints the managed principal
|
|
2949
|
+
* rides the *raw* channel (`channel.sender`), so those bootstrap calls are
|
|
2950
|
+
* themselves never signed — there is no "sign the sign call" recursion and
|
|
2951
|
+
* no reliance on threading `signerOverride: null` through every transport
|
|
2952
|
+
* layer. The returned signing {@link Channel} is what callers hand to
|
|
2953
|
+
* `LinkRpcConnection`; the {@link PrincipalWithStore} is returned alongside
|
|
2954
|
+
* for caps / per-identity storage / nodeId.
|
|
2955
|
+
*/
|
|
2956
|
+
static fromChannelWithManagedPrincipal<TInCtx>(channel: Channel<TInCtx, unknown>, config?: Omit<SigningSenderConfig, 'principal'>): Promise<ManagedSigningChannel<TInCtx>>;
|
|
2957
|
+
/**
|
|
2958
|
+
* Convenience over {@link fromChannelWithManagedPrincipal}: build the
|
|
2959
|
+
* unsigned {@link JsonRpcChannel} from `transport` first. The common entry
|
|
2960
|
+
* point for app hosts / services that own a raw {@link IMessageTransport}.
|
|
2961
|
+
*/
|
|
2962
|
+
static fromTransportWithManagedPrincipal(transport: IMessageTransport, config?: Omit<SigningSenderConfig, 'principal'>): Promise<ManagedSigningChannel<undefined>>;
|
|
2963
|
+
constructor(_inner: IRequestSender<unknown>, _config: SigningSenderConfig);
|
|
2964
|
+
sendRequest(method: string, params: JsonValue | undefined, opts?: SendOpts<SigningCallCtx>): Promise<JsonValue>;
|
|
2965
|
+
sendNotification(method: string, params: JsonValue | undefined, opts?: SendOpts<SigningCallCtx>): Promise<void>;
|
|
2966
|
+
sendRequestWithStream(method: string, params: JsonValue | undefined, opts?: StreamSendOpts<SigningCallCtx>): RawStreamingCall;
|
|
2967
|
+
close(): void;
|
|
2968
|
+
private _prepareOutbound;
|
|
2969
|
+
private _resolveSigner;
|
|
2970
|
+
private _createSigningCoordinates;
|
|
2971
|
+
private _resolveIntentAndCapabilities;
|
|
2972
|
+
private _signPreparedCall;
|
|
2973
|
+
}
|
|
2974
|
+
declare namespace crypto_d_exports {
|
|
2975
|
+
export { generateKeypair, generateX25519Keypair, hpkeOpen, hpkeSeal, keypairFromSeed, sha256, sign, verify, x25519KeypairFromSeed };
|
|
2976
|
+
}
|
|
2977
|
+
/** Generate a fresh Ed25519 keypair (raw 32-byte seed + 32-byte public key). */
|
|
2978
|
+
declare function generateKeypair(): Promise<Keypair>;
|
|
2979
|
+
/**
|
|
2980
|
+
* Deterministically derive an Ed25519 keypair from a raw 32-byte seed. For
|
|
2981
|
+
* tests / reproducible identities only — production keys must be random
|
|
2982
|
+
* (use {@link generateKeypair}).
|
|
2983
|
+
*/
|
|
2984
|
+
declare function keypairFromSeed(seed: Uint8Array): Promise<Keypair>;
|
|
2985
|
+
/**
|
|
2986
|
+
* Deterministically derive an X25519 keypair from a raw 32-byte scalar. For
|
|
2987
|
+
* tests / reproducible identities only.
|
|
2988
|
+
*/
|
|
2989
|
+
declare function x25519KeypairFromSeed(seed: Uint8Array): Promise<X25519Keypair>;
|
|
2990
|
+
/** Ed25519 sign `message` with a raw 32-byte seed private key. */
|
|
2991
|
+
declare function sign(privateKey: PrivateKey, message: Uint8Array): Promise<Signature>;
|
|
2992
|
+
/** Ed25519 verify. Returns `false` (never throws) on malformed input. */
|
|
2993
|
+
declare function verify(publicKey: PublicKey, message: Uint8Array, sig: Signature): Promise<boolean>;
|
|
2994
|
+
/** Generate a fresh X25519 keypair (raw 32-byte scalar + 32-byte public key). */
|
|
2995
|
+
declare function generateX25519Keypair(): Promise<X25519Keypair>;
|
|
2996
|
+
/**
|
|
2997
|
+
* HPKE-base-mode single-shot seal. Output blob layout is
|
|
2998
|
+
* `enc (32 B) || ciphertext || tag (16 B)`; `domain` is bound into both the
|
|
2999
|
+
* HPKE `info` parameter and the AEAD AAD.
|
|
3000
|
+
*/
|
|
3001
|
+
declare function hpkeSeal(args: {
|
|
3002
|
+
readonly recipientPublicKey: Uint8Array;
|
|
3003
|
+
readonly domain: Uint8Array;
|
|
3004
|
+
readonly plaintext: Uint8Array;
|
|
3005
|
+
}): Promise<Uint8Array>;
|
|
3006
|
+
/**
|
|
3007
|
+
* HPKE-base-mode single-shot open. Throws on any tag failure (wrong domain,
|
|
3008
|
+
* wrong recipient key, or tampered blob); the error deliberately does NOT
|
|
3009
|
+
* distinguish these cases.
|
|
3010
|
+
*/
|
|
3011
|
+
declare function hpkeOpen(args: {
|
|
3012
|
+
readonly recipientPrivateKey: Uint8Array;
|
|
3013
|
+
readonly domain: Uint8Array;
|
|
3014
|
+
readonly blob: Uint8Array;
|
|
3015
|
+
}): Promise<Uint8Array>;
|
|
3016
|
+
/** SHA-256 digest (synchronous, dependency-free). */
|
|
3017
|
+
declare function sha256(bytes: Uint8Array): Uint8Array;
|
|
3018
|
+
//#endregion
|
|
3019
|
+
//#region src/identity/identity.interfaces.d.ts
|
|
3020
|
+
/**
|
|
3021
|
+
* Per-participant key oracle. Reached on the participant's root overlay
|
|
3022
|
+
* (form-2 method names like `identity::sign`). Private key material lives
|
|
3023
|
+
* in the executor; the participant only sees the operations.
|
|
3024
|
+
*
|
|
3025
|
+
* Wire shape:
|
|
3026
|
+
* sign / wrap / unwrap all take and return base64url-encoded bytes.
|
|
3027
|
+
* `domain` is a caller-chosen string bound into HPKE info and the AEAD
|
|
3028
|
+
* AAD. `unwrap` succeeds only when invoked with the exact `domain` that
|
|
3029
|
+
* `wrap` was called with. Use a stable, namespaced string (e.g.
|
|
3030
|
+
* `secrets.vault.master-key.v1`).
|
|
3031
|
+
*/
|
|
3032
|
+
declare const identityInterface: InterfaceDefinition<{
|
|
3033
|
+
getPrincipal: RequestType<Record<string, never>, {
|
|
3034
|
+
principal: string;
|
|
3035
|
+
}, any, never, never>;
|
|
3036
|
+
getWrapPublicKey: RequestType<Record<string, never>, {
|
|
3037
|
+
wrapPublicKey: string;
|
|
3038
|
+
}, any, never, never>;
|
|
3039
|
+
sign: RequestType<{
|
|
3040
|
+
bytes: string;
|
|
3041
|
+
}, {
|
|
3042
|
+
signature: string;
|
|
3043
|
+
}, any, never, never>;
|
|
3044
|
+
wrap: RequestType<{
|
|
3045
|
+
domain: string;
|
|
3046
|
+
bytes: string;
|
|
3047
|
+
}, {
|
|
3048
|
+
blob: string;
|
|
3049
|
+
}, any, never, never>;
|
|
3050
|
+
unwrap: RequestType<{
|
|
3051
|
+
domain: string;
|
|
3052
|
+
blob: string;
|
|
3053
|
+
}, {
|
|
3054
|
+
bytes: string;
|
|
3055
|
+
}, any, never, never>;
|
|
3056
|
+
}>;
|
|
3057
|
+
/**
|
|
3058
|
+
* Per-identity persistent key/value store. Reached on the participant's
|
|
3059
|
+
* root overlay as `identity.storage::get` etc. — same overlay that serves
|
|
3060
|
+
* `identity::*`, same single-participant addressability.
|
|
3061
|
+
*
|
|
3062
|
+
* Storage scope is the executor's managed-identity slot (e.g.
|
|
3063
|
+
* `service:demo`). Lifecycle is bound to the identity: when the executor
|
|
3064
|
+
* deletes the identity for a slot, the storage for that slot is wiped in
|
|
3065
|
+
* the same operation. New identity issued for the same slot starts empty.
|
|
3066
|
+
*
|
|
3067
|
+
* The executor encrypts the backing file at rest with the same secret it
|
|
3068
|
+
* uses for identity files — see {@link IdentityKeystore} in
|
|
3069
|
+
* `@hediet/linkrpc/node`.
|
|
3070
|
+
*
|
|
3071
|
+
* Keys must match `^[A-Za-z0-9._/-]{1,256}$`. `/` is conventional for
|
|
3072
|
+
* namespacing (`caps.v1`, `prefs/foo`) and `list({ prefix })` honours
|
|
3073
|
+
* that. Values are arbitrary JSON.
|
|
3074
|
+
*/
|
|
3075
|
+
declare const identityStorageInterface: InterfaceDefinition<{
|
|
3076
|
+
get: RequestType<{
|
|
3077
|
+
key: string;
|
|
3078
|
+
}, {
|
|
3079
|
+
value?: unknown;
|
|
3080
|
+
}, any, never, never>;
|
|
3081
|
+
set: RequestType<{
|
|
3082
|
+
key: string;
|
|
3083
|
+
value: unknown;
|
|
3084
|
+
}, Record<string, never>, any, never, never>;
|
|
3085
|
+
delete: RequestType<{
|
|
3086
|
+
key: string;
|
|
3087
|
+
}, {
|
|
3088
|
+
existed: boolean;
|
|
3089
|
+
}, any, never, never>;
|
|
3090
|
+
list: RequestType<{
|
|
3091
|
+
prefix?: string | undefined;
|
|
3092
|
+
}, {
|
|
3093
|
+
keys: string[];
|
|
3094
|
+
}, any, never, never>;
|
|
3095
|
+
}>;
|
|
3096
|
+
//#endregion
|
|
3097
|
+
//#region src/identity/signedRpcEnvelope.d.ts
|
|
3098
|
+
interface SignRpcCallOptions {
|
|
3099
|
+
readonly method: string;
|
|
3100
|
+
/** The user's params. Must be a plain JSON object or `undefined` (else throws). */
|
|
3101
|
+
readonly params?: JsonValue;
|
|
3102
|
+
/** Identity that produces the signature. */
|
|
3103
|
+
readonly signingIdentity: SigningIdentity;
|
|
3104
|
+
/** Unix milliseconds. Defaults to `Date.now()`. */
|
|
3105
|
+
readonly nowMs?: number;
|
|
3106
|
+
/** Override the random nonce (testing). */
|
|
3107
|
+
readonly nonce?: Uint8Array;
|
|
3108
|
+
/** Optional schema hash assertion. */
|
|
3109
|
+
readonly interfaceHash?: string;
|
|
3110
|
+
}
|
|
3111
|
+
interface SignedRpcCall {
|
|
3112
|
+
/**
|
|
3113
|
+
* Wire params for the JSON-RPC request: the user's params merged with
|
|
3114
|
+
* `$hubrpc` (signed meta) and `$hubrpcSignature` (the `call` signature).
|
|
3115
|
+
* Capabilities are attached separately via {@link attachCapabilities}
|
|
3116
|
+
* since they are unsigned authority hints, not part of what the
|
|
3117
|
+
* signature commits to.
|
|
3118
|
+
*/
|
|
3119
|
+
readonly wireParams: LinkRpcWireParams;
|
|
3120
|
+
/** The signed call meta embedded under `$hubrpc`. */
|
|
3121
|
+
readonly callMeta: CallMeta;
|
|
3122
|
+
/** The call's content hash (`signedHash("call", signedParams)`) — what `callBind` binds to. */
|
|
3123
|
+
readonly callHash: Base64Sha256;
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Sign a single JSON-RPC call. Produces the wire-form params with the
|
|
3127
|
+
* `$hubrpc` (signed meta) and `$hubrpcSignature.call` envelopes attached,
|
|
3128
|
+
* plus the call content hash.
|
|
3129
|
+
*/
|
|
3130
|
+
declare function signRpcCall(opts: SignRpcCallOptions): Promise<SignedRpcCall>;
|
|
3131
|
+
/**
|
|
3132
|
+
* Attach capabilities to a {@link SignedRpcCall.wireParams}. Capabilities
|
|
3133
|
+
* ride in `$hubrpcUnsigned.capabilities` — they are NOT part of what the
|
|
3134
|
+
* signature commits to (which is why this is a separate step). Returns a
|
|
3135
|
+
* new wireParams object; the input is not mutated.
|
|
3136
|
+
*/
|
|
3137
|
+
declare function attachCapabilities(wireParams: LinkRpcWireParams, capabilities: readonly SignedCapability[]): LinkRpcWireParams;
|
|
3138
|
+
interface VerifyRpcCallOptions {
|
|
3139
|
+
readonly wireMethod: string;
|
|
3140
|
+
/** Raw params object received on the wire. */
|
|
3141
|
+
readonly wireParams: unknown;
|
|
3142
|
+
/** Defaults to `Date.now()`. */
|
|
3143
|
+
readonly nowMs?: number;
|
|
3144
|
+
/** Max clock skew in milliseconds. Default 300000 (5 min). */
|
|
3145
|
+
readonly maxSkewMs?: number;
|
|
3146
|
+
/** When `true`, reject calls lacking a signed envelope + signature. */
|
|
3147
|
+
readonly requireSigned?: boolean;
|
|
3148
|
+
}
|
|
3149
|
+
type VerifyRpcCallResult = {
|
|
3150
|
+
readonly ok: true;
|
|
3151
|
+
/** `undefined` when the call had no signed (`$hubrpc` + `$hubrpcSignature.call`) envelope. */
|
|
3152
|
+
readonly identity: undefined | {
|
|
3153
|
+
readonly callMeta: CallMeta;
|
|
3154
|
+
/** The authenticated principal (`callMeta.principal`). */
|
|
3155
|
+
readonly signer: PrincipalId;
|
|
3156
|
+
/** The call's content hash — input to `callBind.payloadHash`. */
|
|
3157
|
+
readonly callHash: Base64Sha256;
|
|
3158
|
+
readonly capabilities: SignedCapability[];
|
|
3159
|
+
};
|
|
3160
|
+
/** Params the handler should see (user params with the reserved keys stripped). */
|
|
3161
|
+
readonly params: JsonValue | undefined;
|
|
3162
|
+
} | {
|
|
3163
|
+
readonly ok: false;
|
|
3164
|
+
readonly reason: string;
|
|
3165
|
+
};
|
|
3166
|
+
/**
|
|
3167
|
+
* Verify the identity envelope on a JSON-RPC call. Does **not** evaluate
|
|
3168
|
+
* capability chains or caveats — that is the hub / authoriser's job.
|
|
3169
|
+
*
|
|
3170
|
+
* A signed call carries `$hubrpc` (with `principal`) and a `call` signature
|
|
3171
|
+
* under `$hubrpcSignature`. The signature is checked against
|
|
3172
|
+
* `signingInput("call", wireParams)` (the reserved keys are stripped by the
|
|
3173
|
+
* signed-object standard). Bare/unsigned calls return `identity: undefined`.
|
|
3174
|
+
*/
|
|
3175
|
+
declare function verifyRpcCall(opts: VerifyRpcCallOptions): Promise<VerifyRpcCallResult>;
|
|
3176
|
+
//#endregion
|
|
3177
|
+
//#region src/identity/metaEnvelope.d.ts
|
|
3178
|
+
/** Plain JSON object with optional values — the wire shape JSON-RPC params take. */
|
|
3179
|
+
type JsonObject = {
|
|
3180
|
+
[key: string]: JsonValue | undefined;
|
|
3181
|
+
};
|
|
3182
|
+
interface SignParamsOptions {
|
|
3183
|
+
method: string;
|
|
3184
|
+
/** Caller-supplied params (without identity envelopes). Must be an object or undefined. */
|
|
3185
|
+
params: JsonObject | undefined;
|
|
3186
|
+
/** Identity that produces the signature. */
|
|
3187
|
+
signingIdentity: SigningIdentity;
|
|
3188
|
+
/**
|
|
3189
|
+
* Caps to attach. They are NOT signed — they ride in `$hubrpcUnsigned`.
|
|
3190
|
+
*/
|
|
3191
|
+
capabilities?: readonly SignedCapability[];
|
|
3192
|
+
/** Unix milliseconds; defaults to `Date.now()`. */
|
|
3193
|
+
nowMs?: number;
|
|
3194
|
+
/** Override nonce (testing); otherwise 16 random bytes. */
|
|
3195
|
+
nonce?: Uint8Array;
|
|
3196
|
+
/** Stamped into the signed envelope as `interfaceHash`. */
|
|
3197
|
+
interfaceHash?: string;
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Returns a wire-form params object carrying the `$hubrpc` envelope, the
|
|
3201
|
+
* `call` signature under `$hubrpcSignature`, and (optionally) capabilities
|
|
3202
|
+
* under `$hubrpcUnsigned`.
|
|
3203
|
+
*/
|
|
3204
|
+
declare function signParams(opts: SignParamsOptions): Promise<JsonObject>;
|
|
3205
|
+
interface VerifyCallOptions {
|
|
3206
|
+
method: string;
|
|
3207
|
+
/** Raw params as received on the wire. Carries `$hubrpc` + `$hubrpcSignature` when signed. */
|
|
3208
|
+
params: unknown;
|
|
3209
|
+
/** Decode the method string into the call's target address. */
|
|
3210
|
+
parseMethod: (method: string) => CallTarget;
|
|
3211
|
+
nowMs?: number;
|
|
3212
|
+
/** Max clock skew in milliseconds. Default 300000 (±5min). */
|
|
3213
|
+
maxSkewMs?: number;
|
|
3214
|
+
/** Reject if no capability is presented. */
|
|
3215
|
+
requireCapability?: boolean;
|
|
3216
|
+
}
|
|
3217
|
+
type VerifyResult = {
|
|
3218
|
+
ok: true;
|
|
3219
|
+
signer: PrincipalId;
|
|
3220
|
+
/**
|
|
3221
|
+
* The resolved, authenticated call — the unit `permits()` judges.
|
|
3222
|
+
* Authorization is intentionally NOT done here; the gate owns it
|
|
3223
|
+
* (and the replay ledger).
|
|
3224
|
+
*/
|
|
3225
|
+
call: Call;
|
|
3226
|
+
/** Params with the reserved keys stripped — pass this to the actual handler. */
|
|
3227
|
+
strippedParams: Record<string, unknown> | undefined;
|
|
3228
|
+
/** Leaf + parent capabilities to hand to `permits()`. */
|
|
3229
|
+
capabilities: SignedCapability[];
|
|
3230
|
+
/** Per-call nonce extracted from the envelope (for replay-protection ledgers). */
|
|
3231
|
+
nonce: string;
|
|
3232
|
+
/** The call's content hash — input to `callBind.payloadHash`. */
|
|
3233
|
+
callHash: Base64Sha256;
|
|
3234
|
+
/** The `$hubrpc` envelope as received. */
|
|
3235
|
+
callMeta: CallMeta;
|
|
3236
|
+
/** Schema-version assertion the caller stamped. */
|
|
3237
|
+
interfaceHash: string | undefined;
|
|
3238
|
+
} | {
|
|
3239
|
+
ok: false;
|
|
3240
|
+
/**
|
|
3241
|
+
* Failure category:
|
|
3242
|
+
* - `"capability"` — the call shape was fine; the caller needs to
|
|
3243
|
+
* acquire (or present) a capability. Hub should surface this as
|
|
3244
|
+
* `permissionRequired` so consumers know to negotiate access.
|
|
3245
|
+
* - `"envelope"` — anything else (missing/bad signature, replay,
|
|
3246
|
+
* malformed nonce, etc). Hub should surface as `invalidRequest`.
|
|
3247
|
+
*/
|
|
3248
|
+
kind: "capability" | "envelope";
|
|
3249
|
+
reason: string;
|
|
3250
|
+
};
|
|
3251
|
+
declare function verifyCall(opts: VerifyCallOptions): Promise<VerifyResult>;
|
|
3252
|
+
//#endregion
|
|
3253
|
+
//#region src/identity/seededPrincipal.d.ts
|
|
3254
|
+
interface SeededPrincipalOptions {
|
|
3255
|
+
/** Numeric seed; the same value always yields the same identity / principal. */
|
|
3256
|
+
readonly seed: number;
|
|
3257
|
+
}
|
|
3258
|
+
/**
|
|
3259
|
+
* Build a **random**, in-memory {@link Principal}: a fresh Ed25519 signing
|
|
3260
|
+
* identity + X25519 wrapping key, paired with an empty memory-only
|
|
3261
|
+
* {@link CapBag}. Full-entropy (unlike {@link createSeededMemoryPrincipal}), so
|
|
3262
|
+
* it is safe to sign trusted calls — e.g. an in-process "agent" identity a hub
|
|
3263
|
+
* admin delegates read authority to (via a minted capability) for directory
|
|
3264
|
+
* discovery. The identity is ephemeral: it vanishes with the process.
|
|
3265
|
+
*/
|
|
3266
|
+
declare function createMemoryPrincipal(): Promise<Principal>;
|
|
3267
|
+
/**
|
|
3268
|
+
* Build a deterministic {@link Principal} from a numeric seed: a reproducible
|
|
3269
|
+
* Ed25519 signing identity (so the nodeId is stable across runs) plus a matching
|
|
3270
|
+
* X25519 wrapping key, paired with an empty, memory-only {@link CapBag}.
|
|
3271
|
+
*
|
|
3272
|
+
* For tests and reproducible local setups ONLY — the key material is derived
|
|
3273
|
+
* from a low-entropy seed and must never be used to sign anything trusted.
|
|
3274
|
+
*/
|
|
3275
|
+
declare function createSeededMemoryPrincipal(opts: SeededPrincipalOptions): Promise<Principal>;
|
|
3276
|
+
/**
|
|
3277
|
+
* Derive a deterministic {@link KeypairSigningIdentity} from a numeric seed: a
|
|
3278
|
+
* reproducible Ed25519 signing identity (so the nodeId is stable across runs),
|
|
3279
|
+
* with no wrapping key. Use it where only signing/issuing is needed — e.g. a
|
|
3280
|
+
* hub admin `issuer` that mints capabilities in tests.
|
|
3281
|
+
*
|
|
3282
|
+
* For tests and reproducible local setups ONLY — the key material is derived
|
|
3283
|
+
* from a low-entropy seed and must never be used to sign anything trusted.
|
|
3284
|
+
*/
|
|
3285
|
+
declare function createSeededSigningIdentity(opts: SeededPrincipalOptions): Promise<KeypairSigningIdentity>;
|
|
3286
|
+
//#endregion
|
|
3287
|
+
//#region src/hub/common/reflection.interfaces.d.ts
|
|
3288
|
+
declare const defaultsInterface: InterfaceDefinition<{
|
|
3289
|
+
get: RequestType<Record<string, never>, {
|
|
3290
|
+
serviceId?: string | undefined;
|
|
3291
|
+
interfaceId?: string | undefined;
|
|
3292
|
+
interfaceHash?: string | undefined;
|
|
3293
|
+
}, any, never, never>;
|
|
3294
|
+
listBindings: RequestType<Record<string, never>, {
|
|
3295
|
+
bindings: {
|
|
3296
|
+
prefix: string;
|
|
3297
|
+
interfaceId: string;
|
|
3298
|
+
interfaceHash: string;
|
|
3299
|
+
serviceId?: string | undefined;
|
|
3300
|
+
}[];
|
|
3301
|
+
}, any, never, never>;
|
|
3302
|
+
}>;
|
|
3303
|
+
/**
|
|
3304
|
+
* A single root-principal requirement. `transitive: true` means the requirement
|
|
3305
|
+
* also applies to every service reachable *through* this one — i.e. when this
|
|
3306
|
+
* listing is a `hubrpc.directory` reference, everything it lists inherits the
|
|
3307
|
+
* requirement (also as transitive).
|
|
3308
|
+
*/
|
|
3309
|
+
declare const zRootPrincipalReq: import("zod/mini").ZodMiniObject<{
|
|
3310
|
+
principal: import("zod/mini").ZodMiniString<string>;
|
|
3311
|
+
transitive: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniBoolean<boolean>>;
|
|
3312
|
+
}, import("zod/v4/core").$strip>;
|
|
3313
|
+
/** One OR-set. The set is satisfied by holding *any one* of its principals. */
|
|
3314
|
+
declare const zRootPrincipalSet: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
|
|
3315
|
+
principal: import("zod/mini").ZodMiniString<string>;
|
|
3316
|
+
transitive: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniBoolean<boolean>>;
|
|
3317
|
+
}, import("zod/v4/core").$strip>>;
|
|
3318
|
+
type RootPrincipalReq = output<typeof zRootPrincipalReq>;
|
|
3319
|
+
type RootPrincipalSet = output<typeof zRootPrincipalSet>;
|
|
3320
|
+
declare const zServiceIdPattern: import("zod/mini").ZodMiniUnion<readonly [import("zod/mini").ZodMiniObject<{
|
|
3321
|
+
exact: import("zod/mini").ZodMiniString<string>;
|
|
3322
|
+
}, import("zod/v4/core").$strip>, import("zod/mini").ZodMiniObject<{
|
|
3323
|
+
prefix: import("zod/mini").ZodMiniString<string>;
|
|
3324
|
+
}, import("zod/v4/core").$strip>]>;
|
|
3325
|
+
type ServiceIdPattern = output<typeof zServiceIdPattern>;
|
|
3326
|
+
declare const directoryInterface: InterfaceDefinition<{
|
|
3327
|
+
list: RequestType<{
|
|
3328
|
+
interfaceId?: string | undefined;
|
|
3329
|
+
interfaceIdPrefix?: string | undefined;
|
|
3330
|
+
serviceId?: string | undefined;
|
|
3331
|
+
serviceIdScopes?: ({
|
|
3332
|
+
exact: string;
|
|
3333
|
+
} | {
|
|
3334
|
+
prefix: string;
|
|
3335
|
+
})[] | undefined;
|
|
3336
|
+
cursor?: string | undefined;
|
|
3337
|
+
limit?: number | undefined;
|
|
3338
|
+
timeoutMs?: number | undefined;
|
|
3339
|
+
}, {
|
|
3340
|
+
items: {
|
|
3341
|
+
serviceId: string;
|
|
3342
|
+
interfaceId: string;
|
|
3343
|
+
interfaceHash: string;
|
|
3344
|
+
serviceDescription?: string | undefined;
|
|
3345
|
+
rootPrincipalSets?: {
|
|
3346
|
+
principal: string;
|
|
3347
|
+
transitive?: boolean | undefined;
|
|
3348
|
+
}[][] | undefined;
|
|
3349
|
+
reachableServiceIds?: ({
|
|
3350
|
+
exact: string;
|
|
3351
|
+
} | {
|
|
3352
|
+
prefix: string;
|
|
3353
|
+
})[] | undefined;
|
|
3354
|
+
}[];
|
|
3355
|
+
nextCursor?: string | undefined;
|
|
3356
|
+
truncated?: boolean | undefined;
|
|
3357
|
+
}, any, never, never>;
|
|
3358
|
+
/**
|
|
3359
|
+
* Coarse change tap on the directory.
|
|
3360
|
+
*
|
|
3361
|
+
* `watch` is a long-lived streaming request that emits an **empty tick**
|
|
3362
|
+
* whenever the (optionally filtered) directory *might* have changed. The
|
|
3363
|
+
* tick carries no delta and no payload — its only meaning is "re-`list`
|
|
3364
|
+
* now". The consumer reconciles against its own last snapshot.
|
|
3365
|
+
*
|
|
3366
|
+
* This keeps the server stateless: it never computes or replays
|
|
3367
|
+
* per-item deltas, never does an initial-sync replay. Over-emission is
|
|
3368
|
+
* allowed (the consumer re-lists and finds nothing new); under-emission
|
|
3369
|
+
* is not. Ticks are coalesced. The `interfaceId` / `serviceId` filters
|
|
3370
|
+
* mirror `list` and are a relevance hint, not a guarantee.
|
|
3371
|
+
*
|
|
3372
|
+
* The request resolves when the caller cancels (or the connection
|
|
3373
|
+
* drops); the runtime auto-detaches the stream when it settles.
|
|
3374
|
+
*/
|
|
3375
|
+
watch: RequestType<{
|
|
3376
|
+
interfaceId?: string | undefined;
|
|
3377
|
+
interfaceIdPrefix?: string | undefined;
|
|
3378
|
+
serviceId?: string | undefined;
|
|
3379
|
+
serviceIdScopes?: ({
|
|
3380
|
+
exact: string;
|
|
3381
|
+
} | {
|
|
3382
|
+
prefix: string;
|
|
3383
|
+
})[] | undefined;
|
|
3384
|
+
}, Record<string, never>, void, any, Record<string, never>>;
|
|
3385
|
+
}>;
|
|
3386
|
+
/**
|
|
3387
|
+
* A `directoryInterface.watch` handler for a **static** directory. It opens the
|
|
3388
|
+
* stream, never ticks, and resolves when the caller cancels. Dynamic providers,
|
|
3389
|
+
* including {@link LinkRpcConnection.enableReflection}, use a change-emitting
|
|
3390
|
+
* implementation instead.
|
|
3391
|
+
*/
|
|
3392
|
+
declare function directoryWatchNever(_params: {
|
|
3393
|
+
interfaceId?: string;
|
|
3394
|
+
interfaceIdPrefix?: string;
|
|
3395
|
+
serviceId?: string;
|
|
3396
|
+
serviceIdScopes?: ServiceIdPattern[];
|
|
3397
|
+
}, _ctx: unknown, stream: StreamApi<unknown, Record<string, never>>): Promise<Record<string, never>>;
|
|
3398
|
+
declare const schemasInterface: InterfaceDefinition<{
|
|
3399
|
+
get: RequestType<{
|
|
3400
|
+
interfaceId: string;
|
|
3401
|
+
hash?: string | undefined;
|
|
3402
|
+
}, {
|
|
3403
|
+
schema: unknown;
|
|
3404
|
+
}, any, never, never>;
|
|
3405
|
+
}>;
|
|
3406
|
+
//#endregion
|
|
3407
|
+
//#region src/connection/linkRpcConnection.d.ts
|
|
3408
|
+
interface LinkRpcConnectionOptions {
|
|
3409
|
+
/** Topology node/port ID generator. Defaults to cryptographic randomness. */
|
|
3410
|
+
readonly generateTopologyId?: TopologyIdGenerator;
|
|
3411
|
+
/**
|
|
3412
|
+
* Validate parameters passed through typed interface clients before sending
|
|
3413
|
+
* them. Enabled by default. Disable only when interoperating with a peer
|
|
3414
|
+
* whose accepted wire shape intentionally differs from the local schema.
|
|
3415
|
+
*/
|
|
3416
|
+
readonly validateOutboundParams?: boolean;
|
|
3417
|
+
}
|
|
3418
|
+
/**
|
|
3419
|
+
* High-level linkrpc connection. Layers method-name routing and zod-driven
|
|
3420
|
+
* validation on top of a plain JSON-RPC channel.
|
|
3421
|
+
*
|
|
3422
|
+
* Typed proxies built by this class merely forward each call's
|
|
3423
|
+
* `interfaceHash` hint to the channel so the signer stamps it into the
|
|
3424
|
+
* `$hubrpc` envelope.
|
|
3425
|
+
*
|
|
3426
|
+
* `TInCtx` is the per-call out-of-band context the connection's transport
|
|
3427
|
+
* carries. Default `undefined` covers cross-process and ordinary in-process
|
|
3428
|
+
* transports. The hub's self/overlay connections instantiate with a
|
|
3429
|
+
* concrete `TInCtx` (e.g. `Participant`) so handlers can see who originated
|
|
3430
|
+
* the call.
|
|
3431
|
+
*
|
|
3432
|
+
* `TOutCtx` is the per-call override / extension bag the outbound sender
|
|
3433
|
+
* understands (see {@link SendOpts.ctx}). It only ever flows into the
|
|
3434
|
+
* sender as input, so it is a contravariant type parameter. Default `any`
|
|
3435
|
+
* keeps the bare `LinkRpcConnection` a valid supertype for holders that do
|
|
3436
|
+
* not care about the outbound ctx; pass a concrete shape (e.g.
|
|
3437
|
+
* `SigningCallCtx`) to get precise {@link get} typing. The connection
|
|
3438
|
+
* itself is agnostic to its contents — it merely forwards the `ctx`
|
|
3439
|
+
* defaults supplied to {@link get}.
|
|
3440
|
+
*/
|
|
3441
|
+
declare class LinkRpcConnection<TInCtx = any, TOutCtx = any> {
|
|
3442
|
+
/**
|
|
3443
|
+
* Convenience: build a {@link JsonRpcChannel} `Channel` from the
|
|
3444
|
+
* given transport and wrap it in an `LinkRpcConnection`. Use this
|
|
3445
|
+
* when you have a transport at hand and don't need a decorator
|
|
3446
|
+
* stack (e.g. signing).
|
|
3447
|
+
*/
|
|
3448
|
+
static fromTransport<TInCtx = undefined>(transport: IMessageTransport<MessageWithCtx<TInCtx>, JsonRpcMessage>, options?: LinkRpcConnectionOptions): LinkRpcConnection<TInCtx>;
|
|
3449
|
+
/** Underlying JSON-RPC sender — useful for callers that need raw access (e.g. to call hub-served methods that bypass the interface registry). */
|
|
3450
|
+
readonly channel: IRequestSender<TOutCtx>;
|
|
3451
|
+
/** key = `${serviceId ?? ""}::${interfaceId}` */
|
|
3452
|
+
private readonly _registry;
|
|
3453
|
+
/** Descriptions for services that have been registered with a `serviceDescription`. */
|
|
3454
|
+
private readonly _serviceDescriptions;
|
|
3455
|
+
/** Root-node-id requirement sets recorded per serviceId. */
|
|
3456
|
+
private readonly _serviceRootPrincipalSets;
|
|
3457
|
+
private readonly _directoryWatchers;
|
|
3458
|
+
private readonly _directoryListeners;
|
|
3459
|
+
private _inspection;
|
|
3460
|
+
private _trafficInspector;
|
|
3461
|
+
private readonly _serviceInspectionRegistrations;
|
|
3462
|
+
private readonly _wireChannel;
|
|
3463
|
+
private readonly _validateOutboundParams;
|
|
3464
|
+
/** Bare-method bindings, keyed by their exact wire prefix. */
|
|
3465
|
+
private readonly _bareBindings;
|
|
3466
|
+
private readonly _generateTopologyId;
|
|
3467
|
+
/**
|
|
3468
|
+
* Construct from a {@link Channel} (binds the inbound handler and uses
|
|
3469
|
+
* `channel.sender` for outbound calls) or from a bare
|
|
3470
|
+
* {@link IRequestSender} (send-only — no inbound handler is registered,
|
|
3471
|
+
* useful for bootstrap flows like `createManagedIdentity`).
|
|
3472
|
+
*/
|
|
3473
|
+
constructor(channel: Channel<TInCtx, TOutCtx> | IRequestSender<TOutCtx>, options?: LinkRpcConnectionOptions);
|
|
3474
|
+
/** Get a typed metadata-free client for a bundled bare interface target. */
|
|
3475
|
+
get<TDef extends InterfaceDefinition<any>>(target: BareInterfaceTarget<TDef>): InterfaceClient<TDef>;
|
|
3476
|
+
/** Get a typed client for `iface`, routed to the implicit (root) service. */
|
|
3477
|
+
get<TDef extends InterfaceDefinition<any>>(iface: TDef, opts?: GetOptions<TOutCtx>): InterfaceClient<TDef>;
|
|
3478
|
+
/**
|
|
3479
|
+
* Get a typed client that emits foreign-protocol bare method names.
|
|
3480
|
+
* Unlike {@link get}, these calls carry no LinkRPC interface metadata.
|
|
3481
|
+
*/
|
|
3482
|
+
getBare<TDef extends InterfaceDefinition<any>>(iface: TDef, opts?: BareGetOptions): InterfaceClient<TDef>;
|
|
3483
|
+
/** Get a service-scoped handle; all interfaces obtained from it route via `serviceId` (form 3). */
|
|
3484
|
+
service(serviceId: string): ServiceHandle<TInCtx, TOutCtx>;
|
|
3485
|
+
/** Register handlers for an interface on this connection's server side. */
|
|
3486
|
+
register<TDef extends InterfaceDefinition<any>>(iface: TDef, handlers: InterfaceHandlers<TDef, TInCtx>, opts?: RegisterOptions): InterfaceRegistration;
|
|
3487
|
+
private _register;
|
|
3488
|
+
/**
|
|
3489
|
+
* Declare the preset interface for form-1 (bare-method) dispatch. The
|
|
3490
|
+
* interface must already be registered under the root (no serviceId).
|
|
3491
|
+
* Surfaced via `hubrpc.defaults::get`.
|
|
3492
|
+
*/
|
|
3493
|
+
setPreset(iface: InterfaceDefinition<any>): void;
|
|
3494
|
+
/**
|
|
3495
|
+
* Bind foreign-protocol bare methods to an already registered interface.
|
|
3496
|
+
* Matching uses the longest prefix; once selected, a missing member does
|
|
3497
|
+
* not fall through to a shorter binding.
|
|
3498
|
+
*/
|
|
3499
|
+
bindBare(iface: InterfaceDefinition<any>, opts: {
|
|
3500
|
+
prefix: string;
|
|
3501
|
+
serviceId?: string;
|
|
3502
|
+
}): InterfaceRegistration;
|
|
3503
|
+
/** Snapshot of every interface currently registered on this connection. */
|
|
3504
|
+
listRegisteredInterfaces(): readonly {
|
|
3505
|
+
readonly serviceId: string;
|
|
3506
|
+
readonly interfaceId: string;
|
|
3507
|
+
readonly interfaceHash: string;
|
|
3508
|
+
readonly serviceDescription?: string;
|
|
3509
|
+
readonly rootPrincipalSets?: readonly RootPrincipalSet[];
|
|
3510
|
+
}[];
|
|
3511
|
+
/** Subscribe to coarse local directory changes; listeners must re-list. */
|
|
3512
|
+
onDidChangeDirectory(listener: () => void): () => void;
|
|
3513
|
+
/**
|
|
3514
|
+
* Look up a registered interface definition by id (and optional content
|
|
3515
|
+
* hash). Returns `undefined` if no registered interface matches.
|
|
3516
|
+
*/
|
|
3517
|
+
findRegisteredInterface(interfaceId: string, hash?: string): InterfaceDefinition<any> | undefined;
|
|
3518
|
+
/**
|
|
3519
|
+
* Register the three linkrpc reflection interfaces (`defaults`,
|
|
3520
|
+
* `directory`, `schemas`), backed by this connection's live registry.
|
|
3521
|
+
*
|
|
3522
|
+
* By default they live under the root service (form-2 reachable as
|
|
3523
|
+
* `hubrpc.directory::list`). Pass `serviceId` to additionally mount
|
|
3524
|
+
* them under a specific service — useful for participants that live
|
|
3525
|
+
* behind a hub, so callers can reach reflection via form-3
|
|
3526
|
+
* `<serviceId>::hubrpc.directory::list`.
|
|
3527
|
+
*
|
|
3528
|
+
* Idempotent: re-registering the same `(serviceId, interfaceId)` pair
|
|
3529
|
+
* is a no-op.
|
|
3530
|
+
*/
|
|
3531
|
+
enableReflection(opts?: {
|
|
3532
|
+
serviceId?: string;
|
|
3533
|
+
}): InterfaceRegistration;
|
|
3534
|
+
private _notifyDirectoryWatchers;
|
|
3535
|
+
/**
|
|
3536
|
+
* Enable the root `hubrpc.node::getNodeId` topology-bootstrap service.
|
|
3537
|
+
*
|
|
3538
|
+
* The generated ids are stable while this registration is active. They are
|
|
3539
|
+
* unauthenticated correlation labels only; callers must not use them for
|
|
3540
|
+
* identity, capability, or authorization decisions.
|
|
3541
|
+
*/
|
|
3542
|
+
enableInspection(descriptors?: readonly ParticipantDescriptorSource[]): InspectionRegistration;
|
|
3543
|
+
/** Number of active endpoint traffic stream subscribers. */
|
|
3544
|
+
get trafficObserverCount(): number;
|
|
3545
|
+
close(): void;
|
|
3546
|
+
private _buildClient;
|
|
3547
|
+
private _validateOutboundParamsFor;
|
|
3548
|
+
private _handleRequest;
|
|
3549
|
+
private _buildStreamApi;
|
|
3550
|
+
private _handleNotification;
|
|
3551
|
+
private _parseRouted;
|
|
3552
|
+
private _installServiceInspection;
|
|
3553
|
+
private _removeServiceInspection;
|
|
3554
|
+
private _hasBusinessServiceRegistration;
|
|
3555
|
+
private _endpointGraph;
|
|
3556
|
+
private _watchTraffic;
|
|
3557
|
+
}
|
|
3558
|
+
/**
|
|
3559
|
+
* Options when obtaining a typed client for an interface.
|
|
3560
|
+
*
|
|
3561
|
+
* - `serviceId`: route to a specific service (form 3). Omit for form 2
|
|
3562
|
+
* (implicit / root service on the connection).
|
|
3563
|
+
* - any `TOutCtx` property: a per-client default merged into the `ctx`
|
|
3564
|
+
* of every call issued through the returned proxy (e.g.
|
|
3565
|
+
* `signerOverride`, `capsOverride` for a signing channel). The
|
|
3566
|
+
* connection forwards these verbatim; it does not interpret them.
|
|
3567
|
+
*
|
|
3568
|
+
* Schema-version pinning travels as interface-level call metadata
|
|
3569
|
+
* ({@link SendOpts.interfaceHash}); the typed proxy stamps
|
|
3570
|
+
* `iface.schemaHash` automatically, independent of `TOutCtx`.
|
|
3571
|
+
*/
|
|
3572
|
+
type GetOptions<TOutCtx = undefined> = {
|
|
3573
|
+
serviceId?: string;
|
|
3574
|
+
} & Partial<TOutCtx>;
|
|
3575
|
+
/** Options for a metadata-free, bare-method typed client. */
|
|
3576
|
+
interface BareGetOptions {
|
|
3577
|
+
prefix?: string;
|
|
3578
|
+
}
|
|
3579
|
+
interface RegisterOptions {
|
|
3580
|
+
/**
|
|
3581
|
+
* If set, this interface is mounted under this service id (form 3).
|
|
3582
|
+
*/
|
|
3583
|
+
serviceId?: string;
|
|
3584
|
+
/**
|
|
3585
|
+
* Optional human description recorded for `serviceId` and surfaced
|
|
3586
|
+
* through `hubrpc.directory::list`. Requires `serviceId`. The first
|
|
3587
|
+
* registration's description wins; any later registration that
|
|
3588
|
+
* supplies a *different* non-undefined description throws.
|
|
3589
|
+
*/
|
|
3590
|
+
serviceDescription?: string;
|
|
3591
|
+
/**
|
|
3592
|
+
* Root node ids required to access `serviceId`, in CNF (AND of OR-sets):
|
|
3593
|
+
* the caller must satisfy **every** set, and a set is satisfied by **any
|
|
3594
|
+
* one** of its node ids. Surfaced through `hubrpc.directory::list`.
|
|
3595
|
+
* Requires `serviceId`. Recorded per service; a later registration that
|
|
3596
|
+
* supplies a *different* value for the same `serviceId` throws.
|
|
3597
|
+
*/
|
|
3598
|
+
rootPrincipalSets?: readonly RootPrincipalSet[];
|
|
3599
|
+
}
|
|
3600
|
+
/** A live interface registration. Disposing it removes dispatch and reflection state. */
|
|
3601
|
+
interface InterfaceRegistration {
|
|
3602
|
+
/** Remove this exact registration. Idempotent. */
|
|
3603
|
+
dispose(): void;
|
|
3604
|
+
}
|
|
3605
|
+
/** Generated topology identity and its live root-interface registration. */
|
|
3606
|
+
interface InspectionRegistration extends InterfaceRegistration, NodeInfo {
|
|
3607
|
+
/**
|
|
3608
|
+
* Observe this endpoint's full-payload traffic in-process. Wire observation
|
|
3609
|
+
* remains disabled until either this or an RPC traffic watch is active.
|
|
3610
|
+
*/
|
|
3611
|
+
observeTraffic(observer: (transit: TrafficTransitEvent) => void): InterfaceRegistration;
|
|
3612
|
+
}
|
|
3613
|
+
/** Service-scoped handle returned by `connection.service(id)`. */
|
|
3614
|
+
declare class ServiceHandle<TInCtx = undefined, TOutCtx = undefined> {
|
|
3615
|
+
private readonly _connection;
|
|
3616
|
+
private readonly _serviceId;
|
|
3617
|
+
constructor(_connection: LinkRpcConnection<TInCtx, TOutCtx>, _serviceId: string);
|
|
3618
|
+
get<TDef extends InterfaceDefinition<any>>(iface: TDef, opts?: Partial<TOutCtx>): InterfaceClient<TDef>;
|
|
3619
|
+
register<TDef extends InterfaceDefinition<any>>(iface: TDef, handlers: InterfaceHandlers<TDef, TInCtx>, opts?: Omit<RegisterOptions, 'serviceId'>): InterfaceRegistration;
|
|
3620
|
+
}
|
|
3621
|
+
//#endregion
|
|
3622
|
+
export { ManagedIdentityStorageBackend as $, DiscriminatorSchema as $i, signedHash as $n, zTopologyLinkEndpoint as $r, Result as $t, VerifyRpcCallResult as A, InterfaceMemberRefMap as Ai, Capability as An, nodeInterface as Ar, WsNoInitEndpoint as At, OneShotCapStaging as B, RequestType as Bi, permissionPermits as Bn, TopologyTransportInfo as Br, ExpBackoffOptions as Bt, VerifyCallOptions as C, JsonValue as Ca, computeInterfaceHash as Ci, ParsedMethodName as Cn, StreamControlReason as Cr, CmdEnvEndpoint as Ct, SignRpcCallOptions as D, InterfaceHandlers as Di, Call as Dn, streamInterface as Dr, ResolvedEndpoint as Dt, verifyCall as E, InterfaceDefinitionOpts as Ei, Ability as En, StreamSendParams as Er, FormatEndpointOptions as Et, identityStorageInterface as F, defineInterface as Fi, TargetPattern as Fn, TopologyLink as Fr, DEFAULT_RPC_TIMEOUT_MS as Ft, createManagedPrincipal as G, zodToSvcJsonSchema as Gi, LINKRPC_META_KEY as Gn, TrafficWatchResult as Gr, Channel as Gt, SigningSender as H, ZodToSvcJsonSchemaOptions as Hi, HUBRPC_META_KEY as Hn, TrafficOverflowEvent as Hr, OnChannelConnect as Ht, crypto_d_exports as I, interfaceFromSchema as Ii, capabilityPermits as In, TopologyLinkEndpoint as Ir, withRpcTimeout as It, CapBagOptions as J, MemberAnnotations as Ji, SignDomain as Jn, zParticipantDescriptor as Jr, IRequestSender as Jt, Principal as K, ErrorSchema as Ki, LINKRPC_SIGNATURE_KEY as Kn, topologyInterface as Kr, ChannelTransport as Kt, CapProvider as L, MemberDocs as Li, hasSignedCapabilityShape as Ln, TopologyNode as Lr, JsonRpcChannel as Lt, signRpcCall as M, StreamApi as Mi, Pattern as Mn, ParticipantDescriptorSource as Mr, isHubEndpoint as Mt, verifyRpcCall as N, StreamCallOptions as Ni, Permission as Nn, RouteClaim as Nr, parseEndpointUri as Nt, SignedRpcCall as O, InterfaceInfo as Oi, CallBind as On, NodeInfo as Or, SocketEndpoint as Ot, identityInterface as P, StreamingCall as Pi, SignedCapability as Pn, TopologyGraph as Pr, CancellableRequest as Pt, ManagedIdentityStorage as Q, ConstSchema as Qi, readSignature as Qn, zTopologyLink as Qr, RawStreamingCall as Qt, CapProviderResult as R, MemberType as Ri, matchParams as Rn, TopologyPort as Rr, ChannelConnector as Rt, SignParamsOptions as S, isResponse as Sa, EXTENSION_PREFIX as Si, stripLinkRpcWireMeta as Sn, STREAM_METHOD as Sr, WrappingIdentity as St, signParams as T, InterfaceDefinition as Ti, parseMethodName as Tn, StreamDir as Tr, EndpointCommand as Tt, SigningSenderConfig as U, notificationType as Ui, HUBRPC_SIGNATURE_KEY as Un, TrafficTransitEndpoint as Ur, BareInterfaceTarget as Ut, SigningCallCtx as V, Schema as Vi, Base64Sha256 as Vn, TrafficEvent as Vr, KeepConnectedHandle as Vt, PrincipalWithStore as W, requestType as Wi, HUBRPC_UNSIGNED_KEY as Wn, TrafficTransitEvent as Wr, bareInterfaceTarget as Wt, InMemoryManagedIdentityStorage as X, ArraySchema as Xi, Signatures as Xn, zRouteClaim as Xr, IncomingStream as Xt, InMemoryManagedIdentity as Y, MethodSchema as Yi, SignatureEnvelope as Yn, zParticipantDescriptorSource as Yr, IncomingCall as Yt, ManagedIdentity as Z, BooleanSchema as Zi, getKeyId as Zn, zTopologyGraph as Zr, MessageWithCtx as Zt, SeededPrincipalOptions as _, JsonRpcResponse as _a, componentSchemaName as _i, LinkRpcJsonRpcRequest as _n, principalForPublicKey as _r, KeypairSigningIdentity as _t, LinkRpcConnection as a, ObjectSchema as aa, zTrafficOverflowEvent as ai, IMessageTransport as an, Keypair as ar, ParamMatcherFor as at, createSeededSigningIdentity as b, isNotification as ba, isAssignable as bi, LinkRpcWireParams as bn, jcsCanonicalize as br, SerializedKeypairSigningIdentity as bt, ServiceHandle as c, SchemaBase as ca, zTrafficWatchResult as ci, MessageTransportWithContext as cn, PrivateKey as cr, capBagFreshAt as ct, ServiceIdPattern as d, UnionSchema as da, SchemaToZodContext as di, connectTransports as dn, Signature as dr, issueCapability as dt, EnumSchema as ea, zTopologyNode as ei, RpcError as en, signingDomainValue as er, createManagedIdentity as et, defaultsInterface as f, ErrorCode as fa, createSchemaToZod as fi, traceMessageTransport as fn, X25519Keypair as fr, permits as ft, zServiceIdPattern as g, JsonRpcRequest as ga, assertSchemaReferences as gi, LinkRpcJsonRpcNotification as gn, keyIdForPublicKey as gr, KeypairIdentity as gt, schemasInterface as h, JsonRpcNotification as ha, materializeJsonSchema as hi, LinkRpcJsonRpcMessage as hn, keyIdForPrincipal as hr, Identity as ht, InterfaceRegistration as i, NumberSchema as ia, zTrafficEvent as ii, MuxEnvelope as in, KeyId as ir, IssueCapabilityOptions as it, attachCapabilities as j, MemberMap as ji, ParamMatcher as jn, ParticipantDescriptor as jr, formatEndpointUri as jt, VerifyRpcCallOptions as k, InterfaceMemberRef as ki, CallTarget as kn, TopologyIdGenerator as kr, WsEndpoint as kt, RootPrincipalReq as l, StringSchema as la, GenerateInterfaceOptions as li, MessageWithContext as ln, PublicKey as lr, capabilityFreshAt as lt, directoryWatchNever as m, JsonRpcMessage as ma, MaterializedJsonSchema as mi, CallMeta as mn, bytesToBase64Url as mr, signCapability as mt, GetOptions as n, LinkRpcJsonSchema as na, zTopologyTransportEndpoint as ni, StreamSendOpts as nn, withSignature as nr, registerLazyIdentityOnOverlay as nt, LinkRpcConnectionOptions as o, OneOfSchema as oa, zTrafficTransitEndpoint as oi, MessageTransportDirection as on, PRINCIPAL_PREFIX as or, ParamMatchers as ot, directoryInterface as p, JsonRpcError as pa, schemaToZod as pi, IDisposable as pn, base64UrlToBytes as pr, prefix as pt, CapBag as q, LinkRpcInterfaceSchema as qi, LINKRPC_UNSIGNED_KEY as qn, trafficInterface as qr, IRequestHandler as qt, InspectionRegistration as r, NullSchema as ra, zTopologyTransportInfo as ri, MultiplexedTransport as rn, KEY_ID_PREFIX as rr, AcceptedRootIssuer as rt, RegisterOptions as s, RefSchema as sa, zTrafficTransitEvent as si, MessageTransportTrace as sn, PrincipalId as sr, PermitResult as st, BareGetOptions as t, IntegerSchema as ta, zTopologyPort as ti, SendOpts as tn, signingInput as tr, registerIdentityOnOverlay as tt, RootPrincipalSet as u, TupleSchema as ua, generateTsInterface as ui, TransportPair as un, ResolveSigningKeyArgs as ur, invoke as ut, createMemoryPrincipal as v, JsonRpcSuccess as va, componentSchemaRef as vi, LinkRpcUnsigned as vn, publicKeyForKeyId as vr, PublicSigningIdentity as vt, VerifyResult as w, InterfaceClient as wi, methodNameToTarget as wn, StreamControlType as wr, CmdStdioEndpoint as wt, JsonObject as x, isRequest as xa, normalizeJsonSchema as xi, requireObjectParams as xn, jcsCanonicalizeBytes as xr, SigningIdentity as xt, createSeededMemoryPrincipal as y, RequestId as ya, Components as yi, LinkRpcWireMeta as yn, resolveSigningKey as yr, PublicWrappingIdentity as yt, ManagedSigningChannel as z, NotificationType as zi, permissionMatchesTarget as zn, TopologyTransportEndpoint as zr, ConnectableChannel as zt };
|
|
3623
|
+
//# sourceMappingURL=linkRpcConnection-CtlQmetO.d.ts.map
|