@naylence/runtime 0.3.5-test.954 → 0.3.5-test.956

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.
@@ -13,12 +13,12 @@ import fastify from 'fastify';
13
13
  import websocketPlugin from '@fastify/websocket';
14
14
 
15
15
  // This file is auto-generated during build - do not edit manually
16
- // Generated from package.json version: 0.3.5-test.954
16
+ // Generated from package.json version: 0.3.5-test.956
17
17
  /**
18
18
  * The package version, injected at build time.
19
19
  * @internal
20
20
  */
21
- const VERSION = '0.3.5-test.954';
21
+ const VERSION = '0.3.5-test.956';
22
22
 
23
23
  /**
24
24
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -836,7 +836,7 @@ class TaskCancelledError extends Error {
836
836
  * Provides functionality similar to Python's asyncio TaskSpawner with proper
837
837
  * error handling, cancellation, and graceful shutdown capabilities.
838
838
  */
839
- const logger$1b = getLogger('naylence.fame.util.task_spawner');
839
+ const logger$1c = getLogger('naylence.fame.util.task_spawner');
840
840
  function firstDefined(source, keys) {
841
841
  for (const key of keys) {
842
842
  if (Object.prototype.hasOwnProperty.call(source, key)) {
@@ -997,7 +997,7 @@ class TaskSpawner {
997
997
  const taskId = `task-${++this._taskCounter}`;
998
998
  const taskName = normalizedOptions.name || `unnamed-${taskId}`;
999
999
  const timeout = normalizedOptions.timeout ?? this._config.defaultTimeout;
1000
- logger$1b.debug('starting_background_task', {
1000
+ logger$1c.debug('starting_background_task', {
1001
1001
  task_name: taskName,
1002
1002
  task_id: taskId,
1003
1003
  });
@@ -1014,7 +1014,7 @@ class TaskSpawner {
1014
1014
  task.promise
1015
1015
  .then(() => {
1016
1016
  if (!this._suppressCompletionLogging) {
1017
- logger$1b.debug('task_completed_successfully', {
1017
+ logger$1c.debug('task_completed_successfully', {
1018
1018
  task_name: taskName,
1019
1019
  task_id: taskId,
1020
1020
  duration_ms: Date.now() - task.startTime,
@@ -1068,7 +1068,7 @@ class TaskSpawner {
1068
1068
  error.name === 'AbortError' ||
1069
1069
  error.message === 'Task cancelled' ||
1070
1070
  error.message === 'Aborted') {
1071
- logger$1b.debug('task_cancelled', {
1071
+ logger$1c.debug('task_cancelled', {
1072
1072
  task_name: taskName,
1073
1073
  note: 'Task cancelled as requested',
1074
1074
  });
@@ -1076,7 +1076,7 @@ class TaskSpawner {
1076
1076
  }
1077
1077
  // Handle timeout
1078
1078
  if (error instanceof TaskTimeoutError) {
1079
- logger$1b.warning('task_timed_out', {
1079
+ logger$1c.warning('task_timed_out', {
1080
1080
  task_name: taskName,
1081
1081
  error: error.message,
1082
1082
  });
@@ -1088,7 +1088,7 @@ class TaskSpawner {
1088
1088
  // Handle known WebSocket shutdown race condition (similar to Python version)
1089
1089
  if (error.message.includes("await wasn't used with future") ||
1090
1090
  error.message.includes('WebSocket closed during receive')) {
1091
- logger$1b.debug('task_shutdown_race_condition_handled', {
1091
+ logger$1c.debug('task_shutdown_race_condition_handled', {
1092
1092
  task_name: taskName,
1093
1093
  note: 'Normal WebSocket close timing during shutdown - not an error',
1094
1094
  });
@@ -1098,7 +1098,7 @@ class TaskSpawner {
1098
1098
  if (error.name === 'FameTransportClose' ||
1099
1099
  error.message.includes('normal closure') ||
1100
1100
  error.message.includes('Connection closed')) {
1101
- logger$1b.debug('task_shutdown_completed_normally', {
1101
+ logger$1c.debug('task_shutdown_completed_normally', {
1102
1102
  task_name: taskName,
1103
1103
  note: 'Task closed normally during shutdown',
1104
1104
  });
@@ -1111,14 +1111,14 @@ class TaskSpawner {
1111
1111
  // Log retriable errors as warnings (they'll be retried by upstream logic)
1112
1112
  // Log non-retriable errors as errors (fatal failures)
1113
1113
  if (isRetriableError) {
1114
- logger$1b.warning('background_task_failed', {
1114
+ logger$1c.warning('background_task_failed', {
1115
1115
  task_name: taskName,
1116
1116
  error: error.message,
1117
1117
  retriable: true,
1118
1118
  });
1119
1119
  }
1120
1120
  else {
1121
- logger$1b.error('background_task_failed', {
1121
+ logger$1c.error('background_task_failed', {
1122
1122
  task_name: taskName,
1123
1123
  error: error.message,
1124
1124
  stack: error.stack,
@@ -1137,11 +1137,11 @@ class TaskSpawner {
1137
1137
  async shutdownTasks(options = {}) {
1138
1138
  const { gracePeriod, cancelHanging, joinTimeout } = normalizeShutdownOptions(options);
1139
1139
  if (this._tasks.size === 0) {
1140
- logger$1b.debug('shutdown_tasks_no_tasks_to_shutdown');
1140
+ logger$1c.debug('shutdown_tasks_no_tasks_to_shutdown');
1141
1141
  return;
1142
1142
  }
1143
1143
  this._suppressCompletionLogging = true;
1144
- logger$1b.debug('shutting_down_tasks', {
1144
+ logger$1c.debug('shutting_down_tasks', {
1145
1145
  task_count: this._tasks.size,
1146
1146
  task_names: Array.from(this._tasks.values()).map((t) => t.name),
1147
1147
  grace_period_ms: gracePeriod,
@@ -1156,7 +1156,7 @@ class TaskSpawner {
1156
1156
  if (cancelHanging) {
1157
1157
  const stillRunning = tasks.filter((task) => task.getState() === TaskState.RUNNING && !completed.has(task));
1158
1158
  if (stillRunning.length > 0) {
1159
- logger$1b.debug('tasks_did_not_complete_within_grace_period', {
1159
+ logger$1c.debug('tasks_did_not_complete_within_grace_period', {
1160
1160
  hanging_count: stillRunning.length,
1161
1161
  });
1162
1162
  // Wait for them to finish with individual timeouts
@@ -1166,7 +1166,7 @@ class TaskSpawner {
1166
1166
  }
1167
1167
  catch (error) {
1168
1168
  if (error instanceof TaskTimeoutError) {
1169
- logger$1b.warning('task_did_not_shutdown', {
1169
+ logger$1c.warning('task_did_not_shutdown', {
1170
1170
  task_name: task.name || task.id,
1171
1171
  join_timeout_ms: joinTimeout,
1172
1172
  });
@@ -1177,7 +1177,7 @@ class TaskSpawner {
1177
1177
  }
1178
1178
  else if (!(error instanceof TaskCancelledError)) {
1179
1179
  /* istanbul ignore next - unreachable defensive branch */
1180
- logger$1b.error('task_raised_during_cancellation', {
1180
+ logger$1c.error('task_raised_during_cancellation', {
1181
1181
  task_name: task.name || task.id,
1182
1182
  error: error instanceof Error ? error.message : String(error),
1183
1183
  });
@@ -2270,6 +2270,7 @@ function validateKeyCorrelationTtlSec(ttlSec) {
2270
2270
  * condition/promise and ensure at most one notifier coroutine exists for a
2271
2271
  * flow at any time.
2272
2272
  */
2273
+ const logger$1b = getLogger('naylence.fame.flow.flow_controller');
2273
2274
  /**
2274
2275
  * Simple condition variable implementation for TypeScript/Node.js
2275
2276
  * Similar to Python's asyncio.Condition
@@ -2395,8 +2396,17 @@ class FlowController {
2395
2396
  // clamp into [0, initialWindow]
2396
2397
  const newBalance = Math.max(0, Math.min(this.initialWindow, prev + delta));
2397
2398
  this.credits.set(flowId, newBalance);
2399
+ const crossedZero = prev <= 0 && newBalance > 0;
2400
+ logger$1b.debug('flow_controller_add_credits', {
2401
+ flow_id: flowId,
2402
+ delta,
2403
+ prev_balance: prev,
2404
+ new_balance: newBalance,
2405
+ initial_window: this.initialWindow,
2406
+ crossed_zero: crossedZero,
2407
+ });
2398
2408
  // wake waiters only if we crossed the zero boundary
2399
- if (prev <= 0 && newBalance > 0) {
2409
+ if (crossedZero) {
2400
2410
  this.wakeWaiters(flowId);
2401
2411
  }
2402
2412
  return newBalance;
@@ -2407,11 +2417,27 @@ class FlowController {
2407
2417
  async acquire(flowId) {
2408
2418
  this.ensureFlow(flowId);
2409
2419
  const condition = this.conditions.get(flowId);
2420
+ logger$1b.debug('flow_controller_acquire_attempt', {
2421
+ flow_id: flowId,
2422
+ current_balance: this.credits.get(flowId),
2423
+ });
2410
2424
  while (this.credits.get(flowId) <= 0) {
2425
+ logger$1b.debug('flow_controller_waiting_for_credit', {
2426
+ flow_id: flowId,
2427
+ });
2411
2428
  await condition.wait();
2429
+ logger$1b.debug('flow_controller_woke_with_credit', {
2430
+ flow_id: flowId,
2431
+ balance_after_wake: this.credits.get(flowId),
2432
+ });
2412
2433
  }
2413
2434
  const current = this.credits.get(flowId);
2414
2435
  this.credits.set(flowId, current - 1);
2436
+ logger$1b.debug('flow_controller_credit_consumed', {
2437
+ flow_id: flowId,
2438
+ prev_balance: current,
2439
+ remaining_balance: current - 1,
2440
+ });
2415
2441
  }
2416
2442
  /**
2417
2443
  * Consume *credits* immediately (non-blocking).
@@ -2431,6 +2457,12 @@ class FlowController {
2431
2457
  const current = this.credits.get(flowId);
2432
2458
  const remaining = Math.max(current - credits, 0);
2433
2459
  this.credits.set(flowId, remaining);
2460
+ logger$1b.debug('flow_controller_consume', {
2461
+ flow_id: flowId,
2462
+ requested: credits,
2463
+ prev_balance: current,
2464
+ remaining_balance: remaining,
2465
+ });
2434
2466
  return remaining;
2435
2467
  }
2436
2468
  /**
@@ -2451,6 +2483,10 @@ class FlowController {
2451
2483
  this.windowIds.delete(flowId);
2452
2484
  this.credits.set(flowId, this.initialWindow);
2453
2485
  this.wakeWaiters(flowId);
2486
+ logger$1b.debug('flow_controller_flow_reset', {
2487
+ flow_id: flowId,
2488
+ reset_balance: this.initialWindow,
2489
+ });
2454
2490
  }
2455
2491
  /**
2456
2492
  * Return `[windowId, flags]` for the next outbound envelope.
@@ -9298,10 +9334,12 @@ class BaseAsyncConnector extends TaskSpawner {
9298
9334
  throw new FameTransportClose('Connection closed', 1006);
9299
9335
  }
9300
9336
  // Apply flow control if enabled and not a credit update
9301
- if (this._fcEnabled &&
9302
- !(envelope.frame &&
9303
- 'flow_id' in envelope.frame &&
9304
- 'credits' in envelope.frame)) {
9337
+ const isCreditUpdateFrame = Boolean(envelope.frame &&
9338
+ (envelope.frame.type === 'CreditUpdate' ||
9339
+ envelope.frame.type === 'credit_update' ||
9340
+ ('flowId' in envelope.frame && 'credits' in envelope.frame) ||
9341
+ ('flow_id' in envelope.frame && 'credits' in envelope.frame)));
9342
+ if (this._fcEnabled && !isCreditUpdateFrame) {
9305
9343
  const flowId = envelope.flowId || this._connectorFlowId;
9306
9344
  envelope.flowId = flowId;
9307
9345
  const t0 = this._metrics ? performance.now() : 0;
@@ -9796,6 +9834,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9796
9834
  ? Math.floor(config.inboxCapacity)
9797
9835
  : DEFAULT_INBOX_CAPACITY$7;
9798
9836
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9837
+ this.inboxCapacity = preferredCapacity;
9799
9838
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9800
9839
  this.channel = new BroadcastChannel(this.channelName);
9801
9840
  logger$_.debug('broadcast_channel_connector_created', {
@@ -9855,15 +9894,27 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9855
9894
  if (typeof this.inbox.tryEnqueue === 'function') {
9856
9895
  const accepted = this.inbox.tryEnqueue(payload);
9857
9896
  if (accepted) {
9897
+ this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
9898
+ source: 'listener',
9899
+ enqueue_strategy: 'try',
9900
+ payload_length: payload.byteLength,
9901
+ });
9858
9902
  return;
9859
9903
  }
9860
9904
  }
9861
9905
  this.inbox.enqueue(payload);
9906
+ this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
9907
+ source: 'listener',
9908
+ enqueue_strategy: 'enqueue',
9909
+ payload_length: payload.byteLength,
9910
+ });
9862
9911
  }
9863
9912
  catch (error) {
9864
9913
  if (error instanceof QueueFullError) {
9865
9914
  logger$_.warning('broadcast_channel_receive_queue_full', {
9866
9915
  channel: this.channelName,
9916
+ inbox_capacity: this.inboxCapacity,
9917
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
9867
9918
  });
9868
9919
  }
9869
9920
  else {
@@ -9874,8 +9925,10 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9874
9925
  }
9875
9926
  }
9876
9927
  };
9877
- this.channel.addEventListener('message', this.onMsg);
9878
- this.listenerRegistered = true;
9928
+ if (!config.passive) {
9929
+ this.channel.addEventListener('message', this.onMsg);
9930
+ this.listenerRegistered = true;
9931
+ }
9879
9932
  // Setup visibility change monitoring
9880
9933
  this.visibilityChangeHandler = () => {
9881
9934
  const isHidden = document.hidden;
@@ -9947,15 +10000,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9947
10000
  if (typeof this.inbox.tryEnqueue === 'function') {
9948
10001
  const accepted = this.inbox.tryEnqueue(item);
9949
10002
  if (accepted) {
10003
+ this.logInboxSnapshot('broadcast_channel_push_enqueued', {
10004
+ enqueue_strategy: 'try',
10005
+ item_type: this._describeInboxItem(item),
10006
+ });
9950
10007
  return;
9951
10008
  }
9952
10009
  }
9953
10010
  this.inbox.enqueue(item);
10011
+ this.logInboxSnapshot('broadcast_channel_push_enqueued', {
10012
+ enqueue_strategy: 'enqueue',
10013
+ item_type: this._describeInboxItem(item),
10014
+ });
9954
10015
  }
9955
10016
  catch (error) {
9956
10017
  if (error instanceof QueueFullError) {
9957
10018
  logger$_.warning('broadcast_channel_push_queue_full', {
9958
10019
  channel: this.channelName,
10020
+ inbox_capacity: this.inboxCapacity,
10021
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
9959
10022
  });
9960
10023
  throw error;
9961
10024
  }
@@ -9978,7 +10041,11 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9978
10041
  });
9979
10042
  }
9980
10043
  async _transportReceive() {
9981
- return await this.inbox.dequeue();
10044
+ const item = await this.inbox.dequeue();
10045
+ this.logInboxSnapshot('broadcast_channel_inbox_dequeued', {
10046
+ item_type: this._describeInboxItem(item),
10047
+ });
10048
+ return item;
9982
10049
  }
9983
10050
  async _transportClose(code, reason) {
9984
10051
  logger$_.debug('broadcast_channel_transport_closing', {
@@ -10032,6 +10099,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10032
10099
  }
10033
10100
  return rawOrEnvelope;
10034
10101
  }
10102
+ _describeInboxItem(item) {
10103
+ if (item instanceof Uint8Array) {
10104
+ return 'bytes';
10105
+ }
10106
+ if (item.envelope) {
10107
+ return 'channel_message';
10108
+ }
10109
+ if (item.frame) {
10110
+ return 'envelope';
10111
+ }
10112
+ return 'unknown';
10113
+ }
10114
+ logInboxSnapshot(event, extra = {}) {
10115
+ logger$_.debug(event, {
10116
+ channel: this.channelName,
10117
+ connector_id: this.connectorId,
10118
+ connector_state: this.state,
10119
+ inbox_capacity: this.inboxCapacity,
10120
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
10121
+ ...extra,
10122
+ });
10123
+ }
10035
10124
  _shouldSkipDuplicateAck(senderId, payload) {
10036
10125
  const dedupKey = this._extractAckDedupKey(payload);
10037
10126
  if (!dedupKey) {
@@ -36170,6 +36259,7 @@ class BroadcastChannelListener extends TransportListener {
36170
36259
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
36171
36260
  channelName: this._channelName,
36172
36261
  inboxCapacity: this._inboxCapacity,
36262
+ passive: true,
36173
36263
  };
36174
36264
  }
36175
36265
  try {
@@ -36230,6 +36320,7 @@ class BroadcastChannelListener extends TransportListener {
36230
36320
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
36231
36321
  channelName: this._channelName,
36232
36322
  inboxCapacity: this._inboxCapacity,
36323
+ passive: true,
36233
36324
  };
36234
36325
  const channelCandidate = candidate.channelName ?? candidate['channel_name'];
36235
36326
  if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {