brass-runtime 1.16.0 → 1.16.1

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 (210) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +283 -18
  3. package/dist/agent/cli/main.cjs +38 -38
  4. package/dist/agent/cli/main.js +6 -6
  5. package/dist/agent/cli/main.mjs +6 -6
  6. package/dist/agent/index.cjs +7 -7
  7. package/dist/agent/index.d.ts +1 -1
  8. package/dist/agent/index.js +6 -6
  9. package/dist/agent/index.mjs +6 -6
  10. package/dist/chunk-2HQTDLHF.mjs +683 -0
  11. package/dist/chunk-36I3M4UC.mjs +370 -0
  12. package/dist/{chunk-QY5FKYEQ.js → chunk-3AYM6WPJ.js} +570 -51
  13. package/dist/chunk-3LOYJFRR.cjs +300 -0
  14. package/dist/chunk-3Y2RIUMM.js +300 -0
  15. package/dist/{chunk-N6VHMOWB.cjs → chunk-4ROBZFL6.cjs} +128 -128
  16. package/dist/{chunk-NC5SDRYE.js → chunk-52OB2ROS.js} +4 -4
  17. package/dist/{chunk-JX3LZQJH.cjs → chunk-52PPNNI4.cjs} +82 -20
  18. package/dist/{chunk-5YOQOXEQ.cjs → chunk-5EC274J5.cjs} +676 -293
  19. package/dist/chunk-5QC7LRZ3.js +229 -0
  20. package/dist/{chunk-7TL2LHQJ.js → chunk-5VRJNBLZ.mjs} +524 -141
  21. package/dist/chunk-62AZW6UT.cjs +313 -0
  22. package/dist/chunk-6IXXWIUM.js +683 -0
  23. package/dist/chunk-74ZTY6CP.js +2871 -0
  24. package/dist/chunk-76YMRMH2.cjs +777 -0
  25. package/dist/chunk-7CMJS3QE.mjs +2871 -0
  26. package/dist/{chunk-2WC63LJK.mjs → chunk-7JIJOVCT.js} +20 -10
  27. package/dist/{chunk-FM4W4QPL.js → chunk-A2OM6NEH.mjs} +5 -4
  28. package/dist/chunk-AGR5B2BC.cjs +683 -0
  29. package/dist/chunk-AVNQLJ5V.js +777 -0
  30. package/dist/chunk-B33ICAKP.js +313 -0
  31. package/dist/{chunk-J3H54ZRV.mjs → chunk-B5JD23U7.mjs} +1 -1
  32. package/dist/chunk-BABBZK4Y.js +2024 -0
  33. package/dist/{chunk-U5KWK3PX.mjs → chunk-C3MDXTRZ.js} +11 -0
  34. package/dist/{chunk-F5EUMJL7.mjs → chunk-CIZFIMK5.js} +55 -5
  35. package/dist/{chunk-SPUEME2B.cjs → chunk-CZIVE6NT.cjs} +12 -1
  36. package/dist/{chunk-TDVMADDN.js → chunk-DNFJLJMW.mjs} +11 -0
  37. package/dist/chunk-DNFO2EIZ.mjs +777 -0
  38. package/dist/{chunk-XDZOO4L5.js → chunk-EJ6BPYVR.mjs} +79 -17
  39. package/dist/{chunk-JNFRRJYH.cjs → chunk-ENKODRU3.cjs} +242 -192
  40. package/dist/chunk-EOC4UHBS.mjs +229 -0
  41. package/dist/{chunk-7LVI2GIN.js → chunk-FH2X7BVP.js} +507 -72
  42. package/dist/{chunk-OOGJ73B6.js → chunk-FHQGHPMO.mjs} +20 -10
  43. package/dist/{chunk-WQ5QNU5R.cjs → chunk-GLE2WY7Z.cjs} +652 -217
  44. package/dist/{chunk-G6IQOE4P.mjs → chunk-GYM3LLGS.mjs} +507 -72
  45. package/dist/chunk-HLWLMW2F.mjs +2024 -0
  46. package/dist/{chunk-TVN5I4U6.cjs → chunk-JF5WGYJJ.cjs} +25 -24
  47. package/dist/{chunk-CY33PGEX.mjs → chunk-KH4SYAOS.mjs} +570 -51
  48. package/dist/chunk-KN32XNTH.mjs +313 -0
  49. package/dist/chunk-KQLYONSE.cjs +2871 -0
  50. package/dist/{chunk-7HUOJA4W.cjs → chunk-KZJQ723N.cjs} +90 -80
  51. package/dist/{chunk-CCKHV5BT.mjs → chunk-L2SYFEBS.js} +5 -4
  52. package/dist/{chunk-IJT6RRQ5.cjs → chunk-L6VB5N7Q.cjs} +20 -9
  53. package/dist/{chunk-ZGLD4TVZ.mjs → chunk-MBEJI5HF.mjs} +4 -4
  54. package/dist/{chunk-PRWCB3QL.mjs → chunk-MIIYDLGM.js} +524 -141
  55. package/dist/{chunk-H55LI6WY.js → chunk-MOO4L7F4.mjs} +15 -4
  56. package/dist/{chunk-7XOPAB5Q.js → chunk-MT3OWDPC.mjs} +55 -5
  57. package/dist/chunk-MVGUEJ5Z.cjs +370 -0
  58. package/dist/chunk-PD4EJTQC.cjs +229 -0
  59. package/dist/chunk-PWC3RBQE.mjs +300 -0
  60. package/dist/{chunk-MWXMNYJS.cjs → chunk-Q2I37RP3.cjs} +643 -124
  61. package/dist/{chunk-VFIUZG7J.mjs → chunk-RKGKFN2A.js} +79 -17
  62. package/dist/{chunk-NYL4D7SK.cjs → chunk-SA6HUJVI.cjs} +5 -5
  63. package/dist/{chunk-K2T3DV26.mjs → chunk-TRM4JUZQ.js} +15 -4
  64. package/dist/chunk-UB4B6OFY.js +370 -0
  65. package/dist/{chunk-G3XGCZDQ.js → chunk-UCUBNWM2.js} +1 -1
  66. package/dist/chunk-VN44DYYT.cjs +2024 -0
  67. package/dist/{client-CtFmoDvM.d.ts → client-CZHU674n.d.ts} +211 -36
  68. package/dist/core/index.cjs +135 -9
  69. package/dist/core/index.d.ts +238 -33
  70. package/dist/core/index.js +155 -29
  71. package/dist/core/index.mjs +155 -29
  72. package/dist/{effect-CGNl5Rqp.d.ts → effect-DIUHZ9IN.d.ts} +89 -1
  73. package/dist/effectRunner-CFLC32IK.cjs +8 -0
  74. package/dist/{effectRunner-A4CHJXJI.js → effectRunner-L4S7IPT3.js} +2 -2
  75. package/dist/{effectRunner-OPUF6QRN.mjs → effectRunner-NNGG75QA.mjs} +2 -2
  76. package/dist/http/index.cjs +324 -2986
  77. package/dist/http/index.d.ts +54 -68
  78. package/dist/http/index.js +238 -2900
  79. package/dist/http/index.mjs +238 -2900
  80. package/dist/http/testing.cjs +14 -12
  81. package/dist/http/testing.d.ts +5 -4
  82. package/dist/http/testing.js +10 -8
  83. package/dist/http/testing.mjs +10 -8
  84. package/dist/index.cjs +423 -255
  85. package/dist/index.d.ts +87 -69
  86. package/dist/index.js +301 -133
  87. package/dist/index.mjs +301 -133
  88. package/dist/observability/index.cjs +16 -531
  89. package/dist/observability/index.d.ts +81 -8
  90. package/dist/observability/index.js +23 -538
  91. package/dist/observability/index.mjs +23 -538
  92. package/dist/perf/cli.cjs +401 -0
  93. package/dist/perf/cli.d.ts +1 -0
  94. package/dist/perf/cli.js +401 -0
  95. package/dist/perf/cli.mjs +401 -0
  96. package/dist/perf/index.cjs +141 -0
  97. package/dist/perf/index.d.ts +483 -0
  98. package/dist/perf/index.js +141 -0
  99. package/dist/perf/index.mjs +141 -0
  100. package/dist/schedule-CK3Ml_7p.d.ts +259 -0
  101. package/dist/schema/index.cjs +6 -2
  102. package/dist/schema/index.d.ts +3 -1
  103. package/dist/schema/index.js +5 -1
  104. package/dist/schema/index.mjs +5 -1
  105. package/dist/{server-C8hDXA74.d.ts → server-GJPg8ZSG.d.ts} +4 -3
  106. package/dist/{stream-dvSs0QS5.d.ts → stream-B4oK9JFP.d.ts} +1 -1
  107. package/dist/{tracer-B5tRH9H7.d.ts → tracer-Hwt1cl7h.d.ts} +13 -54
  108. package/dist/{tracing-Dt9S_6V8.d.ts → tracing-DqbTKGcf.d.ts} +1 -1
  109. package/docs/ARCHITECTURE.md +292 -0
  110. package/docs/README.md +63 -0
  111. package/docs/adr/0001-ai-context-pack.md +32 -0
  112. package/docs/agent-apply-mode.md +104 -0
  113. package/docs/agent-approvals.md +110 -0
  114. package/docs/agent-batch.md +185 -0
  115. package/docs/agent-boundaries.md +112 -0
  116. package/docs/agent-chat-sessions.md +160 -0
  117. package/docs/agent-ci.md +17 -0
  118. package/docs/agent-cli.md +405 -0
  119. package/docs/agent-config.md +480 -0
  120. package/docs/agent-context-discovery.md +159 -0
  121. package/docs/agent-copilot-like-dx.md +126 -0
  122. package/docs/agent-declarative-optimized-planning.md +138 -0
  123. package/docs/agent-dx.md +224 -0
  124. package/docs/agent-env-files.md +126 -0
  125. package/docs/agent-follow-up-context.md +43 -0
  126. package/docs/agent-global-usage.md +180 -0
  127. package/docs/agent-init.md +109 -0
  128. package/docs/agent-install-and-configure.md +516 -0
  129. package/docs/agent-language-workspace-ux.md +99 -0
  130. package/docs/agent-llm-adapters.md +123 -0
  131. package/docs/agent-local-install.md +190 -0
  132. package/docs/agent-local-tests.md +51 -0
  133. package/docs/agent-observability.md +155 -0
  134. package/docs/agent-patch-quality-loop.md +162 -0
  135. package/docs/agent-presets.md +22 -0
  136. package/docs/agent-project-commands.md +237 -0
  137. package/docs/agent-project-intelligence.md +156 -0
  138. package/docs/agent-redaction.md +18 -0
  139. package/docs/agent-release-readiness.md +76 -0
  140. package/docs/agent-rollback-safety.md +162 -0
  141. package/docs/agent-rollback.md +23 -0
  142. package/docs/agent-run-artifacts.md +16 -0
  143. package/docs/agent-vscode-auto-discovery.md +137 -0
  144. package/docs/agent-vscode-batch-runner.md +100 -0
  145. package/docs/agent-vscode-chat-layout.md +90 -0
  146. package/docs/agent-vscode-clean-install.md +147 -0
  147. package/docs/agent-vscode-code-actions.md +70 -0
  148. package/docs/agent-vscode-diff-preview.md +45 -0
  149. package/docs/agent-vscode-inline-assist.md +56 -0
  150. package/docs/agent-vscode-install.md +186 -0
  151. package/docs/agent-vscode-model-setup.md +97 -0
  152. package/docs/agent-vscode-patch-preview.md +92 -0
  153. package/docs/agent-vscode-problems.md +79 -0
  154. package/docs/agent-vscode-project-dashboard.md +106 -0
  155. package/docs/agent-vscode-run-history.md +92 -0
  156. package/docs/agent-vscode-ux.md +73 -0
  157. package/docs/ai/INVARIANTS.md +84 -0
  158. package/docs/ai/PROJECT_MAP.md +338 -0
  159. package/docs/ai/PUBLIC_API.md +336 -0
  160. package/docs/ai/VALIDATION_MATRIX.md +67 -0
  161. package/docs/api-polish.md +37 -0
  162. package/docs/cancellation.md +162 -0
  163. package/docs/coverage.md +46 -0
  164. package/docs/getting-started.md +159 -0
  165. package/docs/guides/README.md +40 -0
  166. package/docs/guides/circuit-breaker.md +89 -0
  167. package/docs/guides/error-handling.md +91 -0
  168. package/docs/guides/getting-started.md +107 -0
  169. package/docs/guides/layers.md +189 -0
  170. package/docs/guides/metrics.md +101 -0
  171. package/docs/guides/resource-management.md +141 -0
  172. package/docs/guides/retry.md +215 -0
  173. package/docs/guides/semaphore.md +66 -0
  174. package/docs/guides/streams.md +117 -0
  175. package/docs/guides/supervisors.md +98 -0
  176. package/docs/guides/testing.md +162 -0
  177. package/docs/guides/tracing.md +71 -0
  178. package/docs/http-recipes.md +399 -0
  179. package/docs/http.md +749 -0
  180. package/docs/modules.md +285 -0
  181. package/docs/observability-collector-smoke.md +31 -0
  182. package/docs/observability-framework-examples.md +98 -0
  183. package/docs/observability.md +542 -0
  184. package/docs/otel-collector-smoke.yaml +27 -0
  185. package/docs/performance-profiler.md +199 -0
  186. package/docs/production-readiness.md +73 -0
  187. package/docs/recipes/README.md +12 -0
  188. package/docs/recipes/http-server.md +45 -0
  189. package/docs/recipes/layers.md +44 -0
  190. package/docs/recipes/performance.md +47 -0
  191. package/docs/recipes/runtime.md +41 -0
  192. package/docs/recipes/testing.md +41 -0
  193. package/docs/release.md +53 -0
  194. package/docs/wasm-bounded-queues.md +44 -0
  195. package/docs/wasm-engine-observability-benchmarks.md +85 -0
  196. package/docs/wasm-fiber-engine.md +117 -0
  197. package/docs/wasm-scheduler-state-machine.md +122 -0
  198. package/docs/wasm-stream-chunks.md +54 -0
  199. package/package.json +22 -2
  200. package/dist/chunk-45F7OKGT.cjs +0 -104
  201. package/dist/chunk-7V4KY4RL.mjs +0 -104
  202. package/dist/chunk-DJQ7OMMB.cjs +0 -144
  203. package/dist/chunk-GOV47PPB.mjs +0 -552
  204. package/dist/chunk-JF4XXPZ5.cjs +0 -552
  205. package/dist/chunk-KCPT2D6G.js +0 -552
  206. package/dist/chunk-NOYZIMUJ.mjs +0 -144
  207. package/dist/chunk-PNVFW245.js +0 -144
  208. package/dist/chunk-ROJC3NBJ.js +0 -104
  209. package/dist/effectRunner-3ZHAD3LE.cjs +0 -8
  210. package/dist/schedule-Fque9Abz.d.ts +0 -70
@@ -1,11 +1,23 @@
1
+ import {
2
+ ensuring
3
+ } from "./chunk-KN32XNTH.mjs";
4
+ import {
5
+ makeScheduleDriver
6
+ } from "./chunk-2HQTDLHF.mjs";
1
7
  import {
2
8
  makeRuntimeEventRecord
3
- } from "./chunk-VFIUZG7J.mjs";
9
+ } from "./chunk-EJ6BPYVR.mjs";
4
10
  import {
5
11
  Runtime,
6
- getCurrentFiber
7
- } from "./chunk-G6IQOE4P.mjs";
12
+ getCurrentFiber,
13
+ liveClock,
14
+ runtimeClockFromEnv,
15
+ toPromise,
16
+ unsafeRunAsync
17
+ } from "./chunk-GYM3LLGS.mjs";
8
18
  import {
19
+ Cause,
20
+ Exit,
9
21
  asyncEffect,
10
22
  asyncFail,
11
23
  asyncFlatMap,
@@ -14,7 +26,7 @@ import {
14
26
  asyncSucceed,
15
27
  asyncSync,
16
28
  unit
17
- } from "./chunk-NOYZIMUJ.mjs";
29
+ } from "./chunk-36I3M4UC.mjs";
18
30
 
19
31
  // src/core/types/cancel.ts
20
32
  function makeCancelToken() {
@@ -49,6 +61,25 @@ function linkAbortController(token, ac) {
49
61
  return token.onCancel(() => ac.abort());
50
62
  }
51
63
 
64
+ // src/core/runtime/dx.ts
65
+ function makeRuntime(env = {}, options = {}) {
66
+ return new Runtime({ ...options, env });
67
+ }
68
+ function runPromise(effect, envOrRuntime) {
69
+ if (envOrRuntime instanceof Runtime) return envOrRuntime.toPromise(effect);
70
+ return toPromise(effect, envOrRuntime);
71
+ }
72
+ function runExit(effect, envOrRuntime) {
73
+ return new Promise((resolve) => {
74
+ if (envOrRuntime instanceof Runtime) {
75
+ envOrRuntime.unsafeRunAsync(effect, resolve);
76
+ return;
77
+ }
78
+ unsafeRunAsync(effect, envOrRuntime, resolve);
79
+ });
80
+ }
81
+ var runEffect = runPromise;
82
+
52
83
  // src/core/runtime/linkedQueue.ts
53
84
  var LinkedQueue = class {
54
85
  head = null;
@@ -267,57 +298,314 @@ function registerShutdownHooks(runtime, config = {}) {
267
298
  }
268
299
 
269
300
  // src/core/runtime/testing.ts
270
- function makeTestRuntime(env, options) {
271
- const runtime = Runtime.make(env ?? {});
272
- const run = (effect) => runtime.toPromise(effect);
273
- const runExit = (effect) => new Promise((resolve) => {
301
+ var TestScheduler = class {
302
+ constructor(options = {}) {
303
+ this.options = options;
304
+ this.maxSteps = options.maxSteps ?? 1e4;
305
+ }
306
+ options;
307
+ queue = [];
308
+ maxSteps;
309
+ autoFlushScheduled = false;
310
+ flushing = false;
311
+ enqueuedTasks = 0;
312
+ executedTasks = 0;
313
+ droppedTasks = 0;
314
+ scheduledFlushes = 0;
315
+ completedFlushes = 0;
316
+ schedule(task, tag = "anonymous") {
317
+ if (typeof task !== "function") {
318
+ this.droppedTasks += 1;
319
+ return "dropped";
320
+ }
321
+ this.queue.push({ tag, run: task });
322
+ this.enqueuedTasks += 1;
323
+ this.requestAutoFlush();
324
+ return "accepted";
325
+ }
326
+ scheduleBatch(tasks) {
327
+ return tasks.map(({ fn, tag }) => this.schedule(fn, tag));
328
+ }
329
+ stats() {
330
+ return {
331
+ engine: "ts",
332
+ fallbackUsed: false,
333
+ data: {
334
+ len: this.queue.length,
335
+ phase: this.flushing ? "flushing" : this.autoFlushScheduled ? "scheduled" : "idle",
336
+ scheduledFlushes: this.scheduledFlushes,
337
+ completedFlushes: this.completedFlushes,
338
+ enqueuedTasks: this.enqueuedTasks,
339
+ executedTasks: this.executedTasks,
340
+ droppedTasks: this.droppedTasks,
341
+ yieldedByBudget: 0,
342
+ lanes: [{
343
+ key: "test",
344
+ len: this.queue.length,
345
+ capacity: Number.POSITIVE_INFINITY,
346
+ enqueuedTasks: this.enqueuedTasks,
347
+ executedTasks: this.executedTasks,
348
+ droppedTasks: this.droppedTasks
349
+ }]
350
+ }
351
+ };
352
+ }
353
+ pending() {
354
+ return this.queue.slice();
355
+ }
356
+ size() {
357
+ return this.queue.length;
358
+ }
359
+ flush(maxTasks = 1) {
360
+ if (this.flushing) return 0;
361
+ this.flushing = true;
362
+ this.autoFlushScheduled = false;
363
+ let ran = 0;
364
+ try {
365
+ while (ran < maxTasks) {
366
+ const next = this.queue.shift();
367
+ if (!next) break;
368
+ ran += 1;
369
+ this.executedTasks += 1;
370
+ try {
371
+ next.run();
372
+ } catch (error) {
373
+ console.error(`[TestScheduler] task threw (tag=${next.tag})`, error);
374
+ }
375
+ }
376
+ return ran;
377
+ } finally {
378
+ this.flushing = false;
379
+ this.completedFlushes += ran > 0 ? 1 : 0;
380
+ if (this.queue.length > 0) this.requestAutoFlush();
381
+ }
382
+ }
383
+ flushAll(maxSteps = this.maxSteps) {
384
+ let ran = 0;
385
+ while (this.queue.length > 0) {
386
+ if (ran >= maxSteps) {
387
+ throw new Error(`TestScheduler.flushAll exceeded ${maxSteps} steps; possible runaway fiber loop`);
388
+ }
389
+ ran += this.flush(Math.max(1, maxSteps - ran));
390
+ }
391
+ return ran;
392
+ }
393
+ requestAutoFlush() {
394
+ const autoFlush = this.options.autoFlush ?? this.options.synchronous ?? true;
395
+ if (!autoFlush || this.autoFlushScheduled || this.flushing) return;
396
+ this.autoFlushScheduled = true;
397
+ this.scheduledFlushes += 1;
398
+ queueMicrotask(() => {
399
+ if (!this.autoFlushScheduled) return;
400
+ this.flushAll();
401
+ });
402
+ }
403
+ };
404
+ var TestClock = class {
405
+ constructor(initialTimeMs = 0, flushScheduler = () => void 0, maxSteps = 1e4) {
406
+ this.flushScheduler = flushScheduler;
407
+ this.maxSteps = maxSteps;
408
+ this.nowMs = Math.max(0, Math.floor(initialTimeMs));
409
+ }
410
+ flushScheduler;
411
+ maxSteps;
412
+ nowMs;
413
+ nextId = 1;
414
+ nextSeq = 1;
415
+ timers = /* @__PURE__ */ new Map();
416
+ now() {
417
+ return this.nowMs;
418
+ }
419
+ setTimeout(task, ms) {
420
+ const id = this.nextId++;
421
+ const delay = Math.max(0, Math.floor(ms));
422
+ this.timers.set(id, {
423
+ id,
424
+ dueAt: this.nowMs + delay,
425
+ seq: this.nextSeq++,
426
+ task
427
+ });
428
+ return id;
429
+ }
430
+ clearTimeout(timer) {
431
+ if (typeof timer === "number") this.timers.delete(timer);
432
+ }
433
+ pendingTimers() {
434
+ return Array.from(this.timers.values()).sort(compareTimers).map((timer) => ({
435
+ id: timer.id,
436
+ dueAt: timer.dueAt,
437
+ delayMs: Math.max(0, timer.dueAt - this.nowMs)
438
+ }));
439
+ }
440
+ adjust(ms) {
441
+ return this.advance(ms);
442
+ }
443
+ advance(ms) {
444
+ return this.advanceTo(this.nowMs + Math.max(0, Math.floor(ms)));
445
+ }
446
+ advanceTo(targetMs) {
447
+ const target = Math.max(this.nowMs, Math.floor(targetMs));
448
+ let ran = 0;
449
+ while (true) {
450
+ if (ran >= this.maxSteps) {
451
+ throw new Error(`TestClock.advanceTo exceeded ${this.maxSteps} timers; possible runaway timer loop`);
452
+ }
453
+ const next = this.nextDueTimer(target);
454
+ if (!next) break;
455
+ this.timers.delete(next.id);
456
+ this.nowMs = next.dueAt;
457
+ ran += 1;
458
+ next.task();
459
+ this.flushScheduler();
460
+ }
461
+ this.nowMs = target;
462
+ this.flushScheduler();
463
+ return ran;
464
+ }
465
+ runDue() {
466
+ return this.advanceTo(this.nowMs);
467
+ }
468
+ runAll(maxSteps = this.maxSteps) {
469
+ let ran = 0;
470
+ while (this.timers.size > 0) {
471
+ if (ran >= maxSteps) {
472
+ throw new Error(`TestClock.runAll exceeded ${maxSteps} timers; possible runaway timer loop`);
473
+ }
474
+ const next = this.nextDueTimer(Number.POSITIVE_INFINITY);
475
+ if (!next) break;
476
+ ran += this.advanceTo(next.dueAt);
477
+ }
478
+ return ran;
479
+ }
480
+ clear() {
481
+ this.timers.clear();
482
+ }
483
+ nextDueTimer(targetMs) {
484
+ let selected;
485
+ for (const timer of this.timers.values()) {
486
+ if (timer.dueAt > targetMs) continue;
487
+ if (!selected || compareTimers(timer, selected) < 0) selected = timer;
488
+ }
489
+ return selected;
490
+ }
491
+ };
492
+ function compareTimers(a, b) {
493
+ return a.dueAt === b.dueAt ? a.seq - b.seq : a.dueAt - b.dueAt;
494
+ }
495
+ function makeTestRuntime(env, options = {}) {
496
+ const scheduler = new TestScheduler(options);
497
+ const clock = new TestClock(options.initialTimeMs, () => scheduler.flushAll(options.maxSteps), options.maxSteps);
498
+ const testEnv = withTestClock(env ?? {}, clock);
499
+ const runtime = Runtime.makeWithEngine(testEnv, "ts", { scheduler });
500
+ const flush = (maxTasks) => scheduler.flush(maxTasks);
501
+ const flushAll = (maxSteps) => scheduler.flushAll(maxSteps);
502
+ const advance = (ms) => clock.advance(ms);
503
+ const advanceTo = (targetMs) => clock.advanceTo(targetMs);
504
+ const runDueTimers = () => clock.runDue();
505
+ const runAllTimers = (maxSteps) => clock.runAll(maxSteps);
506
+ const runExit2 = (effect) => {
507
+ const promise = new Promise((resolve) => {
508
+ runtime.unsafeRunAsync(effect, resolve);
509
+ });
510
+ flushAll();
511
+ return promise;
512
+ };
513
+ const run = async (effect) => exitToPromise(await runExit2(effect));
514
+ const fork = (effect) => {
515
+ const fiber = runtime.fork(effect);
516
+ flushAll();
517
+ return fiber;
518
+ };
519
+ return {
520
+ env: testEnv,
521
+ runtime,
522
+ scheduler,
523
+ clock,
524
+ run,
525
+ runExit: runExit2,
526
+ fork,
527
+ flush,
528
+ flushAll,
529
+ advance,
530
+ advanceTo,
531
+ runDueTimers,
532
+ runAllTimers
533
+ };
534
+ }
535
+ function withTestClock(env, clock) {
536
+ const current = env;
537
+ return {
538
+ ...env,
539
+ brass: {
540
+ ...current.brass ?? {},
541
+ clock
542
+ }
543
+ };
544
+ }
545
+ function exitToPromise(exit) {
546
+ if (exit._tag === "Success") return exit.value;
547
+ const failure = Cause.firstFailure(exit.cause);
548
+ if (failure._tag === "Some") throw failure.value;
549
+ const defect = Cause.firstDefect(exit.cause);
550
+ if (defect._tag === "Some") {
551
+ throw defect.value instanceof Error ? defect.value : new Error(String(defect.value));
552
+ }
553
+ if (Cause.containsInterrupt(exit.cause)) throw new Error("Interrupted");
554
+ throw Cause.toError(exit.cause);
555
+ }
556
+ function runExitWithRuntime(runtime, effect) {
557
+ return new Promise((resolve) => {
274
558
  runtime.unsafeRunAsync(effect, resolve);
275
559
  });
276
- return { runtime, run, runExit };
560
+ }
561
+ function stableJson(value) {
562
+ return JSON.stringify(value);
563
+ }
564
+ function firstFailureValue(exit) {
565
+ if (exit._tag !== "Failure") return void 0;
566
+ const failure = Cause.firstFailure(exit.cause);
567
+ return failure._tag === "Some" ? failure.value : void 0;
568
+ }
569
+ function makeAssertionRuntime(runtime) {
570
+ return runtime ?? Runtime.make({});
277
571
  }
278
572
  async function assertSucceeds(effect, expected, runtime) {
279
- const rt = runtime ?? Runtime.make({});
280
- const exit = await new Promise((resolve) => {
281
- rt.unsafeRunAsync(effect, resolve);
282
- });
573
+ const rt = makeAssertionRuntime(runtime);
574
+ const exit = await runExitWithRuntime(rt, effect);
283
575
  if (exit._tag !== "Success") {
284
- throw new Error(`Expected success with ${JSON.stringify(expected)}, got failure: ${JSON.stringify(exit.cause)}`);
576
+ throw new Error(`Expected success with ${stableJson(expected)}, got failure: ${stableJson(exit.cause)}`);
285
577
  }
286
- if (JSON.stringify(exit.value) !== JSON.stringify(expected)) {
287
- throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(exit.value)}`);
578
+ if (stableJson(exit.value) !== stableJson(expected)) {
579
+ throw new Error(`Expected ${stableJson(expected)}, got ${stableJson(exit.value)}`);
288
580
  }
289
581
  }
290
582
  async function assertFails(effect, expectedError, runtime) {
291
- const rt = runtime ?? Runtime.make({});
292
- const exit = await new Promise((resolve) => {
293
- rt.unsafeRunAsync(effect, resolve);
294
- });
583
+ const rt = makeAssertionRuntime(runtime);
584
+ const exit = await runExitWithRuntime(rt, effect);
295
585
  if (exit._tag !== "Failure") {
296
- throw new Error(`Expected failure with ${JSON.stringify(expectedError)}, got success: ${JSON.stringify(exit.value)}`);
586
+ throw new Error(`Expected failure with ${stableJson(expectedError)}, got success: ${stableJson(exit.value)}`);
297
587
  }
298
- const error = exit.cause.error;
299
- if (JSON.stringify(error) !== JSON.stringify(expectedError)) {
300
- throw new Error(`Expected error ${JSON.stringify(expectedError)}, got ${JSON.stringify(error)}`);
588
+ const error = firstFailureValue(exit);
589
+ if (stableJson(error) !== stableJson(expectedError)) {
590
+ throw new Error(`Expected error ${stableJson(expectedError)}, got ${stableJson(error)}`);
301
591
  }
302
592
  }
303
593
  async function assertFailsWith(effect, predicate, runtime) {
304
- const rt = runtime ?? Runtime.make({});
305
- const exit = await new Promise((resolve) => {
306
- rt.unsafeRunAsync(effect, resolve);
307
- });
594
+ const rt = makeAssertionRuntime(runtime);
595
+ const exit = await runExitWithRuntime(rt, effect);
308
596
  if (exit._tag !== "Failure") {
309
- throw new Error(`Expected failure, got success: ${JSON.stringify(exit.value)}`);
597
+ throw new Error(`Expected failure, got success: ${stableJson(exit.value)}`);
310
598
  }
311
- const error = exit.cause.error;
312
- if (!predicate(error)) {
313
- throw new Error(`Error did not match predicate: ${JSON.stringify(error)}`);
599
+ const error = firstFailureValue(exit);
600
+ if (error === void 0 || !predicate(error)) {
601
+ throw new Error(`Error did not match predicate: ${stableJson(error)}`);
314
602
  }
315
603
  }
316
604
  async function assertCompletesWithin(effect, maxMs, runtime) {
317
- const rt = runtime ?? Runtime.make({});
318
- const start = performance.now();
605
+ const rt = makeAssertionRuntime(runtime);
606
+ const start = runtimeClockFromEnv(rt.env).now();
319
607
  const result = await rt.toPromise(effect);
320
- const elapsed = performance.now() - start;
608
+ const elapsed = runtimeClockFromEnv(rt.env).now() - start;
321
609
  if (elapsed > maxMs) {
322
610
  throw new Error(`Effect took ${elapsed.toFixed(1)}ms, expected < ${maxMs}ms`);
323
611
  }
@@ -328,16 +616,17 @@ function flakyEffect(failCount, successValue, errorValue) {
328
616
  return asyncEffect((_env, cb) => {
329
617
  calls++;
330
618
  if (calls <= failCount) {
331
- cb({ _tag: "Failure", cause: { _tag: "Fail", error: errorValue } });
619
+ cb(Exit.failCause(Cause.fail(errorValue)));
332
620
  } else {
333
- cb({ _tag: "Success", value: successValue });
621
+ cb(Exit.succeed(successValue));
334
622
  }
335
623
  });
336
624
  }
337
625
  function delayedEffect(ms, value) {
338
- return asyncEffect((_env, cb) => {
339
- const id = setTimeout(() => cb({ _tag: "Success", value }), ms);
340
- return () => clearTimeout(id);
626
+ return asyncEffect((env, cb) => {
627
+ const clock = runtimeClockFromEnv(env);
628
+ const id = clock.setTimeout(() => cb(Exit.succeed(value)), ms);
629
+ return () => clock.clearTimeout(id);
341
630
  });
342
631
  }
343
632
  function neverEffect() {
@@ -348,6 +637,124 @@ function neverEffect() {
348
637
  }
349
638
 
350
639
  // src/core/runtime/layer.ts
640
+ var MissingLayerServiceError = class extends Error {
641
+ _tag = "MissingLayerService";
642
+ serviceName;
643
+ constructor(serviceName) {
644
+ super(`Missing layer service '${serviceName}'. Add a layer that provides this ServiceTag or pass a LayerContext containing it.`);
645
+ this.name = "MissingLayerServiceError";
646
+ this.serviceName = serviceName;
647
+ }
648
+ };
649
+ function formatLayerError(error) {
650
+ if (error instanceof MissingLayerServiceError) return error.message;
651
+ if (isObjectRecord(error) && error._tag === "MissingLayerService" && typeof error.serviceName === "string") {
652
+ return `Missing layer service '${error.serviceName}'. Add the provider layer before using the service.`;
653
+ }
654
+ return error instanceof Error ? error.message : String(error);
655
+ }
656
+ function makeServiceTag(name) {
657
+ return {
658
+ _tag: "ServiceTag",
659
+ key: Symbol(name),
660
+ name
661
+ };
662
+ }
663
+ var serviceTag = makeServiceTag;
664
+ var defineService = makeServiceTag;
665
+ var LayerContext = class _LayerContext {
666
+ services;
667
+ constructor(entries) {
668
+ if (entries instanceof Map) {
669
+ this.services = new Map(entries);
670
+ return;
671
+ }
672
+ this.services = /* @__PURE__ */ new Map();
673
+ if (!entries) return;
674
+ for (const [tag, service] of entries) this.services.set(tag.key, service);
675
+ }
676
+ static empty() {
677
+ return new _LayerContext();
678
+ }
679
+ get(tag) {
680
+ return this.services.get(tag.key);
681
+ }
682
+ unsafeGet(tag) {
683
+ if (!this.services.has(tag.key)) {
684
+ throw new MissingLayerServiceError(tag.name);
685
+ }
686
+ return this.services.get(tag.key);
687
+ }
688
+ has(tag) {
689
+ return this.services.has(tag.key);
690
+ }
691
+ add(tag, service) {
692
+ const next = new Map(this.services);
693
+ next.set(tag.key, service);
694
+ return new _LayerContext(next);
695
+ }
696
+ merge(other) {
697
+ const next = new Map(this.services);
698
+ for (const [key, service] of other.services) next.set(key, service);
699
+ return new _LayerContext(next);
700
+ }
701
+ size() {
702
+ return this.services.size;
703
+ }
704
+ };
705
+ function makeLayerScope() {
706
+ const cache = /* @__PURE__ */ new WeakMap();
707
+ const finalizers = [];
708
+ let closed = false;
709
+ const scope = {
710
+ get: (l, deps) => {
711
+ if (closed) return asyncFail(new Error("LayerScope is closed"));
712
+ if (cache.has(l)) return asyncSucceed(cache.get(l));
713
+ const built = l.buildScoped ? l.buildScoped(deps, scope) : asyncFlatMap(
714
+ l.build(deps),
715
+ ({ service, release }) => asyncFlatMap(scope.addFinalizer(release), () => asyncSucceed(service))
716
+ );
717
+ return asyncFlatMap(
718
+ built,
719
+ (service) => asyncSync(() => {
720
+ cache.set(l, service);
721
+ return service;
722
+ })
723
+ );
724
+ },
725
+ close: () => {
726
+ if (closed) return unit();
727
+ closed = true;
728
+ return releaseAll(finalizers);
729
+ },
730
+ size: () => finalizers.length,
731
+ addFinalizer: (release) => asyncSync(() => {
732
+ if (!closed) finalizers.push(release);
733
+ })
734
+ };
735
+ return scope;
736
+ }
737
+ function releaseAll(finalizers) {
738
+ const next = finalizers.pop();
739
+ if (!next) return unit();
740
+ return asyncFold(
741
+ next(),
742
+ () => releaseAll(finalizers),
743
+ () => releaseAll(finalizers)
744
+ );
745
+ }
746
+ function mergeServices(a, b) {
747
+ if (a instanceof LayerContext && b instanceof LayerContext) {
748
+ return a.merge(b);
749
+ }
750
+ if (isObjectRecord(a) && isObjectRecord(b)) {
751
+ return { ...a, ...b };
752
+ }
753
+ return Object.assign({}, a, b);
754
+ }
755
+ function isObjectRecord(value) {
756
+ return typeof value === "object" && value !== null;
757
+ }
351
758
  function layer(acquire, release) {
352
759
  return {
353
760
  _tag: "Layer",
@@ -357,6 +764,23 @@ function layer(acquire, release) {
357
764
  }))
358
765
  };
359
766
  }
767
+ function layerValue(tag, value) {
768
+ return layerEffect(tag, () => asyncSucceed(value));
769
+ }
770
+ function layerEffect(tag, acquire, release) {
771
+ return {
772
+ _tag: "Layer",
773
+ build: (deps = LayerContext.empty()) => asyncFlatMap(acquire(deps), (service) => asyncSucceed({
774
+ service: deps.add(tag, service),
775
+ release: release ? () => release(service) : () => unit()
776
+ }))
777
+ };
778
+ }
779
+ var layerFromContext = layerEffect;
780
+ var defineLayer = layerEffect;
781
+ function getService(tag) {
782
+ return asyncSync((context) => context.unsafeGet(tag));
783
+ }
360
784
  function layerFrom() {
361
785
  return (acquire, release) => ({
362
786
  _tag: "Layer",
@@ -381,6 +805,10 @@ function layerFail(error) {
381
805
  function compose(from, to) {
382
806
  return {
383
807
  _tag: "Layer",
808
+ buildScoped: (deps, scope) => asyncFlatMap(
809
+ scope.get(from, deps),
810
+ (mid) => scope.get(to, mid)
811
+ ),
384
812
  build: (deps) => asyncFlatMap(
385
813
  from.build(deps),
386
814
  ({ service: mid, release: releaseMid }) => asyncFold(
@@ -397,13 +825,20 @@ function compose(from, to) {
397
825
  function merge(left, right) {
398
826
  return {
399
827
  _tag: "Layer",
828
+ buildScoped: (deps, scope) => asyncFlatMap(
829
+ scope.get(left, deps),
830
+ (a) => asyncFlatMap(
831
+ scope.get(right, deps),
832
+ (b) => asyncSucceed(mergeServices(a, b))
833
+ )
834
+ ),
400
835
  build: (deps) => asyncFlatMap(
401
836
  left.build(deps),
402
837
  ({ service: a, release: releaseA }) => asyncFold(
403
838
  right.build(deps),
404
839
  (error) => asyncFlatMap(releaseA(), () => asyncFail(error)),
405
840
  ({ service: b, release: releaseB }) => asyncSucceed({
406
- service: { ...a, ...b },
841
+ service: mergeServices(a, b),
407
842
  release: () => asyncFlatMap(releaseB(), () => releaseA())
408
843
  })
409
844
  )
@@ -413,22 +848,63 @@ function merge(left, right) {
413
848
  function mapLayer(l, f) {
414
849
  return {
415
850
  _tag: "Layer",
851
+ buildScoped: (deps, scope) => asyncFlatMap(
852
+ scope.get(l, deps),
853
+ (service) => asyncSucceed(f(service))
854
+ ),
416
855
  build: (deps) => asyncFlatMap(
417
856
  l.build(deps),
418
857
  ({ service, release }) => asyncSucceed({ service: f(service), release })
419
858
  )
420
859
  };
421
860
  }
861
+ function buildLayer(l, deps) {
862
+ const scope = makeLayerScope();
863
+ return asyncFold(
864
+ scope.get(l, deps),
865
+ (error) => asyncFlatMap(scope.close(), () => asyncFail(error)),
866
+ (service) => asyncSucceed({
867
+ service,
868
+ scope,
869
+ close: scope.close,
870
+ use: (body) => body(service)
871
+ })
872
+ );
873
+ }
422
874
  function provideLayer(l, use, deps) {
423
875
  return asyncFlatMap(
424
- l.build(deps ?? {}),
425
- ({ service, release }) => asyncFold(
876
+ buildLayer(l, deps ?? {}),
877
+ ({ service, close }) => ensuring(
426
878
  use(service),
427
- (error) => asyncFlatMap(release(), () => asyncFail(error)),
428
- (value) => asyncFlatMap(release(), () => asyncSucceed(value))
879
+ () => close()
429
880
  )
430
881
  );
431
882
  }
883
+ function provideLayerContext(l, use, deps = LayerContext.empty()) {
884
+ return provideLayer(l, use, deps);
885
+ }
886
+ var provide = provideLayer;
887
+ var provideContext = provideLayerContext;
888
+ var Layer = Object.freeze({
889
+ make: layer,
890
+ from: layerFrom,
891
+ succeed: layerSucceed,
892
+ fail: layerFail,
893
+ value: layerValue,
894
+ effect: layerEffect,
895
+ define: defineLayer,
896
+ fromContext: layerFromContext,
897
+ compose,
898
+ merge,
899
+ map: mapLayer,
900
+ provide: provideLayer,
901
+ provideContext: provideLayerContext,
902
+ build: buildLayer,
903
+ scope: makeLayerScope,
904
+ context: LayerContext.empty,
905
+ tag: makeServiceTag,
906
+ service: getService
907
+ });
432
908
 
433
909
  // src/core/runtime/workerPool.ts
434
910
  function makeWorkerPool(config = {}) {
@@ -653,7 +1129,7 @@ var RuntimeRegistry = class {
653
1129
  f.runState = "Done";
654
1130
  f.lastActiveAt = rec.wallTs;
655
1131
  f.status = rec.status === "interrupted" ? "Interrupted" : "Done";
656
- f.lastEnd = { status: rec.status, error: rec.error };
1132
+ f.lastEnd = { status: rec.status, error: formatRegistryError(rec.error) };
657
1133
  }
658
1134
  break;
659
1135
  }
@@ -679,6 +1155,17 @@ var RuntimeRegistry = class {
679
1155
  return this.recent.slice();
680
1156
  }
681
1157
  };
1158
+ function formatRegistryError(error) {
1159
+ if (error === void 0) return void 0;
1160
+ if (Cause.isCause(error)) return Cause.pretty(error, { singleLine: true });
1161
+ if (error instanceof Error) return error.message;
1162
+ if (typeof error === "string") return error;
1163
+ try {
1164
+ return JSON.stringify(error);
1165
+ } catch {
1166
+ return String(error);
1167
+ }
1168
+ }
682
1169
 
683
1170
  // src/core/runtime/dump.ts
684
1171
  function dumpAllFibers(reg) {
@@ -943,9 +1430,11 @@ var Supervisor = class {
943
1430
  }
944
1431
  record.restartTimes.push(now);
945
1432
  if (record.restart.schedule) {
946
- const state = record.scheduleState ?? record.restart.schedule.initial();
947
- const [decision, nextState] = record.restart.schedule.step(state, context);
948
- record.scheduleState = nextState;
1433
+ record.scheduleDriver ??= makeScheduleDriver(record.restart.schedule, {
1434
+ name: record.restart.schedule.name ?? "supervisor.restart",
1435
+ clock: this.scheduleClock()
1436
+ });
1437
+ const decision = record.scheduleDriver.next(context);
949
1438
  return decision.continue ? Math.max(0, Math.floor(decision.delayMs)) : void 0;
950
1439
  }
951
1440
  const raw = typeof record.restart.delayMs === "function" ? record.restart.delayMs(context) : record.restart.delayMs ?? 0;
@@ -988,6 +1477,13 @@ var Supervisor = class {
988
1477
  this.onEvent?.(event);
989
1478
  this.runtime.emit(toRuntimeEvent(event));
990
1479
  }
1480
+ scheduleClock() {
1481
+ return {
1482
+ now: this.clock,
1483
+ setTimeout: liveClock.setTimeout,
1484
+ clearTimeout: liveClock.clearTimeout
1485
+ };
1486
+ }
991
1487
  };
992
1488
  function makeSupervisor(runtime, config = {}) {
993
1489
  return new Supervisor(runtime, config);
@@ -1009,14 +1505,14 @@ function resolveRestartPolicy(policy) {
1009
1505
  };
1010
1506
  }
1011
1507
  function shouldRestart(mode, exit) {
1012
- if (exit._tag === "Failure" && exit.cause._tag === "Interrupt") return false;
1508
+ if (exit._tag === "Failure" && Cause.isInterruptedOnly(exit.cause)) return false;
1013
1509
  if (mode === "never") return false;
1014
1510
  if (mode === "always") return true;
1015
1511
  return exit._tag === "Failure";
1016
1512
  }
1017
1513
  function statusFromExit(exit) {
1018
1514
  if (exit._tag === "Success") return "succeeded";
1019
- return exit.cause._tag === "Interrupt" ? "interrupted" : "failed";
1515
+ return Cause.isInterruptedOnly(exit.cause) ? "interrupted" : "failed";
1020
1516
  }
1021
1517
  function toRuntimeEvent(event) {
1022
1518
  switch (event.type) {
@@ -1070,12 +1566,18 @@ function interruptExit() {
1070
1566
  export {
1071
1567
  makeCancelToken,
1072
1568
  linkAbortController,
1569
+ makeRuntime,
1570
+ runPromise,
1571
+ runExit,
1572
+ runEffect,
1073
1573
  LinkedQueue,
1074
1574
  makeSemaphore,
1075
1575
  makeRef,
1076
1576
  derivedRef,
1077
1577
  gracefulShutdown,
1078
1578
  registerShutdownHooks,
1579
+ TestScheduler,
1580
+ TestClock,
1079
1581
  makeTestRuntime,
1080
1582
  assertSucceeds,
1081
1583
  assertFails,
@@ -1084,14 +1586,31 @@ export {
1084
1586
  flakyEffect,
1085
1587
  delayedEffect,
1086
1588
  neverEffect,
1589
+ MissingLayerServiceError,
1590
+ formatLayerError,
1591
+ makeServiceTag,
1592
+ serviceTag,
1593
+ defineService,
1594
+ LayerContext,
1595
+ makeLayerScope,
1087
1596
  layer,
1597
+ layerValue,
1598
+ layerEffect,
1599
+ layerFromContext,
1600
+ defineLayer,
1601
+ getService,
1088
1602
  layerFrom,
1089
1603
  layerSucceed,
1090
1604
  layerFail,
1091
1605
  compose,
1092
1606
  merge,
1093
1607
  mapLayer,
1608
+ buildLayer,
1094
1609
  provideLayer,
1610
+ provideLayerContext,
1611
+ provide,
1612
+ provideContext,
1613
+ Layer,
1095
1614
  makeWorkerPool,
1096
1615
  makeTracer,
1097
1616
  consoleJsonLogger,