@cortexkit/subc-client 0.11.1 → 0.13.0
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/dist/client.d.ts +43 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +134 -20
- package/dist/client.js.map +1 -1
- package/dist/envelope.d.ts +1 -0
- package/dist/envelope.d.ts.map +1 -1
- package/dist/envelope.js +2 -1
- package/dist/envelope.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/route-handle.d.ts +32 -1
- package/dist/route-handle.d.ts.map +1 -1
- package/dist/route-handle.js +61 -5
- package/dist/route-handle.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +217 -16
- package/src/envelope.ts +2 -1
- package/src/index.ts +9 -1
- package/src/route-handle.ts +89 -6
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type ConnectionInfo } from "./connection-file.js";
|
|
2
2
|
import { AdmissionClass, Priority } from "./envelope.js";
|
|
3
|
-
import { RouteHandle } from "./route-handle.js";
|
|
3
|
+
import { ReverseRequestRegistry, RouteHandle } from "./route-handle.js";
|
|
4
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
5
|
+
export declare const TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
6
|
+
export declare const LIVENESS_PROBE_WINDOW_MS = 2000;
|
|
7
|
+
export declare const ROUTE_OPEN_RETRY_DEADLINE_MS = 30000;
|
|
4
8
|
export declare const SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID";
|
|
5
9
|
export declare const SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
|
|
6
10
|
export interface BindIdentity {
|
|
@@ -27,8 +31,11 @@ export interface ConsumerIdentity {
|
|
|
27
31
|
export interface RouteOpenOptions {
|
|
28
32
|
/** Optional override for the consumer identity; by default the SUBC_MODULE_ID and SUBC_LAUNCH_NONCE environment variables are used when both are non-empty. Set null to send route.open without consumer_identity. */
|
|
29
33
|
consumerIdentity?: ConsumerIdentity | null;
|
|
30
|
-
/**
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Handlers prepared before route.open. The client derives consumer_capabilities
|
|
36
|
+
* from this registry; callers cannot declare a capability without a handler.
|
|
37
|
+
*/
|
|
38
|
+
reverseRequests?: ReverseRequestRegistry;
|
|
32
39
|
}
|
|
33
40
|
export interface CatalogCapabilityRequirement {
|
|
34
41
|
capability: string;
|
|
@@ -87,6 +94,22 @@ export interface RequestOptions {
|
|
|
87
94
|
binary?: boolean;
|
|
88
95
|
/** Called for each interim PUSH / StreamData frame before the terminal reply. */
|
|
89
96
|
onProgress?: (body: Uint8Array) => void;
|
|
97
|
+
/**
|
|
98
|
+
* Abort the request: when this fires while the request is still in flight, the
|
|
99
|
+
* client emits a CANCEL frame carrying that request's own corr.
|
|
100
|
+
*
|
|
101
|
+
* The signal exists instead of exposing corr because corr lifetime is already
|
|
102
|
+
* tracked here to settle replies; handing it out would make every caller keep a
|
|
103
|
+
* second map that can disagree with this one, and would let a caller cancel a
|
|
104
|
+
* corr it does not own.
|
|
105
|
+
*
|
|
106
|
+
* Cancellation is best-effort by the wire's construction: the daemon forwards
|
|
107
|
+
* the frame on a live route and silently drops it on a departed one, and no
|
|
108
|
+
* acknowledgement exists at any layer. Only the module's own reply proves what
|
|
109
|
+
* happened, so the promise is NOT rejected here -- it settles however the module
|
|
110
|
+
* answers, which may be a normal completion that beat the cancel.
|
|
111
|
+
*/
|
|
112
|
+
signal?: AbortSignal;
|
|
90
113
|
}
|
|
91
114
|
export interface ManagedCallOptions extends RequestOptions {
|
|
92
115
|
/** Overrides the per-client identity used for route.open before this call. */
|
|
@@ -95,6 +118,8 @@ export interface ManagedCallOptions extends RequestOptions {
|
|
|
95
118
|
targetKind?: ManagedRouteKind;
|
|
96
119
|
/** Optional override for the consumer identity; by default the SUBC_MODULE_ID and SUBC_LAUNCH_NONCE environment variables are used when both are non-empty. Set null to send route.open without consumer_identity. */
|
|
97
120
|
consumerIdentity?: ConsumerIdentity | null;
|
|
121
|
+
/** Reverse-request handlers used by the managed route and all of its reconnect reopens. */
|
|
122
|
+
reverseRequests?: ReverseRequestRegistry;
|
|
98
123
|
}
|
|
99
124
|
export interface SubscribeOptions {
|
|
100
125
|
priority?: Priority;
|
|
@@ -118,6 +143,8 @@ export interface CloseRouteOptions {
|
|
|
118
143
|
export interface ManagedCloseRouteOptions extends CloseRouteOptions {
|
|
119
144
|
/** Consumer identity used by the cached managed route. */
|
|
120
145
|
consumerIdentity?: ConsumerIdentity | null;
|
|
146
|
+
/** The registry whose derived capability set identifies the cached route. */
|
|
147
|
+
reverseRequests?: ReverseRequestRegistry;
|
|
121
148
|
}
|
|
122
149
|
/**
|
|
123
150
|
* Capped exponential reconnect backoff. maxAttempts includes the first immediate
|
|
@@ -313,6 +340,16 @@ export declare class SubcClient {
|
|
|
313
340
|
private managedCall;
|
|
314
341
|
/** Open a held request on exactly one route generation. */
|
|
315
342
|
subscribe(handle: RouteHandle, body: unknown, onEvent: (event: Uint8Array) => void, opts?: SubscribeOptions): Subscription;
|
|
343
|
+
/**
|
|
344
|
+
* Emit CANCEL for one in-flight request when its caller's signal fires.
|
|
345
|
+
*
|
|
346
|
+
* Emits only while the request is unsettled by this client's own book, which is
|
|
347
|
+
* a fact it owns and can check without racing. It deliberately does NOT check
|
|
348
|
+
* whether the route still exists: that would be a guess, and a wrong guess turns
|
|
349
|
+
* a frame the daemon would harmlessly drop into a frame never sent -- removing
|
|
350
|
+
* the caller's only chance at the cost of saving nothing.
|
|
351
|
+
*/
|
|
352
|
+
private attachCancelSignal;
|
|
316
353
|
/** Send a pure-header cancellation for an in-flight request. */
|
|
317
354
|
cancel(handle: RouteHandle, corr: bigint, priority?: Priority): void;
|
|
318
355
|
/** Poll status or liveness for exactly the supplied route generation. */
|
|
@@ -408,6 +445,9 @@ export declare class SubcClient {
|
|
|
408
445
|
*/
|
|
409
446
|
private settle;
|
|
410
447
|
private rejectPending;
|
|
448
|
+
private dispatchReverseRequest;
|
|
449
|
+
private sendReverseError;
|
|
450
|
+
private sendReverseReply;
|
|
411
451
|
private errorFromFrame;
|
|
412
452
|
private evictRouteHandle;
|
|
413
453
|
private failHandle;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAeA,OAAO,EAA2C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACpG,OAAO,EACL,cAAc,EAMd,QAAQ,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -12,12 +12,12 @@ import { promises as fs } from "node:fs";
|
|
|
12
12
|
import { debuglog } from "node:util";
|
|
13
13
|
import { AuthError, authenticateClient } from "./auth.js";
|
|
14
14
|
import { ConnectionFileError, readConnectionFile } from "./connection-file.js";
|
|
15
|
-
import { AdmissionClass, buildFrame, buildFlags, encodeFrame, FrameType, hasBinary, Priority, } from "./envelope.js";
|
|
16
|
-
import { belongsToConnection, createRouteHandle, newConnectionToken, sameRouteHandle, StaleRouteHandleError, } from "./route-handle.js";
|
|
15
|
+
import { AdmissionClass, buildFrame, buildFlags, encodeFrame, FrameType, hasBinary, Priority, SUBSCRIPTION_FLAG, } from "./envelope.js";
|
|
16
|
+
import { belongsToConnection, createRouteHandle, newConnectionToken, reverseRequestHandler, ReverseRequestRegistry, sameRouteHandle, StaleRouteHandleError, } from "./route-handle.js";
|
|
17
17
|
import { SocketClosedError, SocketTimeoutError, SocketWriteNotQueuedError, SocketWriteQueuedError, SubcSocket, writeBorrowed, writeTrackedBorrowed, } from "./socket.js";
|
|
18
18
|
const debug = debuglog("subc-client");
|
|
19
19
|
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
|
|
20
|
-
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
20
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
21
21
|
// When a request-timeout timer fires, its reply may already be sitting in the
|
|
22
22
|
// socket read buffer, unprocessed only because the event loop was starved (Node
|
|
23
23
|
// runs the TIMERS phase before the POLL phase, so an expired timer can beat an
|
|
@@ -28,8 +28,8 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
|
28
28
|
// one loop turn. This is a demux tiebreak for a reply that RACED the deadline,
|
|
29
29
|
// NOT a deadline extension: an absent reply still settles right after the check
|
|
30
30
|
// phase. Capped so it can never approach BODY_READ_TIMEOUT_MS.
|
|
31
|
-
const TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
32
|
-
const LIVENESS_PROBE_WINDOW_MS = 2000;
|
|
31
|
+
export const TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
32
|
+
export const LIVENESS_PROBE_WINDOW_MS = 2000;
|
|
33
33
|
// Internal marker set as the `code` on the SubcError a request-deadline timeout
|
|
34
34
|
// rejects with, so the managed classifier can tell a deadline (reply may simply
|
|
35
35
|
// not have been read in time) from an actual connection drop. Never surfaced to
|
|
@@ -51,7 +51,7 @@ const DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
|
|
|
51
51
|
// rejects with module_timeout, so the deadline must leave room for MULTIPLE
|
|
52
52
|
// full relay waits or one slow bind exhausts the whole retry clock. 30s allows
|
|
53
53
|
// ~2 full relay timeouts plus backoff before surfacing not_sent.
|
|
54
|
-
const ROUTE_OPEN_RETRY_DEADLINE_MS = 30_000;
|
|
54
|
+
export const ROUTE_OPEN_RETRY_DEADLINE_MS = 30_000;
|
|
55
55
|
// Once a header arrives, its body must follow promptly; bound it so a truncated
|
|
56
56
|
// frame cannot wedge the read loop forever.
|
|
57
57
|
const BODY_READ_TIMEOUT_MS = 30_000;
|
|
@@ -230,13 +230,15 @@ export class SubcClient {
|
|
|
230
230
|
/** Open a route and return its connection-bound immutable handle. */
|
|
231
231
|
async routeOpen(target, identity, opts = {}) {
|
|
232
232
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
233
|
-
const
|
|
233
|
+
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry();
|
|
234
|
+
reverseRequests.seal();
|
|
235
|
+
const consumerCapabilities = reverseRequests.capabilities();
|
|
234
236
|
const body = this.encode({
|
|
235
237
|
op: "route.open",
|
|
236
238
|
target,
|
|
237
239
|
identity,
|
|
238
240
|
...(consumerIdentity ? { consumer_identity: consumerIdentity } : {}),
|
|
239
|
-
...(consumerCapabilities
|
|
241
|
+
...(consumerCapabilities.length > 0 ? { consumer_capabilities: consumerCapabilities } : {}),
|
|
240
242
|
});
|
|
241
243
|
let installed = null;
|
|
242
244
|
const install = (frame) => {
|
|
@@ -246,7 +248,7 @@ export class SubcClient {
|
|
|
246
248
|
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number") {
|
|
247
249
|
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
248
250
|
}
|
|
249
|
-
installed = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
251
|
+
installed = this.installRoute(parsed.route_channel, parsed.route_epoch, reverseRequests);
|
|
250
252
|
return true;
|
|
251
253
|
};
|
|
252
254
|
const closeLateRoute = (frame) => {
|
|
@@ -277,7 +279,7 @@ export class SubcClient {
|
|
|
277
279
|
const bytes = binary ? requireBinaryBody(body) : body instanceof Uint8Array ? body : this.encode(body);
|
|
278
280
|
const priority = opts.priority ?? Priority.Interactive;
|
|
279
281
|
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
280
|
-
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, undefined, undefined, binary);
|
|
282
|
+
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, undefined, undefined, binary, opts.signal);
|
|
281
283
|
return this.decodeReply(reply);
|
|
282
284
|
}
|
|
283
285
|
/**
|
|
@@ -384,7 +386,7 @@ export class SubcClient {
|
|
|
384
386
|
subscription: true,
|
|
385
387
|
};
|
|
386
388
|
this.pending.set(key, subscriptionPending);
|
|
387
|
-
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
|
|
389
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission) | SUBSCRIPTION_FLAG, handle.channel, handle.epoch, corr, bytes);
|
|
388
390
|
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
389
391
|
const pending = this.pending.get(key);
|
|
390
392
|
if (pending)
|
|
@@ -403,6 +405,35 @@ export class SubcClient {
|
|
|
403
405
|
};
|
|
404
406
|
return { unsubscribe, closed };
|
|
405
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Emit CANCEL for one in-flight request when its caller's signal fires.
|
|
410
|
+
*
|
|
411
|
+
* Emits only while the request is unsettled by this client's own book, which is
|
|
412
|
+
* a fact it owns and can check without racing. It deliberately does NOT check
|
|
413
|
+
* whether the route still exists: that would be a guess, and a wrong guess turns
|
|
414
|
+
* a frame the daemon would harmlessly drop into a frame never sent -- removing
|
|
415
|
+
* the caller's only chance at the cost of saving nothing.
|
|
416
|
+
*/
|
|
417
|
+
attachCancelSignal(signal, key, handle, corr, priority) {
|
|
418
|
+
if (!handle)
|
|
419
|
+
return;
|
|
420
|
+
const emit = () => {
|
|
421
|
+
if (!this.pending.has(key))
|
|
422
|
+
return;
|
|
423
|
+
try {
|
|
424
|
+
this.cancel(handle, corr, priority);
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// A stale handle or a closed socket cannot be acted on, and the caller
|
|
428
|
+
// already treats an unanswered cancel as unknown rather than as failure.
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
if (signal.aborted) {
|
|
432
|
+
emit();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
signal.addEventListener("abort", emit, { once: true });
|
|
436
|
+
}
|
|
406
437
|
/** Send a pure-header cancellation for an in-flight request. */
|
|
407
438
|
cancel(handle, corr, priority = Priority.Interactive) {
|
|
408
439
|
this.assertLiveHandle(handle);
|
|
@@ -445,7 +476,7 @@ export class SubcClient {
|
|
|
445
476
|
}
|
|
446
477
|
/** Close a cached managed route by its route-open identity tuple. */
|
|
447
478
|
async closeManagedRoute(target, identity, opts = {}) {
|
|
448
|
-
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts));
|
|
479
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry()).capabilities());
|
|
449
480
|
const cached = this.routes.get(key);
|
|
450
481
|
if (!cached)
|
|
451
482
|
return;
|
|
@@ -513,7 +544,7 @@ export class SubcClient {
|
|
|
513
544
|
async controlRpc(body, acceptFrame, onLateResponse) {
|
|
514
545
|
return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
|
|
515
546
|
}
|
|
516
|
-
send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse, binary = false) {
|
|
547
|
+
send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse, binary = false, signal) {
|
|
517
548
|
if (handle)
|
|
518
549
|
this.assertLiveHandle(handle);
|
|
519
550
|
if (this.closedErr)
|
|
@@ -542,6 +573,8 @@ export class SubcClient {
|
|
|
542
573
|
};
|
|
543
574
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
544
575
|
this.pending.set(key, pending);
|
|
576
|
+
if (signal)
|
|
577
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
545
578
|
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + ms).catch((error) => {
|
|
546
579
|
const current = this.pending.get(key);
|
|
547
580
|
if (current)
|
|
@@ -589,7 +622,7 @@ export class SubcClient {
|
|
|
589
622
|
const priority = opts.priority ?? Priority.Interactive;
|
|
590
623
|
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
591
624
|
try {
|
|
592
|
-
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, binary);
|
|
625
|
+
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress, binary, opts.signal);
|
|
593
626
|
return this.decodeReply(reply);
|
|
594
627
|
}
|
|
595
628
|
catch (error) {
|
|
@@ -598,7 +631,7 @@ export class SubcClient {
|
|
|
598
631
|
throw this.terminalCallError("managed call failed", error);
|
|
599
632
|
}
|
|
600
633
|
}
|
|
601
|
-
sendManaged(handle, body, priority, admission, timeoutMs, onProgress, binary = false) {
|
|
634
|
+
sendManaged(handle, body, priority, admission, timeoutMs, onProgress, binary = false, signal) {
|
|
602
635
|
try {
|
|
603
636
|
this.assertLiveHandle(handle);
|
|
604
637
|
}
|
|
@@ -638,6 +671,8 @@ export class SubcClient {
|
|
|
638
671
|
};
|
|
639
672
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
640
673
|
this.pending.set(key, pending);
|
|
674
|
+
if (signal)
|
|
675
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
641
676
|
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
642
677
|
handedToSocket = write.queued;
|
|
643
678
|
write.completed.catch((error) => {
|
|
@@ -654,7 +689,9 @@ export class SubcClient {
|
|
|
654
689
|
}
|
|
655
690
|
const target = { kind: opts.targetKind ?? this.opts.targetKind, module_id: moduleId };
|
|
656
691
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
657
|
-
const
|
|
692
|
+
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry();
|
|
693
|
+
reverseRequests.seal();
|
|
694
|
+
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities());
|
|
658
695
|
let cached = this.routes.get(key);
|
|
659
696
|
if (!cached) {
|
|
660
697
|
cached = {
|
|
@@ -663,6 +700,7 @@ export class SubcClient {
|
|
|
663
700
|
target,
|
|
664
701
|
identity,
|
|
665
702
|
consumerIdentity,
|
|
703
|
+
reverseRequests,
|
|
666
704
|
handle: null,
|
|
667
705
|
opening: null,
|
|
668
706
|
};
|
|
@@ -695,6 +733,7 @@ export class SubcClient {
|
|
|
695
733
|
try {
|
|
696
734
|
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
697
735
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
736
|
+
reverseRequests: cached.reverseRequests,
|
|
698
737
|
});
|
|
699
738
|
if (cached.closed) {
|
|
700
739
|
this.liveRoutes.delete(handle.channel);
|
|
@@ -912,6 +951,7 @@ export class SubcClient {
|
|
|
912
951
|
try {
|
|
913
952
|
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
914
953
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
954
|
+
reverseRequests: cached.reverseRequests,
|
|
915
955
|
});
|
|
916
956
|
if (cached.closed) {
|
|
917
957
|
this.liveRoutes.delete(handle.channel);
|
|
@@ -1013,6 +1053,10 @@ export class SubcClient {
|
|
|
1013
1053
|
return;
|
|
1014
1054
|
}
|
|
1015
1055
|
}
|
|
1056
|
+
if (frame.header.ty === FrameType.Request && handle) {
|
|
1057
|
+
this.dispatchReverseRequest(frame, handle);
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1016
1060
|
const key = pendingKey(handle, frame.header.corr);
|
|
1017
1061
|
const pending = this.pending.get(key);
|
|
1018
1062
|
if (pending) {
|
|
@@ -1107,6 +1151,66 @@ export class SubcClient {
|
|
|
1107
1151
|
rejectPending(key, pending, err) {
|
|
1108
1152
|
this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
|
|
1109
1153
|
}
|
|
1154
|
+
dispatchReverseRequest(frame, handle) {
|
|
1155
|
+
const method = reverseRequestMethod(frame.body);
|
|
1156
|
+
const methodFamily = method?.split("/", 1)[0];
|
|
1157
|
+
const handler = methodFamily ? reverseRequestHandler(handle, methodFamily) : undefined;
|
|
1158
|
+
const sock = this.sock;
|
|
1159
|
+
const generation = this.generation;
|
|
1160
|
+
// ONE CODE, TWO SITES, ON PURPOSE. `reverse_request_unhandled` is emitted
|
|
1161
|
+
// both here (no handler registered) and below (a handler that threw), with
|
|
1162
|
+
// different MESSAGES and the same CODE. That is a ruling, not an oversight:
|
|
1163
|
+
// the distinction between "nobody listening" and "the listener broke" is
|
|
1164
|
+
// ours to log, and every provider's decision is identical for both -- not
|
|
1165
|
+
// granted, retry later. A second code would be a second thing every
|
|
1166
|
+
// consumer's decoder must learn for a branch none of them takes. If one ever
|
|
1167
|
+
// needs it, the message carries it. (Settled with prefrontal, the lane's
|
|
1168
|
+
// first consumer, before anyone pinned on it.)
|
|
1169
|
+
//
|
|
1170
|
+
// What must NOT join them: a handler that RETURNS bytes declining the
|
|
1171
|
+
// request answers with an ordinary Response in the provider's own
|
|
1172
|
+
// vocabulary. Error means "the consumer did not answer", never "the consumer
|
|
1173
|
+
// said no" -- that separation is the reason this lane exists.
|
|
1174
|
+
const reply = async () => {
|
|
1175
|
+
if (!handler || !method) {
|
|
1176
|
+
await this.sendReverseError(frame, handle, "reverse_request_unhandled", methodFamily
|
|
1177
|
+
? `no reverse-request handler is registered for ${methodFamily}`
|
|
1178
|
+
: "reverse request has no valid method", sock, generation);
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
let body;
|
|
1182
|
+
try {
|
|
1183
|
+
body = await handler(frame.body, { corr: frame.header.corr, method });
|
|
1184
|
+
if (!(body instanceof Uint8Array)) {
|
|
1185
|
+
throw new TypeError("reverse-request handler must return a Uint8Array");
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
catch (error) {
|
|
1189
|
+
await this.sendReverseError(frame, handle, "reverse_request_unhandled", error instanceof Error ? error.message : String(error), sock, generation);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
await this.sendReverseReply(frame, handle, FrameType.Response, body, sock, generation);
|
|
1193
|
+
};
|
|
1194
|
+
void reply().catch((error) => {
|
|
1195
|
+
if (this.sock === sock && this.generation === generation && !this.closeStarted) {
|
|
1196
|
+
debug("failed to answer reverse request: %s", causeMessage(error));
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
async sendReverseError(request, handle, code, message, sock, generation) {
|
|
1201
|
+
await this.sendReverseReply(request, handle, FrameType.Error, this.encode({ code, message }), sock, generation);
|
|
1202
|
+
}
|
|
1203
|
+
async sendReverseReply(request, handle, type, body, sock, generation) {
|
|
1204
|
+
if (this.sock !== sock ||
|
|
1205
|
+
this.generation !== generation ||
|
|
1206
|
+
!this.isLiveHandle(handle) ||
|
|
1207
|
+
this.closeStarted ||
|
|
1208
|
+
this.closedErr) {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
const frame = buildFrame(type, buildFlags(false, Priority.Interactive, false), handle.channel, handle.epoch, request.header.corr, body);
|
|
1212
|
+
await writeBorrowed(sock, encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
|
|
1213
|
+
}
|
|
1110
1214
|
errorFromFrame(frame) {
|
|
1111
1215
|
try {
|
|
1112
1216
|
const parsed = JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
@@ -1157,8 +1261,8 @@ export class SubcClient {
|
|
|
1157
1261
|
get droppedIngressFrames() {
|
|
1158
1262
|
return this.ingressEpochDropCount;
|
|
1159
1263
|
}
|
|
1160
|
-
installRoute(channel, epoch) {
|
|
1161
|
-
const handle = createRouteHandle(channel, epoch, this.connectionToken);
|
|
1264
|
+
installRoute(channel, epoch, reverseRequests = new ReverseRequestRegistry()) {
|
|
1265
|
+
const handle = createRouteHandle(channel, epoch, this.connectionToken, reverseRequests);
|
|
1162
1266
|
this.liveRoutes.set(channel, handle);
|
|
1163
1267
|
return handle;
|
|
1164
1268
|
}
|
|
@@ -1301,11 +1405,12 @@ export function isValidCapabilityIdentifier(identifier) {
|
|
|
1301
1405
|
const numericVersion = Number(version);
|
|
1302
1406
|
return Number.isSafeInteger(numericVersion) && numericVersion >= 1 && numericVersion <= 0xffff_ffff;
|
|
1303
1407
|
}
|
|
1304
|
-
function routeCacheKey(target, identity, consumerIdentity) {
|
|
1408
|
+
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities) {
|
|
1305
1409
|
const consumerPart = consumerIdentity
|
|
1306
1410
|
? `${consumerIdentity.module_id}\0${consumerIdentity.launch_nonce}`
|
|
1307
1411
|
: "";
|
|
1308
|
-
|
|
1412
|
+
const capabilitiesPart = [...consumerCapabilities].sort().join("\0");
|
|
1413
|
+
return `${target.kind}\0${target.module_id}\0${identity.project_root}\0${identity.harness}\0${identity.session}\0${consumerPart}\0${capabilitiesPart}`;
|
|
1309
1414
|
}
|
|
1310
1415
|
function routeOpenConsumerIdentity(opts = {}) {
|
|
1311
1416
|
if (opts.consumerIdentity !== undefined)
|
|
@@ -1332,4 +1437,13 @@ function causeMessage(cause) {
|
|
|
1332
1437
|
function pendingKey(handle, corr) {
|
|
1333
1438
|
return handle ? `${handle.channel}:${handle.epoch}:${corr}` : `0:0:${corr}`;
|
|
1334
1439
|
}
|
|
1440
|
+
function reverseRequestMethod(body) {
|
|
1441
|
+
try {
|
|
1442
|
+
const parsed = JSON.parse(Buffer.from(body).toString("utf8"));
|
|
1443
|
+
return typeof parsed.method === "string" && parsed.method.length > 0 ? parsed.method : undefined;
|
|
1444
|
+
}
|
|
1445
|
+
catch {
|
|
1446
|
+
return undefined;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1335
1449
|
//# sourceMappingURL=client.js.map
|