@hraness/direct 0.7.5

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +436 -0
  3. package/dist/core/index.js +162 -0
  4. package/dist/index-1csg00w4.js +1167 -0
  5. package/dist/index-6mdfd2ey.js +464 -0
  6. package/dist/index-7n1h75n6.js +616 -0
  7. package/dist/index.js +232 -0
  8. package/dist/react.js +32 -0
  9. package/dist/testing/index.js +1069 -0
  10. package/dist/tooling/bombadil.js +2117 -0
  11. package/dist/tooling/browser-verification-entry.js +1499 -0
  12. package/dist/tooling/bundle-boundary.js +119 -0
  13. package/dist/web.js +605 -0
  14. package/package.json +179 -0
  15. package/skills/direct/AGENTS.md +13 -0
  16. package/skills/direct/SKILL.md +49 -0
  17. package/skills/direct/agents/openai.yaml +4 -0
  18. package/skills/direct/references/adoption.md +131 -0
  19. package/skills/direct/references/install.md +91 -0
  20. package/skills/direct/references/verification.md +247 -0
  21. package/src/core/coverage.ts +336 -0
  22. package/src/core/definition.ts +378 -0
  23. package/src/core/effects.ts +88 -0
  24. package/src/core/fixture.ts +185 -0
  25. package/src/core/ids.ts +77 -0
  26. package/src/core/index.ts +13 -0
  27. package/src/core/json-value.ts +7 -0
  28. package/src/core/json.ts +593 -0
  29. package/src/core/query.ts +230 -0
  30. package/src/core/reason.ts +16 -0
  31. package/src/core/resource.ts +10 -0
  32. package/src/core/result.ts +19 -0
  33. package/src/core/runtime.ts +229 -0
  34. package/src/core/scenario.ts +149 -0
  35. package/src/core/store.ts +784 -0
  36. package/src/index.ts +51 -0
  37. package/src/react.ts +54 -0
  38. package/src/testing/activity.ts +228 -0
  39. package/src/testing/coverage-binding.ts +99 -0
  40. package/src/testing/evidence.ts +59 -0
  41. package/src/testing/index.ts +22 -0
  42. package/src/testing/manifest.ts +559 -0
  43. package/src/testing/probe.ts +446 -0
  44. package/src/testing/scripted-transport.ts +775 -0
  45. package/src/testing/session.ts +525 -0
  46. package/src/tooling/bombadil-campaign.ts +288 -0
  47. package/src/tooling/bombadil-internal.d.ts +46 -0
  48. package/src/tooling/bombadil-runner.ts +1424 -0
  49. package/src/tooling/bombadil.ts +27 -0
  50. package/src/tooling/browser-verification-entry.ts +32 -0
  51. package/src/tooling/browser-verification.ts +916 -0
  52. package/src/tooling/bundle-boundary.ts +159 -0
  53. package/src/web/browser-bridge.ts +296 -0
  54. package/src/web/browser.ts +277 -0
  55. package/src/web/fetch-firewall.ts +251 -0
  56. package/src/web.ts +27 -0
@@ -0,0 +1,1069 @@
1
+ import {
2
+ createDirectStore
3
+ } from "../index-6mdfd2ey.js";
4
+ import {
5
+ DIRECT_CATALOG_HASH_ALGORITHM,
6
+ DIRECT_PROBE_SCHEMA,
7
+ DIRECT_SESSION_MANIFEST_SCHEMA,
8
+ MAX_DIRECT_PROBE_COUNTERS,
9
+ createDirectProbe,
10
+ createDirectSessionManifest,
11
+ parseDirectProbeSnapshot,
12
+ parseDirectSessionManifest
13
+ } from "../index-7n1h75n6.js";
14
+ import {
15
+ canonicalJson,
16
+ createCoverageCatalogSnapshot,
17
+ createLogicalRuntime,
18
+ err,
19
+ isRecord,
20
+ ok,
21
+ parseAndCloneWorld,
22
+ parseCoverageCatalogSnapshot,
23
+ parseJsonValue,
24
+ parseLogicalRuntimeSnapshot,
25
+ parseTaggedStableHash,
26
+ renderUnknownReason
27
+ } from "../index-1csg00w4.js";
28
+
29
+ // src/testing/activity.ts
30
+ function storeErrorMessage(cause) {
31
+ return renderUnknownReason(cause, "Direct store operation failed");
32
+ }
33
+ function operationError(reason) {
34
+ return Object.freeze({
35
+ code: "operation-id-failed",
36
+ message: renderUnknownReason(reason),
37
+ operation: null,
38
+ storeError: null,
39
+ reason
40
+ });
41
+ }
42
+ function closedScopeError() {
43
+ return Object.freeze({
44
+ code: "scope-closed",
45
+ message: "The Direct activity scope is closed",
46
+ operation: null,
47
+ storeError: null,
48
+ reason: null
49
+ });
50
+ }
51
+ function storeError(code, operation, cause) {
52
+ return Object.freeze({
53
+ code,
54
+ message: storeErrorMessage(cause),
55
+ operation,
56
+ storeError: cause,
57
+ reason: null
58
+ });
59
+ }
60
+ function createDirectActivityScope(store, runtime, options = {}) {
61
+ const signal = options.signal;
62
+ const isClosed = () => signal?.aborted === true;
63
+ const begin = (namespace = "activity") => {
64
+ if (isClosed())
65
+ return err(closedScopeError());
66
+ let operation;
67
+ try {
68
+ operation = runtime.nextOperationId(namespace);
69
+ } catch (reason) {
70
+ return err(operationError(reason));
71
+ }
72
+ if (isClosed())
73
+ return err(closedScopeError());
74
+ const currentGeneration = store.getSnapshot().generation;
75
+ const started = store.beginActivity(currentGeneration, operation);
76
+ if (!started.ok) {
77
+ return err(storeError("store-begin-failed", operation, started.error));
78
+ }
79
+ if (isClosed()) {
80
+ const settled = started.value.settle();
81
+ return settled.ok ? err(closedScopeError()) : err(storeError("store-settle-failed", operation, settled.error));
82
+ }
83
+ let released = false;
84
+ let releaseResult = null;
85
+ const lease = Object.freeze({
86
+ generation: currentGeneration,
87
+ operation,
88
+ isReleased: () => released,
89
+ release: () => {
90
+ if (releaseResult !== null)
91
+ return releaseResult;
92
+ released = true;
93
+ const settled = started.value.settle();
94
+ releaseResult = settled.ok ? ok(true) : err(storeError("store-settle-failed", operation, settled.error));
95
+ return releaseResult;
96
+ }
97
+ });
98
+ return ok(lease);
99
+ };
100
+ const run = async (namespace, work) => {
101
+ const started = begin(namespace);
102
+ if (!started.ok) {
103
+ return err(Object.freeze({
104
+ code: "begin-failed",
105
+ message: started.error.message,
106
+ operation: null,
107
+ workError: null,
108
+ activityError: started.error
109
+ }));
110
+ }
111
+ let workResult;
112
+ try {
113
+ workResult = ok(await work());
114
+ } catch (reason) {
115
+ workResult = err(reason);
116
+ }
117
+ const released = started.value.release();
118
+ if (workResult.ok && released.ok)
119
+ return ok(workResult.value);
120
+ if (!workResult.ok && released.ok) {
121
+ return err(Object.freeze({
122
+ code: "work-failed",
123
+ message: renderUnknownReason(workResult.error),
124
+ operation: started.value.operation,
125
+ workError: workResult.error,
126
+ activityError: null
127
+ }));
128
+ }
129
+ if (workResult.ok && !released.ok) {
130
+ return err(Object.freeze({
131
+ code: "settlement-failed",
132
+ message: released.error.message,
133
+ operation: started.value.operation,
134
+ workError: null,
135
+ activityError: released.error
136
+ }));
137
+ }
138
+ if (!workResult.ok && !released.ok) {
139
+ return err(Object.freeze({
140
+ code: "work-and-settlement-failed",
141
+ message: `${renderUnknownReason(workResult.error)}; settlement failed: ${released.error.message}`,
142
+ operation: started.value.operation,
143
+ workError: workResult.error,
144
+ activityError: released.error
145
+ }));
146
+ }
147
+ throw new Error("Unreachable activity result");
148
+ };
149
+ return Object.freeze({ begin, run });
150
+ }
151
+ // src/testing/coverage-binding.ts
152
+ function sameEntry(actual, expected) {
153
+ if (actual.key !== expected.key || actual.mode !== expected.mode || actual.claim !== expected.claim || actual.scenarios.length !== expected.scenarios.length)
154
+ return false;
155
+ return actual.scenarios.every((scenario, index) => scenario === expected.scenarios[index]);
156
+ }
157
+ function parseExpectedCoverageCatalogSnapshot(input, expected) {
158
+ const parsed = parseCoverageCatalogSnapshot(input);
159
+ if (!parsed.ok) {
160
+ return err(Object.freeze({
161
+ code: "invalid-coverage",
162
+ message: parsed.error.message,
163
+ coverageError: parsed.error
164
+ }));
165
+ }
166
+ try {
167
+ if (parsed.value.schema !== expected.schema || parsed.value.entries.length !== expected.entries.length || parsed.value.entries.some((entry, index) => {
168
+ const expectedEntry = expected.entries[index];
169
+ return expectedEntry === undefined || !sameEntry(entry, expectedEntry);
170
+ })) {
171
+ return err(Object.freeze({
172
+ code: "coverage-mismatch",
173
+ message: "Published Direct coverage does not exactly match the authored definition",
174
+ coverageError: null
175
+ }));
176
+ }
177
+ } catch (reason) {
178
+ return err(Object.freeze({
179
+ code: "invalid-definition",
180
+ message: renderUnknownReason(reason, "Expected Direct coverage could not be inspected"),
181
+ coverageError: null
182
+ }));
183
+ }
184
+ return ok(expected);
185
+ }
186
+ function parseDefinitionCoverageSnapshot(input, definition) {
187
+ let expected;
188
+ try {
189
+ expected = createCoverageCatalogSnapshot(definition.coverage);
190
+ } catch (reason) {
191
+ return err(Object.freeze({
192
+ code: "invalid-definition",
193
+ message: renderUnknownReason(reason, "Direct definition coverage could not be inspected"),
194
+ coverageError: null
195
+ }));
196
+ }
197
+ return parseExpectedCoverageCatalogSnapshot(input, expected);
198
+ }
199
+ // src/testing/evidence.ts
200
+ function classifyCoverageEvidence(entry, facts) {
201
+ const directVerified = facts.directEvidence === "verified";
202
+ if (entry.mode === "direct") {
203
+ return directVerified ? "verified" : "direct-required";
204
+ }
205
+ let exercised = 0;
206
+ for (const scenario of entry.scenarios) {
207
+ if (facts.exercisedScenarios.has(scenario))
208
+ exercised += 1;
209
+ }
210
+ if (exercised === 0) {
211
+ return directVerified && entry.mode === "mixed" ? "partial" : "not-exercised";
212
+ }
213
+ if (exercised < entry.scenarios.length) {
214
+ return "partial";
215
+ }
216
+ if (entry.mode === "fixture") {
217
+ return "verified";
218
+ }
219
+ return directVerified ? "verified" : "fixture-verified";
220
+ }
221
+ // src/testing/session.ts
222
+ function frozenMessages(messages) {
223
+ return Object.freeze([...messages]);
224
+ }
225
+ function isPromiseLike(value) {
226
+ return (typeof value === "object" && value !== null || typeof value === "function") && typeof Reflect.get(value, "then") === "function";
227
+ }
228
+ function freezeCounterSources(sources) {
229
+ return Object.freeze([...sources]);
230
+ }
231
+ function prepareSessionObservation(input) {
232
+ const candidate = input;
233
+ if (!isRecord(candidate)) {
234
+ throw new Error("Direct observation must be an object");
235
+ }
236
+ const pending = input.pending;
237
+ const violations = input.violations;
238
+ const readRemainingWork = input.readRemainingWork;
239
+ if (pending !== undefined && !Array.isArray(pending)) {
240
+ throw new Error("Direct observation pending counters must be an array");
241
+ }
242
+ if (violations !== undefined && !Array.isArray(violations)) {
243
+ throw new Error("Direct observation violation counters must be an array");
244
+ }
245
+ if (readRemainingWork !== undefined && typeof readRemainingWork !== "function") {
246
+ throw new Error("Direct observation remaining-work reader must be a function");
247
+ }
248
+ return Object.freeze({
249
+ ...pending === undefined ? {} : { pending: freezeCounterSources(pending) },
250
+ ...violations === undefined ? {} : { violations: freezeCounterSources(violations) },
251
+ ...readRemainingWork === undefined ? {} : { readRemainingWork }
252
+ });
253
+ }
254
+ function sessionError(error, cleanupErrors = []) {
255
+ const failures = frozenMessages(cleanupErrors);
256
+ switch (error.code) {
257
+ case "invalid-options":
258
+ case "activation-failed":
259
+ case "store-failed":
260
+ case "harness-failed":
261
+ case "observation-failed":
262
+ case "probe-failed":
263
+ return Object.freeze({ ...error, cleanupErrors: failures });
264
+ }
265
+ }
266
+ function runCleanup(controller, cleanups) {
267
+ const failures = [];
268
+ try {
269
+ controller.abort();
270
+ } catch (reason) {
271
+ failures.push(`Abort failed: ${renderUnknownReason(reason)}`);
272
+ }
273
+ for (const cleanup of [...cleanups].reverse()) {
274
+ try {
275
+ const returned = cleanup();
276
+ if (returned !== undefined) {
277
+ if (isPromiseLike(returned)) {
278
+ Promise.resolve(returned).catch(() => {
279
+ return;
280
+ });
281
+ }
282
+ failures.push("Direct cleanup must complete synchronously and return undefined");
283
+ }
284
+ } catch (reason) {
285
+ failures.push(renderUnknownReason(reason, "Direct cleanup failed"));
286
+ }
287
+ }
288
+ return frozenMessages(failures);
289
+ }
290
+ function activateSession(definition, activation) {
291
+ switch (activation.kind) {
292
+ case "query":
293
+ return definition.activate(activation.source);
294
+ case "scenario":
295
+ return definition.activateScenario(activation.scenario);
296
+ }
297
+ }
298
+ function createDirectSession(options) {
299
+ let definition;
300
+ let requestedActivation;
301
+ let createHarness;
302
+ let observeHarness;
303
+ let parseWorld;
304
+ let sleep;
305
+ let storeOptions;
306
+ try {
307
+ definition = options.definition;
308
+ parseWorld = definition.parseWorld;
309
+ const activationInput = options.activation;
310
+ if (activationInput.kind === "query") {
311
+ requestedActivation = Object.freeze({ kind: "query", source: activationInput.source });
312
+ } else if (activationInput.kind === "scenario") {
313
+ requestedActivation = Object.freeze({ kind: "scenario", scenario: activationInput.scenario });
314
+ } else {
315
+ throw new Error("Direct session activation kind must be query or scenario");
316
+ }
317
+ createHarness = options.create;
318
+ observeHarness = options.observe;
319
+ sleep = options.sleep;
320
+ const storeOptionsInput = options.storeOptions;
321
+ if (storeOptionsInput === undefined) {
322
+ storeOptions = undefined;
323
+ } else {
324
+ const onListenerError = storeOptionsInput.onListenerError;
325
+ const validateReplacements = storeOptionsInput.validateReplacements;
326
+ storeOptions = Object.freeze({
327
+ ...onListenerError === undefined ? {} : { onListenerError },
328
+ ...validateReplacements === undefined ? {} : { validateReplacements }
329
+ });
330
+ }
331
+ } catch (reason) {
332
+ return err(sessionError({
333
+ code: "invalid-options",
334
+ message: renderUnknownReason(reason, "Direct session options could not be inspected"),
335
+ queryError: null,
336
+ storeError: null,
337
+ probeError: null
338
+ }));
339
+ }
340
+ let activationSource;
341
+ let activationScenario;
342
+ let activationRoute;
343
+ let activationWorld;
344
+ let activationRuntime;
345
+ let activationHash;
346
+ try {
347
+ const activated = activateSession(definition, requestedActivation);
348
+ if (!activated.ok) {
349
+ const queryError = Object.freeze({
350
+ code: activated.error.code,
351
+ message: activated.error.message
352
+ });
353
+ return err(sessionError({
354
+ code: "activation-failed",
355
+ message: queryError.message,
356
+ queryError,
357
+ storeError: null,
358
+ probeError: null
359
+ }));
360
+ }
361
+ const candidate = activated.value;
362
+ if (candidate.kind !== "active")
363
+ throw new Error("Direct activation kind must be active");
364
+ if (candidate.source !== "scenario" && candidate.source !== "fixture") {
365
+ throw new Error("Direct activation source must be scenario or fixture");
366
+ }
367
+ if (typeof candidate.scenario !== "string") {
368
+ throw new Error("Direct activation scenario must be a string");
369
+ }
370
+ if (typeof candidate.route !== "string") {
371
+ throw new Error("Direct activation route must be a string");
372
+ }
373
+ const parsedActivationHash = parseTaggedStableHash(candidate.activationHash);
374
+ if (!parsedActivationHash.ok)
375
+ throw new Error(parsedActivationHash.error.message);
376
+ const parsedRuntime = parseLogicalRuntimeSnapshot(candidate.runtime);
377
+ if (!parsedRuntime.ok)
378
+ throw new Error(parsedRuntime.error.message);
379
+ activationSource = candidate.source;
380
+ activationScenario = candidate.scenario;
381
+ activationRoute = candidate.route;
382
+ activationWorld = candidate.world;
383
+ activationRuntime = parsedRuntime.value;
384
+ activationHash = parsedActivationHash.value;
385
+ } catch (reason) {
386
+ return err(sessionError({
387
+ code: "invalid-options",
388
+ message: renderUnknownReason(reason, "Direct session activation failed unexpectedly"),
389
+ queryError: null,
390
+ storeError: null,
391
+ probeError: null
392
+ }));
393
+ }
394
+ const store = storeOptions === undefined ? createDirectStore(activationWorld, parseWorld) : createDirectStore(activationWorld, parseWorld, storeOptions);
395
+ if (!store.ok) {
396
+ return err(sessionError({
397
+ code: "store-failed",
398
+ message: store.error.message,
399
+ queryError: null,
400
+ storeError: store.error,
401
+ probeError: null
402
+ }));
403
+ }
404
+ const clock = sleep === undefined ? createLogicalRuntime(activationRuntime) : createLogicalRuntime(activationRuntime, sleep);
405
+ const activation = Object.freeze({
406
+ kind: "active",
407
+ source: activationSource,
408
+ scenario: activationScenario,
409
+ route: activationRoute,
410
+ world: store.value.getSnapshot().world,
411
+ runtime: clock.snapshot(),
412
+ activationHash
413
+ });
414
+ const createdManifest = createDirectSessionManifest(definition, activation);
415
+ if (!createdManifest.ok) {
416
+ return err(sessionError({
417
+ code: "invalid-options",
418
+ message: createdManifest.error.message,
419
+ queryError: null,
420
+ storeError: null,
421
+ probeError: null
422
+ }));
423
+ }
424
+ const manifest = createdManifest.value;
425
+ const controller = new AbortController;
426
+ const activity = createDirectActivityScope(store.value, clock, { signal: controller.signal });
427
+ const cleanups = [];
428
+ let registrationOpen = true;
429
+ const context = Object.freeze({
430
+ activation,
431
+ world: activation.world,
432
+ store: store.value,
433
+ clock,
434
+ activity,
435
+ signal: controller.signal,
436
+ onDispose: (cleanup) => {
437
+ if (!registrationOpen) {
438
+ throw new Error("Direct cleanup must be registered during synchronous session construction");
439
+ }
440
+ cleanups.push(cleanup);
441
+ return;
442
+ }
443
+ });
444
+ let harness;
445
+ try {
446
+ harness = createHarness(context);
447
+ if (isPromiseLike(harness)) {
448
+ Promise.resolve(harness).catch(() => {
449
+ return;
450
+ });
451
+ throw new Error("Direct harness construction must complete synchronously");
452
+ }
453
+ } catch (reason) {
454
+ registrationOpen = false;
455
+ const cleanupErrors = runCleanup(controller, cleanups);
456
+ return err(sessionError({
457
+ code: "harness-failed",
458
+ message: renderUnknownReason(reason, "Direct harness construction failed"),
459
+ queryError: null,
460
+ storeError: null,
461
+ probeError: null
462
+ }, cleanupErrors));
463
+ }
464
+ let observation;
465
+ try {
466
+ const observed = observeHarness === undefined ? Object.freeze({}) : observeHarness(harness, context);
467
+ if (isPromiseLike(observed)) {
468
+ Promise.resolve(observed).catch(() => {
469
+ return;
470
+ });
471
+ throw new Error("Direct observation construction must complete synchronously");
472
+ }
473
+ observation = prepareSessionObservation(observed);
474
+ } catch (reason) {
475
+ registrationOpen = false;
476
+ const cleanupErrors = runCleanup(controller, cleanups);
477
+ return err(sessionError({
478
+ code: "observation-failed",
479
+ message: renderUnknownReason(reason, "Direct observation construction failed"),
480
+ queryError: null,
481
+ storeError: null,
482
+ probeError: null
483
+ }, cleanupErrors));
484
+ }
485
+ registrationOpen = false;
486
+ const probe = createDirectProbe({
487
+ store: store.value,
488
+ activationHash: activation.activationHash,
489
+ ...observation.pending === undefined ? {} : { pending: observation.pending },
490
+ ...observation.violations === undefined ? {} : { violations: observation.violations },
491
+ ...observation.readRemainingWork === undefined ? {} : { readRemainingWork: observation.readRemainingWork }
492
+ });
493
+ if (!probe.ok) {
494
+ const cleanupErrors = runCleanup(controller, cleanups);
495
+ return err(sessionError({
496
+ code: "probe-failed",
497
+ message: probe.error.message,
498
+ queryError: null,
499
+ storeError: null,
500
+ probeError: probe.error
501
+ }, cleanupErrors));
502
+ }
503
+ let disposed = false;
504
+ let disposalErrors = Object.freeze([]);
505
+ const onDispose = (cleanup) => {
506
+ if (typeof cleanup !== "function") {
507
+ return err(Object.freeze({
508
+ code: "invalid-cleanup",
509
+ message: "Direct cleanup must be a function"
510
+ }));
511
+ }
512
+ if (disposed) {
513
+ return err(Object.freeze({
514
+ code: "session-disposed",
515
+ message: "Cannot register cleanup on a disposed Direct session"
516
+ }));
517
+ }
518
+ cleanups.push(cleanup);
519
+ return ok(true);
520
+ };
521
+ const dispose = () => {
522
+ if (disposed)
523
+ return;
524
+ disposed = true;
525
+ disposalErrors = runCleanup(controller, cleanups);
526
+ return;
527
+ };
528
+ return ok(Object.freeze({
529
+ activation,
530
+ world: activation.world,
531
+ store: store.value,
532
+ clock,
533
+ activity,
534
+ harness,
535
+ probe: probe.value,
536
+ manifest,
537
+ coverage: manifest.coverage,
538
+ signal: controller.signal,
539
+ onDispose,
540
+ dispose,
541
+ isDisposed: () => disposed,
542
+ disposalErrors: () => disposalErrors
543
+ }));
544
+ }
545
+ // src/testing/scripted-transport.ts
546
+ var DEFAULT_SCRIPTED_TRANSPORT_LIMITS = Object.freeze({
547
+ maxSteps: 1e4,
548
+ maxEventsPerStep: 1000,
549
+ maxRecordedInternalErrors: 32,
550
+ maxLogicalDelayPerStepMs: 60000,
551
+ maxTotalLogicalDelayMs: 3600000
552
+ });
553
+ var STEP_KEYS = new Set(["request", "outcome", "delayMs", "eventsBefore", "eventsAfter"]);
554
+ var OUTCOME_KEYS = new Set(["kind", "value", "error"]);
555
+ var LIMIT_KEYS = new Set([
556
+ "maxSteps",
557
+ "maxEventsPerStep",
558
+ "maxRecordedInternalErrors",
559
+ "maxLogicalDelayPerStepMs",
560
+ "maxTotalLogicalDelayMs"
561
+ ]);
562
+ function definitionError(code, message, step = null) {
563
+ return Object.freeze({ code, message, step });
564
+ }
565
+ function validateLimits(limits) {
566
+ for (const key of Object.keys(limits)) {
567
+ if (!LIMIT_KEYS.has(key)) {
568
+ return err(definitionError("invalid-limits", `Unknown scripted transport limit: ${key}`));
569
+ }
570
+ }
571
+ for (const [name, value] of [
572
+ ["maxSteps", limits.maxSteps],
573
+ ["maxEventsPerStep", limits.maxEventsPerStep],
574
+ ["maxRecordedInternalErrors", limits.maxRecordedInternalErrors]
575
+ ]) {
576
+ if (!Number.isSafeInteger(value) || value < 1 || value > 1e5) {
577
+ return err(definitionError("invalid-limits", `${name} must be a positive safe integer no greater than 100000`));
578
+ }
579
+ }
580
+ for (const [name, value] of [
581
+ ["maxLogicalDelayPerStepMs", limits.maxLogicalDelayPerStepMs],
582
+ ["maxTotalLogicalDelayMs", limits.maxTotalLogicalDelayMs]
583
+ ]) {
584
+ if (!Number.isSafeInteger(value) || value < 0) {
585
+ return err(definitionError("invalid-limits", `${name} must be a non-negative safe integer`));
586
+ }
587
+ }
588
+ return ok(Object.freeze({ ...limits }));
589
+ }
590
+ function parseOwned(input, parser, label, step) {
591
+ const parsed = parseAndCloneWorld(input, parser);
592
+ return parsed.ok ? parsed : err(definitionError("invalid-step", `${label}: ${parsed.error.message}`, step));
593
+ }
594
+ function parseEvents(input, parser, label, step) {
595
+ if (!Array.isArray(input)) {
596
+ return err(definitionError("invalid-step", `${label} must be an array`, step));
597
+ }
598
+ const events = [];
599
+ for (const candidate of input) {
600
+ const parsed = parseOwned(candidate, parser, label, step);
601
+ if (!parsed.ok)
602
+ return parsed;
603
+ events.push(parsed.value);
604
+ }
605
+ return ok(Object.freeze(events));
606
+ }
607
+ function parseSteps(options, limits) {
608
+ const json = parseJsonValue(options.steps);
609
+ if (!json.ok || !Array.isArray(json.value)) {
610
+ return err(definitionError("invalid-script", json.ok ? "Transport steps must be an array" : json.error.message));
611
+ }
612
+ if (json.value.length > limits.maxSteps) {
613
+ return err(definitionError("script-too-large", `Transport script exceeds ${String(limits.maxSteps)} steps`));
614
+ }
615
+ const steps = [];
616
+ let totalLogicalDelayMs = 0;
617
+ for (const [index, candidate] of json.value.entries()) {
618
+ if (!isRecord(candidate)) {
619
+ return err(definitionError("invalid-step", "Transport steps must be objects", index));
620
+ }
621
+ for (const key of Object.keys(candidate)) {
622
+ if (!STEP_KEYS.has(key)) {
623
+ return err(definitionError("invalid-step", `Unknown transport step key: ${key}`, index));
624
+ }
625
+ }
626
+ if (!("request" in candidate) || !("outcome" in candidate)) {
627
+ return err(definitionError("invalid-step", "Transport steps require request and outcome", index));
628
+ }
629
+ const request = parseOwned(candidate.request, options.parseRequest, "Invalid scripted request", index);
630
+ if (!request.ok)
631
+ return request;
632
+ const requestCanonical = canonicalJson(request.value);
633
+ if (!requestCanonical.ok) {
634
+ return err(definitionError("invalid-step", requestCanonical.error.message, index));
635
+ }
636
+ if (!isRecord(candidate.outcome)) {
637
+ return err(definitionError("invalid-step", "Transport outcome must be an object", index));
638
+ }
639
+ for (const key of Object.keys(candidate.outcome)) {
640
+ if (!OUTCOME_KEYS.has(key)) {
641
+ return err(definitionError("invalid-step", `Unknown transport outcome key: ${key}`, index));
642
+ }
643
+ }
644
+ let outcome;
645
+ if (candidate.outcome.kind === "response") {
646
+ if (!("value" in candidate.outcome) || "error" in candidate.outcome) {
647
+ return err(definitionError("invalid-step", "A response outcome requires only value", index));
648
+ }
649
+ const response = parseOwned(candidate.outcome.value, options.parseResponse, "Invalid scripted response", index);
650
+ if (!response.ok)
651
+ return response;
652
+ outcome = Object.freeze({ kind: "response", value: response.value });
653
+ } else if (candidate.outcome.kind === "failure") {
654
+ if (!("error" in candidate.outcome) || "value" in candidate.outcome) {
655
+ return err(definitionError("invalid-step", "A failure outcome requires only error", index));
656
+ }
657
+ const failure = parseOwned(candidate.outcome.error, options.parseFailure, "Invalid scripted failure", index);
658
+ if (!failure.ok)
659
+ return failure;
660
+ outcome = Object.freeze({ kind: "failure", error: failure.value });
661
+ } else {
662
+ return err(definitionError("invalid-step", "Transport outcome kind must be response or failure", index));
663
+ }
664
+ const delayMs = "delayMs" in candidate ? candidate.delayMs : 0;
665
+ if (typeof delayMs !== "number" || !Number.isSafeInteger(delayMs) || delayMs < 0) {
666
+ return err(definitionError("invalid-step", "Transport delayMs must be a non-negative safe integer", index));
667
+ }
668
+ if (delayMs > limits.maxLogicalDelayPerStepMs) {
669
+ return err(definitionError("script-too-large", `Transport delayMs exceeds the per-step limit of ${String(limits.maxLogicalDelayPerStepMs)}`, index));
670
+ }
671
+ if (totalLogicalDelayMs > limits.maxTotalLogicalDelayMs - delayMs) {
672
+ return err(definitionError("script-too-large", `Transport script exceeds the total logical delay limit of ${String(limits.maxTotalLogicalDelayMs)}`, index));
673
+ }
674
+ totalLogicalDelayMs += delayMs;
675
+ const eventsBeforeInput = "eventsBefore" in candidate ? candidate.eventsBefore : [];
676
+ const eventsAfterInput = "eventsAfter" in candidate ? candidate.eventsAfter : [];
677
+ if (!Array.isArray(eventsBeforeInput) || !Array.isArray(eventsAfterInput)) {
678
+ return err(definitionError("invalid-step", "eventsBefore and eventsAfter must be arrays", index));
679
+ }
680
+ if (eventsBeforeInput.length > limits.maxEventsPerStep || eventsAfterInput.length > limits.maxEventsPerStep - eventsBeforeInput.length) {
681
+ return err(definitionError("script-too-large", `Transport step exceeds the combined event limit of ${String(limits.maxEventsPerStep)}`, index));
682
+ }
683
+ const eventsBefore = parseEvents(eventsBeforeInput, options.parseEvent, "eventsBefore", index);
684
+ if (!eventsBefore.ok)
685
+ return eventsBefore;
686
+ const eventsAfter = parseEvents(eventsAfterInput, options.parseEvent, "eventsAfter", index);
687
+ if (!eventsAfter.ok)
688
+ return eventsAfter;
689
+ steps.push(Object.freeze({
690
+ request: request.value,
691
+ requestCanonical: requestCanonical.value,
692
+ outcome,
693
+ delayMs,
694
+ eventsBefore: eventsBefore.value,
695
+ eventsAfter: eventsAfter.value
696
+ }));
697
+ }
698
+ return ok(Object.freeze(steps));
699
+ }
700
+ function transportError(code, message, step, expectedRequest, actualRequest, remainingSteps, pendingDeliveries) {
701
+ return Object.freeze({
702
+ code,
703
+ message,
704
+ step,
705
+ expectedRequest,
706
+ actualRequest,
707
+ remainingSteps,
708
+ pendingDeliveries
709
+ });
710
+ }
711
+ function runtimeFailureMessage(error) {
712
+ return `Logical wait failed: ${renderUnknownReason(error, "Unknown logical runtime failure")}`;
713
+ }
714
+ function isPromiseLike2(value) {
715
+ return (typeof value === "object" && value !== null || typeof value === "function") && typeof Reflect.get(value, "then") === "function";
716
+ }
717
+ function synchronousReturnViolation(value, label) {
718
+ if (value === undefined)
719
+ return null;
720
+ if (isPromiseLike2(value)) {
721
+ Promise.resolve(value).catch(() => {
722
+ return;
723
+ });
724
+ }
725
+ return new Error(`${label} must complete synchronously and return undefined`);
726
+ }
727
+ function createExactScriptedTransport(options) {
728
+ let captured;
729
+ try {
730
+ captured = Object.freeze({
731
+ wait: options.runtime.wait,
732
+ parseRequest: options.parseRequest,
733
+ parseResponse: options.parseResponse,
734
+ parseEvent: options.parseEvent,
735
+ parseFailure: options.parseFailure,
736
+ steps: options.steps,
737
+ beginActivity: options.activity?.begin,
738
+ activityNamespace: options.activityNamespace ?? "transport",
739
+ limits: options.limits ?? DEFAULT_SCRIPTED_TRANSPORT_LIMITS,
740
+ onListenerError: options.onListenerError
741
+ });
742
+ } catch (reason) {
743
+ return err(definitionError("invalid-options", renderUnknownReason(reason, "Scripted transport options could not be inspected")));
744
+ }
745
+ let limits;
746
+ try {
747
+ limits = validateLimits(captured.limits);
748
+ } catch (reason) {
749
+ return err(definitionError("invalid-options", renderUnknownReason(reason, "Scripted transport limits could not be inspected")));
750
+ }
751
+ if (!limits.ok)
752
+ return limits;
753
+ const parsedSteps = parseSteps(captured, limits.value);
754
+ if (!parsedSteps.ok)
755
+ return parsedSteps;
756
+ const listeners = new Set;
757
+ const activeCancellations = new Set;
758
+ const disposalController = new AbortController;
759
+ const internalErrors = [];
760
+ let droppedInternalErrors = 0;
761
+ let nextStep = 0;
762
+ let pending = 0;
763
+ let disposed = false;
764
+ let idleWaiters = [];
765
+ const remainingSteps = () => parsedSteps.value.length - nextStep;
766
+ const recordInternalError = (reason) => {
767
+ const message = renderUnknownReason(reason, "Uninspectable internal transport failure").slice(0, 500);
768
+ if (internalErrors.length < limits.value.maxRecordedInternalErrors)
769
+ internalErrors.push(message);
770
+ else
771
+ droppedInternalErrors += 1;
772
+ };
773
+ const notifyIdle = () => {
774
+ if (pending !== 0)
775
+ return;
776
+ const waiting = idleWaiters;
777
+ idleWaiters = [];
778
+ for (const resolve of waiting)
779
+ resolve();
780
+ };
781
+ const reportListenerError = (reason) => {
782
+ if (captured.onListenerError === undefined)
783
+ return;
784
+ try {
785
+ const returned = captured.onListenerError(reason);
786
+ const violation = synchronousReturnViolation(returned, "Scripted transport listener-error reporters");
787
+ if (violation !== null)
788
+ recordInternalError(violation);
789
+ } catch (reporterReason) {
790
+ recordInternalError(reporterReason);
791
+ }
792
+ };
793
+ const emit = (events) => {
794
+ if (disposed)
795
+ return false;
796
+ for (const event of events) {
797
+ if (disposed)
798
+ return false;
799
+ for (const listener of [...listeners]) {
800
+ if (disposed)
801
+ return false;
802
+ try {
803
+ const returned = listener(event);
804
+ const violation = synchronousReturnViolation(returned, "Scripted transport listeners");
805
+ if (violation !== null) {
806
+ recordInternalError(violation);
807
+ reportListenerError(violation);
808
+ }
809
+ } catch (reason) {
810
+ recordInternalError(reason);
811
+ reportListenerError(reason);
812
+ }
813
+ }
814
+ }
815
+ return !disposed;
816
+ };
817
+ const currentError = (code, message, step = null, expected = null, actual = null) => transportError(code, message, step, expected, actual, remainingSteps(), pending);
818
+ const internalError = () => {
819
+ if (internalErrors.length === 0 && droppedInternalErrors === 0)
820
+ return null;
821
+ const suffix = droppedInternalErrors === 0 ? "" : `; ${String(droppedInternalErrors)} more omitted`;
822
+ return currentError("internal-failure", `${internalErrors.join("; ")}${suffix}`);
823
+ };
824
+ const settleLease = (lease) => {
825
+ if (lease === null)
826
+ return;
827
+ try {
828
+ const released = lease.release();
829
+ if (!released.ok)
830
+ recordInternalError(released.error);
831
+ } catch (reason) {
832
+ recordInternalError(reason);
833
+ }
834
+ };
835
+ const beginLease = () => {
836
+ if (captured.beginActivity === undefined)
837
+ return ok(null);
838
+ try {
839
+ const started = captured.beginActivity(captured.activityNamespace);
840
+ return started.ok ? ok(started.value) : err(renderUnknownReason(started.error, "Activity scope failed"));
841
+ } catch (reason) {
842
+ return err(renderUnknownReason(reason, "Activity scope threw"));
843
+ }
844
+ };
845
+ const fail = (error) => {
846
+ recordInternalError(`${error.code}: ${error.message}`);
847
+ return err(Object.freeze({ kind: "transport", error }));
848
+ };
849
+ const disposedFailure = (step = null, expected = null, actual = null) => err(Object.freeze({
850
+ kind: "transport",
851
+ error: currentError("transport-disposed", "The scripted transport has been disposed", step, expected, actual)
852
+ }));
853
+ const dispose = () => {
854
+ if (disposed)
855
+ return;
856
+ disposed = true;
857
+ listeners.clear();
858
+ for (const cancel of [...activeCancellations]) {
859
+ try {
860
+ cancel();
861
+ } catch (reason) {
862
+ recordInternalError(reason);
863
+ }
864
+ }
865
+ activeCancellations.clear();
866
+ try {
867
+ disposalController.abort();
868
+ } catch (reason) {
869
+ recordInternalError(reason);
870
+ }
871
+ notifyIdle();
872
+ return;
873
+ };
874
+ const request = (input) => {
875
+ if (disposed)
876
+ return Promise.resolve(disposedFailure());
877
+ const parsedRequest = parseAndCloneWorld(input, captured.parseRequest);
878
+ if (!parsedRequest.ok) {
879
+ if (disposed)
880
+ return Promise.resolve(disposedFailure());
881
+ return Promise.resolve(fail(currentError("invalid-request", parsedRequest.error.message)));
882
+ }
883
+ if (disposed)
884
+ return Promise.resolve(disposedFailure(null, null, parsedRequest.value));
885
+ const requestCanonical = canonicalJson(parsedRequest.value);
886
+ if (!requestCanonical.ok) {
887
+ return Promise.resolve(fail(currentError("invalid-request", requestCanonical.error.message)));
888
+ }
889
+ if (disposed)
890
+ return Promise.resolve(disposedFailure(null, null, parsedRequest.value));
891
+ const step = parsedSteps.value[nextStep];
892
+ if (step === undefined) {
893
+ return Promise.resolve(fail(currentError("unexpected-request", "Transport received a request after its script was exhausted", nextStep, null, parsedRequest.value)));
894
+ }
895
+ if (requestCanonical.value !== step.requestCanonical) {
896
+ return Promise.resolve(fail(currentError("request-mismatch", `Request does not match scripted step ${String(nextStep)}`, nextStep, step.request, parsedRequest.value)));
897
+ }
898
+ const stepIndex = nextStep;
899
+ nextStep += 1;
900
+ pending += 1;
901
+ const lease = beginLease();
902
+ if (!lease.ok) {
903
+ pending -= 1;
904
+ notifyIdle();
905
+ if (disposed) {
906
+ return Promise.resolve(disposedFailure(stepIndex, step.request, parsedRequest.value));
907
+ }
908
+ return Promise.resolve(fail(currentError("activity-failed", lease.error, stepIndex, step.request, parsedRequest.value)));
909
+ }
910
+ if (disposed) {
911
+ settleLease(lease.value);
912
+ pending -= 1;
913
+ notifyIdle();
914
+ return Promise.resolve(disposedFailure(stepIndex, step.request, parsedRequest.value));
915
+ }
916
+ return new Promise((resolve) => {
917
+ let completed = false;
918
+ let finalized = false;
919
+ const finalize = (deliverAfterEvents) => {
920
+ if (finalized)
921
+ return;
922
+ finalized = true;
923
+ activeCancellations.delete(cancel);
924
+ if (deliverAfterEvents && !disposed)
925
+ emit(step.eventsAfter);
926
+ settleLease(lease.value);
927
+ if (pending > 0)
928
+ pending -= 1;
929
+ else
930
+ recordInternalError("Transport pending-delivery accounting underflow");
931
+ notifyIdle();
932
+ };
933
+ const scheduleFinalize = (deliverAfterEvents) => {
934
+ try {
935
+ queueMicrotask(() => finalize(deliverAfterEvents));
936
+ } catch (reason) {
937
+ recordInternalError(reason);
938
+ finalize(deliverAfterEvents);
939
+ }
940
+ };
941
+ const complete = (result, deliverAfterEvents) => {
942
+ if (completed)
943
+ return;
944
+ completed = true;
945
+ resolve(result);
946
+ scheduleFinalize(deliverAfterEvents);
947
+ };
948
+ const completeTransportFailure = (code, message) => {
949
+ complete(fail(currentError(code, message, stepIndex, step.request, parsedRequest.value)), false);
950
+ };
951
+ const cancel = () => {
952
+ if (!completed) {
953
+ completed = true;
954
+ resolve(disposedFailure(stepIndex, step.request, parsedRequest.value));
955
+ }
956
+ finalize(false);
957
+ };
958
+ activeCancellations.add(cancel);
959
+ if (disposed) {
960
+ cancel();
961
+ return;
962
+ }
963
+ const execute = async () => {
964
+ if (!emit(step.eventsBefore)) {
965
+ cancel();
966
+ return;
967
+ }
968
+ let waited;
969
+ try {
970
+ waited = await captured.wait(step.delayMs, disposalController.signal);
971
+ } catch (reason) {
972
+ if (disposed) {
973
+ cancel();
974
+ return;
975
+ }
976
+ completeTransportFailure("logical-wait-failed", `Logical wait failed: ${renderUnknownReason(reason, "Logical wait threw")}`);
977
+ return;
978
+ }
979
+ if (disposed) {
980
+ cancel();
981
+ return;
982
+ }
983
+ if (!waited.ok) {
984
+ completeTransportFailure("logical-wait-failed", runtimeFailureMessage(waited.error));
985
+ return;
986
+ }
987
+ const outcome = step.outcome.kind === "response" ? ok(step.outcome.value) : err(Object.freeze({
988
+ kind: "scripted",
989
+ failure: step.outcome.error
990
+ }));
991
+ complete(outcome, true);
992
+ };
993
+ execute().catch((reason) => {
994
+ completeTransportFailure("internal-failure", `Transport delivery failed: ${renderUnknownReason(reason)}`);
995
+ });
996
+ });
997
+ };
998
+ const assertNoInternalError = () => {
999
+ const failure = internalError();
1000
+ return failure === null ? ok(true) : err(failure);
1001
+ };
1002
+ const transport = {
1003
+ request,
1004
+ subscribe: (listener) => {
1005
+ if (disposed)
1006
+ return () => {
1007
+ return;
1008
+ };
1009
+ listeners.add(listener);
1010
+ let active = true;
1011
+ return () => {
1012
+ if (!active)
1013
+ return;
1014
+ active = false;
1015
+ listeners.delete(listener);
1016
+ return;
1017
+ };
1018
+ },
1019
+ dispose,
1020
+ isDisposed: () => disposed,
1021
+ remainingSteps,
1022
+ pendingDeliveries: () => pending,
1023
+ violationCount: () => internalErrors.length + droppedInternalErrors,
1024
+ whenIdle: async () => {
1025
+ while (pending !== 0) {
1026
+ await new Promise((resolve) => {
1027
+ idleWaiters.push(resolve);
1028
+ });
1029
+ }
1030
+ return assertNoInternalError();
1031
+ },
1032
+ assertDrained: () => {
1033
+ const failure = internalError();
1034
+ if (failure !== null)
1035
+ return err(failure);
1036
+ if (pending !== 0) {
1037
+ return err(currentError("pending-deliveries", `${String(pending)} transport deliveries are still pending`));
1038
+ }
1039
+ if (remainingSteps() !== 0) {
1040
+ return err(currentError("remaining-steps", `${String(remainingSteps())} scripted transport steps remain`));
1041
+ }
1042
+ return ok(true);
1043
+ }
1044
+ };
1045
+ return ok(Object.freeze(transport));
1046
+ }
1047
+
1048
+ // src/testing/index.ts
1049
+ function parseCoverageCatalogSnapshot2(input) {
1050
+ return parseCoverageCatalogSnapshot(input);
1051
+ }
1052
+ export {
1053
+ parseExpectedCoverageCatalogSnapshot,
1054
+ parseDirectSessionManifest,
1055
+ parseDirectProbeSnapshot,
1056
+ parseDefinitionCoverageSnapshot,
1057
+ parseCoverageCatalogSnapshot2 as parseCoverageCatalogSnapshot,
1058
+ createExactScriptedTransport,
1059
+ createDirectSessionManifest,
1060
+ createDirectSession,
1061
+ createDirectProbe,
1062
+ createDirectActivityScope,
1063
+ classifyCoverageEvidence,
1064
+ MAX_DIRECT_PROBE_COUNTERS,
1065
+ DIRECT_SESSION_MANIFEST_SCHEMA,
1066
+ DIRECT_PROBE_SCHEMA,
1067
+ DIRECT_CATALOG_HASH_ALGORITHM,
1068
+ DEFAULT_SCRIPTED_TRANSPORT_LIMITS
1069
+ };