@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.
@@ -724,7 +724,7 @@ class TaskCancelledError extends Error {
724
724
  * Provides functionality similar to Python's asyncio TaskSpawner with proper
725
725
  * error handling, cancellation, and graceful shutdown capabilities.
726
726
  */
727
- const logger$1g = getLogger('naylence.fame.util.task_spawner');
727
+ const logger$1h = getLogger('naylence.fame.util.task_spawner');
728
728
  function firstDefined(source, keys) {
729
729
  for (const key of keys) {
730
730
  if (Object.prototype.hasOwnProperty.call(source, key)) {
@@ -885,7 +885,7 @@ class TaskSpawner {
885
885
  const taskId = `task-${++this._taskCounter}`;
886
886
  const taskName = normalizedOptions.name || `unnamed-${taskId}`;
887
887
  const timeout = normalizedOptions.timeout ?? this._config.defaultTimeout;
888
- logger$1g.debug('starting_background_task', {
888
+ logger$1h.debug('starting_background_task', {
889
889
  task_name: taskName,
890
890
  task_id: taskId,
891
891
  });
@@ -902,7 +902,7 @@ class TaskSpawner {
902
902
  task.promise
903
903
  .then(() => {
904
904
  if (!this._suppressCompletionLogging) {
905
- logger$1g.debug('task_completed_successfully', {
905
+ logger$1h.debug('task_completed_successfully', {
906
906
  task_name: taskName,
907
907
  task_id: taskId,
908
908
  duration_ms: Date.now() - task.startTime,
@@ -956,7 +956,7 @@ class TaskSpawner {
956
956
  error.name === 'AbortError' ||
957
957
  error.message === 'Task cancelled' ||
958
958
  error.message === 'Aborted') {
959
- logger$1g.debug('task_cancelled', {
959
+ logger$1h.debug('task_cancelled', {
960
960
  task_name: taskName,
961
961
  note: 'Task cancelled as requested',
962
962
  });
@@ -964,7 +964,7 @@ class TaskSpawner {
964
964
  }
965
965
  // Handle timeout
966
966
  if (error instanceof TaskTimeoutError) {
967
- logger$1g.warning('task_timed_out', {
967
+ logger$1h.warning('task_timed_out', {
968
968
  task_name: taskName,
969
969
  error: error.message,
970
970
  });
@@ -976,7 +976,7 @@ class TaskSpawner {
976
976
  // Handle known WebSocket shutdown race condition (similar to Python version)
977
977
  if (error.message.includes("await wasn't used with future") ||
978
978
  error.message.includes('WebSocket closed during receive')) {
979
- logger$1g.debug('task_shutdown_race_condition_handled', {
979
+ logger$1h.debug('task_shutdown_race_condition_handled', {
980
980
  task_name: taskName,
981
981
  note: 'Normal WebSocket close timing during shutdown - not an error',
982
982
  });
@@ -986,7 +986,7 @@ class TaskSpawner {
986
986
  if (error.name === 'FameTransportClose' ||
987
987
  error.message.includes('normal closure') ||
988
988
  error.message.includes('Connection closed')) {
989
- logger$1g.debug('task_shutdown_completed_normally', {
989
+ logger$1h.debug('task_shutdown_completed_normally', {
990
990
  task_name: taskName,
991
991
  note: 'Task closed normally during shutdown',
992
992
  });
@@ -999,14 +999,14 @@ class TaskSpawner {
999
999
  // Log retriable errors as warnings (they'll be retried by upstream logic)
1000
1000
  // Log non-retriable errors as errors (fatal failures)
1001
1001
  if (isRetriableError) {
1002
- logger$1g.warning('background_task_failed', {
1002
+ logger$1h.warning('background_task_failed', {
1003
1003
  task_name: taskName,
1004
1004
  error: error.message,
1005
1005
  retriable: true,
1006
1006
  });
1007
1007
  }
1008
1008
  else {
1009
- logger$1g.error('background_task_failed', {
1009
+ logger$1h.error('background_task_failed', {
1010
1010
  task_name: taskName,
1011
1011
  error: error.message,
1012
1012
  stack: error.stack,
@@ -1025,11 +1025,11 @@ class TaskSpawner {
1025
1025
  async shutdownTasks(options = {}) {
1026
1026
  const { gracePeriod, cancelHanging, joinTimeout } = normalizeShutdownOptions(options);
1027
1027
  if (this._tasks.size === 0) {
1028
- logger$1g.debug('shutdown_tasks_no_tasks_to_shutdown');
1028
+ logger$1h.debug('shutdown_tasks_no_tasks_to_shutdown');
1029
1029
  return;
1030
1030
  }
1031
1031
  this._suppressCompletionLogging = true;
1032
- logger$1g.debug('shutting_down_tasks', {
1032
+ logger$1h.debug('shutting_down_tasks', {
1033
1033
  task_count: this._tasks.size,
1034
1034
  task_names: Array.from(this._tasks.values()).map((t) => t.name),
1035
1035
  grace_period_ms: gracePeriod,
@@ -1044,7 +1044,7 @@ class TaskSpawner {
1044
1044
  if (cancelHanging) {
1045
1045
  const stillRunning = tasks.filter((task) => task.getState() === TaskState.RUNNING && !completed.has(task));
1046
1046
  if (stillRunning.length > 0) {
1047
- logger$1g.debug('tasks_did_not_complete_within_grace_period', {
1047
+ logger$1h.debug('tasks_did_not_complete_within_grace_period', {
1048
1048
  hanging_count: stillRunning.length,
1049
1049
  });
1050
1050
  // Wait for them to finish with individual timeouts
@@ -1054,7 +1054,7 @@ class TaskSpawner {
1054
1054
  }
1055
1055
  catch (error) {
1056
1056
  if (error instanceof TaskTimeoutError) {
1057
- logger$1g.warning('task_did_not_shutdown', {
1057
+ logger$1h.warning('task_did_not_shutdown', {
1058
1058
  task_name: task.name || task.id,
1059
1059
  join_timeout_ms: joinTimeout,
1060
1060
  });
@@ -1065,7 +1065,7 @@ class TaskSpawner {
1065
1065
  }
1066
1066
  else if (!(error instanceof TaskCancelledError)) {
1067
1067
  /* istanbul ignore next - unreachable defensive branch */
1068
- logger$1g.error('task_raised_during_cancellation', {
1068
+ logger$1h.error('task_raised_during_cancellation', {
1069
1069
  task_name: task.name || task.id,
1070
1070
  error: error instanceof Error ? error.message : String(error),
1071
1071
  });
@@ -1184,6 +1184,7 @@ class TaskSpawner {
1184
1184
  * condition/promise and ensure at most one notifier coroutine exists for a
1185
1185
  * flow at any time.
1186
1186
  */
1187
+ const logger$1g = getLogger('naylence.fame.flow.flow_controller');
1187
1188
  /**
1188
1189
  * Simple condition variable implementation for TypeScript/Node.js
1189
1190
  * Similar to Python's asyncio.Condition
@@ -1309,8 +1310,17 @@ class FlowController {
1309
1310
  // clamp into [0, initialWindow]
1310
1311
  const newBalance = Math.max(0, Math.min(this.initialWindow, prev + delta));
1311
1312
  this.credits.set(flowId, newBalance);
1313
+ const crossedZero = prev <= 0 && newBalance > 0;
1314
+ logger$1g.debug('flow_controller_add_credits', {
1315
+ flow_id: flowId,
1316
+ delta,
1317
+ prev_balance: prev,
1318
+ new_balance: newBalance,
1319
+ initial_window: this.initialWindow,
1320
+ crossed_zero: crossedZero,
1321
+ });
1312
1322
  // wake waiters only if we crossed the zero boundary
1313
- if (prev <= 0 && newBalance > 0) {
1323
+ if (crossedZero) {
1314
1324
  this.wakeWaiters(flowId);
1315
1325
  }
1316
1326
  return newBalance;
@@ -1321,11 +1331,27 @@ class FlowController {
1321
1331
  async acquire(flowId) {
1322
1332
  this.ensureFlow(flowId);
1323
1333
  const condition = this.conditions.get(flowId);
1334
+ logger$1g.debug('flow_controller_acquire_attempt', {
1335
+ flow_id: flowId,
1336
+ current_balance: this.credits.get(flowId),
1337
+ });
1324
1338
  while (this.credits.get(flowId) <= 0) {
1339
+ logger$1g.debug('flow_controller_waiting_for_credit', {
1340
+ flow_id: flowId,
1341
+ });
1325
1342
  await condition.wait();
1343
+ logger$1g.debug('flow_controller_woke_with_credit', {
1344
+ flow_id: flowId,
1345
+ balance_after_wake: this.credits.get(flowId),
1346
+ });
1326
1347
  }
1327
1348
  const current = this.credits.get(flowId);
1328
1349
  this.credits.set(flowId, current - 1);
1350
+ logger$1g.debug('flow_controller_credit_consumed', {
1351
+ flow_id: flowId,
1352
+ prev_balance: current,
1353
+ remaining_balance: current - 1,
1354
+ });
1329
1355
  }
1330
1356
  /**
1331
1357
  * Consume *credits* immediately (non-blocking).
@@ -1345,6 +1371,12 @@ class FlowController {
1345
1371
  const current = this.credits.get(flowId);
1346
1372
  const remaining = Math.max(current - credits, 0);
1347
1373
  this.credits.set(flowId, remaining);
1374
+ logger$1g.debug('flow_controller_consume', {
1375
+ flow_id: flowId,
1376
+ requested: credits,
1377
+ prev_balance: current,
1378
+ remaining_balance: remaining,
1379
+ });
1348
1380
  return remaining;
1349
1381
  }
1350
1382
  /**
@@ -1365,6 +1397,10 @@ class FlowController {
1365
1397
  this.windowIds.delete(flowId);
1366
1398
  this.credits.set(flowId, this.initialWindow);
1367
1399
  this.wakeWaiters(flowId);
1400
+ logger$1g.debug('flow_controller_flow_reset', {
1401
+ flow_id: flowId,
1402
+ reset_balance: this.initialWindow,
1403
+ });
1368
1404
  }
1369
1405
  /**
1370
1406
  * Return `[windowId, flags]` for the next outbound envelope.
@@ -2228,10 +2264,12 @@ class BaseAsyncConnector extends TaskSpawner {
2228
2264
  throw new FameTransportClose('Connection closed', 1006);
2229
2265
  }
2230
2266
  // Apply flow control if enabled and not a credit update
2231
- if (this._fcEnabled &&
2232
- !(envelope.frame &&
2233
- 'flow_id' in envelope.frame &&
2234
- 'credits' in envelope.frame)) {
2267
+ const isCreditUpdateFrame = Boolean(envelope.frame &&
2268
+ (envelope.frame.type === 'CreditUpdate' ||
2269
+ envelope.frame.type === 'credit_update' ||
2270
+ ('flowId' in envelope.frame && 'credits' in envelope.frame) ||
2271
+ ('flow_id' in envelope.frame && 'credits' in envelope.frame)));
2272
+ if (this._fcEnabled && !isCreditUpdateFrame) {
2235
2273
  const flowId = envelope.flowId || this._connectorFlowId;
2236
2274
  envelope.flowId = flowId;
2237
2275
  const t0 = this._metrics ? performance.now() : 0;
@@ -5477,12 +5515,12 @@ for (const [name, config] of Object.entries(SQLITE_PROFILES)) {
5477
5515
  }
5478
5516
 
5479
5517
  // This file is auto-generated during build - do not edit manually
5480
- // Generated from package.json version: 0.3.5-test.954
5518
+ // Generated from package.json version: 0.3.5-test.956
5481
5519
  /**
5482
5520
  * The package version, injected at build time.
5483
5521
  * @internal
5484
5522
  */
5485
- const VERSION = '0.3.5-test.954';
5523
+ const VERSION = '0.3.5-test.956';
5486
5524
 
5487
5525
  /**
5488
5526
  * Fame errors module - Fame protocol specific error classes
@@ -11533,6 +11571,7 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11533
11571
  ? Math.floor(config.inboxCapacity)
11534
11572
  : DEFAULT_INBOX_CAPACITY$7;
11535
11573
  this.inbox = new BoundedAsyncQueue(preferredCapacity);
11574
+ this.inboxCapacity = preferredCapacity;
11536
11575
  this.connectorId = BroadcastChannelConnector.generateConnectorId();
11537
11576
  this.channel = new BroadcastChannel(this.channelName);
11538
11577
  logger$10.debug('broadcast_channel_connector_created', {
@@ -11592,15 +11631,27 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11592
11631
  if (typeof this.inbox.tryEnqueue === 'function') {
11593
11632
  const accepted = this.inbox.tryEnqueue(payload);
11594
11633
  if (accepted) {
11634
+ this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
11635
+ source: 'listener',
11636
+ enqueue_strategy: 'try',
11637
+ payload_length: payload.byteLength,
11638
+ });
11595
11639
  return;
11596
11640
  }
11597
11641
  }
11598
11642
  this.inbox.enqueue(payload);
11643
+ this.logInboxSnapshot('broadcast_channel_inbox_enqueued', {
11644
+ source: 'listener',
11645
+ enqueue_strategy: 'enqueue',
11646
+ payload_length: payload.byteLength,
11647
+ });
11599
11648
  }
11600
11649
  catch (error) {
11601
11650
  if (error instanceof QueueFullError) {
11602
11651
  logger$10.warning('broadcast_channel_receive_queue_full', {
11603
11652
  channel: this.channelName,
11653
+ inbox_capacity: this.inboxCapacity,
11654
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
11604
11655
  });
11605
11656
  }
11606
11657
  else {
@@ -11611,8 +11662,10 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11611
11662
  }
11612
11663
  }
11613
11664
  };
11614
- this.channel.addEventListener('message', this.onMsg);
11615
- this.listenerRegistered = true;
11665
+ if (!config.passive) {
11666
+ this.channel.addEventListener('message', this.onMsg);
11667
+ this.listenerRegistered = true;
11668
+ }
11616
11669
  // Setup visibility change monitoring
11617
11670
  this.visibilityChangeHandler = () => {
11618
11671
  const isHidden = document.hidden;
@@ -11684,15 +11737,25 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11684
11737
  if (typeof this.inbox.tryEnqueue === 'function') {
11685
11738
  const accepted = this.inbox.tryEnqueue(item);
11686
11739
  if (accepted) {
11740
+ this.logInboxSnapshot('broadcast_channel_push_enqueued', {
11741
+ enqueue_strategy: 'try',
11742
+ item_type: this._describeInboxItem(item),
11743
+ });
11687
11744
  return;
11688
11745
  }
11689
11746
  }
11690
11747
  this.inbox.enqueue(item);
11748
+ this.logInboxSnapshot('broadcast_channel_push_enqueued', {
11749
+ enqueue_strategy: 'enqueue',
11750
+ item_type: this._describeInboxItem(item),
11751
+ });
11691
11752
  }
11692
11753
  catch (error) {
11693
11754
  if (error instanceof QueueFullError) {
11694
11755
  logger$10.warning('broadcast_channel_push_queue_full', {
11695
11756
  channel: this.channelName,
11757
+ inbox_capacity: this.inboxCapacity,
11758
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
11696
11759
  });
11697
11760
  throw error;
11698
11761
  }
@@ -11715,7 +11778,11 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11715
11778
  });
11716
11779
  }
11717
11780
  async _transportReceive() {
11718
- return await this.inbox.dequeue();
11781
+ const item = await this.inbox.dequeue();
11782
+ this.logInboxSnapshot('broadcast_channel_inbox_dequeued', {
11783
+ item_type: this._describeInboxItem(item),
11784
+ });
11785
+ return item;
11719
11786
  }
11720
11787
  async _transportClose(code, reason) {
11721
11788
  logger$10.debug('broadcast_channel_transport_closing', {
@@ -11769,6 +11836,28 @@ let BroadcastChannelConnector$2 = class BroadcastChannelConnector extends BaseAs
11769
11836
  }
11770
11837
  return rawOrEnvelope;
11771
11838
  }
11839
+ _describeInboxItem(item) {
11840
+ if (item instanceof Uint8Array) {
11841
+ return 'bytes';
11842
+ }
11843
+ if (item.envelope) {
11844
+ return 'channel_message';
11845
+ }
11846
+ if (item.frame) {
11847
+ return 'envelope';
11848
+ }
11849
+ return 'unknown';
11850
+ }
11851
+ logInboxSnapshot(event, extra = {}) {
11852
+ logger$10.debug(event, {
11853
+ channel: this.channelName,
11854
+ connector_id: this.connectorId,
11855
+ connector_state: this.state,
11856
+ inbox_capacity: this.inboxCapacity,
11857
+ inbox_remaining_capacity: this.inbox.remainingCapacity,
11858
+ ...extra,
11859
+ });
11860
+ }
11772
11861
  _shouldSkipDuplicateAck(senderId, payload) {
11773
11862
  const dedupKey = this._extractAckDedupKey(payload);
11774
11863
  if (!dedupKey) {
@@ -32683,6 +32772,7 @@ class BroadcastChannelListener extends TransportListener {
32683
32772
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE$1,
32684
32773
  channelName: this._channelName,
32685
32774
  inboxCapacity: this._inboxCapacity,
32775
+ passive: true,
32686
32776
  };
32687
32777
  }
32688
32778
  try {
@@ -32743,6 +32833,7 @@ class BroadcastChannelListener extends TransportListener {
32743
32833
  type: BROADCAST_CHANNEL_CONNECTOR_TYPE$1,
32744
32834
  channelName: this._channelName,
32745
32835
  inboxCapacity: this._inboxCapacity,
32836
+ passive: true,
32746
32837
  };
32747
32838
  const channelCandidate = candidate.channelName ?? candidate['channel_name'];
32748
32839
  if (typeof channelCandidate === 'string' && channelCandidate.trim().length > 0) {
@@ -7,11 +7,13 @@ export interface BroadcastChannelConnectorConfig extends ConnectorConfig {
7
7
  channelName?: string;
8
8
  inboxCapacity?: number;
9
9
  initialWindow?: number;
10
+ passive?: boolean;
10
11
  }
11
12
  type BroadcastChannelInboxItem = Uint8Array | FameEnvelope | FameChannelMessage;
12
13
  export declare class BroadcastChannelConnector extends BaseAsyncConnector {
13
14
  private readonly channelName;
14
15
  private readonly inbox;
16
+ private readonly inboxCapacity;
15
17
  private listenerRegistered;
16
18
  private readonly connectorId;
17
19
  private readonly onMsg;
@@ -31,6 +33,8 @@ export declare class BroadcastChannelConnector extends BaseAsyncConnector {
31
33
  protected _transportReceive(): Promise<BroadcastChannelInboxItem>;
32
34
  protected _transportClose(code: number, reason: string): Promise<void>;
33
35
  private _normalizeInboxItem;
36
+ private _describeInboxItem;
37
+ private logInboxSnapshot;
34
38
  private _shouldSkipDuplicateAck;
35
39
  private _shouldSkipDuplicateAckFromInboxItem;
36
40
  private _checkDuplicateAck;
@@ -2,4 +2,4 @@
2
2
  * The package version, injected at build time.
3
3
  * @internal
4
4
  */
5
- export declare const VERSION = "0.3.5-test.954";
5
+ export declare const VERSION = "0.3.5-test.956";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naylence/runtime",
3
- "version": "0.3.5-test.954",
3
+ "version": "0.3.5-test.956",
4
4
  "type": "module",
5
5
  "description": "Naylence Runtime - Complete TypeScript runtime",
6
6
  "author": "Naylence Dev <naylencedev@gmail.com>",