@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/browser/index.cjs
CHANGED
|
@@ -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.
|
|
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.
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
1225
|
+
logger$1c.debug('shutdown_tasks_no_tasks_to_shutdown');
|
|
1226
1226
|
return;
|
|
1227
1227
|
}
|
|
1228
1228
|
this._suppressCompletionLogging = true;
|
|
1229
|
-
logger$
|
|
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$
|
|
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$
|
|
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$
|
|
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 (
|
|
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
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
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
|
-
|
|
9963
|
-
|
|
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
|
-
|
|
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) {
|