@cadenza.io/service 2.8.0 → 2.10.0
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/index.d.mts +63 -29
- package/dist/index.d.ts +63 -29
- package/dist/index.js +1410 -609
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1410 -609
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
Actor: () => import_core4.Actor,
|
|
33
34
|
DatabaseTask: () => DatabaseTask,
|
|
34
35
|
DebounceTask: () => import_core4.DebounceTask,
|
|
35
36
|
DeputyTask: () => DeputyTask,
|
|
@@ -2474,6 +2475,8 @@ var RestController = class _RestController {
|
|
|
2474
2475
|
constructor() {
|
|
2475
2476
|
this.fetchClientDiagnostics = /* @__PURE__ */ new Map();
|
|
2476
2477
|
this.diagnosticsErrorHistoryLimit = 100;
|
|
2478
|
+
this.diagnosticsMaxClientEntries = 500;
|
|
2479
|
+
this.destroyedDiagnosticsTtlMs = 15 * 6e4;
|
|
2477
2480
|
/**
|
|
2478
2481
|
* Fetches data from the given URL with a specified timeout. This function performs
|
|
2479
2482
|
* a fetch request with the ability to cancel the request if it exceeds the provided timeout duration.
|
|
@@ -3127,6 +3130,28 @@ var RestController = class _RestController {
|
|
|
3127
3130
|
if (!this._instance) this._instance = new _RestController();
|
|
3128
3131
|
return this._instance;
|
|
3129
3132
|
}
|
|
3133
|
+
pruneFetchClientDiagnostics(now = Date.now()) {
|
|
3134
|
+
for (const [fetchId, state] of this.fetchClientDiagnostics.entries()) {
|
|
3135
|
+
if (state.destroyed && now - state.updatedAt > this.destroyedDiagnosticsTtlMs) {
|
|
3136
|
+
this.fetchClientDiagnostics.delete(fetchId);
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
if (this.fetchClientDiagnostics.size <= this.diagnosticsMaxClientEntries) {
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
const entriesByEvictionPriority = Array.from(
|
|
3143
|
+
this.fetchClientDiagnostics.entries()
|
|
3144
|
+
).sort((left, right) => {
|
|
3145
|
+
if (left[1].destroyed !== right[1].destroyed) {
|
|
3146
|
+
return left[1].destroyed ? -1 : 1;
|
|
3147
|
+
}
|
|
3148
|
+
return left[1].updatedAt - right[1].updatedAt;
|
|
3149
|
+
});
|
|
3150
|
+
while (this.fetchClientDiagnostics.size > this.diagnosticsMaxClientEntries && entriesByEvictionPriority.length > 0) {
|
|
3151
|
+
const [fetchId] = entriesByEvictionPriority.shift();
|
|
3152
|
+
this.fetchClientDiagnostics.delete(fetchId);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3130
3155
|
resolveTransportDiagnosticsOptions(ctx) {
|
|
3131
3156
|
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
3132
3157
|
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
@@ -3139,6 +3164,8 @@ var RestController = class _RestController {
|
|
|
3139
3164
|
};
|
|
3140
3165
|
}
|
|
3141
3166
|
ensureFetchClientDiagnostics(fetchId, serviceName, url) {
|
|
3167
|
+
const now = Date.now();
|
|
3168
|
+
this.pruneFetchClientDiagnostics(now);
|
|
3142
3169
|
let state = this.fetchClientDiagnostics.get(fetchId);
|
|
3143
3170
|
if (!state) {
|
|
3144
3171
|
state = {
|
|
@@ -3158,13 +3185,14 @@ var RestController = class _RestController {
|
|
|
3158
3185
|
signalFailures: 0,
|
|
3159
3186
|
statusChecks: 0,
|
|
3160
3187
|
statusFailures: 0,
|
|
3161
|
-
updatedAt:
|
|
3188
|
+
updatedAt: now
|
|
3162
3189
|
};
|
|
3163
3190
|
this.fetchClientDiagnostics.set(fetchId, state);
|
|
3164
3191
|
} else {
|
|
3165
3192
|
state.serviceName = serviceName;
|
|
3166
3193
|
state.url = url;
|
|
3167
3194
|
}
|
|
3195
|
+
this.pruneFetchClientDiagnostics(now);
|
|
3168
3196
|
return state;
|
|
3169
3197
|
}
|
|
3170
3198
|
getErrorMessage(error) {
|
|
@@ -3196,6 +3224,7 @@ var RestController = class _RestController {
|
|
|
3196
3224
|
}
|
|
3197
3225
|
}
|
|
3198
3226
|
collectFetchTransportDiagnostics(ctx) {
|
|
3227
|
+
this.pruneFetchClientDiagnostics();
|
|
3199
3228
|
const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
|
|
3200
3229
|
const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
|
|
3201
3230
|
const states = Array.from(this.fetchClientDiagnostics.values()).sort(
|
|
@@ -3312,55 +3341,261 @@ var waitForSocketConnection = async (socket, timeoutMs, createError) => {
|
|
|
3312
3341
|
|
|
3313
3342
|
// src/network/SocketController.ts
|
|
3314
3343
|
var SocketController = class _SocketController {
|
|
3315
|
-
/**
|
|
3316
|
-
* Constructs the `SocketServer`, setting up a WebSocket server with specific configurations,
|
|
3317
|
-
* including connection state recovery, rate limiting, CORS handling, and custom event handling.
|
|
3318
|
-
* This class sets up the communication infrastructure for scalable, resilient, and secure WebSocket-based interactions
|
|
3319
|
-
* using metadata-driven task execution with `Cadenza`.
|
|
3320
|
-
*
|
|
3321
|
-
* It provides support for:
|
|
3322
|
-
* - Origin-based access control for connections.
|
|
3323
|
-
* - Optional payload sanitization.
|
|
3324
|
-
* - Configurable rate limiting and behavior on limit breaches (soft/hard disconnects).
|
|
3325
|
-
* - Event handlers for connection, handshake, delegation, signaling, status checks, and disconnection.
|
|
3326
|
-
*
|
|
3327
|
-
* The server can handle both internal and external interactions depending on the provided configurations,
|
|
3328
|
-
* and integrates directly with Cadenza's task workflow engine.
|
|
3329
|
-
*
|
|
3330
|
-
* Initializes the `SocketServer` to be ready for WebSocket communication.
|
|
3331
|
-
*/
|
|
3332
3344
|
constructor() {
|
|
3333
|
-
this.socketClientDiagnostics = /* @__PURE__ */ new Map();
|
|
3334
3345
|
this.diagnosticsErrorHistoryLimit = 100;
|
|
3346
|
+
this.diagnosticsMaxClientEntries = 500;
|
|
3347
|
+
this.destroyedDiagnosticsTtlMs = 15 * 6e4;
|
|
3348
|
+
this.socketServerDefaultKey = "socket-server-default";
|
|
3349
|
+
this.socketServerInitialSessionState = {
|
|
3350
|
+
serverKey: this.socketServerDefaultKey,
|
|
3351
|
+
useSocket: false,
|
|
3352
|
+
status: "inactive",
|
|
3353
|
+
securityProfile: "medium",
|
|
3354
|
+
networkType: "internal",
|
|
3355
|
+
connectionCount: 0,
|
|
3356
|
+
lastStartedAt: null,
|
|
3357
|
+
lastConnectedAt: null,
|
|
3358
|
+
lastDisconnectedAt: null,
|
|
3359
|
+
lastShutdownAt: null,
|
|
3360
|
+
updatedAt: 0
|
|
3361
|
+
};
|
|
3362
|
+
this.socketClientInitialSessionState = {
|
|
3363
|
+
fetchId: "",
|
|
3364
|
+
serviceInstanceId: "",
|
|
3365
|
+
communicationTypes: [],
|
|
3366
|
+
serviceName: "",
|
|
3367
|
+
serviceAddress: "",
|
|
3368
|
+
servicePort: 0,
|
|
3369
|
+
protocol: "http",
|
|
3370
|
+
url: "",
|
|
3371
|
+
socketId: null,
|
|
3372
|
+
connected: false,
|
|
3373
|
+
handshake: false,
|
|
3374
|
+
pendingDelegations: 0,
|
|
3375
|
+
pendingTimers: 0,
|
|
3376
|
+
reconnectAttempts: 0,
|
|
3377
|
+
connectErrors: 0,
|
|
3378
|
+
reconnectErrors: 0,
|
|
3379
|
+
socketErrors: 0,
|
|
3380
|
+
errorCount: 0,
|
|
3381
|
+
destroyed: false,
|
|
3382
|
+
lastHandshakeAt: null,
|
|
3383
|
+
lastHandshakeError: null,
|
|
3384
|
+
lastDisconnectAt: null,
|
|
3385
|
+
updatedAt: 0
|
|
3386
|
+
};
|
|
3387
|
+
this.socketServerActor = CadenzaService.createActor(
|
|
3388
|
+
{
|
|
3389
|
+
name: "SocketServerActor",
|
|
3390
|
+
description: "Holds durable socket server session state and runtime socket server handle",
|
|
3391
|
+
defaultKey: this.socketServerDefaultKey,
|
|
3392
|
+
keyResolver: (input) => this.resolveSocketServerKey(input),
|
|
3393
|
+
loadPolicy: "lazy",
|
|
3394
|
+
writeContract: "overwrite",
|
|
3395
|
+
initState: this.socketServerInitialSessionState
|
|
3396
|
+
},
|
|
3397
|
+
{ isMeta: true }
|
|
3398
|
+
);
|
|
3399
|
+
this.socketClientActor = CadenzaService.createActor(
|
|
3400
|
+
{
|
|
3401
|
+
name: "SocketClientActor",
|
|
3402
|
+
description: "Holds durable socket client session state and runtime socket connection handles",
|
|
3403
|
+
defaultKey: "socket-client-default",
|
|
3404
|
+
keyResolver: (input) => this.resolveSocketClientFetchId(input),
|
|
3405
|
+
loadPolicy: "lazy",
|
|
3406
|
+
writeContract: "overwrite",
|
|
3407
|
+
initState: this.socketClientInitialSessionState
|
|
3408
|
+
},
|
|
3409
|
+
{ isMeta: true }
|
|
3410
|
+
);
|
|
3411
|
+
this.socketClientDiagnosticsActor = CadenzaService.createActor(
|
|
3412
|
+
{
|
|
3413
|
+
name: "SocketClientDiagnosticsActor",
|
|
3414
|
+
description: "Tracks socket client diagnostics snapshots per fetchId for transport observability",
|
|
3415
|
+
defaultKey: "socket-client-diagnostics",
|
|
3416
|
+
loadPolicy: "eager",
|
|
3417
|
+
writeContract: "overwrite",
|
|
3418
|
+
initState: {
|
|
3419
|
+
entries: {}
|
|
3420
|
+
}
|
|
3421
|
+
},
|
|
3422
|
+
{ isMeta: true }
|
|
3423
|
+
);
|
|
3424
|
+
this.registerDiagnosticsTasks();
|
|
3425
|
+
this.registerSocketServerTasks();
|
|
3426
|
+
this.registerSocketClientTasks();
|
|
3335
3427
|
CadenzaService.createMetaTask(
|
|
3336
3428
|
"Collect socket transport diagnostics",
|
|
3337
|
-
|
|
3429
|
+
this.socketClientDiagnosticsActor.task(
|
|
3430
|
+
({ state, input }) => this.collectSocketTransportDiagnostics(input, state.entries),
|
|
3431
|
+
{ mode: "read" }
|
|
3432
|
+
),
|
|
3338
3433
|
"Responds to distributed transport diagnostics inquiries with socket client data."
|
|
3339
3434
|
).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3435
|
+
}
|
|
3436
|
+
static get instance() {
|
|
3437
|
+
if (!this._instance) this._instance = new _SocketController();
|
|
3438
|
+
return this._instance;
|
|
3439
|
+
}
|
|
3440
|
+
registerDiagnosticsTasks() {
|
|
3441
|
+
CadenzaService.createThrottledMetaTask(
|
|
3442
|
+
"SocketClientDiagnosticsActor.Upsert",
|
|
3443
|
+
this.socketClientDiagnosticsActor.task(
|
|
3444
|
+
({ state, input, setState }) => {
|
|
3445
|
+
const fetchId = String(input.fetchId ?? "").trim();
|
|
3446
|
+
if (!fetchId) {
|
|
3345
3447
|
return;
|
|
3346
3448
|
}
|
|
3347
|
-
const
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3449
|
+
const now = Date.now();
|
|
3450
|
+
const entries = { ...state.entries };
|
|
3451
|
+
const existing = entries[fetchId];
|
|
3452
|
+
const base = existing ? {
|
|
3453
|
+
...existing,
|
|
3454
|
+
errorHistory: [...existing.errorHistory]
|
|
3455
|
+
} : {
|
|
3456
|
+
fetchId,
|
|
3457
|
+
serviceName: String(input.serviceName ?? ""),
|
|
3458
|
+
url: String(input.url ?? ""),
|
|
3459
|
+
socketId: null,
|
|
3460
|
+
connected: false,
|
|
3461
|
+
handshake: false,
|
|
3462
|
+
reconnectAttempts: 0,
|
|
3463
|
+
connectErrors: 0,
|
|
3464
|
+
reconnectErrors: 0,
|
|
3465
|
+
socketErrors: 0,
|
|
3466
|
+
pendingDelegations: 0,
|
|
3467
|
+
pendingTimers: 0,
|
|
3468
|
+
destroyed: false,
|
|
3469
|
+
lastHandshakeAt: null,
|
|
3470
|
+
lastHandshakeError: null,
|
|
3471
|
+
lastDisconnectAt: null,
|
|
3472
|
+
lastError: null,
|
|
3473
|
+
lastErrorAt: 0,
|
|
3474
|
+
errorHistory: [],
|
|
3475
|
+
updatedAt: now
|
|
3476
|
+
};
|
|
3477
|
+
if (input.serviceName !== void 0) {
|
|
3478
|
+
base.serviceName = String(input.serviceName);
|
|
3479
|
+
}
|
|
3480
|
+
if (input.url !== void 0) {
|
|
3481
|
+
base.url = String(input.url);
|
|
3482
|
+
}
|
|
3483
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3484
|
+
Object.assign(base, patch);
|
|
3485
|
+
base.fetchId = fetchId;
|
|
3486
|
+
base.updatedAt = now;
|
|
3487
|
+
const errorMessage = input.error !== void 0 ? this.getErrorMessage(input.error) : void 0;
|
|
3488
|
+
if (errorMessage) {
|
|
3489
|
+
base.lastError = errorMessage;
|
|
3490
|
+
base.lastErrorAt = now;
|
|
3491
|
+
base.errorHistory.push({
|
|
3492
|
+
at: new Date(now).toISOString(),
|
|
3493
|
+
message: errorMessage
|
|
3494
|
+
});
|
|
3495
|
+
if (base.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
|
|
3496
|
+
base.errorHistory.splice(
|
|
3497
|
+
0,
|
|
3498
|
+
base.errorHistory.length - this.diagnosticsErrorHistoryLimit
|
|
3499
|
+
);
|
|
3357
3500
|
}
|
|
3501
|
+
}
|
|
3502
|
+
entries[fetchId] = base;
|
|
3503
|
+
this.pruneDiagnosticsEntries(entries, now);
|
|
3504
|
+
setState({ entries });
|
|
3505
|
+
},
|
|
3506
|
+
{ mode: "write" }
|
|
3507
|
+
),
|
|
3508
|
+
(context) => String(context?.fetchId ?? "default"),
|
|
3509
|
+
"Upserts socket client diagnostics in actor state."
|
|
3510
|
+
).doOn("meta.socket_client.diagnostics_upsert_requested");
|
|
3511
|
+
}
|
|
3512
|
+
registerSocketServerTasks() {
|
|
3513
|
+
CadenzaService.createThrottledMetaTask(
|
|
3514
|
+
"SocketServerActor.PatchSession",
|
|
3515
|
+
this.socketServerActor.task(
|
|
3516
|
+
({ state, input, setState }) => {
|
|
3517
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3518
|
+
setState({
|
|
3519
|
+
...state,
|
|
3520
|
+
...patch,
|
|
3521
|
+
updatedAt: Date.now()
|
|
3522
|
+
});
|
|
3523
|
+
},
|
|
3524
|
+
{ mode: "write" }
|
|
3525
|
+
),
|
|
3526
|
+
(context) => String(context?.serverKey ?? this.socketServerDefaultKey),
|
|
3527
|
+
"Applies partial durable session updates for socket server actor."
|
|
3528
|
+
).doOn("meta.socket_server.session_patch_requested");
|
|
3529
|
+
CadenzaService.createMetaTask(
|
|
3530
|
+
"SocketServerActor.ClearRuntime",
|
|
3531
|
+
this.socketServerActor.task(
|
|
3532
|
+
({ setRuntimeState }) => {
|
|
3533
|
+
setRuntimeState(null);
|
|
3534
|
+
},
|
|
3535
|
+
{ mode: "write" }
|
|
3536
|
+
),
|
|
3537
|
+
"Clears socket server runtime handle after shutdown."
|
|
3538
|
+
).doOn("meta.socket_server.runtime_clear_requested");
|
|
3539
|
+
const setupSocketServerTask = CadenzaService.createMetaTask(
|
|
3540
|
+
"Setup SocketServer",
|
|
3541
|
+
this.socketServerActor.task(
|
|
3542
|
+
({ state, runtimeState, input, actor, setState, setRuntimeState, emit }) => {
|
|
3543
|
+
const serverKey = this.resolveSocketServerKey(input) ?? actor.key ?? this.socketServerDefaultKey;
|
|
3544
|
+
const shouldUseSocket = Boolean(input.__useSocket);
|
|
3545
|
+
if (!shouldUseSocket) {
|
|
3546
|
+
this.destroySocketServerRuntimeHandle(runtimeState);
|
|
3547
|
+
setRuntimeState(null);
|
|
3548
|
+
setState({
|
|
3549
|
+
...state,
|
|
3550
|
+
serverKey,
|
|
3551
|
+
useSocket: false,
|
|
3552
|
+
status: "inactive",
|
|
3553
|
+
connectionCount: 0,
|
|
3554
|
+
lastShutdownAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3555
|
+
updatedAt: Date.now()
|
|
3556
|
+
});
|
|
3557
|
+
return;
|
|
3558
|
+
}
|
|
3559
|
+
let runtimeHandle = runtimeState;
|
|
3560
|
+
if (!runtimeHandle) {
|
|
3561
|
+
runtimeHandle = this.createSocketServerRuntimeHandleFromContext(input);
|
|
3562
|
+
setRuntimeState(runtimeHandle);
|
|
3563
|
+
}
|
|
3564
|
+
const profile = String(input.__securityProfile ?? state.securityProfile ?? "medium");
|
|
3565
|
+
const networkType = String(input.__networkType ?? state.networkType ?? "internal");
|
|
3566
|
+
const schedulePatch = (patch) => {
|
|
3567
|
+
CadenzaService.emit("meta.socket_server.session_patch_requested", {
|
|
3568
|
+
serverKey,
|
|
3569
|
+
patch
|
|
3570
|
+
});
|
|
3571
|
+
};
|
|
3572
|
+
if (runtimeHandle.initialized) {
|
|
3573
|
+
schedulePatch({
|
|
3574
|
+
status: "active",
|
|
3575
|
+
useSocket: true,
|
|
3576
|
+
securityProfile: profile,
|
|
3577
|
+
networkType,
|
|
3578
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3579
|
+
lastStartedAt: state.lastStartedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3580
|
+
});
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
const server = runtimeHandle.server;
|
|
3584
|
+
runtimeHandle.initialized = true;
|
|
3585
|
+
setState({
|
|
3586
|
+
...state,
|
|
3587
|
+
serverKey,
|
|
3588
|
+
useSocket: true,
|
|
3589
|
+
status: "active",
|
|
3590
|
+
securityProfile: profile,
|
|
3591
|
+
networkType,
|
|
3592
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3593
|
+
lastStartedAt: state.lastStartedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3594
|
+
updatedAt: Date.now()
|
|
3358
3595
|
});
|
|
3359
|
-
const profile = ctx.__securityProfile ?? "medium";
|
|
3360
3596
|
server.use((socket, next) => {
|
|
3361
3597
|
const origin = socket?.handshake?.headers?.origin;
|
|
3362
3598
|
const allowedOrigins = ["*"];
|
|
3363
|
-
const networkType = ctx.__networkType ?? "internal";
|
|
3364
3599
|
let effectiveOrigin = origin || "unknown";
|
|
3365
3600
|
if (networkType === "internal") effectiveOrigin = "internal";
|
|
3366
3601
|
if (profile !== "low" && !allowedOrigins.includes(effectiveOrigin) && !allowedOrigins.includes("*")) {
|
|
@@ -3371,7 +3606,9 @@ var SocketController = class _SocketController {
|
|
|
3371
3606
|
medium: { points: 1e4, duration: 10 },
|
|
3372
3607
|
high: { points: 1e3, duration: 60, blockDuration: 300 }
|
|
3373
3608
|
};
|
|
3374
|
-
const limiter = new import_rate_limiter_flexible2.RateLimiterMemory(
|
|
3609
|
+
const limiter = new import_rate_limiter_flexible2.RateLimiterMemory(
|
|
3610
|
+
limiterOptions[profile] ?? limiterOptions.medium
|
|
3611
|
+
);
|
|
3375
3612
|
const clientKey = socket?.handshake?.address || "unknown";
|
|
3376
3613
|
socket.use((packet, packetNext) => {
|
|
3377
3614
|
limiter.consume(clientKey).then(() => packetNext()).catch((rej) => {
|
|
@@ -3406,116 +3643,111 @@ var SocketController = class _SocketController {
|
|
|
3406
3643
|
});
|
|
3407
3644
|
next();
|
|
3408
3645
|
});
|
|
3409
|
-
if (!server) {
|
|
3410
|
-
CadenzaService.log("Socket setup error: No server", {}, "error");
|
|
3411
|
-
return { ...ctx, __error: "No server", errored: true };
|
|
3412
|
-
}
|
|
3413
3646
|
server.on("connection", (ws) => {
|
|
3647
|
+
runtimeHandle.connectedSocketIds.add(ws.id);
|
|
3648
|
+
schedulePatch({
|
|
3649
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3650
|
+
lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3651
|
+
status: "active"
|
|
3652
|
+
});
|
|
3414
3653
|
try {
|
|
3415
|
-
ws.on(
|
|
3416
|
-
"
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
}
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
if (ctx3.__routineExecId) {
|
|
3436
|
-
emit(
|
|
3437
|
-
`meta.socket_client.transmitted:${ctx3.__routineExecId}`,
|
|
3438
|
-
{}
|
|
3439
|
-
);
|
|
3440
|
-
}
|
|
3654
|
+
ws.on("handshake", (ctx, callback) => {
|
|
3655
|
+
CadenzaService.log("SocketServer: New connection", {
|
|
3656
|
+
...ctx,
|
|
3657
|
+
socketId: ws.id
|
|
3658
|
+
});
|
|
3659
|
+
callback({
|
|
3660
|
+
status: "success",
|
|
3661
|
+
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
3662
|
+
});
|
|
3663
|
+
if (ctx.isFrontend) {
|
|
3664
|
+
const fetchId = `browser:${ctx.serviceInstanceId}`;
|
|
3665
|
+
CadenzaService.createMetaTask(
|
|
3666
|
+
`Transmit signal to ${fetchId}`,
|
|
3667
|
+
(c, emitter) => {
|
|
3668
|
+
if (c.__signalName === void 0) {
|
|
3669
|
+
return;
|
|
3670
|
+
}
|
|
3671
|
+
ws.emit("signal", c);
|
|
3672
|
+
if (c.__routineExecId) {
|
|
3673
|
+
emitter(`meta.socket_client.transmitted:${c.__routineExecId}`, {});
|
|
3441
3674
|
}
|
|
3442
|
-
).doOn(
|
|
3443
|
-
`meta.service_registry.selected_instance_for_socket:${fetchId}`
|
|
3444
|
-
).attachSignal("meta.socket_client.transmitted");
|
|
3445
|
-
}
|
|
3446
|
-
CadenzaService.emit("meta.socket.handshake", ctx2);
|
|
3447
|
-
}
|
|
3448
|
-
);
|
|
3449
|
-
ws.on(
|
|
3450
|
-
"delegation",
|
|
3451
|
-
(ctx2, callback) => {
|
|
3452
|
-
const deputyExecId = ctx2.__metadata.__deputyExecId;
|
|
3453
|
-
CadenzaService.createEphemeralMetaTask(
|
|
3454
|
-
"Resolve delegation",
|
|
3455
|
-
(ctx3) => {
|
|
3456
|
-
callback(ctx3);
|
|
3457
|
-
},
|
|
3458
|
-
"Resolves a delegation request using the provided callback from the client (.emitWithAck())",
|
|
3459
|
-
{ register: false }
|
|
3460
|
-
).doOn(`meta.node.graph_completed:${deputyExecId}`).emits(`meta.socket.delegation_resolved:${deputyExecId}`);
|
|
3461
|
-
CadenzaService.createEphemeralMetaTask(
|
|
3462
|
-
"Delegation progress update",
|
|
3463
|
-
(ctx3) => {
|
|
3464
|
-
if (ctx3.__progress !== void 0)
|
|
3465
|
-
ws.emit("delegation_progress", ctx3);
|
|
3466
3675
|
},
|
|
3467
|
-
"
|
|
3468
|
-
|
|
3469
|
-
once: false,
|
|
3470
|
-
destroyCondition: (ctx3) => ctx3.data.progress === 1 || ctx3.data?.progress === void 0,
|
|
3471
|
-
register: false
|
|
3472
|
-
}
|
|
3473
|
-
).doOn(
|
|
3474
|
-
`meta.node.routine_execution_progress:${deputyExecId}`,
|
|
3475
|
-
`meta.node.graph_completed:${deputyExecId}`
|
|
3476
|
-
).emitsOnFail(`meta.socket.progress_failed:${deputyExecId}`);
|
|
3477
|
-
CadenzaService.emit("meta.socket.delegation_requested", {
|
|
3478
|
-
...ctx2,
|
|
3479
|
-
__name: ctx2.__remoteRoutineName
|
|
3480
|
-
});
|
|
3676
|
+
"Transmit frontend bound signal through active websocket."
|
|
3677
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
3481
3678
|
}
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3679
|
+
CadenzaService.emit("meta.socket.handshake", ctx);
|
|
3680
|
+
});
|
|
3681
|
+
ws.on("delegation", (ctx, callback) => {
|
|
3682
|
+
const deputyExecId = ctx.__metadata.__deputyExecId;
|
|
3683
|
+
CadenzaService.createEphemeralMetaTask(
|
|
3684
|
+
"Resolve delegation",
|
|
3685
|
+
(delegationCtx) => {
|
|
3686
|
+
callback(delegationCtx);
|
|
3687
|
+
},
|
|
3688
|
+
"Resolves a delegation request using client callback.",
|
|
3689
|
+
{ register: false }
|
|
3690
|
+
).doOn(`meta.node.graph_completed:${deputyExecId}`).emits(`meta.socket.delegation_resolved:${deputyExecId}`);
|
|
3691
|
+
CadenzaService.createEphemeralMetaTask(
|
|
3692
|
+
"Delegation progress update",
|
|
3693
|
+
(progressCtx) => {
|
|
3694
|
+
if (progressCtx.__progress !== void 0) {
|
|
3695
|
+
ws.emit("delegation_progress", progressCtx);
|
|
3696
|
+
}
|
|
3697
|
+
},
|
|
3698
|
+
"Updates delegation progress to client.",
|
|
3699
|
+
{
|
|
3700
|
+
once: false,
|
|
3701
|
+
destroyCondition: (progressCtx) => progressCtx.data.progress === 1 || progressCtx.data?.progress === void 0,
|
|
3702
|
+
register: false
|
|
3503
3703
|
}
|
|
3704
|
+
).doOn(
|
|
3705
|
+
`meta.node.routine_execution_progress:${deputyExecId}`,
|
|
3706
|
+
`meta.node.graph_completed:${deputyExecId}`
|
|
3707
|
+
).emitsOnFail(`meta.socket.progress_failed:${deputyExecId}`);
|
|
3708
|
+
CadenzaService.emit("meta.socket.delegation_requested", {
|
|
3709
|
+
...ctx,
|
|
3710
|
+
__name: ctx.__remoteRoutineName
|
|
3711
|
+
});
|
|
3712
|
+
});
|
|
3713
|
+
ws.on("signal", (ctx, callback) => {
|
|
3714
|
+
if (CadenzaService.signalBroker.listObservedSignals().includes(ctx.__signalName)) {
|
|
3715
|
+
callback({
|
|
3716
|
+
__status: "success",
|
|
3717
|
+
__signalName: ctx.__signalName
|
|
3718
|
+
});
|
|
3719
|
+
CadenzaService.emit(ctx.__signalName, ctx);
|
|
3720
|
+
} else {
|
|
3721
|
+
CadenzaService.log(
|
|
3722
|
+
`No such signal ${ctx.__signalName} on ${ctx.__serviceName}`,
|
|
3723
|
+
"warning"
|
|
3724
|
+
);
|
|
3725
|
+
callback({
|
|
3726
|
+
...ctx,
|
|
3727
|
+
__status: "error",
|
|
3728
|
+
__error: `No such signal: ${ctx.__signalName}`,
|
|
3729
|
+
errored: true
|
|
3730
|
+
});
|
|
3504
3731
|
}
|
|
3505
|
-
);
|
|
3732
|
+
});
|
|
3506
3733
|
ws.on(
|
|
3507
3734
|
"status_check",
|
|
3508
|
-
(
|
|
3735
|
+
(ctx, callback) => {
|
|
3509
3736
|
CadenzaService.createEphemeralMetaTask(
|
|
3510
3737
|
"Resolve status check",
|
|
3511
3738
|
callback,
|
|
3512
3739
|
"Resolves a status check request",
|
|
3513
3740
|
{ register: false }
|
|
3514
3741
|
).doAfter(CadenzaService.serviceRegistry.getStatusTask);
|
|
3515
|
-
CadenzaService.emit("meta.socket.status_check_requested",
|
|
3742
|
+
CadenzaService.emit("meta.socket.status_check_requested", ctx);
|
|
3516
3743
|
}
|
|
3517
3744
|
);
|
|
3518
3745
|
ws.on("disconnect", () => {
|
|
3746
|
+
runtimeHandle.connectedSocketIds.delete(ws.id);
|
|
3747
|
+
schedulePatch({
|
|
3748
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3749
|
+
lastDisconnectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3750
|
+
});
|
|
3519
3751
|
CadenzaService.log(
|
|
3520
3752
|
"Socket client disconnected",
|
|
3521
3753
|
{ socketId: ws.id },
|
|
@@ -3525,514 +3757,846 @@ var SocketController = class _SocketController {
|
|
|
3525
3757
|
__wsId: ws.id
|
|
3526
3758
|
});
|
|
3527
3759
|
});
|
|
3528
|
-
} catch (
|
|
3760
|
+
} catch (error) {
|
|
3529
3761
|
CadenzaService.log(
|
|
3530
3762
|
"SocketServer: Error in socket event",
|
|
3531
|
-
{ error
|
|
3763
|
+
{ error },
|
|
3532
3764
|
"error"
|
|
3533
3765
|
);
|
|
3534
3766
|
}
|
|
3535
3767
|
CadenzaService.emit("meta.socket.connected", { __wsId: ws.id });
|
|
3536
3768
|
});
|
|
3537
|
-
CadenzaService.createMetaTask(
|
|
3538
|
-
|
|
3539
|
-
(
|
|
3769
|
+
runtimeHandle.broadcastStatusTask = CadenzaService.createMetaTask(
|
|
3770
|
+
`Broadcast status ${serverKey}`,
|
|
3771
|
+
(ctx) => server.emit("status_update", ctx),
|
|
3540
3772
|
"Broadcasts the status of the server to all clients"
|
|
3541
3773
|
).doOn("meta.service.updated");
|
|
3542
|
-
CadenzaService.createMetaTask(
|
|
3543
|
-
|
|
3544
|
-
() =>
|
|
3774
|
+
runtimeHandle.shutdownTask = CadenzaService.createMetaTask(
|
|
3775
|
+
`Shutdown SocketServer ${serverKey}`,
|
|
3776
|
+
async () => {
|
|
3777
|
+
this.destroySocketServerRuntimeHandle(runtimeHandle);
|
|
3778
|
+
CadenzaService.emit("meta.socket_server.runtime_clear_requested", {
|
|
3779
|
+
serverKey
|
|
3780
|
+
});
|
|
3781
|
+
CadenzaService.emit("meta.socket_server.session_patch_requested", {
|
|
3782
|
+
serverKey,
|
|
3783
|
+
patch: {
|
|
3784
|
+
useSocket: false,
|
|
3785
|
+
status: "shutdown",
|
|
3786
|
+
connectionCount: 0,
|
|
3787
|
+
lastShutdownAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3788
|
+
}
|
|
3789
|
+
});
|
|
3790
|
+
},
|
|
3545
3791
|
"Shuts down the socket server"
|
|
3546
3792
|
).doOn("meta.socket_server_shutdown_requested").emits("meta.socket.shutdown");
|
|
3547
|
-
return
|
|
3548
|
-
}
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3793
|
+
return true;
|
|
3794
|
+
},
|
|
3795
|
+
{ mode: "write" }
|
|
3796
|
+
),
|
|
3797
|
+
"Initializes socket server runtime through actor state."
|
|
3798
|
+
);
|
|
3799
|
+
setupSocketServerTask.doOn("global.meta.rest.network_configured");
|
|
3800
|
+
}
|
|
3801
|
+
registerSocketClientTasks() {
|
|
3802
|
+
CadenzaService.createThrottledMetaTask(
|
|
3803
|
+
"SocketClientActor.ApplySessionOperation",
|
|
3804
|
+
this.socketClientActor.task(
|
|
3805
|
+
({ state, input, setState }) => {
|
|
3806
|
+
const operation = String(
|
|
3807
|
+
input.operation ?? "transmit"
|
|
3808
|
+
);
|
|
3809
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3810
|
+
let next = {
|
|
3811
|
+
...state,
|
|
3812
|
+
...patch,
|
|
3813
|
+
communicationTypes: patch.communicationTypes !== void 0 ? this.normalizeCommunicationTypes(patch.communicationTypes) : state.communicationTypes,
|
|
3814
|
+
updatedAt: Date.now()
|
|
3815
|
+
};
|
|
3816
|
+
if (input.serviceName !== void 0) {
|
|
3817
|
+
next.serviceName = String(input.serviceName);
|
|
3818
|
+
}
|
|
3819
|
+
if (input.serviceAddress !== void 0) {
|
|
3820
|
+
next.serviceAddress = String(input.serviceAddress);
|
|
3821
|
+
}
|
|
3822
|
+
if (input.serviceInstanceId !== void 0) {
|
|
3823
|
+
next.serviceInstanceId = String(input.serviceInstanceId);
|
|
3824
|
+
}
|
|
3825
|
+
if (input.protocol !== void 0) {
|
|
3826
|
+
next.protocol = String(input.protocol);
|
|
3827
|
+
}
|
|
3828
|
+
if (input.url !== void 0) {
|
|
3829
|
+
next.url = String(input.url);
|
|
3830
|
+
}
|
|
3831
|
+
if (input.servicePort !== void 0) {
|
|
3832
|
+
next.servicePort = Number(input.servicePort);
|
|
3833
|
+
}
|
|
3834
|
+
if (input.fetchId !== void 0) {
|
|
3835
|
+
next.fetchId = String(input.fetchId);
|
|
3836
|
+
}
|
|
3837
|
+
if (operation === "connect") {
|
|
3838
|
+
next.destroyed = false;
|
|
3839
|
+
} else if (operation === "handshake") {
|
|
3840
|
+
next.destroyed = false;
|
|
3841
|
+
next.connected = patch.connected ?? true;
|
|
3842
|
+
next.handshake = patch.handshake ?? true;
|
|
3843
|
+
} else if (operation === "shutdown") {
|
|
3844
|
+
next.connected = false;
|
|
3845
|
+
next.handshake = false;
|
|
3846
|
+
next.destroyed = true;
|
|
3847
|
+
next.pendingDelegations = 0;
|
|
3848
|
+
next.pendingTimers = 0;
|
|
3849
|
+
}
|
|
3850
|
+
setState(next);
|
|
3851
|
+
return next;
|
|
3852
|
+
},
|
|
3853
|
+
{ mode: "write" }
|
|
3854
|
+
),
|
|
3855
|
+
(context) => String(this.resolveSocketClientFetchId(context ?? {}) ?? "default"),
|
|
3856
|
+
"Applies socket client session operation patch in actor durable state."
|
|
3857
|
+
).doOn("meta.socket_client.session_operation_requested");
|
|
3858
|
+
CadenzaService.createMetaTask(
|
|
3859
|
+
"SocketClientActor.ClearRuntime",
|
|
3860
|
+
this.socketClientActor.task(
|
|
3861
|
+
({ setRuntimeState }) => {
|
|
3862
|
+
setRuntimeState(null);
|
|
3863
|
+
},
|
|
3864
|
+
{ mode: "write" }
|
|
3865
|
+
),
|
|
3866
|
+
"Clears socket client runtime handle."
|
|
3867
|
+
).doOn("meta.socket_client.runtime_clear_requested");
|
|
3552
3868
|
CadenzaService.createMetaTask(
|
|
3553
3869
|
"Connect to socket server",
|
|
3554
|
-
(
|
|
3555
|
-
|
|
3556
|
-
serviceInstanceId
|
|
3557
|
-
communicationTypes
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
fetchId,
|
|
3569
|
-
serviceName,
|
|
3570
|
-
URL
|
|
3571
|
-
);
|
|
3572
|
-
socketDiagnostics.destroyed = false;
|
|
3573
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3574
|
-
let handshake = false;
|
|
3575
|
-
let errorCount = 0;
|
|
3576
|
-
const ERROR_LIMIT = 5;
|
|
3577
|
-
if (CadenzaService.get(`Socket handshake with ${URL}`)) {
|
|
3578
|
-
console.error("Socket client already exists", URL);
|
|
3579
|
-
return;
|
|
3580
|
-
}
|
|
3581
|
-
const pendingDelegationIds = /* @__PURE__ */ new Set();
|
|
3582
|
-
const pendingTimers = /* @__PURE__ */ new Set();
|
|
3583
|
-
const syncPendingCounts = () => {
|
|
3584
|
-
socketDiagnostics.pendingDelegations = pendingDelegationIds.size;
|
|
3585
|
-
socketDiagnostics.pendingTimers = pendingTimers.size;
|
|
3586
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3587
|
-
};
|
|
3588
|
-
let handshakeTask = null;
|
|
3589
|
-
let emitWhenReady = null;
|
|
3590
|
-
let transmitTask = null;
|
|
3591
|
-
let delegateTask = null;
|
|
3592
|
-
let socket = null;
|
|
3593
|
-
socket = (0, import_socket2.io)(URL, {
|
|
3594
|
-
reconnection: true,
|
|
3595
|
-
reconnectionAttempts: 5,
|
|
3596
|
-
reconnectionDelay: 2e3,
|
|
3597
|
-
reconnectionDelayMax: 1e4,
|
|
3598
|
-
randomizationFactor: 0.5,
|
|
3599
|
-
transports: ["websocket"],
|
|
3600
|
-
autoConnect: false
|
|
3601
|
-
});
|
|
3602
|
-
emitWhenReady = (event, data, timeoutMs = 6e4, ack) => {
|
|
3603
|
-
return new Promise((resolve) => {
|
|
3604
|
-
const resolveWithError = (errorMessage, fallbackError) => {
|
|
3605
|
-
resolve({
|
|
3606
|
-
...data,
|
|
3607
|
-
errored: true,
|
|
3608
|
-
__error: errorMessage,
|
|
3609
|
-
error: fallbackError instanceof Error ? fallbackError.message : errorMessage,
|
|
3610
|
-
socketId: socket?.id,
|
|
3870
|
+
this.socketClientActor.task(
|
|
3871
|
+
({ state, runtimeState, input, setState, setRuntimeState, emit }) => {
|
|
3872
|
+
const serviceInstanceId = String(input.serviceInstanceId ?? "");
|
|
3873
|
+
const communicationTypes = this.normalizeCommunicationTypes(
|
|
3874
|
+
input.communicationTypes
|
|
3875
|
+
);
|
|
3876
|
+
const serviceName = String(input.serviceName ?? "");
|
|
3877
|
+
const serviceAddress = String(input.serviceAddress ?? "");
|
|
3878
|
+
const protocol = String(input.protocol ?? "http");
|
|
3879
|
+
const normalizedPort = this.resolveServicePort(protocol, input.servicePort);
|
|
3880
|
+
if (!serviceAddress || !normalizedPort) {
|
|
3881
|
+
CadenzaService.log(
|
|
3882
|
+
"Socket client setup skipped due to missing address/port",
|
|
3883
|
+
{
|
|
3611
3884
|
serviceName,
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3885
|
+
serviceAddress,
|
|
3886
|
+
servicePort: input.servicePort,
|
|
3887
|
+
protocol
|
|
3888
|
+
},
|
|
3889
|
+
"warning"
|
|
3890
|
+
);
|
|
3891
|
+
return false;
|
|
3892
|
+
}
|
|
3893
|
+
const socketProtocol = protocol === "https" ? "wss" : "ws";
|
|
3894
|
+
const url = `${socketProtocol}://${serviceAddress}:${normalizedPort}`;
|
|
3895
|
+
const fetchId = `${serviceAddress}_${normalizedPort}`;
|
|
3896
|
+
const applySessionOperation = (operation, patch = {}) => {
|
|
3897
|
+
CadenzaService.emit("meta.socket_client.session_operation_requested", {
|
|
3898
|
+
fetchId,
|
|
3899
|
+
operation,
|
|
3900
|
+
patch,
|
|
3901
|
+
serviceInstanceId,
|
|
3902
|
+
communicationTypes,
|
|
3903
|
+
serviceName,
|
|
3904
|
+
serviceAddress,
|
|
3905
|
+
servicePort: normalizedPort,
|
|
3906
|
+
protocol,
|
|
3907
|
+
url
|
|
3908
|
+
});
|
|
3909
|
+
};
|
|
3910
|
+
const upsertDiagnostics = (patch, error) => {
|
|
3911
|
+
CadenzaService.emit("meta.socket_client.diagnostics_upsert_requested", {
|
|
3912
|
+
fetchId,
|
|
3913
|
+
serviceName,
|
|
3914
|
+
url,
|
|
3915
|
+
patch,
|
|
3916
|
+
error
|
|
3917
|
+
});
|
|
3918
|
+
};
|
|
3919
|
+
setState({
|
|
3920
|
+
...state,
|
|
3921
|
+
fetchId,
|
|
3922
|
+
serviceInstanceId,
|
|
3923
|
+
communicationTypes,
|
|
3924
|
+
serviceName,
|
|
3925
|
+
serviceAddress,
|
|
3926
|
+
servicePort: normalizedPort,
|
|
3927
|
+
protocol,
|
|
3928
|
+
url,
|
|
3929
|
+
destroyed: false,
|
|
3930
|
+
updatedAt: Date.now()
|
|
3931
|
+
});
|
|
3932
|
+
let runtimeHandle = runtimeState;
|
|
3933
|
+
if (!runtimeHandle || runtimeHandle.url !== url) {
|
|
3934
|
+
this.destroySocketClientRuntimeHandle(runtimeHandle);
|
|
3935
|
+
runtimeHandle = this.createSocketClientRuntimeHandle(url);
|
|
3936
|
+
setRuntimeState(runtimeHandle);
|
|
3937
|
+
}
|
|
3938
|
+
upsertDiagnostics({
|
|
3939
|
+
destroyed: false,
|
|
3940
|
+
connected: false,
|
|
3941
|
+
handshake: false,
|
|
3942
|
+
socketId: runtimeHandle.socket.id ?? null
|
|
3943
|
+
});
|
|
3944
|
+
applySessionOperation("connect", {
|
|
3945
|
+
destroyed: false,
|
|
3946
|
+
connected: false,
|
|
3947
|
+
handshake: false,
|
|
3948
|
+
socketId: runtimeHandle.socket.id ?? null,
|
|
3949
|
+
pendingDelegations: runtimeHandle.pendingDelegationIds.size,
|
|
3950
|
+
pendingTimers: runtimeHandle.pendingTimers.size,
|
|
3951
|
+
errorCount: runtimeHandle.errorCount
|
|
3952
|
+
});
|
|
3953
|
+
if (runtimeHandle.initialized) {
|
|
3954
|
+
return true;
|
|
3955
|
+
}
|
|
3956
|
+
runtimeHandle.initialized = true;
|
|
3957
|
+
runtimeHandle.handshake = false;
|
|
3958
|
+
runtimeHandle.errorCount = 0;
|
|
3959
|
+
const syncPendingCounts = () => {
|
|
3960
|
+
const pendingDelegations = runtimeHandle.pendingDelegationIds.size;
|
|
3961
|
+
const pendingTimers = runtimeHandle.pendingTimers.size;
|
|
3962
|
+
upsertDiagnostics({
|
|
3963
|
+
pendingDelegations,
|
|
3964
|
+
pendingTimers
|
|
3965
|
+
});
|
|
3966
|
+
applySessionOperation("delegate", {
|
|
3967
|
+
pendingDelegations,
|
|
3968
|
+
pendingTimers
|
|
3969
|
+
});
|
|
3970
|
+
};
|
|
3971
|
+
runtimeHandle.emitWhenReady = (event, data, timeoutMs = 6e4, ack) => {
|
|
3972
|
+
return new Promise((resolve) => {
|
|
3973
|
+
const parsedTimeout = Number(timeoutMs);
|
|
3974
|
+
const normalizedTimeoutMs = Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? Math.trunc(parsedTimeout) : 6e4;
|
|
3975
|
+
let timer = null;
|
|
3976
|
+
let settled = false;
|
|
3977
|
+
const clearPendingTimer = () => {
|
|
3978
|
+
if (!timer) {
|
|
3979
|
+
return;
|
|
3629
3980
|
}
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
)
|
|
3637
|
-
|
|
3638
|
-
|
|
3981
|
+
clearTimeout(timer);
|
|
3982
|
+
runtimeHandle.pendingTimers.delete(timer);
|
|
3983
|
+
syncPendingCounts();
|
|
3984
|
+
timer = null;
|
|
3985
|
+
};
|
|
3986
|
+
const settle = (response) => {
|
|
3987
|
+
if (settled) {
|
|
3988
|
+
return;
|
|
3989
|
+
}
|
|
3990
|
+
settled = true;
|
|
3991
|
+
clearPendingTimer();
|
|
3992
|
+
if (ack) ack(response);
|
|
3993
|
+
resolve(response);
|
|
3994
|
+
};
|
|
3995
|
+
const resolveWithError = (errorMessage, fallbackError) => {
|
|
3996
|
+
settle({
|
|
3997
|
+
...data,
|
|
3998
|
+
errored: true,
|
|
3999
|
+
__error: errorMessage,
|
|
4000
|
+
error: fallbackError instanceof Error ? fallbackError.message : errorMessage,
|
|
4001
|
+
socketId: runtimeHandle.socket.id,
|
|
3639
4002
|
serviceName,
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
4003
|
+
url
|
|
4004
|
+
});
|
|
4005
|
+
};
|
|
4006
|
+
const tryEmit = async () => {
|
|
4007
|
+
const waitResult = await waitForSocketConnection(
|
|
4008
|
+
runtimeHandle.socket,
|
|
4009
|
+
normalizedTimeoutMs + 10,
|
|
4010
|
+
(reason, error) => {
|
|
4011
|
+
if (reason === "connect_timeout") {
|
|
4012
|
+
return `Socket connect timed out before '${event}'`;
|
|
4013
|
+
}
|
|
4014
|
+
if (reason === "connect_error") {
|
|
4015
|
+
const errMessage = error instanceof Error ? error.message : String(error);
|
|
4016
|
+
return `Socket connect error before '${event}': ${errMessage}`;
|
|
4017
|
+
}
|
|
4018
|
+
return `Socket disconnected before '${event}'`;
|
|
3653
4019
|
}
|
|
4020
|
+
);
|
|
4021
|
+
if (!waitResult.ok) {
|
|
3654
4022
|
CadenzaService.log(
|
|
3655
|
-
|
|
3656
|
-
{
|
|
4023
|
+
waitResult.error,
|
|
4024
|
+
{
|
|
4025
|
+
socketId: runtimeHandle.socket.id,
|
|
4026
|
+
serviceName,
|
|
4027
|
+
url,
|
|
4028
|
+
event
|
|
4029
|
+
},
|
|
3657
4030
|
"error"
|
|
3658
4031
|
);
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
URL,
|
|
3663
|
-
`Socket event '${event}' timed out`
|
|
3664
|
-
);
|
|
3665
|
-
resolveWithError(`Socket event '${event}' timed out`);
|
|
3666
|
-
}, timeoutMs + 10);
|
|
3667
|
-
pendingTimers.add(timer);
|
|
3668
|
-
syncPendingCounts();
|
|
3669
|
-
}
|
|
3670
|
-
const connectedSocket = socket;
|
|
3671
|
-
if (!connectedSocket) {
|
|
3672
|
-
resolveWithError(
|
|
3673
|
-
`Socket unavailable before emitting '${event}'`
|
|
3674
|
-
);
|
|
3675
|
-
return;
|
|
3676
|
-
}
|
|
3677
|
-
connectedSocket.timeout(timeoutMs).emit(event, data, (err, response) => {
|
|
3678
|
-
if (timer) {
|
|
3679
|
-
clearTimeout(timer);
|
|
3680
|
-
pendingTimers.delete(timer);
|
|
3681
|
-
syncPendingCounts();
|
|
3682
|
-
timer = null;
|
|
4032
|
+
upsertDiagnostics({}, waitResult.error);
|
|
4033
|
+
resolveWithError(waitResult.error);
|
|
4034
|
+
return;
|
|
3683
4035
|
}
|
|
3684
|
-
|
|
4036
|
+
timer = setTimeout(() => {
|
|
4037
|
+
if (settled) {
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
4040
|
+
clearPendingTimer();
|
|
4041
|
+
const message = `Socket event '${event}' timed out`;
|
|
3685
4042
|
CadenzaService.log(
|
|
3686
|
-
|
|
4043
|
+
message,
|
|
4044
|
+
{ socketId: runtimeHandle.socket.id, serviceName, url },
|
|
4045
|
+
"error"
|
|
4046
|
+
);
|
|
4047
|
+
upsertDiagnostics(
|
|
3687
4048
|
{
|
|
3688
|
-
|
|
3689
|
-
error: err.message,
|
|
3690
|
-
socketId: socket?.id,
|
|
3691
|
-
serviceName
|
|
4049
|
+
lastHandshakeError: message
|
|
3692
4050
|
},
|
|
3693
|
-
|
|
4051
|
+
message
|
|
3694
4052
|
);
|
|
3695
|
-
|
|
3696
|
-
|
|
4053
|
+
applySessionOperation("transmit", {
|
|
4054
|
+
lastHandshakeError: message
|
|
4055
|
+
});
|
|
4056
|
+
resolveWithError(message);
|
|
4057
|
+
}, normalizedTimeoutMs + 10);
|
|
4058
|
+
runtimeHandle.pendingTimers.add(timer);
|
|
4059
|
+
syncPendingCounts();
|
|
4060
|
+
runtimeHandle.socket.timeout(normalizedTimeoutMs).emit(event, data, (err, response) => {
|
|
4061
|
+
if (err) {
|
|
4062
|
+
CadenzaService.log(
|
|
4063
|
+
"Socket timeout.",
|
|
4064
|
+
{
|
|
4065
|
+
event,
|
|
4066
|
+
error: err.message,
|
|
4067
|
+
socketId: runtimeHandle.socket.id,
|
|
4068
|
+
serviceName
|
|
4069
|
+
},
|
|
4070
|
+
"warning"
|
|
4071
|
+
);
|
|
4072
|
+
upsertDiagnostics(
|
|
4073
|
+
{
|
|
4074
|
+
lastHandshakeError: err.message
|
|
4075
|
+
},
|
|
4076
|
+
err
|
|
4077
|
+
);
|
|
4078
|
+
applySessionOperation("transmit", {
|
|
4079
|
+
lastHandshakeError: err.message
|
|
4080
|
+
});
|
|
4081
|
+
response = {
|
|
4082
|
+
__error: `Timeout error: ${err}`,
|
|
4083
|
+
errored: true,
|
|
4084
|
+
...data
|
|
4085
|
+
};
|
|
4086
|
+
}
|
|
4087
|
+
settle(response);
|
|
4088
|
+
});
|
|
4089
|
+
};
|
|
4090
|
+
void tryEmit().catch((error) => {
|
|
4091
|
+
CadenzaService.log(
|
|
4092
|
+
"Socket emit failed unexpectedly",
|
|
4093
|
+
{
|
|
4094
|
+
event,
|
|
4095
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4096
|
+
socketId: runtimeHandle.socket.id,
|
|
3697
4097
|
serviceName,
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
}
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
4098
|
+
url
|
|
4099
|
+
},
|
|
4100
|
+
"error"
|
|
4101
|
+
);
|
|
4102
|
+
const message = `Socket event '${event}' failed`;
|
|
4103
|
+
upsertDiagnostics(
|
|
4104
|
+
{
|
|
4105
|
+
lastHandshakeError: error instanceof Error ? error.message : String(error)
|
|
4106
|
+
},
|
|
4107
|
+
error
|
|
4108
|
+
);
|
|
4109
|
+
applySessionOperation("transmit", {
|
|
4110
|
+
lastHandshakeError: error instanceof Error ? error.message : String(error)
|
|
4111
|
+
});
|
|
4112
|
+
resolveWithError(message, error);
|
|
3710
4113
|
});
|
|
3711
|
-
};
|
|
3712
|
-
|
|
4114
|
+
});
|
|
4115
|
+
};
|
|
4116
|
+
const socket = runtimeHandle.socket;
|
|
4117
|
+
socket.on("connect", () => {
|
|
4118
|
+
if (runtimeHandle.handshake) return;
|
|
4119
|
+
upsertDiagnostics({
|
|
4120
|
+
connected: true,
|
|
4121
|
+
destroyed: false,
|
|
4122
|
+
socketId: socket.id ?? null
|
|
4123
|
+
});
|
|
4124
|
+
applySessionOperation("connect", {
|
|
4125
|
+
connected: true,
|
|
4126
|
+
destroyed: false,
|
|
4127
|
+
socketId: socket.id ?? null
|
|
4128
|
+
});
|
|
4129
|
+
CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, input);
|
|
3713
4130
|
});
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
socketDiagnostics.socketId = socket?.id ?? null;
|
|
3720
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3721
|
-
CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, ctx);
|
|
3722
|
-
});
|
|
3723
|
-
socket.on("delegation_progress", (ctx2) => {
|
|
3724
|
-
CadenzaService.emit(
|
|
3725
|
-
`meta.socket_client.delegation_progress:${ctx2.__metadata.__deputyExecId}`,
|
|
3726
|
-
ctx2
|
|
3727
|
-
);
|
|
3728
|
-
});
|
|
3729
|
-
socket.on("signal", (ctx2) => {
|
|
3730
|
-
if (CadenzaService.signalBroker.listObservedSignals().includes(ctx2.__signalName)) {
|
|
3731
|
-
CadenzaService.emit(ctx2.__signalName, ctx2);
|
|
3732
|
-
}
|
|
3733
|
-
});
|
|
3734
|
-
socket.on("status_update", (status) => {
|
|
3735
|
-
CadenzaService.emit("meta.socket_client.status_received", status);
|
|
3736
|
-
});
|
|
3737
|
-
socket.on("connect_error", (err) => {
|
|
3738
|
-
handshake = false;
|
|
3739
|
-
socketDiagnostics.connected = false;
|
|
3740
|
-
socketDiagnostics.handshake = false;
|
|
3741
|
-
socketDiagnostics.connectErrors++;
|
|
3742
|
-
socketDiagnostics.lastHandshakeError = err.message;
|
|
3743
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3744
|
-
this.recordSocketClientError(fetchId, serviceName, URL, err);
|
|
3745
|
-
CadenzaService.log(
|
|
3746
|
-
"Socket connect error",
|
|
3747
|
-
{ error: err.message, serviceName, socketId: socket?.id, URL },
|
|
3748
|
-
"error"
|
|
3749
|
-
);
|
|
3750
|
-
CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
|
|
3751
|
-
});
|
|
3752
|
-
socket.on("reconnect_attempt", (attempt) => {
|
|
3753
|
-
socketDiagnostics.reconnectAttempts = Math.max(
|
|
3754
|
-
socketDiagnostics.reconnectAttempts,
|
|
3755
|
-
attempt
|
|
3756
|
-
);
|
|
3757
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3758
|
-
CadenzaService.log(`Reconnect attempt: ${attempt}`);
|
|
3759
|
-
});
|
|
3760
|
-
socket.on("reconnect", (attempt) => {
|
|
3761
|
-
socketDiagnostics.connected = true;
|
|
3762
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3763
|
-
CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
|
|
3764
|
-
socketId: socket?.id,
|
|
3765
|
-
URL,
|
|
3766
|
-
serviceName
|
|
4131
|
+
socket.on("delegation_progress", (delegationCtx) => {
|
|
4132
|
+
CadenzaService.emit(
|
|
4133
|
+
`meta.socket_client.delegation_progress:${delegationCtx.__metadata.__deputyExecId}`,
|
|
4134
|
+
delegationCtx
|
|
4135
|
+
);
|
|
3767
4136
|
});
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
socketDiagnostics.handshake = false;
|
|
3773
|
-
socketDiagnostics.reconnectErrors++;
|
|
3774
|
-
socketDiagnostics.lastHandshakeError = err.message;
|
|
3775
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3776
|
-
this.recordSocketClientError(fetchId, serviceName, URL, err);
|
|
3777
|
-
CadenzaService.log(
|
|
3778
|
-
"Socket reconnect failed.",
|
|
3779
|
-
{ error: err.message, serviceName, URL, socketId: socket?.id },
|
|
3780
|
-
"warning"
|
|
3781
|
-
);
|
|
3782
|
-
});
|
|
3783
|
-
socket.on("error", (err) => {
|
|
3784
|
-
errorCount++;
|
|
3785
|
-
socketDiagnostics.socketErrors++;
|
|
3786
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3787
|
-
this.recordSocketClientError(fetchId, serviceName, URL, err);
|
|
3788
|
-
CadenzaService.log(
|
|
3789
|
-
"Socket error",
|
|
3790
|
-
{ error: err, socketId: socket?.id, URL, serviceName },
|
|
3791
|
-
"error"
|
|
3792
|
-
);
|
|
3793
|
-
CadenzaService.emit("meta.socket_client.error", err);
|
|
3794
|
-
});
|
|
3795
|
-
socket.on("disconnect", () => {
|
|
3796
|
-
const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3797
|
-
socketDiagnostics.connected = false;
|
|
3798
|
-
socketDiagnostics.handshake = false;
|
|
3799
|
-
socketDiagnostics.lastDisconnectAt = disconnectedAt;
|
|
3800
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3801
|
-
CadenzaService.log(
|
|
3802
|
-
"Socket disconnected.",
|
|
3803
|
-
{ URL, serviceName, socketId: socket?.id },
|
|
3804
|
-
"warning"
|
|
3805
|
-
);
|
|
3806
|
-
CadenzaService.emit(`meta.socket_client.disconnected:${fetchId}`, {
|
|
3807
|
-
serviceName,
|
|
3808
|
-
serviceAddress,
|
|
3809
|
-
servicePort
|
|
4137
|
+
socket.on("signal", (signalCtx) => {
|
|
4138
|
+
if (CadenzaService.signalBroker.listObservedSignals().includes(signalCtx.__signalName)) {
|
|
4139
|
+
CadenzaService.emit(signalCtx.__signalName, signalCtx);
|
|
4140
|
+
}
|
|
3810
4141
|
});
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
if (handshake) return;
|
|
3818
|
-
handshake = true;
|
|
3819
|
-
socketDiagnostics.handshake = true;
|
|
3820
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3821
|
-
await emitWhenReady?.(
|
|
3822
|
-
"handshake",
|
|
4142
|
+
socket.on("status_update", (status) => {
|
|
4143
|
+
CadenzaService.emit("meta.socket_client.status_received", status);
|
|
4144
|
+
});
|
|
4145
|
+
socket.on("connect_error", (err) => {
|
|
4146
|
+
runtimeHandle.handshake = false;
|
|
4147
|
+
upsertDiagnostics(
|
|
3823
4148
|
{
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
4149
|
+
connected: false,
|
|
4150
|
+
handshake: false,
|
|
4151
|
+
connectErrors: state.connectErrors + 1,
|
|
4152
|
+
lastHandshakeError: err.message
|
|
3828
4153
|
},
|
|
3829
|
-
|
|
3830
|
-
(result) => {
|
|
3831
|
-
if (result.status === "success") {
|
|
3832
|
-
socketDiagnostics.connected = true;
|
|
3833
|
-
socketDiagnostics.handshake = true;
|
|
3834
|
-
socketDiagnostics.lastHandshakeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3835
|
-
socketDiagnostics.lastHandshakeError = null;
|
|
3836
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3837
|
-
CadenzaService.log("Socket client connected", {
|
|
3838
|
-
result,
|
|
3839
|
-
serviceName,
|
|
3840
|
-
socketId: socket?.id,
|
|
3841
|
-
URL
|
|
3842
|
-
});
|
|
3843
|
-
} else {
|
|
3844
|
-
socketDiagnostics.connected = false;
|
|
3845
|
-
socketDiagnostics.handshake = false;
|
|
3846
|
-
socketDiagnostics.lastHandshakeError = result?.__error ?? result?.error ?? "Socket handshake failed";
|
|
3847
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3848
|
-
this.recordSocketClientError(
|
|
3849
|
-
fetchId,
|
|
3850
|
-
serviceName,
|
|
3851
|
-
URL,
|
|
3852
|
-
socketDiagnostics.lastHandshakeError
|
|
3853
|
-
);
|
|
3854
|
-
CadenzaService.log(
|
|
3855
|
-
"Socket handshake failed",
|
|
3856
|
-
{ result, serviceName, socketId: socket?.id, URL },
|
|
3857
|
-
"warning"
|
|
3858
|
-
);
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
4154
|
+
err
|
|
3861
4155
|
);
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
4156
|
+
applySessionOperation("connect", {
|
|
4157
|
+
connected: false,
|
|
4158
|
+
handshake: false,
|
|
4159
|
+
connectErrors: state.connectErrors + 1,
|
|
4160
|
+
lastHandshakeError: err.message
|
|
4161
|
+
});
|
|
4162
|
+
CadenzaService.log(
|
|
4163
|
+
"Socket connect error",
|
|
4164
|
+
{
|
|
4165
|
+
error: err.message,
|
|
4166
|
+
serviceName,
|
|
4167
|
+
socketId: socket.id,
|
|
4168
|
+
url
|
|
4169
|
+
},
|
|
4170
|
+
"error"
|
|
4171
|
+
);
|
|
4172
|
+
CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
|
|
4173
|
+
});
|
|
4174
|
+
socket.on("reconnect_attempt", (attempt) => {
|
|
4175
|
+
upsertDiagnostics({ reconnectAttempts: attempt });
|
|
4176
|
+
applySessionOperation("connect", {
|
|
4177
|
+
reconnectAttempts: attempt
|
|
4178
|
+
});
|
|
4179
|
+
CadenzaService.log(`Reconnect attempt: ${attempt}`);
|
|
4180
|
+
});
|
|
4181
|
+
socket.on("reconnect", (attempt) => {
|
|
4182
|
+
upsertDiagnostics({ connected: true });
|
|
4183
|
+
applySessionOperation("connect", {
|
|
4184
|
+
connected: true
|
|
4185
|
+
});
|
|
4186
|
+
CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
|
|
4187
|
+
socketId: socket.id,
|
|
4188
|
+
url,
|
|
4189
|
+
serviceName
|
|
4190
|
+
});
|
|
4191
|
+
});
|
|
4192
|
+
socket.on("reconnect_error", (err) => {
|
|
4193
|
+
runtimeHandle.handshake = false;
|
|
4194
|
+
upsertDiagnostics(
|
|
4195
|
+
{
|
|
4196
|
+
connected: false,
|
|
4197
|
+
handshake: false,
|
|
4198
|
+
reconnectErrors: state.reconnectErrors + 1,
|
|
4199
|
+
lastHandshakeError: err.message
|
|
4200
|
+
},
|
|
4201
|
+
err
|
|
4202
|
+
);
|
|
4203
|
+
applySessionOperation("connect", {
|
|
4204
|
+
connected: false,
|
|
4205
|
+
handshake: false,
|
|
4206
|
+
reconnectErrors: state.reconnectErrors + 1,
|
|
4207
|
+
lastHandshakeError: err.message
|
|
4208
|
+
});
|
|
4209
|
+
CadenzaService.log(
|
|
4210
|
+
"Socket reconnect failed.",
|
|
4211
|
+
{ error: err.message, serviceName, url, socketId: socket.id },
|
|
4212
|
+
"warning"
|
|
4213
|
+
);
|
|
4214
|
+
});
|
|
4215
|
+
socket.on("error", (err) => {
|
|
4216
|
+
runtimeHandle.errorCount += 1;
|
|
4217
|
+
upsertDiagnostics(
|
|
4218
|
+
{
|
|
4219
|
+
socketErrors: state.socketErrors + 1,
|
|
4220
|
+
lastHandshakeError: this.getErrorMessage(err)
|
|
4221
|
+
},
|
|
4222
|
+
err
|
|
4223
|
+
);
|
|
4224
|
+
applySessionOperation("transmit", {
|
|
4225
|
+
socketErrors: state.socketErrors + 1,
|
|
4226
|
+
errorCount: runtimeHandle.errorCount,
|
|
4227
|
+
lastHandshakeError: this.getErrorMessage(err)
|
|
4228
|
+
});
|
|
4229
|
+
CadenzaService.log(
|
|
4230
|
+
"Socket error",
|
|
4231
|
+
{ error: err, socketId: socket.id, url, serviceName },
|
|
4232
|
+
"error"
|
|
4233
|
+
);
|
|
4234
|
+
CadenzaService.emit("meta.socket_client.error", err);
|
|
4235
|
+
});
|
|
4236
|
+
socket.on("disconnect", () => {
|
|
4237
|
+
const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4238
|
+
upsertDiagnostics({
|
|
4239
|
+
connected: false,
|
|
4240
|
+
handshake: false,
|
|
4241
|
+
lastDisconnectAt: disconnectedAt
|
|
4242
|
+
});
|
|
4243
|
+
applySessionOperation("connect", {
|
|
4244
|
+
connected: false,
|
|
4245
|
+
handshake: false,
|
|
4246
|
+
lastDisconnectAt: disconnectedAt
|
|
4247
|
+
});
|
|
4248
|
+
CadenzaService.log(
|
|
4249
|
+
"Socket disconnected.",
|
|
4250
|
+
{ url, serviceName, socketId: socket.id },
|
|
4251
|
+
"warning"
|
|
4252
|
+
);
|
|
4253
|
+
CadenzaService.emit(`meta.socket_client.disconnected:${fetchId}`, {
|
|
4254
|
+
serviceName,
|
|
4255
|
+
serviceAddress,
|
|
4256
|
+
servicePort: normalizedPort
|
|
4257
|
+
});
|
|
4258
|
+
runtimeHandle.handshake = false;
|
|
4259
|
+
});
|
|
4260
|
+
socket.connect();
|
|
4261
|
+
runtimeHandle.handshakeTask = CadenzaService.createMetaTask(
|
|
4262
|
+
`Socket handshake with ${url}`,
|
|
4263
|
+
async (_ctx, emitter) => {
|
|
4264
|
+
if (runtimeHandle.handshake) return;
|
|
4265
|
+
runtimeHandle.handshake = true;
|
|
4266
|
+
upsertDiagnostics({
|
|
4267
|
+
handshake: true
|
|
4268
|
+
});
|
|
4269
|
+
applySessionOperation("handshake", {
|
|
4270
|
+
handshake: true
|
|
4271
|
+
});
|
|
4272
|
+
await runtimeHandle.emitWhenReady?.(
|
|
4273
|
+
"handshake",
|
|
4274
|
+
{
|
|
4275
|
+
serviceInstanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
4276
|
+
serviceName: CadenzaService.serviceRegistry.serviceName,
|
|
4277
|
+
isFrontend: isBrowser,
|
|
4278
|
+
__status: "success"
|
|
4279
|
+
},
|
|
4280
|
+
1e4,
|
|
4281
|
+
(result) => {
|
|
4282
|
+
if (result.status === "success") {
|
|
4283
|
+
const handshakeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4284
|
+
upsertDiagnostics({
|
|
4285
|
+
connected: true,
|
|
4286
|
+
handshake: true,
|
|
4287
|
+
lastHandshakeAt: handshakeAt,
|
|
4288
|
+
lastHandshakeError: null,
|
|
4289
|
+
socketId: socket.id ?? null
|
|
4290
|
+
});
|
|
4291
|
+
applySessionOperation("handshake", {
|
|
4292
|
+
connected: true,
|
|
4293
|
+
handshake: true,
|
|
4294
|
+
lastHandshakeAt: handshakeAt,
|
|
4295
|
+
lastHandshakeError: null,
|
|
4296
|
+
socketId: socket.id ?? null
|
|
4297
|
+
});
|
|
4298
|
+
CadenzaService.log("Socket client connected", {
|
|
4299
|
+
result,
|
|
3898
4300
|
serviceName,
|
|
3899
|
-
|
|
3900
|
-
|
|
4301
|
+
socketId: socket.id,
|
|
4302
|
+
url
|
|
4303
|
+
});
|
|
4304
|
+
} else {
|
|
4305
|
+
const errorMessage = result?.__error ?? result?.error ?? "Socket handshake failed";
|
|
4306
|
+
upsertDiagnostics(
|
|
4307
|
+
{
|
|
4308
|
+
connected: false,
|
|
4309
|
+
handshake: false,
|
|
4310
|
+
lastHandshakeError: errorMessage
|
|
4311
|
+
},
|
|
4312
|
+
errorMessage
|
|
4313
|
+
);
|
|
4314
|
+
applySessionOperation("handshake", {
|
|
4315
|
+
connected: false,
|
|
4316
|
+
handshake: false,
|
|
4317
|
+
lastHandshakeError: errorMessage
|
|
4318
|
+
});
|
|
4319
|
+
CadenzaService.log(
|
|
4320
|
+
"Socket handshake failed",
|
|
4321
|
+
{ result, serviceName, socketId: socket.id, url },
|
|
4322
|
+
"warning"
|
|
3901
4323
|
);
|
|
3902
4324
|
}
|
|
3903
|
-
|
|
4325
|
+
void emitter;
|
|
3904
4326
|
}
|
|
3905
4327
|
);
|
|
3906
|
-
}
|
|
3907
|
-
|
|
3908
|
-
`
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
4328
|
+
},
|
|
4329
|
+
"Handshakes with socket server"
|
|
4330
|
+
).doOn(`meta.socket_client.connected:${fetchId}`);
|
|
4331
|
+
runtimeHandle.delegateTask = CadenzaService.createMetaTask(
|
|
4332
|
+
`Delegate flow to Socket service ${url}`,
|
|
4333
|
+
async (delegateCtx, emitter) => {
|
|
4334
|
+
if (delegateCtx.__remoteRoutineName === void 0) {
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4337
|
+
delete delegateCtx.__isSubMeta;
|
|
4338
|
+
delete delegateCtx.__broadcast;
|
|
4339
|
+
const deputyExecId = delegateCtx.__metadata?.__deputyExecId;
|
|
4340
|
+
const requestSentAt = Date.now();
|
|
4341
|
+
if (deputyExecId) {
|
|
4342
|
+
runtimeHandle.pendingDelegationIds.add(deputyExecId);
|
|
4343
|
+
syncPendingCounts();
|
|
4344
|
+
}
|
|
4345
|
+
try {
|
|
4346
|
+
const resultContext = await runtimeHandle.emitWhenReady?.(
|
|
4347
|
+
"delegation",
|
|
4348
|
+
delegateCtx,
|
|
4349
|
+
delegateCtx.__timeout ?? 6e4
|
|
4350
|
+
) ?? {
|
|
4351
|
+
errored: true,
|
|
4352
|
+
__error: "Socket delegation returned no response"
|
|
4353
|
+
};
|
|
4354
|
+
const requestDuration = Date.now() - requestSentAt;
|
|
4355
|
+
const metadata = resultContext.__metadata;
|
|
4356
|
+
delete resultContext.__metadata;
|
|
4357
|
+
if (deputyExecId) {
|
|
4358
|
+
emitter(`meta.socket_client.delegated:${deputyExecId}`, {
|
|
4359
|
+
...resultContext,
|
|
4360
|
+
...metadata,
|
|
4361
|
+
__requestDuration: requestDuration
|
|
4362
|
+
});
|
|
4363
|
+
}
|
|
4364
|
+
if (resultContext?.errored || resultContext?.failed) {
|
|
4365
|
+
const errorMessage = resultContext?.__error ?? resultContext?.error ?? "Socket delegation failed";
|
|
4366
|
+
upsertDiagnostics(
|
|
4367
|
+
{
|
|
4368
|
+
lastHandshakeError: String(errorMessage)
|
|
4369
|
+
},
|
|
4370
|
+
errorMessage
|
|
3926
4371
|
);
|
|
4372
|
+
applySessionOperation("delegate", {
|
|
4373
|
+
lastHandshakeError: String(errorMessage)
|
|
4374
|
+
});
|
|
4375
|
+
}
|
|
4376
|
+
return resultContext;
|
|
4377
|
+
} catch (error) {
|
|
4378
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4379
|
+
const failedContext = {
|
|
4380
|
+
errored: true,
|
|
4381
|
+
__error: message
|
|
4382
|
+
};
|
|
4383
|
+
if (deputyExecId) {
|
|
4384
|
+
emitter(`meta.socket_client.delegated:${deputyExecId}`, {
|
|
4385
|
+
...failedContext,
|
|
4386
|
+
__requestDuration: Date.now() - requestSentAt
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
upsertDiagnostics(
|
|
4390
|
+
{
|
|
4391
|
+
lastHandshakeError: message
|
|
4392
|
+
},
|
|
4393
|
+
error
|
|
4394
|
+
);
|
|
4395
|
+
applySessionOperation("delegate", {
|
|
4396
|
+
lastHandshakeError: message
|
|
4397
|
+
});
|
|
4398
|
+
return failedContext;
|
|
4399
|
+
} finally {
|
|
4400
|
+
if (deputyExecId) {
|
|
4401
|
+
runtimeHandle.pendingDelegationIds.delete(deputyExecId);
|
|
4402
|
+
syncPendingCounts();
|
|
3927
4403
|
}
|
|
3928
|
-
resolve(response);
|
|
3929
|
-
});
|
|
3930
|
-
});
|
|
3931
|
-
},
|
|
3932
|
-
`Transmits signal to service ${serviceName} with address ${URL}`
|
|
3933
|
-
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
3934
|
-
CadenzaService.createEphemeralMetaTask(
|
|
3935
|
-
`Shutdown SocketClient ${URL}`,
|
|
3936
|
-
(ctx2, emit) => {
|
|
3937
|
-
handshake = false;
|
|
3938
|
-
socketDiagnostics.connected = false;
|
|
3939
|
-
socketDiagnostics.handshake = false;
|
|
3940
|
-
socketDiagnostics.destroyed = true;
|
|
3941
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3942
|
-
CadenzaService.log("Shutting down socket client", { URL, serviceName });
|
|
3943
|
-
socket?.close();
|
|
3944
|
-
handshakeTask?.destroy();
|
|
3945
|
-
delegateTask?.destroy();
|
|
3946
|
-
transmitTask?.destroy();
|
|
3947
|
-
handshakeTask = null;
|
|
3948
|
-
delegateTask = null;
|
|
3949
|
-
transmitTask = null;
|
|
3950
|
-
emitWhenReady = null;
|
|
3951
|
-
socket = null;
|
|
3952
|
-
emit(`meta.fetch.handshake_requested:${fetchId}`, {
|
|
3953
|
-
serviceInstanceId,
|
|
3954
|
-
serviceName,
|
|
3955
|
-
communicationTypes,
|
|
3956
|
-
serviceAddress,
|
|
3957
|
-
servicePort,
|
|
3958
|
-
protocol,
|
|
3959
|
-
handshakeData: {
|
|
3960
|
-
instanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
3961
|
-
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
3962
4404
|
}
|
|
3963
|
-
}
|
|
3964
|
-
|
|
3965
|
-
|
|
4405
|
+
},
|
|
4406
|
+
`Delegate flow to service ${serviceName} with address ${url}`
|
|
4407
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal(
|
|
4408
|
+
"meta.socket_client.delegated",
|
|
4409
|
+
"meta.socket_shutdown_requested"
|
|
4410
|
+
);
|
|
4411
|
+
runtimeHandle.transmitTask = CadenzaService.createMetaTask(
|
|
4412
|
+
`Transmit signal to socket server ${url}`,
|
|
4413
|
+
async (signalCtx, emitter) => {
|
|
4414
|
+
if (signalCtx.__signalName === void 0) {
|
|
4415
|
+
return;
|
|
4416
|
+
}
|
|
4417
|
+
delete signalCtx.__broadcast;
|
|
4418
|
+
const response = await runtimeHandle.emitWhenReady?.("signal", signalCtx, 5e3) ?? {
|
|
3966
4419
|
errored: true,
|
|
3967
|
-
__error: "
|
|
4420
|
+
__error: "Socket signal transmission returned no response"
|
|
4421
|
+
};
|
|
4422
|
+
applySessionOperation("transmit", {});
|
|
4423
|
+
if (signalCtx.__routineExecId) {
|
|
4424
|
+
emitter(`meta.socket_client.transmitted:${signalCtx.__routineExecId}`, {
|
|
4425
|
+
...response
|
|
4426
|
+
});
|
|
4427
|
+
}
|
|
4428
|
+
return response;
|
|
4429
|
+
},
|
|
4430
|
+
`Transmits signal to service ${serviceName} with address ${url}`
|
|
4431
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
4432
|
+
CadenzaService.createEphemeralMetaTask(
|
|
4433
|
+
`Shutdown SocketClient ${url}`,
|
|
4434
|
+
(_ctx, emitter) => {
|
|
4435
|
+
runtimeHandle.handshake = false;
|
|
4436
|
+
upsertDiagnostics({
|
|
4437
|
+
connected: false,
|
|
4438
|
+
handshake: false,
|
|
4439
|
+
destroyed: true,
|
|
4440
|
+
pendingDelegations: 0,
|
|
4441
|
+
pendingTimers: 0
|
|
3968
4442
|
});
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
4443
|
+
applySessionOperation("shutdown", {
|
|
4444
|
+
connected: false,
|
|
4445
|
+
handshake: false,
|
|
4446
|
+
destroyed: true,
|
|
4447
|
+
pendingDelegations: 0,
|
|
4448
|
+
pendingTimers: 0
|
|
4449
|
+
});
|
|
4450
|
+
CadenzaService.log("Shutting down socket client", { url, serviceName });
|
|
4451
|
+
emitter(`meta.fetch.handshake_requested:${fetchId}`, {
|
|
4452
|
+
serviceInstanceId,
|
|
4453
|
+
serviceName,
|
|
4454
|
+
communicationTypes,
|
|
4455
|
+
serviceAddress,
|
|
4456
|
+
servicePort: normalizedPort,
|
|
4457
|
+
protocol,
|
|
4458
|
+
handshakeData: {
|
|
4459
|
+
instanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
4460
|
+
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
4461
|
+
}
|
|
4462
|
+
});
|
|
4463
|
+
for (const id of runtimeHandle.pendingDelegationIds) {
|
|
4464
|
+
emitter(`meta.socket_client.delegated:${id}`, {
|
|
4465
|
+
errored: true,
|
|
4466
|
+
__error: "Shutting down socket client"
|
|
4467
|
+
});
|
|
4468
|
+
}
|
|
4469
|
+
this.destroySocketClientRuntimeHandle(runtimeHandle);
|
|
4470
|
+
emitter("meta.socket_client.runtime_clear_requested", {
|
|
4471
|
+
fetchId
|
|
4472
|
+
});
|
|
4473
|
+
},
|
|
4474
|
+
"Shuts down the socket client"
|
|
4475
|
+
).doOn(
|
|
4476
|
+
`meta.socket_shutdown_requested:${fetchId}`,
|
|
4477
|
+
`meta.socket_client.disconnected:${fetchId}`,
|
|
4478
|
+
`meta.fetch.handshake_failed:${fetchId}`,
|
|
4479
|
+
`meta.socket_client.connect_error:${fetchId}`
|
|
4480
|
+
).attachSignal("meta.fetch.handshake_requested").emits("meta.socket_client_shutdown_complete");
|
|
4481
|
+
return true;
|
|
4482
|
+
},
|
|
4483
|
+
{ mode: "write" }
|
|
4484
|
+
),
|
|
4485
|
+
"Connects to a specified socket server and wires runtime tasks."
|
|
3988
4486
|
).doOn("meta.fetch.handshake_complete").emitsOnFail("meta.socket_client.connect_failed");
|
|
3989
4487
|
}
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
return this._instance;
|
|
4488
|
+
resolveSocketServerKey(input) {
|
|
4489
|
+
return String(input.serverKey ?? input.__socketServerKey ?? this.socketServerDefaultKey).trim() || this.socketServerDefaultKey;
|
|
3993
4490
|
}
|
|
3994
|
-
|
|
3995
|
-
const
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
4491
|
+
resolveSocketClientFetchId(input) {
|
|
4492
|
+
const explicitFetchId = String(input.fetchId ?? "").trim();
|
|
4493
|
+
if (explicitFetchId) {
|
|
4494
|
+
return explicitFetchId;
|
|
4495
|
+
}
|
|
4496
|
+
const serviceAddress = String(input.serviceAddress ?? "").trim();
|
|
4497
|
+
const protocol = String(input.protocol ?? "http").trim();
|
|
4498
|
+
const port = this.resolveServicePort(protocol, input.servicePort);
|
|
4499
|
+
if (!serviceAddress || !port) {
|
|
4500
|
+
return void 0;
|
|
4501
|
+
}
|
|
4502
|
+
return `${serviceAddress}_${port}`;
|
|
4503
|
+
}
|
|
4504
|
+
resolveServicePort(protocol, rawPort) {
|
|
4505
|
+
if (protocol === "https") {
|
|
4506
|
+
return 443;
|
|
4507
|
+
}
|
|
4508
|
+
const parsed = Number(rawPort);
|
|
4509
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
4510
|
+
return void 0;
|
|
4511
|
+
}
|
|
4512
|
+
return Math.trunc(parsed);
|
|
4513
|
+
}
|
|
4514
|
+
createSocketServerRuntimeHandleFromContext(context) {
|
|
4515
|
+
const baseServer = context.httpsServer ?? context.httpServer;
|
|
4516
|
+
if (!baseServer) {
|
|
4517
|
+
throw new Error(
|
|
4518
|
+
"Socket server runtime setup requires either httpsServer or httpServer"
|
|
4519
|
+
);
|
|
4520
|
+
}
|
|
4521
|
+
const server = new import_socket.Server(baseServer, {
|
|
4522
|
+
pingInterval: 3e4,
|
|
4523
|
+
pingTimeout: 2e4,
|
|
4524
|
+
maxHttpBufferSize: 1e7,
|
|
4525
|
+
connectionStateRecovery: {
|
|
4526
|
+
maxDisconnectionDuration: 2 * 60 * 1e3,
|
|
4527
|
+
skipMiddlewares: true
|
|
4528
|
+
}
|
|
4529
|
+
});
|
|
3999
4530
|
return {
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4531
|
+
server,
|
|
4532
|
+
initialized: false,
|
|
4533
|
+
connectedSocketIds: /* @__PURE__ */ new Set(),
|
|
4534
|
+
broadcastStatusTask: null,
|
|
4535
|
+
shutdownTask: null
|
|
4003
4536
|
};
|
|
4004
4537
|
}
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
state = {
|
|
4009
|
-
fetchId,
|
|
4010
|
-
serviceName,
|
|
4011
|
-
url,
|
|
4012
|
-
socketId: null,
|
|
4013
|
-
connected: false,
|
|
4014
|
-
handshake: false,
|
|
4015
|
-
reconnectAttempts: 0,
|
|
4016
|
-
connectErrors: 0,
|
|
4017
|
-
reconnectErrors: 0,
|
|
4018
|
-
socketErrors: 0,
|
|
4019
|
-
pendingDelegations: 0,
|
|
4020
|
-
pendingTimers: 0,
|
|
4021
|
-
destroyed: false,
|
|
4022
|
-
lastHandshakeAt: null,
|
|
4023
|
-
lastHandshakeError: null,
|
|
4024
|
-
lastDisconnectAt: null,
|
|
4025
|
-
lastError: null,
|
|
4026
|
-
lastErrorAt: 0,
|
|
4027
|
-
errorHistory: [],
|
|
4028
|
-
updatedAt: Date.now()
|
|
4029
|
-
};
|
|
4030
|
-
this.socketClientDiagnostics.set(fetchId, state);
|
|
4031
|
-
} else {
|
|
4032
|
-
state.serviceName = serviceName;
|
|
4033
|
-
state.url = url;
|
|
4538
|
+
destroySocketServerRuntimeHandle(runtimeHandle) {
|
|
4539
|
+
if (!runtimeHandle) {
|
|
4540
|
+
return;
|
|
4034
4541
|
}
|
|
4035
|
-
|
|
4542
|
+
runtimeHandle.broadcastStatusTask?.destroy();
|
|
4543
|
+
runtimeHandle.shutdownTask?.destroy();
|
|
4544
|
+
runtimeHandle.broadcastStatusTask = null;
|
|
4545
|
+
runtimeHandle.shutdownTask = null;
|
|
4546
|
+
runtimeHandle.connectedSocketIds.clear();
|
|
4547
|
+
runtimeHandle.initialized = false;
|
|
4548
|
+
runtimeHandle.server.close();
|
|
4549
|
+
runtimeHandle.server.removeAllListeners();
|
|
4550
|
+
}
|
|
4551
|
+
createSocketClientRuntimeHandle(url) {
|
|
4552
|
+
return {
|
|
4553
|
+
url,
|
|
4554
|
+
socket: (0, import_socket2.io)(url, {
|
|
4555
|
+
reconnection: true,
|
|
4556
|
+
reconnectionAttempts: 5,
|
|
4557
|
+
reconnectionDelay: 2e3,
|
|
4558
|
+
reconnectionDelayMax: 1e4,
|
|
4559
|
+
randomizationFactor: 0.5,
|
|
4560
|
+
transports: ["websocket"],
|
|
4561
|
+
autoConnect: false
|
|
4562
|
+
}),
|
|
4563
|
+
initialized: false,
|
|
4564
|
+
handshake: false,
|
|
4565
|
+
errorCount: 0,
|
|
4566
|
+
pendingDelegationIds: /* @__PURE__ */ new Set(),
|
|
4567
|
+
pendingTimers: /* @__PURE__ */ new Set(),
|
|
4568
|
+
emitWhenReady: null,
|
|
4569
|
+
handshakeTask: null,
|
|
4570
|
+
delegateTask: null,
|
|
4571
|
+
transmitTask: null
|
|
4572
|
+
};
|
|
4573
|
+
}
|
|
4574
|
+
destroySocketClientRuntimeHandle(runtimeHandle) {
|
|
4575
|
+
if (!runtimeHandle) {
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4578
|
+
runtimeHandle.initialized = false;
|
|
4579
|
+
runtimeHandle.handshake = false;
|
|
4580
|
+
runtimeHandle.emitWhenReady = null;
|
|
4581
|
+
runtimeHandle.handshakeTask?.destroy();
|
|
4582
|
+
runtimeHandle.delegateTask?.destroy();
|
|
4583
|
+
runtimeHandle.transmitTask?.destroy();
|
|
4584
|
+
runtimeHandle.handshakeTask = null;
|
|
4585
|
+
runtimeHandle.delegateTask = null;
|
|
4586
|
+
runtimeHandle.transmitTask = null;
|
|
4587
|
+
for (const timer of runtimeHandle.pendingTimers) {
|
|
4588
|
+
clearTimeout(timer);
|
|
4589
|
+
}
|
|
4590
|
+
runtimeHandle.pendingTimers.clear();
|
|
4591
|
+
runtimeHandle.pendingDelegationIds.clear();
|
|
4592
|
+
runtimeHandle.socket.close();
|
|
4593
|
+
runtimeHandle.socket.removeAllListeners();
|
|
4594
|
+
}
|
|
4595
|
+
normalizeCommunicationTypes(value) {
|
|
4596
|
+
if (!Array.isArray(value)) {
|
|
4597
|
+
return [];
|
|
4598
|
+
}
|
|
4599
|
+
return value.map((item) => String(item)).filter((item) => item.trim().length > 0);
|
|
4036
4600
|
}
|
|
4037
4601
|
getErrorMessage(error) {
|
|
4038
4602
|
if (error instanceof Error) {
|
|
@@ -4047,28 +4611,53 @@ var SocketController = class _SocketController {
|
|
|
4047
4611
|
return String(error);
|
|
4048
4612
|
}
|
|
4049
4613
|
}
|
|
4050
|
-
|
|
4051
|
-
const state
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4614
|
+
pruneDiagnosticsEntries(entries, now = Date.now()) {
|
|
4615
|
+
for (const [fetchId, state] of Object.entries(entries)) {
|
|
4616
|
+
if (state.destroyed && now - state.updatedAt > this.destroyedDiagnosticsTtlMs) {
|
|
4617
|
+
delete entries[fetchId];
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
if (Object.keys(entries).length <= this.diagnosticsMaxClientEntries) {
|
|
4621
|
+
return;
|
|
4622
|
+
}
|
|
4623
|
+
const entriesByEvictionPriority = Object.entries(entries).sort((left, right) => {
|
|
4624
|
+
if (left[1].destroyed !== right[1].destroyed) {
|
|
4625
|
+
return left[1].destroyed ? -1 : 1;
|
|
4626
|
+
}
|
|
4627
|
+
return left[1].updatedAt - right[1].updatedAt;
|
|
4060
4628
|
});
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4629
|
+
while (Object.keys(entries).length > this.diagnosticsMaxClientEntries && entriesByEvictionPriority.length > 0) {
|
|
4630
|
+
const [fetchId] = entriesByEvictionPriority.shift();
|
|
4631
|
+
delete entries[fetchId];
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
async getSocketClientDiagnosticsEntry(fetchId) {
|
|
4635
|
+
const normalized = String(fetchId ?? "").trim();
|
|
4636
|
+
if (!normalized) {
|
|
4637
|
+
return void 0;
|
|
4066
4638
|
}
|
|
4639
|
+
const snapshot = this.socketClientDiagnosticsActor.getState();
|
|
4640
|
+
const entries = { ...snapshot.entries };
|
|
4641
|
+
this.pruneDiagnosticsEntries(entries);
|
|
4642
|
+
return entries[normalized];
|
|
4643
|
+
}
|
|
4644
|
+
resolveTransportDiagnosticsOptions(ctx) {
|
|
4645
|
+
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
4646
|
+
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
4647
|
+
const requestedLimit = Number(ctx.errorHistoryLimit);
|
|
4648
|
+
const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
|
|
4649
|
+
return {
|
|
4650
|
+
detailLevel,
|
|
4651
|
+
includeErrorHistory,
|
|
4652
|
+
errorHistoryLimit
|
|
4653
|
+
};
|
|
4067
4654
|
}
|
|
4068
|
-
collectSocketTransportDiagnostics(ctx) {
|
|
4655
|
+
collectSocketTransportDiagnostics(ctx, diagnosticsEntries) {
|
|
4069
4656
|
const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
|
|
4070
4657
|
const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
|
|
4071
|
-
const
|
|
4658
|
+
const entries = { ...diagnosticsEntries };
|
|
4659
|
+
this.pruneDiagnosticsEntries(entries);
|
|
4660
|
+
const states = Object.values(entries).sort(
|
|
4072
4661
|
(a, b) => a.fetchId.localeCompare(b.fetchId)
|
|
4073
4662
|
);
|
|
4074
4663
|
const summary = {
|
|
@@ -4086,10 +4675,7 @@ var SocketController = class _SocketController {
|
|
|
4086
4675
|
0
|
|
4087
4676
|
),
|
|
4088
4677
|
connectErrors: states.reduce((acc, state) => acc + state.connectErrors, 0),
|
|
4089
|
-
reconnectErrors: states.reduce(
|
|
4090
|
-
(acc, state) => acc + state.reconnectErrors,
|
|
4091
|
-
0
|
|
4092
|
-
),
|
|
4678
|
+
reconnectErrors: states.reduce((acc, state) => acc + state.reconnectErrors, 0),
|
|
4093
4679
|
socketErrors: states.reduce((acc, state) => acc + state.socketErrors, 0),
|
|
4094
4680
|
latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
|
|
4095
4681
|
};
|
|
@@ -4448,6 +5034,22 @@ var GraphMetadataController = class _GraphMetadataController {
|
|
|
4448
5034
|
"Handles task execution relationship creation",
|
|
4449
5035
|
{ concurrency: 100, isSubMeta: true }
|
|
4450
5036
|
).doOn("meta.node.mapped", "meta.node.detected_previous_task_execution").emits("global.meta.graph_metadata.relationship_executed");
|
|
5037
|
+
CadenzaService.createMetaTask("Handle actor creation", (ctx) => {
|
|
5038
|
+
return {
|
|
5039
|
+
data: {
|
|
5040
|
+
...ctx.data,
|
|
5041
|
+
service_name: CadenzaService.serviceRegistry.serviceName
|
|
5042
|
+
}
|
|
5043
|
+
};
|
|
5044
|
+
}).doOn("meta.actor.created").emits("global.meta.graph_metadata.actor_created");
|
|
5045
|
+
CadenzaService.createMetaTask("Handle actor task association", (ctx) => {
|
|
5046
|
+
return {
|
|
5047
|
+
data: {
|
|
5048
|
+
...ctx.data,
|
|
5049
|
+
service_name: CadenzaService.serviceRegistry.serviceName
|
|
5050
|
+
}
|
|
5051
|
+
};
|
|
5052
|
+
}).doOn("meta.actor.task_associated").emits("global.meta.graph_metadata.actor_task_associated");
|
|
4451
5053
|
CadenzaService.createMetaTask("Handle Intent Creation", (ctx) => {
|
|
4452
5054
|
const intentName = ctx.data?.name;
|
|
4453
5055
|
return {
|
|
@@ -6119,8 +6721,69 @@ function tableFieldTypeToSchemaType(type) {
|
|
|
6119
6721
|
var import_uuid3 = require("uuid");
|
|
6120
6722
|
|
|
6121
6723
|
// src/graph/controllers/GraphSyncController.ts
|
|
6724
|
+
var ACTOR_TASK_METADATA = /* @__PURE__ */ Symbol.for("@cadenza.io/core/actor-task-meta");
|
|
6725
|
+
function getActorTaskRuntimeMetadata(taskFunction) {
|
|
6726
|
+
if (typeof taskFunction !== "function") {
|
|
6727
|
+
return void 0;
|
|
6728
|
+
}
|
|
6729
|
+
return taskFunction[ACTOR_TASK_METADATA];
|
|
6730
|
+
}
|
|
6731
|
+
function sanitizeActorMetadataValue(value) {
|
|
6732
|
+
if (value === null) {
|
|
6733
|
+
return null;
|
|
6734
|
+
}
|
|
6735
|
+
if (value === void 0 || typeof value === "function") {
|
|
6736
|
+
return void 0;
|
|
6737
|
+
}
|
|
6738
|
+
if (Array.isArray(value)) {
|
|
6739
|
+
const items = [];
|
|
6740
|
+
for (const item of value) {
|
|
6741
|
+
const sanitizedItem = sanitizeActorMetadataValue(item);
|
|
6742
|
+
if (sanitizedItem !== void 0) {
|
|
6743
|
+
items.push(sanitizedItem);
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
return items;
|
|
6747
|
+
}
|
|
6748
|
+
if (typeof value === "object") {
|
|
6749
|
+
const output = {};
|
|
6750
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
6751
|
+
const sanitizedNestedValue = sanitizeActorMetadataValue(nestedValue);
|
|
6752
|
+
if (sanitizedNestedValue !== void 0) {
|
|
6753
|
+
output[key] = sanitizedNestedValue;
|
|
6754
|
+
}
|
|
6755
|
+
}
|
|
6756
|
+
return output;
|
|
6757
|
+
}
|
|
6758
|
+
return value;
|
|
6759
|
+
}
|
|
6760
|
+
function buildActorRegistrationData(actor) {
|
|
6761
|
+
const definition = sanitizeActorMetadataValue(
|
|
6762
|
+
typeof actor?.toDefinition === "function" ? actor.toDefinition() : {}
|
|
6763
|
+
);
|
|
6764
|
+
const stateDefinition = definition?.state && typeof definition.state === "object" ? definition.state : {};
|
|
6765
|
+
const actorKind = typeof definition?.kind === "string" ? definition.kind : actor?.kind;
|
|
6766
|
+
return {
|
|
6767
|
+
name: definition?.name ?? actor?.spec?.name ?? "",
|
|
6768
|
+
description: definition?.description ?? actor?.spec?.description ?? "",
|
|
6769
|
+
default_key: definition?.defaultKey ?? actor?.spec?.defaultKey ?? "default",
|
|
6770
|
+
load_policy: definition?.loadPolicy ?? actor?.spec?.loadPolicy ?? "eager",
|
|
6771
|
+
write_contract: definition?.writeContract ?? actor?.spec?.writeContract ?? "overwrite",
|
|
6772
|
+
runtime_read_guard: definition?.runtimeReadGuard ?? actor?.spec?.runtimeReadGuard ?? "none",
|
|
6773
|
+
consistency_profile: definition?.consistencyProfile ?? actor?.spec?.consistencyProfile ?? null,
|
|
6774
|
+
key_definition: definition?.key ?? null,
|
|
6775
|
+
state_definition: stateDefinition,
|
|
6776
|
+
retry_policy: definition?.retry ?? {},
|
|
6777
|
+
idempotency_policy: definition?.idempotency ?? {},
|
|
6778
|
+
session_policy: definition?.session ?? {},
|
|
6779
|
+
is_meta: actorKind === "meta",
|
|
6780
|
+
version: 1
|
|
6781
|
+
};
|
|
6782
|
+
}
|
|
6122
6783
|
var GraphSyncController = class _GraphSyncController {
|
|
6123
6784
|
constructor() {
|
|
6785
|
+
this.registeredActors = /* @__PURE__ */ new Set();
|
|
6786
|
+
this.registeredActorTaskMaps = /* @__PURE__ */ new Set();
|
|
6124
6787
|
this.isCadenzaDBReady = false;
|
|
6125
6788
|
}
|
|
6126
6789
|
static get instance() {
|
|
@@ -6378,6 +7041,120 @@ var GraphSyncController = class _GraphSyncController {
|
|
|
6378
7041
|
)
|
|
6379
7042
|
)
|
|
6380
7043
|
);
|
|
7044
|
+
this.splitActorsForRegistration = CadenzaService.createMetaTask(
|
|
7045
|
+
"Split actors for registration",
|
|
7046
|
+
function* (ctx) {
|
|
7047
|
+
CadenzaService.debounce("meta.sync_controller.synced_resource", {
|
|
7048
|
+
delayMs: 3e3
|
|
7049
|
+
});
|
|
7050
|
+
const actors = ctx.actors ?? [];
|
|
7051
|
+
for (const actor of actors) {
|
|
7052
|
+
const data = {
|
|
7053
|
+
...buildActorRegistrationData(actor),
|
|
7054
|
+
service_name: CadenzaService.serviceRegistry.serviceName
|
|
7055
|
+
};
|
|
7056
|
+
if (!data.name) {
|
|
7057
|
+
continue;
|
|
7058
|
+
}
|
|
7059
|
+
const registrationKey = `${data.name}|${data.version}|${data.service_name}`;
|
|
7060
|
+
if (this.registeredActors.has(registrationKey)) {
|
|
7061
|
+
continue;
|
|
7062
|
+
}
|
|
7063
|
+
yield {
|
|
7064
|
+
data,
|
|
7065
|
+
__actorRegistrationKey: registrationKey
|
|
7066
|
+
};
|
|
7067
|
+
}
|
|
7068
|
+
}.bind(this)
|
|
7069
|
+
).then(
|
|
7070
|
+
(this.isCadenzaDBReady ? CadenzaService.createCadenzaDBInsertTask(
|
|
7071
|
+
"actor",
|
|
7072
|
+
{
|
|
7073
|
+
onConflict: {
|
|
7074
|
+
target: ["name", "service_name", "version"],
|
|
7075
|
+
action: {
|
|
7076
|
+
do: "nothing"
|
|
7077
|
+
}
|
|
7078
|
+
}
|
|
7079
|
+
},
|
|
7080
|
+
{ concurrency: 30 }
|
|
7081
|
+
) : CadenzaService.get("dbInsertActor"))?.then(
|
|
7082
|
+
CadenzaService.createMetaTask("Record actor registration", (ctx) => {
|
|
7083
|
+
if (!ctx.__syncing) {
|
|
7084
|
+
return;
|
|
7085
|
+
}
|
|
7086
|
+
CadenzaService.debounce("meta.sync_controller.synced_resource", {
|
|
7087
|
+
delayMs: 3e3
|
|
7088
|
+
});
|
|
7089
|
+
this.registeredActors.add(ctx.__actorRegistrationKey);
|
|
7090
|
+
return true;
|
|
7091
|
+
}).then(
|
|
7092
|
+
CadenzaService.createUniqueMetaTask(
|
|
7093
|
+
"Gather actor registration",
|
|
7094
|
+
() => true
|
|
7095
|
+
).emits("meta.sync_controller.synced_actors")
|
|
7096
|
+
)
|
|
7097
|
+
)
|
|
7098
|
+
);
|
|
7099
|
+
this.registerActorTaskMapTask = CadenzaService.createMetaTask(
|
|
7100
|
+
"Split actor task maps",
|
|
7101
|
+
function* (ctx) {
|
|
7102
|
+
const task = ctx.task;
|
|
7103
|
+
if (task.hidden || !task.register) {
|
|
7104
|
+
return;
|
|
7105
|
+
}
|
|
7106
|
+
const metadata = getActorTaskRuntimeMetadata(task.taskFunction);
|
|
7107
|
+
if (!metadata?.actorName) {
|
|
7108
|
+
return;
|
|
7109
|
+
}
|
|
7110
|
+
const registrationKey = `${metadata.actorName}|${task.name}|${task.version}|${CadenzaService.serviceRegistry.serviceName}`;
|
|
7111
|
+
if (this.registeredActorTaskMaps.has(registrationKey)) {
|
|
7112
|
+
return;
|
|
7113
|
+
}
|
|
7114
|
+
yield {
|
|
7115
|
+
data: {
|
|
7116
|
+
actor_name: metadata.actorName,
|
|
7117
|
+
actor_version: 1,
|
|
7118
|
+
task_name: task.name,
|
|
7119
|
+
task_version: task.version,
|
|
7120
|
+
service_name: CadenzaService.serviceRegistry.serviceName,
|
|
7121
|
+
mode: metadata.mode,
|
|
7122
|
+
description: task.description ?? metadata.actorDescription ?? "",
|
|
7123
|
+
is_meta: metadata.actorKind === "meta" || task.isMeta === true
|
|
7124
|
+
},
|
|
7125
|
+
__actorTaskMapRegistrationKey: registrationKey
|
|
7126
|
+
};
|
|
7127
|
+
}.bind(this)
|
|
7128
|
+
).then(
|
|
7129
|
+
(this.isCadenzaDBReady ? CadenzaService.createCadenzaDBInsertTask(
|
|
7130
|
+
"actor_task_map",
|
|
7131
|
+
{
|
|
7132
|
+
onConflict: {
|
|
7133
|
+
target: [
|
|
7134
|
+
"actor_name",
|
|
7135
|
+
"actor_version",
|
|
7136
|
+
"task_name",
|
|
7137
|
+
"task_version",
|
|
7138
|
+
"service_name"
|
|
7139
|
+
],
|
|
7140
|
+
action: {
|
|
7141
|
+
do: "nothing"
|
|
7142
|
+
}
|
|
7143
|
+
}
|
|
7144
|
+
},
|
|
7145
|
+
{ concurrency: 30 }
|
|
7146
|
+
) : CadenzaService.get("dbInsertActorTaskMap"))?.then(
|
|
7147
|
+
CadenzaService.createMetaTask("Record actor task map registration", (ctx) => {
|
|
7148
|
+
if (!ctx.__syncing) {
|
|
7149
|
+
return;
|
|
7150
|
+
}
|
|
7151
|
+
CadenzaService.debounce("meta.sync_controller.synced_resource", {
|
|
7152
|
+
delayMs: 3e3
|
|
7153
|
+
});
|
|
7154
|
+
this.registeredActorTaskMaps.add(ctx.__actorTaskMapRegistrationKey);
|
|
7155
|
+
})
|
|
7156
|
+
)
|
|
7157
|
+
);
|
|
6381
7158
|
const registerSignalTask = CadenzaService.createMetaTask(
|
|
6382
7159
|
"Record signal registration",
|
|
6383
7160
|
(ctx) => {
|
|
@@ -6619,12 +7396,19 @@ var GraphSyncController = class _GraphSyncController {
|
|
|
6619
7396
|
).then(this.splitSignalsTask);
|
|
6620
7397
|
CadenzaService.registry.getAllTasks.clone().doOn("meta.sync_controller.synced_signals").then(this.splitTasksForRegistration);
|
|
6621
7398
|
CadenzaService.registry.getAllRoutines.clone().doOn("meta.sync_controller.synced_tasks").then(this.splitRoutinesTask);
|
|
7399
|
+
CadenzaService.createMetaTask("Get all actors", (ctx) => {
|
|
7400
|
+
return {
|
|
7401
|
+
...ctx,
|
|
7402
|
+
actors: CadenzaService.getAllActors()
|
|
7403
|
+
};
|
|
7404
|
+
}).doOn("meta.sync_controller.synced_tasks").then(this.splitActorsForRegistration);
|
|
6622
7405
|
CadenzaService.registry.doForEachTask.clone().doOn("meta.sync_controller.synced_tasks").then(
|
|
6623
7406
|
this.registerTaskMapTask,
|
|
6624
7407
|
this.registerSignalToTaskMapTask,
|
|
6625
7408
|
this.registerIntentToTaskMapTask,
|
|
6626
7409
|
this.registerDeputyRelationshipTask
|
|
6627
7410
|
);
|
|
7411
|
+
CadenzaService.registry.doForEachTask.clone().doOn("meta.sync_controller.synced_tasks", "meta.sync_controller.synced_actors").then(this.registerActorTaskMapTask);
|
|
6628
7412
|
CadenzaService.registry.getAllRoutines.clone().doOn("meta.sync_controller.synced_routines").then(this.splitTasksInRoutines);
|
|
6629
7413
|
CadenzaService.createMetaTask("Finish sync", (ctx, emit) => {
|
|
6630
7414
|
emit("global.meta.sync_controller.synced", {
|
|
@@ -7011,6 +7795,14 @@ var CadenzaService = class {
|
|
|
7011
7795
|
static get(taskName) {
|
|
7012
7796
|
return import_core3.default.get(taskName);
|
|
7013
7797
|
}
|
|
7798
|
+
static getActor(actorName) {
|
|
7799
|
+
const cadenzaWithActors = import_core3.default;
|
|
7800
|
+
return cadenzaWithActors.getActor?.(actorName);
|
|
7801
|
+
}
|
|
7802
|
+
static getAllActors() {
|
|
7803
|
+
const cadenzaWithActors = import_core3.default;
|
|
7804
|
+
return cadenzaWithActors.getAllActors?.() ?? [];
|
|
7805
|
+
}
|
|
7014
7806
|
static getRoutine(routineName) {
|
|
7015
7807
|
return import_core3.default.getRoutine(routineName);
|
|
7016
7808
|
}
|
|
@@ -7576,6 +8368,14 @@ var CadenzaService = class {
|
|
|
7576
8368
|
options.isMeta = true;
|
|
7577
8369
|
this.createDatabaseService(name, schema, description, options);
|
|
7578
8370
|
}
|
|
8371
|
+
static createActor(spec, options = {}) {
|
|
8372
|
+
this.bootstrap();
|
|
8373
|
+
return import_core3.default.createActor(spec, options);
|
|
8374
|
+
}
|
|
8375
|
+
static createActorFromDefinition(definition, options = {}) {
|
|
8376
|
+
this.bootstrap();
|
|
8377
|
+
return import_core3.default.createActorFromDefinition(definition, options);
|
|
8378
|
+
}
|
|
7579
8379
|
/**
|
|
7580
8380
|
* Creates and registers a new task with the provided name, function, and optional details.
|
|
7581
8381
|
*
|
|
@@ -7993,6 +8793,7 @@ var import_core4 = require("@cadenza.io/core");
|
|
|
7993
8793
|
var index_default = CadenzaService;
|
|
7994
8794
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7995
8795
|
0 && (module.exports = {
|
|
8796
|
+
Actor,
|
|
7996
8797
|
DatabaseTask,
|
|
7997
8798
|
DebounceTask,
|
|
7998
8799
|
DeputyTask,
|