@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.mjs
CHANGED
|
@@ -2426,6 +2426,8 @@ var RestController = class _RestController {
|
|
|
2426
2426
|
constructor() {
|
|
2427
2427
|
this.fetchClientDiagnostics = /* @__PURE__ */ new Map();
|
|
2428
2428
|
this.diagnosticsErrorHistoryLimit = 100;
|
|
2429
|
+
this.diagnosticsMaxClientEntries = 500;
|
|
2430
|
+
this.destroyedDiagnosticsTtlMs = 15 * 6e4;
|
|
2429
2431
|
/**
|
|
2430
2432
|
* Fetches data from the given URL with a specified timeout. This function performs
|
|
2431
2433
|
* a fetch request with the ability to cancel the request if it exceeds the provided timeout duration.
|
|
@@ -3079,6 +3081,28 @@ var RestController = class _RestController {
|
|
|
3079
3081
|
if (!this._instance) this._instance = new _RestController();
|
|
3080
3082
|
return this._instance;
|
|
3081
3083
|
}
|
|
3084
|
+
pruneFetchClientDiagnostics(now = Date.now()) {
|
|
3085
|
+
for (const [fetchId, state] of this.fetchClientDiagnostics.entries()) {
|
|
3086
|
+
if (state.destroyed && now - state.updatedAt > this.destroyedDiagnosticsTtlMs) {
|
|
3087
|
+
this.fetchClientDiagnostics.delete(fetchId);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
if (this.fetchClientDiagnostics.size <= this.diagnosticsMaxClientEntries) {
|
|
3091
|
+
return;
|
|
3092
|
+
}
|
|
3093
|
+
const entriesByEvictionPriority = Array.from(
|
|
3094
|
+
this.fetchClientDiagnostics.entries()
|
|
3095
|
+
).sort((left, right) => {
|
|
3096
|
+
if (left[1].destroyed !== right[1].destroyed) {
|
|
3097
|
+
return left[1].destroyed ? -1 : 1;
|
|
3098
|
+
}
|
|
3099
|
+
return left[1].updatedAt - right[1].updatedAt;
|
|
3100
|
+
});
|
|
3101
|
+
while (this.fetchClientDiagnostics.size > this.diagnosticsMaxClientEntries && entriesByEvictionPriority.length > 0) {
|
|
3102
|
+
const [fetchId] = entriesByEvictionPriority.shift();
|
|
3103
|
+
this.fetchClientDiagnostics.delete(fetchId);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3082
3106
|
resolveTransportDiagnosticsOptions(ctx) {
|
|
3083
3107
|
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
3084
3108
|
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
@@ -3091,6 +3115,8 @@ var RestController = class _RestController {
|
|
|
3091
3115
|
};
|
|
3092
3116
|
}
|
|
3093
3117
|
ensureFetchClientDiagnostics(fetchId, serviceName, url) {
|
|
3118
|
+
const now = Date.now();
|
|
3119
|
+
this.pruneFetchClientDiagnostics(now);
|
|
3094
3120
|
let state = this.fetchClientDiagnostics.get(fetchId);
|
|
3095
3121
|
if (!state) {
|
|
3096
3122
|
state = {
|
|
@@ -3110,13 +3136,14 @@ var RestController = class _RestController {
|
|
|
3110
3136
|
signalFailures: 0,
|
|
3111
3137
|
statusChecks: 0,
|
|
3112
3138
|
statusFailures: 0,
|
|
3113
|
-
updatedAt:
|
|
3139
|
+
updatedAt: now
|
|
3114
3140
|
};
|
|
3115
3141
|
this.fetchClientDiagnostics.set(fetchId, state);
|
|
3116
3142
|
} else {
|
|
3117
3143
|
state.serviceName = serviceName;
|
|
3118
3144
|
state.url = url;
|
|
3119
3145
|
}
|
|
3146
|
+
this.pruneFetchClientDiagnostics(now);
|
|
3120
3147
|
return state;
|
|
3121
3148
|
}
|
|
3122
3149
|
getErrorMessage(error) {
|
|
@@ -3148,6 +3175,7 @@ var RestController = class _RestController {
|
|
|
3148
3175
|
}
|
|
3149
3176
|
}
|
|
3150
3177
|
collectFetchTransportDiagnostics(ctx) {
|
|
3178
|
+
this.pruneFetchClientDiagnostics();
|
|
3151
3179
|
const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
|
|
3152
3180
|
const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
|
|
3153
3181
|
const states = Array.from(this.fetchClientDiagnostics.values()).sort(
|
|
@@ -3264,55 +3292,261 @@ var waitForSocketConnection = async (socket, timeoutMs, createError) => {
|
|
|
3264
3292
|
|
|
3265
3293
|
// src/network/SocketController.ts
|
|
3266
3294
|
var SocketController = class _SocketController {
|
|
3267
|
-
/**
|
|
3268
|
-
* Constructs the `SocketServer`, setting up a WebSocket server with specific configurations,
|
|
3269
|
-
* including connection state recovery, rate limiting, CORS handling, and custom event handling.
|
|
3270
|
-
* This class sets up the communication infrastructure for scalable, resilient, and secure WebSocket-based interactions
|
|
3271
|
-
* using metadata-driven task execution with `Cadenza`.
|
|
3272
|
-
*
|
|
3273
|
-
* It provides support for:
|
|
3274
|
-
* - Origin-based access control for connections.
|
|
3275
|
-
* - Optional payload sanitization.
|
|
3276
|
-
* - Configurable rate limiting and behavior on limit breaches (soft/hard disconnects).
|
|
3277
|
-
* - Event handlers for connection, handshake, delegation, signaling, status checks, and disconnection.
|
|
3278
|
-
*
|
|
3279
|
-
* The server can handle both internal and external interactions depending on the provided configurations,
|
|
3280
|
-
* and integrates directly with Cadenza's task workflow engine.
|
|
3281
|
-
*
|
|
3282
|
-
* Initializes the `SocketServer` to be ready for WebSocket communication.
|
|
3283
|
-
*/
|
|
3284
3295
|
constructor() {
|
|
3285
|
-
this.socketClientDiagnostics = /* @__PURE__ */ new Map();
|
|
3286
3296
|
this.diagnosticsErrorHistoryLimit = 100;
|
|
3297
|
+
this.diagnosticsMaxClientEntries = 500;
|
|
3298
|
+
this.destroyedDiagnosticsTtlMs = 15 * 6e4;
|
|
3299
|
+
this.socketServerDefaultKey = "socket-server-default";
|
|
3300
|
+
this.socketServerInitialSessionState = {
|
|
3301
|
+
serverKey: this.socketServerDefaultKey,
|
|
3302
|
+
useSocket: false,
|
|
3303
|
+
status: "inactive",
|
|
3304
|
+
securityProfile: "medium",
|
|
3305
|
+
networkType: "internal",
|
|
3306
|
+
connectionCount: 0,
|
|
3307
|
+
lastStartedAt: null,
|
|
3308
|
+
lastConnectedAt: null,
|
|
3309
|
+
lastDisconnectedAt: null,
|
|
3310
|
+
lastShutdownAt: null,
|
|
3311
|
+
updatedAt: 0
|
|
3312
|
+
};
|
|
3313
|
+
this.socketClientInitialSessionState = {
|
|
3314
|
+
fetchId: "",
|
|
3315
|
+
serviceInstanceId: "",
|
|
3316
|
+
communicationTypes: [],
|
|
3317
|
+
serviceName: "",
|
|
3318
|
+
serviceAddress: "",
|
|
3319
|
+
servicePort: 0,
|
|
3320
|
+
protocol: "http",
|
|
3321
|
+
url: "",
|
|
3322
|
+
socketId: null,
|
|
3323
|
+
connected: false,
|
|
3324
|
+
handshake: false,
|
|
3325
|
+
pendingDelegations: 0,
|
|
3326
|
+
pendingTimers: 0,
|
|
3327
|
+
reconnectAttempts: 0,
|
|
3328
|
+
connectErrors: 0,
|
|
3329
|
+
reconnectErrors: 0,
|
|
3330
|
+
socketErrors: 0,
|
|
3331
|
+
errorCount: 0,
|
|
3332
|
+
destroyed: false,
|
|
3333
|
+
lastHandshakeAt: null,
|
|
3334
|
+
lastHandshakeError: null,
|
|
3335
|
+
lastDisconnectAt: null,
|
|
3336
|
+
updatedAt: 0
|
|
3337
|
+
};
|
|
3338
|
+
this.socketServerActor = CadenzaService.createActor(
|
|
3339
|
+
{
|
|
3340
|
+
name: "SocketServerActor",
|
|
3341
|
+
description: "Holds durable socket server session state and runtime socket server handle",
|
|
3342
|
+
defaultKey: this.socketServerDefaultKey,
|
|
3343
|
+
keyResolver: (input) => this.resolveSocketServerKey(input),
|
|
3344
|
+
loadPolicy: "lazy",
|
|
3345
|
+
writeContract: "overwrite",
|
|
3346
|
+
initState: this.socketServerInitialSessionState
|
|
3347
|
+
},
|
|
3348
|
+
{ isMeta: true }
|
|
3349
|
+
);
|
|
3350
|
+
this.socketClientActor = CadenzaService.createActor(
|
|
3351
|
+
{
|
|
3352
|
+
name: "SocketClientActor",
|
|
3353
|
+
description: "Holds durable socket client session state and runtime socket connection handles",
|
|
3354
|
+
defaultKey: "socket-client-default",
|
|
3355
|
+
keyResolver: (input) => this.resolveSocketClientFetchId(input),
|
|
3356
|
+
loadPolicy: "lazy",
|
|
3357
|
+
writeContract: "overwrite",
|
|
3358
|
+
initState: this.socketClientInitialSessionState
|
|
3359
|
+
},
|
|
3360
|
+
{ isMeta: true }
|
|
3361
|
+
);
|
|
3362
|
+
this.socketClientDiagnosticsActor = CadenzaService.createActor(
|
|
3363
|
+
{
|
|
3364
|
+
name: "SocketClientDiagnosticsActor",
|
|
3365
|
+
description: "Tracks socket client diagnostics snapshots per fetchId for transport observability",
|
|
3366
|
+
defaultKey: "socket-client-diagnostics",
|
|
3367
|
+
loadPolicy: "eager",
|
|
3368
|
+
writeContract: "overwrite",
|
|
3369
|
+
initState: {
|
|
3370
|
+
entries: {}
|
|
3371
|
+
}
|
|
3372
|
+
},
|
|
3373
|
+
{ isMeta: true }
|
|
3374
|
+
);
|
|
3375
|
+
this.registerDiagnosticsTasks();
|
|
3376
|
+
this.registerSocketServerTasks();
|
|
3377
|
+
this.registerSocketClientTasks();
|
|
3287
3378
|
CadenzaService.createMetaTask(
|
|
3288
3379
|
"Collect socket transport diagnostics",
|
|
3289
|
-
|
|
3380
|
+
this.socketClientDiagnosticsActor.task(
|
|
3381
|
+
({ state, input }) => this.collectSocketTransportDiagnostics(input, state.entries),
|
|
3382
|
+
{ mode: "read" }
|
|
3383
|
+
),
|
|
3290
3384
|
"Responds to distributed transport diagnostics inquiries with socket client data."
|
|
3291
3385
|
).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3386
|
+
}
|
|
3387
|
+
static get instance() {
|
|
3388
|
+
if (!this._instance) this._instance = new _SocketController();
|
|
3389
|
+
return this._instance;
|
|
3390
|
+
}
|
|
3391
|
+
registerDiagnosticsTasks() {
|
|
3392
|
+
CadenzaService.createThrottledMetaTask(
|
|
3393
|
+
"SocketClientDiagnosticsActor.Upsert",
|
|
3394
|
+
this.socketClientDiagnosticsActor.task(
|
|
3395
|
+
({ state, input, setState }) => {
|
|
3396
|
+
const fetchId = String(input.fetchId ?? "").trim();
|
|
3397
|
+
if (!fetchId) {
|
|
3297
3398
|
return;
|
|
3298
3399
|
}
|
|
3299
|
-
const
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3400
|
+
const now = Date.now();
|
|
3401
|
+
const entries = { ...state.entries };
|
|
3402
|
+
const existing = entries[fetchId];
|
|
3403
|
+
const base = existing ? {
|
|
3404
|
+
...existing,
|
|
3405
|
+
errorHistory: [...existing.errorHistory]
|
|
3406
|
+
} : {
|
|
3407
|
+
fetchId,
|
|
3408
|
+
serviceName: String(input.serviceName ?? ""),
|
|
3409
|
+
url: String(input.url ?? ""),
|
|
3410
|
+
socketId: null,
|
|
3411
|
+
connected: false,
|
|
3412
|
+
handshake: false,
|
|
3413
|
+
reconnectAttempts: 0,
|
|
3414
|
+
connectErrors: 0,
|
|
3415
|
+
reconnectErrors: 0,
|
|
3416
|
+
socketErrors: 0,
|
|
3417
|
+
pendingDelegations: 0,
|
|
3418
|
+
pendingTimers: 0,
|
|
3419
|
+
destroyed: false,
|
|
3420
|
+
lastHandshakeAt: null,
|
|
3421
|
+
lastHandshakeError: null,
|
|
3422
|
+
lastDisconnectAt: null,
|
|
3423
|
+
lastError: null,
|
|
3424
|
+
lastErrorAt: 0,
|
|
3425
|
+
errorHistory: [],
|
|
3426
|
+
updatedAt: now
|
|
3427
|
+
};
|
|
3428
|
+
if (input.serviceName !== void 0) {
|
|
3429
|
+
base.serviceName = String(input.serviceName);
|
|
3430
|
+
}
|
|
3431
|
+
if (input.url !== void 0) {
|
|
3432
|
+
base.url = String(input.url);
|
|
3433
|
+
}
|
|
3434
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3435
|
+
Object.assign(base, patch);
|
|
3436
|
+
base.fetchId = fetchId;
|
|
3437
|
+
base.updatedAt = now;
|
|
3438
|
+
const errorMessage = input.error !== void 0 ? this.getErrorMessage(input.error) : void 0;
|
|
3439
|
+
if (errorMessage) {
|
|
3440
|
+
base.lastError = errorMessage;
|
|
3441
|
+
base.lastErrorAt = now;
|
|
3442
|
+
base.errorHistory.push({
|
|
3443
|
+
at: new Date(now).toISOString(),
|
|
3444
|
+
message: errorMessage
|
|
3445
|
+
});
|
|
3446
|
+
if (base.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
|
|
3447
|
+
base.errorHistory.splice(
|
|
3448
|
+
0,
|
|
3449
|
+
base.errorHistory.length - this.diagnosticsErrorHistoryLimit
|
|
3450
|
+
);
|
|
3309
3451
|
}
|
|
3452
|
+
}
|
|
3453
|
+
entries[fetchId] = base;
|
|
3454
|
+
this.pruneDiagnosticsEntries(entries, now);
|
|
3455
|
+
setState({ entries });
|
|
3456
|
+
},
|
|
3457
|
+
{ mode: "write" }
|
|
3458
|
+
),
|
|
3459
|
+
(context) => String(context?.fetchId ?? "default"),
|
|
3460
|
+
"Upserts socket client diagnostics in actor state."
|
|
3461
|
+
).doOn("meta.socket_client.diagnostics_upsert_requested");
|
|
3462
|
+
}
|
|
3463
|
+
registerSocketServerTasks() {
|
|
3464
|
+
CadenzaService.createThrottledMetaTask(
|
|
3465
|
+
"SocketServerActor.PatchSession",
|
|
3466
|
+
this.socketServerActor.task(
|
|
3467
|
+
({ state, input, setState }) => {
|
|
3468
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3469
|
+
setState({
|
|
3470
|
+
...state,
|
|
3471
|
+
...patch,
|
|
3472
|
+
updatedAt: Date.now()
|
|
3473
|
+
});
|
|
3474
|
+
},
|
|
3475
|
+
{ mode: "write" }
|
|
3476
|
+
),
|
|
3477
|
+
(context) => String(context?.serverKey ?? this.socketServerDefaultKey),
|
|
3478
|
+
"Applies partial durable session updates for socket server actor."
|
|
3479
|
+
).doOn("meta.socket_server.session_patch_requested");
|
|
3480
|
+
CadenzaService.createMetaTask(
|
|
3481
|
+
"SocketServerActor.ClearRuntime",
|
|
3482
|
+
this.socketServerActor.task(
|
|
3483
|
+
({ setRuntimeState }) => {
|
|
3484
|
+
setRuntimeState(null);
|
|
3485
|
+
},
|
|
3486
|
+
{ mode: "write" }
|
|
3487
|
+
),
|
|
3488
|
+
"Clears socket server runtime handle after shutdown."
|
|
3489
|
+
).doOn("meta.socket_server.runtime_clear_requested");
|
|
3490
|
+
const setupSocketServerTask = CadenzaService.createMetaTask(
|
|
3491
|
+
"Setup SocketServer",
|
|
3492
|
+
this.socketServerActor.task(
|
|
3493
|
+
({ state, runtimeState, input, actor, setState, setRuntimeState, emit }) => {
|
|
3494
|
+
const serverKey = this.resolveSocketServerKey(input) ?? actor.key ?? this.socketServerDefaultKey;
|
|
3495
|
+
const shouldUseSocket = Boolean(input.__useSocket);
|
|
3496
|
+
if (!shouldUseSocket) {
|
|
3497
|
+
this.destroySocketServerRuntimeHandle(runtimeState);
|
|
3498
|
+
setRuntimeState(null);
|
|
3499
|
+
setState({
|
|
3500
|
+
...state,
|
|
3501
|
+
serverKey,
|
|
3502
|
+
useSocket: false,
|
|
3503
|
+
status: "inactive",
|
|
3504
|
+
connectionCount: 0,
|
|
3505
|
+
lastShutdownAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3506
|
+
updatedAt: Date.now()
|
|
3507
|
+
});
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
let runtimeHandle = runtimeState;
|
|
3511
|
+
if (!runtimeHandle) {
|
|
3512
|
+
runtimeHandle = this.createSocketServerRuntimeHandleFromContext(input);
|
|
3513
|
+
setRuntimeState(runtimeHandle);
|
|
3514
|
+
}
|
|
3515
|
+
const profile = String(input.__securityProfile ?? state.securityProfile ?? "medium");
|
|
3516
|
+
const networkType = String(input.__networkType ?? state.networkType ?? "internal");
|
|
3517
|
+
const schedulePatch = (patch) => {
|
|
3518
|
+
CadenzaService.emit("meta.socket_server.session_patch_requested", {
|
|
3519
|
+
serverKey,
|
|
3520
|
+
patch
|
|
3521
|
+
});
|
|
3522
|
+
};
|
|
3523
|
+
if (runtimeHandle.initialized) {
|
|
3524
|
+
schedulePatch({
|
|
3525
|
+
status: "active",
|
|
3526
|
+
useSocket: true,
|
|
3527
|
+
securityProfile: profile,
|
|
3528
|
+
networkType,
|
|
3529
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3530
|
+
lastStartedAt: state.lastStartedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3531
|
+
});
|
|
3532
|
+
return;
|
|
3533
|
+
}
|
|
3534
|
+
const server = runtimeHandle.server;
|
|
3535
|
+
runtimeHandle.initialized = true;
|
|
3536
|
+
setState({
|
|
3537
|
+
...state,
|
|
3538
|
+
serverKey,
|
|
3539
|
+
useSocket: true,
|
|
3540
|
+
status: "active",
|
|
3541
|
+
securityProfile: profile,
|
|
3542
|
+
networkType,
|
|
3543
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3544
|
+
lastStartedAt: state.lastStartedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3545
|
+
updatedAt: Date.now()
|
|
3310
3546
|
});
|
|
3311
|
-
const profile = ctx.__securityProfile ?? "medium";
|
|
3312
3547
|
server.use((socket, next) => {
|
|
3313
3548
|
const origin = socket?.handshake?.headers?.origin;
|
|
3314
3549
|
const allowedOrigins = ["*"];
|
|
3315
|
-
const networkType = ctx.__networkType ?? "internal";
|
|
3316
3550
|
let effectiveOrigin = origin || "unknown";
|
|
3317
3551
|
if (networkType === "internal") effectiveOrigin = "internal";
|
|
3318
3552
|
if (profile !== "low" && !allowedOrigins.includes(effectiveOrigin) && !allowedOrigins.includes("*")) {
|
|
@@ -3323,7 +3557,9 @@ var SocketController = class _SocketController {
|
|
|
3323
3557
|
medium: { points: 1e4, duration: 10 },
|
|
3324
3558
|
high: { points: 1e3, duration: 60, blockDuration: 300 }
|
|
3325
3559
|
};
|
|
3326
|
-
const limiter = new RateLimiterMemory2(
|
|
3560
|
+
const limiter = new RateLimiterMemory2(
|
|
3561
|
+
limiterOptions[profile] ?? limiterOptions.medium
|
|
3562
|
+
);
|
|
3327
3563
|
const clientKey = socket?.handshake?.address || "unknown";
|
|
3328
3564
|
socket.use((packet, packetNext) => {
|
|
3329
3565
|
limiter.consume(clientKey).then(() => packetNext()).catch((rej) => {
|
|
@@ -3358,116 +3594,111 @@ var SocketController = class _SocketController {
|
|
|
3358
3594
|
});
|
|
3359
3595
|
next();
|
|
3360
3596
|
});
|
|
3361
|
-
if (!server) {
|
|
3362
|
-
CadenzaService.log("Socket setup error: No server", {}, "error");
|
|
3363
|
-
return { ...ctx, __error: "No server", errored: true };
|
|
3364
|
-
}
|
|
3365
3597
|
server.on("connection", (ws) => {
|
|
3598
|
+
runtimeHandle.connectedSocketIds.add(ws.id);
|
|
3599
|
+
schedulePatch({
|
|
3600
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3601
|
+
lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3602
|
+
status: "active"
|
|
3603
|
+
});
|
|
3366
3604
|
try {
|
|
3367
|
-
ws.on(
|
|
3368
|
-
"
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
if (ctx3.__routineExecId) {
|
|
3388
|
-
emit(
|
|
3389
|
-
`meta.socket_client.transmitted:${ctx3.__routineExecId}`,
|
|
3390
|
-
{}
|
|
3391
|
-
);
|
|
3392
|
-
}
|
|
3605
|
+
ws.on("handshake", (ctx, callback) => {
|
|
3606
|
+
CadenzaService.log("SocketServer: New connection", {
|
|
3607
|
+
...ctx,
|
|
3608
|
+
socketId: ws.id
|
|
3609
|
+
});
|
|
3610
|
+
callback({
|
|
3611
|
+
status: "success",
|
|
3612
|
+
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
3613
|
+
});
|
|
3614
|
+
if (ctx.isFrontend) {
|
|
3615
|
+
const fetchId = `browser:${ctx.serviceInstanceId}`;
|
|
3616
|
+
CadenzaService.createMetaTask(
|
|
3617
|
+
`Transmit signal to ${fetchId}`,
|
|
3618
|
+
(c, emitter) => {
|
|
3619
|
+
if (c.__signalName === void 0) {
|
|
3620
|
+
return;
|
|
3621
|
+
}
|
|
3622
|
+
ws.emit("signal", c);
|
|
3623
|
+
if (c.__routineExecId) {
|
|
3624
|
+
emitter(`meta.socket_client.transmitted:${c.__routineExecId}`, {});
|
|
3393
3625
|
}
|
|
3394
|
-
).doOn(
|
|
3395
|
-
`meta.service_registry.selected_instance_for_socket:${fetchId}`
|
|
3396
|
-
).attachSignal("meta.socket_client.transmitted");
|
|
3397
|
-
}
|
|
3398
|
-
CadenzaService.emit("meta.socket.handshake", ctx2);
|
|
3399
|
-
}
|
|
3400
|
-
);
|
|
3401
|
-
ws.on(
|
|
3402
|
-
"delegation",
|
|
3403
|
-
(ctx2, callback) => {
|
|
3404
|
-
const deputyExecId = ctx2.__metadata.__deputyExecId;
|
|
3405
|
-
CadenzaService.createEphemeralMetaTask(
|
|
3406
|
-
"Resolve delegation",
|
|
3407
|
-
(ctx3) => {
|
|
3408
|
-
callback(ctx3);
|
|
3409
|
-
},
|
|
3410
|
-
"Resolves a delegation request using the provided callback from the client (.emitWithAck())",
|
|
3411
|
-
{ register: false }
|
|
3412
|
-
).doOn(`meta.node.graph_completed:${deputyExecId}`).emits(`meta.socket.delegation_resolved:${deputyExecId}`);
|
|
3413
|
-
CadenzaService.createEphemeralMetaTask(
|
|
3414
|
-
"Delegation progress update",
|
|
3415
|
-
(ctx3) => {
|
|
3416
|
-
if (ctx3.__progress !== void 0)
|
|
3417
|
-
ws.emit("delegation_progress", ctx3);
|
|
3418
3626
|
},
|
|
3419
|
-
"
|
|
3420
|
-
|
|
3421
|
-
once: false,
|
|
3422
|
-
destroyCondition: (ctx3) => ctx3.data.progress === 1 || ctx3.data?.progress === void 0,
|
|
3423
|
-
register: false
|
|
3424
|
-
}
|
|
3425
|
-
).doOn(
|
|
3426
|
-
`meta.node.routine_execution_progress:${deputyExecId}`,
|
|
3427
|
-
`meta.node.graph_completed:${deputyExecId}`
|
|
3428
|
-
).emitsOnFail(`meta.socket.progress_failed:${deputyExecId}`);
|
|
3429
|
-
CadenzaService.emit("meta.socket.delegation_requested", {
|
|
3430
|
-
...ctx2,
|
|
3431
|
-
__name: ctx2.__remoteRoutineName
|
|
3432
|
-
});
|
|
3627
|
+
"Transmit frontend bound signal through active websocket."
|
|
3628
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
3433
3629
|
}
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3630
|
+
CadenzaService.emit("meta.socket.handshake", ctx);
|
|
3631
|
+
});
|
|
3632
|
+
ws.on("delegation", (ctx, callback) => {
|
|
3633
|
+
const deputyExecId = ctx.__metadata.__deputyExecId;
|
|
3634
|
+
CadenzaService.createEphemeralMetaTask(
|
|
3635
|
+
"Resolve delegation",
|
|
3636
|
+
(delegationCtx) => {
|
|
3637
|
+
callback(delegationCtx);
|
|
3638
|
+
},
|
|
3639
|
+
"Resolves a delegation request using client callback.",
|
|
3640
|
+
{ register: false }
|
|
3641
|
+
).doOn(`meta.node.graph_completed:${deputyExecId}`).emits(`meta.socket.delegation_resolved:${deputyExecId}`);
|
|
3642
|
+
CadenzaService.createEphemeralMetaTask(
|
|
3643
|
+
"Delegation progress update",
|
|
3644
|
+
(progressCtx) => {
|
|
3645
|
+
if (progressCtx.__progress !== void 0) {
|
|
3646
|
+
ws.emit("delegation_progress", progressCtx);
|
|
3647
|
+
}
|
|
3648
|
+
},
|
|
3649
|
+
"Updates delegation progress to client.",
|
|
3650
|
+
{
|
|
3651
|
+
once: false,
|
|
3652
|
+
destroyCondition: (progressCtx) => progressCtx.data.progress === 1 || progressCtx.data?.progress === void 0,
|
|
3653
|
+
register: false
|
|
3455
3654
|
}
|
|
3655
|
+
).doOn(
|
|
3656
|
+
`meta.node.routine_execution_progress:${deputyExecId}`,
|
|
3657
|
+
`meta.node.graph_completed:${deputyExecId}`
|
|
3658
|
+
).emitsOnFail(`meta.socket.progress_failed:${deputyExecId}`);
|
|
3659
|
+
CadenzaService.emit("meta.socket.delegation_requested", {
|
|
3660
|
+
...ctx,
|
|
3661
|
+
__name: ctx.__remoteRoutineName
|
|
3662
|
+
});
|
|
3663
|
+
});
|
|
3664
|
+
ws.on("signal", (ctx, callback) => {
|
|
3665
|
+
if (CadenzaService.signalBroker.listObservedSignals().includes(ctx.__signalName)) {
|
|
3666
|
+
callback({
|
|
3667
|
+
__status: "success",
|
|
3668
|
+
__signalName: ctx.__signalName
|
|
3669
|
+
});
|
|
3670
|
+
CadenzaService.emit(ctx.__signalName, ctx);
|
|
3671
|
+
} else {
|
|
3672
|
+
CadenzaService.log(
|
|
3673
|
+
`No such signal ${ctx.__signalName} on ${ctx.__serviceName}`,
|
|
3674
|
+
"warning"
|
|
3675
|
+
);
|
|
3676
|
+
callback({
|
|
3677
|
+
...ctx,
|
|
3678
|
+
__status: "error",
|
|
3679
|
+
__error: `No such signal: ${ctx.__signalName}`,
|
|
3680
|
+
errored: true
|
|
3681
|
+
});
|
|
3456
3682
|
}
|
|
3457
|
-
);
|
|
3683
|
+
});
|
|
3458
3684
|
ws.on(
|
|
3459
3685
|
"status_check",
|
|
3460
|
-
(
|
|
3686
|
+
(ctx, callback) => {
|
|
3461
3687
|
CadenzaService.createEphemeralMetaTask(
|
|
3462
3688
|
"Resolve status check",
|
|
3463
3689
|
callback,
|
|
3464
3690
|
"Resolves a status check request",
|
|
3465
3691
|
{ register: false }
|
|
3466
3692
|
).doAfter(CadenzaService.serviceRegistry.getStatusTask);
|
|
3467
|
-
CadenzaService.emit("meta.socket.status_check_requested",
|
|
3693
|
+
CadenzaService.emit("meta.socket.status_check_requested", ctx);
|
|
3468
3694
|
}
|
|
3469
3695
|
);
|
|
3470
3696
|
ws.on("disconnect", () => {
|
|
3697
|
+
runtimeHandle.connectedSocketIds.delete(ws.id);
|
|
3698
|
+
schedulePatch({
|
|
3699
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3700
|
+
lastDisconnectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3701
|
+
});
|
|
3471
3702
|
CadenzaService.log(
|
|
3472
3703
|
"Socket client disconnected",
|
|
3473
3704
|
{ socketId: ws.id },
|
|
@@ -3477,514 +3708,846 @@ var SocketController = class _SocketController {
|
|
|
3477
3708
|
__wsId: ws.id
|
|
3478
3709
|
});
|
|
3479
3710
|
});
|
|
3480
|
-
} catch (
|
|
3711
|
+
} catch (error) {
|
|
3481
3712
|
CadenzaService.log(
|
|
3482
3713
|
"SocketServer: Error in socket event",
|
|
3483
|
-
{ error
|
|
3714
|
+
{ error },
|
|
3484
3715
|
"error"
|
|
3485
3716
|
);
|
|
3486
3717
|
}
|
|
3487
3718
|
CadenzaService.emit("meta.socket.connected", { __wsId: ws.id });
|
|
3488
3719
|
});
|
|
3489
|
-
CadenzaService.createMetaTask(
|
|
3490
|
-
|
|
3491
|
-
(
|
|
3720
|
+
runtimeHandle.broadcastStatusTask = CadenzaService.createMetaTask(
|
|
3721
|
+
`Broadcast status ${serverKey}`,
|
|
3722
|
+
(ctx) => server.emit("status_update", ctx),
|
|
3492
3723
|
"Broadcasts the status of the server to all clients"
|
|
3493
3724
|
).doOn("meta.service.updated");
|
|
3494
|
-
CadenzaService.createMetaTask(
|
|
3495
|
-
|
|
3496
|
-
() =>
|
|
3725
|
+
runtimeHandle.shutdownTask = CadenzaService.createMetaTask(
|
|
3726
|
+
`Shutdown SocketServer ${serverKey}`,
|
|
3727
|
+
async () => {
|
|
3728
|
+
this.destroySocketServerRuntimeHandle(runtimeHandle);
|
|
3729
|
+
CadenzaService.emit("meta.socket_server.runtime_clear_requested", {
|
|
3730
|
+
serverKey
|
|
3731
|
+
});
|
|
3732
|
+
CadenzaService.emit("meta.socket_server.session_patch_requested", {
|
|
3733
|
+
serverKey,
|
|
3734
|
+
patch: {
|
|
3735
|
+
useSocket: false,
|
|
3736
|
+
status: "shutdown",
|
|
3737
|
+
connectionCount: 0,
|
|
3738
|
+
lastShutdownAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3739
|
+
}
|
|
3740
|
+
});
|
|
3741
|
+
},
|
|
3497
3742
|
"Shuts down the socket server"
|
|
3498
3743
|
).doOn("meta.socket_server_shutdown_requested").emits("meta.socket.shutdown");
|
|
3499
|
-
return
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3744
|
+
return true;
|
|
3745
|
+
},
|
|
3746
|
+
{ mode: "write" }
|
|
3747
|
+
),
|
|
3748
|
+
"Initializes socket server runtime through actor state."
|
|
3749
|
+
);
|
|
3750
|
+
setupSocketServerTask.doOn("global.meta.rest.network_configured");
|
|
3751
|
+
}
|
|
3752
|
+
registerSocketClientTasks() {
|
|
3753
|
+
CadenzaService.createThrottledMetaTask(
|
|
3754
|
+
"SocketClientActor.ApplySessionOperation",
|
|
3755
|
+
this.socketClientActor.task(
|
|
3756
|
+
({ state, input, setState }) => {
|
|
3757
|
+
const operation = String(
|
|
3758
|
+
input.operation ?? "transmit"
|
|
3759
|
+
);
|
|
3760
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3761
|
+
let next = {
|
|
3762
|
+
...state,
|
|
3763
|
+
...patch,
|
|
3764
|
+
communicationTypes: patch.communicationTypes !== void 0 ? this.normalizeCommunicationTypes(patch.communicationTypes) : state.communicationTypes,
|
|
3765
|
+
updatedAt: Date.now()
|
|
3766
|
+
};
|
|
3767
|
+
if (input.serviceName !== void 0) {
|
|
3768
|
+
next.serviceName = String(input.serviceName);
|
|
3769
|
+
}
|
|
3770
|
+
if (input.serviceAddress !== void 0) {
|
|
3771
|
+
next.serviceAddress = String(input.serviceAddress);
|
|
3772
|
+
}
|
|
3773
|
+
if (input.serviceInstanceId !== void 0) {
|
|
3774
|
+
next.serviceInstanceId = String(input.serviceInstanceId);
|
|
3775
|
+
}
|
|
3776
|
+
if (input.protocol !== void 0) {
|
|
3777
|
+
next.protocol = String(input.protocol);
|
|
3778
|
+
}
|
|
3779
|
+
if (input.url !== void 0) {
|
|
3780
|
+
next.url = String(input.url);
|
|
3781
|
+
}
|
|
3782
|
+
if (input.servicePort !== void 0) {
|
|
3783
|
+
next.servicePort = Number(input.servicePort);
|
|
3784
|
+
}
|
|
3785
|
+
if (input.fetchId !== void 0) {
|
|
3786
|
+
next.fetchId = String(input.fetchId);
|
|
3787
|
+
}
|
|
3788
|
+
if (operation === "connect") {
|
|
3789
|
+
next.destroyed = false;
|
|
3790
|
+
} else if (operation === "handshake") {
|
|
3791
|
+
next.destroyed = false;
|
|
3792
|
+
next.connected = patch.connected ?? true;
|
|
3793
|
+
next.handshake = patch.handshake ?? true;
|
|
3794
|
+
} else if (operation === "shutdown") {
|
|
3795
|
+
next.connected = false;
|
|
3796
|
+
next.handshake = false;
|
|
3797
|
+
next.destroyed = true;
|
|
3798
|
+
next.pendingDelegations = 0;
|
|
3799
|
+
next.pendingTimers = 0;
|
|
3800
|
+
}
|
|
3801
|
+
setState(next);
|
|
3802
|
+
return next;
|
|
3803
|
+
},
|
|
3804
|
+
{ mode: "write" }
|
|
3805
|
+
),
|
|
3806
|
+
(context) => String(this.resolveSocketClientFetchId(context ?? {}) ?? "default"),
|
|
3807
|
+
"Applies socket client session operation patch in actor durable state."
|
|
3808
|
+
).doOn("meta.socket_client.session_operation_requested");
|
|
3809
|
+
CadenzaService.createMetaTask(
|
|
3810
|
+
"SocketClientActor.ClearRuntime",
|
|
3811
|
+
this.socketClientActor.task(
|
|
3812
|
+
({ setRuntimeState }) => {
|
|
3813
|
+
setRuntimeState(null);
|
|
3814
|
+
},
|
|
3815
|
+
{ mode: "write" }
|
|
3816
|
+
),
|
|
3817
|
+
"Clears socket client runtime handle."
|
|
3818
|
+
).doOn("meta.socket_client.runtime_clear_requested");
|
|
3504
3819
|
CadenzaService.createMetaTask(
|
|
3505
3820
|
"Connect to socket server",
|
|
3506
|
-
(
|
|
3507
|
-
|
|
3508
|
-
serviceInstanceId
|
|
3509
|
-
communicationTypes
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
fetchId,
|
|
3521
|
-
serviceName,
|
|
3522
|
-
URL
|
|
3523
|
-
);
|
|
3524
|
-
socketDiagnostics.destroyed = false;
|
|
3525
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3526
|
-
let handshake = false;
|
|
3527
|
-
let errorCount = 0;
|
|
3528
|
-
const ERROR_LIMIT = 5;
|
|
3529
|
-
if (CadenzaService.get(`Socket handshake with ${URL}`)) {
|
|
3530
|
-
console.error("Socket client already exists", URL);
|
|
3531
|
-
return;
|
|
3532
|
-
}
|
|
3533
|
-
const pendingDelegationIds = /* @__PURE__ */ new Set();
|
|
3534
|
-
const pendingTimers = /* @__PURE__ */ new Set();
|
|
3535
|
-
const syncPendingCounts = () => {
|
|
3536
|
-
socketDiagnostics.pendingDelegations = pendingDelegationIds.size;
|
|
3537
|
-
socketDiagnostics.pendingTimers = pendingTimers.size;
|
|
3538
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3539
|
-
};
|
|
3540
|
-
let handshakeTask = null;
|
|
3541
|
-
let emitWhenReady = null;
|
|
3542
|
-
let transmitTask = null;
|
|
3543
|
-
let delegateTask = null;
|
|
3544
|
-
let socket = null;
|
|
3545
|
-
socket = io(URL, {
|
|
3546
|
-
reconnection: true,
|
|
3547
|
-
reconnectionAttempts: 5,
|
|
3548
|
-
reconnectionDelay: 2e3,
|
|
3549
|
-
reconnectionDelayMax: 1e4,
|
|
3550
|
-
randomizationFactor: 0.5,
|
|
3551
|
-
transports: ["websocket"],
|
|
3552
|
-
autoConnect: false
|
|
3553
|
-
});
|
|
3554
|
-
emitWhenReady = (event, data, timeoutMs = 6e4, ack) => {
|
|
3555
|
-
return new Promise((resolve) => {
|
|
3556
|
-
const resolveWithError = (errorMessage, fallbackError) => {
|
|
3557
|
-
resolve({
|
|
3558
|
-
...data,
|
|
3559
|
-
errored: true,
|
|
3560
|
-
__error: errorMessage,
|
|
3561
|
-
error: fallbackError instanceof Error ? fallbackError.message : errorMessage,
|
|
3562
|
-
socketId: socket?.id,
|
|
3821
|
+
this.socketClientActor.task(
|
|
3822
|
+
({ state, runtimeState, input, setState, setRuntimeState, emit }) => {
|
|
3823
|
+
const serviceInstanceId = String(input.serviceInstanceId ?? "");
|
|
3824
|
+
const communicationTypes = this.normalizeCommunicationTypes(
|
|
3825
|
+
input.communicationTypes
|
|
3826
|
+
);
|
|
3827
|
+
const serviceName = String(input.serviceName ?? "");
|
|
3828
|
+
const serviceAddress = String(input.serviceAddress ?? "");
|
|
3829
|
+
const protocol = String(input.protocol ?? "http");
|
|
3830
|
+
const normalizedPort = this.resolveServicePort(protocol, input.servicePort);
|
|
3831
|
+
if (!serviceAddress || !normalizedPort) {
|
|
3832
|
+
CadenzaService.log(
|
|
3833
|
+
"Socket client setup skipped due to missing address/port",
|
|
3834
|
+
{
|
|
3563
3835
|
serviceName,
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3836
|
+
serviceAddress,
|
|
3837
|
+
servicePort: input.servicePort,
|
|
3838
|
+
protocol
|
|
3839
|
+
},
|
|
3840
|
+
"warning"
|
|
3841
|
+
);
|
|
3842
|
+
return false;
|
|
3843
|
+
}
|
|
3844
|
+
const socketProtocol = protocol === "https" ? "wss" : "ws";
|
|
3845
|
+
const url = `${socketProtocol}://${serviceAddress}:${normalizedPort}`;
|
|
3846
|
+
const fetchId = `${serviceAddress}_${normalizedPort}`;
|
|
3847
|
+
const applySessionOperation = (operation, patch = {}) => {
|
|
3848
|
+
CadenzaService.emit("meta.socket_client.session_operation_requested", {
|
|
3849
|
+
fetchId,
|
|
3850
|
+
operation,
|
|
3851
|
+
patch,
|
|
3852
|
+
serviceInstanceId,
|
|
3853
|
+
communicationTypes,
|
|
3854
|
+
serviceName,
|
|
3855
|
+
serviceAddress,
|
|
3856
|
+
servicePort: normalizedPort,
|
|
3857
|
+
protocol,
|
|
3858
|
+
url
|
|
3859
|
+
});
|
|
3860
|
+
};
|
|
3861
|
+
const upsertDiagnostics = (patch, error) => {
|
|
3862
|
+
CadenzaService.emit("meta.socket_client.diagnostics_upsert_requested", {
|
|
3863
|
+
fetchId,
|
|
3864
|
+
serviceName,
|
|
3865
|
+
url,
|
|
3866
|
+
patch,
|
|
3867
|
+
error
|
|
3868
|
+
});
|
|
3869
|
+
};
|
|
3870
|
+
setState({
|
|
3871
|
+
...state,
|
|
3872
|
+
fetchId,
|
|
3873
|
+
serviceInstanceId,
|
|
3874
|
+
communicationTypes,
|
|
3875
|
+
serviceName,
|
|
3876
|
+
serviceAddress,
|
|
3877
|
+
servicePort: normalizedPort,
|
|
3878
|
+
protocol,
|
|
3879
|
+
url,
|
|
3880
|
+
destroyed: false,
|
|
3881
|
+
updatedAt: Date.now()
|
|
3882
|
+
});
|
|
3883
|
+
let runtimeHandle = runtimeState;
|
|
3884
|
+
if (!runtimeHandle || runtimeHandle.url !== url) {
|
|
3885
|
+
this.destroySocketClientRuntimeHandle(runtimeHandle);
|
|
3886
|
+
runtimeHandle = this.createSocketClientRuntimeHandle(url);
|
|
3887
|
+
setRuntimeState(runtimeHandle);
|
|
3888
|
+
}
|
|
3889
|
+
upsertDiagnostics({
|
|
3890
|
+
destroyed: false,
|
|
3891
|
+
connected: false,
|
|
3892
|
+
handshake: false,
|
|
3893
|
+
socketId: runtimeHandle.socket.id ?? null
|
|
3894
|
+
});
|
|
3895
|
+
applySessionOperation("connect", {
|
|
3896
|
+
destroyed: false,
|
|
3897
|
+
connected: false,
|
|
3898
|
+
handshake: false,
|
|
3899
|
+
socketId: runtimeHandle.socket.id ?? null,
|
|
3900
|
+
pendingDelegations: runtimeHandle.pendingDelegationIds.size,
|
|
3901
|
+
pendingTimers: runtimeHandle.pendingTimers.size,
|
|
3902
|
+
errorCount: runtimeHandle.errorCount
|
|
3903
|
+
});
|
|
3904
|
+
if (runtimeHandle.initialized) {
|
|
3905
|
+
return true;
|
|
3906
|
+
}
|
|
3907
|
+
runtimeHandle.initialized = true;
|
|
3908
|
+
runtimeHandle.handshake = false;
|
|
3909
|
+
runtimeHandle.errorCount = 0;
|
|
3910
|
+
const syncPendingCounts = () => {
|
|
3911
|
+
const pendingDelegations = runtimeHandle.pendingDelegationIds.size;
|
|
3912
|
+
const pendingTimers = runtimeHandle.pendingTimers.size;
|
|
3913
|
+
upsertDiagnostics({
|
|
3914
|
+
pendingDelegations,
|
|
3915
|
+
pendingTimers
|
|
3916
|
+
});
|
|
3917
|
+
applySessionOperation("delegate", {
|
|
3918
|
+
pendingDelegations,
|
|
3919
|
+
pendingTimers
|
|
3920
|
+
});
|
|
3921
|
+
};
|
|
3922
|
+
runtimeHandle.emitWhenReady = (event, data, timeoutMs = 6e4, ack) => {
|
|
3923
|
+
return new Promise((resolve) => {
|
|
3924
|
+
const parsedTimeout = Number(timeoutMs);
|
|
3925
|
+
const normalizedTimeoutMs = Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? Math.trunc(parsedTimeout) : 6e4;
|
|
3926
|
+
let timer = null;
|
|
3927
|
+
let settled = false;
|
|
3928
|
+
const clearPendingTimer = () => {
|
|
3929
|
+
if (!timer) {
|
|
3930
|
+
return;
|
|
3581
3931
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
)
|
|
3589
|
-
|
|
3590
|
-
|
|
3932
|
+
clearTimeout(timer);
|
|
3933
|
+
runtimeHandle.pendingTimers.delete(timer);
|
|
3934
|
+
syncPendingCounts();
|
|
3935
|
+
timer = null;
|
|
3936
|
+
};
|
|
3937
|
+
const settle = (response) => {
|
|
3938
|
+
if (settled) {
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3941
|
+
settled = true;
|
|
3942
|
+
clearPendingTimer();
|
|
3943
|
+
if (ack) ack(response);
|
|
3944
|
+
resolve(response);
|
|
3945
|
+
};
|
|
3946
|
+
const resolveWithError = (errorMessage, fallbackError) => {
|
|
3947
|
+
settle({
|
|
3948
|
+
...data,
|
|
3949
|
+
errored: true,
|
|
3950
|
+
__error: errorMessage,
|
|
3951
|
+
error: fallbackError instanceof Error ? fallbackError.message : errorMessage,
|
|
3952
|
+
socketId: runtimeHandle.socket.id,
|
|
3591
3953
|
serviceName,
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3954
|
+
url
|
|
3955
|
+
});
|
|
3956
|
+
};
|
|
3957
|
+
const tryEmit = async () => {
|
|
3958
|
+
const waitResult = await waitForSocketConnection(
|
|
3959
|
+
runtimeHandle.socket,
|
|
3960
|
+
normalizedTimeoutMs + 10,
|
|
3961
|
+
(reason, error) => {
|
|
3962
|
+
if (reason === "connect_timeout") {
|
|
3963
|
+
return `Socket connect timed out before '${event}'`;
|
|
3964
|
+
}
|
|
3965
|
+
if (reason === "connect_error") {
|
|
3966
|
+
const errMessage = error instanceof Error ? error.message : String(error);
|
|
3967
|
+
return `Socket connect error before '${event}': ${errMessage}`;
|
|
3968
|
+
}
|
|
3969
|
+
return `Socket disconnected before '${event}'`;
|
|
3605
3970
|
}
|
|
3971
|
+
);
|
|
3972
|
+
if (!waitResult.ok) {
|
|
3606
3973
|
CadenzaService.log(
|
|
3607
|
-
|
|
3608
|
-
{
|
|
3974
|
+
waitResult.error,
|
|
3975
|
+
{
|
|
3976
|
+
socketId: runtimeHandle.socket.id,
|
|
3977
|
+
serviceName,
|
|
3978
|
+
url,
|
|
3979
|
+
event
|
|
3980
|
+
},
|
|
3609
3981
|
"error"
|
|
3610
3982
|
);
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
URL,
|
|
3615
|
-
`Socket event '${event}' timed out`
|
|
3616
|
-
);
|
|
3617
|
-
resolveWithError(`Socket event '${event}' timed out`);
|
|
3618
|
-
}, timeoutMs + 10);
|
|
3619
|
-
pendingTimers.add(timer);
|
|
3620
|
-
syncPendingCounts();
|
|
3621
|
-
}
|
|
3622
|
-
const connectedSocket = socket;
|
|
3623
|
-
if (!connectedSocket) {
|
|
3624
|
-
resolveWithError(
|
|
3625
|
-
`Socket unavailable before emitting '${event}'`
|
|
3626
|
-
);
|
|
3627
|
-
return;
|
|
3628
|
-
}
|
|
3629
|
-
connectedSocket.timeout(timeoutMs).emit(event, data, (err, response) => {
|
|
3630
|
-
if (timer) {
|
|
3631
|
-
clearTimeout(timer);
|
|
3632
|
-
pendingTimers.delete(timer);
|
|
3633
|
-
syncPendingCounts();
|
|
3634
|
-
timer = null;
|
|
3983
|
+
upsertDiagnostics({}, waitResult.error);
|
|
3984
|
+
resolveWithError(waitResult.error);
|
|
3985
|
+
return;
|
|
3635
3986
|
}
|
|
3636
|
-
|
|
3987
|
+
timer = setTimeout(() => {
|
|
3988
|
+
if (settled) {
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
clearPendingTimer();
|
|
3992
|
+
const message = `Socket event '${event}' timed out`;
|
|
3637
3993
|
CadenzaService.log(
|
|
3638
|
-
|
|
3994
|
+
message,
|
|
3995
|
+
{ socketId: runtimeHandle.socket.id, serviceName, url },
|
|
3996
|
+
"error"
|
|
3997
|
+
);
|
|
3998
|
+
upsertDiagnostics(
|
|
3639
3999
|
{
|
|
3640
|
-
|
|
3641
|
-
error: err.message,
|
|
3642
|
-
socketId: socket?.id,
|
|
3643
|
-
serviceName
|
|
4000
|
+
lastHandshakeError: message
|
|
3644
4001
|
},
|
|
3645
|
-
|
|
4002
|
+
message
|
|
3646
4003
|
);
|
|
3647
|
-
|
|
3648
|
-
|
|
4004
|
+
applySessionOperation("transmit", {
|
|
4005
|
+
lastHandshakeError: message
|
|
4006
|
+
});
|
|
4007
|
+
resolveWithError(message);
|
|
4008
|
+
}, normalizedTimeoutMs + 10);
|
|
4009
|
+
runtimeHandle.pendingTimers.add(timer);
|
|
4010
|
+
syncPendingCounts();
|
|
4011
|
+
runtimeHandle.socket.timeout(normalizedTimeoutMs).emit(event, data, (err, response) => {
|
|
4012
|
+
if (err) {
|
|
4013
|
+
CadenzaService.log(
|
|
4014
|
+
"Socket timeout.",
|
|
4015
|
+
{
|
|
4016
|
+
event,
|
|
4017
|
+
error: err.message,
|
|
4018
|
+
socketId: runtimeHandle.socket.id,
|
|
4019
|
+
serviceName
|
|
4020
|
+
},
|
|
4021
|
+
"warning"
|
|
4022
|
+
);
|
|
4023
|
+
upsertDiagnostics(
|
|
4024
|
+
{
|
|
4025
|
+
lastHandshakeError: err.message
|
|
4026
|
+
},
|
|
4027
|
+
err
|
|
4028
|
+
);
|
|
4029
|
+
applySessionOperation("transmit", {
|
|
4030
|
+
lastHandshakeError: err.message
|
|
4031
|
+
});
|
|
4032
|
+
response = {
|
|
4033
|
+
__error: `Timeout error: ${err}`,
|
|
4034
|
+
errored: true,
|
|
4035
|
+
...data
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
4038
|
+
settle(response);
|
|
4039
|
+
});
|
|
4040
|
+
};
|
|
4041
|
+
void tryEmit().catch((error) => {
|
|
4042
|
+
CadenzaService.log(
|
|
4043
|
+
"Socket emit failed unexpectedly",
|
|
4044
|
+
{
|
|
4045
|
+
event,
|
|
4046
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4047
|
+
socketId: runtimeHandle.socket.id,
|
|
3649
4048
|
serviceName,
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
4049
|
+
url
|
|
4050
|
+
},
|
|
4051
|
+
"error"
|
|
4052
|
+
);
|
|
4053
|
+
const message = `Socket event '${event}' failed`;
|
|
4054
|
+
upsertDiagnostics(
|
|
4055
|
+
{
|
|
4056
|
+
lastHandshakeError: error instanceof Error ? error.message : String(error)
|
|
4057
|
+
},
|
|
4058
|
+
error
|
|
4059
|
+
);
|
|
4060
|
+
applySessionOperation("transmit", {
|
|
4061
|
+
lastHandshakeError: error instanceof Error ? error.message : String(error)
|
|
4062
|
+
});
|
|
4063
|
+
resolveWithError(message, error);
|
|
3662
4064
|
});
|
|
3663
|
-
};
|
|
3664
|
-
|
|
4065
|
+
});
|
|
4066
|
+
};
|
|
4067
|
+
const socket = runtimeHandle.socket;
|
|
4068
|
+
socket.on("connect", () => {
|
|
4069
|
+
if (runtimeHandle.handshake) return;
|
|
4070
|
+
upsertDiagnostics({
|
|
4071
|
+
connected: true,
|
|
4072
|
+
destroyed: false,
|
|
4073
|
+
socketId: socket.id ?? null
|
|
4074
|
+
});
|
|
4075
|
+
applySessionOperation("connect", {
|
|
4076
|
+
connected: true,
|
|
4077
|
+
destroyed: false,
|
|
4078
|
+
socketId: socket.id ?? null
|
|
4079
|
+
});
|
|
4080
|
+
CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, input);
|
|
3665
4081
|
});
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
socketDiagnostics.socketId = socket?.id ?? null;
|
|
3672
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3673
|
-
CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, ctx);
|
|
3674
|
-
});
|
|
3675
|
-
socket.on("delegation_progress", (ctx2) => {
|
|
3676
|
-
CadenzaService.emit(
|
|
3677
|
-
`meta.socket_client.delegation_progress:${ctx2.__metadata.__deputyExecId}`,
|
|
3678
|
-
ctx2
|
|
3679
|
-
);
|
|
3680
|
-
});
|
|
3681
|
-
socket.on("signal", (ctx2) => {
|
|
3682
|
-
if (CadenzaService.signalBroker.listObservedSignals().includes(ctx2.__signalName)) {
|
|
3683
|
-
CadenzaService.emit(ctx2.__signalName, ctx2);
|
|
3684
|
-
}
|
|
3685
|
-
});
|
|
3686
|
-
socket.on("status_update", (status) => {
|
|
3687
|
-
CadenzaService.emit("meta.socket_client.status_received", status);
|
|
3688
|
-
});
|
|
3689
|
-
socket.on("connect_error", (err) => {
|
|
3690
|
-
handshake = false;
|
|
3691
|
-
socketDiagnostics.connected = false;
|
|
3692
|
-
socketDiagnostics.handshake = false;
|
|
3693
|
-
socketDiagnostics.connectErrors++;
|
|
3694
|
-
socketDiagnostics.lastHandshakeError = err.message;
|
|
3695
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3696
|
-
this.recordSocketClientError(fetchId, serviceName, URL, err);
|
|
3697
|
-
CadenzaService.log(
|
|
3698
|
-
"Socket connect error",
|
|
3699
|
-
{ error: err.message, serviceName, socketId: socket?.id, URL },
|
|
3700
|
-
"error"
|
|
3701
|
-
);
|
|
3702
|
-
CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
|
|
3703
|
-
});
|
|
3704
|
-
socket.on("reconnect_attempt", (attempt) => {
|
|
3705
|
-
socketDiagnostics.reconnectAttempts = Math.max(
|
|
3706
|
-
socketDiagnostics.reconnectAttempts,
|
|
3707
|
-
attempt
|
|
3708
|
-
);
|
|
3709
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3710
|
-
CadenzaService.log(`Reconnect attempt: ${attempt}`);
|
|
3711
|
-
});
|
|
3712
|
-
socket.on("reconnect", (attempt) => {
|
|
3713
|
-
socketDiagnostics.connected = true;
|
|
3714
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3715
|
-
CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
|
|
3716
|
-
socketId: socket?.id,
|
|
3717
|
-
URL,
|
|
3718
|
-
serviceName
|
|
4082
|
+
socket.on("delegation_progress", (delegationCtx) => {
|
|
4083
|
+
CadenzaService.emit(
|
|
4084
|
+
`meta.socket_client.delegation_progress:${delegationCtx.__metadata.__deputyExecId}`,
|
|
4085
|
+
delegationCtx
|
|
4086
|
+
);
|
|
3719
4087
|
});
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
socketDiagnostics.handshake = false;
|
|
3725
|
-
socketDiagnostics.reconnectErrors++;
|
|
3726
|
-
socketDiagnostics.lastHandshakeError = err.message;
|
|
3727
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3728
|
-
this.recordSocketClientError(fetchId, serviceName, URL, err);
|
|
3729
|
-
CadenzaService.log(
|
|
3730
|
-
"Socket reconnect failed.",
|
|
3731
|
-
{ error: err.message, serviceName, URL, socketId: socket?.id },
|
|
3732
|
-
"warning"
|
|
3733
|
-
);
|
|
3734
|
-
});
|
|
3735
|
-
socket.on("error", (err) => {
|
|
3736
|
-
errorCount++;
|
|
3737
|
-
socketDiagnostics.socketErrors++;
|
|
3738
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3739
|
-
this.recordSocketClientError(fetchId, serviceName, URL, err);
|
|
3740
|
-
CadenzaService.log(
|
|
3741
|
-
"Socket error",
|
|
3742
|
-
{ error: err, socketId: socket?.id, URL, serviceName },
|
|
3743
|
-
"error"
|
|
3744
|
-
);
|
|
3745
|
-
CadenzaService.emit("meta.socket_client.error", err);
|
|
3746
|
-
});
|
|
3747
|
-
socket.on("disconnect", () => {
|
|
3748
|
-
const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3749
|
-
socketDiagnostics.connected = false;
|
|
3750
|
-
socketDiagnostics.handshake = false;
|
|
3751
|
-
socketDiagnostics.lastDisconnectAt = disconnectedAt;
|
|
3752
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3753
|
-
CadenzaService.log(
|
|
3754
|
-
"Socket disconnected.",
|
|
3755
|
-
{ URL, serviceName, socketId: socket?.id },
|
|
3756
|
-
"warning"
|
|
3757
|
-
);
|
|
3758
|
-
CadenzaService.emit(`meta.socket_client.disconnected:${fetchId}`, {
|
|
3759
|
-
serviceName,
|
|
3760
|
-
serviceAddress,
|
|
3761
|
-
servicePort
|
|
4088
|
+
socket.on("signal", (signalCtx) => {
|
|
4089
|
+
if (CadenzaService.signalBroker.listObservedSignals().includes(signalCtx.__signalName)) {
|
|
4090
|
+
CadenzaService.emit(signalCtx.__signalName, signalCtx);
|
|
4091
|
+
}
|
|
3762
4092
|
});
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
if (handshake) return;
|
|
3770
|
-
handshake = true;
|
|
3771
|
-
socketDiagnostics.handshake = true;
|
|
3772
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3773
|
-
await emitWhenReady?.(
|
|
3774
|
-
"handshake",
|
|
4093
|
+
socket.on("status_update", (status) => {
|
|
4094
|
+
CadenzaService.emit("meta.socket_client.status_received", status);
|
|
4095
|
+
});
|
|
4096
|
+
socket.on("connect_error", (err) => {
|
|
4097
|
+
runtimeHandle.handshake = false;
|
|
4098
|
+
upsertDiagnostics(
|
|
3775
4099
|
{
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
4100
|
+
connected: false,
|
|
4101
|
+
handshake: false,
|
|
4102
|
+
connectErrors: state.connectErrors + 1,
|
|
4103
|
+
lastHandshakeError: err.message
|
|
3780
4104
|
},
|
|
3781
|
-
|
|
3782
|
-
(result) => {
|
|
3783
|
-
if (result.status === "success") {
|
|
3784
|
-
socketDiagnostics.connected = true;
|
|
3785
|
-
socketDiagnostics.handshake = true;
|
|
3786
|
-
socketDiagnostics.lastHandshakeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3787
|
-
socketDiagnostics.lastHandshakeError = null;
|
|
3788
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3789
|
-
CadenzaService.log("Socket client connected", {
|
|
3790
|
-
result,
|
|
3791
|
-
serviceName,
|
|
3792
|
-
socketId: socket?.id,
|
|
3793
|
-
URL
|
|
3794
|
-
});
|
|
3795
|
-
} else {
|
|
3796
|
-
socketDiagnostics.connected = false;
|
|
3797
|
-
socketDiagnostics.handshake = false;
|
|
3798
|
-
socketDiagnostics.lastHandshakeError = result?.__error ?? result?.error ?? "Socket handshake failed";
|
|
3799
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3800
|
-
this.recordSocketClientError(
|
|
3801
|
-
fetchId,
|
|
3802
|
-
serviceName,
|
|
3803
|
-
URL,
|
|
3804
|
-
socketDiagnostics.lastHandshakeError
|
|
3805
|
-
);
|
|
3806
|
-
CadenzaService.log(
|
|
3807
|
-
"Socket handshake failed",
|
|
3808
|
-
{ result, serviceName, socketId: socket?.id, URL },
|
|
3809
|
-
"warning"
|
|
3810
|
-
);
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
4105
|
+
err
|
|
3813
4106
|
);
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
4107
|
+
applySessionOperation("connect", {
|
|
4108
|
+
connected: false,
|
|
4109
|
+
handshake: false,
|
|
4110
|
+
connectErrors: state.connectErrors + 1,
|
|
4111
|
+
lastHandshakeError: err.message
|
|
4112
|
+
});
|
|
4113
|
+
CadenzaService.log(
|
|
4114
|
+
"Socket connect error",
|
|
4115
|
+
{
|
|
4116
|
+
error: err.message,
|
|
4117
|
+
serviceName,
|
|
4118
|
+
socketId: socket.id,
|
|
4119
|
+
url
|
|
4120
|
+
},
|
|
4121
|
+
"error"
|
|
4122
|
+
);
|
|
4123
|
+
CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
|
|
4124
|
+
});
|
|
4125
|
+
socket.on("reconnect_attempt", (attempt) => {
|
|
4126
|
+
upsertDiagnostics({ reconnectAttempts: attempt });
|
|
4127
|
+
applySessionOperation("connect", {
|
|
4128
|
+
reconnectAttempts: attempt
|
|
4129
|
+
});
|
|
4130
|
+
CadenzaService.log(`Reconnect attempt: ${attempt}`);
|
|
4131
|
+
});
|
|
4132
|
+
socket.on("reconnect", (attempt) => {
|
|
4133
|
+
upsertDiagnostics({ connected: true });
|
|
4134
|
+
applySessionOperation("connect", {
|
|
4135
|
+
connected: true
|
|
4136
|
+
});
|
|
4137
|
+
CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
|
|
4138
|
+
socketId: socket.id,
|
|
4139
|
+
url,
|
|
4140
|
+
serviceName
|
|
4141
|
+
});
|
|
4142
|
+
});
|
|
4143
|
+
socket.on("reconnect_error", (err) => {
|
|
4144
|
+
runtimeHandle.handshake = false;
|
|
4145
|
+
upsertDiagnostics(
|
|
4146
|
+
{
|
|
4147
|
+
connected: false,
|
|
4148
|
+
handshake: false,
|
|
4149
|
+
reconnectErrors: state.reconnectErrors + 1,
|
|
4150
|
+
lastHandshakeError: err.message
|
|
4151
|
+
},
|
|
4152
|
+
err
|
|
4153
|
+
);
|
|
4154
|
+
applySessionOperation("connect", {
|
|
4155
|
+
connected: false,
|
|
4156
|
+
handshake: false,
|
|
4157
|
+
reconnectErrors: state.reconnectErrors + 1,
|
|
4158
|
+
lastHandshakeError: err.message
|
|
4159
|
+
});
|
|
4160
|
+
CadenzaService.log(
|
|
4161
|
+
"Socket reconnect failed.",
|
|
4162
|
+
{ error: err.message, serviceName, url, socketId: socket.id },
|
|
4163
|
+
"warning"
|
|
4164
|
+
);
|
|
4165
|
+
});
|
|
4166
|
+
socket.on("error", (err) => {
|
|
4167
|
+
runtimeHandle.errorCount += 1;
|
|
4168
|
+
upsertDiagnostics(
|
|
4169
|
+
{
|
|
4170
|
+
socketErrors: state.socketErrors + 1,
|
|
4171
|
+
lastHandshakeError: this.getErrorMessage(err)
|
|
4172
|
+
},
|
|
4173
|
+
err
|
|
4174
|
+
);
|
|
4175
|
+
applySessionOperation("transmit", {
|
|
4176
|
+
socketErrors: state.socketErrors + 1,
|
|
4177
|
+
errorCount: runtimeHandle.errorCount,
|
|
4178
|
+
lastHandshakeError: this.getErrorMessage(err)
|
|
4179
|
+
});
|
|
4180
|
+
CadenzaService.log(
|
|
4181
|
+
"Socket error",
|
|
4182
|
+
{ error: err, socketId: socket.id, url, serviceName },
|
|
4183
|
+
"error"
|
|
4184
|
+
);
|
|
4185
|
+
CadenzaService.emit("meta.socket_client.error", err);
|
|
4186
|
+
});
|
|
4187
|
+
socket.on("disconnect", () => {
|
|
4188
|
+
const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4189
|
+
upsertDiagnostics({
|
|
4190
|
+
connected: false,
|
|
4191
|
+
handshake: false,
|
|
4192
|
+
lastDisconnectAt: disconnectedAt
|
|
4193
|
+
});
|
|
4194
|
+
applySessionOperation("connect", {
|
|
4195
|
+
connected: false,
|
|
4196
|
+
handshake: false,
|
|
4197
|
+
lastDisconnectAt: disconnectedAt
|
|
4198
|
+
});
|
|
4199
|
+
CadenzaService.log(
|
|
4200
|
+
"Socket disconnected.",
|
|
4201
|
+
{ url, serviceName, socketId: socket.id },
|
|
4202
|
+
"warning"
|
|
4203
|
+
);
|
|
4204
|
+
CadenzaService.emit(`meta.socket_client.disconnected:${fetchId}`, {
|
|
4205
|
+
serviceName,
|
|
4206
|
+
serviceAddress,
|
|
4207
|
+
servicePort: normalizedPort
|
|
4208
|
+
});
|
|
4209
|
+
runtimeHandle.handshake = false;
|
|
4210
|
+
});
|
|
4211
|
+
socket.connect();
|
|
4212
|
+
runtimeHandle.handshakeTask = CadenzaService.createMetaTask(
|
|
4213
|
+
`Socket handshake with ${url}`,
|
|
4214
|
+
async (_ctx, emitter) => {
|
|
4215
|
+
if (runtimeHandle.handshake) return;
|
|
4216
|
+
runtimeHandle.handshake = true;
|
|
4217
|
+
upsertDiagnostics({
|
|
4218
|
+
handshake: true
|
|
4219
|
+
});
|
|
4220
|
+
applySessionOperation("handshake", {
|
|
4221
|
+
handshake: true
|
|
4222
|
+
});
|
|
4223
|
+
await runtimeHandle.emitWhenReady?.(
|
|
4224
|
+
"handshake",
|
|
4225
|
+
{
|
|
4226
|
+
serviceInstanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
4227
|
+
serviceName: CadenzaService.serviceRegistry.serviceName,
|
|
4228
|
+
isFrontend: isBrowser,
|
|
4229
|
+
__status: "success"
|
|
4230
|
+
},
|
|
4231
|
+
1e4,
|
|
4232
|
+
(result) => {
|
|
4233
|
+
if (result.status === "success") {
|
|
4234
|
+
const handshakeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4235
|
+
upsertDiagnostics({
|
|
4236
|
+
connected: true,
|
|
4237
|
+
handshake: true,
|
|
4238
|
+
lastHandshakeAt: handshakeAt,
|
|
4239
|
+
lastHandshakeError: null,
|
|
4240
|
+
socketId: socket.id ?? null
|
|
4241
|
+
});
|
|
4242
|
+
applySessionOperation("handshake", {
|
|
4243
|
+
connected: true,
|
|
4244
|
+
handshake: true,
|
|
4245
|
+
lastHandshakeAt: handshakeAt,
|
|
4246
|
+
lastHandshakeError: null,
|
|
4247
|
+
socketId: socket.id ?? null
|
|
4248
|
+
});
|
|
4249
|
+
CadenzaService.log("Socket client connected", {
|
|
4250
|
+
result,
|
|
3850
4251
|
serviceName,
|
|
3851
|
-
|
|
3852
|
-
|
|
4252
|
+
socketId: socket.id,
|
|
4253
|
+
url
|
|
4254
|
+
});
|
|
4255
|
+
} else {
|
|
4256
|
+
const errorMessage = result?.__error ?? result?.error ?? "Socket handshake failed";
|
|
4257
|
+
upsertDiagnostics(
|
|
4258
|
+
{
|
|
4259
|
+
connected: false,
|
|
4260
|
+
handshake: false,
|
|
4261
|
+
lastHandshakeError: errorMessage
|
|
4262
|
+
},
|
|
4263
|
+
errorMessage
|
|
4264
|
+
);
|
|
4265
|
+
applySessionOperation("handshake", {
|
|
4266
|
+
connected: false,
|
|
4267
|
+
handshake: false,
|
|
4268
|
+
lastHandshakeError: errorMessage
|
|
4269
|
+
});
|
|
4270
|
+
CadenzaService.log(
|
|
4271
|
+
"Socket handshake failed",
|
|
4272
|
+
{ result, serviceName, socketId: socket.id, url },
|
|
4273
|
+
"warning"
|
|
3853
4274
|
);
|
|
3854
4275
|
}
|
|
3855
|
-
|
|
4276
|
+
void emitter;
|
|
3856
4277
|
}
|
|
3857
4278
|
);
|
|
3858
|
-
}
|
|
3859
|
-
|
|
3860
|
-
`
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
4279
|
+
},
|
|
4280
|
+
"Handshakes with socket server"
|
|
4281
|
+
).doOn(`meta.socket_client.connected:${fetchId}`);
|
|
4282
|
+
runtimeHandle.delegateTask = CadenzaService.createMetaTask(
|
|
4283
|
+
`Delegate flow to Socket service ${url}`,
|
|
4284
|
+
async (delegateCtx, emitter) => {
|
|
4285
|
+
if (delegateCtx.__remoteRoutineName === void 0) {
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4288
|
+
delete delegateCtx.__isSubMeta;
|
|
4289
|
+
delete delegateCtx.__broadcast;
|
|
4290
|
+
const deputyExecId = delegateCtx.__metadata?.__deputyExecId;
|
|
4291
|
+
const requestSentAt = Date.now();
|
|
4292
|
+
if (deputyExecId) {
|
|
4293
|
+
runtimeHandle.pendingDelegationIds.add(deputyExecId);
|
|
4294
|
+
syncPendingCounts();
|
|
4295
|
+
}
|
|
4296
|
+
try {
|
|
4297
|
+
const resultContext = await runtimeHandle.emitWhenReady?.(
|
|
4298
|
+
"delegation",
|
|
4299
|
+
delegateCtx,
|
|
4300
|
+
delegateCtx.__timeout ?? 6e4
|
|
4301
|
+
) ?? {
|
|
4302
|
+
errored: true,
|
|
4303
|
+
__error: "Socket delegation returned no response"
|
|
4304
|
+
};
|
|
4305
|
+
const requestDuration = Date.now() - requestSentAt;
|
|
4306
|
+
const metadata = resultContext.__metadata;
|
|
4307
|
+
delete resultContext.__metadata;
|
|
4308
|
+
if (deputyExecId) {
|
|
4309
|
+
emitter(`meta.socket_client.delegated:${deputyExecId}`, {
|
|
4310
|
+
...resultContext,
|
|
4311
|
+
...metadata,
|
|
4312
|
+
__requestDuration: requestDuration
|
|
4313
|
+
});
|
|
4314
|
+
}
|
|
4315
|
+
if (resultContext?.errored || resultContext?.failed) {
|
|
4316
|
+
const errorMessage = resultContext?.__error ?? resultContext?.error ?? "Socket delegation failed";
|
|
4317
|
+
upsertDiagnostics(
|
|
4318
|
+
{
|
|
4319
|
+
lastHandshakeError: String(errorMessage)
|
|
4320
|
+
},
|
|
4321
|
+
errorMessage
|
|
3878
4322
|
);
|
|
4323
|
+
applySessionOperation("delegate", {
|
|
4324
|
+
lastHandshakeError: String(errorMessage)
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
return resultContext;
|
|
4328
|
+
} catch (error) {
|
|
4329
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4330
|
+
const failedContext = {
|
|
4331
|
+
errored: true,
|
|
4332
|
+
__error: message
|
|
4333
|
+
};
|
|
4334
|
+
if (deputyExecId) {
|
|
4335
|
+
emitter(`meta.socket_client.delegated:${deputyExecId}`, {
|
|
4336
|
+
...failedContext,
|
|
4337
|
+
__requestDuration: Date.now() - requestSentAt
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
upsertDiagnostics(
|
|
4341
|
+
{
|
|
4342
|
+
lastHandshakeError: message
|
|
4343
|
+
},
|
|
4344
|
+
error
|
|
4345
|
+
);
|
|
4346
|
+
applySessionOperation("delegate", {
|
|
4347
|
+
lastHandshakeError: message
|
|
4348
|
+
});
|
|
4349
|
+
return failedContext;
|
|
4350
|
+
} finally {
|
|
4351
|
+
if (deputyExecId) {
|
|
4352
|
+
runtimeHandle.pendingDelegationIds.delete(deputyExecId);
|
|
4353
|
+
syncPendingCounts();
|
|
3879
4354
|
}
|
|
3880
|
-
resolve(response);
|
|
3881
|
-
});
|
|
3882
|
-
});
|
|
3883
|
-
},
|
|
3884
|
-
`Transmits signal to service ${serviceName} with address ${URL}`
|
|
3885
|
-
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
3886
|
-
CadenzaService.createEphemeralMetaTask(
|
|
3887
|
-
`Shutdown SocketClient ${URL}`,
|
|
3888
|
-
(ctx2, emit) => {
|
|
3889
|
-
handshake = false;
|
|
3890
|
-
socketDiagnostics.connected = false;
|
|
3891
|
-
socketDiagnostics.handshake = false;
|
|
3892
|
-
socketDiagnostics.destroyed = true;
|
|
3893
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3894
|
-
CadenzaService.log("Shutting down socket client", { URL, serviceName });
|
|
3895
|
-
socket?.close();
|
|
3896
|
-
handshakeTask?.destroy();
|
|
3897
|
-
delegateTask?.destroy();
|
|
3898
|
-
transmitTask?.destroy();
|
|
3899
|
-
handshakeTask = null;
|
|
3900
|
-
delegateTask = null;
|
|
3901
|
-
transmitTask = null;
|
|
3902
|
-
emitWhenReady = null;
|
|
3903
|
-
socket = null;
|
|
3904
|
-
emit(`meta.fetch.handshake_requested:${fetchId}`, {
|
|
3905
|
-
serviceInstanceId,
|
|
3906
|
-
serviceName,
|
|
3907
|
-
communicationTypes,
|
|
3908
|
-
serviceAddress,
|
|
3909
|
-
servicePort,
|
|
3910
|
-
protocol,
|
|
3911
|
-
handshakeData: {
|
|
3912
|
-
instanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
3913
|
-
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
3914
4355
|
}
|
|
3915
|
-
}
|
|
3916
|
-
|
|
3917
|
-
|
|
4356
|
+
},
|
|
4357
|
+
`Delegate flow to service ${serviceName} with address ${url}`
|
|
4358
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal(
|
|
4359
|
+
"meta.socket_client.delegated",
|
|
4360
|
+
"meta.socket_shutdown_requested"
|
|
4361
|
+
);
|
|
4362
|
+
runtimeHandle.transmitTask = CadenzaService.createMetaTask(
|
|
4363
|
+
`Transmit signal to socket server ${url}`,
|
|
4364
|
+
async (signalCtx, emitter) => {
|
|
4365
|
+
if (signalCtx.__signalName === void 0) {
|
|
4366
|
+
return;
|
|
4367
|
+
}
|
|
4368
|
+
delete signalCtx.__broadcast;
|
|
4369
|
+
const response = await runtimeHandle.emitWhenReady?.("signal", signalCtx, 5e3) ?? {
|
|
3918
4370
|
errored: true,
|
|
3919
|
-
__error: "
|
|
4371
|
+
__error: "Socket signal transmission returned no response"
|
|
4372
|
+
};
|
|
4373
|
+
applySessionOperation("transmit", {});
|
|
4374
|
+
if (signalCtx.__routineExecId) {
|
|
4375
|
+
emitter(`meta.socket_client.transmitted:${signalCtx.__routineExecId}`, {
|
|
4376
|
+
...response
|
|
4377
|
+
});
|
|
4378
|
+
}
|
|
4379
|
+
return response;
|
|
4380
|
+
},
|
|
4381
|
+
`Transmits signal to service ${serviceName} with address ${url}`
|
|
4382
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
4383
|
+
CadenzaService.createEphemeralMetaTask(
|
|
4384
|
+
`Shutdown SocketClient ${url}`,
|
|
4385
|
+
(_ctx, emitter) => {
|
|
4386
|
+
runtimeHandle.handshake = false;
|
|
4387
|
+
upsertDiagnostics({
|
|
4388
|
+
connected: false,
|
|
4389
|
+
handshake: false,
|
|
4390
|
+
destroyed: true,
|
|
4391
|
+
pendingDelegations: 0,
|
|
4392
|
+
pendingTimers: 0
|
|
3920
4393
|
});
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
4394
|
+
applySessionOperation("shutdown", {
|
|
4395
|
+
connected: false,
|
|
4396
|
+
handshake: false,
|
|
4397
|
+
destroyed: true,
|
|
4398
|
+
pendingDelegations: 0,
|
|
4399
|
+
pendingTimers: 0
|
|
4400
|
+
});
|
|
4401
|
+
CadenzaService.log("Shutting down socket client", { url, serviceName });
|
|
4402
|
+
emitter(`meta.fetch.handshake_requested:${fetchId}`, {
|
|
4403
|
+
serviceInstanceId,
|
|
4404
|
+
serviceName,
|
|
4405
|
+
communicationTypes,
|
|
4406
|
+
serviceAddress,
|
|
4407
|
+
servicePort: normalizedPort,
|
|
4408
|
+
protocol,
|
|
4409
|
+
handshakeData: {
|
|
4410
|
+
instanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
4411
|
+
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
4412
|
+
}
|
|
4413
|
+
});
|
|
4414
|
+
for (const id of runtimeHandle.pendingDelegationIds) {
|
|
4415
|
+
emitter(`meta.socket_client.delegated:${id}`, {
|
|
4416
|
+
errored: true,
|
|
4417
|
+
__error: "Shutting down socket client"
|
|
4418
|
+
});
|
|
4419
|
+
}
|
|
4420
|
+
this.destroySocketClientRuntimeHandle(runtimeHandle);
|
|
4421
|
+
emitter("meta.socket_client.runtime_clear_requested", {
|
|
4422
|
+
fetchId
|
|
4423
|
+
});
|
|
4424
|
+
},
|
|
4425
|
+
"Shuts down the socket client"
|
|
4426
|
+
).doOn(
|
|
4427
|
+
`meta.socket_shutdown_requested:${fetchId}`,
|
|
4428
|
+
`meta.socket_client.disconnected:${fetchId}`,
|
|
4429
|
+
`meta.fetch.handshake_failed:${fetchId}`,
|
|
4430
|
+
`meta.socket_client.connect_error:${fetchId}`
|
|
4431
|
+
).attachSignal("meta.fetch.handshake_requested").emits("meta.socket_client_shutdown_complete");
|
|
4432
|
+
return true;
|
|
4433
|
+
},
|
|
4434
|
+
{ mode: "write" }
|
|
4435
|
+
),
|
|
4436
|
+
"Connects to a specified socket server and wires runtime tasks."
|
|
3940
4437
|
).doOn("meta.fetch.handshake_complete").emitsOnFail("meta.socket_client.connect_failed");
|
|
3941
4438
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
return this._instance;
|
|
4439
|
+
resolveSocketServerKey(input) {
|
|
4440
|
+
return String(input.serverKey ?? input.__socketServerKey ?? this.socketServerDefaultKey).trim() || this.socketServerDefaultKey;
|
|
3945
4441
|
}
|
|
3946
|
-
|
|
3947
|
-
const
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
4442
|
+
resolveSocketClientFetchId(input) {
|
|
4443
|
+
const explicitFetchId = String(input.fetchId ?? "").trim();
|
|
4444
|
+
if (explicitFetchId) {
|
|
4445
|
+
return explicitFetchId;
|
|
4446
|
+
}
|
|
4447
|
+
const serviceAddress = String(input.serviceAddress ?? "").trim();
|
|
4448
|
+
const protocol = String(input.protocol ?? "http").trim();
|
|
4449
|
+
const port = this.resolveServicePort(protocol, input.servicePort);
|
|
4450
|
+
if (!serviceAddress || !port) {
|
|
4451
|
+
return void 0;
|
|
4452
|
+
}
|
|
4453
|
+
return `${serviceAddress}_${port}`;
|
|
4454
|
+
}
|
|
4455
|
+
resolveServicePort(protocol, rawPort) {
|
|
4456
|
+
if (protocol === "https") {
|
|
4457
|
+
return 443;
|
|
4458
|
+
}
|
|
4459
|
+
const parsed = Number(rawPort);
|
|
4460
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
4461
|
+
return void 0;
|
|
4462
|
+
}
|
|
4463
|
+
return Math.trunc(parsed);
|
|
4464
|
+
}
|
|
4465
|
+
createSocketServerRuntimeHandleFromContext(context) {
|
|
4466
|
+
const baseServer = context.httpsServer ?? context.httpServer;
|
|
4467
|
+
if (!baseServer) {
|
|
4468
|
+
throw new Error(
|
|
4469
|
+
"Socket server runtime setup requires either httpsServer or httpServer"
|
|
4470
|
+
);
|
|
4471
|
+
}
|
|
4472
|
+
const server = new Server(baseServer, {
|
|
4473
|
+
pingInterval: 3e4,
|
|
4474
|
+
pingTimeout: 2e4,
|
|
4475
|
+
maxHttpBufferSize: 1e7,
|
|
4476
|
+
connectionStateRecovery: {
|
|
4477
|
+
maxDisconnectionDuration: 2 * 60 * 1e3,
|
|
4478
|
+
skipMiddlewares: true
|
|
4479
|
+
}
|
|
4480
|
+
});
|
|
3951
4481
|
return {
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
4482
|
+
server,
|
|
4483
|
+
initialized: false,
|
|
4484
|
+
connectedSocketIds: /* @__PURE__ */ new Set(),
|
|
4485
|
+
broadcastStatusTask: null,
|
|
4486
|
+
shutdownTask: null
|
|
3955
4487
|
};
|
|
3956
4488
|
}
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
state = {
|
|
3961
|
-
fetchId,
|
|
3962
|
-
serviceName,
|
|
3963
|
-
url,
|
|
3964
|
-
socketId: null,
|
|
3965
|
-
connected: false,
|
|
3966
|
-
handshake: false,
|
|
3967
|
-
reconnectAttempts: 0,
|
|
3968
|
-
connectErrors: 0,
|
|
3969
|
-
reconnectErrors: 0,
|
|
3970
|
-
socketErrors: 0,
|
|
3971
|
-
pendingDelegations: 0,
|
|
3972
|
-
pendingTimers: 0,
|
|
3973
|
-
destroyed: false,
|
|
3974
|
-
lastHandshakeAt: null,
|
|
3975
|
-
lastHandshakeError: null,
|
|
3976
|
-
lastDisconnectAt: null,
|
|
3977
|
-
lastError: null,
|
|
3978
|
-
lastErrorAt: 0,
|
|
3979
|
-
errorHistory: [],
|
|
3980
|
-
updatedAt: Date.now()
|
|
3981
|
-
};
|
|
3982
|
-
this.socketClientDiagnostics.set(fetchId, state);
|
|
3983
|
-
} else {
|
|
3984
|
-
state.serviceName = serviceName;
|
|
3985
|
-
state.url = url;
|
|
4489
|
+
destroySocketServerRuntimeHandle(runtimeHandle) {
|
|
4490
|
+
if (!runtimeHandle) {
|
|
4491
|
+
return;
|
|
3986
4492
|
}
|
|
3987
|
-
|
|
4493
|
+
runtimeHandle.broadcastStatusTask?.destroy();
|
|
4494
|
+
runtimeHandle.shutdownTask?.destroy();
|
|
4495
|
+
runtimeHandle.broadcastStatusTask = null;
|
|
4496
|
+
runtimeHandle.shutdownTask = null;
|
|
4497
|
+
runtimeHandle.connectedSocketIds.clear();
|
|
4498
|
+
runtimeHandle.initialized = false;
|
|
4499
|
+
runtimeHandle.server.close();
|
|
4500
|
+
runtimeHandle.server.removeAllListeners();
|
|
4501
|
+
}
|
|
4502
|
+
createSocketClientRuntimeHandle(url) {
|
|
4503
|
+
return {
|
|
4504
|
+
url,
|
|
4505
|
+
socket: io(url, {
|
|
4506
|
+
reconnection: true,
|
|
4507
|
+
reconnectionAttempts: 5,
|
|
4508
|
+
reconnectionDelay: 2e3,
|
|
4509
|
+
reconnectionDelayMax: 1e4,
|
|
4510
|
+
randomizationFactor: 0.5,
|
|
4511
|
+
transports: ["websocket"],
|
|
4512
|
+
autoConnect: false
|
|
4513
|
+
}),
|
|
4514
|
+
initialized: false,
|
|
4515
|
+
handshake: false,
|
|
4516
|
+
errorCount: 0,
|
|
4517
|
+
pendingDelegationIds: /* @__PURE__ */ new Set(),
|
|
4518
|
+
pendingTimers: /* @__PURE__ */ new Set(),
|
|
4519
|
+
emitWhenReady: null,
|
|
4520
|
+
handshakeTask: null,
|
|
4521
|
+
delegateTask: null,
|
|
4522
|
+
transmitTask: null
|
|
4523
|
+
};
|
|
4524
|
+
}
|
|
4525
|
+
destroySocketClientRuntimeHandle(runtimeHandle) {
|
|
4526
|
+
if (!runtimeHandle) {
|
|
4527
|
+
return;
|
|
4528
|
+
}
|
|
4529
|
+
runtimeHandle.initialized = false;
|
|
4530
|
+
runtimeHandle.handshake = false;
|
|
4531
|
+
runtimeHandle.emitWhenReady = null;
|
|
4532
|
+
runtimeHandle.handshakeTask?.destroy();
|
|
4533
|
+
runtimeHandle.delegateTask?.destroy();
|
|
4534
|
+
runtimeHandle.transmitTask?.destroy();
|
|
4535
|
+
runtimeHandle.handshakeTask = null;
|
|
4536
|
+
runtimeHandle.delegateTask = null;
|
|
4537
|
+
runtimeHandle.transmitTask = null;
|
|
4538
|
+
for (const timer of runtimeHandle.pendingTimers) {
|
|
4539
|
+
clearTimeout(timer);
|
|
4540
|
+
}
|
|
4541
|
+
runtimeHandle.pendingTimers.clear();
|
|
4542
|
+
runtimeHandle.pendingDelegationIds.clear();
|
|
4543
|
+
runtimeHandle.socket.close();
|
|
4544
|
+
runtimeHandle.socket.removeAllListeners();
|
|
4545
|
+
}
|
|
4546
|
+
normalizeCommunicationTypes(value) {
|
|
4547
|
+
if (!Array.isArray(value)) {
|
|
4548
|
+
return [];
|
|
4549
|
+
}
|
|
4550
|
+
return value.map((item) => String(item)).filter((item) => item.trim().length > 0);
|
|
3988
4551
|
}
|
|
3989
4552
|
getErrorMessage(error) {
|
|
3990
4553
|
if (error instanceof Error) {
|
|
@@ -3999,28 +4562,53 @@ var SocketController = class _SocketController {
|
|
|
3999
4562
|
return String(error);
|
|
4000
4563
|
}
|
|
4001
4564
|
}
|
|
4002
|
-
|
|
4003
|
-
const state
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4565
|
+
pruneDiagnosticsEntries(entries, now = Date.now()) {
|
|
4566
|
+
for (const [fetchId, state] of Object.entries(entries)) {
|
|
4567
|
+
if (state.destroyed && now - state.updatedAt > this.destroyedDiagnosticsTtlMs) {
|
|
4568
|
+
delete entries[fetchId];
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
if (Object.keys(entries).length <= this.diagnosticsMaxClientEntries) {
|
|
4572
|
+
return;
|
|
4573
|
+
}
|
|
4574
|
+
const entriesByEvictionPriority = Object.entries(entries).sort((left, right) => {
|
|
4575
|
+
if (left[1].destroyed !== right[1].destroyed) {
|
|
4576
|
+
return left[1].destroyed ? -1 : 1;
|
|
4577
|
+
}
|
|
4578
|
+
return left[1].updatedAt - right[1].updatedAt;
|
|
4012
4579
|
});
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4580
|
+
while (Object.keys(entries).length > this.diagnosticsMaxClientEntries && entriesByEvictionPriority.length > 0) {
|
|
4581
|
+
const [fetchId] = entriesByEvictionPriority.shift();
|
|
4582
|
+
delete entries[fetchId];
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
async getSocketClientDiagnosticsEntry(fetchId) {
|
|
4586
|
+
const normalized = String(fetchId ?? "").trim();
|
|
4587
|
+
if (!normalized) {
|
|
4588
|
+
return void 0;
|
|
4018
4589
|
}
|
|
4590
|
+
const snapshot = this.socketClientDiagnosticsActor.getState();
|
|
4591
|
+
const entries = { ...snapshot.entries };
|
|
4592
|
+
this.pruneDiagnosticsEntries(entries);
|
|
4593
|
+
return entries[normalized];
|
|
4594
|
+
}
|
|
4595
|
+
resolveTransportDiagnosticsOptions(ctx) {
|
|
4596
|
+
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
4597
|
+
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
4598
|
+
const requestedLimit = Number(ctx.errorHistoryLimit);
|
|
4599
|
+
const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
|
|
4600
|
+
return {
|
|
4601
|
+
detailLevel,
|
|
4602
|
+
includeErrorHistory,
|
|
4603
|
+
errorHistoryLimit
|
|
4604
|
+
};
|
|
4019
4605
|
}
|
|
4020
|
-
collectSocketTransportDiagnostics(ctx) {
|
|
4606
|
+
collectSocketTransportDiagnostics(ctx, diagnosticsEntries) {
|
|
4021
4607
|
const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
|
|
4022
4608
|
const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
|
|
4023
|
-
const
|
|
4609
|
+
const entries = { ...diagnosticsEntries };
|
|
4610
|
+
this.pruneDiagnosticsEntries(entries);
|
|
4611
|
+
const states = Object.values(entries).sort(
|
|
4024
4612
|
(a, b) => a.fetchId.localeCompare(b.fetchId)
|
|
4025
4613
|
);
|
|
4026
4614
|
const summary = {
|
|
@@ -4038,10 +4626,7 @@ var SocketController = class _SocketController {
|
|
|
4038
4626
|
0
|
|
4039
4627
|
),
|
|
4040
4628
|
connectErrors: states.reduce((acc, state) => acc + state.connectErrors, 0),
|
|
4041
|
-
reconnectErrors: states.reduce(
|
|
4042
|
-
(acc, state) => acc + state.reconnectErrors,
|
|
4043
|
-
0
|
|
4044
|
-
),
|
|
4629
|
+
reconnectErrors: states.reduce((acc, state) => acc + state.reconnectErrors, 0),
|
|
4045
4630
|
socketErrors: states.reduce((acc, state) => acc + state.socketErrors, 0),
|
|
4046
4631
|
latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
|
|
4047
4632
|
};
|
|
@@ -4400,6 +4985,22 @@ var GraphMetadataController = class _GraphMetadataController {
|
|
|
4400
4985
|
"Handles task execution relationship creation",
|
|
4401
4986
|
{ concurrency: 100, isSubMeta: true }
|
|
4402
4987
|
).doOn("meta.node.mapped", "meta.node.detected_previous_task_execution").emits("global.meta.graph_metadata.relationship_executed");
|
|
4988
|
+
CadenzaService.createMetaTask("Handle actor creation", (ctx) => {
|
|
4989
|
+
return {
|
|
4990
|
+
data: {
|
|
4991
|
+
...ctx.data,
|
|
4992
|
+
service_name: CadenzaService.serviceRegistry.serviceName
|
|
4993
|
+
}
|
|
4994
|
+
};
|
|
4995
|
+
}).doOn("meta.actor.created").emits("global.meta.graph_metadata.actor_created");
|
|
4996
|
+
CadenzaService.createMetaTask("Handle actor task association", (ctx) => {
|
|
4997
|
+
return {
|
|
4998
|
+
data: {
|
|
4999
|
+
...ctx.data,
|
|
5000
|
+
service_name: CadenzaService.serviceRegistry.serviceName
|
|
5001
|
+
}
|
|
5002
|
+
};
|
|
5003
|
+
}).doOn("meta.actor.task_associated").emits("global.meta.graph_metadata.actor_task_associated");
|
|
4403
5004
|
CadenzaService.createMetaTask("Handle Intent Creation", (ctx) => {
|
|
4404
5005
|
const intentName = ctx.data?.name;
|
|
4405
5006
|
return {
|
|
@@ -6071,8 +6672,69 @@ function tableFieldTypeToSchemaType(type) {
|
|
|
6071
6672
|
import { v4 as uuid3 } from "uuid";
|
|
6072
6673
|
|
|
6073
6674
|
// src/graph/controllers/GraphSyncController.ts
|
|
6675
|
+
var ACTOR_TASK_METADATA = /* @__PURE__ */ Symbol.for("@cadenza.io/core/actor-task-meta");
|
|
6676
|
+
function getActorTaskRuntimeMetadata(taskFunction) {
|
|
6677
|
+
if (typeof taskFunction !== "function") {
|
|
6678
|
+
return void 0;
|
|
6679
|
+
}
|
|
6680
|
+
return taskFunction[ACTOR_TASK_METADATA];
|
|
6681
|
+
}
|
|
6682
|
+
function sanitizeActorMetadataValue(value) {
|
|
6683
|
+
if (value === null) {
|
|
6684
|
+
return null;
|
|
6685
|
+
}
|
|
6686
|
+
if (value === void 0 || typeof value === "function") {
|
|
6687
|
+
return void 0;
|
|
6688
|
+
}
|
|
6689
|
+
if (Array.isArray(value)) {
|
|
6690
|
+
const items = [];
|
|
6691
|
+
for (const item of value) {
|
|
6692
|
+
const sanitizedItem = sanitizeActorMetadataValue(item);
|
|
6693
|
+
if (sanitizedItem !== void 0) {
|
|
6694
|
+
items.push(sanitizedItem);
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6697
|
+
return items;
|
|
6698
|
+
}
|
|
6699
|
+
if (typeof value === "object") {
|
|
6700
|
+
const output = {};
|
|
6701
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
6702
|
+
const sanitizedNestedValue = sanitizeActorMetadataValue(nestedValue);
|
|
6703
|
+
if (sanitizedNestedValue !== void 0) {
|
|
6704
|
+
output[key] = sanitizedNestedValue;
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
return output;
|
|
6708
|
+
}
|
|
6709
|
+
return value;
|
|
6710
|
+
}
|
|
6711
|
+
function buildActorRegistrationData(actor) {
|
|
6712
|
+
const definition = sanitizeActorMetadataValue(
|
|
6713
|
+
typeof actor?.toDefinition === "function" ? actor.toDefinition() : {}
|
|
6714
|
+
);
|
|
6715
|
+
const stateDefinition = definition?.state && typeof definition.state === "object" ? definition.state : {};
|
|
6716
|
+
const actorKind = typeof definition?.kind === "string" ? definition.kind : actor?.kind;
|
|
6717
|
+
return {
|
|
6718
|
+
name: definition?.name ?? actor?.spec?.name ?? "",
|
|
6719
|
+
description: definition?.description ?? actor?.spec?.description ?? "",
|
|
6720
|
+
default_key: definition?.defaultKey ?? actor?.spec?.defaultKey ?? "default",
|
|
6721
|
+
load_policy: definition?.loadPolicy ?? actor?.spec?.loadPolicy ?? "eager",
|
|
6722
|
+
write_contract: definition?.writeContract ?? actor?.spec?.writeContract ?? "overwrite",
|
|
6723
|
+
runtime_read_guard: definition?.runtimeReadGuard ?? actor?.spec?.runtimeReadGuard ?? "none",
|
|
6724
|
+
consistency_profile: definition?.consistencyProfile ?? actor?.spec?.consistencyProfile ?? null,
|
|
6725
|
+
key_definition: definition?.key ?? null,
|
|
6726
|
+
state_definition: stateDefinition,
|
|
6727
|
+
retry_policy: definition?.retry ?? {},
|
|
6728
|
+
idempotency_policy: definition?.idempotency ?? {},
|
|
6729
|
+
session_policy: definition?.session ?? {},
|
|
6730
|
+
is_meta: actorKind === "meta",
|
|
6731
|
+
version: 1
|
|
6732
|
+
};
|
|
6733
|
+
}
|
|
6074
6734
|
var GraphSyncController = class _GraphSyncController {
|
|
6075
6735
|
constructor() {
|
|
6736
|
+
this.registeredActors = /* @__PURE__ */ new Set();
|
|
6737
|
+
this.registeredActorTaskMaps = /* @__PURE__ */ new Set();
|
|
6076
6738
|
this.isCadenzaDBReady = false;
|
|
6077
6739
|
}
|
|
6078
6740
|
static get instance() {
|
|
@@ -6330,6 +6992,120 @@ var GraphSyncController = class _GraphSyncController {
|
|
|
6330
6992
|
)
|
|
6331
6993
|
)
|
|
6332
6994
|
);
|
|
6995
|
+
this.splitActorsForRegistration = CadenzaService.createMetaTask(
|
|
6996
|
+
"Split actors for registration",
|
|
6997
|
+
function* (ctx) {
|
|
6998
|
+
CadenzaService.debounce("meta.sync_controller.synced_resource", {
|
|
6999
|
+
delayMs: 3e3
|
|
7000
|
+
});
|
|
7001
|
+
const actors = ctx.actors ?? [];
|
|
7002
|
+
for (const actor of actors) {
|
|
7003
|
+
const data = {
|
|
7004
|
+
...buildActorRegistrationData(actor),
|
|
7005
|
+
service_name: CadenzaService.serviceRegistry.serviceName
|
|
7006
|
+
};
|
|
7007
|
+
if (!data.name) {
|
|
7008
|
+
continue;
|
|
7009
|
+
}
|
|
7010
|
+
const registrationKey = `${data.name}|${data.version}|${data.service_name}`;
|
|
7011
|
+
if (this.registeredActors.has(registrationKey)) {
|
|
7012
|
+
continue;
|
|
7013
|
+
}
|
|
7014
|
+
yield {
|
|
7015
|
+
data,
|
|
7016
|
+
__actorRegistrationKey: registrationKey
|
|
7017
|
+
};
|
|
7018
|
+
}
|
|
7019
|
+
}.bind(this)
|
|
7020
|
+
).then(
|
|
7021
|
+
(this.isCadenzaDBReady ? CadenzaService.createCadenzaDBInsertTask(
|
|
7022
|
+
"actor",
|
|
7023
|
+
{
|
|
7024
|
+
onConflict: {
|
|
7025
|
+
target: ["name", "service_name", "version"],
|
|
7026
|
+
action: {
|
|
7027
|
+
do: "nothing"
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
},
|
|
7031
|
+
{ concurrency: 30 }
|
|
7032
|
+
) : CadenzaService.get("dbInsertActor"))?.then(
|
|
7033
|
+
CadenzaService.createMetaTask("Record actor registration", (ctx) => {
|
|
7034
|
+
if (!ctx.__syncing) {
|
|
7035
|
+
return;
|
|
7036
|
+
}
|
|
7037
|
+
CadenzaService.debounce("meta.sync_controller.synced_resource", {
|
|
7038
|
+
delayMs: 3e3
|
|
7039
|
+
});
|
|
7040
|
+
this.registeredActors.add(ctx.__actorRegistrationKey);
|
|
7041
|
+
return true;
|
|
7042
|
+
}).then(
|
|
7043
|
+
CadenzaService.createUniqueMetaTask(
|
|
7044
|
+
"Gather actor registration",
|
|
7045
|
+
() => true
|
|
7046
|
+
).emits("meta.sync_controller.synced_actors")
|
|
7047
|
+
)
|
|
7048
|
+
)
|
|
7049
|
+
);
|
|
7050
|
+
this.registerActorTaskMapTask = CadenzaService.createMetaTask(
|
|
7051
|
+
"Split actor task maps",
|
|
7052
|
+
function* (ctx) {
|
|
7053
|
+
const task = ctx.task;
|
|
7054
|
+
if (task.hidden || !task.register) {
|
|
7055
|
+
return;
|
|
7056
|
+
}
|
|
7057
|
+
const metadata = getActorTaskRuntimeMetadata(task.taskFunction);
|
|
7058
|
+
if (!metadata?.actorName) {
|
|
7059
|
+
return;
|
|
7060
|
+
}
|
|
7061
|
+
const registrationKey = `${metadata.actorName}|${task.name}|${task.version}|${CadenzaService.serviceRegistry.serviceName}`;
|
|
7062
|
+
if (this.registeredActorTaskMaps.has(registrationKey)) {
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
yield {
|
|
7066
|
+
data: {
|
|
7067
|
+
actor_name: metadata.actorName,
|
|
7068
|
+
actor_version: 1,
|
|
7069
|
+
task_name: task.name,
|
|
7070
|
+
task_version: task.version,
|
|
7071
|
+
service_name: CadenzaService.serviceRegistry.serviceName,
|
|
7072
|
+
mode: metadata.mode,
|
|
7073
|
+
description: task.description ?? metadata.actorDescription ?? "",
|
|
7074
|
+
is_meta: metadata.actorKind === "meta" || task.isMeta === true
|
|
7075
|
+
},
|
|
7076
|
+
__actorTaskMapRegistrationKey: registrationKey
|
|
7077
|
+
};
|
|
7078
|
+
}.bind(this)
|
|
7079
|
+
).then(
|
|
7080
|
+
(this.isCadenzaDBReady ? CadenzaService.createCadenzaDBInsertTask(
|
|
7081
|
+
"actor_task_map",
|
|
7082
|
+
{
|
|
7083
|
+
onConflict: {
|
|
7084
|
+
target: [
|
|
7085
|
+
"actor_name",
|
|
7086
|
+
"actor_version",
|
|
7087
|
+
"task_name",
|
|
7088
|
+
"task_version",
|
|
7089
|
+
"service_name"
|
|
7090
|
+
],
|
|
7091
|
+
action: {
|
|
7092
|
+
do: "nothing"
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
},
|
|
7096
|
+
{ concurrency: 30 }
|
|
7097
|
+
) : CadenzaService.get("dbInsertActorTaskMap"))?.then(
|
|
7098
|
+
CadenzaService.createMetaTask("Record actor task map registration", (ctx) => {
|
|
7099
|
+
if (!ctx.__syncing) {
|
|
7100
|
+
return;
|
|
7101
|
+
}
|
|
7102
|
+
CadenzaService.debounce("meta.sync_controller.synced_resource", {
|
|
7103
|
+
delayMs: 3e3
|
|
7104
|
+
});
|
|
7105
|
+
this.registeredActorTaskMaps.add(ctx.__actorTaskMapRegistrationKey);
|
|
7106
|
+
})
|
|
7107
|
+
)
|
|
7108
|
+
);
|
|
6333
7109
|
const registerSignalTask = CadenzaService.createMetaTask(
|
|
6334
7110
|
"Record signal registration",
|
|
6335
7111
|
(ctx) => {
|
|
@@ -6571,12 +7347,19 @@ var GraphSyncController = class _GraphSyncController {
|
|
|
6571
7347
|
).then(this.splitSignalsTask);
|
|
6572
7348
|
CadenzaService.registry.getAllTasks.clone().doOn("meta.sync_controller.synced_signals").then(this.splitTasksForRegistration);
|
|
6573
7349
|
CadenzaService.registry.getAllRoutines.clone().doOn("meta.sync_controller.synced_tasks").then(this.splitRoutinesTask);
|
|
7350
|
+
CadenzaService.createMetaTask("Get all actors", (ctx) => {
|
|
7351
|
+
return {
|
|
7352
|
+
...ctx,
|
|
7353
|
+
actors: CadenzaService.getAllActors()
|
|
7354
|
+
};
|
|
7355
|
+
}).doOn("meta.sync_controller.synced_tasks").then(this.splitActorsForRegistration);
|
|
6574
7356
|
CadenzaService.registry.doForEachTask.clone().doOn("meta.sync_controller.synced_tasks").then(
|
|
6575
7357
|
this.registerTaskMapTask,
|
|
6576
7358
|
this.registerSignalToTaskMapTask,
|
|
6577
7359
|
this.registerIntentToTaskMapTask,
|
|
6578
7360
|
this.registerDeputyRelationshipTask
|
|
6579
7361
|
);
|
|
7362
|
+
CadenzaService.registry.doForEachTask.clone().doOn("meta.sync_controller.synced_tasks", "meta.sync_controller.synced_actors").then(this.registerActorTaskMapTask);
|
|
6580
7363
|
CadenzaService.registry.getAllRoutines.clone().doOn("meta.sync_controller.synced_routines").then(this.splitTasksInRoutines);
|
|
6581
7364
|
CadenzaService.createMetaTask("Finish sync", (ctx, emit) => {
|
|
6582
7365
|
emit("global.meta.sync_controller.synced", {
|
|
@@ -6963,6 +7746,14 @@ var CadenzaService = class {
|
|
|
6963
7746
|
static get(taskName) {
|
|
6964
7747
|
return Cadenza.get(taskName);
|
|
6965
7748
|
}
|
|
7749
|
+
static getActor(actorName) {
|
|
7750
|
+
const cadenzaWithActors = Cadenza;
|
|
7751
|
+
return cadenzaWithActors.getActor?.(actorName);
|
|
7752
|
+
}
|
|
7753
|
+
static getAllActors() {
|
|
7754
|
+
const cadenzaWithActors = Cadenza;
|
|
7755
|
+
return cadenzaWithActors.getAllActors?.() ?? [];
|
|
7756
|
+
}
|
|
6966
7757
|
static getRoutine(routineName) {
|
|
6967
7758
|
return Cadenza.getRoutine(routineName);
|
|
6968
7759
|
}
|
|
@@ -7528,6 +8319,14 @@ var CadenzaService = class {
|
|
|
7528
8319
|
options.isMeta = true;
|
|
7529
8320
|
this.createDatabaseService(name, schema, description, options);
|
|
7530
8321
|
}
|
|
8322
|
+
static createActor(spec, options = {}) {
|
|
8323
|
+
this.bootstrap();
|
|
8324
|
+
return Cadenza.createActor(spec, options);
|
|
8325
|
+
}
|
|
8326
|
+
static createActorFromDefinition(definition, options = {}) {
|
|
8327
|
+
this.bootstrap();
|
|
8328
|
+
return Cadenza.createActorFromDefinition(definition, options);
|
|
8329
|
+
}
|
|
7531
8330
|
/**
|
|
7532
8331
|
* Creates and registers a new task with the provided name, function, and optional details.
|
|
7533
8332
|
*
|
|
@@ -7942,6 +8741,7 @@ CadenzaService.warnedInvalidMetaIntentResponderKeys = /* @__PURE__ */ new Set();
|
|
|
7942
8741
|
|
|
7943
8742
|
// src/index.ts
|
|
7944
8743
|
import {
|
|
8744
|
+
Actor as Actor2,
|
|
7945
8745
|
DebounceTask as DebounceTask2,
|
|
7946
8746
|
EphemeralTask as EphemeralTask2,
|
|
7947
8747
|
GraphRoutine as GraphRoutine2,
|
|
@@ -7949,6 +8749,7 @@ import {
|
|
|
7949
8749
|
} from "@cadenza.io/core";
|
|
7950
8750
|
var index_default = CadenzaService;
|
|
7951
8751
|
export {
|
|
8752
|
+
Actor2 as Actor,
|
|
7952
8753
|
DatabaseTask,
|
|
7953
8754
|
DebounceTask2 as DebounceTask,
|
|
7954
8755
|
DeputyTask,
|