@cadenza.io/service 2.8.0 → 2.9.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 +61 -29
- package/dist/index.d.ts +61 -29
- package/dist/index.js +1218 -617
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1221 -618
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/Cadenza.ts
|
|
2
|
-
import Cadenza
|
|
2
|
+
import Cadenza, {
|
|
3
|
+
Actor
|
|
4
|
+
} from "@cadenza.io/core";
|
|
3
5
|
|
|
4
6
|
// src/graph/definition/DeputyTask.ts
|
|
5
7
|
import { v4 as uuid } from "uuid";
|
|
@@ -2426,6 +2428,8 @@ var RestController = class _RestController {
|
|
|
2426
2428
|
constructor() {
|
|
2427
2429
|
this.fetchClientDiagnostics = /* @__PURE__ */ new Map();
|
|
2428
2430
|
this.diagnosticsErrorHistoryLimit = 100;
|
|
2431
|
+
this.diagnosticsMaxClientEntries = 500;
|
|
2432
|
+
this.destroyedDiagnosticsTtlMs = 15 * 6e4;
|
|
2429
2433
|
/**
|
|
2430
2434
|
* Fetches data from the given URL with a specified timeout. This function performs
|
|
2431
2435
|
* a fetch request with the ability to cancel the request if it exceeds the provided timeout duration.
|
|
@@ -3079,6 +3083,28 @@ var RestController = class _RestController {
|
|
|
3079
3083
|
if (!this._instance) this._instance = new _RestController();
|
|
3080
3084
|
return this._instance;
|
|
3081
3085
|
}
|
|
3086
|
+
pruneFetchClientDiagnostics(now = Date.now()) {
|
|
3087
|
+
for (const [fetchId, state] of this.fetchClientDiagnostics.entries()) {
|
|
3088
|
+
if (state.destroyed && now - state.updatedAt > this.destroyedDiagnosticsTtlMs) {
|
|
3089
|
+
this.fetchClientDiagnostics.delete(fetchId);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
if (this.fetchClientDiagnostics.size <= this.diagnosticsMaxClientEntries) {
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
const entriesByEvictionPriority = Array.from(
|
|
3096
|
+
this.fetchClientDiagnostics.entries()
|
|
3097
|
+
).sort((left, right) => {
|
|
3098
|
+
if (left[1].destroyed !== right[1].destroyed) {
|
|
3099
|
+
return left[1].destroyed ? -1 : 1;
|
|
3100
|
+
}
|
|
3101
|
+
return left[1].updatedAt - right[1].updatedAt;
|
|
3102
|
+
});
|
|
3103
|
+
while (this.fetchClientDiagnostics.size > this.diagnosticsMaxClientEntries && entriesByEvictionPriority.length > 0) {
|
|
3104
|
+
const [fetchId] = entriesByEvictionPriority.shift();
|
|
3105
|
+
this.fetchClientDiagnostics.delete(fetchId);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3082
3108
|
resolveTransportDiagnosticsOptions(ctx) {
|
|
3083
3109
|
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
3084
3110
|
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
@@ -3091,6 +3117,8 @@ var RestController = class _RestController {
|
|
|
3091
3117
|
};
|
|
3092
3118
|
}
|
|
3093
3119
|
ensureFetchClientDiagnostics(fetchId, serviceName, url) {
|
|
3120
|
+
const now = Date.now();
|
|
3121
|
+
this.pruneFetchClientDiagnostics(now);
|
|
3094
3122
|
let state = this.fetchClientDiagnostics.get(fetchId);
|
|
3095
3123
|
if (!state) {
|
|
3096
3124
|
state = {
|
|
@@ -3110,13 +3138,14 @@ var RestController = class _RestController {
|
|
|
3110
3138
|
signalFailures: 0,
|
|
3111
3139
|
statusChecks: 0,
|
|
3112
3140
|
statusFailures: 0,
|
|
3113
|
-
updatedAt:
|
|
3141
|
+
updatedAt: now
|
|
3114
3142
|
};
|
|
3115
3143
|
this.fetchClientDiagnostics.set(fetchId, state);
|
|
3116
3144
|
} else {
|
|
3117
3145
|
state.serviceName = serviceName;
|
|
3118
3146
|
state.url = url;
|
|
3119
3147
|
}
|
|
3148
|
+
this.pruneFetchClientDiagnostics(now);
|
|
3120
3149
|
return state;
|
|
3121
3150
|
}
|
|
3122
3151
|
getErrorMessage(error) {
|
|
@@ -3148,6 +3177,7 @@ var RestController = class _RestController {
|
|
|
3148
3177
|
}
|
|
3149
3178
|
}
|
|
3150
3179
|
collectFetchTransportDiagnostics(ctx) {
|
|
3180
|
+
this.pruneFetchClientDiagnostics();
|
|
3151
3181
|
const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
|
|
3152
3182
|
const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
|
|
3153
3183
|
const states = Array.from(this.fetchClientDiagnostics.values()).sort(
|
|
@@ -3264,55 +3294,225 @@ var waitForSocketConnection = async (socket, timeoutMs, createError) => {
|
|
|
3264
3294
|
|
|
3265
3295
|
// src/network/SocketController.ts
|
|
3266
3296
|
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
3297
|
constructor() {
|
|
3285
|
-
this.socketClientDiagnostics = /* @__PURE__ */ new Map();
|
|
3286
3298
|
this.diagnosticsErrorHistoryLimit = 100;
|
|
3299
|
+
this.diagnosticsMaxClientEntries = 500;
|
|
3300
|
+
this.destroyedDiagnosticsTtlMs = 15 * 6e4;
|
|
3301
|
+
this.socketServerDefaultKey = "socket-server-default";
|
|
3302
|
+
this.socketServerActor = CadenzaService.createActor(
|
|
3303
|
+
{
|
|
3304
|
+
name: "SocketServerActor",
|
|
3305
|
+
description: "Holds durable socket server session state and runtime socket server handle",
|
|
3306
|
+
defaultKey: this.socketServerDefaultKey,
|
|
3307
|
+
keyResolver: (input) => this.resolveSocketServerKey(input),
|
|
3308
|
+
loadPolicy: "lazy",
|
|
3309
|
+
writeContract: "overwrite",
|
|
3310
|
+
initState: this.createInitialSocketServerSessionState(
|
|
3311
|
+
this.socketServerDefaultKey
|
|
3312
|
+
)
|
|
3313
|
+
},
|
|
3314
|
+
{ isMeta: true }
|
|
3315
|
+
);
|
|
3316
|
+
this.socketClientActor = CadenzaService.createActor(
|
|
3317
|
+
{
|
|
3318
|
+
name: "SocketClientActor",
|
|
3319
|
+
description: "Holds durable socket client session state and runtime socket connection handles",
|
|
3320
|
+
defaultKey: "socket-client-default",
|
|
3321
|
+
keyResolver: (input) => this.resolveSocketClientFetchId(input),
|
|
3322
|
+
loadPolicy: "lazy",
|
|
3323
|
+
writeContract: "overwrite",
|
|
3324
|
+
initState: this.createInitialSocketClientSessionState()
|
|
3325
|
+
},
|
|
3326
|
+
{ isMeta: true }
|
|
3327
|
+
);
|
|
3328
|
+
this.socketClientDiagnosticsActor = CadenzaService.createActor(
|
|
3329
|
+
{
|
|
3330
|
+
name: "SocketClientDiagnosticsActor",
|
|
3331
|
+
description: "Tracks socket client diagnostics snapshots per fetchId for transport observability",
|
|
3332
|
+
defaultKey: "socket-client-diagnostics",
|
|
3333
|
+
loadPolicy: "eager",
|
|
3334
|
+
writeContract: "overwrite",
|
|
3335
|
+
initState: {
|
|
3336
|
+
entries: {}
|
|
3337
|
+
}
|
|
3338
|
+
},
|
|
3339
|
+
{ isMeta: true }
|
|
3340
|
+
);
|
|
3341
|
+
this.registerDiagnosticsTasks();
|
|
3342
|
+
this.registerSocketServerTasks();
|
|
3343
|
+
this.registerSocketClientTasks();
|
|
3287
3344
|
CadenzaService.createMetaTask(
|
|
3288
3345
|
"Collect socket transport diagnostics",
|
|
3289
|
-
|
|
3346
|
+
this.socketClientDiagnosticsActor.task(
|
|
3347
|
+
({ state, input }) => this.collectSocketTransportDiagnostics(input, state.entries),
|
|
3348
|
+
{ mode: "read" }
|
|
3349
|
+
),
|
|
3290
3350
|
"Responds to distributed transport diagnostics inquiries with socket client data."
|
|
3291
3351
|
).respondsTo(META_RUNTIME_TRANSPORT_DIAGNOSTICS_INTENT);
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3352
|
+
}
|
|
3353
|
+
static get instance() {
|
|
3354
|
+
if (!this._instance) this._instance = new _SocketController();
|
|
3355
|
+
return this._instance;
|
|
3356
|
+
}
|
|
3357
|
+
registerDiagnosticsTasks() {
|
|
3358
|
+
CadenzaService.createThrottledMetaTask(
|
|
3359
|
+
"SocketClientDiagnosticsActor.Upsert",
|
|
3360
|
+
this.socketClientDiagnosticsActor.task(
|
|
3361
|
+
({ state, input, setState }) => {
|
|
3362
|
+
const fetchId = String(input.fetchId ?? "").trim();
|
|
3363
|
+
if (!fetchId) {
|
|
3297
3364
|
return;
|
|
3298
3365
|
}
|
|
3299
|
-
const
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3366
|
+
const now = Date.now();
|
|
3367
|
+
const entries = { ...state.entries };
|
|
3368
|
+
const existing = entries[fetchId];
|
|
3369
|
+
const base = existing ? {
|
|
3370
|
+
...existing,
|
|
3371
|
+
errorHistory: [...existing.errorHistory]
|
|
3372
|
+
} : {
|
|
3373
|
+
fetchId,
|
|
3374
|
+
serviceName: String(input.serviceName ?? ""),
|
|
3375
|
+
url: String(input.url ?? ""),
|
|
3376
|
+
socketId: null,
|
|
3377
|
+
connected: false,
|
|
3378
|
+
handshake: false,
|
|
3379
|
+
reconnectAttempts: 0,
|
|
3380
|
+
connectErrors: 0,
|
|
3381
|
+
reconnectErrors: 0,
|
|
3382
|
+
socketErrors: 0,
|
|
3383
|
+
pendingDelegations: 0,
|
|
3384
|
+
pendingTimers: 0,
|
|
3385
|
+
destroyed: false,
|
|
3386
|
+
lastHandshakeAt: null,
|
|
3387
|
+
lastHandshakeError: null,
|
|
3388
|
+
lastDisconnectAt: null,
|
|
3389
|
+
lastError: null,
|
|
3390
|
+
lastErrorAt: 0,
|
|
3391
|
+
errorHistory: [],
|
|
3392
|
+
updatedAt: now
|
|
3393
|
+
};
|
|
3394
|
+
if (input.serviceName !== void 0) {
|
|
3395
|
+
base.serviceName = String(input.serviceName);
|
|
3396
|
+
}
|
|
3397
|
+
if (input.url !== void 0) {
|
|
3398
|
+
base.url = String(input.url);
|
|
3399
|
+
}
|
|
3400
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3401
|
+
Object.assign(base, patch);
|
|
3402
|
+
base.fetchId = fetchId;
|
|
3403
|
+
base.updatedAt = now;
|
|
3404
|
+
const errorMessage = input.error !== void 0 ? this.getErrorMessage(input.error) : void 0;
|
|
3405
|
+
if (errorMessage) {
|
|
3406
|
+
base.lastError = errorMessage;
|
|
3407
|
+
base.lastErrorAt = now;
|
|
3408
|
+
base.errorHistory.push({
|
|
3409
|
+
at: new Date(now).toISOString(),
|
|
3410
|
+
message: errorMessage
|
|
3411
|
+
});
|
|
3412
|
+
if (base.errorHistory.length > this.diagnosticsErrorHistoryLimit) {
|
|
3413
|
+
base.errorHistory.splice(
|
|
3414
|
+
0,
|
|
3415
|
+
base.errorHistory.length - this.diagnosticsErrorHistoryLimit
|
|
3416
|
+
);
|
|
3309
3417
|
}
|
|
3418
|
+
}
|
|
3419
|
+
entries[fetchId] = base;
|
|
3420
|
+
this.pruneDiagnosticsEntries(entries, now);
|
|
3421
|
+
setState({ entries });
|
|
3422
|
+
},
|
|
3423
|
+
{ mode: "write" }
|
|
3424
|
+
),
|
|
3425
|
+
(context) => String(context?.fetchId ?? "default"),
|
|
3426
|
+
"Upserts socket client diagnostics in actor state."
|
|
3427
|
+
).doOn("meta.socket_client.diagnostics_upsert_requested");
|
|
3428
|
+
}
|
|
3429
|
+
registerSocketServerTasks() {
|
|
3430
|
+
CadenzaService.createThrottledMetaTask(
|
|
3431
|
+
"SocketServerActor.PatchSession",
|
|
3432
|
+
this.socketServerActor.task(
|
|
3433
|
+
({ state, input, setState }) => {
|
|
3434
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3435
|
+
setState({
|
|
3436
|
+
...state,
|
|
3437
|
+
...patch,
|
|
3438
|
+
updatedAt: Date.now()
|
|
3439
|
+
});
|
|
3440
|
+
},
|
|
3441
|
+
{ mode: "write" }
|
|
3442
|
+
),
|
|
3443
|
+
(context) => String(context?.serverKey ?? this.socketServerDefaultKey),
|
|
3444
|
+
"Applies partial durable session updates for socket server actor."
|
|
3445
|
+
).doOn("meta.socket_server.session_patch_requested");
|
|
3446
|
+
CadenzaService.createMetaTask(
|
|
3447
|
+
"SocketServerActor.ClearRuntime",
|
|
3448
|
+
this.socketServerActor.task(
|
|
3449
|
+
({ setRuntimeState }) => {
|
|
3450
|
+
setRuntimeState(null);
|
|
3451
|
+
},
|
|
3452
|
+
{ mode: "write" }
|
|
3453
|
+
),
|
|
3454
|
+
"Clears socket server runtime handle after shutdown."
|
|
3455
|
+
).doOn("meta.socket_server.runtime_clear_requested");
|
|
3456
|
+
const setupSocketServerTask = CadenzaService.createMetaTask(
|
|
3457
|
+
"Setup SocketServer",
|
|
3458
|
+
this.socketServerActor.task(
|
|
3459
|
+
({ state, runtimeState, input, actor, setState, setRuntimeState, emit }) => {
|
|
3460
|
+
const serverKey = this.resolveSocketServerKey(input) ?? actor.key ?? this.socketServerDefaultKey;
|
|
3461
|
+
const shouldUseSocket = Boolean(input.__useSocket);
|
|
3462
|
+
if (!shouldUseSocket) {
|
|
3463
|
+
this.destroySocketServerRuntimeHandle(runtimeState);
|
|
3464
|
+
setRuntimeState(null);
|
|
3465
|
+
setState({
|
|
3466
|
+
...state,
|
|
3467
|
+
serverKey,
|
|
3468
|
+
useSocket: false,
|
|
3469
|
+
status: "inactive",
|
|
3470
|
+
connectionCount: 0,
|
|
3471
|
+
lastShutdownAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3472
|
+
updatedAt: Date.now()
|
|
3473
|
+
});
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3476
|
+
let runtimeHandle = runtimeState;
|
|
3477
|
+
if (!runtimeHandle) {
|
|
3478
|
+
runtimeHandle = this.createSocketServerRuntimeHandleFromContext(input);
|
|
3479
|
+
setRuntimeState(runtimeHandle);
|
|
3480
|
+
}
|
|
3481
|
+
const profile = String(input.__securityProfile ?? state.securityProfile ?? "medium");
|
|
3482
|
+
const networkType = String(input.__networkType ?? state.networkType ?? "internal");
|
|
3483
|
+
const schedulePatch = (patch) => {
|
|
3484
|
+
CadenzaService.emit("meta.socket_server.session_patch_requested", {
|
|
3485
|
+
serverKey,
|
|
3486
|
+
patch
|
|
3487
|
+
});
|
|
3488
|
+
};
|
|
3489
|
+
if (runtimeHandle.initialized) {
|
|
3490
|
+
schedulePatch({
|
|
3491
|
+
status: "active",
|
|
3492
|
+
useSocket: true,
|
|
3493
|
+
securityProfile: profile,
|
|
3494
|
+
networkType,
|
|
3495
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3496
|
+
lastStartedAt: state.lastStartedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3497
|
+
});
|
|
3498
|
+
return;
|
|
3499
|
+
}
|
|
3500
|
+
const server = runtimeHandle.server;
|
|
3501
|
+
runtimeHandle.initialized = true;
|
|
3502
|
+
setState({
|
|
3503
|
+
...state,
|
|
3504
|
+
serverKey,
|
|
3505
|
+
useSocket: true,
|
|
3506
|
+
status: "active",
|
|
3507
|
+
securityProfile: profile,
|
|
3508
|
+
networkType,
|
|
3509
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3510
|
+
lastStartedAt: state.lastStartedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3511
|
+
updatedAt: Date.now()
|
|
3310
3512
|
});
|
|
3311
|
-
const profile = ctx.__securityProfile ?? "medium";
|
|
3312
3513
|
server.use((socket, next) => {
|
|
3313
3514
|
const origin = socket?.handshake?.headers?.origin;
|
|
3314
3515
|
const allowedOrigins = ["*"];
|
|
3315
|
-
const networkType = ctx.__networkType ?? "internal";
|
|
3316
3516
|
let effectiveOrigin = origin || "unknown";
|
|
3317
3517
|
if (networkType === "internal") effectiveOrigin = "internal";
|
|
3318
3518
|
if (profile !== "low" && !allowedOrigins.includes(effectiveOrigin) && !allowedOrigins.includes("*")) {
|
|
@@ -3323,7 +3523,9 @@ var SocketController = class _SocketController {
|
|
|
3323
3523
|
medium: { points: 1e4, duration: 10 },
|
|
3324
3524
|
high: { points: 1e3, duration: 60, blockDuration: 300 }
|
|
3325
3525
|
};
|
|
3326
|
-
const limiter = new RateLimiterMemory2(
|
|
3526
|
+
const limiter = new RateLimiterMemory2(
|
|
3527
|
+
limiterOptions[profile] ?? limiterOptions.medium
|
|
3528
|
+
);
|
|
3327
3529
|
const clientKey = socket?.handshake?.address || "unknown";
|
|
3328
3530
|
socket.use((packet, packetNext) => {
|
|
3329
3531
|
limiter.consume(clientKey).then(() => packetNext()).catch((rej) => {
|
|
@@ -3358,116 +3560,111 @@ var SocketController = class _SocketController {
|
|
|
3358
3560
|
});
|
|
3359
3561
|
next();
|
|
3360
3562
|
});
|
|
3361
|
-
if (!server) {
|
|
3362
|
-
CadenzaService.log("Socket setup error: No server", {}, "error");
|
|
3363
|
-
return { ...ctx, __error: "No server", errored: true };
|
|
3364
|
-
}
|
|
3365
3563
|
server.on("connection", (ws) => {
|
|
3564
|
+
runtimeHandle.connectedSocketIds.add(ws.id);
|
|
3565
|
+
schedulePatch({
|
|
3566
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3567
|
+
lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3568
|
+
status: "active"
|
|
3569
|
+
});
|
|
3366
3570
|
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
|
-
}
|
|
3571
|
+
ws.on("handshake", (ctx, callback) => {
|
|
3572
|
+
CadenzaService.log("SocketServer: New connection", {
|
|
3573
|
+
...ctx,
|
|
3574
|
+
socketId: ws.id
|
|
3575
|
+
});
|
|
3576
|
+
callback({
|
|
3577
|
+
status: "success",
|
|
3578
|
+
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
3579
|
+
});
|
|
3580
|
+
if (ctx.isFrontend) {
|
|
3581
|
+
const fetchId = `browser:${ctx.serviceInstanceId}`;
|
|
3582
|
+
CadenzaService.createMetaTask(
|
|
3583
|
+
`Transmit signal to ${fetchId}`,
|
|
3584
|
+
(c, emitter) => {
|
|
3585
|
+
if (c.__signalName === void 0) {
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
ws.emit("signal", c);
|
|
3589
|
+
if (c.__routineExecId) {
|
|
3590
|
+
emitter(`meta.socket_client.transmitted:${c.__routineExecId}`, {});
|
|
3393
3591
|
}
|
|
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
3592
|
},
|
|
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
|
-
});
|
|
3593
|
+
"Transmit frontend bound signal through active websocket."
|
|
3594
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
3433
3595
|
}
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3596
|
+
CadenzaService.emit("meta.socket.handshake", ctx);
|
|
3597
|
+
});
|
|
3598
|
+
ws.on("delegation", (ctx, callback) => {
|
|
3599
|
+
const deputyExecId = ctx.__metadata.__deputyExecId;
|
|
3600
|
+
CadenzaService.createEphemeralMetaTask(
|
|
3601
|
+
"Resolve delegation",
|
|
3602
|
+
(delegationCtx) => {
|
|
3603
|
+
callback(delegationCtx);
|
|
3604
|
+
},
|
|
3605
|
+
"Resolves a delegation request using client callback.",
|
|
3606
|
+
{ register: false }
|
|
3607
|
+
).doOn(`meta.node.graph_completed:${deputyExecId}`).emits(`meta.socket.delegation_resolved:${deputyExecId}`);
|
|
3608
|
+
CadenzaService.createEphemeralMetaTask(
|
|
3609
|
+
"Delegation progress update",
|
|
3610
|
+
(progressCtx) => {
|
|
3611
|
+
if (progressCtx.__progress !== void 0) {
|
|
3612
|
+
ws.emit("delegation_progress", progressCtx);
|
|
3613
|
+
}
|
|
3614
|
+
},
|
|
3615
|
+
"Updates delegation progress to client.",
|
|
3616
|
+
{
|
|
3617
|
+
once: false,
|
|
3618
|
+
destroyCondition: (progressCtx) => progressCtx.data.progress === 1 || progressCtx.data?.progress === void 0,
|
|
3619
|
+
register: false
|
|
3455
3620
|
}
|
|
3621
|
+
).doOn(
|
|
3622
|
+
`meta.node.routine_execution_progress:${deputyExecId}`,
|
|
3623
|
+
`meta.node.graph_completed:${deputyExecId}`
|
|
3624
|
+
).emitsOnFail(`meta.socket.progress_failed:${deputyExecId}`);
|
|
3625
|
+
CadenzaService.emit("meta.socket.delegation_requested", {
|
|
3626
|
+
...ctx,
|
|
3627
|
+
__name: ctx.__remoteRoutineName
|
|
3628
|
+
});
|
|
3629
|
+
});
|
|
3630
|
+
ws.on("signal", (ctx, callback) => {
|
|
3631
|
+
if (CadenzaService.signalBroker.listObservedSignals().includes(ctx.__signalName)) {
|
|
3632
|
+
callback({
|
|
3633
|
+
__status: "success",
|
|
3634
|
+
__signalName: ctx.__signalName
|
|
3635
|
+
});
|
|
3636
|
+
CadenzaService.emit(ctx.__signalName, ctx);
|
|
3637
|
+
} else {
|
|
3638
|
+
CadenzaService.log(
|
|
3639
|
+
`No such signal ${ctx.__signalName} on ${ctx.__serviceName}`,
|
|
3640
|
+
"warning"
|
|
3641
|
+
);
|
|
3642
|
+
callback({
|
|
3643
|
+
...ctx,
|
|
3644
|
+
__status: "error",
|
|
3645
|
+
__error: `No such signal: ${ctx.__signalName}`,
|
|
3646
|
+
errored: true
|
|
3647
|
+
});
|
|
3456
3648
|
}
|
|
3457
|
-
);
|
|
3649
|
+
});
|
|
3458
3650
|
ws.on(
|
|
3459
3651
|
"status_check",
|
|
3460
|
-
(
|
|
3652
|
+
(ctx, callback) => {
|
|
3461
3653
|
CadenzaService.createEphemeralMetaTask(
|
|
3462
3654
|
"Resolve status check",
|
|
3463
3655
|
callback,
|
|
3464
3656
|
"Resolves a status check request",
|
|
3465
3657
|
{ register: false }
|
|
3466
3658
|
).doAfter(CadenzaService.serviceRegistry.getStatusTask);
|
|
3467
|
-
CadenzaService.emit("meta.socket.status_check_requested",
|
|
3659
|
+
CadenzaService.emit("meta.socket.status_check_requested", ctx);
|
|
3468
3660
|
}
|
|
3469
3661
|
);
|
|
3470
3662
|
ws.on("disconnect", () => {
|
|
3663
|
+
runtimeHandle.connectedSocketIds.delete(ws.id);
|
|
3664
|
+
schedulePatch({
|
|
3665
|
+
connectionCount: runtimeHandle.connectedSocketIds.size,
|
|
3666
|
+
lastDisconnectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3667
|
+
});
|
|
3471
3668
|
CadenzaService.log(
|
|
3472
3669
|
"Socket client disconnected",
|
|
3473
3670
|
{ socketId: ws.id },
|
|
@@ -3477,514 +3674,888 @@ var SocketController = class _SocketController {
|
|
|
3477
3674
|
__wsId: ws.id
|
|
3478
3675
|
});
|
|
3479
3676
|
});
|
|
3480
|
-
} catch (
|
|
3677
|
+
} catch (error) {
|
|
3481
3678
|
CadenzaService.log(
|
|
3482
3679
|
"SocketServer: Error in socket event",
|
|
3483
|
-
{ error
|
|
3680
|
+
{ error },
|
|
3484
3681
|
"error"
|
|
3485
3682
|
);
|
|
3486
3683
|
}
|
|
3487
3684
|
CadenzaService.emit("meta.socket.connected", { __wsId: ws.id });
|
|
3488
3685
|
});
|
|
3489
|
-
CadenzaService.createMetaTask(
|
|
3490
|
-
|
|
3491
|
-
(
|
|
3686
|
+
runtimeHandle.broadcastStatusTask = CadenzaService.createMetaTask(
|
|
3687
|
+
`Broadcast status ${serverKey}`,
|
|
3688
|
+
(ctx) => server.emit("status_update", ctx),
|
|
3492
3689
|
"Broadcasts the status of the server to all clients"
|
|
3493
3690
|
).doOn("meta.service.updated");
|
|
3494
|
-
CadenzaService.createMetaTask(
|
|
3495
|
-
|
|
3496
|
-
() =>
|
|
3691
|
+
runtimeHandle.shutdownTask = CadenzaService.createMetaTask(
|
|
3692
|
+
`Shutdown SocketServer ${serverKey}`,
|
|
3693
|
+
async () => {
|
|
3694
|
+
this.destroySocketServerRuntimeHandle(runtimeHandle);
|
|
3695
|
+
CadenzaService.emit("meta.socket_server.runtime_clear_requested", {
|
|
3696
|
+
serverKey
|
|
3697
|
+
});
|
|
3698
|
+
CadenzaService.emit("meta.socket_server.session_patch_requested", {
|
|
3699
|
+
serverKey,
|
|
3700
|
+
patch: {
|
|
3701
|
+
useSocket: false,
|
|
3702
|
+
status: "shutdown",
|
|
3703
|
+
connectionCount: 0,
|
|
3704
|
+
lastShutdownAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3705
|
+
}
|
|
3706
|
+
});
|
|
3707
|
+
},
|
|
3497
3708
|
"Shuts down the socket server"
|
|
3498
3709
|
).doOn("meta.socket_server_shutdown_requested").emits("meta.socket.shutdown");
|
|
3499
|
-
return
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3710
|
+
return true;
|
|
3711
|
+
},
|
|
3712
|
+
{ mode: "write" }
|
|
3713
|
+
),
|
|
3714
|
+
"Initializes socket server runtime through actor state."
|
|
3715
|
+
);
|
|
3716
|
+
setupSocketServerTask.doOn("global.meta.rest.network_configured");
|
|
3717
|
+
}
|
|
3718
|
+
registerSocketClientTasks() {
|
|
3719
|
+
CadenzaService.createThrottledMetaTask(
|
|
3720
|
+
"SocketClientActor.ApplySessionOperation",
|
|
3721
|
+
this.socketClientActor.task(
|
|
3722
|
+
({ state, input, setState }) => {
|
|
3723
|
+
const operation = String(
|
|
3724
|
+
input.operation ?? "transmit"
|
|
3725
|
+
);
|
|
3726
|
+
const patch = input.patch && typeof input.patch === "object" ? input.patch : {};
|
|
3727
|
+
let next = {
|
|
3728
|
+
...state,
|
|
3729
|
+
...patch,
|
|
3730
|
+
communicationTypes: patch.communicationTypes !== void 0 ? this.normalizeCommunicationTypes(patch.communicationTypes) : state.communicationTypes,
|
|
3731
|
+
updatedAt: Date.now()
|
|
3732
|
+
};
|
|
3733
|
+
if (input.serviceName !== void 0) {
|
|
3734
|
+
next.serviceName = String(input.serviceName);
|
|
3735
|
+
}
|
|
3736
|
+
if (input.serviceAddress !== void 0) {
|
|
3737
|
+
next.serviceAddress = String(input.serviceAddress);
|
|
3738
|
+
}
|
|
3739
|
+
if (input.serviceInstanceId !== void 0) {
|
|
3740
|
+
next.serviceInstanceId = String(input.serviceInstanceId);
|
|
3741
|
+
}
|
|
3742
|
+
if (input.protocol !== void 0) {
|
|
3743
|
+
next.protocol = String(input.protocol);
|
|
3744
|
+
}
|
|
3745
|
+
if (input.url !== void 0) {
|
|
3746
|
+
next.url = String(input.url);
|
|
3747
|
+
}
|
|
3748
|
+
if (input.servicePort !== void 0) {
|
|
3749
|
+
next.servicePort = Number(input.servicePort);
|
|
3750
|
+
}
|
|
3751
|
+
if (input.fetchId !== void 0) {
|
|
3752
|
+
next.fetchId = String(input.fetchId);
|
|
3753
|
+
}
|
|
3754
|
+
if (operation === "connect") {
|
|
3755
|
+
next.destroyed = false;
|
|
3756
|
+
} else if (operation === "handshake") {
|
|
3757
|
+
next.destroyed = false;
|
|
3758
|
+
next.connected = patch.connected ?? true;
|
|
3759
|
+
next.handshake = patch.handshake ?? true;
|
|
3760
|
+
} else if (operation === "shutdown") {
|
|
3761
|
+
next.connected = false;
|
|
3762
|
+
next.handshake = false;
|
|
3763
|
+
next.destroyed = true;
|
|
3764
|
+
next.pendingDelegations = 0;
|
|
3765
|
+
next.pendingTimers = 0;
|
|
3766
|
+
}
|
|
3767
|
+
setState(next);
|
|
3768
|
+
return next;
|
|
3769
|
+
},
|
|
3770
|
+
{ mode: "write" }
|
|
3771
|
+
),
|
|
3772
|
+
(context) => String(this.resolveSocketClientFetchId(context ?? {}) ?? "default"),
|
|
3773
|
+
"Applies socket client session operation patch in actor durable state."
|
|
3774
|
+
).doOn("meta.socket_client.session_operation_requested");
|
|
3775
|
+
CadenzaService.createMetaTask(
|
|
3776
|
+
"SocketClientActor.ClearRuntime",
|
|
3777
|
+
this.socketClientActor.task(
|
|
3778
|
+
({ setRuntimeState }) => {
|
|
3779
|
+
setRuntimeState(null);
|
|
3780
|
+
},
|
|
3781
|
+
{ mode: "write" }
|
|
3782
|
+
),
|
|
3783
|
+
"Clears socket client runtime handle."
|
|
3784
|
+
).doOn("meta.socket_client.runtime_clear_requested");
|
|
3504
3785
|
CadenzaService.createMetaTask(
|
|
3505
3786
|
"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,
|
|
3787
|
+
this.socketClientActor.task(
|
|
3788
|
+
({ state, runtimeState, input, setState, setRuntimeState, emit }) => {
|
|
3789
|
+
const serviceInstanceId = String(input.serviceInstanceId ?? "");
|
|
3790
|
+
const communicationTypes = this.normalizeCommunicationTypes(
|
|
3791
|
+
input.communicationTypes
|
|
3792
|
+
);
|
|
3793
|
+
const serviceName = String(input.serviceName ?? "");
|
|
3794
|
+
const serviceAddress = String(input.serviceAddress ?? "");
|
|
3795
|
+
const protocol = String(input.protocol ?? "http");
|
|
3796
|
+
const normalizedPort = this.resolveServicePort(protocol, input.servicePort);
|
|
3797
|
+
if (!serviceAddress || !normalizedPort) {
|
|
3798
|
+
CadenzaService.log(
|
|
3799
|
+
"Socket client setup skipped due to missing address/port",
|
|
3800
|
+
{
|
|
3563
3801
|
serviceName,
|
|
3564
|
-
|
|
3802
|
+
serviceAddress,
|
|
3803
|
+
servicePort: input.servicePort,
|
|
3804
|
+
protocol
|
|
3805
|
+
},
|
|
3806
|
+
"warning"
|
|
3807
|
+
);
|
|
3808
|
+
return false;
|
|
3809
|
+
}
|
|
3810
|
+
const socketProtocol = protocol === "https" ? "wss" : "ws";
|
|
3811
|
+
const url = `${socketProtocol}://${serviceAddress}:${normalizedPort}`;
|
|
3812
|
+
const fetchId = `${serviceAddress}_${normalizedPort}`;
|
|
3813
|
+
const applySessionOperation = (operation, patch = {}) => {
|
|
3814
|
+
CadenzaService.emit("meta.socket_client.session_operation_requested", {
|
|
3815
|
+
fetchId,
|
|
3816
|
+
operation,
|
|
3817
|
+
patch,
|
|
3818
|
+
serviceInstanceId,
|
|
3819
|
+
communicationTypes,
|
|
3820
|
+
serviceName,
|
|
3821
|
+
serviceAddress,
|
|
3822
|
+
servicePort: normalizedPort,
|
|
3823
|
+
protocol,
|
|
3824
|
+
url
|
|
3825
|
+
});
|
|
3826
|
+
};
|
|
3827
|
+
const upsertDiagnostics = (patch, error) => {
|
|
3828
|
+
CadenzaService.emit("meta.socket_client.diagnostics_upsert_requested", {
|
|
3829
|
+
fetchId,
|
|
3830
|
+
serviceName,
|
|
3831
|
+
url,
|
|
3832
|
+
patch,
|
|
3833
|
+
error
|
|
3834
|
+
});
|
|
3835
|
+
};
|
|
3836
|
+
setState({
|
|
3837
|
+
...state,
|
|
3838
|
+
fetchId,
|
|
3839
|
+
serviceInstanceId,
|
|
3840
|
+
communicationTypes,
|
|
3841
|
+
serviceName,
|
|
3842
|
+
serviceAddress,
|
|
3843
|
+
servicePort: normalizedPort,
|
|
3844
|
+
protocol,
|
|
3845
|
+
url,
|
|
3846
|
+
destroyed: false,
|
|
3847
|
+
updatedAt: Date.now()
|
|
3848
|
+
});
|
|
3849
|
+
let runtimeHandle = runtimeState;
|
|
3850
|
+
if (!runtimeHandle || runtimeHandle.url !== url) {
|
|
3851
|
+
this.destroySocketClientRuntimeHandle(runtimeHandle);
|
|
3852
|
+
runtimeHandle = this.createSocketClientRuntimeHandle(url);
|
|
3853
|
+
setRuntimeState(runtimeHandle);
|
|
3854
|
+
}
|
|
3855
|
+
upsertDiagnostics({
|
|
3856
|
+
destroyed: false,
|
|
3857
|
+
connected: false,
|
|
3858
|
+
handshake: false,
|
|
3859
|
+
socketId: runtimeHandle.socket.id ?? null
|
|
3860
|
+
});
|
|
3861
|
+
applySessionOperation("connect", {
|
|
3862
|
+
destroyed: false,
|
|
3863
|
+
connected: false,
|
|
3864
|
+
handshake: false,
|
|
3865
|
+
socketId: runtimeHandle.socket.id ?? null,
|
|
3866
|
+
pendingDelegations: runtimeHandle.pendingDelegationIds.size,
|
|
3867
|
+
pendingTimers: runtimeHandle.pendingTimers.size,
|
|
3868
|
+
errorCount: runtimeHandle.errorCount
|
|
3869
|
+
});
|
|
3870
|
+
if (runtimeHandle.initialized) {
|
|
3871
|
+
return true;
|
|
3872
|
+
}
|
|
3873
|
+
runtimeHandle.initialized = true;
|
|
3874
|
+
runtimeHandle.handshake = false;
|
|
3875
|
+
runtimeHandle.errorCount = 0;
|
|
3876
|
+
const syncPendingCounts = () => {
|
|
3877
|
+
const pendingDelegations = runtimeHandle.pendingDelegationIds.size;
|
|
3878
|
+
const pendingTimers = runtimeHandle.pendingTimers.size;
|
|
3879
|
+
upsertDiagnostics({
|
|
3880
|
+
pendingDelegations,
|
|
3881
|
+
pendingTimers
|
|
3882
|
+
});
|
|
3883
|
+
applySessionOperation("delegate", {
|
|
3884
|
+
pendingDelegations,
|
|
3885
|
+
pendingTimers
|
|
3886
|
+
});
|
|
3887
|
+
};
|
|
3888
|
+
runtimeHandle.emitWhenReady = (event, data, timeoutMs = 6e4, ack) => {
|
|
3889
|
+
return new Promise((resolve) => {
|
|
3890
|
+
const parsedTimeout = Number(timeoutMs);
|
|
3891
|
+
const normalizedTimeoutMs = Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? Math.trunc(parsedTimeout) : 6e4;
|
|
3892
|
+
let timer = null;
|
|
3893
|
+
let settled = false;
|
|
3894
|
+
const clearPendingTimer = () => {
|
|
3895
|
+
if (!timer) {
|
|
3896
|
+
return;
|
|
3897
|
+
}
|
|
3898
|
+
clearTimeout(timer);
|
|
3899
|
+
runtimeHandle.pendingTimers.delete(timer);
|
|
3900
|
+
syncPendingCounts();
|
|
3901
|
+
timer = null;
|
|
3902
|
+
};
|
|
3903
|
+
const settle = (response) => {
|
|
3904
|
+
if (settled) {
|
|
3905
|
+
return;
|
|
3906
|
+
}
|
|
3907
|
+
settled = true;
|
|
3908
|
+
clearPendingTimer();
|
|
3909
|
+
if (ack) ack(response);
|
|
3910
|
+
resolve(response);
|
|
3911
|
+
};
|
|
3912
|
+
const resolveWithError = (errorMessage, fallbackError) => {
|
|
3913
|
+
settle({
|
|
3914
|
+
...data,
|
|
3915
|
+
errored: true,
|
|
3916
|
+
__error: errorMessage,
|
|
3917
|
+
error: fallbackError instanceof Error ? fallbackError.message : errorMessage,
|
|
3918
|
+
socketId: runtimeHandle.socket.id,
|
|
3919
|
+
serviceName,
|
|
3920
|
+
url
|
|
3921
|
+
});
|
|
3922
|
+
};
|
|
3923
|
+
const tryEmit = async () => {
|
|
3924
|
+
const waitResult = await waitForSocketConnection(
|
|
3925
|
+
runtimeHandle.socket,
|
|
3926
|
+
normalizedTimeoutMs + 10,
|
|
3927
|
+
(reason, error) => {
|
|
3928
|
+
if (reason === "connect_timeout") {
|
|
3929
|
+
return `Socket connect timed out before '${event}'`;
|
|
3930
|
+
}
|
|
3931
|
+
if (reason === "connect_error") {
|
|
3932
|
+
const errMessage = error instanceof Error ? error.message : String(error);
|
|
3933
|
+
return `Socket connect error before '${event}': ${errMessage}`;
|
|
3934
|
+
}
|
|
3935
|
+
return `Socket disconnected before '${event}'`;
|
|
3936
|
+
}
|
|
3937
|
+
);
|
|
3938
|
+
if (!waitResult.ok) {
|
|
3939
|
+
CadenzaService.log(
|
|
3940
|
+
waitResult.error,
|
|
3941
|
+
{
|
|
3942
|
+
socketId: runtimeHandle.socket.id,
|
|
3943
|
+
serviceName,
|
|
3944
|
+
url,
|
|
3945
|
+
event
|
|
3946
|
+
},
|
|
3947
|
+
"error"
|
|
3948
|
+
);
|
|
3949
|
+
upsertDiagnostics({}, waitResult.error);
|
|
3950
|
+
resolveWithError(waitResult.error);
|
|
3951
|
+
return;
|
|
3952
|
+
}
|
|
3953
|
+
timer = setTimeout(() => {
|
|
3954
|
+
if (settled) {
|
|
3955
|
+
return;
|
|
3956
|
+
}
|
|
3957
|
+
clearPendingTimer();
|
|
3958
|
+
const message = `Socket event '${event}' timed out`;
|
|
3959
|
+
CadenzaService.log(
|
|
3960
|
+
message,
|
|
3961
|
+
{ socketId: runtimeHandle.socket.id, serviceName, url },
|
|
3962
|
+
"error"
|
|
3963
|
+
);
|
|
3964
|
+
upsertDiagnostics(
|
|
3965
|
+
{
|
|
3966
|
+
lastHandshakeError: message
|
|
3967
|
+
},
|
|
3968
|
+
message
|
|
3969
|
+
);
|
|
3970
|
+
applySessionOperation("transmit", {
|
|
3971
|
+
lastHandshakeError: message
|
|
3972
|
+
});
|
|
3973
|
+
resolveWithError(message);
|
|
3974
|
+
}, normalizedTimeoutMs + 10);
|
|
3975
|
+
runtimeHandle.pendingTimers.add(timer);
|
|
3976
|
+
syncPendingCounts();
|
|
3977
|
+
runtimeHandle.socket.timeout(normalizedTimeoutMs).emit(event, data, (err, response) => {
|
|
3978
|
+
if (err) {
|
|
3979
|
+
CadenzaService.log(
|
|
3980
|
+
"Socket timeout.",
|
|
3981
|
+
{
|
|
3982
|
+
event,
|
|
3983
|
+
error: err.message,
|
|
3984
|
+
socketId: runtimeHandle.socket.id,
|
|
3985
|
+
serviceName
|
|
3986
|
+
},
|
|
3987
|
+
"warning"
|
|
3988
|
+
);
|
|
3989
|
+
upsertDiagnostics(
|
|
3990
|
+
{
|
|
3991
|
+
lastHandshakeError: err.message
|
|
3992
|
+
},
|
|
3993
|
+
err
|
|
3994
|
+
);
|
|
3995
|
+
applySessionOperation("transmit", {
|
|
3996
|
+
lastHandshakeError: err.message
|
|
3997
|
+
});
|
|
3998
|
+
response = {
|
|
3999
|
+
__error: `Timeout error: ${err}`,
|
|
4000
|
+
errored: true,
|
|
4001
|
+
...data
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
settle(response);
|
|
4005
|
+
});
|
|
4006
|
+
};
|
|
4007
|
+
void tryEmit().catch((error) => {
|
|
4008
|
+
CadenzaService.log(
|
|
4009
|
+
"Socket emit failed unexpectedly",
|
|
4010
|
+
{
|
|
4011
|
+
event,
|
|
4012
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4013
|
+
socketId: runtimeHandle.socket.id,
|
|
4014
|
+
serviceName,
|
|
4015
|
+
url
|
|
4016
|
+
},
|
|
4017
|
+
"error"
|
|
4018
|
+
);
|
|
4019
|
+
const message = `Socket event '${event}' failed`;
|
|
4020
|
+
upsertDiagnostics(
|
|
4021
|
+
{
|
|
4022
|
+
lastHandshakeError: error instanceof Error ? error.message : String(error)
|
|
4023
|
+
},
|
|
4024
|
+
error
|
|
4025
|
+
);
|
|
4026
|
+
applySessionOperation("transmit", {
|
|
4027
|
+
lastHandshakeError: error instanceof Error ? error.message : String(error)
|
|
4028
|
+
});
|
|
4029
|
+
resolveWithError(message, error);
|
|
4030
|
+
});
|
|
4031
|
+
});
|
|
4032
|
+
};
|
|
4033
|
+
const socket = runtimeHandle.socket;
|
|
4034
|
+
socket.on("connect", () => {
|
|
4035
|
+
if (runtimeHandle.handshake) return;
|
|
4036
|
+
upsertDiagnostics({
|
|
4037
|
+
connected: true,
|
|
4038
|
+
destroyed: false,
|
|
4039
|
+
socketId: socket.id ?? null
|
|
4040
|
+
});
|
|
4041
|
+
applySessionOperation("connect", {
|
|
4042
|
+
connected: true,
|
|
4043
|
+
destroyed: false,
|
|
4044
|
+
socketId: socket.id ?? null
|
|
4045
|
+
});
|
|
4046
|
+
CadenzaService.emit(`meta.socket_client.connected:${fetchId}`, input);
|
|
4047
|
+
});
|
|
4048
|
+
socket.on("delegation_progress", (delegationCtx) => {
|
|
4049
|
+
CadenzaService.emit(
|
|
4050
|
+
`meta.socket_client.delegation_progress:${delegationCtx.__metadata.__deputyExecId}`,
|
|
4051
|
+
delegationCtx
|
|
4052
|
+
);
|
|
4053
|
+
});
|
|
4054
|
+
socket.on("signal", (signalCtx) => {
|
|
4055
|
+
if (CadenzaService.signalBroker.listObservedSignals().includes(signalCtx.__signalName)) {
|
|
4056
|
+
CadenzaService.emit(signalCtx.__signalName, signalCtx);
|
|
4057
|
+
}
|
|
4058
|
+
});
|
|
4059
|
+
socket.on("status_update", (status) => {
|
|
4060
|
+
CadenzaService.emit("meta.socket_client.status_received", status);
|
|
4061
|
+
});
|
|
4062
|
+
socket.on("connect_error", (err) => {
|
|
4063
|
+
runtimeHandle.handshake = false;
|
|
4064
|
+
upsertDiagnostics(
|
|
4065
|
+
{
|
|
4066
|
+
connected: false,
|
|
4067
|
+
handshake: false,
|
|
4068
|
+
connectErrors: state.connectErrors + 1,
|
|
4069
|
+
lastHandshakeError: err.message
|
|
4070
|
+
},
|
|
4071
|
+
err
|
|
4072
|
+
);
|
|
4073
|
+
applySessionOperation("connect", {
|
|
4074
|
+
connected: false,
|
|
4075
|
+
handshake: false,
|
|
4076
|
+
connectErrors: state.connectErrors + 1,
|
|
4077
|
+
lastHandshakeError: err.message
|
|
4078
|
+
});
|
|
4079
|
+
CadenzaService.log(
|
|
4080
|
+
"Socket connect error",
|
|
4081
|
+
{
|
|
4082
|
+
error: err.message,
|
|
4083
|
+
serviceName,
|
|
4084
|
+
socketId: socket.id,
|
|
4085
|
+
url
|
|
4086
|
+
},
|
|
4087
|
+
"error"
|
|
4088
|
+
);
|
|
4089
|
+
CadenzaService.emit(`meta.socket_client.connect_error:${fetchId}`, err);
|
|
4090
|
+
});
|
|
4091
|
+
socket.on("reconnect_attempt", (attempt) => {
|
|
4092
|
+
upsertDiagnostics({ reconnectAttempts: attempt });
|
|
4093
|
+
applySessionOperation("connect", {
|
|
4094
|
+
reconnectAttempts: attempt
|
|
4095
|
+
});
|
|
4096
|
+
CadenzaService.log(`Reconnect attempt: ${attempt}`);
|
|
4097
|
+
});
|
|
4098
|
+
socket.on("reconnect", (attempt) => {
|
|
4099
|
+
upsertDiagnostics({ connected: true });
|
|
4100
|
+
applySessionOperation("connect", {
|
|
4101
|
+
connected: true
|
|
4102
|
+
});
|
|
4103
|
+
CadenzaService.log(`Socket reconnected after ${attempt} tries`, {
|
|
4104
|
+
socketId: socket.id,
|
|
4105
|
+
url,
|
|
4106
|
+
serviceName
|
|
4107
|
+
});
|
|
4108
|
+
});
|
|
4109
|
+
socket.on("reconnect_error", (err) => {
|
|
4110
|
+
runtimeHandle.handshake = false;
|
|
4111
|
+
upsertDiagnostics(
|
|
4112
|
+
{
|
|
4113
|
+
connected: false,
|
|
4114
|
+
handshake: false,
|
|
4115
|
+
reconnectErrors: state.reconnectErrors + 1,
|
|
4116
|
+
lastHandshakeError: err.message
|
|
4117
|
+
},
|
|
4118
|
+
err
|
|
4119
|
+
);
|
|
4120
|
+
applySessionOperation("connect", {
|
|
4121
|
+
connected: false,
|
|
4122
|
+
handshake: false,
|
|
4123
|
+
reconnectErrors: state.reconnectErrors + 1,
|
|
4124
|
+
lastHandshakeError: err.message
|
|
4125
|
+
});
|
|
4126
|
+
CadenzaService.log(
|
|
4127
|
+
"Socket reconnect failed.",
|
|
4128
|
+
{ error: err.message, serviceName, url, socketId: socket.id },
|
|
4129
|
+
"warning"
|
|
4130
|
+
);
|
|
4131
|
+
});
|
|
4132
|
+
socket.on("error", (err) => {
|
|
4133
|
+
runtimeHandle.errorCount += 1;
|
|
4134
|
+
upsertDiagnostics(
|
|
4135
|
+
{
|
|
4136
|
+
socketErrors: state.socketErrors + 1,
|
|
4137
|
+
lastHandshakeError: this.getErrorMessage(err)
|
|
4138
|
+
},
|
|
4139
|
+
err
|
|
4140
|
+
);
|
|
4141
|
+
applySessionOperation("transmit", {
|
|
4142
|
+
socketErrors: state.socketErrors + 1,
|
|
4143
|
+
errorCount: runtimeHandle.errorCount,
|
|
4144
|
+
lastHandshakeError: this.getErrorMessage(err)
|
|
4145
|
+
});
|
|
4146
|
+
CadenzaService.log(
|
|
4147
|
+
"Socket error",
|
|
4148
|
+
{ error: err, socketId: socket.id, url, serviceName },
|
|
4149
|
+
"error"
|
|
4150
|
+
);
|
|
4151
|
+
CadenzaService.emit("meta.socket_client.error", err);
|
|
4152
|
+
});
|
|
4153
|
+
socket.on("disconnect", () => {
|
|
4154
|
+
const disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4155
|
+
upsertDiagnostics({
|
|
4156
|
+
connected: false,
|
|
4157
|
+
handshake: false,
|
|
4158
|
+
lastDisconnectAt: disconnectedAt
|
|
4159
|
+
});
|
|
4160
|
+
applySessionOperation("connect", {
|
|
4161
|
+
connected: false,
|
|
4162
|
+
handshake: false,
|
|
4163
|
+
lastDisconnectAt: disconnectedAt
|
|
4164
|
+
});
|
|
4165
|
+
CadenzaService.log(
|
|
4166
|
+
"Socket disconnected.",
|
|
4167
|
+
{ url, serviceName, socketId: socket.id },
|
|
4168
|
+
"warning"
|
|
4169
|
+
);
|
|
4170
|
+
CadenzaService.emit(`meta.socket_client.disconnected:${fetchId}`, {
|
|
4171
|
+
serviceName,
|
|
4172
|
+
serviceAddress,
|
|
4173
|
+
servicePort: normalizedPort
|
|
4174
|
+
});
|
|
4175
|
+
runtimeHandle.handshake = false;
|
|
4176
|
+
});
|
|
4177
|
+
socket.connect();
|
|
4178
|
+
runtimeHandle.handshakeTask = CadenzaService.createMetaTask(
|
|
4179
|
+
`Socket handshake with ${url}`,
|
|
4180
|
+
async (_ctx, emitter) => {
|
|
4181
|
+
if (runtimeHandle.handshake) return;
|
|
4182
|
+
runtimeHandle.handshake = true;
|
|
4183
|
+
upsertDiagnostics({
|
|
4184
|
+
handshake: true
|
|
3565
4185
|
});
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
4186
|
+
applySessionOperation("handshake", {
|
|
4187
|
+
handshake: true
|
|
4188
|
+
});
|
|
4189
|
+
await runtimeHandle.emitWhenReady?.(
|
|
4190
|
+
"handshake",
|
|
4191
|
+
{
|
|
4192
|
+
serviceInstanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
4193
|
+
serviceName: CadenzaService.serviceRegistry.serviceName,
|
|
4194
|
+
isFrontend: isBrowser,
|
|
4195
|
+
__status: "success"
|
|
4196
|
+
},
|
|
4197
|
+
1e4,
|
|
4198
|
+
(result) => {
|
|
4199
|
+
if (result.status === "success") {
|
|
4200
|
+
const handshakeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4201
|
+
upsertDiagnostics({
|
|
4202
|
+
connected: true,
|
|
4203
|
+
handshake: true,
|
|
4204
|
+
lastHandshakeAt: handshakeAt,
|
|
4205
|
+
lastHandshakeError: null,
|
|
4206
|
+
socketId: socket.id ?? null
|
|
4207
|
+
});
|
|
4208
|
+
applySessionOperation("handshake", {
|
|
4209
|
+
connected: true,
|
|
4210
|
+
handshake: true,
|
|
4211
|
+
lastHandshakeAt: handshakeAt,
|
|
4212
|
+
lastHandshakeError: null,
|
|
4213
|
+
socketId: socket.id ?? null
|
|
4214
|
+
});
|
|
4215
|
+
CadenzaService.log("Socket client connected", {
|
|
4216
|
+
result,
|
|
4217
|
+
serviceName,
|
|
4218
|
+
socketId: socket.id,
|
|
4219
|
+
url
|
|
4220
|
+
});
|
|
4221
|
+
} else {
|
|
4222
|
+
const errorMessage = result?.__error ?? result?.error ?? "Socket handshake failed";
|
|
4223
|
+
upsertDiagnostics(
|
|
4224
|
+
{
|
|
4225
|
+
connected: false,
|
|
4226
|
+
handshake: false,
|
|
4227
|
+
lastHandshakeError: errorMessage
|
|
4228
|
+
},
|
|
4229
|
+
errorMessage
|
|
4230
|
+
);
|
|
4231
|
+
applySessionOperation("handshake", {
|
|
4232
|
+
connected: false,
|
|
4233
|
+
handshake: false,
|
|
4234
|
+
lastHandshakeError: errorMessage
|
|
4235
|
+
});
|
|
4236
|
+
CadenzaService.log(
|
|
4237
|
+
"Socket handshake failed",
|
|
4238
|
+
{ result, serviceName, socketId: socket.id, url },
|
|
4239
|
+
"warning"
|
|
4240
|
+
);
|
|
3579
4241
|
}
|
|
3580
|
-
|
|
4242
|
+
void emitter;
|
|
3581
4243
|
}
|
|
3582
4244
|
);
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
fetchId,
|
|
3591
|
-
serviceName,
|
|
3592
|
-
URL,
|
|
3593
|
-
waitResult.error
|
|
3594
|
-
);
|
|
3595
|
-
resolveWithError(waitResult.error);
|
|
4245
|
+
},
|
|
4246
|
+
"Handshakes with socket server"
|
|
4247
|
+
).doOn(`meta.socket_client.connected:${fetchId}`);
|
|
4248
|
+
runtimeHandle.delegateTask = CadenzaService.createMetaTask(
|
|
4249
|
+
`Delegate flow to Socket service ${url}`,
|
|
4250
|
+
async (delegateCtx, emitter) => {
|
|
4251
|
+
if (delegateCtx.__remoteRoutineName === void 0) {
|
|
3596
4252
|
return;
|
|
3597
4253
|
}
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
timer = null;
|
|
3605
|
-
}
|
|
3606
|
-
CadenzaService.log(
|
|
3607
|
-
`Socket event '${event}' timed out`,
|
|
3608
|
-
{ socketId: socket?.id, serviceName, URL },
|
|
3609
|
-
"error"
|
|
3610
|
-
);
|
|
3611
|
-
this.recordSocketClientError(
|
|
3612
|
-
fetchId,
|
|
3613
|
-
serviceName,
|
|
3614
|
-
URL,
|
|
3615
|
-
`Socket event '${event}' timed out`
|
|
3616
|
-
);
|
|
3617
|
-
resolveWithError(`Socket event '${event}' timed out`);
|
|
3618
|
-
}, timeoutMs + 10);
|
|
3619
|
-
pendingTimers.add(timer);
|
|
4254
|
+
delete delegateCtx.__isSubMeta;
|
|
4255
|
+
delete delegateCtx.__broadcast;
|
|
4256
|
+
const deputyExecId = delegateCtx.__metadata?.__deputyExecId;
|
|
4257
|
+
const requestSentAt = Date.now();
|
|
4258
|
+
if (deputyExecId) {
|
|
4259
|
+
runtimeHandle.pendingDelegationIds.add(deputyExecId);
|
|
3620
4260
|
syncPendingCounts();
|
|
3621
4261
|
}
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
4262
|
+
try {
|
|
4263
|
+
const resultContext = await runtimeHandle.emitWhenReady?.(
|
|
4264
|
+
"delegation",
|
|
4265
|
+
delegateCtx,
|
|
4266
|
+
delegateCtx.__timeout ?? 6e4
|
|
4267
|
+
) ?? {
|
|
4268
|
+
errored: true,
|
|
4269
|
+
__error: "Socket delegation returned no response"
|
|
4270
|
+
};
|
|
4271
|
+
const requestDuration = Date.now() - requestSentAt;
|
|
4272
|
+
const metadata = resultContext.__metadata;
|
|
4273
|
+
delete resultContext.__metadata;
|
|
4274
|
+
if (deputyExecId) {
|
|
4275
|
+
emitter(`meta.socket_client.delegated:${deputyExecId}`, {
|
|
4276
|
+
...resultContext,
|
|
4277
|
+
...metadata,
|
|
4278
|
+
__requestDuration: requestDuration
|
|
4279
|
+
});
|
|
3635
4280
|
}
|
|
3636
|
-
if (
|
|
3637
|
-
|
|
3638
|
-
|
|
4281
|
+
if (resultContext?.errored || resultContext?.failed) {
|
|
4282
|
+
const errorMessage = resultContext?.__error ?? resultContext?.error ?? "Socket delegation failed";
|
|
4283
|
+
upsertDiagnostics(
|
|
3639
4284
|
{
|
|
3640
|
-
|
|
3641
|
-
error: err.message,
|
|
3642
|
-
socketId: socket?.id,
|
|
3643
|
-
serviceName
|
|
4285
|
+
lastHandshakeError: String(errorMessage)
|
|
3644
4286
|
},
|
|
3645
|
-
|
|
3646
|
-
);
|
|
3647
|
-
this.recordSocketClientError(
|
|
3648
|
-
fetchId,
|
|
3649
|
-
serviceName,
|
|
3650
|
-
URL,
|
|
3651
|
-
err
|
|
4287
|
+
errorMessage
|
|
3652
4288
|
);
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
...ctx,
|
|
3657
|
-
...ctx.__metadata
|
|
3658
|
-
};
|
|
4289
|
+
applySessionOperation("delegate", {
|
|
4290
|
+
lastHandshakeError: String(errorMessage)
|
|
4291
|
+
});
|
|
3659
4292
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
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
|
|
3719
|
-
});
|
|
3720
|
-
});
|
|
3721
|
-
socket.on("reconnect_error", (err) => {
|
|
3722
|
-
handshake = false;
|
|
3723
|
-
socketDiagnostics.connected = false;
|
|
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
|
|
3762
|
-
});
|
|
3763
|
-
handshake = false;
|
|
3764
|
-
});
|
|
3765
|
-
socket.connect();
|
|
3766
|
-
handshakeTask = CadenzaService.createMetaTask(
|
|
3767
|
-
`Socket handshake with ${URL}`,
|
|
3768
|
-
async (ctx2, emit) => {
|
|
3769
|
-
if (handshake) return;
|
|
3770
|
-
handshake = true;
|
|
3771
|
-
socketDiagnostics.handshake = true;
|
|
3772
|
-
socketDiagnostics.updatedAt = Date.now();
|
|
3773
|
-
await emitWhenReady?.(
|
|
3774
|
-
"handshake",
|
|
3775
|
-
{
|
|
3776
|
-
serviceInstanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
3777
|
-
serviceName: CadenzaService.serviceRegistry.serviceName,
|
|
3778
|
-
isFrontend: isBrowser,
|
|
3779
|
-
__status: "success"
|
|
3780
|
-
},
|
|
3781
|
-
1e4,
|
|
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
|
|
4293
|
+
return resultContext;
|
|
4294
|
+
} catch (error) {
|
|
4295
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4296
|
+
const failedContext = {
|
|
4297
|
+
errored: true,
|
|
4298
|
+
__error: message
|
|
4299
|
+
};
|
|
4300
|
+
if (deputyExecId) {
|
|
4301
|
+
emitter(`meta.socket_client.delegated:${deputyExecId}`, {
|
|
4302
|
+
...failedContext,
|
|
4303
|
+
__requestDuration: Date.now() - requestSentAt
|
|
3794
4304
|
});
|
|
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
4305
|
}
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
delete ctx2.__broadcast;
|
|
3826
|
-
const requestSentAt = Date.now();
|
|
3827
|
-
pendingDelegationIds.add(ctx2.__metadata.__deputyExecId);
|
|
3828
|
-
syncPendingCounts();
|
|
3829
|
-
emitWhenReady?.(
|
|
3830
|
-
"delegation",
|
|
3831
|
-
ctx2,
|
|
3832
|
-
ctx2.__timeout ?? 6e4,
|
|
3833
|
-
(resultContext) => {
|
|
3834
|
-
const requestDuration = Date.now() - requestSentAt;
|
|
3835
|
-
const metadata = resultContext.__metadata;
|
|
3836
|
-
delete resultContext.__metadata;
|
|
3837
|
-
emit(
|
|
3838
|
-
`meta.socket_client.delegated:${ctx2.__metadata.__deputyExecId}`,
|
|
3839
|
-
{
|
|
3840
|
-
...resultContext,
|
|
3841
|
-
...metadata,
|
|
3842
|
-
__requestDuration: requestDuration
|
|
3843
|
-
}
|
|
3844
|
-
);
|
|
3845
|
-
pendingDelegationIds.delete(ctx2.__metadata.__deputyExecId);
|
|
4306
|
+
upsertDiagnostics(
|
|
4307
|
+
{
|
|
4308
|
+
lastHandshakeError: message
|
|
4309
|
+
},
|
|
4310
|
+
error
|
|
4311
|
+
);
|
|
4312
|
+
applySessionOperation("delegate", {
|
|
4313
|
+
lastHandshakeError: message
|
|
4314
|
+
});
|
|
4315
|
+
return failedContext;
|
|
4316
|
+
} finally {
|
|
4317
|
+
if (deputyExecId) {
|
|
4318
|
+
runtimeHandle.pendingDelegationIds.delete(deputyExecId);
|
|
3846
4319
|
syncPendingCounts();
|
|
3847
|
-
if (resultContext?.errored || resultContext?.failed) {
|
|
3848
|
-
this.recordSocketClientError(
|
|
3849
|
-
fetchId,
|
|
3850
|
-
serviceName,
|
|
3851
|
-
URL,
|
|
3852
|
-
resultContext?.__error ?? resultContext?.error ?? "Socket delegation failed"
|
|
3853
|
-
);
|
|
3854
|
-
}
|
|
3855
|
-
resolve(resultContext);
|
|
3856
4320
|
}
|
|
3857
|
-
|
|
3858
|
-
}
|
|
3859
|
-
|
|
3860
|
-
`
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
4321
|
+
}
|
|
4322
|
+
},
|
|
4323
|
+
`Delegate flow to service ${serviceName} with address ${url}`
|
|
4324
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal(
|
|
4325
|
+
"meta.socket_client.delegated",
|
|
4326
|
+
"meta.socket_shutdown_requested"
|
|
4327
|
+
);
|
|
4328
|
+
runtimeHandle.transmitTask = CadenzaService.createMetaTask(
|
|
4329
|
+
`Transmit signal to socket server ${url}`,
|
|
4330
|
+
async (signalCtx, emitter) => {
|
|
4331
|
+
if (signalCtx.__signalName === void 0) {
|
|
4332
|
+
return;
|
|
4333
|
+
}
|
|
4334
|
+
delete signalCtx.__broadcast;
|
|
4335
|
+
const response = await runtimeHandle.emitWhenReady?.("signal", signalCtx, 5e3) ?? {
|
|
4336
|
+
errored: true,
|
|
4337
|
+
__error: "Socket signal transmission returned no response"
|
|
4338
|
+
};
|
|
4339
|
+
applySessionOperation("transmit", {});
|
|
4340
|
+
if (signalCtx.__routineExecId) {
|
|
4341
|
+
emitter(`meta.socket_client.transmitted:${signalCtx.__routineExecId}`, {
|
|
4342
|
+
...response
|
|
4343
|
+
});
|
|
4344
|
+
}
|
|
4345
|
+
return response;
|
|
4346
|
+
},
|
|
4347
|
+
`Transmits signal to service ${serviceName} with address ${url}`
|
|
4348
|
+
).doOn(`meta.service_registry.selected_instance_for_socket:${fetchId}`).attachSignal("meta.socket_client.transmitted");
|
|
4349
|
+
CadenzaService.createEphemeralMetaTask(
|
|
4350
|
+
`Shutdown SocketClient ${url}`,
|
|
4351
|
+
(_ctx, emitter) => {
|
|
4352
|
+
runtimeHandle.handshake = false;
|
|
4353
|
+
upsertDiagnostics({
|
|
4354
|
+
connected: false,
|
|
4355
|
+
handshake: false,
|
|
4356
|
+
destroyed: true,
|
|
4357
|
+
pendingDelegations: 0,
|
|
4358
|
+
pendingTimers: 0
|
|
4359
|
+
});
|
|
4360
|
+
applySessionOperation("shutdown", {
|
|
4361
|
+
connected: false,
|
|
4362
|
+
handshake: false,
|
|
4363
|
+
destroyed: true,
|
|
4364
|
+
pendingDelegations: 0,
|
|
4365
|
+
pendingTimers: 0
|
|
4366
|
+
});
|
|
4367
|
+
CadenzaService.log("Shutting down socket client", { url, serviceName });
|
|
4368
|
+
emitter(`meta.fetch.handshake_requested:${fetchId}`, {
|
|
4369
|
+
serviceInstanceId,
|
|
4370
|
+
serviceName,
|
|
4371
|
+
communicationTypes,
|
|
4372
|
+
serviceAddress,
|
|
4373
|
+
servicePort: normalizedPort,
|
|
4374
|
+
protocol,
|
|
4375
|
+
handshakeData: {
|
|
4376
|
+
instanceId: CadenzaService.serviceRegistry.serviceInstanceId,
|
|
4377
|
+
serviceName: CadenzaService.serviceRegistry.serviceName
|
|
3879
4378
|
}
|
|
3880
|
-
resolve(response);
|
|
3881
4379
|
});
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
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
|
|
4380
|
+
for (const id of runtimeHandle.pendingDelegationIds) {
|
|
4381
|
+
emitter(`meta.socket_client.delegated:${id}`, {
|
|
4382
|
+
errored: true,
|
|
4383
|
+
__error: "Shutting down socket client"
|
|
4384
|
+
});
|
|
3914
4385
|
}
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
errored: true,
|
|
3919
|
-
__error: "Shutting down socket client"
|
|
4386
|
+
this.destroySocketClientRuntimeHandle(runtimeHandle);
|
|
4387
|
+
emitter("meta.socket_client.runtime_clear_requested", {
|
|
4388
|
+
fetchId
|
|
3920
4389
|
});
|
|
3921
|
-
}
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
}
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
`meta.fetch.handshake_failed:${fetchId}`,
|
|
3935
|
-
`meta.socket_client.connect_error:${fetchId}`
|
|
3936
|
-
).attachSignal("meta.fetch.handshake_requested").emits("meta.socket_client_shutdown_complete");
|
|
3937
|
-
return true;
|
|
3938
|
-
},
|
|
3939
|
-
"Connects to a specified socket server"
|
|
4390
|
+
},
|
|
4391
|
+
"Shuts down the socket client"
|
|
4392
|
+
).doOn(
|
|
4393
|
+
`meta.socket_shutdown_requested:${fetchId}`,
|
|
4394
|
+
`meta.socket_client.disconnected:${fetchId}`,
|
|
4395
|
+
`meta.fetch.handshake_failed:${fetchId}`,
|
|
4396
|
+
`meta.socket_client.connect_error:${fetchId}`
|
|
4397
|
+
).attachSignal("meta.fetch.handshake_requested").emits("meta.socket_client_shutdown_complete");
|
|
4398
|
+
return true;
|
|
4399
|
+
},
|
|
4400
|
+
{ mode: "write" }
|
|
4401
|
+
),
|
|
4402
|
+
"Connects to a specified socket server and wires runtime tasks."
|
|
3940
4403
|
).doOn("meta.fetch.handshake_complete").emitsOnFail("meta.socket_client.connect_failed");
|
|
3941
4404
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
4405
|
+
createInitialSocketServerSessionState(serverKey) {
|
|
4406
|
+
return {
|
|
4407
|
+
serverKey,
|
|
4408
|
+
useSocket: false,
|
|
4409
|
+
status: "inactive",
|
|
4410
|
+
securityProfile: "medium",
|
|
4411
|
+
networkType: "internal",
|
|
4412
|
+
connectionCount: 0,
|
|
4413
|
+
lastStartedAt: null,
|
|
4414
|
+
lastConnectedAt: null,
|
|
4415
|
+
lastDisconnectedAt: null,
|
|
4416
|
+
lastShutdownAt: null,
|
|
4417
|
+
updatedAt: 0
|
|
4418
|
+
};
|
|
3945
4419
|
}
|
|
3946
|
-
|
|
3947
|
-
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
3948
|
-
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
3949
|
-
const requestedLimit = Number(ctx.errorHistoryLimit);
|
|
3950
|
-
const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
|
|
4420
|
+
createInitialSocketClientSessionState() {
|
|
3951
4421
|
return {
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
4422
|
+
fetchId: "",
|
|
4423
|
+
serviceInstanceId: "",
|
|
4424
|
+
communicationTypes: [],
|
|
4425
|
+
serviceName: "",
|
|
4426
|
+
serviceAddress: "",
|
|
4427
|
+
servicePort: 0,
|
|
4428
|
+
protocol: "http",
|
|
4429
|
+
url: "",
|
|
4430
|
+
socketId: null,
|
|
4431
|
+
connected: false,
|
|
4432
|
+
handshake: false,
|
|
4433
|
+
pendingDelegations: 0,
|
|
4434
|
+
pendingTimers: 0,
|
|
4435
|
+
reconnectAttempts: 0,
|
|
4436
|
+
connectErrors: 0,
|
|
4437
|
+
reconnectErrors: 0,
|
|
4438
|
+
socketErrors: 0,
|
|
4439
|
+
errorCount: 0,
|
|
4440
|
+
destroyed: false,
|
|
4441
|
+
lastHandshakeAt: null,
|
|
4442
|
+
lastHandshakeError: null,
|
|
4443
|
+
lastDisconnectAt: null,
|
|
4444
|
+
updatedAt: 0
|
|
3955
4445
|
};
|
|
3956
4446
|
}
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
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;
|
|
4447
|
+
resolveSocketServerKey(input) {
|
|
4448
|
+
return String(input.serverKey ?? input.__socketServerKey ?? this.socketServerDefaultKey).trim() || this.socketServerDefaultKey;
|
|
4449
|
+
}
|
|
4450
|
+
resolveSocketClientFetchId(input) {
|
|
4451
|
+
const explicitFetchId = String(input.fetchId ?? "").trim();
|
|
4452
|
+
if (explicitFetchId) {
|
|
4453
|
+
return explicitFetchId;
|
|
3986
4454
|
}
|
|
3987
|
-
|
|
4455
|
+
const serviceAddress = String(input.serviceAddress ?? "").trim();
|
|
4456
|
+
const protocol = String(input.protocol ?? "http").trim();
|
|
4457
|
+
const port = this.resolveServicePort(protocol, input.servicePort);
|
|
4458
|
+
if (!serviceAddress || !port) {
|
|
4459
|
+
return void 0;
|
|
4460
|
+
}
|
|
4461
|
+
return `${serviceAddress}_${port}`;
|
|
4462
|
+
}
|
|
4463
|
+
resolveServicePort(protocol, rawPort) {
|
|
4464
|
+
if (protocol === "https") {
|
|
4465
|
+
return 443;
|
|
4466
|
+
}
|
|
4467
|
+
const parsed = Number(rawPort);
|
|
4468
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
4469
|
+
return void 0;
|
|
4470
|
+
}
|
|
4471
|
+
return Math.trunc(parsed);
|
|
4472
|
+
}
|
|
4473
|
+
createSocketServerRuntimeHandleFromContext(context) {
|
|
4474
|
+
const baseServer = context.httpsServer ?? context.httpServer;
|
|
4475
|
+
if (!baseServer) {
|
|
4476
|
+
throw new Error(
|
|
4477
|
+
"Socket server runtime setup requires either httpsServer or httpServer"
|
|
4478
|
+
);
|
|
4479
|
+
}
|
|
4480
|
+
const server = new Server(baseServer, {
|
|
4481
|
+
pingInterval: 3e4,
|
|
4482
|
+
pingTimeout: 2e4,
|
|
4483
|
+
maxHttpBufferSize: 1e7,
|
|
4484
|
+
connectionStateRecovery: {
|
|
4485
|
+
maxDisconnectionDuration: 2 * 60 * 1e3,
|
|
4486
|
+
skipMiddlewares: true
|
|
4487
|
+
}
|
|
4488
|
+
});
|
|
4489
|
+
return {
|
|
4490
|
+
server,
|
|
4491
|
+
initialized: false,
|
|
4492
|
+
connectedSocketIds: /* @__PURE__ */ new Set(),
|
|
4493
|
+
broadcastStatusTask: null,
|
|
4494
|
+
shutdownTask: null
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
destroySocketServerRuntimeHandle(runtimeHandle) {
|
|
4498
|
+
if (!runtimeHandle) {
|
|
4499
|
+
return;
|
|
4500
|
+
}
|
|
4501
|
+
runtimeHandle.broadcastStatusTask?.destroy();
|
|
4502
|
+
runtimeHandle.shutdownTask?.destroy();
|
|
4503
|
+
runtimeHandle.broadcastStatusTask = null;
|
|
4504
|
+
runtimeHandle.shutdownTask = null;
|
|
4505
|
+
runtimeHandle.connectedSocketIds.clear();
|
|
4506
|
+
runtimeHandle.initialized = false;
|
|
4507
|
+
runtimeHandle.server.close();
|
|
4508
|
+
runtimeHandle.server.removeAllListeners();
|
|
4509
|
+
}
|
|
4510
|
+
createSocketClientRuntimeHandle(url) {
|
|
4511
|
+
return {
|
|
4512
|
+
url,
|
|
4513
|
+
socket: io(url, {
|
|
4514
|
+
reconnection: true,
|
|
4515
|
+
reconnectionAttempts: 5,
|
|
4516
|
+
reconnectionDelay: 2e3,
|
|
4517
|
+
reconnectionDelayMax: 1e4,
|
|
4518
|
+
randomizationFactor: 0.5,
|
|
4519
|
+
transports: ["websocket"],
|
|
4520
|
+
autoConnect: false
|
|
4521
|
+
}),
|
|
4522
|
+
initialized: false,
|
|
4523
|
+
handshake: false,
|
|
4524
|
+
errorCount: 0,
|
|
4525
|
+
pendingDelegationIds: /* @__PURE__ */ new Set(),
|
|
4526
|
+
pendingTimers: /* @__PURE__ */ new Set(),
|
|
4527
|
+
emitWhenReady: null,
|
|
4528
|
+
handshakeTask: null,
|
|
4529
|
+
delegateTask: null,
|
|
4530
|
+
transmitTask: null
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
destroySocketClientRuntimeHandle(runtimeHandle) {
|
|
4534
|
+
if (!runtimeHandle) {
|
|
4535
|
+
return;
|
|
4536
|
+
}
|
|
4537
|
+
runtimeHandle.initialized = false;
|
|
4538
|
+
runtimeHandle.handshake = false;
|
|
4539
|
+
runtimeHandle.emitWhenReady = null;
|
|
4540
|
+
runtimeHandle.handshakeTask?.destroy();
|
|
4541
|
+
runtimeHandle.delegateTask?.destroy();
|
|
4542
|
+
runtimeHandle.transmitTask?.destroy();
|
|
4543
|
+
runtimeHandle.handshakeTask = null;
|
|
4544
|
+
runtimeHandle.delegateTask = null;
|
|
4545
|
+
runtimeHandle.transmitTask = null;
|
|
4546
|
+
for (const timer of runtimeHandle.pendingTimers) {
|
|
4547
|
+
clearTimeout(timer);
|
|
4548
|
+
}
|
|
4549
|
+
runtimeHandle.pendingTimers.clear();
|
|
4550
|
+
runtimeHandle.pendingDelegationIds.clear();
|
|
4551
|
+
runtimeHandle.socket.close();
|
|
4552
|
+
runtimeHandle.socket.removeAllListeners();
|
|
4553
|
+
}
|
|
4554
|
+
normalizeCommunicationTypes(value) {
|
|
4555
|
+
if (!Array.isArray(value)) {
|
|
4556
|
+
return [];
|
|
4557
|
+
}
|
|
4558
|
+
return value.map((item) => String(item)).filter((item) => item.trim().length > 0);
|
|
3988
4559
|
}
|
|
3989
4560
|
getErrorMessage(error) {
|
|
3990
4561
|
if (error instanceof Error) {
|
|
@@ -3999,28 +4570,53 @@ var SocketController = class _SocketController {
|
|
|
3999
4570
|
return String(error);
|
|
4000
4571
|
}
|
|
4001
4572
|
}
|
|
4002
|
-
|
|
4003
|
-
const state
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4573
|
+
pruneDiagnosticsEntries(entries, now = Date.now()) {
|
|
4574
|
+
for (const [fetchId, state] of Object.entries(entries)) {
|
|
4575
|
+
if (state.destroyed && now - state.updatedAt > this.destroyedDiagnosticsTtlMs) {
|
|
4576
|
+
delete entries[fetchId];
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
if (Object.keys(entries).length <= this.diagnosticsMaxClientEntries) {
|
|
4580
|
+
return;
|
|
4581
|
+
}
|
|
4582
|
+
const entriesByEvictionPriority = Object.entries(entries).sort((left, right) => {
|
|
4583
|
+
if (left[1].destroyed !== right[1].destroyed) {
|
|
4584
|
+
return left[1].destroyed ? -1 : 1;
|
|
4585
|
+
}
|
|
4586
|
+
return left[1].updatedAt - right[1].updatedAt;
|
|
4012
4587
|
});
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
state.errorHistory.length - this.diagnosticsErrorHistoryLimit
|
|
4017
|
-
);
|
|
4588
|
+
while (Object.keys(entries).length > this.diagnosticsMaxClientEntries && entriesByEvictionPriority.length > 0) {
|
|
4589
|
+
const [fetchId] = entriesByEvictionPriority.shift();
|
|
4590
|
+
delete entries[fetchId];
|
|
4018
4591
|
}
|
|
4019
4592
|
}
|
|
4020
|
-
|
|
4593
|
+
async getSocketClientDiagnosticsEntry(fetchId) {
|
|
4594
|
+
const normalized = String(fetchId ?? "").trim();
|
|
4595
|
+
if (!normalized) {
|
|
4596
|
+
return void 0;
|
|
4597
|
+
}
|
|
4598
|
+
const snapshot = this.socketClientDiagnosticsActor.getState();
|
|
4599
|
+
const entries = { ...snapshot.entries };
|
|
4600
|
+
this.pruneDiagnosticsEntries(entries);
|
|
4601
|
+
return entries[normalized];
|
|
4602
|
+
}
|
|
4603
|
+
resolveTransportDiagnosticsOptions(ctx) {
|
|
4604
|
+
const detailLevel = ctx.detailLevel === "full" ? "full" : "summary";
|
|
4605
|
+
const includeErrorHistory = Boolean(ctx.includeErrorHistory);
|
|
4606
|
+
const requestedLimit = Number(ctx.errorHistoryLimit);
|
|
4607
|
+
const errorHistoryLimit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(200, Math.trunc(requestedLimit))) : 10;
|
|
4608
|
+
return {
|
|
4609
|
+
detailLevel,
|
|
4610
|
+
includeErrorHistory,
|
|
4611
|
+
errorHistoryLimit
|
|
4612
|
+
};
|
|
4613
|
+
}
|
|
4614
|
+
collectSocketTransportDiagnostics(ctx, diagnosticsEntries) {
|
|
4021
4615
|
const { detailLevel, includeErrorHistory, errorHistoryLimit } = this.resolveTransportDiagnosticsOptions(ctx);
|
|
4022
4616
|
const serviceName = CadenzaService.serviceRegistry.serviceName ?? "UnknownService";
|
|
4023
|
-
const
|
|
4617
|
+
const entries = { ...diagnosticsEntries };
|
|
4618
|
+
this.pruneDiagnosticsEntries(entries);
|
|
4619
|
+
const states = Object.values(entries).sort(
|
|
4024
4620
|
(a, b) => a.fetchId.localeCompare(b.fetchId)
|
|
4025
4621
|
);
|
|
4026
4622
|
const summary = {
|
|
@@ -4038,10 +4634,7 @@ var SocketController = class _SocketController {
|
|
|
4038
4634
|
0
|
|
4039
4635
|
),
|
|
4040
4636
|
connectErrors: states.reduce((acc, state) => acc + state.connectErrors, 0),
|
|
4041
|
-
reconnectErrors: states.reduce(
|
|
4042
|
-
(acc, state) => acc + state.reconnectErrors,
|
|
4043
|
-
0
|
|
4044
|
-
),
|
|
4637
|
+
reconnectErrors: states.reduce((acc, state) => acc + state.reconnectErrors, 0),
|
|
4045
4638
|
socketErrors: states.reduce((acc, state) => acc + state.socketErrors, 0),
|
|
4046
4639
|
latestError: states.slice().sort((a, b) => b.lastErrorAt - a.lastErrorAt).find((state) => state.lastError)?.lastError ?? null
|
|
4047
4640
|
};
|
|
@@ -7528,6 +8121,14 @@ var CadenzaService = class {
|
|
|
7528
8121
|
options.isMeta = true;
|
|
7529
8122
|
this.createDatabaseService(name, schema, description, options);
|
|
7530
8123
|
}
|
|
8124
|
+
static createActor(spec, options = {}) {
|
|
8125
|
+
this.bootstrap();
|
|
8126
|
+
return new Actor(spec, options);
|
|
8127
|
+
}
|
|
8128
|
+
static createActorFromDefinition(definition, options = {}) {
|
|
8129
|
+
this.bootstrap();
|
|
8130
|
+
return Cadenza.createActorFromDefinition(definition, options);
|
|
8131
|
+
}
|
|
7531
8132
|
/**
|
|
7532
8133
|
* Creates and registers a new task with the provided name, function, and optional details.
|
|
7533
8134
|
*
|
|
@@ -7942,6 +8543,7 @@ CadenzaService.warnedInvalidMetaIntentResponderKeys = /* @__PURE__ */ new Set();
|
|
|
7942
8543
|
|
|
7943
8544
|
// src/index.ts
|
|
7944
8545
|
import {
|
|
8546
|
+
Actor as Actor2,
|
|
7945
8547
|
DebounceTask as DebounceTask2,
|
|
7946
8548
|
EphemeralTask as EphemeralTask2,
|
|
7947
8549
|
GraphRoutine as GraphRoutine2,
|
|
@@ -7949,6 +8551,7 @@ import {
|
|
|
7949
8551
|
} from "@cadenza.io/core";
|
|
7950
8552
|
var index_default = CadenzaService;
|
|
7951
8553
|
export {
|
|
8554
|
+
Actor2 as Actor,
|
|
7952
8555
|
DatabaseTask,
|
|
7953
8556
|
DebounceTask2 as DebounceTask,
|
|
7954
8557
|
DeputyTask,
|