@cortexkit/subc-client 0.20.0 → 0.21.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 +48 -1
- package/dist/client.d.ts +23 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +97 -12
- package/dist/client.js.map +1 -1
- package/dist/connection-file.d.ts +6 -1
- package/dist/connection-file.d.ts.map +1 -1
- package/dist/connection-file.js +32 -10
- package/dist/connection-file.js.map +1 -1
- package/dist/envelope.d.ts +11 -1
- package/dist/envelope.d.ts.map +1 -1
- package/dist/envelope.js +23 -7
- package/dist/envelope.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +17 -8
- package/dist/provider.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +112 -9
- package/src/connection-file.ts +37 -10
- package/src/envelope.ts +32 -13
- package/src/index.ts +1 -0
- package/src/provider.ts +19 -7
package/README.md
CHANGED
|
@@ -78,6 +78,45 @@ fails loud with an `AuthError` rather than connecting insecurely.
|
|
|
78
78
|
- **Connection-file security.** On unix the file must be owner-only (`0600`); a
|
|
79
79
|
group/world-readable file is rejected, because the key has effectively leaked.
|
|
80
80
|
|
|
81
|
+
### Scoped routes
|
|
82
|
+
|
|
83
|
+
Use a daemon advertising `scopes/v1` and pass a selector to `routeOpen()` or to
|
|
84
|
+
managed `call()` / `callBinary()` options:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const scope = {
|
|
88
|
+
owner: { kind: "reserved", module_id: "session-owner" } as const,
|
|
89
|
+
ref: "session-1",
|
|
90
|
+
scopeEpoch: 1,
|
|
91
|
+
};
|
|
92
|
+
const route = await client.routeOpen(target, identity, { scope });
|
|
93
|
+
await client.call("provider", "echo", {}, { scope });
|
|
94
|
+
// Close only this scoped managed route, not the unscoped or another scope's route.
|
|
95
|
+
await client.closeManagedRoute(target, identity, { scope });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The daemon checks who the caller is from its own connection record, not from the request: the caller must be the module that registered the scope (its owner), or a module the owner listed as a carrier on that scope record.
|
|
99
|
+
Passing `scope` does not grant that authority: use the supervised caller's
|
|
100
|
+
`consumerIdentity` (or its default environment identity) as usual. `owner` uses
|
|
101
|
+
the existing `Principal` type; the reserved form above is the normal case, and
|
|
102
|
+
the daemon refuses other owner kinds.
|
|
103
|
+
|
|
104
|
+
The exact wire selector is
|
|
105
|
+
`scope: { owner: { kind: "reserved", module_id: "session-owner" }, ref: "session-1", scope_epoch: 1 }`.
|
|
106
|
+
With no selector, there is no `scope` key. A reserved owner's module id and the
|
|
107
|
+
ref must be non-empty strings, and `scopeEpoch` must be present and a safe
|
|
108
|
+
non-negative integer (zero is valid); invalid selectors fail locally before I/O.
|
|
109
|
+
|
|
110
|
+
Managed routes are cached separately by target, bind and consumer identities,
|
|
111
|
+
reverse capabilities, and the whole scope selector (owner, ref, epoch). Scoped
|
|
112
|
+
and unscoped calls never share a route, nor do different scopes or daemon
|
|
113
|
+
incarnations. Reconnect opens a fresh handle under the same selector.
|
|
114
|
+
`scope_not_synced` and `scope_changed` refusals retain the managed path's retries
|
|
115
|
+
within the caller's deadline. A scope-revocation close (such as `scope_ended` or
|
|
116
|
+
`scope_carrier_removed`) is terminal: neither a later managed call nor a
|
|
117
|
+
reconnect automatically reopens that cache entry. Use a new selector for a new
|
|
118
|
+
session; reload and restart close reasons still permit reopens.
|
|
119
|
+
|
|
81
120
|
### Launch nonce
|
|
82
121
|
|
|
83
122
|
A module the daemon spawns proves who it is with a launch nonce. On macOS and
|
|
@@ -115,7 +154,9 @@ the environment copy. The environment copy is read only when
|
|
|
115
154
|
## Testing
|
|
116
155
|
|
|
117
156
|
```sh
|
|
118
|
-
bun
|
|
157
|
+
bun run typecheck
|
|
158
|
+
bun test # unit/mock suite; live tests are gated by RUN_SUBC_LIVE=1
|
|
159
|
+
RUN_SUBC_LIVE=1 bun test tests/live-scoped-open.test.ts
|
|
119
160
|
```
|
|
120
161
|
|
|
121
162
|
The live-handshake tests boot the real daemon binary
|
|
@@ -124,6 +165,12 @@ byte-identity authority for this client. They skip automatically when the binary
|
|
|
124
165
|
is not built; run `cargo build -p subc-core` first (the CI lane does this; the
|
|
125
166
|
package builds the `ck-subc` executable).
|
|
126
167
|
|
|
168
|
+
The scope admission test uses the same supervised `fake-aft-stub` fixture as the
|
|
169
|
+
Rust SDK (built by `cargo build -p subc-core --bins`) to sync an owner's scope and
|
|
170
|
+
observe the real provider bind. Live helpers hard-link the daemon as
|
|
171
|
+
`ckdev-subc` inside the scratch runtime directory before spawning it, and isolate
|
|
172
|
+
all three XDG homes so tests cannot reach the operator's daemon data or config.
|
|
173
|
+
|
|
127
174
|
## Layout
|
|
128
175
|
|
|
129
176
|
| File | Responsibility |
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ConnectionInfo } from "./connection-file.js";
|
|
2
2
|
import { AdmissionClass, Priority } from "./envelope.js";
|
|
3
|
+
import type { Principal } from "./provider.js";
|
|
3
4
|
import { ReverseRequestRegistry, RouteHandle } from "./route-handle.js";
|
|
4
5
|
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
5
6
|
export declare const TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
@@ -29,7 +30,18 @@ export interface ConsumerIdentity {
|
|
|
29
30
|
module_id: string;
|
|
30
31
|
launch_nonce: string;
|
|
31
32
|
}
|
|
33
|
+
/** A scope selector; scopeEpoch is required even for the scope's owner. */
|
|
34
|
+
export interface RouteScope {
|
|
35
|
+
/** Owner principal, passed verbatim. The daemon decides which principal kinds may own scopes. */
|
|
36
|
+
owner: Principal;
|
|
37
|
+
/** Opaque, non-empty ref within the owner. */
|
|
38
|
+
ref: string;
|
|
39
|
+
/** Safe non-negative integer, including zero; serialized as scope_epoch. */
|
|
40
|
+
scopeEpoch: number;
|
|
41
|
+
}
|
|
32
42
|
export interface RouteOpenOptions {
|
|
43
|
+
/** Open under this scope. The caller must be its owner or a listed carrier; use a daemon advertising scopes/v1. Serialized as { owner, ref, scope_epoch }. */
|
|
44
|
+
scope?: RouteScope;
|
|
33
45
|
/** Optional override for the consumer identity; by default the SUBC_MODULE_ID environment variable and the process's launch nonce (see `launchNonce`) are used when both are non-empty; a refused launch-nonce descriptor counts as no nonce. Set null to send route.open without consumer_identity. */
|
|
34
46
|
consumerIdentity?: ConsumerIdentity | null;
|
|
35
47
|
/**
|
|
@@ -113,6 +125,8 @@ export interface RequestOptions {
|
|
|
113
125
|
signal?: AbortSignal;
|
|
114
126
|
}
|
|
115
127
|
export interface ManagedCallOptions extends RequestOptions {
|
|
128
|
+
/** Scope for the cached route and its reconnect reopens. The caller must be the scope's owner or a listed carrier; use a daemon advertising scopes/v1. Each owner/ref/epoch has a separate cache entry. */
|
|
129
|
+
scope?: RouteScope;
|
|
116
130
|
/** Overrides the per-client identity used for route.open before this call. */
|
|
117
131
|
identity?: BindIdentity;
|
|
118
132
|
/** Defaults to management_surface, matching the store/host management APIs. */
|
|
@@ -142,6 +156,8 @@ export interface CloseRouteOptions {
|
|
|
142
156
|
drain?: boolean;
|
|
143
157
|
}
|
|
144
158
|
export interface ManagedCloseRouteOptions extends CloseRouteOptions {
|
|
159
|
+
/** Scope selector used by the cached managed route; omitted selects only an unscoped route. */
|
|
160
|
+
scope?: RouteScope;
|
|
145
161
|
/** Consumer identity used by the cached managed route. */
|
|
146
162
|
consumerIdentity?: ConsumerIdentity | null;
|
|
147
163
|
/** The registry whose derived capability set identifies the cached route. */
|
|
@@ -343,6 +359,7 @@ export declare class SubcClient {
|
|
|
343
359
|
* Open a route and return its connection-bound immutable handle. At most
|
|
344
360
|
* MAX_ROUTE_OPENS_IN_FLIGHT opens are outstanding at once; further calls wait
|
|
345
361
|
* their turn.
|
|
362
|
+
* A scoped open requires the caller to be the scope's owner or a listed carrier.
|
|
346
363
|
*/
|
|
347
364
|
routeOpen(target: RouteTarget, identity: BindIdentity, opts?: RouteOpenOptions): Promise<RouteHandle>;
|
|
348
365
|
/** One route.open exchange. The caller holds a route-open gate slot. */
|
|
@@ -351,7 +368,10 @@ export declare class SubcClient {
|
|
|
351
368
|
request(handle: RouteHandle, body: unknown, opts?: RequestOptions): Promise<unknown>;
|
|
352
369
|
/**
|
|
353
370
|
* Managed route + request convenience. Opens and caches a route for the module,
|
|
354
|
-
* reconnecting
|
|
371
|
+
* reconnecting after transport drops with fresh route.open requests.
|
|
372
|
+
* Scoped routes are isolated by owner/ref/epoch and require owner or carrier authority.
|
|
373
|
+
* A scope-ending close permanently ends that scoped cache entry; other route
|
|
374
|
+
* closes are evicted so a later call asks the daemon to open a fresh route.
|
|
355
375
|
*/
|
|
356
376
|
call<Response = unknown>(moduleId: string, method: string, params?: unknown, opts?: ManagedCallOptions): Promise<Response>;
|
|
357
377
|
/**
|
|
@@ -464,6 +484,7 @@ export declare class SubcClient {
|
|
|
464
484
|
private reopenCachedRoutes;
|
|
465
485
|
private timeoutMessage;
|
|
466
486
|
private routeClosedDuringOpen;
|
|
487
|
+
private endedCachedRoute;
|
|
467
488
|
private readLoop;
|
|
468
489
|
private dispatch;
|
|
469
490
|
/**
|
|
@@ -483,6 +504,7 @@ export declare class SubcClient {
|
|
|
483
504
|
private errorFromFrame;
|
|
484
505
|
private evictRouteHandle;
|
|
485
506
|
private recordRouteEnd;
|
|
507
|
+
private handleCachedRouteClose;
|
|
486
508
|
private routeEndReason;
|
|
487
509
|
private failHandle;
|
|
488
510
|
private fail;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -140,6 +140,12 @@ function requireBinaryBody(body) {
|
|
|
140
140
|
const type = body === null ? "null" : Array.isArray(body) ? "array" : typeof body;
|
|
141
141
|
throw new SubcError(`binary request body must be a Uint8Array; got ${type}`, "binary_body_required");
|
|
142
142
|
}
|
|
143
|
+
const SCOPE_ROUTE_CLOSE_REASONS = new Set([
|
|
144
|
+
"scope_ended",
|
|
145
|
+
"scope_carrier_removed",
|
|
146
|
+
"scope_delegation_changed",
|
|
147
|
+
"scope_parent_ended",
|
|
148
|
+
]);
|
|
143
149
|
/**
|
|
144
150
|
* Decode a daemon close reason without rejecting a future wire value.
|
|
145
151
|
*
|
|
@@ -346,12 +352,14 @@ export class SubcClient {
|
|
|
346
352
|
* Open a route and return its connection-bound immutable handle. At most
|
|
347
353
|
* MAX_ROUTE_OPENS_IN_FLIGHT opens are outstanding at once; further calls wait
|
|
348
354
|
* their turn.
|
|
355
|
+
* A scoped open requires the caller to be the scope's owner or a listed carrier.
|
|
349
356
|
*/
|
|
350
357
|
async routeOpen(target, identity, opts = {}) {
|
|
358
|
+
const scope = validatedRouteScope(opts.scope);
|
|
351
359
|
// Without a timeout the gate always grants a slot eventually.
|
|
352
360
|
const release = (await this.routeOpenGate.acquire());
|
|
353
361
|
try {
|
|
354
|
-
return await this.sendRouteOpen(target, identity, opts);
|
|
362
|
+
return await this.sendRouteOpen(target, identity, { ...opts, scope });
|
|
355
363
|
}
|
|
356
364
|
finally {
|
|
357
365
|
release();
|
|
@@ -359,6 +367,7 @@ export class SubcClient {
|
|
|
359
367
|
}
|
|
360
368
|
/** One route.open exchange. The caller holds a route-open gate slot. */
|
|
361
369
|
async sendRouteOpen(target, identity, opts = {}) {
|
|
370
|
+
const scope = validatedRouteScope(opts.scope);
|
|
362
371
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
363
372
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry();
|
|
364
373
|
reverseRequests.seal();
|
|
@@ -369,6 +378,7 @@ export class SubcClient {
|
|
|
369
378
|
identity,
|
|
370
379
|
...(consumerIdentity ? { consumer_identity: consumerIdentity } : {}),
|
|
371
380
|
...(consumerCapabilities.length > 0 ? { consumer_capabilities: consumerCapabilities } : {}),
|
|
381
|
+
...(scope ? { scope: { owner: scope.owner, ref: scope.ref, scope_epoch: scope.scopeEpoch } } : {}),
|
|
372
382
|
});
|
|
373
383
|
let installed = null;
|
|
374
384
|
const install = (frame) => {
|
|
@@ -415,7 +425,10 @@ export class SubcClient {
|
|
|
415
425
|
}
|
|
416
426
|
/**
|
|
417
427
|
* Managed route + request convenience. Opens and caches a route for the module,
|
|
418
|
-
* reconnecting
|
|
428
|
+
* reconnecting after transport drops with fresh route.open requests.
|
|
429
|
+
* Scoped routes are isolated by owner/ref/epoch and require owner or carrier authority.
|
|
430
|
+
* A scope-ending close permanently ends that scoped cache entry; other route
|
|
431
|
+
* closes are evicted so a later call asks the daemon to open a fresh route.
|
|
419
432
|
*/
|
|
420
433
|
async call(moduleId, method, params, opts = {}) {
|
|
421
434
|
if (opts.binary) {
|
|
@@ -637,7 +650,7 @@ export class SubcClient {
|
|
|
637
650
|
}
|
|
638
651
|
/** Close a cached managed route by its route-open identity tuple. */
|
|
639
652
|
async closeManagedRoute(target, identity, opts = {}) {
|
|
640
|
-
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry()).capabilities());
|
|
653
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry()).capabilities(), validatedRouteScope(opts.scope));
|
|
641
654
|
const cached = this.routes.get(key);
|
|
642
655
|
if (!cached)
|
|
643
656
|
return;
|
|
@@ -734,13 +747,15 @@ export class SubcClient {
|
|
|
734
747
|
};
|
|
735
748
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
736
749
|
this.pending.set(key, pending);
|
|
737
|
-
if (signal)
|
|
738
|
-
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
739
750
|
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + ms).catch((error) => {
|
|
740
751
|
const current = this.pending.get(key);
|
|
741
752
|
if (current)
|
|
742
753
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
743
754
|
});
|
|
755
|
+
// Queue REQUEST first: an already-aborted signal emits CANCEL immediately,
|
|
756
|
+
// and the daemon can only cancel a correlation it has already received.
|
|
757
|
+
if (signal)
|
|
758
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
744
759
|
});
|
|
745
760
|
}
|
|
746
761
|
/**
|
|
@@ -832,8 +847,6 @@ export class SubcClient {
|
|
|
832
847
|
};
|
|
833
848
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
834
849
|
this.pending.set(key, pending);
|
|
835
|
-
if (signal)
|
|
836
|
-
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
837
850
|
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
838
851
|
handedToSocket = write.queued;
|
|
839
852
|
write.completed.catch((error) => {
|
|
@@ -841,9 +854,13 @@ export class SubcClient {
|
|
|
841
854
|
if (current)
|
|
842
855
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
843
856
|
});
|
|
857
|
+
// Preserve REQUEST/CANCEL order even when the signal was already aborted.
|
|
858
|
+
if (signal && write.queued)
|
|
859
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
844
860
|
});
|
|
845
861
|
}
|
|
846
862
|
async cachedRouteHandle(moduleId, opts) {
|
|
863
|
+
const scope = validatedRouteScope(opts.scope);
|
|
847
864
|
const identity = opts.identity ?? this.opts.identity;
|
|
848
865
|
if (!identity) {
|
|
849
866
|
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
@@ -852,7 +869,7 @@ export class SubcClient {
|
|
|
852
869
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
853
870
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry();
|
|
854
871
|
reverseRequests.seal();
|
|
855
|
-
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities());
|
|
872
|
+
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities(), scope);
|
|
856
873
|
let cached = this.routes.get(key);
|
|
857
874
|
if (!cached) {
|
|
858
875
|
cached = {
|
|
@@ -860,6 +877,7 @@ export class SubcClient {
|
|
|
860
877
|
moduleId,
|
|
861
878
|
target,
|
|
862
879
|
identity,
|
|
880
|
+
scope,
|
|
863
881
|
consumerIdentity,
|
|
864
882
|
reverseRequests,
|
|
865
883
|
handle: null,
|
|
@@ -867,6 +885,8 @@ export class SubcClient {
|
|
|
867
885
|
};
|
|
868
886
|
this.routes.set(key, cached);
|
|
869
887
|
}
|
|
888
|
+
if (cached.endReason !== undefined)
|
|
889
|
+
throw this.endedCachedRoute(cached.endReason);
|
|
870
890
|
if (cached.handle && this.isLiveHandle(cached.handle))
|
|
871
891
|
return cached.handle;
|
|
872
892
|
// The retries end at the retry deadline or at the call's own timeoutMs,
|
|
@@ -907,6 +927,8 @@ export class SubcClient {
|
|
|
907
927
|
for (;;) {
|
|
908
928
|
if (cached.closed)
|
|
909
929
|
throw this.routeClosedDuringOpen();
|
|
930
|
+
if (cached.endReason !== undefined)
|
|
931
|
+
throw this.endedCachedRoute(cached.endReason);
|
|
910
932
|
try {
|
|
911
933
|
await this.ensureConnectedForManaged();
|
|
912
934
|
}
|
|
@@ -923,6 +945,7 @@ export class SubcClient {
|
|
|
923
945
|
try {
|
|
924
946
|
outcome = {
|
|
925
947
|
handle: await this.sendRouteOpen(cached.target, cached.identity, {
|
|
948
|
+
scope: cached.scope,
|
|
926
949
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
927
950
|
reverseRequests: cached.reverseRequests,
|
|
928
951
|
}),
|
|
@@ -1168,10 +1191,11 @@ export class SubcClient {
|
|
|
1168
1191
|
}
|
|
1169
1192
|
for (const key of routeKeys) {
|
|
1170
1193
|
const cached = this.routes.get(key);
|
|
1171
|
-
if (!cached || cached.closed)
|
|
1194
|
+
if (!cached || cached.closed || cached.endReason !== undefined)
|
|
1172
1195
|
continue;
|
|
1173
1196
|
try {
|
|
1174
1197
|
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
1198
|
+
scope: cached.scope,
|
|
1175
1199
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
1176
1200
|
reverseRequests: cached.reverseRequests,
|
|
1177
1201
|
});
|
|
@@ -1204,6 +1228,12 @@ export class SubcClient {
|
|
|
1204
1228
|
const cause = new SubcError("route was closed before route.open completed", "route_closed", undefined, "closed_by_caller");
|
|
1205
1229
|
return new SubcCallError("not_sent", cause.message, cause.code, cause);
|
|
1206
1230
|
}
|
|
1231
|
+
endedCachedRoute(reason) {
|
|
1232
|
+
// This is only retained for scope-ending closes: the session is over, so a
|
|
1233
|
+
// later call under the same selector must not reopen it.
|
|
1234
|
+
const cause = new SubcError(`cached route closed by subc (${reason}); must not reopen`, "route_closed", undefined, reason);
|
|
1235
|
+
return this.terminalCallError(cause.message, cause);
|
|
1236
|
+
}
|
|
1207
1237
|
async readLoop(sock, generation) {
|
|
1208
1238
|
try {
|
|
1209
1239
|
for (;;) {
|
|
@@ -1342,6 +1372,7 @@ export class SubcClient {
|
|
|
1342
1372
|
// drop, and never the not_sent/unknown_channel class that call() retries.
|
|
1343
1373
|
// The code says WHICH route ended, not that it is safe to send again.
|
|
1344
1374
|
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)", "route_closed", undefined, this.routeEndReason(handle.channel)));
|
|
1375
|
+
this.handleCachedRouteClose(handle.channel, this.routeEndReason(handle.channel));
|
|
1345
1376
|
if (this.liveRoutes.get(handle.channel) === handle)
|
|
1346
1377
|
this.liveRoutes.delete(handle.channel);
|
|
1347
1378
|
this.evictRouteHandle(handle);
|
|
@@ -1464,14 +1495,35 @@ export class SubcClient {
|
|
|
1464
1495
|
const final = push.op === "route.closed";
|
|
1465
1496
|
if (final || !this.routeEndReasons.get(channel)?.final) {
|
|
1466
1497
|
this.routeEndReasons.set(channel, { reason, final });
|
|
1498
|
+
if (final)
|
|
1499
|
+
this.handleCachedRouteClose(channel, reason);
|
|
1467
1500
|
}
|
|
1468
1501
|
}
|
|
1469
1502
|
}
|
|
1470
1503
|
}
|
|
1471
1504
|
else if (typeof push.body.module_id === "string") {
|
|
1472
1505
|
for (const [channel, module] of this.routeModules) {
|
|
1473
|
-
if (module === push.body.module_id)
|
|
1506
|
+
if (module === push.body.module_id) {
|
|
1474
1507
|
this.legacyChannelReasons.set(channel, reason);
|
|
1508
|
+
if (push.op === "route.closed")
|
|
1509
|
+
this.handleCachedRouteClose(channel, reason);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
handleCachedRouteClose(channel, reason) {
|
|
1515
|
+
for (const [key, cached] of this.routes) {
|
|
1516
|
+
if (cached.handle?.channel !== channel)
|
|
1517
|
+
continue;
|
|
1518
|
+
if (SCOPE_ROUTE_CLOSE_REASONS.has(reason)) {
|
|
1519
|
+
// The session authority represented by this selector has ended. Keeping
|
|
1520
|
+
// this tombstone prevents a later call from reviving that same scope.
|
|
1521
|
+
cached.endReason = reason;
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
// A later call is new work: let route.open, not the close disposition,
|
|
1525
|
+
// decide whether this target may serve it.
|
|
1526
|
+
this.routes.delete(key);
|
|
1475
1527
|
}
|
|
1476
1528
|
}
|
|
1477
1529
|
}
|
|
@@ -1495,6 +1547,12 @@ export class SubcClient {
|
|
|
1495
1547
|
this.routeEndReasons.clear();
|
|
1496
1548
|
this.legacyChannelReasons.clear();
|
|
1497
1549
|
this.routeModules.clear();
|
|
1550
|
+
// Connection loss invalidates active routes. Keep only scope tombstones,
|
|
1551
|
+
// whose session-ending decision must survive transport replacement.
|
|
1552
|
+
for (const [key, cached] of this.routes) {
|
|
1553
|
+
if (cached.endReason === undefined)
|
|
1554
|
+
this.routes.delete(key);
|
|
1555
|
+
}
|
|
1498
1556
|
for (const [key, pending] of this.pending) {
|
|
1499
1557
|
this.rejectPending(key, pending, pending.handle ? routeError : err);
|
|
1500
1558
|
}
|
|
@@ -1623,6 +1681,9 @@ export function isRetryableRouteOpenCode(code) {
|
|
|
1623
1681
|
// A capability deny is policy, never a transient target-availability failure.
|
|
1624
1682
|
if (code === "capability_forbidden")
|
|
1625
1683
|
return false;
|
|
1684
|
+
// Decoding a flow stamp alone does not promise flow behaviour.
|
|
1685
|
+
if (code === "target_flow_unsupported")
|
|
1686
|
+
return false;
|
|
1626
1687
|
return (code === "module_reloading" ||
|
|
1627
1688
|
code === "module_warming" ||
|
|
1628
1689
|
code === "target_unavailable" ||
|
|
@@ -1739,12 +1800,36 @@ export function isValidCapabilityIdentifier(identifier) {
|
|
|
1739
1800
|
const numericVersion = Number(version);
|
|
1740
1801
|
return Number.isSafeInteger(numericVersion) && numericVersion >= 1 && numericVersion <= 0xffff_ffff;
|
|
1741
1802
|
}
|
|
1742
|
-
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities) {
|
|
1803
|
+
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities, scope) {
|
|
1743
1804
|
const consumerPart = consumerIdentity
|
|
1744
1805
|
? `${consumerIdentity.module_id}\0${consumerIdentity.launch_nonce}`
|
|
1745
1806
|
: "";
|
|
1746
1807
|
const capabilitiesPart = [...consumerCapabilities].sort().join("\0");
|
|
1747
|
-
|
|
1808
|
+
// A JSON tuple keeps opaque refs (including embedded separators) distinct.
|
|
1809
|
+
const scopePart = scope
|
|
1810
|
+
? JSON.stringify([scope.owner.kind, scope.owner.kind === "reserved" ? scope.owner.module_id : null, scope.ref, scope.scopeEpoch])
|
|
1811
|
+
: "";
|
|
1812
|
+
return `${target.kind}\0${target.module_id}\0${identity.project_root}\0${identity.harness}\0${identity.session}\0${consumerPart}\0${capabilitiesPart}\0${scopePart}`;
|
|
1813
|
+
}
|
|
1814
|
+
/** Validate before any route-open I/O, and snapshot caller-owned mutable values for reopens. */
|
|
1815
|
+
function validatedRouteScope(scope) {
|
|
1816
|
+
if (scope === undefined)
|
|
1817
|
+
return undefined;
|
|
1818
|
+
if (!scope || typeof scope.owner !== "object" || !scope.owner ||
|
|
1819
|
+
!["reserved", "direct", "unverified"].includes(scope.owner.kind) ||
|
|
1820
|
+
(scope.owner.kind === "reserved" && (typeof scope.owner.module_id !== "string" || scope.owner.module_id.length === 0))) {
|
|
1821
|
+
throw new SubcError("scope.owner must be a Principal with a non-empty reserved module_id", "invalid_scope");
|
|
1822
|
+
}
|
|
1823
|
+
if (typeof scope.ref !== "string" || scope.ref.length === 0) {
|
|
1824
|
+
throw new SubcError("scope.ref must be a non-empty string", "invalid_scope");
|
|
1825
|
+
}
|
|
1826
|
+
if (scope.scopeEpoch === undefined) {
|
|
1827
|
+
throw new SubcError("scope.scopeEpoch is required", "scope_epoch_required");
|
|
1828
|
+
}
|
|
1829
|
+
if (!Number.isSafeInteger(scope.scopeEpoch) || scope.scopeEpoch < 0) {
|
|
1830
|
+
throw new SubcError("scope.scopeEpoch must be a safe non-negative integer", "invalid_scope");
|
|
1831
|
+
}
|
|
1832
|
+
return { owner: { ...scope.owner }, ref: scope.ref, scopeEpoch: scope.scopeEpoch };
|
|
1748
1833
|
}
|
|
1749
1834
|
function routeOpenConsumerIdentity(opts = {}) {
|
|
1750
1835
|
if (opts.consumerIdentity !== undefined)
|