@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.
@@ -98,12 +98,12 @@ installProcessEnvShim();
98
98
  // --- END ENV SHIM ---
99
99
 
100
100
  // This file is auto-generated during build - do not edit manually
101
- // Generated from package.json version: 0.3.5-test.954
101
+ // Generated from package.json version: 0.3.5-test.956
102
102
  /**
103
103
  * The package version, injected at build time.
104
104
  * @internal
105
105
  */
106
- const VERSION = '0.3.5-test.954';
106
+ const VERSION = '0.3.5-test.956';
107
107
 
108
108
  /**
109
109
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -921,7 +921,7 @@ class TaskCancelledError extends Error {
921
921
  * Provides functionality similar to Python's asyncio TaskSpawner with proper
922
922
  * error handling, cancellation, and graceful shutdown capabilities.
923
923
  */
924
- const logger$1b = getLogger('naylence.fame.util.task_spawner');
924
+ const logger$1c = getLogger('naylence.fame.util.task_spawner');
925
925
  function firstDefined(source, keys) {
926
926
  for (const key of keys) {
927
927
  if (Object.prototype.hasOwnProperty.call(source, key)) {
@@ -1082,7 +1082,7 @@ class TaskSpawner {
1082
1082
  const taskId = `task-${++this._taskCounter}`;
1083
1083
  const taskName = normalizedOptions.name || `unnamed-${taskId}`;
1084
1084
  const timeout = normalizedOptions.timeout ?? this._config.defaultTimeout;
1085
- logger$1b.debug('starting_background_task', {
1085
+ logger$1c.debug('starting_background_task', {
1086
1086
  task_name: taskName,
1087
1087
  task_id: taskId,
1088
1088
  });
@@ -1099,7 +1099,7 @@ class TaskSpawner {
1099
1099
  task.promise
1100
1100
  .then(() => {
1101
1101
  if (!this._suppressCompletionLogging) {
1102
- logger$1b.debug('task_completed_successfully', {
1102
+ logger$1c.debug('task_completed_successfully', {
1103
1103
  task_name: taskName,
1104
1104
  task_id: taskId,
1105
1105
  duration_ms: Date.now() - task.startTime,
@@ -1153,7 +1153,7 @@ class TaskSpawner {
1153
1153
  error.name === 'AbortError' ||
1154
1154
  error.message === 'Task cancelled' ||
1155
1155
  error.message === 'Aborted') {
1156
- logger$1b.debug('task_cancelled', {
1156
+ logger$1c.debug('task_cancelled', {
1157
1157
  task_name: taskName,
1158
1158
  note: 'Task cancelled as requested',
1159
1159
  });
@@ -1161,7 +1161,7 @@ class TaskSpawner {
1161
1161
  }
1162
1162
  // Handle timeout
1163
1163
  if (error instanceof TaskTimeoutError) {
1164
- logger$1b.warning('task_timed_out', {
1164
+ logger$1c.warning('task_timed_out', {
1165
1165
  task_name: taskName,
1166
1166
  error: error.message,
1167
1167
  });
@@ -1173,7 +1173,7 @@ class TaskSpawner {
1173
1173
  // Handle known WebSocket shutdown race condition (similar to Python version)
1174
1174
  if (error.message.includes("await wasn't used with future") ||
1175
1175
  error.message.includes('WebSocket closed during receive')) {
1176
- logger$1b.debug('task_shutdown_race_condition_handled', {
1176
+ logger$1c.debug('task_shutdown_race_condition_handled', {
1177
1177
  task_name: taskName,
1178
1178
  note: 'Normal WebSocket close timing during shutdown - not an error',
1179
1179
  });
@@ -1183,7 +1183,7 @@ class TaskSpawner {
1183
1183
  if (error.name === 'FameTransportClose' ||
1184
1184
  error.message.includes('normal closure') ||
1185
1185
  error.message.includes('Connection closed')) {
1186
- logger$1b.debug('task_shutdown_completed_normally', {
1186
+ logger$1c.debug('task_shutdown_completed_normally', {
1187
1187
  task_name: taskName,
1188
1188
  note: 'Task closed normally during shutdown',
1189
1189
  });
@@ -1196,14 +1196,14 @@ class TaskSpawner {
1196
1196
  // Log retriable errors as warnings (they'll be retried by upstream logic)
1197
1197
  // Log non-retriable errors as errors (fatal failures)
1198
1198
  if (isRetriableError) {
1199
- logger$1b.warning('background_task_failed', {
1199
+ logger$1c.warning('background_task_failed', {
1200
1200
  task_name: taskName,
1201
1201
  error: error.message,
1202
1202
  retriable: true,
1203
1203
  });
1204
1204
  }
1205
1205
  else {
1206
- logger$1b.error('background_task_failed', {
1206
+ logger$1c.error('background_task_failed', {
1207
1207
  task_name: taskName,
1208
1208
  error: error.message,
1209
1209
  stack: error.stack,
@@ -1222,11 +1222,11 @@ class TaskSpawner {
1222
1222
  async shutdownTasks(options = {}) {
1223
1223
  const { gracePeriod, cancelHanging, joinTimeout } = normalizeShutdownOptions(options);
1224
1224
  if (this._tasks.size === 0) {
1225
- logger$1b.debug('shutdown_tasks_no_tasks_to_shutdown');
1225
+ logger$1c.debug('shutdown_tasks_no_tasks_to_shutdown');
1226
1226
  return;
1227
1227
  }
1228
1228
  this._suppressCompletionLogging = true;
1229
- logger$1b.debug('shutting_down_tasks', {
1229
+ logger$1c.debug('shutting_down_tasks', {
1230
1230
  task_count: this._tasks.size,
1231
1231
  task_names: Array.from(this._tasks.values()).map((t) => t.name),
1232
1232
  grace_period_ms: gracePeriod,
@@ -1241,7 +1241,7 @@ class TaskSpawner {
1241
1241
  if (cancelHanging) {
1242
1242
  const stillRunning = tasks.filter((task) => task.getState() === TaskState.RUNNING && !completed.has(task));
1243
1243
  if (stillRunning.length > 0) {
1244
- logger$1b.debug('tasks_did_not_complete_within_grace_period', {
1244
+ logger$1c.debug('tasks_did_not_complete_within_grace_period', {
1245
1245
  hanging_count: stillRunning.length,
1246
1246
  });
1247
1247
  // Wait for them to finish with individual timeouts
@@ -1251,7 +1251,7 @@ class TaskSpawner {
1251
1251
  }
1252
1252
  catch (error) {
1253
1253
  if (error instanceof TaskTimeoutError) {
1254
- logger$1b.warning('task_did_not_shutdown', {
1254
+ logger$1c.warning('task_did_not_shutdown', {
1255
1255
  task_name: task.name || task.id,
1256
1256
  join_timeout_ms: joinTimeout,
1257
1257
  });
@@ -1262,7 +1262,7 @@ class TaskSpawner {
1262
1262
  }
1263
1263
  else if (!(error instanceof TaskCancelledError)) {
1264
1264
  /* istanbul ignore next - unreachable defensive branch */
1265
- logger$1b.error('task_raised_during_cancellation', {
1265
+ logger$1c.error('task_raised_during_cancellation', {
1266
1266
  task_name: task.name || task.id,
1267
1267
  error: error instanceof Error ? error.message : String(error),
1268
1268
  });
@@ -2355,6 +2355,7 @@ function validateKeyCorrelationTtlSec(ttlSec) {
2355
2355
  * condition/promise and ensure at most one notifier coroutine exists for a
2356
2356
  * flow at any time.
2357
2357
  */
2358
+ const logger$1b = getLogger('naylence.fame.flow.flow_controller');
2358
2359
  /**
2359
2360
  * Simple condition variable implementation for TypeScript/Node.js
2360
2361
  * Similar to Python's asyncio.Condition
@@ -2480,8 +2481,17 @@ class FlowController {
2480
2481
  // clamp into [0, initialWindow]
2481
2482
  const newBalance = Math.max(0, Math.min(this.initialWindow, prev + delta));
2482
2483
  this.credits.set(flowId, newBalance);
2484
+ const crossedZero = prev <= 0 && newBalance > 0;
2485
+ logger$1b.debug('flow_controller_add_credits', {
2486
+ flow_id: flowId,
2487
+ delta,
2488
+ prev_balance: prev,
2489
+ new_balance: newBalance,
2490
+ initial_window: this.initialWindow,
2491
+ crossed_zero: crossedZero,
2492
+ });
2483
2493
  // wake waiters only if we crossed the zero boundary
2484
- if (prev <= 0 && newBalance > 0) {
2494
+ if (crossedZero) {
2485
2495
  this.wakeWaiters(flowId);
2486
2496
  }
2487
2497
  return newBalance;
@@ -2492,11 +2502,27 @@ class FlowController {
2492
2502
  async acquire(flowId) {
2493
2503
  this.ensureFlow(flowId);
2494
2504
  const condition = this.conditions.get(flowId);
2505
+ logger$1b.debug('flow_controller_acquire_attempt', {
2506
+ flow_id: flowId,
2507
+ current_balance: this.credits.get(flowId),
2508
+ });
2495
2509
  while (this.credits.get(flowId) <= 0) {
2510
+ logger$1b.debug('flow_controller_waiting_for_credit', {
2511
+ flow_id: flowId,
2512
+ });
2496
2513
  await condition.wait();
2514
+ logger$1b.debug('flow_controller_woke_with_credit', {
2515
+ flow_id: flowId,
2516
+ balance_after_wake: this.credits.get(flowId),
2517
+ });
2497
2518
  }
2498
2519
  const current = this.credits.get(flowId);
2499
2520
  this.credits.set(flowId, current - 1);
2521
+ logger$1b.debug('flow_controller_credit_consumed', {
2522
+ flow_id: flowId,
2523
+ prev_balance: current,
2524
+ remaining_balance: current - 1,
2525
+ });
2500
2526
  }
2501
2527
  /**
2502
2528
  * Consume *credits* immediately (non-blocking).
@@ -2516,6 +2542,12 @@ class FlowController {
2516
2542
  const current = this.credits.get(flowId);
2517
2543
  const remaining = Math.max(current - credits, 0);
2518
2544
  this.credits.set(flowId, remaining);
2545
+ logger$1b.debug('flow_controller_consume', {
2546
+ flow_id: flowId,
2547
+ requested: credits,
2548
+ prev_balance: current,
2549
+ remaining_balance: remaining,
2550
+ });
2519
2551
  return remaining;
2520
2552
  }
2521
2553
  /**
@@ -2536,6 +2568,10 @@ class FlowController {
2536
2568
  this.windowIds.delete(flowId);
2537
2569
  this.credits.set(flowId, this.initialWindow);
2538
2570
  this.wakeWaiters(flowId);
2571
+ logger$1b.debug('flow_controller_flow_reset', {
2572
+ flow_id: flowId,
2573
+ reset_balance: this.initialWindow,
2574
+ });
2539
2575
  }
2540
2576
  /**
2541
2577
  * Return `[windowId, flags]` for the next outbound envelope.
@@ -9383,10 +9419,12 @@ class BaseAsyncConnector extends TaskSpawner {
9383
9419
  throw new FameTransportClose('Connection closed', 1006);
9384
9420
  }
9385
9421
  // Apply flow control if enabled and not a credit update
9386
- if (this._fcEnabled &&
9387
- !(envelope.frame &&
9388
- 'flow_id' in envelope.frame &&
9389
- 'credits' in envelope.frame)) {
9422
+ const isCreditUpdateFrame = Boolean(envelope.frame &&
9423
+ (envelope.frame.type === 'CreditUpdate' ||
9424
+ envelope.frame.type === 'credit_update' ||
9425
+ ('flowId' in envelope.frame && 'credits' in envelope.frame) ||
9426
+ ('flow_id' in envelope.frame && 'credits' in envelope.frame)));
9427
+ if (this._fcEnabled && !isCreditUpdateFrame) {
9390
9428
  const flowId = envelope.flowId || this._connectorFlowId;
9391
9429
  envelope.flowId = flowId;
9392
9430
  const t0 = this._metrics ? performance.now() : 0;
@@ -9881,6 +9919,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9881
9919
  ? Math.floor(config.inboxCapacity)
9882
9920
  : DEFAULT_INBOX_CAPACITY$7;
9883
9921
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
9922
+ this.inboxCapacity = preferredCapacity;
9884
9923
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
9885
9924
  this.channel = new BroadcastChannel(this.channelName);
9886
9925
  logger$_.debug('broadcast_channel_connector_created', {
@@ -9940,15 +9979,27 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9940
9979
  if (typeof this.inbox.tryEnqueue === 'function') {
9941
9980
  const accepted = this.inbox.tryEnqueue(payload);
9942
9981
  if (accepted) {
9982
+ this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
9983
+ source: 'listener',
9984
+ enqueue_strategy: 'try',
9985
+ payload_length: payload.byteLength,
9986
+ });
9943
9987
  return;
9944
9988
  }
9945
9989
  }
9946
9990
  this.inbox.enqueue(payload);
9991
+ this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
9992
+ source: 'listener',
9993
+ enqueue_strategy: 'enqueue',
9994
+ payload_length: payload.byteLength,
9995
+ });
9947
9996
  }
9948
9997
  catch (error) {
9949
9998
  if (error instanceof QueueFullError) {
9950
9999
  logger$_.warning('broadcast_channel_receive_queue_full', {
9951
10000
  channel: this.channelName,
10001
+ inbox_capacity: this.inboxCapacity,
10002
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
9952
10003
  });
9953
10004
  }
9954
10005
  else {
@@ -9959,8 +10010,10 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
9959
10010
  }
9960
10011
  }
9961
10012
  };
9962
- this.channel.addEventListener('message', this.onMsg);
9963
- this.listenerRegistered = true;
10013
+ if (!config.passive) {
10014
+ this.channel.addEventListener('message', this.onMsg);
10015
+ this.listenerRegistered = true;
10016
+ }
9964
10017
  // Setup visibility change monitoring
9965
10018
  this.visibilityChangeHandler = () => {
9966
10019
  const isHidden = document.hidden;
@@ -10032,15 +10085,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10032
10085
  if (typeof this.inbox.tryEnqueue === 'function') {
10033
10086
  const accepted = this.inbox.tryEnqueue(item);
10034
10087
  if (accepted) {
10088
+ this.logInboxSnapshot('broadcast_channel_push_enqueued', {
10089
+ enqueue_strategy: 'try',
10090
+ item_type: this._describeInboxItem(item),
10091
+ });
10035
10092
  return;
10036
10093
  }
10037
10094
  }
10038
10095
  this.inbox.enqueue(item);
10096
+ this.logInboxSnapshot('broadcast_channel_push_enqueued', {
10097
+ enqueue_strategy: 'enqueue',
10098
+ item_type: this._describeInboxItem(item),
10099
+ });
10039
10100
  }
10040
10101
  catch (error) {
10041
10102
  if (error instanceof QueueFullError) {
10042
10103
  logger$_.warning('broadcast_channel_push_queue_full', {
10043
10104
  channel: this.channelName,
10105
+ inbox_capacity: this.inboxCapacity,
10106
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
10044
10107
  });
10045
10108
  throw error;
10046
10109
  }
@@ -10063,7 +10126,11 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10063
10126
  });
10064
10127
  }
10065
10128
  async _transportReceive() {
10066
- return await this.inbox.dequeue();
10129
+ const item = await this.inbox.dequeue();
10130
+ this.logInboxSnapshot('broadcast_channel_inbox_dequeued', {
10131
+ item_type: this._describeInboxItem(item),
10132
+ });
10133
+ return item;
10067
10134
  }
10068
10135
  async _transportClose(code, reason) {
10069
10136
  logger$_.debug('broadcast_channel_transport_closing', {
@@ -10117,6 +10184,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
10117
10184
  }
10118
10185
  return rawOrEnvelope;
10119
10186
  }
10187
+ _describeInboxItem(item) {
10188
+ if (item instanceof Uint8Array) {
10189
+ return 'bytes';
10190
+ }
10191
+ if (item.envelope) {
10192
+ return 'channel_message';
10193
+ }
10194
+ if (item.frame) {
10195
+ return 'envelope';
10196
+ }
10197
+ return 'unknown';
10198
+ }
10199
+ logInboxSnapshot(event, extra = {}) {
10200
+ logger$_.debug(event, {
10201
+ channel: this.channelName,
10202
+ connector_id: this.connectorId,
10203
+ connector_state: this.state,
10204
+ inbox_capacity: this.inboxCapacity,
10205
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
10206
+ ...extra,
10207
+ });
10208
+ }
10120
10209
  _shouldSkipDuplicateAck(senderId, payload) {
10121
10210
  const dedupKey = this._extractAckDedupKey(payload);
10122
10211
  if (!dedupKey) {
@@ -29943,6 +30032,7 @@ class BroadcastChannelListener extends TransportListener {
29943
30032
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
29944
30033
  channelName: this._channelName,
29945
30034
  inboxCapacity: this._inboxCapacity,
30035
+ passive: true,
29946
30036
  };
29947
30037
  }
29948
30038
  try {
@@ -30003,6 +30093,7 @@ class BroadcastChannelListener extends TransportListener {
30003
30093
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE,
30004
30094
  channelName: this._channelName,
30005
30095
  inboxCapacity: this._inboxCapacity,
30096
+ passive: true,
30006
30097
  };
30007
30098
  const channelCandidate = candidate.channelName ?? candidate['channel_name'];
30008
30099
  if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {