@powerhousedao/reactor 6.1.0-dev.0 → 6.1.0-dev.10
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/build-worker-executor-DDVXB921.js +83 -0
- package/dist/build-worker-executor-DDVXB921.js.map +1 -0
- package/dist/document-indexer-B2iLRB0o.js +917 -0
- package/dist/document-indexer-B2iLRB0o.js.map +1 -0
- package/dist/drive-container-types-BNpMlgT_.js +2964 -0
- package/dist/drive-container-types-BNpMlgT_.js.map +1 -0
- package/dist/entry.d.ts +1 -0
- package/dist/entry.js +313 -0
- package/dist/entry.js.map +1 -0
- package/dist/error-info-Cpu4OY3o.js +62 -0
- package/dist/error-info-Cpu4OY3o.js.map +1 -0
- package/dist/errors-D3S6Eysd.js +56 -0
- package/dist/errors-D3S6Eysd.js.map +1 -0
- package/dist/forwarding-logger-BBkMSxuJ.js +85 -0
- package/dist/forwarding-logger-BBkMSxuJ.js.map +1 -0
- package/dist/index.d.ts +991 -75
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +900 -3889
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.d.ts +1 -0
- package/dist/projection-entry.js +406 -0
- package/dist/projection-entry.js.map +1 -0
- package/dist/projection-shard-manager-_c7orNo5.js +313 -0
- package/dist/projection-shard-manager-_c7orNo5.js.map +1 -0
- package/dist/projection-worker-wI4PwcV2.js +13 -0
- package/dist/projection-worker-wI4PwcV2.js.map +1 -0
- package/dist/transport-ByGviWdZ.js +33 -0
- package/dist/transport-ByGviWdZ.js.map +1 -0
- package/dist/transport-CuogVKN_.js +23 -0
- package/dist/transport-CuogVKN_.js.map +1 -0
- package/dist/types-CxSpmNGK.js +32 -0
- package/dist/types-CxSpmNGK.js.map +1 -0
- package/dist/worker-SUoDhurA.js +22 -0
- package/dist/worker-SUoDhurA.js.map +1 -0
- package/dist/worker-handle-B1w03nRA.js +383 -0
- package/dist/worker-handle-B1w03nRA.js.map +1 -0
- package/package.json +6 -4
package/dist/entry.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/entry.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { o as instrumentPgPool } from "./drive-container-types-BNpMlgT_.js";
|
|
2
|
+
import { n as errorToInfo, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
|
|
3
|
+
import { n as defaultLoadFactory, t as buildWorkerExecutor } from "./build-worker-executor-DDVXB921.js";
|
|
4
|
+
import { ConsoleLogger } from "document-model";
|
|
5
|
+
import { isMainThread, parentPort } from "node:worker_threads";
|
|
6
|
+
//#region src/executor/worker/run-worker.ts
|
|
7
|
+
const POOL_SAMPLE_INTERVAL_MS = 1e3;
|
|
8
|
+
async function defaultCreateDatabase(config, workerId) {
|
|
9
|
+
const { Kysely, PostgresDialect } = await import("kysely");
|
|
10
|
+
const Pool = (await import("pg")).default.Pool;
|
|
11
|
+
const pool = new Pool({
|
|
12
|
+
host: config.host,
|
|
13
|
+
port: config.port,
|
|
14
|
+
database: config.database,
|
|
15
|
+
user: config.user,
|
|
16
|
+
password: config.password,
|
|
17
|
+
ssl: config.ssl ? { rejectUnauthorized: false } : void 0,
|
|
18
|
+
application_name: config.applicationName ?? workerId,
|
|
19
|
+
max: config.poolSize,
|
|
20
|
+
connectionTimeoutMillis: config.connectionTimeoutMillis,
|
|
21
|
+
idleTimeoutMillis: config.idleTimeoutMillis
|
|
22
|
+
});
|
|
23
|
+
const poolInstrumentation = instrumentPgPool(pool, workerId);
|
|
24
|
+
const kysely = new Kysely({ dialect: new PostgresDialect({ pool }) });
|
|
25
|
+
return {
|
|
26
|
+
kysely,
|
|
27
|
+
poolInstrumentation,
|
|
28
|
+
async shutdown() {
|
|
29
|
+
try {
|
|
30
|
+
await kysely.destroy();
|
|
31
|
+
} catch {}
|
|
32
|
+
try {
|
|
33
|
+
await pool.end();
|
|
34
|
+
} catch {}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Drives the worker's message loop. Owns lifecycle of the database handle
|
|
40
|
+
* and executor stack. The default factories build a real Postgres pool and
|
|
41
|
+
* use dynamic `import()` for model/verifier specs; tests inject overrides
|
|
42
|
+
* for an in-process PGlite path.
|
|
43
|
+
*/
|
|
44
|
+
function runWorker(parentPort, overrides = {}) {
|
|
45
|
+
let workerId = "";
|
|
46
|
+
let initCompleted = false;
|
|
47
|
+
let initConfig = null;
|
|
48
|
+
let executorStack = null;
|
|
49
|
+
let database = null;
|
|
50
|
+
let poolSampleTimer = null;
|
|
51
|
+
let pendingPoolSamples = [];
|
|
52
|
+
let detachPoolListener = null;
|
|
53
|
+
const activeLoadFactory = overrides.loadFactory ?? defaultLoadFactory;
|
|
54
|
+
function post(msg) {
|
|
55
|
+
parentPort.postMessage(msg);
|
|
56
|
+
}
|
|
57
|
+
const logger = createForwardingLogger(post);
|
|
58
|
+
function startPoolReporter(instrumentation) {
|
|
59
|
+
detachPoolListener = instrumentation.onAcquire((durationMs) => {
|
|
60
|
+
pendingPoolSamples.push(durationMs);
|
|
61
|
+
});
|
|
62
|
+
poolSampleTimer = setInterval(() => {
|
|
63
|
+
if (pendingPoolSamples.length === 0) return;
|
|
64
|
+
const durations = pendingPoolSamples;
|
|
65
|
+
pendingPoolSamples = [];
|
|
66
|
+
const stats = instrumentation.getStats();
|
|
67
|
+
post({
|
|
68
|
+
type: "pool-acquire-samples",
|
|
69
|
+
workerId,
|
|
70
|
+
poolName: instrumentation.name,
|
|
71
|
+
timestamp: Date.now(),
|
|
72
|
+
durations,
|
|
73
|
+
size: stats.size,
|
|
74
|
+
idle: stats.idle,
|
|
75
|
+
waiting: stats.waiting
|
|
76
|
+
});
|
|
77
|
+
}, POOL_SAMPLE_INTERVAL_MS);
|
|
78
|
+
poolSampleTimer.unref();
|
|
79
|
+
}
|
|
80
|
+
function stopPoolReporter() {
|
|
81
|
+
if (detachPoolListener) {
|
|
82
|
+
detachPoolListener();
|
|
83
|
+
detachPoolListener = null;
|
|
84
|
+
}
|
|
85
|
+
if (poolSampleTimer) {
|
|
86
|
+
clearInterval(poolSampleTimer);
|
|
87
|
+
poolSampleTimer = null;
|
|
88
|
+
}
|
|
89
|
+
pendingPoolSamples = [];
|
|
90
|
+
}
|
|
91
|
+
process.on("uncaughtException", (err) => {
|
|
92
|
+
try {
|
|
93
|
+
post({
|
|
94
|
+
type: "log",
|
|
95
|
+
level: "error",
|
|
96
|
+
message: "worker uncaughtException",
|
|
97
|
+
args: [errorToInfo(err)],
|
|
98
|
+
timestamp: Date.now()
|
|
99
|
+
});
|
|
100
|
+
} catch {}
|
|
101
|
+
throw err;
|
|
102
|
+
});
|
|
103
|
+
process.on("unhandledRejection", (reason) => {
|
|
104
|
+
try {
|
|
105
|
+
post({
|
|
106
|
+
type: "log",
|
|
107
|
+
level: "error",
|
|
108
|
+
message: "worker unhandledRejection",
|
|
109
|
+
args: [errorToInfo(reason)],
|
|
110
|
+
timestamp: Date.now()
|
|
111
|
+
});
|
|
112
|
+
} catch {}
|
|
113
|
+
});
|
|
114
|
+
async function handleInit(msg) {
|
|
115
|
+
workerId = msg.workerId;
|
|
116
|
+
initConfig = msg;
|
|
117
|
+
database = await (overrides.createDatabase ?? defaultCreateDatabase)(msg.db, msg.workerId);
|
|
118
|
+
if (overrides.beforeBuildExecutor) await overrides.beforeBuildExecutor(database.kysely);
|
|
119
|
+
executorStack = await buildWorkerExecutor({
|
|
120
|
+
init: msg,
|
|
121
|
+
database: database.kysely,
|
|
122
|
+
logger: new ConsoleLogger([`reactor-worker:${msg.workerId}`]),
|
|
123
|
+
loadFactory: activeLoadFactory
|
|
124
|
+
});
|
|
125
|
+
if (database.poolInstrumentation) startPoolReporter(database.poolInstrumentation);
|
|
126
|
+
initCompleted = true;
|
|
127
|
+
logger.info("worker initialized: @workerId", msg.workerId);
|
|
128
|
+
post({
|
|
129
|
+
type: "ready",
|
|
130
|
+
correlationId: msg.correlationId,
|
|
131
|
+
workerId: msg.workerId
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async function handleExecute(correlationId, job) {
|
|
135
|
+
if (!executorStack) {
|
|
136
|
+
post({
|
|
137
|
+
type: "result",
|
|
138
|
+
correlationId,
|
|
139
|
+
result: {
|
|
140
|
+
job,
|
|
141
|
+
success: false
|
|
142
|
+
},
|
|
143
|
+
error: errorToInfo(/* @__PURE__ */ new Error("execute received before init"))
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
post({
|
|
149
|
+
type: "result",
|
|
150
|
+
correlationId,
|
|
151
|
+
result: await executorStack.executor.executeJob(job),
|
|
152
|
+
writeReady: executorStack.takeLastWriteReady() ?? void 0
|
|
153
|
+
});
|
|
154
|
+
} catch (error) {
|
|
155
|
+
post({
|
|
156
|
+
type: "result",
|
|
157
|
+
correlationId,
|
|
158
|
+
result: {
|
|
159
|
+
job,
|
|
160
|
+
success: false
|
|
161
|
+
},
|
|
162
|
+
error: errorToInfo(error)
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function handleLoadModel(msg) {
|
|
167
|
+
const stack = executorStack;
|
|
168
|
+
if (!stack) {
|
|
169
|
+
post({
|
|
170
|
+
type: "model-load-failed",
|
|
171
|
+
correlationId: msg.correlationId,
|
|
172
|
+
documentType: msg.model.documentType,
|
|
173
|
+
version: msg.model.version,
|
|
174
|
+
error: errorToInfo(/* @__PURE__ */ new Error("load-model received before init"))
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
let module;
|
|
179
|
+
try {
|
|
180
|
+
module = await activeLoadFactory(msg.model.spec);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
post({
|
|
183
|
+
type: "model-load-failed",
|
|
184
|
+
correlationId: msg.correlationId,
|
|
185
|
+
documentType: msg.model.documentType,
|
|
186
|
+
version: msg.model.version,
|
|
187
|
+
error: errorToInfo(error)
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const [result] = stack.registry.registerModules(module);
|
|
192
|
+
if (result.status === "error") {
|
|
193
|
+
post({
|
|
194
|
+
type: "model-load-failed",
|
|
195
|
+
correlationId: msg.correlationId,
|
|
196
|
+
documentType: msg.model.documentType,
|
|
197
|
+
version: msg.model.version,
|
|
198
|
+
error: errorToInfo(result.error)
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
post({
|
|
203
|
+
type: "model-loaded",
|
|
204
|
+
correlationId: msg.correlationId,
|
|
205
|
+
documentType: msg.model.documentType,
|
|
206
|
+
version: msg.model.version
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async function shutdownDatabase() {
|
|
210
|
+
stopPoolReporter();
|
|
211
|
+
if (database) await database.shutdown();
|
|
212
|
+
}
|
|
213
|
+
function handleParentMessage(msg) {
|
|
214
|
+
switch (msg.type) {
|
|
215
|
+
case "init":
|
|
216
|
+
handleInit(msg).catch((err) => {
|
|
217
|
+
post({
|
|
218
|
+
type: "log",
|
|
219
|
+
level: "error",
|
|
220
|
+
message: "worker init failed",
|
|
221
|
+
args: [errorToInfo(err)],
|
|
222
|
+
timestamp: Date.now()
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
break;
|
|
226
|
+
case "execute":
|
|
227
|
+
if (!initCompleted) {
|
|
228
|
+
logger.warn("received execute before init");
|
|
229
|
+
post({
|
|
230
|
+
type: "result",
|
|
231
|
+
correlationId: msg.correlationId,
|
|
232
|
+
result: {
|
|
233
|
+
job: msg.job,
|
|
234
|
+
success: false
|
|
235
|
+
},
|
|
236
|
+
error: errorToInfo(/* @__PURE__ */ new Error("execute received before init"))
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
handleExecute(msg.correlationId, msg.job).catch((err) => {
|
|
241
|
+
post({
|
|
242
|
+
type: "result",
|
|
243
|
+
correlationId: msg.correlationId,
|
|
244
|
+
result: {
|
|
245
|
+
job: msg.job,
|
|
246
|
+
success: false
|
|
247
|
+
},
|
|
248
|
+
error: errorToInfo(err)
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
break;
|
|
252
|
+
case "shutdown":
|
|
253
|
+
logger.info("worker shutting down: @workerId", workerId);
|
|
254
|
+
shutdownDatabase().finally(() => {
|
|
255
|
+
post({
|
|
256
|
+
type: "log",
|
|
257
|
+
level: "info",
|
|
258
|
+
message: "worker shutdown",
|
|
259
|
+
args: [],
|
|
260
|
+
timestamp: Date.now()
|
|
261
|
+
});
|
|
262
|
+
process.exit(0);
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
case "abort":
|
|
266
|
+
logger.warn("abort received (no-op stub): @correlationId", msg.correlationId);
|
|
267
|
+
break;
|
|
268
|
+
case "load-model":
|
|
269
|
+
handleLoadModel(msg).catch((err) => {
|
|
270
|
+
post({
|
|
271
|
+
type: "model-load-failed",
|
|
272
|
+
correlationId: msg.correlationId,
|
|
273
|
+
documentType: msg.model.documentType,
|
|
274
|
+
version: msg.model.version,
|
|
275
|
+
error: errorToInfo(err)
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
break;
|
|
279
|
+
default: {
|
|
280
|
+
const raw = msg;
|
|
281
|
+
if (raw["type"] === "__test_throw") {
|
|
282
|
+
const rawReason = raw["reason"];
|
|
283
|
+
const reason = typeof rawReason === "string" ? rawReason : "synthetic uncaughtException";
|
|
284
|
+
setTimeout(() => {
|
|
285
|
+
throw new Error(reason);
|
|
286
|
+
}, 0);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
parentPort.on("message", handleParentMessage);
|
|
294
|
+
parentPort.__reactorWorkerHarness = {
|
|
295
|
+
handleParentMessage,
|
|
296
|
+
get initCompleted() {
|
|
297
|
+
return initCompleted;
|
|
298
|
+
},
|
|
299
|
+
get initConfig() {
|
|
300
|
+
return initConfig;
|
|
301
|
+
},
|
|
302
|
+
get workerId() {
|
|
303
|
+
return workerId;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/executor/worker/entry.ts
|
|
309
|
+
if (isMainThread || parentPort === null) throw new Error("entry.ts must be run as a worker thread");
|
|
310
|
+
runWorker(parentPort);
|
|
311
|
+
//#endregion
|
|
312
|
+
|
|
313
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","names":[],"sources":["../src/executor/worker/run-worker.ts","../src/executor/worker/entry.ts"],"sourcesContent":["import type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { MessagePort } from \"node:worker_threads\";\nimport type { Database } from \"../../core/types.js\";\nimport type { Job } from \"../../queue/types.js\";\nimport {\n instrumentPgPool,\n type PoolInstrumentation,\n} from \"../../storage/pool-instrumentation.js\";\nimport {\n buildWorkerExecutor,\n defaultLoadFactory,\n type BuildWorkerExecutorOptions,\n type WorkerExecutorStack,\n} from \"./build-worker-executor.js\";\nimport { createForwardingLogger } from \"./forwarding-logger.js\";\nimport type {\n DbConfig,\n FactorySpec,\n InitMessage,\n LoadModelMessage,\n ParentMessage,\n WorkerMessage,\n} from \"./protocol.js\";\nimport { errorToInfo } from \"./sanitize.js\";\n\nconst POOL_SAMPLE_INTERVAL_MS = 1_000;\n\n/**\n * Closeable handle around the parent's Postgres pool the worker owns.\n * Decoupled so tests can substitute a PGlite-backed Kysely.\n *\n * When the worker owns a real pg.Pool, the handle exposes a\n * {@link PoolInstrumentation} so the run-loop can subscribe to acquire-wait\n * samples and forward them to the host. Tests that swap in PGlite leave\n * this undefined; pool metrics simply do not emit in that path.\n */\nexport type WorkerDatabaseHandle = {\n kysely: Kysely<Database>;\n poolInstrumentation?: PoolInstrumentation;\n shutdown(): Promise<void>;\n};\n\nexport type RunWorkerOverrides = {\n /**\n * Builds the worker's Kysely instance from the init's DbConfig. Defaults\n * to a real Postgres pool. Tests typically swap this for PGlite.\n */\n createDatabase?: (\n config: DbConfig,\n workerId: string,\n ) => Promise<WorkerDatabaseHandle>;\n /**\n * Overrides the dynamic-import factory loader used for the signature\n * verifier and document model manifest. Tests usually inject this so\n * they don't need real packages.\n */\n loadFactory?: BuildWorkerExecutorOptions[\"loadFactory\"];\n /**\n * Called after the database is created but before the executor stack\n * is built. Lets tests run reactor migrations against PGlite, since\n * the production parent runs them once against shared Postgres.\n */\n beforeBuildExecutor?: (db: Kysely<Database>) => Promise<void>;\n};\n\nasync function defaultCreateDatabase(\n config: DbConfig,\n workerId: string,\n): Promise<WorkerDatabaseHandle> {\n const { Kysely, PostgresDialect } = await import(\"kysely\");\n const pgModule = await import(\"pg\");\n const Pool = pgModule.default.Pool;\n const pool = new Pool({\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n ssl: config.ssl ? { rejectUnauthorized: false } : undefined,\n application_name: config.applicationName ?? workerId,\n max: config.poolSize,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n idleTimeoutMillis: config.idleTimeoutMillis,\n });\n const poolInstrumentation = instrumentPgPool(pool, workerId);\n const kysely = new Kysely<Database>({\n dialect: new PostgresDialect({ pool }),\n });\n return {\n kysely,\n poolInstrumentation,\n async shutdown(): Promise<void> {\n try {\n await kysely.destroy();\n } catch {\n // best-effort\n }\n try {\n await pool.end();\n } catch {\n // best-effort\n }\n },\n };\n}\n\n/**\n * Drives the worker's message loop. Owns lifecycle of the database handle\n * and executor stack. The default factories build a real Postgres pool and\n * use dynamic `import()` for model/verifier specs; tests inject overrides\n * for an in-process PGlite path.\n */\nexport function runWorker(\n parentPort: MessagePort,\n overrides: RunWorkerOverrides = {},\n): void {\n let workerId = \"\";\n let initCompleted = false;\n let initConfig: InitMessage | null = null;\n let executorStack: WorkerExecutorStack | null = null;\n let database: WorkerDatabaseHandle | null = null;\n let poolSampleTimer: NodeJS.Timeout | null = null;\n let pendingPoolSamples: number[] = [];\n let detachPoolListener: (() => void) | null = null;\n const activeLoadFactory: NonNullable<\n BuildWorkerExecutorOptions[\"loadFactory\"]\n > = overrides.loadFactory ?? defaultLoadFactory;\n\n function post(msg: WorkerMessage): void {\n parentPort.postMessage(msg);\n }\n\n const logger = createForwardingLogger(post);\n\n function startPoolReporter(instrumentation: PoolInstrumentation): void {\n detachPoolListener = instrumentation.onAcquire((durationMs) => {\n pendingPoolSamples.push(durationMs);\n });\n poolSampleTimer = setInterval(() => {\n if (pendingPoolSamples.length === 0) {\n return;\n }\n const durations = pendingPoolSamples;\n pendingPoolSamples = [];\n const stats = instrumentation.getStats();\n post({\n type: \"pool-acquire-samples\",\n workerId,\n poolName: instrumentation.name,\n timestamp: Date.now(),\n durations,\n size: stats.size,\n idle: stats.idle,\n waiting: stats.waiting,\n });\n }, POOL_SAMPLE_INTERVAL_MS);\n poolSampleTimer.unref();\n }\n\n function stopPoolReporter(): void {\n if (detachPoolListener) {\n detachPoolListener();\n detachPoolListener = null;\n }\n if (poolSampleTimer) {\n clearInterval(poolSampleTimer);\n poolSampleTimer = null;\n }\n pendingPoolSamples = [];\n }\n\n process.on(\"uncaughtException\", (err: unknown) => {\n try {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker uncaughtException\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n throw err;\n });\n\n process.on(\"unhandledRejection\", (reason: unknown) => {\n try {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker unhandledRejection\",\n args: [errorToInfo(reason)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n });\n\n async function handleInit(msg: InitMessage): Promise<void> {\n workerId = msg.workerId;\n initConfig = msg;\n const createDb = overrides.createDatabase ?? defaultCreateDatabase;\n database = await createDb(msg.db, msg.workerId);\n if (overrides.beforeBuildExecutor) {\n await overrides.beforeBuildExecutor(database.kysely);\n }\n executorStack = await buildWorkerExecutor({\n init: msg,\n database: database.kysely,\n logger: new ConsoleLogger([`reactor-worker:${msg.workerId}`]),\n loadFactory: activeLoadFactory,\n });\n if (database.poolInstrumentation) {\n startPoolReporter(database.poolInstrumentation);\n }\n initCompleted = true;\n logger.info(\"worker initialized: @workerId\", msg.workerId);\n post({\n type: \"ready\",\n correlationId: msg.correlationId,\n workerId: msg.workerId,\n });\n }\n\n async function handleExecute(correlationId: string, job: Job): Promise<void> {\n if (!executorStack) {\n post({\n type: \"result\",\n correlationId,\n result: { job, success: false },\n error: errorToInfo(new Error(\"execute received before init\")),\n });\n return;\n }\n try {\n const result = await executorStack.executor.executeJob(job);\n const writeReady = executorStack.takeLastWriteReady();\n post({\n type: \"result\",\n correlationId,\n result,\n writeReady: writeReady ?? undefined,\n });\n } catch (error) {\n post({\n type: \"result\",\n correlationId,\n result: { job, success: false },\n error: errorToInfo(error),\n });\n }\n }\n\n async function handleLoadModel(msg: LoadModelMessage): Promise<void> {\n const stack = executorStack;\n if (!stack) {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(new Error(\"load-model received before init\")),\n });\n return;\n }\n let module: DocumentModelModule;\n try {\n module = (await activeLoadFactory(msg.model.spec)) as DocumentModelModule;\n } catch (error) {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(error),\n });\n return;\n }\n const [result] = stack.registry.registerModules(module);\n if (result.status === \"error\") {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(result.error),\n });\n return;\n }\n post({\n type: \"model-loaded\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n });\n }\n\n async function shutdownDatabase(): Promise<void> {\n stopPoolReporter();\n if (database) {\n await database.shutdown();\n }\n }\n\n function handleParentMessage(msg: ParentMessage): void {\n switch (msg.type) {\n case \"init\": {\n handleInit(msg).catch((err: unknown) => {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker init failed\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n });\n break;\n }\n\n case \"execute\": {\n if (!initCompleted) {\n logger.warn(\"received execute before init\");\n post({\n type: \"result\",\n correlationId: msg.correlationId,\n result: { job: msg.job, success: false },\n error: errorToInfo(new Error(\"execute received before init\")),\n });\n break;\n }\n handleExecute(msg.correlationId, msg.job).catch((err: unknown) => {\n post({\n type: \"result\",\n correlationId: msg.correlationId,\n result: { job: msg.job, success: false },\n error: errorToInfo(err),\n });\n });\n break;\n }\n\n case \"shutdown\": {\n logger.info(\"worker shutting down: @workerId\", workerId);\n void shutdownDatabase().finally(() => {\n post({\n type: \"log\",\n level: \"info\",\n message: \"worker shutdown\",\n args: [],\n timestamp: Date.now(),\n });\n process.exit(0);\n });\n break;\n }\n\n case \"abort\": {\n logger.warn(\n \"abort received (no-op stub): @correlationId\",\n msg.correlationId,\n );\n break;\n }\n\n case \"load-model\": {\n handleLoadModel(msg).catch((err: unknown) => {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(err),\n });\n });\n break;\n }\n\n default: {\n const raw = msg as Record<string, unknown>;\n if (raw[\"type\"] === \"__test_throw\") {\n const rawReason = raw[\"reason\"];\n const reason =\n typeof rawReason === \"string\"\n ? rawReason\n : \"synthetic uncaughtException\";\n setTimeout(() => {\n throw new Error(reason);\n }, 0);\n return;\n }\n const _exhaustive: never = msg;\n void _exhaustive;\n break;\n }\n }\n }\n\n parentPort.on(\"message\", handleParentMessage);\n\n // Test-only exports surfaced on a side-channel for unit tests that drive\n // the message loop directly without a real worker_threads parent.\n const harness = {\n handleParentMessage,\n get initCompleted(): boolean {\n return initCompleted;\n },\n get initConfig(): InitMessage | null {\n return initConfig;\n },\n get workerId(): string {\n return workerId;\n },\n };\n (\n parentPort as unknown as { __reactorWorkerHarness?: unknown }\n ).__reactorWorkerHarness = harness;\n}\n\nexport type FactorySpecForTesting = FactorySpec;\n","import { isMainThread, parentPort } from \"node:worker_threads\";\nimport { runWorker } from \"./run-worker.js\";\n\nif (isMainThread || parentPort === null) {\n throw new Error(\"entry.ts must be run as a worker thread\");\n}\n\nrunWorker(parentPort);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/executor/worker/error-info.ts
|
|
2
|
+
const MAX_STACK_LENGTH = 8 * 1024;
|
|
3
|
+
const MAX_CAUSE_DEPTH = 8;
|
|
4
|
+
function toErrorInfo(err, depth = 0) {
|
|
5
|
+
if (depth > MAX_CAUSE_DEPTH) return {
|
|
6
|
+
name: "Error",
|
|
7
|
+
message: "cause chain truncated"
|
|
8
|
+
};
|
|
9
|
+
if (err instanceof Error) {
|
|
10
|
+
const info = {
|
|
11
|
+
name: err.name || "Error",
|
|
12
|
+
message: err.message
|
|
13
|
+
};
|
|
14
|
+
if (typeof err.stack === "string") info.stack = err.stack.length > MAX_STACK_LENGTH ? err.stack.slice(0, MAX_STACK_LENGTH) : err.stack;
|
|
15
|
+
if (err.cause !== void 0) info.cause = toErrorInfo(err.cause, depth + 1);
|
|
16
|
+
return info;
|
|
17
|
+
}
|
|
18
|
+
if (typeof err === "string") return {
|
|
19
|
+
name: "Error",
|
|
20
|
+
message: err
|
|
21
|
+
};
|
|
22
|
+
if (err && typeof err === "object") {
|
|
23
|
+
const record = err;
|
|
24
|
+
return {
|
|
25
|
+
name: typeof record.name === "string" ? record.name : "Error",
|
|
26
|
+
message: typeof record.message === "string" ? record.message : safeStringify(err)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
name: "Error",
|
|
31
|
+
message: safeStringify(err)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function safeStringify(value) {
|
|
35
|
+
if (value === null || value === void 0) return String(value);
|
|
36
|
+
if (typeof value === "string") return value;
|
|
37
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
38
|
+
try {
|
|
39
|
+
return JSON.stringify(value);
|
|
40
|
+
} catch {
|
|
41
|
+
return "[unserializable]";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function fromErrorInfo(info) {
|
|
45
|
+
const err = new Error(info.message);
|
|
46
|
+
Object.defineProperty(err, "name", {
|
|
47
|
+
value: info.name,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true
|
|
50
|
+
});
|
|
51
|
+
if (info.stack !== void 0) err.stack = info.stack;
|
|
52
|
+
if (info.cause !== void 0) Object.defineProperty(err, "cause", {
|
|
53
|
+
value: fromErrorInfo(info.cause),
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true
|
|
56
|
+
});
|
|
57
|
+
return err;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { toErrorInfo as n, fromErrorInfo as t };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=error-info-Cpu4OY3o.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-info-Cpu4OY3o.js","names":[],"sources":["../src/executor/worker/error-info.ts"],"sourcesContent":["/**\n * Helpers for marshalling `Error` instances across the worker IPC boundary.\n *\n * Class instances cannot be structured-cloned, so {@link ErrorInfo} is the\n * canonical wire shape (see `protocol.ts`). These helpers walk `cause`\n * chains, truncate stacks, and drop non-cloneable fields.\n */\n\nimport type { ErrorInfo } from \"./protocol.js\";\n\nconst MAX_STACK_LENGTH = 8 * 1024;\nconst MAX_CAUSE_DEPTH = 8;\n\nexport function toErrorInfo(err: unknown, depth = 0): ErrorInfo {\n if (depth > MAX_CAUSE_DEPTH) {\n return { name: \"Error\", message: \"cause chain truncated\" };\n }\n if (err instanceof Error) {\n const info: ErrorInfo = {\n name: err.name || \"Error\",\n message: err.message,\n };\n if (typeof err.stack === \"string\") {\n info.stack =\n err.stack.length > MAX_STACK_LENGTH\n ? err.stack.slice(0, MAX_STACK_LENGTH)\n : err.stack;\n }\n if (err.cause !== undefined) {\n info.cause = toErrorInfo(err.cause, depth + 1);\n }\n return info;\n }\n if (typeof err === \"string\") {\n return { name: \"Error\", message: err };\n }\n if (err && typeof err === \"object\") {\n const record = err as { name?: unknown; message?: unknown };\n return {\n name: typeof record.name === \"string\" ? record.name : \"Error\",\n message:\n typeof record.message === \"string\"\n ? record.message\n : safeStringify(err),\n };\n }\n return { name: \"Error\", message: safeStringify(err) };\n}\n\nfunction safeStringify(value: unknown): string {\n if (value === null || value === undefined) {\n return String(value);\n }\n if (typeof value === \"string\") {\n return value;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function fromErrorInfo(info: ErrorInfo): Error {\n const err = new Error(info.message);\n Object.defineProperty(err, \"name\", {\n value: info.name,\n configurable: true,\n writable: true,\n });\n if (info.stack !== undefined) {\n err.stack = info.stack;\n }\n if (info.cause !== undefined) {\n Object.defineProperty(err, \"cause\", {\n value: fromErrorInfo(info.cause),\n configurable: true,\n writable: true,\n });\n }\n return err;\n}\n"],"mappings":";AAUA,MAAM,mBAAmB,IAAI;AAC7B,MAAM,kBAAkB;AAExB,SAAgB,YAAY,KAAc,QAAQ,GAAc;AAC9D,KAAI,QAAQ,gBACV,QAAO;EAAE,MAAM;EAAS,SAAS;EAAyB;AAE5D,KAAI,eAAe,OAAO;EACxB,MAAM,OAAkB;GACtB,MAAM,IAAI,QAAQ;GAClB,SAAS,IAAI;GACd;AACD,MAAI,OAAO,IAAI,UAAU,SACvB,MAAK,QACH,IAAI,MAAM,SAAS,mBACf,IAAI,MAAM,MAAM,GAAG,iBAAiB,GACpC,IAAI;AAEZ,MAAI,IAAI,UAAU,KAAA,EAChB,MAAK,QAAQ,YAAY,IAAI,OAAO,QAAQ,EAAE;AAEhD,SAAO;;AAET,KAAI,OAAO,QAAQ,SACjB,QAAO;EAAE,MAAM;EAAS,SAAS;EAAK;AAExC,KAAI,OAAO,OAAO,QAAQ,UAAU;EAClC,MAAM,SAAS;AACf,SAAO;GACL,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;GACtD,SACE,OAAO,OAAO,YAAY,WACtB,OAAO,UACP,cAAc,IAAI;GACzB;;AAEH,QAAO;EAAE,MAAM;EAAS,SAAS,cAAc,IAAI;EAAE;;AAGvD,SAAS,cAAc,OAAwB;AAC7C,KAAI,UAAU,QAAQ,UAAU,KAAA,EAC9B,QAAO,OAAO,MAAM;AAEtB,KAAI,OAAO,UAAU,SACnB,QAAO;AAET,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,OAAO,MAAM;AAEtB,KAAI;AACF,SAAO,KAAK,UAAU,MAAM;SACtB;AACN,SAAO;;;AAIX,SAAgB,cAAc,MAAwB;CACpD,MAAM,MAAM,IAAI,MAAM,KAAK,QAAQ;AACnC,QAAO,eAAe,KAAK,QAAQ;EACjC,OAAO,KAAK;EACZ,cAAc;EACd,UAAU;EACX,CAAC;AACF,KAAI,KAAK,UAAU,KAAA,EACjB,KAAI,QAAQ,KAAK;AAEnB,KAAI,KAAK,UAAU,KAAA,EACjB,QAAO,eAAe,KAAK,SAAS;EAClC,OAAO,cAAc,KAAK,MAAM;EAChC,cAAc;EACd,UAAU;EACX,CAAC;AAEJ,QAAO"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/executor/worker/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Errors emitted by {@link WorkerHandle} at the IPC / transport boundary.
|
|
4
|
+
*
|
|
5
|
+
* These are distinct from per-job failures (which surface as
|
|
6
|
+
* `JobResult.success === false`). The manager branches on `name` to decide
|
|
7
|
+
* between retrying, replacing the worker, or surfacing the failure.
|
|
8
|
+
*/
|
|
9
|
+
var WorkerBusyError = class extends Error {
|
|
10
|
+
name = "WorkerBusyError";
|
|
11
|
+
constructor(workerId, jobId) {
|
|
12
|
+
super(`worker ${workerId} is busy; cannot dispatch job ${jobId} while another job is in flight`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var WorkerExitedError = class extends Error {
|
|
16
|
+
name = "WorkerExitedError";
|
|
17
|
+
constructor(workerId, exitCode, lastCorrelationId) {
|
|
18
|
+
super(`worker ${workerId} exited with code ${exitCode}` + (lastCorrelationId ? ` while handling correlationId ${lastCorrelationId}` : ""));
|
|
19
|
+
this.exitCode = exitCode;
|
|
20
|
+
this.lastCorrelationId = lastCorrelationId;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var WorkerAbortTimeoutError = class extends Error {
|
|
24
|
+
name = "WorkerAbortTimeoutError";
|
|
25
|
+
constructor(workerId, correlationId, graceMs) {
|
|
26
|
+
super(`worker ${workerId} did not acknowledge abort for ${correlationId} within ${graceMs}ms; terminating`);
|
|
27
|
+
this.correlationId = correlationId;
|
|
28
|
+
this.graceMs = graceMs;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var WorkerInitFailedError = class extends Error {
|
|
32
|
+
name = "WorkerInitFailedError";
|
|
33
|
+
constructor(workerId, reason, options) {
|
|
34
|
+
super(`worker ${workerId} failed to initialize: ${reason}`, options);
|
|
35
|
+
this.reason = reason;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var WorkerLoadModelFailedError = class extends Error {
|
|
39
|
+
name = "WorkerLoadModelFailedError";
|
|
40
|
+
constructor(workerId, documentType, version, reason, options) {
|
|
41
|
+
super(`worker ${workerId} failed to load model ${documentType}@${version}: ${reason}`, options);
|
|
42
|
+
this.documentType = documentType;
|
|
43
|
+
this.version = version;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var WorkerShutdownTimeoutError = class extends Error {
|
|
47
|
+
name = "WorkerShutdownTimeoutError";
|
|
48
|
+
constructor(workerId, graceMs) {
|
|
49
|
+
super(`worker ${workerId} did not drain within ${graceMs}ms; force-terminating`);
|
|
50
|
+
this.graceMs = graceMs;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { WorkerLoadModelFailedError as a, WorkerInitFailedError as i, WorkerBusyError as n, WorkerShutdownTimeoutError as o, WorkerExitedError as r, WorkerAbortTimeoutError as t };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=errors-D3S6Eysd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-D3S6Eysd.js","names":[],"sources":["../src/executor/worker/errors.ts"],"sourcesContent":["/**\n * Errors emitted by {@link WorkerHandle} at the IPC / transport boundary.\n *\n * These are distinct from per-job failures (which surface as\n * `JobResult.success === false`). The manager branches on `name` to decide\n * between retrying, replacing the worker, or surfacing the failure.\n */\n\nexport class WorkerBusyError extends Error {\n readonly name = \"WorkerBusyError\";\n constructor(workerId: string, jobId: string) {\n super(\n `worker ${workerId} is busy; cannot dispatch job ${jobId} while another job is in flight`,\n );\n }\n}\n\nexport class WorkerExitedError extends Error {\n readonly name = \"WorkerExitedError\";\n constructor(\n workerId: string,\n readonly exitCode: number,\n readonly lastCorrelationId: string | null,\n ) {\n super(\n `worker ${workerId} exited with code ${exitCode}` +\n (lastCorrelationId\n ? ` while handling correlationId ${lastCorrelationId}`\n : \"\"),\n );\n }\n}\n\nexport class WorkerAbortTimeoutError extends Error {\n readonly name = \"WorkerAbortTimeoutError\";\n constructor(\n workerId: string,\n readonly correlationId: string,\n readonly graceMs: number,\n ) {\n super(\n `worker ${workerId} did not acknowledge abort for ${correlationId} within ${graceMs}ms; terminating`,\n );\n }\n}\n\nexport class WorkerInitFailedError extends Error {\n readonly name = \"WorkerInitFailedError\";\n constructor(\n workerId: string,\n readonly reason: string,\n options?: { cause?: unknown },\n ) {\n super(`worker ${workerId} failed to initialize: ${reason}`, options);\n }\n}\n\nexport class WorkerLoadModelFailedError extends Error {\n readonly name = \"WorkerLoadModelFailedError\";\n constructor(\n workerId: string,\n readonly documentType: string,\n readonly version: string,\n reason: string,\n options?: { cause?: unknown },\n ) {\n super(\n `worker ${workerId} failed to load model ${documentType}@${version}: ${reason}`,\n options,\n );\n }\n}\n\nexport class WorkerShutdownTimeoutError extends Error {\n readonly name = \"WorkerShutdownTimeoutError\";\n constructor(\n workerId: string,\n readonly graceMs: number,\n ) {\n super(\n `worker ${workerId} did not drain within ${graceMs}ms; force-terminating`,\n );\n }\n}\n"],"mappings":";;;;;;;;AAQA,IAAa,kBAAb,cAAqC,MAAM;CACzC,OAAgB;CAChB,YAAY,UAAkB,OAAe;AAC3C,QACE,UAAU,SAAS,gCAAgC,MAAM,iCAC1D;;;AAIL,IAAa,oBAAb,cAAuC,MAAM;CAC3C,OAAgB;CAChB,YACE,UACA,UACA,mBACA;AACA,QACE,UAAU,SAAS,oBAAoB,cACpC,oBACG,iCAAiC,sBACjC,IACP;AARQ,OAAA,WAAA;AACA,OAAA,oBAAA;;;AAWb,IAAa,0BAAb,cAA6C,MAAM;CACjD,OAAgB;CAChB,YACE,UACA,eACA,SACA;AACA,QACE,UAAU,SAAS,iCAAiC,cAAc,UAAU,QAAQ,iBACrF;AALQ,OAAA,gBAAA;AACA,OAAA,UAAA;;;AAQb,IAAa,wBAAb,cAA2C,MAAM;CAC/C,OAAgB;CAChB,YACE,UACA,QACA,SACA;AACA,QAAM,UAAU,SAAS,yBAAyB,UAAU,QAAQ;AAH3D,OAAA,SAAA;;;AAOb,IAAa,6BAAb,cAAgD,MAAM;CACpD,OAAgB;CAChB,YACE,UACA,cACA,SACA,QACA,SACA;AACA,QACE,UAAU,SAAS,wBAAwB,aAAa,GAAG,QAAQ,IAAI,UACvE,QACD;AARQ,OAAA,eAAA;AACA,OAAA,UAAA;;;AAWb,IAAa,6BAAb,cAAgD,MAAM;CACpD,OAAgB;CAChB,YACE,UACA,SACA;AACA,QACE,UAAU,SAAS,wBAAwB,QAAQ,uBACpD;AAJQ,OAAA,UAAA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { n as toErrorInfo } from "./error-info-Cpu4OY3o.js";
|
|
2
|
+
//#region src/executor/worker/sanitize.ts
|
|
3
|
+
const MAX_DEPTH = 8;
|
|
4
|
+
function errorToInfo(err) {
|
|
5
|
+
return toErrorInfo(err);
|
|
6
|
+
}
|
|
7
|
+
function sanitizeArg(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
8
|
+
if (depth > MAX_DEPTH) return "[Truncated]";
|
|
9
|
+
if (value === null || value === void 0) return null;
|
|
10
|
+
if (typeof value === "boolean") return value;
|
|
11
|
+
if (typeof value === "number") return value;
|
|
12
|
+
if (typeof value === "string") return value;
|
|
13
|
+
if (typeof value === "bigint") return value.toString();
|
|
14
|
+
if (typeof value === "function" || typeof value === "symbol") return null;
|
|
15
|
+
if (value instanceof Error) return toErrorInfo(value);
|
|
16
|
+
if (value instanceof Date) return value.toISOString();
|
|
17
|
+
if (value instanceof Map) {
|
|
18
|
+
const obj = {};
|
|
19
|
+
for (const [k, v] of value.entries()) obj[String(k)] = sanitizeArg(v, depth + 1, seen);
|
|
20
|
+
return obj;
|
|
21
|
+
}
|
|
22
|
+
if (value instanceof Set) {
|
|
23
|
+
const arr = [];
|
|
24
|
+
for (const v of value.values()) arr.push(sanitizeArg(v, depth + 1, seen));
|
|
25
|
+
return arr;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
if (seen.has(value)) return "[Circular]";
|
|
29
|
+
seen.add(value);
|
|
30
|
+
const result = value.map((v) => sanitizeArg(v, depth + 1, seen));
|
|
31
|
+
seen.delete(value);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === "object") {
|
|
35
|
+
const proto = Object.getPrototypeOf(value);
|
|
36
|
+
if (proto !== Object.prototype && proto !== null) return { __nonClonable: value.constructor?.name || "Unknown" };
|
|
37
|
+
if (seen.has(value)) return "[Circular]";
|
|
38
|
+
seen.add(value);
|
|
39
|
+
const result = {};
|
|
40
|
+
for (const [k, v] of Object.entries(value)) result[k] = sanitizeArg(v, depth + 1, seen);
|
|
41
|
+
seen.delete(value);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/executor/worker/forwarding-logger.ts
|
|
48
|
+
function createForwardingLogger(post, level = "info", tags = []) {
|
|
49
|
+
function safePost(msg) {
|
|
50
|
+
try {
|
|
51
|
+
post(msg);
|
|
52
|
+
} catch {
|
|
53
|
+
try {
|
|
54
|
+
process.stderr.write(`[forwarding-logger] postMessage failed: ${msg.level} ${msg.message}\n`);
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function emit(wireLevel, message, args) {
|
|
59
|
+
const sanitizedArgs = args.map((a) => sanitizeArg(a));
|
|
60
|
+
safePost({
|
|
61
|
+
type: "log",
|
|
62
|
+
level: wireLevel,
|
|
63
|
+
message,
|
|
64
|
+
args: tags.length > 0 ? [sanitizeArg({ tags }), ...sanitizedArgs] : sanitizedArgs,
|
|
65
|
+
timestamp: Date.now()
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
level,
|
|
70
|
+
verbose: (message, ...args) => emit("debug", message, args),
|
|
71
|
+
debug: (message, ...args) => emit("debug", message, args),
|
|
72
|
+
info: (message, ...args) => emit("info", message, args),
|
|
73
|
+
warn: (message, ...args) => emit("warn", message, args),
|
|
74
|
+
error: (message, ...args) => emit("error", message, args),
|
|
75
|
+
errorHandler: ((...data) => {
|
|
76
|
+
const first = data[0];
|
|
77
|
+
emit("error", typeof first === "string" ? first : "", data.slice(1));
|
|
78
|
+
}),
|
|
79
|
+
child: (childTags) => createForwardingLogger(post, level, [...tags, ...childTags])
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { errorToInfo as n, sanitizeArg as r, createForwardingLogger as t };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=forwarding-logger-BBkMSxuJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forwarding-logger-BBkMSxuJ.js","names":[],"sources":["../src/executor/worker/sanitize.ts","../src/executor/worker/forwarding-logger.ts"],"sourcesContent":["import type { ErrorInfo, SanitizedArg } from \"./protocol.js\";\nimport { toErrorInfo } from \"./error-info.js\";\n\nconst MAX_DEPTH = 8;\n\nexport function errorToInfo(err: unknown): ErrorInfo {\n return toErrorInfo(err);\n}\n\nexport function sanitizeArg(\n value: unknown,\n depth = 0,\n seen = new WeakSet<object>(),\n): SanitizedArg {\n if (depth > MAX_DEPTH) {\n return \"[Truncated]\";\n }\n\n if (value === null || value === undefined) return null;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") return value;\n if (typeof value === \"bigint\") return value.toString();\n if (typeof value === \"function\" || typeof value === \"symbol\") return null;\n\n if (value instanceof Error) {\n return toErrorInfo(value);\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (value instanceof Map) {\n const obj: { [key: string]: SanitizedArg } = {};\n for (const [k, v] of value.entries()) {\n obj[String(k)] = sanitizeArg(v, depth + 1, seen);\n }\n return obj;\n }\n\n if (value instanceof Set) {\n const arr: SanitizedArg[] = [];\n for (const v of value.values()) {\n arr.push(sanitizeArg(v, depth + 1, seen));\n }\n return arr;\n }\n\n if (Array.isArray(value)) {\n if (seen.has(value)) return \"[Circular]\";\n seen.add(value);\n const result: SanitizedArg[] = value.map((v) =>\n sanitizeArg(v, depth + 1, seen),\n );\n seen.delete(value);\n return result;\n }\n\n if (typeof value === \"object\") {\n const proto: object | null = Object.getPrototypeOf(value) as object | null;\n if (proto !== Object.prototype && proto !== null) {\n const ctorName =\n (value as { constructor?: { name?: string } }).constructor?.name ||\n \"Unknown\";\n return { __nonClonable: ctorName };\n }\n\n if (seen.has(value as object)) return \"[Circular]\";\n seen.add(value as object);\n const result: { [key: string]: SanitizedArg } = {};\n for (const [k, v] of Object.entries(value as object)) {\n result[k] = sanitizeArg(v, depth + 1, seen);\n }\n seen.delete(value as object);\n return result;\n }\n\n return null;\n}\n","import type { ILogger, LoggerErrorHandler } from \"document-model\";\nimport type { LogMessage } from \"./protocol.js\";\nimport { sanitizeArg } from \"./sanitize.js\";\n\nexport function createForwardingLogger(\n post: (msg: LogMessage) => void,\n level: ILogger[\"level\"] = \"info\",\n tags: string[] = [],\n): ILogger {\n function safePost(msg: LogMessage): void {\n try {\n post(msg);\n } catch {\n try {\n process.stderr.write(\n `[forwarding-logger] postMessage failed: ${msg.level} ${msg.message}\\n`,\n );\n } catch {\n // nowhere left to report\n }\n }\n }\n\n function emit(\n wireLevel: LogMessage[\"level\"],\n message: string,\n args: unknown[],\n ): void {\n const sanitizedArgs = args.map((a) => sanitizeArg(a));\n const finalArgs =\n tags.length > 0\n ? [sanitizeArg({ tags }), ...sanitizedArgs]\n : sanitizedArgs;\n safePost({\n type: \"log\",\n level: wireLevel,\n message,\n args: finalArgs,\n timestamp: Date.now(),\n });\n }\n\n return {\n level,\n verbose: (message, ...args) => emit(\"debug\", message, args),\n debug: (message, ...args) => emit(\"debug\", message, args),\n info: (message, ...args) => emit(\"info\", message, args),\n warn: (message, ...args) => emit(\"warn\", message, args),\n error: (message, ...args) => emit(\"error\", message, args),\n errorHandler: ((...data: unknown[]) => {\n const first = data[0];\n const head = typeof first === \"string\" ? first : \"\";\n emit(\"error\", head, data.slice(1));\n }) as LoggerErrorHandler,\n child: (childTags) =>\n createForwardingLogger(post, level, [...tags, ...childTags]),\n };\n}\n"],"mappings":";;AAGA,MAAM,YAAY;AAElB,SAAgB,YAAY,KAAyB;AACnD,QAAO,YAAY,IAAI;;AAGzB,SAAgB,YACd,OACA,QAAQ,GACR,uBAAO,IAAI,SAAiB,EACd;AACd,KAAI,QAAQ,UACV,QAAO;AAGT,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAClD,KAAI,OAAO,UAAU,UAAW,QAAO;AACvC,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,OAAO,UAAU,SAAU,QAAO,MAAM,UAAU;AACtD,KAAI,OAAO,UAAU,cAAc,OAAO,UAAU,SAAU,QAAO;AAErE,KAAI,iBAAiB,MACnB,QAAO,YAAY,MAAM;AAG3B,KAAI,iBAAiB,KACnB,QAAO,MAAM,aAAa;AAG5B,KAAI,iBAAiB,KAAK;EACxB,MAAM,MAAuC,EAAE;AAC/C,OAAK,MAAM,CAAC,GAAG,MAAM,MAAM,SAAS,CAClC,KAAI,OAAO,EAAE,IAAI,YAAY,GAAG,QAAQ,GAAG,KAAK;AAElD,SAAO;;AAGT,KAAI,iBAAiB,KAAK;EACxB,MAAM,MAAsB,EAAE;AAC9B,OAAK,MAAM,KAAK,MAAM,QAAQ,CAC5B,KAAI,KAAK,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE3C,SAAO;;AAGT,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,MAAI,KAAK,IAAI,MAAM,CAAE,QAAO;AAC5B,OAAK,IAAI,MAAM;EACf,MAAM,SAAyB,MAAM,KAAK,MACxC,YAAY,GAAG,QAAQ,GAAG,KAAK,CAChC;AACD,OAAK,OAAO,MAAM;AAClB,SAAO;;AAGT,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,QAAuB,OAAO,eAAe,MAAM;AACzD,MAAI,UAAU,OAAO,aAAa,UAAU,KAI1C,QAAO,EAAE,eAFN,MAA8C,aAAa,QAC5D,WACgC;AAGpC,MAAI,KAAK,IAAI,MAAgB,CAAE,QAAO;AACtC,OAAK,IAAI,MAAgB;EACzB,MAAM,SAA0C,EAAE;AAClD,OAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAgB,CAClD,QAAO,KAAK,YAAY,GAAG,QAAQ,GAAG,KAAK;AAE7C,OAAK,OAAO,MAAgB;AAC5B,SAAO;;AAGT,QAAO;;;;AC1ET,SAAgB,uBACd,MACA,QAA0B,QAC1B,OAAiB,EAAE,EACV;CACT,SAAS,SAAS,KAAuB;AACvC,MAAI;AACF,QAAK,IAAI;UACH;AACN,OAAI;AACF,YAAQ,OAAO,MACb,2CAA2C,IAAI,MAAM,GAAG,IAAI,QAAQ,IACrE;WACK;;;CAMZ,SAAS,KACP,WACA,SACA,MACM;EACN,MAAM,gBAAgB,KAAK,KAAK,MAAM,YAAY,EAAE,CAAC;AAKrD,WAAS;GACP,MAAM;GACN,OAAO;GACP;GACA,MAPA,KAAK,SAAS,IACV,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,GAAG,cAAc,GACzC;GAMJ,WAAW,KAAK,KAAK;GACtB,CAAC;;AAGJ,QAAO;EACL;EACA,UAAU,SAAS,GAAG,SAAS,KAAK,SAAS,SAAS,KAAK;EAC3D,QAAQ,SAAS,GAAG,SAAS,KAAK,SAAS,SAAS,KAAK;EACzD,OAAO,SAAS,GAAG,SAAS,KAAK,QAAQ,SAAS,KAAK;EACvD,OAAO,SAAS,GAAG,SAAS,KAAK,QAAQ,SAAS,KAAK;EACvD,QAAQ,SAAS,GAAG,SAAS,KAAK,SAAS,SAAS,KAAK;EACzD,gBAAgB,GAAG,SAAoB;GACrC,MAAM,QAAQ,KAAK;AAEnB,QAAK,SADQ,OAAO,UAAU,WAAW,QAAQ,IAC7B,KAAK,MAAM,EAAE,CAAC;;EAEpC,QAAQ,cACN,uBAAuB,MAAM,OAAO,CAAC,GAAG,MAAM,GAAG,UAAU,CAAC;EAC/D"}
|