@zlink-systems/framework 0.10.0 → 0.12.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.
Files changed (125) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/EndpointNotation.d.ts +5 -4
  3. package/dist/contracts/Configuration/EndpointNotation.js +5 -4
  4. package/dist/contracts/Configuration/InternalDefaults.d.ts +0 -1
  5. package/dist/contracts/Configuration/InternalDefaults.js +1 -2
  6. package/dist/contracts/Configuration/RegistrationNormalizers.js +1 -2
  7. package/dist/contracts/Configuration/RegistrationTypes.d.ts +2 -5
  8. package/dist/contracts/Configuration/RegistrationValidators.js +0 -1
  9. package/dist/contracts/Errors/ZLinkFrameworkException.d.ts +6 -7
  10. package/dist/contracts/Errors/ZLinkFrameworkException.js +6 -7
  11. package/dist/runtime/actors/actor-client.js +1 -1
  12. package/dist/runtime/actors/actor-mailbox.js +1 -5
  13. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  14. package/dist/runtime/backend/mesh-completion-table.d.ts +1 -3
  15. package/dist/runtime/backend/mesh-completion-table.js +11 -15
  16. package/dist/runtime/backend/mesh-dispatch-pump.d.ts +11 -4
  17. package/dist/runtime/backend/mesh-dispatch-pump.js +201 -134
  18. package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +3 -4
  19. package/dist/runtime/backend/node/node-backend-adapter-factory.js +51 -37
  20. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
  21. package/dist/runtime/backend/node/node-backend-adapter-support.js +2 -2
  22. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +2 -2
  23. package/dist/runtime/backend/node/node-event-loop-poller.d.ts +16 -0
  24. package/dist/runtime/backend/node/node-event-loop-poller.js +49 -0
  25. package/dist/runtime/backend/node/node-raw-binding-port.js +24 -41
  26. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +8 -8
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.js +100 -107
  28. package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +3 -2
  29. package/dist/runtime/backend/node/node-socket-backend-adapter.js +18 -67
  30. package/dist/runtime/backend/raw-binding-port.d.ts +1 -0
  31. package/dist/runtime/backend/runtime-values.d.ts +6 -1
  32. package/dist/runtime/backend/runtime-values.js +6 -0
  33. package/dist/runtime/channels/channel-dispatchers.js +2 -2
  34. package/dist/runtime/channels/channel-envelope.js +324 -121
  35. package/dist/runtime/channels/channel-multipart.d.ts +8 -1
  36. package/dist/runtime/channels/channel-outbound-operations.js +0 -2
  37. package/dist/runtime/channels/channel-receive-loops.d.ts +1 -3
  38. package/dist/runtime/channels/channel-receive-loops.js +25 -65
  39. package/dist/runtime/channels/channel-socket-registry.js +14 -1
  40. package/dist/runtime/channels/channel-transports.d.ts +2 -1
  41. package/dist/runtime/channels/channel-transports.js +12 -10
  42. package/dist/runtime/channels/client-server-location-runtime.js +15 -3
  43. package/dist/runtime/execution/serial-execution-queue.d.ts +5 -18
  44. package/dist/runtime/execution/serial-execution-queue.js +16 -80
  45. package/dist/runtime/foundation/event-loop-resources.d.ts +0 -3
  46. package/dist/runtime/foundation/event-loop-resources.js +2 -11
  47. package/dist/runtime/foundation/operation-registry.d.ts +2 -8
  48. package/dist/runtime/foundation/operation-registry.js +3 -21
  49. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +19 -7
  50. package/dist/runtime/foundation/raw-service-mesh-runtime.js +56 -10
  51. package/dist/runtime/foundation/service-mailbox.d.ts +1 -7
  52. package/dist/runtime/foundation/service-mailbox.js +3 -2
  53. package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -1
  54. package/dist/runtime/foundation/service-stateful-registry.d.ts +1 -1
  55. package/dist/runtime/foundation/service-stateful-registry.js +2 -2
  56. package/dist/runtime/foundation/service-stateful-runtime.js +14 -14
  57. package/dist/runtime/framework-errors-internal.d.ts +4 -4
  58. package/dist/runtime/framework-errors-internal.js +8 -10
  59. package/dist/runtime/host/actor-placement-coordinator.js +1 -1
  60. package/dist/runtime/host/index.js +1 -1
  61. package/dist/runtime/host/service-relocation-host-runtime.js +0 -4
  62. package/dist/runtime/host/spot-address-transport.js +1 -1
  63. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -1
  64. package/dist/runtime/locations/location-store-repository.d.ts +1 -0
  65. package/dist/runtime/locations/location-store-repository.js +64 -75
  66. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +0 -12
  67. package/dist/runtime/spots/spot-node-runtime-manager.js +6 -166
  68. package/dist/runtime/spots/spot-serial-turn-executor.js +1 -4
  69. package/dist/runtime/spots/spot-timer.d.ts +3 -0
  70. package/dist/runtime/spots/spot-timer.js +87 -18
  71. package/dist/runtime/streams/session-serial-executor.js +1 -3
  72. package/dist/runtime/streams/stream-session-runtime.d.ts +2 -2
  73. package/dist/runtime/streams/stream-session-runtime.js +13 -19
  74. package/dist/runtime/workers/index.d.ts +0 -1
  75. package/dist/runtime/workers/index.js +1 -9
  76. package/package.json +9 -3
  77. package/src/contracts/Configuration/EndpointNotation.ts +5 -4
  78. package/src/contracts/Configuration/InternalDefaults.ts +0 -1
  79. package/src/contracts/Configuration/RegistrationNormalizers.ts +2 -4
  80. package/src/contracts/Configuration/RegistrationTypes.ts +2 -5
  81. package/src/contracts/Configuration/RegistrationValidators.ts +0 -1
  82. package/src/contracts/Errors/ZLinkFrameworkException.ts +6 -7
  83. package/src/runtime/actors/actor-client.ts +1 -1
  84. package/src/runtime/actors/actor-mailbox.ts +1 -12
  85. package/src/runtime/backend/contracts/index.ts +2 -0
  86. package/src/runtime/backend/mesh-completion-table.ts +12 -23
  87. package/src/runtime/backend/mesh-dispatch-pump.ts +206 -138
  88. package/src/runtime/backend/node/node-backend-adapter-factory.ts +66 -35
  89. package/src/runtime/backend/node/node-backend-adapter-support.ts +4 -4
  90. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +2 -2
  91. package/src/runtime/backend/node/node-event-loop-poller.ts +55 -0
  92. package/src/runtime/backend/node/node-raw-binding-port.ts +30 -45
  93. package/src/runtime/backend/node/node-raw-mesh-backend.ts +97 -110
  94. package/src/runtime/backend/node/node-socket-backend-adapter.ts +23 -69
  95. package/src/runtime/backend/raw-binding-port.ts +1 -0
  96. package/src/runtime/backend/runtime-values.ts +13 -1
  97. package/src/runtime/channels/channel-dispatchers.ts +2 -2
  98. package/src/runtime/channels/channel-envelope.ts +379 -109
  99. package/src/runtime/channels/channel-multipart.ts +9 -1
  100. package/src/runtime/channels/channel-outbound-operations.ts +0 -1
  101. package/src/runtime/channels/channel-receive-loops.ts +24 -75
  102. package/src/runtime/channels/channel-socket-registry.ts +17 -1
  103. package/src/runtime/channels/channel-transports.ts +20 -10
  104. package/src/runtime/channels/client-server-location-runtime.ts +16 -2
  105. package/src/runtime/execution/serial-execution-queue.ts +16 -108
  106. package/src/runtime/foundation/event-loop-resources.ts +2 -11
  107. package/src/runtime/foundation/operation-registry.ts +2 -22
  108. package/src/runtime/foundation/raw-service-mesh-runtime.ts +74 -19
  109. package/src/runtime/foundation/service-mailbox.ts +3 -13
  110. package/src/runtime/foundation/service-runtime-contracts.ts +2 -1
  111. package/src/runtime/foundation/service-stateful-registry.ts +2 -2
  112. package/src/runtime/foundation/service-stateful-runtime.ts +14 -14
  113. package/src/runtime/framework-errors-internal.ts +8 -10
  114. package/src/runtime/host/actor-placement-coordinator.ts +1 -1
  115. package/src/runtime/host/index.ts +2 -1
  116. package/src/runtime/host/service-relocation-host-runtime.ts +0 -4
  117. package/src/runtime/host/spot-address-transport.ts +1 -1
  118. package/src/runtime/host/user-spot-creation-coordinator.ts +1 -1
  119. package/src/runtime/locations/location-store-repository.ts +118 -80
  120. package/src/runtime/spots/spot-node-runtime-manager.ts +13 -233
  121. package/src/runtime/spots/spot-serial-turn-executor.ts +1 -8
  122. package/src/runtime/spots/spot-timer.ts +109 -18
  123. package/src/runtime/streams/session-serial-executor.ts +1 -7
  124. package/src/runtime/streams/stream-session-runtime.ts +13 -17
  125. package/src/runtime/workers/index.ts +2 -17
@@ -59,7 +59,9 @@ class ZLinkSpotTimerRegistry {
59
59
  : undefined;
60
60
  if (this.executionBarrier !== undefined)
61
61
  executionSerial?.setExecutionBarrier(this.executionBarrier);
62
+ let callbackStarted = false;
62
63
  const timer = startOutsideStateLane(() => new ZLinkManagedTimer(name, periodMs, normalizeTimerOptions(options), async (tick) => {
64
+ callbackStarted = false;
63
65
  const timerFlow = (0, flow_context_1.createInboundFlow)(undefined, 'Timer', this.flowCreationEnabled());
64
66
  const operation = () => {
65
67
  const current = this.timers.get(name);
@@ -68,6 +70,7 @@ class ZLinkSpotTimerRegistry {
68
70
  }
69
71
  if (!this.executionAllowed())
70
72
  return undefined;
73
+ callbackStarted = true;
71
74
  return (0, flow_context_1.runWithFlow)(timerFlow, () => handler.handle(spot, tick));
72
75
  };
73
76
  if (this.executeTimer !== undefined) {
@@ -76,9 +79,7 @@ class ZLinkSpotTimerRegistry {
76
79
  else {
77
80
  await executionSerial.execute(operation);
78
81
  }
79
- }, reportFailure, () => this.executeTimer === undefined
80
- ? !executionSerial.isExecuting
81
- : this.isTimerExecuting?.(name) !== true, this.clock));
82
+ }, reportFailure, () => callbackStarted, this.clock));
82
83
  const registered = await this.lane.run(() => this.completeAddCore(name, prepared.generation, handlerType.name, timer));
83
84
  if (!registered)
84
85
  await timer.cancel(signal);
@@ -86,13 +87,18 @@ class ZLinkSpotTimerRegistry {
86
87
  }
87
88
  async dispose() {
88
89
  const timers = await this.lane.run(() => {
89
- const active = [...this.timers.values()].map((entry) => entry.timer);
90
+ const active = [...this.timers.entries()].map(([name, entry]) => ({
91
+ name,
92
+ timer: entry.timer,
93
+ wait: this.executeTimer === undefined
94
+ ? true
95
+ : this.isTimerExecuting?.(name) !== true
96
+ }));
90
97
  this.timers.clear();
91
98
  return active;
92
99
  });
93
- for (const timer of timers) {
94
- await timer.dispose();
95
- }
100
+ const finalizations = timers.map(({ timer }) => timer.dispose());
101
+ await Promise.all(finalizations.filter((_, index) => timers[index].wait));
96
102
  }
97
103
  async captureRelocation() {
98
104
  const timers = await this.lane.run(() => [...this.timers.entries()].sort(([left], [right]) => left.localeCompare(right)));
@@ -155,6 +161,7 @@ class ZLinkRegisteredTimer {
155
161
  name;
156
162
  generation;
157
163
  timer;
164
+ finalization;
158
165
  constructor(registry, name, generation, timer) {
159
166
  this.registry = registry;
160
167
  this.name = name;
@@ -165,7 +172,16 @@ class ZLinkRegisteredTimer {
165
172
  return this.timer.isDisposed;
166
173
  }
167
174
  cancel(signal) {
168
- return this.registry.cancel(this.name, this.generation, this.timer, signal);
175
+ if (this.finalization !== undefined)
176
+ return this.finalization;
177
+ try {
178
+ (0, abort_1.throwIfAborted)(signal);
179
+ }
180
+ catch (failure) {
181
+ return Promise.reject(failure);
182
+ }
183
+ this.finalization = this.registry.cancel(this.name, this.generation, this.timer);
184
+ return this.finalization;
169
185
  }
170
186
  dispose() {
171
187
  return this.cancel();
@@ -188,6 +204,8 @@ class ZLinkManagedTimer {
188
204
  lastScheduledIndex = 0n;
189
205
  timeout;
190
206
  running = Promise.resolve();
207
+ dispatchFailure;
208
+ finalization;
191
209
  constructor(name, periodMs, options, onTick, onFailure, shouldWaitForRunningOnCancel = () => true, clock = systemTimerClock) {
192
210
  this.name = name;
193
211
  this.periodMs = periodMs;
@@ -203,9 +221,27 @@ class ZLinkManagedTimer {
203
221
  get isDisposed() {
204
222
  return this.disposed;
205
223
  }
206
- async cancel(_signal) {
207
- const prepared = await this.lane.run(() => this.cancelCore());
208
- await prepared.running;
224
+ cancel(_signal) {
225
+ if (this.finalization !== undefined)
226
+ return this.finalization.promise;
227
+ let resolve;
228
+ let reject;
229
+ const promise = new Promise((complete, fail) => {
230
+ resolve = complete;
231
+ reject = fail;
232
+ });
233
+ const finalization = { promise, resolve, reject };
234
+ this.finalization = finalization;
235
+ try {
236
+ const preparation = this.lane.run(() => this.cancelCore());
237
+ void preparation.then(start => {
238
+ void this.completeFinalization(start, finalization).then(undefined, reject);
239
+ }, reject);
240
+ }
241
+ catch (failure) {
242
+ reject(failure);
243
+ }
244
+ return promise;
209
245
  }
210
246
  dispose() {
211
247
  return this.cancel();
@@ -264,7 +300,10 @@ class ZLinkManagedTimer {
264
300
  : Math.max(0, Number(this.lastScheduledIndex + 1n) * this.periodMs - this.elapsedMs());
265
301
  this.timeout = this.clock.setTimeout(() => {
266
302
  this.timeout = undefined;
267
- this.running = startOutsideStateLane(() => this.fire()).catch(() => undefined);
303
+ const execution = startOutsideStateLane(() => this.fire());
304
+ this.running = execution.then(() => undefined, cause => this.lane.run(() => {
305
+ this.dispatchFailure ??= { cause };
306
+ }));
268
307
  }, delayMs);
269
308
  }
270
309
  async fire() {
@@ -284,14 +323,44 @@ class ZLinkManagedTimer {
284
323
  await this.lane.run(() => this.completeFireCore(prepared.scheduledIndex, shouldContinue));
285
324
  }
286
325
  cancelCore() {
287
- if (this.disposed)
288
- return { running: Promise.resolve() };
289
326
  this.disposed = true;
290
- if (this.timeout !== undefined) {
291
- this.clock.clearTimeout(this.timeout);
292
- this.timeout = undefined;
327
+ const timeout = this.timeout;
328
+ this.timeout = undefined;
329
+ return {
330
+ timeout,
331
+ running: this.shouldWaitForRunningOnCancel()
332
+ ? this.running
333
+ : Promise.resolve()
334
+ };
335
+ }
336
+ async completeFinalization(start, finalization) {
337
+ const failures = [];
338
+ if (start.timeout !== undefined) {
339
+ try {
340
+ this.clock.clearTimeout(start.timeout);
341
+ }
342
+ catch (failure) {
343
+ failures.push(failure);
344
+ }
345
+ }
346
+ try {
347
+ await start.running;
348
+ }
349
+ catch (failure) {
350
+ failures.push(failure);
351
+ }
352
+ const dispatchFailure = await this.lane.run(() => this.dispatchFailure);
353
+ if (dispatchFailure !== undefined)
354
+ failures.push(dispatchFailure.cause);
355
+ if (failures.length === 0) {
356
+ finalization.resolve();
357
+ }
358
+ else if (failures.length === 1) {
359
+ finalization.reject(failures[0]);
360
+ }
361
+ else {
362
+ finalization.reject(new AggregateError(failures, `Timer '${this.name}' cleanup failed.`));
293
363
  }
294
- return { running: this.shouldWaitForRunningOnCancel() ? this.running : Promise.resolve() };
295
364
  }
296
365
  prepareCaptureCore() {
297
366
  if (this.disposed)
@@ -5,9 +5,7 @@ const serial_execution_queue_1 = require("../execution/serial-execution-queue");
5
5
  const framework_errors_internal_1 = require("../framework-errors-internal");
6
6
  const application_job_queue_scope_1 = require("../application-jobs/application-job-queue-scope");
7
7
  class ZLinkSessionSerialExecutor {
8
- scheduler = new serial_execution_queue_1.ZLinkSerialExecutionQueue(async (record) => this.execute(record), {
9
- capacityError: (lane) => (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.WorkerQueueFull, `Stream Session ${lane} execution capacity is full.`, true)
10
- });
8
+ scheduler = new serial_execution_queue_1.ZLinkSerialExecutionQueue(async (record) => this.execute(record), {});
11
9
  closed = false;
12
10
  executeApplication(work, options = {}, onRejected) {
13
11
  return this.submit(work, 'application', options, onRejected);
@@ -131,7 +131,7 @@ export declare class ZLinkStreamSessionNodeRuntime {
131
131
  private stopped;
132
132
  private readonly receiveAbortController;
133
133
  private receiveLoop;
134
- private readonly receiveIdleWaiter;
134
+ private readonly receiveWake;
135
135
  private receiveWorkSinceYield;
136
136
  constructor(options: ZLinkStreamSessionNodeRuntimeOptions & ZLinkStreamLivenessOptions);
137
137
  start(): void;
@@ -159,7 +159,7 @@ export declare class ZLinkStreamSessionNodeRuntime {
159
159
  private removePendingConnectionMetadata;
160
160
  private resolveMonitorSession;
161
161
  private enqueueEndpointlessDisconnect;
162
- private waitReceiveLoopIdle;
162
+ private waitReceiveLoopWake;
163
163
  private wakeReceiveLoop;
164
164
  private getOrCreateSession;
165
165
  }
@@ -30,31 +30,21 @@ const systemLivenessClock = {
30
30
  setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
31
31
  clearTimer: (timer) => clearTimeout(timer)
32
32
  };
33
- class ZLinkStreamIdleWaiter {
34
- delayMs;
33
+ class ZLinkStreamReceiveWake {
35
34
  pending;
36
35
  resolvePending;
37
- timer;
38
36
  finish = () => {
39
- if (this.timer !== undefined) {
40
- clearTimeout(this.timer);
41
- this.timer = undefined;
42
- }
43
37
  const resolve = this.resolvePending;
44
38
  this.pending = undefined;
45
39
  this.resolvePending = undefined;
46
40
  resolve?.();
47
41
  };
48
- constructor(delayMs) {
49
- this.delayMs = delayMs;
50
- }
51
42
  wait() {
52
43
  if (this.pending !== undefined)
53
44
  return this.pending;
54
45
  this.pending = new Promise((resolve) => {
55
46
  this.resolvePending = resolve;
56
47
  });
57
- this.timer = setTimeout(this.finish, this.delayMs);
58
48
  return this.pending;
59
49
  }
60
50
  wake() {
@@ -664,7 +654,7 @@ class ZLinkStreamSessionNodeRuntime {
664
654
  stopped = false;
665
655
  receiveAbortController = new AbortController();
666
656
  receiveLoop;
667
- receiveIdleWaiter = new ZLinkStreamIdleWaiter(5);
657
+ receiveWake = new ZLinkStreamReceiveWake();
668
658
  receiveWorkSinceYield = 0;
669
659
  constructor(options) {
670
660
  this.options = options;
@@ -734,14 +724,15 @@ class ZLinkStreamSessionNodeRuntime {
734
724
  }
735
725
  if (!this.options.readablePoller.wait(0)) {
736
726
  permit.releaseAfterInternalProcessing();
737
- await this.waitReceiveLoopIdle();
727
+ await this.waitReceiveLoopWake(signal);
738
728
  continue;
739
729
  }
740
730
  packet = this.takePacket();
741
731
  if (!this.options.socket.recvPacket(packet, ZLINK_RECV_DONT_WAIT)) {
732
+ this.options.readablePoller.markDrained();
742
733
  permit.releaseAfterInternalProcessing();
743
734
  this.recyclePacket(packet);
744
- await this.waitReceiveLoopIdle();
735
+ await this.waitReceiveLoopWake(signal);
745
736
  continue;
746
737
  }
747
738
  packetTransferred = true;
@@ -757,11 +748,11 @@ class ZLinkStreamSessionNodeRuntime {
757
748
  if (!this.isReceiveStopped(signal) && !packetTransferred) {
758
749
  this.options.onError?.(error);
759
750
  this.receiveWorkSinceYield = 0;
760
- await this.waitReceiveLoopIdle();
751
+ await new Promise((resolve) => setImmediate(resolve));
761
752
  }
762
753
  else if (!this.isReceiveStopped(signal)) {
763
754
  this.receiveWorkSinceYield = 0;
764
- await this.waitReceiveLoopIdle();
755
+ await new Promise((resolve) => setImmediate(resolve));
765
756
  }
766
757
  continue;
767
758
  }
@@ -1091,11 +1082,14 @@ class ZLinkStreamSessionNodeRuntime {
1091
1082
  pending.session.enqueueDisconnected(pending.error);
1092
1083
  });
1093
1084
  }
1094
- waitReceiveLoopIdle() {
1095
- return this.receiveIdleWaiter.wait();
1085
+ async waitReceiveLoopWake(signal) {
1086
+ await Promise.race([
1087
+ this.options.readablePoller.waitForReadable(signal),
1088
+ this.receiveWake.wait()
1089
+ ]);
1096
1090
  }
1097
1091
  wakeReceiveLoop() {
1098
- this.receiveIdleWaiter.wake();
1092
+ this.receiveWake.wake();
1099
1093
  }
1100
1094
  getOrCreateSession(routingId) {
1101
1095
  const sessionId = (0, managed_stream_1.streamSessionIdFromRoutingId)(routingId);
@@ -6,7 +6,6 @@ export interface ZLinkWorkerRuntimeOptions {
6
6
  readonly minThreads: number;
7
7
  readonly maxThreads: number;
8
8
  readonly idleTimeoutMs: number;
9
- readonly maxQueueLength: number;
10
9
  }
11
10
  export declare function resolveWorkerRuntimeOptions(options?: ZLinkWorkerOptions): ZLinkWorkerRuntimeOptions;
12
11
  export declare class ZLinkWorkerRuntime {
@@ -13,13 +13,11 @@ function resolveWorkerRuntimeOptions(options) {
13
13
  const resolved = {
14
14
  minThreads: options?.minThreads ?? InternalDefaults_1.DEFAULT_WORKER_MIN_THREADS,
15
15
  maxThreads: options?.maxThreads ?? (0, InternalDefaults_1.defaultWorkerMaxThreads)(),
16
- idleTimeoutMs: options?.idleTimeoutMs ?? InternalDefaults_1.DEFAULT_WORKER_IDLE_TIMEOUT_MS,
17
- maxQueueLength: options?.maxQueueLength ?? InternalDefaults_1.DEFAULT_WORKER_QUEUE_LENGTH
16
+ idleTimeoutMs: options?.idleTimeoutMs ?? InternalDefaults_1.DEFAULT_WORKER_IDLE_TIMEOUT_MS
18
17
  };
19
18
  requireNonNegativeInteger('Worker minThreads', resolved.minThreads);
20
19
  requirePositiveInteger('Worker maxThreads', resolved.maxThreads);
21
20
  requireNonNegativeInteger('Worker idleTimeoutMs', resolved.idleTimeoutMs);
22
- requirePositiveInteger('Worker maxQueueLength', resolved.maxQueueLength);
23
21
  if (resolved.maxThreads < resolved.minThreads) {
24
22
  throw new configuration_1.ZLinkConfigurationException('Worker maxThreads must be greater than or equal to minThreads.');
25
23
  }
@@ -53,9 +51,6 @@ class ZLinkCpuWorkerPool {
53
51
  if (signal?.aborted === true) {
54
52
  return Promise.reject((0, abort_1.createAbortError)());
55
53
  }
56
- if (this.queueCount >= this.options.maxQueueLength) {
57
- return Promise.reject(workerQueueFull(this.options.maxQueueLength));
58
- }
59
54
  return new Promise((resolve, reject) => {
60
55
  const job = {
61
56
  source: work.toString(),
@@ -446,9 +441,6 @@ class ZLinkSerialDeliveredPromise {
446
441
  return this.then((value) => this.serial.execute(() => { onfinally?.(); return value; }), (reason) => this.serial.execute(() => { onfinally?.(); throw reason; }));
447
442
  }
448
443
  }
449
- function workerQueueFull(maxQueueLength) {
450
- return (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.WorkerQueueFull, `CPU worker queue is full (maxQueueLength=${maxQueueLength}).`, true);
451
- }
452
444
  function workerTimedOut(timeoutMs) {
453
445
  return (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.WorkerTimedOut, `Worker job timed out after ${timeoutMs}ms.`, true);
454
446
  }
package/package.json CHANGED
@@ -1,7 +1,13 @@
1
1
  {
2
2
  "name": "@zlink-systems/framework",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/zlink-systems/zlink.git",
8
+ "directory": "framework/languages/node/packages/framework"
9
+ },
10
+ "homepage": "https://zlink.systems/",
5
11
  "main": "dist/index.js",
6
12
  "types": "dist/index.d.ts",
7
13
  "exports": {
@@ -13,7 +19,7 @@
13
19
  "dependencies": {
14
20
  "@opentelemetry/api": "^1.9.1",
15
21
  "@opentelemetry/api-logs": "^0.221.0",
16
- "@zlink-systems/stream-wire": "0.10.0",
17
- "@zlink-systems/zlink": "0.17.3"
22
+ "@zlink-systems/stream-wire": "0.12.0",
23
+ "@zlink-systems/zlink": "1.1.0"
18
24
  }
19
25
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Deterministic endpoint-notation normalization (endpoint 표기 정책,
3
- * doc/plan/endpoint-notation-policy.ko.md).
2
+ * Deterministic endpoint-notation normalization (endpoint 표기 정책),
3
+ * applied identically across all four language bindings so endpoint
4
+ * strings compare equal.
4
5
  *
5
6
  * `new URL()` cannot represent this domain: it throws on bracketed IPv6
6
7
  * literals that carry a zone id (`tcp://[fe80::1%eth0]:80`, even with the
@@ -16,7 +17,7 @@
16
17
  * public connect/disconnect APIs). Everything downstream compares the
17
18
  * resulting strings with plain `===`.
18
19
  *
19
- * Rules (policy 2.2), applied only to authority-bearing schemes
20
+ * Rules, applied only to authority-bearing schemes
20
21
  * (`tcp`, `tls`, `ws`, `wss` -- schemes whose grammar after `scheme://` is
21
22
  * `host:port`):
22
23
  * - scheme: lowercased
@@ -39,7 +40,7 @@
39
40
  * `normalizeEndpoint(normalizeEndpoint(x)) === normalizeEndpoint(x)`.
40
41
  *
41
42
  * DNS names are never resolved. `localhost` and `127.0.0.1` remain distinct
42
- * endpoints (policy 2.1).
43
+ * endpoints.
43
44
  */
44
45
 
45
46
  const SCHEME_PATTERN = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/(.*)$/s;
@@ -6,7 +6,6 @@ export const DEFAULT_STREAM_NODE_MAX_MESSAGE_SIZE = 64 * 1024;
6
6
  /** Defaults shared by registration normalization and the runtime worker pool. */
7
7
  export const DEFAULT_WORKER_MIN_THREADS = 0;
8
8
  export const DEFAULT_WORKER_IDLE_TIMEOUT_MS = 30_000;
9
- export const DEFAULT_WORKER_QUEUE_LENGTH = 1024;
10
9
 
11
10
  export function defaultWorkerMaxThreads(): number {
12
11
  return Math.max(2, os.availableParallelism());
@@ -25,8 +25,7 @@ import {
25
25
  defaultWorkerMaxThreads,
26
26
  DEFAULT_STREAM_NODE_MAX_MESSAGE_SIZE,
27
27
  DEFAULT_WORKER_IDLE_TIMEOUT_MS,
28
- DEFAULT_WORKER_MIN_THREADS,
29
- DEFAULT_WORKER_QUEUE_LENGTH
28
+ DEFAULT_WORKER_MIN_THREADS
30
29
  } from './InternalDefaults';
31
30
 
32
31
  export function normalizeWorkerOptions(
@@ -37,8 +36,7 @@ export function normalizeWorkerOptions(
37
36
  return {
38
37
  minThreads: partial.minThreads ?? DEFAULT_WORKER_MIN_THREADS,
39
38
  maxThreads: partial.maxThreads ?? defaultWorkerMaxThreads(),
40
- idleTimeoutMs: partial.idleTimeoutMs ?? DEFAULT_WORKER_IDLE_TIMEOUT_MS,
41
- maxQueueLength: partial.maxQueueLength ?? DEFAULT_WORKER_QUEUE_LENGTH
39
+ idleTimeoutMs: partial.idleTimeoutMs ?? DEFAULT_WORKER_IDLE_TIMEOUT_MS
42
40
  };
43
41
  }
44
42
 
@@ -104,16 +104,13 @@ export interface ZLinkLocationRegistration {
104
104
  /**
105
105
  * CPU worker-thread pool settings. I/O workers do not consume this pool.
106
106
  * `minThreads` keeps a warm baseline, `maxThreads` bounds active CPU jobs,
107
- * `idleTimeoutMs` reclaims workers above the baseline, and `maxQueueLength`
108
- * bounds queued jobs. The registration normalizer supplies defaults when a
109
- * JavaScript caller omits a field: `0`, `max(2, availableParallelism())`,
110
- * `30000`, and `1024`, respectively.
107
+ * `idleTimeoutMs` reclaims workers above the baseline. The registration
108
+ * normalizer supplies defaults when a JavaScript caller omits a field.
111
109
  */
112
110
  export interface ZLinkWorkerOptions {
113
111
  readonly minThreads: number;
114
112
  readonly maxThreads: number;
115
113
  readonly idleTimeoutMs: number;
116
- readonly maxQueueLength: number;
117
114
  }
118
115
 
119
116
  export interface ZLinkCodecSerializerRegistration {
@@ -97,7 +97,6 @@ function validateWorkerOptions(worker: ZLinkWorkerOptions | undefined): void {
97
97
  requireNonNegativeInteger('Worker minThreads', worker.minThreads);
98
98
  requirePositiveInteger('Worker maxThreads', worker.maxThreads);
99
99
  requireNonNegativeInteger('Worker idleTimeoutMs', worker.idleTimeoutMs);
100
- requirePositiveInteger('Worker maxQueueLength', worker.maxQueueLength);
101
100
  if (worker.maxThreads < worker.minThreads) {
102
101
  throw new ZLinkConfigurationException(
103
102
  'Worker maxThreads must be greater than or equal to minThreads.'
@@ -6,13 +6,12 @@ export enum ZLinkFrameworkErrorKind {
6
6
  NotConfigured = 3,
7
7
  Rejected = 4,
8
8
  Unavailable = 5,
9
- CapacityExceeded = 6,
10
- DeadlineExceeded = 7,
11
- ShuttingDown = 8,
12
- ProtocolError = 9,
13
- InvalidOperation = 10,
14
- DataLost = 11,
15
- InternalFailure = 12
9
+ DeadlineExceeded = 6,
10
+ ShuttingDown = 7,
11
+ ProtocolError = 8,
12
+ InvalidOperation = 9,
13
+ DataLost = 10,
14
+ InternalFailure = 11
16
15
  }
17
16
 
18
17
  export class ZLinkFrameworkException extends Error {
@@ -916,7 +916,7 @@ function submitted(): ZLinkSubmitResult {
916
916
  // the reply comes from a remote target, so a fine framework failure code
917
917
  // refines the coarse terminal, and a terminal-only conflict/busy is the
918
918
  // target's owner/queue state (retryable stale/Unavailable), never a
919
- // source-owned CapacityExceeded. Backpressure never appears on this reply
919
+ // source-owned queue exhaustion. Backpressure never appears on this reply
920
920
  // path. Internal kinds are preserved so the stale-actor re-resolve retry
921
921
  // (isStaleActorError) keeps working.
922
922
  function actorFailureCodeKind(
@@ -5,10 +5,6 @@ import {
5
5
  type ZLinkSerialWorkOptions,
6
6
  type ZLinkSerialWorkRecord
7
7
  } from '../execution/serial-execution-queue';
8
- import {
9
- ZLinkFrameworkInternalErrorKind,
10
- createInternalFrameworkException
11
- } from '../framework-errors-internal';
12
8
  import { runZLinkActorExecution } from './actor-execution-context';
13
9
  import {
14
10
  bindApplicationJobPermit,
@@ -25,14 +21,7 @@ export class ZLinkActorSerialExecutor {
25
21
  ) {
26
22
  this.scheduler = new ZLinkSerialExecutionQueue(
27
23
  (record) => this.runRecord(record),
28
- {
29
- ...options,
30
- capacityError: options?.capacityError ?? (() =>
31
- createInternalFrameworkException(
32
- ZLinkFrameworkInternalErrorKind.WorkerQueueFull,
33
- 'Actor execution queue capacity was exceeded.'
34
- ))
35
- }
24
+ options
36
25
  );
37
26
  }
38
27
 
@@ -578,6 +578,8 @@ export interface ZLinkBackendSubscriberSocket extends ZLinkBackendConnectableSoc
578
578
 
579
579
  export interface ZLinkBackendReadablePoller {
580
580
  wait(timeoutMs: number): boolean;
581
+ waitForReadable(signal?: AbortSignal): Promise<boolean>;
582
+ markDrained(): void;
581
583
  dispose(): void;
582
584
  }
583
585
 
@@ -1,16 +1,11 @@
1
1
  import { ZLinkBufferMessage as Message } from './runtime-message';
2
+ import type { Message as ZLinkMessage } from '../../contracts/Common/Message';
2
3
  import type {
3
4
  MeshOperationId,
4
5
  ReceiveKindData,
5
6
  ReceiveRecord
6
7
  } from '../foundation/service-runtime-contracts';
7
8
  import { operationIdentityKey } from '../foundation/operation-identity';
8
- import {
9
- ZLinkFrameworkErrorKind,
10
- ZLinkFrameworkException
11
- } from '../../contracts';
12
-
13
- export const ZLINK_MESH_COMPLETION_CAPACITY = 4_096;
14
9
 
15
10
  export interface ZLinkMeshCompletionDiagnostic {
16
11
  readonly kind: 'unknownOrLate';
@@ -36,14 +31,7 @@ export class ZLinkMeshCompletionTable {
36
31
  private readonly pending = new Map<string, PendingCompletion>();
37
32
  private disposed = false;
38
33
 
39
- constructor(
40
- private readonly maxPendingOperations = ZLINK_MESH_COMPLETION_CAPACITY,
41
- private readonly onDiagnostic?: (diagnostic: ZLinkMeshCompletionDiagnostic) => void
42
- ) {
43
- if (!Number.isSafeInteger(maxPendingOperations) || maxPendingOperations <= 0) {
44
- throw new RangeError('maxPendingOperations must be a positive safe integer.');
45
- }
46
- }
34
+ constructor(private readonly onDiagnostic?: (diagnostic: ZLinkMeshCompletionDiagnostic) => void) {}
47
35
 
48
36
  /**
49
37
  * Submits and registers without yielding control to the event loop. Mesh
@@ -63,12 +51,6 @@ export class ZLinkMeshCompletionTable {
63
51
  signal.reason ?? new DOMException('The operation was aborted.', 'AbortError')
64
52
  );
65
53
  }
66
- if (this.pending.size >= this.maxPendingOperations) {
67
- return Promise.reject(new ZLinkFrameworkException(
68
- ZLinkFrameworkErrorKind.CapacityExceeded,
69
- `Mesh completion capacity ${this.maxPendingOperations} is exhausted.`
70
- ));
71
- }
72
54
  const operationId = operation();
73
55
  // The backend submission callback can synchronously re-enter `dispose()`.
74
56
  // TypeScript's control-flow analysis cannot observe mutation through it.
@@ -117,7 +99,7 @@ export class ZLinkMeshCompletionTable {
117
99
  this.pending.delete(key);
118
100
  pending.removeAbort?.();
119
101
  try {
120
- pending.resolve(copyCompletion(record));
102
+ pending.resolve(retainCompletion(record));
121
103
  } catch (error) {
122
104
  pending.reject(error);
123
105
  }
@@ -144,16 +126,23 @@ export class ZLinkMeshCompletionTable {
144
126
 
145
127
  }
146
128
 
147
- function copyCompletion(record: ReceiveRecord): ZLinkMeshCompletion {
129
+ function retainCompletion(record: ReceiveRecord): ZLinkMeshCompletion {
148
130
  return {
149
131
  terminalResult: record.terminalResult,
150
132
  failureErrno: record.failureErrno,
151
133
  operationKind: record.operationKind,
152
134
  kindData: record.kindData,
153
- parts: record.parts.map((part) => Message.fromOwned(Buffer.from(part.data())))
135
+ parts: record.parts.map(retainCompletionPart)
154
136
  };
155
137
  }
156
138
 
139
+ function retainCompletionPart(part: ZLinkMessage): Message {
140
+ // A runtime message already owns a managed Buffer and close() is a no-op.
141
+ // Binding-native messages can expose native storage through data(), which
142
+ // becomes invalid when their owner closes; materialize that one boundary copy.
143
+ return part instanceof Message ? part : Message.fromOwned(Buffer.from(part.data()));
144
+ }
145
+
157
146
  export function closeMeshCompletion(completion: ZLinkMeshCompletion): void {
158
147
  closeParts(completion.parts);
159
148
  }