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