@api.global/typedsocket 7.0.0 → 8.0.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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@api.global/typedsocket',
6
- version: '7.0.0',
6
+ version: '8.0.0',
7
7
  description: 'A library for creating typed WebSocket connections, supporting bi-directional communication with type safety.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLCtHQUErRztDQUM3SCxDQUFBIn0=
@@ -0,0 +1,61 @@
1
+ import * as plugins from './plugins.js';
2
+ export declare const TYPEDSOCKET_MAX_ACTIVE_REQUEST_HANDLERS_PER_CONNECTION = 64;
3
+ export declare const TYPEDSOCKET_MAX_ACTIVE_REQUEST_HANDLERS = 1024;
4
+ export declare const TYPEDSOCKET_MAX_REQUEST_CANCELLATION_TOMBSTONES_PER_CONNECTION = 1024;
5
+ export declare const TYPEDSOCKET_MAX_REQUEST_CANCELLATION_TOMBSTONES = 16384;
6
+ export declare const TYPEDSOCKET_REQUEST_HANDLER_MAX_LIFETIME_MS: number;
7
+ export declare const TYPEDSOCKET_REQUEST_CANCELLATION_TOMBSTONE_RETENTION_MS = 10000;
8
+ /** Internal control-flow error for an authenticated remote cancellation. */
9
+ export declare class TypedSocketRemoteRequestCancellationError extends plugins.typedrequest.TypedResponseError {
10
+ constructor();
11
+ }
12
+ export interface IRequestCancellationIdentity {
13
+ readonly method: string;
14
+ readonly correlationId: string;
15
+ readonly requestInstanceId: string;
16
+ }
17
+ export interface IRequestCancellationContext {
18
+ readonly connection: object;
19
+ readonly routingSurface: object;
20
+ readonly generation: number;
21
+ close(reasonArg: string): void;
22
+ }
23
+ export type TRequestSignalRegistration = plugins.typedrequest.ITypedRouterRequestSignalRegistration;
24
+ export interface IRequestCancellationManagerOptions {
25
+ readonly maxActivePerConnection?: number;
26
+ readonly maxActive?: number;
27
+ readonly maxTombstonesPerConnection?: number;
28
+ readonly maxTombstones?: number;
29
+ readonly activeLifetimeMs?: number;
30
+ readonly tombstoneRetentionMs?: number;
31
+ }
32
+ export declare class RequestCancellationManager {
33
+ private readonly states;
34
+ private readonly limits;
35
+ private activeCount;
36
+ private detachedActiveCount;
37
+ private tombstoneCount;
38
+ private stopped;
39
+ constructor(optionsArg?: IRequestCancellationManagerOptions);
40
+ register(contextArg: IRequestCancellationContext, identityArg: IRequestCancellationIdentity): TRequestSignalRegistration;
41
+ cancel(contextArg: IRequestCancellationContext, identityArg: IRequestCancellationIdentity): void;
42
+ closeConnection(connectionArg: object, reasonArg: unknown): void;
43
+ stop(reasonArg: unknown): void;
44
+ getStats(): Readonly<{
45
+ /** Registrations that have not called complete(), including detached work. */
46
+ active: number;
47
+ /** Aborted registrations detached from their connection but not yet complete. */
48
+ detachedActive: number;
49
+ tombstones: number;
50
+ /** Cancellation states still attached to a physical connection. */
51
+ connections: number;
52
+ }>;
53
+ private getState;
54
+ private completeActive;
55
+ private addTombstone;
56
+ private deleteTombstone;
57
+ private reclaimGlobalTombstoneCapacity;
58
+ private failConnection;
59
+ private createKey;
60
+ private getValidatedKey;
61
+ }
@@ -0,0 +1,234 @@
1
+ import * as plugins from './plugins.js';
2
+ export const TYPEDSOCKET_MAX_ACTIVE_REQUEST_HANDLERS_PER_CONNECTION = 64;
3
+ export const TYPEDSOCKET_MAX_ACTIVE_REQUEST_HANDLERS = 1024;
4
+ export const TYPEDSOCKET_MAX_REQUEST_CANCELLATION_TOMBSTONES_PER_CONNECTION = 1024;
5
+ export const TYPEDSOCKET_MAX_REQUEST_CANCELLATION_TOMBSTONES = 16_384;
6
+ export const TYPEDSOCKET_REQUEST_HANDLER_MAX_LIFETIME_MS = 5 * 60 * 1000;
7
+ export const TYPEDSOCKET_REQUEST_CANCELLATION_TOMBSTONE_RETENTION_MS = 10_000;
8
+ /** Internal control-flow error for an authenticated remote cancellation. */
9
+ export class TypedSocketRemoteRequestCancellationError extends plugins.typedrequest.TypedResponseError {
10
+ constructor() {
11
+ super('TypedSocket remote request cancelled');
12
+ this.name = 'TypedSocketRemoteRequestCancellationError';
13
+ }
14
+ }
15
+ export class RequestCancellationManager {
16
+ constructor(optionsArg = {}) {
17
+ this.states = new Map();
18
+ this.activeCount = 0;
19
+ this.detachedActiveCount = 0;
20
+ this.tombstoneCount = 0;
21
+ this.stopped = false;
22
+ this.limits = {
23
+ maxActivePerConnection: optionsArg.maxActivePerConnection
24
+ ?? TYPEDSOCKET_MAX_ACTIVE_REQUEST_HANDLERS_PER_CONNECTION,
25
+ maxActive: optionsArg.maxActive ?? TYPEDSOCKET_MAX_ACTIVE_REQUEST_HANDLERS,
26
+ maxTombstonesPerConnection: optionsArg.maxTombstonesPerConnection
27
+ ?? TYPEDSOCKET_MAX_REQUEST_CANCELLATION_TOMBSTONES_PER_CONNECTION,
28
+ maxTombstones: optionsArg.maxTombstones
29
+ ?? TYPEDSOCKET_MAX_REQUEST_CANCELLATION_TOMBSTONES,
30
+ activeLifetimeMs: optionsArg.activeLifetimeMs
31
+ ?? TYPEDSOCKET_REQUEST_HANDLER_MAX_LIFETIME_MS,
32
+ tombstoneRetentionMs: optionsArg.tombstoneRetentionMs
33
+ ?? TYPEDSOCKET_REQUEST_CANCELLATION_TOMBSTONE_RETENTION_MS,
34
+ };
35
+ for (const [name, value] of Object.entries(this.limits)) {
36
+ if (!Number.isSafeInteger(value) || value < 1) {
37
+ throw new RangeError(`Request cancellation limit ${name} must be a positive safe integer`);
38
+ }
39
+ }
40
+ }
41
+ register(contextArg, identityArg) {
42
+ const state = this.getState(contextArg);
43
+ const key = this.getValidatedKey(state, identityArg);
44
+ if (state.active.has(key) || state.tombstones.get(key)?.kind === 'terminal') {
45
+ this.failConnection(state, 'TypedSocket request instance was replayed');
46
+ throw new Error('TypedSocket request instance was replayed');
47
+ }
48
+ if (state.active.size >= this.limits.maxActivePerConnection
49
+ || this.activeCount >= this.limits.maxActive) {
50
+ this.failConnection(state, 'TypedSocket active request handler capacity exhausted');
51
+ throw new Error('TypedSocket active request handler capacity exhausted');
52
+ }
53
+ const earlyCancel = state.tombstones.get(key)?.kind === 'early-cancel';
54
+ if (earlyCancel)
55
+ this.deleteTombstone(state, key);
56
+ const abortController = new AbortController();
57
+ const activeRequest = {
58
+ key,
59
+ context: state.context,
60
+ abortController,
61
+ completed: false,
62
+ timeout: setTimeout(() => {
63
+ this.failConnection(state, 'TypedSocket request handler lifetime exceeded');
64
+ }, this.limits.activeLifetimeMs),
65
+ };
66
+ activeRequest.timeout.unref?.();
67
+ state.active.set(key, activeRequest);
68
+ this.activeCount++;
69
+ if (earlyCancel) {
70
+ abortController.abort(new TypedSocketRemoteRequestCancellationError());
71
+ }
72
+ return Object.freeze({
73
+ abortSignal: abortController.signal,
74
+ complete: () => this.completeActive(state, activeRequest),
75
+ });
76
+ }
77
+ cancel(contextArg, identityArg) {
78
+ const state = this.getState(contextArg);
79
+ const key = this.getValidatedKey(state, identityArg);
80
+ const activeRequest = state.active.get(key);
81
+ if (activeRequest) {
82
+ activeRequest.abortController.abort(new TypedSocketRemoteRequestCancellationError());
83
+ return;
84
+ }
85
+ if (state.tombstones.has(key))
86
+ return;
87
+ this.addTombstone(state, key, 'early-cancel');
88
+ }
89
+ closeConnection(connectionArg, reasonArg) {
90
+ const state = this.states.get(connectionArg);
91
+ if (!state)
92
+ return;
93
+ state.closed = true;
94
+ this.states.delete(connectionArg);
95
+ for (const activeRequest of state.active.values()) {
96
+ clearTimeout(activeRequest.timeout);
97
+ this.detachedActiveCount++;
98
+ activeRequest.abortController.abort(reasonArg);
99
+ }
100
+ for (const key of Array.from(state.tombstones.keys())) {
101
+ this.deleteTombstone(state, key);
102
+ }
103
+ }
104
+ stop(reasonArg) {
105
+ if (this.stopped)
106
+ return;
107
+ this.stopped = true;
108
+ for (const connection of Array.from(this.states.keys())) {
109
+ this.closeConnection(connection, reasonArg);
110
+ }
111
+ }
112
+ getStats() {
113
+ return Object.freeze({
114
+ active: this.activeCount,
115
+ detachedActive: this.detachedActiveCount,
116
+ tombstones: this.tombstoneCount,
117
+ connections: this.states.size,
118
+ });
119
+ }
120
+ getState(contextArg) {
121
+ if (this.stopped)
122
+ throw new Error('TypedSocket request cancellation manager stopped');
123
+ const existing = this.states.get(contextArg.connection);
124
+ if (existing) {
125
+ if (existing.closed
126
+ || existing.context.routingSurface !== contextArg.routingSurface
127
+ || existing.context.generation !== contextArg.generation) {
128
+ this.failConnection(existing, 'TypedSocket request cancellation authority mismatch');
129
+ throw new Error('TypedSocket request cancellation authority mismatch');
130
+ }
131
+ return existing;
132
+ }
133
+ const state = {
134
+ context: Object.freeze({ ...contextArg }),
135
+ active: new Map(),
136
+ tombstones: new Map(),
137
+ closed: false,
138
+ };
139
+ this.states.set(contextArg.connection, state);
140
+ return state;
141
+ }
142
+ completeActive(stateArg, activeRequestArg) {
143
+ if (activeRequestArg.completed)
144
+ return;
145
+ activeRequestArg.completed = true;
146
+ clearTimeout(activeRequestArg.timeout);
147
+ if (stateArg.active.get(activeRequestArg.key) === activeRequestArg) {
148
+ stateArg.active.delete(activeRequestArg.key);
149
+ this.activeCount--;
150
+ if (stateArg.closed)
151
+ this.detachedActiveCount--;
152
+ }
153
+ if (!stateArg.closed) {
154
+ this.addTombstone(stateArg, activeRequestArg.key, 'terminal');
155
+ }
156
+ }
157
+ addTombstone(stateArg, keyArg, kindArg) {
158
+ if (stateArg.closed)
159
+ return;
160
+ if (stateArg.tombstones.size >= this.limits.maxTombstonesPerConnection) {
161
+ this.failConnection(stateArg, 'TypedSocket per-connection request cancellation tombstone capacity exhausted');
162
+ return;
163
+ }
164
+ if (this.tombstoneCount >= this.limits.maxTombstones
165
+ && !this.reclaimGlobalTombstoneCapacity()) {
166
+ this.failConnection(stateArg, 'TypedSocket global request cancellation tombstone capacity exhausted');
167
+ return;
168
+ }
169
+ if (stateArg.closed)
170
+ return;
171
+ const timeout = setTimeout(() => {
172
+ this.deleteTombstone(stateArg, keyArg);
173
+ }, this.limits.tombstoneRetentionMs);
174
+ timeout.unref?.();
175
+ stateArg.tombstones.set(keyArg, { kind: kindArg, timeout });
176
+ this.tombstoneCount++;
177
+ }
178
+ deleteTombstone(stateArg, keyArg) {
179
+ const tombstone = stateArg.tombstones.get(keyArg);
180
+ if (!tombstone)
181
+ return;
182
+ clearTimeout(tombstone.timeout);
183
+ stateArg.tombstones.delete(keyArg);
184
+ this.tombstoneCount--;
185
+ }
186
+ reclaimGlobalTombstoneCapacity() {
187
+ let largestConsumer;
188
+ // Map iteration is insertion ordered, so equal shares evict the oldest state.
189
+ for (const state of this.states.values()) {
190
+ if (state.closed
191
+ || state.tombstones.size === 0
192
+ || (largestConsumer
193
+ && state.tombstones.size <= largestConsumer.tombstones.size))
194
+ continue;
195
+ largestConsumer = state;
196
+ }
197
+ if (!largestConsumer)
198
+ return false;
199
+ this.failConnection(largestConsumer, 'TypedSocket global request cancellation tombstone capacity exhausted');
200
+ return this.tombstoneCount < this.limits.maxTombstones;
201
+ }
202
+ failConnection(stateArg, reasonArg) {
203
+ if (stateArg.closed)
204
+ return;
205
+ try {
206
+ stateArg.context.close(reasonArg);
207
+ }
208
+ finally {
209
+ this.closeConnection(stateArg.context.connection, new Error(reasonArg));
210
+ }
211
+ }
212
+ createKey(identityArg) {
213
+ return JSON.stringify([
214
+ identityArg.method,
215
+ identityArg.correlationId,
216
+ identityArg.requestInstanceId,
217
+ ]);
218
+ }
219
+ getValidatedKey(stateArg, identityArg) {
220
+ const values = [
221
+ identityArg.method,
222
+ identityArg.correlationId,
223
+ identityArg.requestInstanceId,
224
+ ];
225
+ if (values.some((valueArg) => (typeof valueArg !== 'string'
226
+ || valueArg.length === 0
227
+ || new TextEncoder().encode(valueArg).byteLength > 256))) {
228
+ this.failConnection(stateArg, 'TypedSocket request cancellation identity is malformed');
229
+ throw new Error('TypedSocket request cancellation identity is malformed');
230
+ }
231
+ return this.createKey(identityArg);
232
+ }
233
+ }
234
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZXF1ZXN0Y2FuY2VsbGF0aW9ubWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucmVxdWVzdGNhbmNlbGxhdGlvbm1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFFeEMsTUFBTSxDQUFDLE1BQU0sc0RBQXNELEdBQUcsRUFBRSxDQUFDO0FBQ3pFLE1BQU0sQ0FBQyxNQUFNLHVDQUF1QyxHQUFHLElBQUksQ0FBQztBQUM1RCxNQUFNLENBQUMsTUFBTSw4REFBOEQsR0FBRyxJQUFJLENBQUM7QUFDbkYsTUFBTSxDQUFDLE1BQU0sK0NBQStDLEdBQUcsTUFBTSxDQUFDO0FBQ3RFLE1BQU0sQ0FBQyxNQUFNLDJDQUEyQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3pFLE1BQU0sQ0FBQyxNQUFNLHVEQUF1RCxHQUFHLE1BQU0sQ0FBQztBQUU5RSw0RUFBNEU7QUFDNUUsTUFBTSxPQUFPLHlDQUNYLFNBQVEsT0FBTyxDQUFDLFlBQVksQ0FBQyxrQkFBa0I7SUFDL0M7UUFDRSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsSUFBSSxHQUFHLDJDQUEyQyxDQUFDO0lBQzFELENBQUM7Q0FDRjtBQStDRCxNQUFNLE9BQU8sMEJBQTBCO0lBUXJDLFlBQVksYUFBaUQsRUFBRTtRQVA5QyxXQUFNLEdBQUcsSUFBSSxHQUFHLEVBQXdDLENBQUM7UUFFbEUsZ0JBQVcsR0FBRyxDQUFDLENBQUM7UUFDaEIsd0JBQW1CLEdBQUcsQ0FBQyxDQUFDO1FBQ3hCLG1CQUFjLEdBQUcsQ0FBQyxDQUFDO1FBQ25CLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFHdEIsSUFBSSxDQUFDLE1BQU0sR0FBRztZQUNaLHNCQUFzQixFQUFFLFVBQVUsQ0FBQyxzQkFBc0I7bUJBQ3BELHNEQUFzRDtZQUMzRCxT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
@@ -2,6 +2,7 @@ import * as plugins from './plugins.js';
2
2
  import { VirtualStreamManager } from './classes.virtualstreammanager.js';
3
3
  export { toClientWebSocketCloseCode } from './helpers.websocket.js';
4
4
  export { TYPEDSOCKET_MAX_RETAINED_CLIENT_TAG_MUTATIONS, TYPEDSOCKET_MAX_RETAINED_CLIENT_TAG_MUTATIONS_PER_NAME, } from './classes.clienttagreconciler.js';
5
+ export { TYPEDSOCKET_REQUEST_CANCELLATION_METHOD, TYPEDSOCKET_REQUEST_CANCELLATION_PROTOCOL, } from './constants.virtualstream.js';
5
6
  import type { TTypedSocketDiagnosticEvent } from './interfaces.diagnostics.js';
6
7
  import type { ICreateVirtualStreamOptions, IVirtualStreamAuthorizationAdapter, TVirtualStreamDirection } from './interfaces.virtualstream.js';
7
8
  import type { ITypedSocketClientTagPolicy } from './classes.typedsockettagpolicymanager.js';
@@ -68,6 +69,8 @@ export interface ITypedSocketClientOptions {
68
69
  export interface ITypedSocketServerOptions {
69
70
  virtualStreamAuthorizationAdapter?: IVirtualStreamAuthorizationAdapter;
70
71
  clientTagPolicy?: ITypedSocketClientTagPolicy;
72
+ /** Runs synchronously after the exact server handshake response is settled. */
73
+ onServerConnectionReady?: (connectionArg: ISmartServeConnectionWrapper) => undefined;
71
74
  }
72
75
  /**
73
76
  * Lifecycle controls for one TypedRequest sent over a TypedSocket.
@@ -158,13 +161,16 @@ export declare class TypedSocket {
158
161
  private unsubscribeSmartServeConnectionClose;
159
162
  private detachVirtualStreamResolvers;
160
163
  private detachIncomingRequestAuthorityGuards;
164
+ private detachIncomingRequestSignalResolvers;
161
165
  private detachResponseAuthorityGuards;
162
166
  private pendingServerRequests;
163
167
  private pendingServerRequestIdsByPeer;
164
168
  private retainedServerInterests;
165
169
  private retainedServerInterestsByPeer;
170
+ private readonly serverReadyCallbackPeers;
166
171
  private readonly virtualStreamAuthorizationAdapter;
167
172
  private readonly tagPolicyManager;
173
+ private readonly requestCancellationManager;
168
174
  private constructor();
169
175
  /**
170
176
  * Attaches the SmartServe transport after server router composition.
@@ -189,6 +195,9 @@ export declare class TypedSocket {
189
195
  private isCurrentClientConnection;
190
196
  private failClientHandshake;
191
197
  private abortClientConnectionState;
198
+ private registerClientRequestSignal;
199
+ private handleClientCancellation;
200
+ private createClientCancellationContext;
192
201
  /**
193
202
  * Handles WebSocket disconnection
194
203
  */
@@ -228,7 +237,12 @@ export declare class TypedSocket {
228
237
  private sendRequest;
229
238
  private normalizeRequestTimeout;
230
239
  private requireClientOptions;
240
+ private sendClientCancellation;
231
241
  private cancelPendingServerRequestsForPeer;
242
+ private registerServerRequestSignal;
243
+ private handleServerCancellation;
244
+ private createServerCancellationContext;
245
+ private sendServerCancellation;
232
246
  private retainServerInterestRegistration;
233
247
  private authorizeIncomingServerResponse;
234
248
  private isAttachedServerPeer;