@hydraharness/harness-code-runtime-worker-thread 0.1.1-rc.6

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/lib/worker.cjs ADDED
@@ -0,0 +1,909 @@
1
+ let node_worker_threads = require("node:worker_threads");
2
+ let node_util = require("node:util");
3
+ //#region lib/types/output-json.js
4
+ /** JSON string-prefix accounting for the outer-output ledger. @module @hydraharness/harness-code-runtime-worker-thread/output-json */
5
+ const intrinsicReflectApply$1 = Reflect.apply;
6
+ const intrinsicArrayIsArray$1 = Array.isArray;
7
+ const IntrinsicBuffer = Buffer;
8
+ const intrinsicBufferByteLength = Reflect.get(Buffer, "byteLength");
9
+ const intrinsicObjectCreate$1 = Object.create;
10
+ const intrinsicObjectDefineProperty$1 = Object.defineProperty;
11
+ const intrinsicObjectKeys$1 = Object.keys;
12
+ const intrinsicString = String;
13
+ const intrinsicStringCharCodeAt = Reflect.get(String.prototype, "charCodeAt");
14
+ const intrinsicStringCodePointAt = Reflect.get(String.prototype, "codePointAt");
15
+ const intrinsicStringSlice = Reflect.get(String.prototype, "slice");
16
+ /** Build a data descriptor that cannot inherit model-defined accessor fields. */
17
+ function dataDescriptor$1(value) {
18
+ const descriptor = intrinsicObjectCreate$1(null);
19
+ descriptor.value = value;
20
+ return descriptor;
21
+ }
22
+ /** Define an ordinary enumerable data slot without a prototype-bearing descriptor. */
23
+ function defineEnumerableDataProperty$1(target, key, value) {
24
+ const descriptor = dataDescriptor$1(value);
25
+ descriptor.enumerable = true;
26
+ descriptor.configurable = true;
27
+ descriptor.writable = true;
28
+ intrinsicObjectDefineProperty$1(target, key, descriptor);
29
+ }
30
+ /** UTF-8 byte length through the module-captured Node intrinsic. */
31
+ function byteLength(text) {
32
+ return intrinsicReflectApply$1(intrinsicBufferByteLength, IntrinsicBuffer, [text, "utf8"]);
33
+ }
34
+ /** Append without consulting a model-mutated `Array.prototype`. */
35
+ function append$1(target, value) {
36
+ defineEnumerableDataProperty$1(target, target.length, value);
37
+ }
38
+ /** Pop without consulting a model-mutated `Array.prototype`. */
39
+ function takeLast$1(target) {
40
+ if (target.length === 0) return void 0;
41
+ const index = target.length - 1;
42
+ const value = target[index];
43
+ intrinsicObjectDefineProperty$1(target, "length", dataDescriptor$1(index));
44
+ return value;
45
+ }
46
+ /** One code-point-aligned character from a string. */
47
+ function characterAt(text, index) {
48
+ return intrinsicReflectApply$1(intrinsicStringSlice, text, [index, index + (intrinsicReflectApply$1(intrinsicStringCodePointAt, text, [index]) > 65535 ? 2 : 1)]);
49
+ }
50
+ /** Serialized bytes contributed by one complete Unicode code point inside JSON quotes. */
51
+ function serializedCharacterBytes(character) {
52
+ if (character.length === 2) return 4;
53
+ if (character === "\"" || character === "\\") return 2;
54
+ const code = intrinsicReflectApply$1(intrinsicStringCharCodeAt, character, [0]);
55
+ if (code >= 55296 && code <= 57343) return 6;
56
+ if (code < 32) return code === 8 || code === 9 || code === 10 || code === 12 || code === 13 ? 2 : 6;
57
+ return byteLength(character);
58
+ }
59
+ /**
60
+ * Measure one JSON string without materializing its complete escaped form.
61
+ * @param text - the candidate string.
62
+ * @param maxBytes - largest serialized size the caller can admit.
63
+ * @returns Exact serialized bytes, or `undefined` as soon as the cap is crossed.
64
+ */
65
+ function jsonStringBytesUpTo(text, maxBytes) {
66
+ if (maxBytes < 2) return void 0;
67
+ let bytes = 2;
68
+ for (let index = 0; index < text.length;) {
69
+ const character = characterAt(text, index);
70
+ bytes += serializedCharacterBytes(character);
71
+ if (bytes > maxBytes) return void 0;
72
+ index += character.length;
73
+ }
74
+ return bytes;
75
+ }
76
+ /**
77
+ * Measure one lossless JSON value without allocating its serialized form.
78
+ * @param value - already validated lossless JSON.
79
+ * @param maxBytes - largest serialized size the caller can admit.
80
+ * @returns Exact serialized bytes, or `undefined` as soon as the cap is crossed.
81
+ */
82
+ function jsonValueBytesUpTo(value, maxBytes) {
83
+ let bytes = 0;
84
+ const add = (cost) => {
85
+ bytes += cost;
86
+ return bytes <= maxBytes;
87
+ };
88
+ const tasks = [{
89
+ kind: "value",
90
+ value
91
+ }];
92
+ for (let task = takeLast$1(tasks); task !== void 0; task = takeLast$1(tasks)) {
93
+ if (task.kind === "value") {
94
+ const current = task.value;
95
+ if (current === null) {
96
+ if (!add(4)) return void 0;
97
+ } else if (typeof current === "string") {
98
+ const stringBytes = jsonStringBytesUpTo(current, maxBytes - bytes);
99
+ if (stringBytes === void 0) return void 0;
100
+ bytes += stringBytes;
101
+ } else if (typeof current === "number") {
102
+ if (!add(byteLength(intrinsicString(current)))) return void 0;
103
+ } else if (typeof current === "boolean") {
104
+ if (!add(current ? 4 : 5)) return void 0;
105
+ } else if (intrinsicArrayIsArray$1(current)) {
106
+ if (!add(2)) return void 0;
107
+ if (current.length > 0) append$1(tasks, {
108
+ kind: "array",
109
+ value: current,
110
+ index: 0
111
+ });
112
+ } else {
113
+ if (!add(2)) return void 0;
114
+ const keys = intrinsicObjectKeys$1(current);
115
+ if (keys.length > 0) append$1(tasks, {
116
+ kind: "object",
117
+ value: current,
118
+ keys,
119
+ index: 0
120
+ });
121
+ }
122
+ continue;
123
+ }
124
+ if (task.index > 0 && !add(1)) return void 0;
125
+ if (task.kind === "array") {
126
+ const item = task.value[task.index];
127
+ if (item === void 0) return void 0;
128
+ if (task.index + 1 < task.value.length) append$1(tasks, {
129
+ ...task,
130
+ index: task.index + 1
131
+ });
132
+ append$1(tasks, {
133
+ kind: "value",
134
+ value: item
135
+ });
136
+ continue;
137
+ }
138
+ const key = task.keys[task.index];
139
+ /* v8 ignore next -- an object frame is created and advanced only for an existing Object.keys entry. */
140
+ if (key === void 0) return void 0;
141
+ const keyBytes = jsonStringBytesUpTo(key, maxBytes - bytes);
142
+ if (keyBytes === void 0) return void 0;
143
+ if (!add(keyBytes + 1)) return void 0;
144
+ const item = task.value[key];
145
+ if (item === void 0) return void 0;
146
+ if (task.index + 1 < task.keys.length) append$1(tasks, {
147
+ ...task,
148
+ index: task.index + 1
149
+ });
150
+ append$1(tasks, {
151
+ kind: "value",
152
+ value: item
153
+ });
154
+ }
155
+ return bytes;
156
+ }
157
+ /**
158
+ * Return the longest code-point-aligned prefix whose JSON string encoding,
159
+ * including its surrounding quotes, fits `maxBytes`.
160
+ *
161
+ * @param text - the candidate string.
162
+ * @param maxBytes - serialized JSON-string bytes available.
163
+ * @returns the fitting prefix, or an empty string when even useful content cannot fit.
164
+ */
165
+ function truncateJsonStringBytes(text, maxBytes) {
166
+ if (maxBytes < 2) return "";
167
+ let bytes = 2;
168
+ let end = 0;
169
+ for (let index = 0; index < text.length;) {
170
+ const character = characterAt(text, index);
171
+ const cost = serializedCharacterBytes(character);
172
+ if (bytes + cost > maxBytes) break;
173
+ bytes += cost;
174
+ end += character.length;
175
+ index += character.length;
176
+ }
177
+ return end === text.length ? text : intrinsicReflectApply$1(intrinsicStringSlice, text, [0, end]);
178
+ }
179
+ //#endregion
180
+ //#region lib/types/worker-json.js
181
+ /**
182
+ * Lossless-JSON snapshots for the dependency-free source worker closure.
183
+ * @module @hydraharness/harness-code-runtime-worker-thread/worker-json
184
+ */
185
+ const intrinsicFunctionToString = Reflect.get(Function.prototype, "toString");
186
+ const intrinsicReflectApply = Reflect.get(Reflect, "apply");
187
+ const IntrinsicError = Error;
188
+ const IntrinsicSet = Set;
189
+ const intrinsicArrayIsArray = Array.isArray;
190
+ const intrinsicArrayPrototype = Array.prototype;
191
+ const intrinsicNumberIsFinite = Number.isFinite;
192
+ const intrinsicNumberIsSafeInteger = Number.isSafeInteger;
193
+ const intrinsicObjectCreate = Object.create;
194
+ const intrinsicObjectDefineProperty = Object.defineProperty;
195
+ const intrinsicObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
196
+ const intrinsicObjectGetPrototypeOf = Object.getPrototypeOf;
197
+ const intrinsicObjectHasOwn = Object.hasOwn;
198
+ const intrinsicObjectIs = Object.is;
199
+ const intrinsicObjectKeys = Object.keys;
200
+ const intrinsicObjectPrototype = Object.prototype;
201
+ const intrinsicObjectPropertyIsEnumerable = Reflect.get(intrinsicObjectPrototype, "propertyIsEnumerable");
202
+ const intrinsicReflectOwnKeys = Reflect.ownKeys;
203
+ const intrinsicSetAdd = Reflect.get(Set.prototype, "add");
204
+ const intrinsicSetDelete = Reflect.get(Set.prototype, "delete");
205
+ const intrinsicSetHas = Reflect.get(Set.prototype, "has");
206
+ /** Build a data descriptor that cannot inherit model-defined accessor fields. */
207
+ function dataDescriptor(value) {
208
+ const descriptor = intrinsicObjectCreate(null);
209
+ descriptor.value = value;
210
+ return descriptor;
211
+ }
212
+ /** Define an ordinary enumerable data slot without a prototype-bearing descriptor. */
213
+ function defineEnumerableDataProperty(target, key, value) {
214
+ const descriptor = dataDescriptor(value);
215
+ descriptor.enumerable = true;
216
+ descriptor.configurable = true;
217
+ descriptor.writable = true;
218
+ intrinsicObjectDefineProperty(target, key, descriptor);
219
+ }
220
+ /** Append without consulting a model-mutated `Array.prototype`. */
221
+ function append(target, value) {
222
+ defineEnumerableDataProperty(target, target.length, value);
223
+ }
224
+ /** Pop without consulting a model-mutated `Array.prototype`. */
225
+ function takeLast(target) {
226
+ if (target.length === 0) return void 0;
227
+ const index = target.length - 1;
228
+ const value = target[index];
229
+ intrinsicObjectDefineProperty(target, "length", dataDescriptor(index));
230
+ return value;
231
+ }
232
+ /** Whether one captured-intrinsic Set contains a value. */
233
+ function setHas(target, value) {
234
+ return intrinsicReflectApply(intrinsicSetHas, target, [value]);
235
+ }
236
+ /** Add to one captured-intrinsic Set. */
237
+ function setAdd(target, value) {
238
+ intrinsicReflectApply(intrinsicSetAdd, target, [value]);
239
+ }
240
+ /** Delete from one captured-intrinsic Set. */
241
+ function setDelete(target, value) {
242
+ intrinsicReflectApply(intrinsicSetDelete, target, [value]);
243
+ }
244
+ /** Whether a realm-owned intrinsic prototype is backed by its native constructor. */
245
+ function hasIntrinsicConstructor(prototype, name) {
246
+ const constructor = intrinsicObjectGetOwnPropertyDescriptor(prototype, "constructor")?.value;
247
+ if (typeof constructor !== "function") return false;
248
+ try {
249
+ return constructor.name === name && constructor.prototype === prototype && intrinsicReflectApply(intrinsicFunctionToString, constructor, []) === `function ${name}() { [native code] }`;
250
+ } catch {
251
+ return false;
252
+ }
253
+ }
254
+ /** Whether a candidate is a foreign realm's intrinsic `Object.prototype`. */
255
+ function isForeignIntrinsicObjectPrototype(value) {
256
+ return intrinsicObjectGetPrototypeOf(value) === null && hasIntrinsicConstructor(value, "Object");
257
+ }
258
+ /** Whether an array uses one realm's intrinsic `Array.prototype`, not a subclass or forged prototype. */
259
+ function hasPlainArrayPrototype(value) {
260
+ const prototype = intrinsicObjectGetPrototypeOf(value);
261
+ if (prototype === intrinsicArrayPrototype) return true;
262
+ if (!intrinsicArrayIsArray(prototype) || !hasIntrinsicConstructor(prototype, "Array")) return false;
263
+ const objectPrototype = intrinsicObjectGetPrototypeOf(prototype);
264
+ return typeof objectPrototype === "object" && objectPrototype !== null && isForeignIntrinsicObjectPrototype(objectPrototype);
265
+ }
266
+ /** Whether an object is a plain or null-prototype record from any JavaScript realm. */
267
+ function hasPlainObjectPrototype(value) {
268
+ const prototype = intrinsicObjectGetPrototypeOf(value);
269
+ return prototype === null || prototype === intrinsicObjectPrototype || typeof prototype === "object" && isForeignIntrinsicObjectPrototype(prototype);
270
+ }
271
+ /** Return every JSON-visible object key, or reject own data JSON would discard. */
272
+ function enumerableStringKeys(value) {
273
+ const keys = intrinsicReflectOwnKeys(value);
274
+ for (let index = 0; index < keys.length; index++) {
275
+ const key = keys[index];
276
+ if (typeof key !== "string" || !intrinsicReflectApply(intrinsicObjectPropertyIsEnumerable, value, [key])) return void 0;
277
+ }
278
+ return keys;
279
+ }
280
+ /**
281
+ * Validate and detach one worker-boundary value without loading another
282
+ * workspace package at runtime. This mirrors the session-owned canonical
283
+ * JSON boundary while remaining safe to import from the unbuilt worker.
284
+ * Its iterative traversal adds no JavaScript call-stack depth limit.
285
+ *
286
+ * @param value - the candidate completion value.
287
+ * @returns a detached lossless-JSON snapshot, or `undefined` when invalid.
288
+ */
289
+ function snapshotCodeJsonValue(value) {
290
+ const active = new IntrinsicSet();
291
+ let root;
292
+ const assign = (destination, item) => {
293
+ if (destination.kind === "root") root = item;
294
+ else if (destination.kind === "array") defineEnumerableDataProperty(destination.target, destination.index, item);
295
+ else defineEnumerableDataProperty(destination.target, destination.key, item);
296
+ };
297
+ const tasks = [{
298
+ kind: "visit",
299
+ value,
300
+ destination: { kind: "root" }
301
+ }];
302
+ for (let task = takeLast(tasks); task !== void 0; task = takeLast(tasks)) {
303
+ if (task.kind === "leave") {
304
+ setDelete(active, task.source);
305
+ continue;
306
+ }
307
+ if (task.kind === "array-item") {
308
+ if (!intrinsicObjectHasOwn(task.source, task.index)) return void 0;
309
+ append(tasks, {
310
+ kind: "visit",
311
+ value: task.source[task.index],
312
+ destination: {
313
+ kind: "array",
314
+ target: task.target,
315
+ index: task.index
316
+ }
317
+ });
318
+ continue;
319
+ }
320
+ if (task.kind === "object-property") {
321
+ append(tasks, {
322
+ kind: "visit",
323
+ value: task.source[task.key],
324
+ destination: {
325
+ kind: "object",
326
+ target: task.target,
327
+ key: task.key
328
+ }
329
+ });
330
+ continue;
331
+ }
332
+ const candidate = task.value;
333
+ if (candidate === null) {
334
+ assign(task.destination, null);
335
+ continue;
336
+ }
337
+ if (typeof candidate === "boolean" || typeof candidate === "string") {
338
+ assign(task.destination, candidate);
339
+ continue;
340
+ }
341
+ if (typeof candidate === "number") {
342
+ if (!intrinsicNumberIsFinite(candidate) || intrinsicObjectIs(candidate, -0)) return void 0;
343
+ assign(task.destination, candidate);
344
+ continue;
345
+ }
346
+ if (typeof candidate !== "object") return void 0;
347
+ if (setHas(active, candidate)) return void 0;
348
+ if (intrinsicArrayIsArray(candidate)) {
349
+ if (!hasPlainArrayPrototype(candidate)) return void 0;
350
+ const length = candidate.length;
351
+ if (intrinsicReflectOwnKeys(candidate).length !== length + 1) return void 0;
352
+ const target = [];
353
+ assign(task.destination, target);
354
+ setAdd(active, candidate);
355
+ append(tasks, {
356
+ kind: "leave",
357
+ source: candidate
358
+ });
359
+ for (let index = length - 1; index >= 0; index--) append(tasks, {
360
+ kind: "array-item",
361
+ source: candidate,
362
+ index,
363
+ target
364
+ });
365
+ continue;
366
+ }
367
+ if (!hasPlainObjectPrototype(candidate)) return void 0;
368
+ const keys = enumerableStringKeys(candidate);
369
+ if (keys === void 0) return void 0;
370
+ const target = {};
371
+ assign(task.destination, target);
372
+ setAdd(active, candidate);
373
+ append(tasks, {
374
+ kind: "leave",
375
+ source: candidate
376
+ });
377
+ for (let index = keys.length - 1; index >= 0; index--) {
378
+ const key = keys[index];
379
+ /* v8 ignore next -- the loop is bounded by the captured key count. */
380
+ if (key === void 0) return void 0;
381
+ append(tasks, {
382
+ kind: "object-property",
383
+ source: candidate,
384
+ key,
385
+ target
386
+ });
387
+ }
388
+ }
389
+ return root;
390
+ }
391
+ /**
392
+ * Flatten one validated JSON value for the worker-thread message port.
393
+ * @param value - the lossless JSON value to transport.
394
+ * @returns a pre-order token stream whose own nesting is bounded.
395
+ */
396
+ function encodeWorkerJson(value) {
397
+ const wire = [];
398
+ const pending = [value];
399
+ for (let current = takeLast(pending); current !== void 0; current = takeLast(pending)) {
400
+ if (current === null || typeof current === "boolean" || typeof current === "number" || typeof current === "string") {
401
+ append(wire, current);
402
+ continue;
403
+ }
404
+ if (intrinsicArrayIsArray(current)) {
405
+ append(wire, {
406
+ kind: "array",
407
+ length: current.length
408
+ });
409
+ for (let index = current.length - 1; index >= 0; index--) {
410
+ const item = current[index];
411
+ if (item === void 0) throw new IntrinsicError("cannot encode a sparse JSON array");
412
+ append(pending, item);
413
+ }
414
+ continue;
415
+ }
416
+ const keys = intrinsicObjectKeys(current);
417
+ append(wire, {
418
+ kind: "object",
419
+ keys
420
+ });
421
+ for (let index = keys.length - 1; index >= 0; index--) {
422
+ const key = keys[index];
423
+ /* v8 ignore next -- the loop is bounded by the captured key count. */
424
+ if (key === void 0) throw new IntrinsicError("cannot encode a missing JSON object key");
425
+ const item = current[key];
426
+ if (item === void 0) throw new IntrinsicError("cannot encode an undefined JSON object property");
427
+ append(pending, item);
428
+ }
429
+ }
430
+ return wire;
431
+ }
432
+ /** Whether an array contains exactly its dense indexed slots and `length`. */
433
+ function isDenseArray(value) {
434
+ if (!hasPlainArrayPrototype(value) || intrinsicReflectOwnKeys(value).length !== value.length + 1) return false;
435
+ for (let index = 0; index < value.length; index++) if (!intrinsicObjectHasOwn(value, index)) return false;
436
+ return true;
437
+ }
438
+ /** Whether one exact string-key list contains a key, without consulting its prototype. */
439
+ function keysContain(keys, expected) {
440
+ for (let index = 0; index < keys.length; index++) if (keys[index] === expected) return true;
441
+ return false;
442
+ }
443
+ /** Return one exact container marker, or reject any extra/missing fields. */
444
+ function containerToken(value) {
445
+ if (intrinsicArrayIsArray(value) || !hasPlainObjectPrototype(value)) return void 0;
446
+ const keys = enumerableStringKeys(value);
447
+ if (keys === void 0) return void 0;
448
+ const token = value;
449
+ if (token.kind === "array") {
450
+ if (keys.length !== 2 || !keysContain(keys, "kind") || !keysContain(keys, "length")) return void 0;
451
+ const length = token.length;
452
+ return typeof length === "number" && intrinsicNumberIsSafeInteger(length) && length >= 0 ? {
453
+ kind: "array",
454
+ length
455
+ } : void 0;
456
+ }
457
+ if (token.kind === "object") {
458
+ if (keys.length !== 2 || !keysContain(keys, "kind") || !keysContain(keys, "keys")) return void 0;
459
+ const objectKeys = token.keys;
460
+ if (!intrinsicArrayIsArray(objectKeys) || !isDenseArray(objectKeys)) return void 0;
461
+ const unique = new IntrinsicSet();
462
+ const normalizedKeys = [];
463
+ const objectKeyValues = objectKeys;
464
+ for (let index = 0; index < objectKeyValues.length; index++) {
465
+ const key = objectKeyValues[index];
466
+ if (typeof key !== "string" || setHas(unique, key)) return void 0;
467
+ setAdd(unique, key);
468
+ append(normalizedKeys, key);
469
+ }
470
+ return {
471
+ kind: "object",
472
+ keys: normalizedKeys
473
+ };
474
+ }
475
+ }
476
+ /**
477
+ * Rebuild one lossless JSON value from the flat worker-thread wire format.
478
+ * Malformed or incomplete traffic returns `undefined`; traversal is iterative
479
+ * and therefore independent of the transported value's application depth.
480
+ * @param input - untrusted message-port payload.
481
+ * @returns the detached JSON value, or `undefined` when the wire is invalid.
482
+ */
483
+ function decodeWorkerJson(input) {
484
+ try {
485
+ if (!intrinsicArrayIsArray(input) || !isDenseArray(input) || input.length === 0) return void 0;
486
+ const wire = input;
487
+ const frames = [];
488
+ let root;
489
+ let rootAssigned = false;
490
+ const attach = (value) => {
491
+ const parent = frames[frames.length - 1];
492
+ if (!parent) {
493
+ if (rootAssigned) return false;
494
+ root = value;
495
+ rootAssigned = true;
496
+ return true;
497
+ }
498
+ /* v8 ignore next -- completed frames are popped before another token can attach. */
499
+ if (parent.index >= (parent.kind === "array" ? parent.length : parent.keys.length)) return false;
500
+ if (parent.kind === "array") append(parent.target, value);
501
+ else {
502
+ const key = parent.keys[parent.index];
503
+ /* v8 ignore next -- object frames are built from validated keys and their exact length. */
504
+ if (key === void 0) return false;
505
+ defineEnumerableDataProperty(parent.target, key, value);
506
+ }
507
+ parent.index += 1;
508
+ return true;
509
+ };
510
+ for (let tokenIndex = 0; tokenIndex < wire.length; tokenIndex++) {
511
+ const token = wire[tokenIndex];
512
+ let value;
513
+ let frame;
514
+ if (token === null || typeof token === "boolean" || typeof token === "string") value = token;
515
+ else if (typeof token === "number") {
516
+ if (!intrinsicNumberIsFinite(token) || intrinsicObjectIs(token, -0)) return void 0;
517
+ value = token;
518
+ } else {
519
+ if (typeof token !== "object") return void 0;
520
+ const marker = containerToken(token);
521
+ if (!marker) return void 0;
522
+ const remainingTokens = wire.length - tokenIndex - 1;
523
+ if (marker.kind === "array") {
524
+ if (marker.length > remainingTokens) return void 0;
525
+ const target = [];
526
+ value = target;
527
+ if (marker.length > 0) frame = {
528
+ kind: "array",
529
+ target,
530
+ length: marker.length,
531
+ index: 0
532
+ };
533
+ } else {
534
+ if (marker.keys.length > remainingTokens) return void 0;
535
+ const target = {};
536
+ value = target;
537
+ if (marker.keys.length > 0) frame = {
538
+ kind: "object",
539
+ target,
540
+ keys: marker.keys,
541
+ index: 0
542
+ };
543
+ }
544
+ }
545
+ if (!attach(value)) return void 0;
546
+ if (frame) append(frames, frame);
547
+ while (frames.length > 0) {
548
+ const current = frames[frames.length - 1];
549
+ /* v8 ignore next -- the loop condition guarantees a final frame. */
550
+ if (current === void 0) break;
551
+ if (current.index < (current.kind === "array" ? current.length : current.keys.length)) break;
552
+ takeLast(frames);
553
+ }
554
+ }
555
+ return frames.length === 0 ? root : void 0;
556
+ } catch {
557
+ return;
558
+ }
559
+ }
560
+ //#endregion
561
+ //#region lib/types/bootstrap.js
562
+ /**
563
+ * Worker-side execution logic, written as plain functions over an injected port so the unit
564
+ * suite can run every line IN-PROCESS against a fake port (a real worker thread is a separate
565
+ * V8 isolate the coverage provider cannot observe).
566
+ * @module @hydraharness/harness-code-runtime-worker-thread/src/bootstrap
567
+ */
568
+ const CapturedError = Error;
569
+ const capturedObjectCreate = Object.create;
570
+ const capturedObjectDefineProperty = Object.defineProperty;
571
+ /** Define one public binding-error field without consulting mutable globals or descriptor prototypes. */
572
+ function defineBindingErrorField(error, key, value) {
573
+ const attributes = capturedObjectCreate(null);
574
+ attributes.enumerable = true;
575
+ attributes.value = value;
576
+ capturedObjectDefineProperty(error, key, attributes);
577
+ }
578
+ /**
579
+ * Ordered text capture under the shared outer JSON-byte budget, delivered to
580
+ * a sink as each item lands (the real sink streams text over the port eagerly,
581
+ * so captured output survives a mid-run termination). It includes the log
582
+ * array syntax and string escaping in its accounting. Once exhausted it emits
583
+ * the fitting prefix and reports the limit once; the host turns that condition
584
+ * into an explicit `output-limit` run failure.
585
+ */
586
+ var LogBuffer = class {
587
+ bytes = 2;
588
+ entries = 0;
589
+ truncated = false;
590
+ sink;
591
+ onLimit;
592
+ maxBytes;
593
+ constructor(maxBytes, sink, onLimit = () => {}) {
594
+ this.maxBytes = maxBytes;
595
+ this.sink = sink;
596
+ this.onLimit = onLimit;
597
+ }
598
+ /**
599
+ * Emit text to the sink, charging it against the budget (drops + marks once exhausted).
600
+ * @param text - the captured text to deliver.
601
+ */
602
+ push(text) {
603
+ if (this.truncated) return;
604
+ const separatorBytes = this.entries > 0 ? 1 : 0;
605
+ const availableBytes = this.maxBytes - this.bytes - separatorBytes;
606
+ const stringBytes = jsonStringBytesUpTo(text, availableBytes);
607
+ if (stringBytes === void 0) {
608
+ this.truncated = true;
609
+ const prefix = truncateJsonStringBytes(text, availableBytes);
610
+ if (prefix.length > 0) {
611
+ const prefixBytes = jsonStringBytesUpTo(prefix, availableBytes);
612
+ /* v8 ignore next -- truncateJsonStringBytes guarantees the returned prefix fits. */
613
+ if (prefixBytes === void 0) throw new CapturedError("worker output ledger produced an oversized log prefix");
614
+ this.bytes += prefixBytes + separatorBytes;
615
+ this.entries += 1;
616
+ this.sink(prefix);
617
+ }
618
+ this.onLimit();
619
+ return;
620
+ }
621
+ this.bytes += stringBytes + separatorBytes;
622
+ this.entries += 1;
623
+ this.sink(text);
624
+ }
625
+ /** Remaining exact JSON-byte budget for the completion value or failure message. */
626
+ remainingOutputBytes() {
627
+ return this.maxBytes - this.bytes;
628
+ }
629
+ };
630
+ /** The five console methods the shim captures, in the seam's level vocabulary. */
631
+ const CONSOLE_LEVELS = [
632
+ "log",
633
+ "info",
634
+ "warn",
635
+ "error",
636
+ "debug"
637
+ ];
638
+ /**
639
+ * A `console` replacement whose five leveled methods render their arguments
640
+ * `util.inspect`-style (matching real console formatting closely enough for
641
+ * a model to recognize its own output) into the buffer. Only these five
642
+ * exist — the program gets a deliberately small console, not Node's full
643
+ * console API.
644
+ * @param logs - the buffer every rendered line is pushed into.
645
+ * @returns the five-method console object handed to the program.
646
+ */
647
+ function makeConsoleShim(logs) {
648
+ const render = (args) => args.map((arg) => typeof arg === "string" ? arg : (0, node_util.inspect)(arg, INSPECT_OPTIONS)).join(" ");
649
+ const shim = Object.create(null);
650
+ for (const level of CONSOLE_LEVELS) shim[level] = (...args) => {
651
+ logs.push(render(args));
652
+ };
653
+ return shim;
654
+ }
655
+ /**
656
+ * Redirect a stream's `write` into the log buffer (the program-visible
657
+ * `process.stdout`/`process.stderr` in the real worker), so raw writes land in emission order
658
+ * alongside console output instead of racing down a pipe. It preserves Node's optional callback
659
+ * contract: the callback runs asynchronously after admission, even when the log budget drops
660
+ * the write.
661
+ *
662
+ * @param logs - the buffer captured writes are pushed into.
663
+ * @param stream - the stream whose `write` slot is patched.
664
+ * @returns the restore function (the in-process tests un-patch; the real
665
+ * worker never needs to).
666
+ */
667
+ function captureStreamWrites(logs, stream) {
668
+ const original = stream.write;
669
+ stream.write = (chunk, ...rest) => {
670
+ logs.push(typeof chunk === "string" ? chunk : String(chunk));
671
+ const callback = [rest[0], rest[1]].find((arg) => typeof arg === "function");
672
+ if (callback) queueMicrotask(() => {
673
+ callback(null);
674
+ });
675
+ return true;
676
+ };
677
+ return () => {
678
+ stream.write = original;
679
+ };
680
+ }
681
+ /** Bounded inspect options: deep enough to be useful, bounded so a pathological value cannot explode the rendering. */
682
+ const INSPECT_OPTIONS = {
683
+ depth: 4,
684
+ maxArrayLength: 100,
685
+ maxStringLength: 1e4
686
+ };
687
+ /**
688
+ * Prepare the program's completion value for the done message. Only lossless
689
+ * JSON crosses, and a value that does not fit the remaining combined outer
690
+ * budget reports `output-limit`; the host revalidates hostile traffic and
691
+ * remains authoritative for native pipe writes the worker cannot observe.
692
+ *
693
+ * @param value - the program's completion value.
694
+ * @param remainingOutputBytes - exact bytes left after captured logs.
695
+ * @param maxOutputBytes - the configured cap named in an overflow diagnostic.
696
+ * @returns the done-message fragment: `{}` for `undefined`, else a flat wire `{ value }`.
697
+ */
698
+ function prepareCompletion(value, remainingOutputBytes, maxOutputBytes = remainingOutputBytes) {
699
+ if (value === void 0) return {};
700
+ let snapshot;
701
+ try {
702
+ snapshot = snapshotCodeJsonValue(value);
703
+ } catch {
704
+ snapshot = void 0;
705
+ }
706
+ if (snapshot === void 0) return prepareFailure("invalid-output", "program completion must be lossless JSON", remainingOutputBytes, maxOutputBytes);
707
+ if (jsonValueBytesUpTo(snapshot, remainingOutputBytes) === void 0) return outputLimit(maxOutputBytes);
708
+ return { value: encodeWorkerJson(snapshot) };
709
+ }
710
+ /** Build the fixed overflow fragment without carrying rejected variable bytes. */
711
+ function outputLimit(maxOutputBytes) {
712
+ return { error: {
713
+ kind: "output-limit",
714
+ message: `outer output exceeded ${maxOutputBytes} bytes`
715
+ } };
716
+ }
717
+ /** Admit one bounded failure message or replace it with the fixed overflow diagnostic. */
718
+ function prepareFailure(kind, message, remainingOutputBytes, maxOutputBytes) {
719
+ if (jsonStringBytesUpTo(message, remainingOutputBytes) === void 0) return outputLimit(maxOutputBytes);
720
+ return { error: {
721
+ kind,
722
+ message
723
+ } };
724
+ }
725
+ /**
726
+ * Prepare a thrown program value without sending an unbounded stack or
727
+ * string across the worker port.
728
+ * @param error - the value thrown by the program.
729
+ * @param remainingOutputBytes - exact bytes left after captured logs.
730
+ * @param maxOutputBytes - the configured cap named in an overflow diagnostic.
731
+ * @returns a bounded exception or fixed output-limit fragment.
732
+ */
733
+ function prepareException(error, remainingOutputBytes, maxOutputBytes = remainingOutputBytes) {
734
+ let message;
735
+ try {
736
+ const detail = error instanceof CapturedError ? error.stack ?? error.message : error;
737
+ message = typeof detail === "string" ? detail : String(detail);
738
+ } catch {
739
+ message = "program threw an unrenderable value";
740
+ }
741
+ return prepareFailure("exception", message, remainingOutputBytes, maxOutputBytes);
742
+ }
743
+ /**
744
+ * Materialize the real error constructor declared by one namespace.
745
+ * @param descriptor - program-global class name and member-name property.
746
+ * @returns the constructor injected into the program and used for rejections.
747
+ */
748
+ function makeBindingErrorClass(descriptor) {
749
+ return class BindingCallError extends CapturedError {
750
+ constructor(memberName, message) {
751
+ super(message);
752
+ defineBindingErrorField(this, "name", descriptor.name);
753
+ defineBindingErrorField(this, descriptor.memberNameProperty, memberName);
754
+ }
755
+ };
756
+ }
757
+ /** Create the namespace-specific rejection for one failed binding call. */
758
+ function bindingFailure(errorClass, memberName, message) {
759
+ return errorClass ? new errorClass(memberName, message) : new CapturedError(message);
760
+ }
761
+ /**
762
+ * Build each declared error class once so calls and `instanceof` share constructor identity.
763
+ * @param data - binding namespace declarations from the boot payload.
764
+ * @returns constructors keyed by their owning namespace global.
765
+ */
766
+ function makeBindingErrorClasses(data) {
767
+ const classes = /* @__PURE__ */ new Map();
768
+ for (const namespace of data.namespaces) if (namespace.errorClass) classes.set(namespace.global, makeBindingErrorClass(namespace.errorClass));
769
+ return classes;
770
+ }
771
+ /**
772
+ * Route host replies into the pending-call map: each reply settles its call
773
+ * at most once, and a reply for an unknown id (stray, or a duplicate answer
774
+ * to an id already settled) is ignored. Shared wiring between
775
+ * {@link runWorkerMain} and the tests that exercise {@link makeNamespaces}
776
+ * standalone.
777
+ * @param port - the port whose `message` events carry the replies.
778
+ * @param pending - the id-keyed map of unsettled binding calls.
779
+ */
780
+ function wireReplies(port, pending) {
781
+ port.on("message", (message) => {
782
+ const entry = pending.get(message.id);
783
+ if (!entry) return;
784
+ pending.delete(message.id);
785
+ if (message.ok) {
786
+ const value = decodeWorkerJson(message.value);
787
+ if (value === void 0) entry.reject(new CapturedError("binding resolution must be lossless JSON"));
788
+ else entry.resolve(value);
789
+ } else entry.reject(new CapturedError(message.message));
790
+ });
791
+ }
792
+ /**
793
+ * Build the binding namespace objects the program sees: one null-prototype global per
794
+ * namespace, each declared name an own enumerable async function that bridges over the port
795
+ * (`__proto__`/`constructor`/`toString` are ordinary keys, never prototype collisions).
796
+ * Lossy arguments reject before posting; clone failures and host failure
797
+ * replies reject only the corresponding call.
798
+ *
799
+ * @param data - the boot payload's namespace declarations (globals + names).
800
+ * @param port - the port binding calls are posted to.
801
+ * @param pending - the id-keyed map each posted call parks its handles in.
802
+ * @param nextId - the shared mutable id counter (worker-issued correlation ids).
803
+ * @param errorClasses - per-namespace constructors shared with program globals.
804
+ * @returns one namespace object per declaration, in declaration order.
805
+ */
806
+ function makeNamespaces(data, port, pending, nextId, errorClasses = makeBindingErrorClasses(data)) {
807
+ return data.namespaces.map(({ global, names }) => {
808
+ const errorClass = errorClasses.get(global);
809
+ const namespace = Object.create(null);
810
+ for (const name of names) Object.defineProperty(namespace, name, {
811
+ enumerable: true,
812
+ value: (args) => {
813
+ let detached;
814
+ try {
815
+ detached = snapshotCodeJsonValue(args);
816
+ } catch {
817
+ detached = void 0;
818
+ }
819
+ if (detached === void 0) return Promise.reject(bindingFailure(errorClass, name, "binding arguments must be lossless JSON"));
820
+ return new Promise((resolve, reject) => {
821
+ const id = nextId.value++;
822
+ pending.set(id, {
823
+ resolve,
824
+ reject: (error) => {
825
+ reject(bindingFailure(errorClass, name, error.message));
826
+ }
827
+ });
828
+ try {
829
+ port.postMessage({
830
+ type: "call",
831
+ id,
832
+ global,
833
+ name,
834
+ args: encodeWorkerJson(detached)
835
+ });
836
+ } catch (error) {
837
+ pending.delete(id);
838
+ reject(bindingFailure(errorClass, name, `binding arguments must be structured-cloneable: ${error instanceof CapturedError ? error.message : String(error)}`));
839
+ }
840
+ });
841
+ }
842
+ });
843
+ return namespace;
844
+ });
845
+ }
846
+ /**
847
+ * Run one strict async-function body, allowing top-level `await` and `return`, and post exactly
848
+ * one terminal {@link DoneMessage}; a thrown program error becomes its `error` field.
849
+ * @param port - host message port or test double.
850
+ * @param data - the boot payload the host sent.
851
+ * @param streams - stdout/stderr objects captured as program logs.
852
+ * @returns after posting the done message.
853
+ */
854
+ async function runWorkerMain(port, data, streams) {
855
+ const logs = new LogBuffer(data.maxOutputBytes, (text) => {
856
+ port.postMessage({
857
+ type: "log",
858
+ text
859
+ });
860
+ }, () => {
861
+ port.postMessage({ type: "output-limit" });
862
+ });
863
+ captureStreamWrites(logs, streams.stdout);
864
+ captureStreamWrites(logs, streams.stderr);
865
+ const pending = /* @__PURE__ */ new Map();
866
+ wireReplies(port, pending);
867
+ const nextId = { value: 1 };
868
+ const errorClasses = makeBindingErrorClasses(data);
869
+ const namespaces = makeNamespaces(data, port, pending, nextId, errorClasses);
870
+ const errorClassParameters = [];
871
+ const errorClassValues = [];
872
+ for (const namespace of data.namespaces) {
873
+ if (!namespace.errorClass) continue;
874
+ errorClassParameters.push(namespace.errorClass.name);
875
+ const errorClass = errorClasses.get(namespace.global);
876
+ /* v8 ignore next -- makeBindingErrorClasses covers every declaration in the same data. */
877
+ if (!errorClass) throw new CapturedError(`missing binding error class for ${namespace.global}`);
878
+ errorClassValues.push(errorClass);
879
+ }
880
+ const consoleShim = makeConsoleShim(logs);
881
+ let done;
882
+ try {
883
+ /* v8 ignore next -- the arrow exists only to reach the AsyncFunction constructor; it is never invoked. */
884
+ const AsyncFunction = (async () => {}).constructor;
885
+ done = {
886
+ type: "done",
887
+ ...prepareCompletion(await new AsyncFunction(...data.namespaces.map((namespace) => namespace.global), ...errorClassParameters, "console", `'use strict';\n${data.code}`)(...namespaces, ...errorClassValues, consoleShim), logs.remainingOutputBytes(), data.maxOutputBytes)
888
+ };
889
+ } catch (error) {
890
+ done = {
891
+ type: "done",
892
+ ...prepareException(error, logs.remainingOutputBytes(), data.maxOutputBytes)
893
+ };
894
+ }
895
+ port.postMessage(done);
896
+ }
897
+ //#endregion
898
+ //#region lib/types/worker.js
899
+ /**
900
+ * Spawn-only worker entrypoint over {@link runWorkerMain}. Executable logic stays in
901
+ * `bootstrap.ts` for in-process coverage; real-worker tests cover this glue.
902
+ * @module @hydraharness/harness-code-runtime-worker-thread/src/worker
903
+ */
904
+ if (!node_worker_threads.parentPort) throw new Error("hydra-code-runtime-worker-thread: worker entry loaded outside a worker thread");
905
+ runWorkerMain(node_worker_threads.parentPort, node_worker_threads.workerData, {
906
+ stdout: process.stdout,
907
+ stderr: process.stderr
908
+ });
909
+ //#endregion