brass-runtime 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +409 -137
  2. package/dist/agent/cli/main.cjs +40 -35
  3. package/dist/agent/cli/main.js +9 -4
  4. package/dist/agent/cli/main.mjs +9 -4
  5. package/dist/agent/index.cjs +8 -4
  6. package/dist/agent/index.d.ts +1 -1
  7. package/dist/agent/index.js +7 -3
  8. package/dist/agent/index.mjs +7 -3
  9. package/dist/{chunk-PPUXIH5R.js → chunk-2WC63LJK.mjs} +11 -7
  10. package/dist/chunk-3RG5ZIWI.js +10 -0
  11. package/dist/chunk-45F7OKGT.cjs +104 -0
  12. package/dist/chunk-5YOQOXEQ.cjs +2491 -0
  13. package/dist/{chunk-STVLQ3XD.cjs → chunk-7HUOJA4W.cjs} +78 -74
  14. package/dist/{chunk-BMH5AV44.js → chunk-7LVI2GIN.js} +251 -370
  15. package/dist/chunk-7TL2LHQJ.js +2491 -0
  16. package/dist/chunk-7V4KY4RL.mjs +104 -0
  17. package/dist/chunk-7XOPAB5Q.js +2143 -0
  18. package/dist/chunk-CCKHV5BT.mjs +193 -0
  19. package/dist/{chunk-AR22SXML.js → chunk-CY33PGEX.mjs} +488 -421
  20. package/dist/chunk-DJQ7OMMB.cjs +144 -0
  21. package/dist/chunk-F5EUMJL7.mjs +2143 -0
  22. package/dist/chunk-FM4W4QPL.js +193 -0
  23. package/dist/{chunk-TO7IKXYT.js → chunk-G3XGCZDQ.js} +1 -1
  24. package/dist/{chunk-BDF4AMWX.mjs → chunk-G6IQOE4P.mjs} +251 -370
  25. package/dist/chunk-GOV47PPB.mjs +552 -0
  26. package/dist/chunk-H55LI6WY.js +93 -0
  27. package/dist/chunk-IJT6RRQ5.cjs +93 -0
  28. package/dist/{chunk-ELOOF35R.mjs → chunk-J3H54ZRV.mjs} +1 -1
  29. package/dist/chunk-JF4XXPZ5.cjs +552 -0
  30. package/dist/chunk-JNFRRJYH.cjs +2143 -0
  31. package/dist/chunk-JX3LZQJH.cjs +354 -0
  32. package/dist/chunk-K2T3DV26.mjs +93 -0
  33. package/dist/chunk-KCPT2D6G.js +552 -0
  34. package/dist/chunk-MWXMNYJS.cjs +1110 -0
  35. package/dist/{chunk-VEZNF5GZ.cjs → chunk-N6VHMOWB.cjs} +130 -126
  36. package/dist/{chunk-3QMOKAS5.js → chunk-NC5SDRYE.js} +9 -5
  37. package/dist/chunk-NOYZIMUJ.mjs +144 -0
  38. package/dist/{chunk-R3R2FVLG.cjs → chunk-NYL4D7SK.cjs} +5 -5
  39. package/dist/chunk-OBGZSXTJ.cjs +10 -0
  40. package/dist/{chunk-4NHES7VK.mjs → chunk-OOGJ73B6.js} +11 -7
  41. package/dist/chunk-PNVFW245.js +144 -0
  42. package/dist/chunk-PRWCB3QL.mjs +2491 -0
  43. package/dist/{chunk-JFPU5GQI.mjs → chunk-QY5FKYEQ.js} +488 -421
  44. package/dist/chunk-ROJC3NBJ.js +104 -0
  45. package/dist/chunk-SPUEME2B.cjs +343 -0
  46. package/dist/chunk-TDVMADDN.js +343 -0
  47. package/dist/chunk-TVN5I4U6.cjs +193 -0
  48. package/dist/chunk-U5KWK3PX.mjs +343 -0
  49. package/dist/chunk-VFIUZG7J.mjs +354 -0
  50. package/dist/{chunk-TGIFUAK4.cjs → chunk-WQ5QNU5R.cjs} +459 -578
  51. package/dist/chunk-XDZOO4L5.js +354 -0
  52. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  53. package/dist/{chunk-K6M7MDZ4.mjs → chunk-ZGLD4TVZ.mjs} +9 -5
  54. package/dist/client-CtFmoDvM.d.ts +645 -0
  55. package/dist/core/index.cjs +72 -4
  56. package/dist/core/index.d.ts +92 -198
  57. package/dist/core/index.js +106 -38
  58. package/dist/core/index.mjs +106 -38
  59. package/dist/{effect-CMOQKX8y.d.ts → effect-CGNl5Rqp.d.ts} +107 -1
  60. package/dist/effectRunner-3ZHAD3LE.cjs +8 -0
  61. package/dist/effectRunner-A4CHJXJI.js +8 -0
  62. package/dist/effectRunner-OPUF6QRN.mjs +8 -0
  63. package/dist/http/index.cjs +2189 -1271
  64. package/dist/http/index.d.ts +830 -270
  65. package/dist/http/index.js +2008 -1090
  66. package/dist/http/index.mjs +2008 -1090
  67. package/dist/http/testing.cjs +159 -0
  68. package/dist/http/testing.d.ts +42 -0
  69. package/dist/http/testing.js +159 -0
  70. package/dist/http/testing.mjs +159 -0
  71. package/dist/index.cjs +246 -178
  72. package/dist/index.d.ts +9 -35
  73. package/dist/index.js +120 -52
  74. package/dist/index.mjs +120 -52
  75. package/dist/observability/index.cjs +677 -0
  76. package/dist/observability/index.d.ts +79 -0
  77. package/dist/observability/index.js +677 -0
  78. package/dist/observability/index.mjs +677 -0
  79. package/dist/schedule-Fque9Abz.d.ts +70 -0
  80. package/dist/schema/index.cjs +25 -0
  81. package/dist/schema/index.d.ts +177 -0
  82. package/dist/schema/index.js +25 -0
  83. package/dist/schema/index.mjs +25 -0
  84. package/dist/server-C8hDXA74.d.ts +674 -0
  85. package/dist/{stream-FQm9h4Mg.d.ts → stream-dvSs0QS5.d.ts} +1 -1
  86. package/dist/tracer-B5tRH9H7.d.ts +230 -0
  87. package/dist/tracing-Dt9S_6V8.d.ts +148 -0
  88. package/package.json +27 -1
  89. package/dist/chunk-BDYEENHT.js +0 -224
  90. package/dist/chunk-MS34J5LY.cjs +0 -224
  91. package/dist/chunk-UMAZLXAB.mjs +0 -224
  92. package/dist/chunk-XPZNXSVN.cjs +0 -1043
  93. package/dist/tracing-DNT9jEbr.d.ts +0 -106
@@ -1,11 +1,11 @@
1
1
  import {
2
- sleep
3
- } from "./chunk-UMAZLXAB.mjs";
2
+ makeRuntimeEventRecord
3
+ } from "./chunk-XDZOO4L5.js";
4
4
  import {
5
- Cause,
6
- Exit,
7
5
  Runtime,
8
- Scope,
6
+ getCurrentFiber
7
+ } from "./chunk-7LVI2GIN.js";
8
+ import {
9
9
  asyncEffect,
10
10
  asyncFail,
11
11
  asyncFlatMap,
@@ -13,9 +13,8 @@ import {
13
13
  asyncMap,
14
14
  asyncSucceed,
15
15
  asyncSync,
16
- unit,
17
- unsafeGetCurrentRuntime
18
- } from "./chunk-BDF4AMWX.mjs";
16
+ unit
17
+ } from "./chunk-PNVFW245.js";
19
18
 
20
19
  // src/core/types/cancel.ts
21
20
  function makeCancelToken() {
@@ -50,135 +49,6 @@ function linkAbortController(token, ac) {
50
49
  return token.onCancel(() => ac.abort());
51
50
  }
52
51
 
53
- // src/core/runtime/resource.ts
54
- function bracket(acquire, use, release) {
55
- return asyncEffect((env, cb) => {
56
- const runtime = unsafeGetCurrentRuntime();
57
- const scope = new Scope(runtime);
58
- const acquireFiber = scope.fork(acquire);
59
- acquireFiber.join((acquireExit) => {
60
- if (acquireExit._tag === "Failure") {
61
- scope.close(acquireExit);
62
- cb(acquireExit);
63
- return;
64
- }
65
- const resource = acquireExit.value;
66
- const useFiber = scope.fork(use(resource));
67
- useFiber.join((useExit) => {
68
- const releaseEffect = safeRelease(release, resource, useExit);
69
- const releaseFiber = runtime.fork(releaseEffect);
70
- releaseFiber.join(() => {
71
- scope.close(useExit);
72
- cb(useExit);
73
- });
74
- });
75
- });
76
- return () => {
77
- scope.close(Exit.failCause(Cause.interrupt()));
78
- };
79
- });
80
- }
81
- function safeRelease(release, resource, exit) {
82
- return asyncFold(
83
- (() => {
84
- try {
85
- return release(resource, exit);
86
- } catch {
87
- return unit();
88
- }
89
- })(),
90
- () => unit(),
91
- () => unit()
92
- );
93
- }
94
- function ensuring(effect, finalizer) {
95
- return asyncEffect((env, cb) => {
96
- const runtime = unsafeGetCurrentRuntime();
97
- const fiber = runtime.fork(effect);
98
- fiber.join((exit) => {
99
- const fin = asyncFold(
100
- (() => {
101
- try {
102
- return finalizer(exit);
103
- } catch {
104
- return unit();
105
- }
106
- })(),
107
- () => unit(),
108
- () => unit()
109
- );
110
- runtime.fork(fin).join(() => {
111
- cb(exit);
112
- });
113
- });
114
- return () => {
115
- fiber.interrupt();
116
- };
117
- });
118
- }
119
- function managed(acquire, release) {
120
- return {
121
- _tag: "Managed",
122
- acquire,
123
- release: (resource, exit) => release(resource, exit)
124
- };
125
- }
126
- function useManaged(m, body) {
127
- return bracket(m.acquire, body, m.release);
128
- }
129
- function managedAll(manageds) {
130
- const acquire = asyncEffect((env, cb) => {
131
- const runtime = unsafeGetCurrentRuntime();
132
- const resources = [];
133
- let i = 0;
134
- const acquireNext = () => {
135
- if (i >= manageds.length) {
136
- cb({ _tag: "Success", value: resources });
137
- return;
138
- }
139
- const m = manageds[i];
140
- const fiber = runtime.fork(m.acquire);
141
- fiber.join((exit) => {
142
- if (exit._tag === "Failure") {
143
- releaseAcquired(runtime, manageds, resources, exit).then(() => {
144
- cb(exit);
145
- });
146
- return;
147
- }
148
- resources.push(exit.value);
149
- i++;
150
- acquireNext();
151
- });
152
- };
153
- acquireNext();
154
- });
155
- const release = (resources, exit) => {
156
- return asyncEffect((_env, cb) => {
157
- const runtime = unsafeGetCurrentRuntime();
158
- releaseAcquired(runtime, manageds, resources, exit).then(() => {
159
- cb({ _tag: "Success", value: void 0 });
160
- });
161
- });
162
- };
163
- return { _tag: "Managed", acquire, release };
164
- }
165
- async function releaseAcquired(runtime, manageds, resources, exit) {
166
- for (let i = resources.length - 1; i >= 0; i--) {
167
- try {
168
- const m = manageds[i];
169
- await new Promise((resolve) => {
170
- const releaseEff = asyncFold(
171
- m.release(resources[i], exit),
172
- () => unit(),
173
- () => unit()
174
- );
175
- runtime.fork(releaseEff).join(() => resolve());
176
- });
177
- } catch {
178
- }
179
- }
180
- }
181
-
182
52
  // src/core/runtime/linkedQueue.ts
183
53
  var LinkedQueue = class {
184
54
  head = null;
@@ -252,29 +122,36 @@ function makeSemaphore(n) {
252
122
  }
253
123
  available++;
254
124
  };
255
- const withPermit = (effect) => {
256
- return asyncFlatMap(
257
- acquire(),
258
- () => asyncEffect((_env, cb) => {
259
- const runtime = unsafeGetCurrentRuntime();
260
- const fiber = runtime.fork(effect);
261
- let released = false;
262
- const releaseOnce = () => {
263
- if (released) return;
264
- released = true;
265
- release();
266
- };
267
- fiber.join((exit) => {
268
- releaseOnce();
269
- cb(exit);
270
- });
271
- return () => {
272
- fiber.interrupt();
273
- releaseOnce();
274
- };
275
- })
276
- );
277
- };
125
+ const acquirePermit = () => asyncFlatMap(
126
+ acquire(),
127
+ () => asyncEffect((_env, cb) => {
128
+ let released = false;
129
+ const releaseOnce = () => {
130
+ if (released) return;
131
+ released = true;
132
+ release();
133
+ };
134
+ const fiber = getCurrentFiber();
135
+ fiber?.addFinalizer(() => {
136
+ releaseOnce();
137
+ });
138
+ cb({ _tag: "Success", value: releaseOnce });
139
+ })
140
+ );
141
+ const withPermit = (effect) => asyncFlatMap(
142
+ acquirePermit(),
143
+ (releaseOnce) => asyncFold(
144
+ effect,
145
+ (error) => {
146
+ releaseOnce();
147
+ return asyncFail(error);
148
+ },
149
+ (value) => {
150
+ releaseOnce();
151
+ return asyncSucceed(value);
152
+ }
153
+ )
154
+ );
278
155
  return {
279
156
  capacity,
280
157
  available: () => available,
@@ -321,161 +198,6 @@ function derivedRef(parent, get, set) {
321
198
  };
322
199
  }
323
200
 
324
- // src/core/runtime/schedule.ts
325
- function recurs(n) {
326
- return {
327
- _tag: "Schedule",
328
- initial: () => 0,
329
- step: (count, _input) => {
330
- const next = count + 1;
331
- return [{ continue: next < n, delayMs: 0 }, next, next];
332
- }
333
- };
334
- }
335
- function fixed(delayMs) {
336
- return {
337
- _tag: "Schedule",
338
- initial: () => 0,
339
- step: (count, _input) => {
340
- return [{ continue: true, delayMs }, count + 1, count + 1];
341
- }
342
- };
343
- }
344
- function exponential(baseMs, maxMs = Infinity) {
345
- return {
346
- _tag: "Schedule",
347
- initial: () => 0,
348
- step: (count, _input) => {
349
- const delay = Math.min(baseMs * Math.pow(2, count), maxMs);
350
- return [{ continue: true, delayMs: delay }, count + 1, count + 1];
351
- }
352
- };
353
- }
354
- function jittered(baseMs, maxMs = Infinity) {
355
- return {
356
- _tag: "Schedule",
357
- initial: () => 0,
358
- step: (count, _input) => {
359
- const cap = Math.min(baseMs * Math.pow(2, count), maxMs);
360
- const delay = Math.floor(Math.random() * cap);
361
- return [{ continue: true, delayMs: delay }, count + 1, count + 1];
362
- }
363
- };
364
- }
365
- function elapsed(maxMs) {
366
- return {
367
- _tag: "Schedule",
368
- initial: () => performance.now(),
369
- step: (startedAt, _input) => {
370
- const el = performance.now() - startedAt;
371
- return [{ continue: el < maxMs, delayMs: 0 }, startedAt, el];
372
- }
373
- };
374
- }
375
- function whileInput(pred) {
376
- return {
377
- _tag: "Schedule",
378
- initial: () => void 0,
379
- step: (_state, input) => {
380
- return [{ continue: pred(input), delayMs: 0 }, void 0, input];
381
- }
382
- };
383
- }
384
- function take(schedule, n) {
385
- return {
386
- _tag: "Schedule",
387
- initial: () => ({ inner: schedule.initial(), count: 0 }),
388
- step: (state, input) => {
389
- if (state.count >= n) return [{ continue: false, delayMs: 0 }, state, void 0];
390
- const [decision, nextInner, output] = schedule.step(state.inner, input);
391
- const nextState = { inner: nextInner, count: state.count + 1 };
392
- return [{ continue: decision.continue && state.count + 1 < n, delayMs: decision.delayMs }, nextState, output];
393
- }
394
- };
395
- }
396
- function andThen(first, second) {
397
- return {
398
- _tag: "Schedule",
399
- initial: () => ({ phase: "first", inner: first.initial() }),
400
- step: (state, input) => {
401
- if (state.phase === "first") {
402
- const [decision2, nextInner2, output2] = first.step(state.inner, input);
403
- if (decision2.continue) {
404
- return [decision2, { phase: "first", inner: nextInner2 }, output2];
405
- }
406
- return [{ continue: true, delayMs: decision2.delayMs }, { phase: "second", inner: second.initial() }, output2];
407
- }
408
- const [decision, nextInner, output] = second.step(state.inner, input);
409
- return [decision, { phase: "second", inner: nextInner }, output];
410
- }
411
- };
412
- }
413
- function intersect(left, right) {
414
- return {
415
- _tag: "Schedule",
416
- initial: () => ({ left: left.initial(), right: right.initial() }),
417
- step: (state, input) => {
418
- const [ld, ls, lo] = left.step(state.left, input);
419
- const [rd, rs, ro] = right.step(state.right, input);
420
- const cont = ld.continue && rd.continue;
421
- const delay = Math.max(ld.delayMs, rd.delayMs);
422
- return [{ continue: cont, delayMs: delay }, { left: ls, right: rs }, [lo, ro]];
423
- }
424
- };
425
- }
426
- function union(left, right) {
427
- return {
428
- _tag: "Schedule",
429
- initial: () => ({ left: left.initial(), right: right.initial() }),
430
- step: (state, input) => {
431
- const [ld, ls, lo] = left.step(state.left, input);
432
- const [rd, rs, ro] = right.step(state.right, input);
433
- const cont = ld.continue || rd.continue;
434
- const delay = Math.min(ld.delayMs, rd.delayMs);
435
- return [{ continue: cont, delayMs: delay }, { left: ls, right: rs }, [lo, ro]];
436
- }
437
- };
438
- }
439
- function retryWithSchedule(effect, schedule) {
440
- const loop = (state) => asyncFold(
441
- effect,
442
- (error) => {
443
- const [decision, nextState, _output] = schedule.step(state, error);
444
- if (!decision.continue) return asyncFail(error);
445
- if (decision.delayMs <= 0) return loop(nextState);
446
- return asyncFlatMap(sleep(decision.delayMs), () => loop(nextState));
447
- },
448
- (value) => asyncSucceed(value)
449
- );
450
- return loop(schedule.initial());
451
- }
452
- function repeatWithSchedule(effect, schedule) {
453
- const loop = (state, lastValue) => {
454
- const [decision, nextState, _output] = schedule.step(state, lastValue);
455
- if (!decision.continue) return asyncSucceed(lastValue);
456
- if (decision.delayMs <= 0) {
457
- return asyncFold(
458
- effect,
459
- (error) => asyncFail(error),
460
- (value) => loop(nextState, value)
461
- );
462
- }
463
- return asyncFlatMap(
464
- sleep(decision.delayMs),
465
- () => asyncFold(
466
- effect,
467
- (error) => asyncFail(error),
468
- (value) => loop(nextState, value)
469
- )
470
- );
471
- };
472
- return asyncFold(
473
- effect,
474
- (error) => asyncFail(error),
475
- (value) => loop(schedule.initial(), value)
476
- );
477
- }
478
-
479
201
  // src/core/runtime/shutdown.ts
480
202
  async function gracefulShutdown(runtime, config = {}) {
481
203
  const timeoutMs = config.timeoutMs ?? 3e4;
@@ -595,9 +317,9 @@ async function assertCompletesWithin(effect, maxMs, runtime) {
595
317
  const rt = runtime ?? Runtime.make({});
596
318
  const start = performance.now();
597
319
  const result = await rt.toPromise(effect);
598
- const elapsed2 = performance.now() - start;
599
- if (elapsed2 > maxMs) {
600
- throw new Error(`Effect took ${elapsed2.toFixed(1)}ms, expected < ${maxMs}ms`);
320
+ const elapsed = performance.now() - start;
321
+ if (elapsed > maxMs) {
322
+ throw new Error(`Effect took ${elapsed.toFixed(1)}ms, expected < ${maxMs}ms`);
601
323
  }
602
324
  return result;
603
325
  }
@@ -856,97 +578,136 @@ function makeTracer(config) {
856
578
  };
857
579
  }
858
580
 
859
- // src/core/runtime/metrics.ts
860
- var DEFAULT_BOUNDARIES = [1, 5, 10, 25, 50, 100, 250, 500, 1e3, 5e3, 1e4];
861
- function makeMetrics() {
862
- const counters = /* @__PURE__ */ new Map();
863
- const gauges = /* @__PURE__ */ new Map();
864
- const histograms = /* @__PURE__ */ new Map();
865
- const key = (name, labels) => labels ? `${name}|${Object.entries(labels).sort().map(([k, v]) => `${k}=${v}`).join(",")}` : name;
866
- const counter = (name, labels = {}) => {
867
- const k = key(name, labels);
868
- if (!counters.has(k)) counters.set(k, { labels, value: 0 });
869
- const entry = counters.get(k);
870
- return {
871
- increment: (n = 1) => {
872
- entry.value += Math.max(0, n);
873
- },
874
- value: () => entry.value
875
- };
876
- };
877
- const gauge = (name, labels = {}) => {
878
- const k = key(name, labels);
879
- if (!gauges.has(k)) gauges.set(k, { labels, value: 0 });
880
- const entry = gauges.get(k);
881
- return {
882
- set: (v) => {
883
- entry.value = v;
884
- },
885
- increment: (n = 1) => {
886
- entry.value += n;
887
- },
888
- decrement: (n = 1) => {
889
- entry.value -= n;
890
- },
891
- value: () => entry.value
892
- };
893
- };
894
- const histogram = (name, boundaries = DEFAULT_BOUNDARIES, labels = {}) => {
895
- const k = key(name, labels);
896
- if (!histograms.has(k)) {
897
- const sorted = [...boundaries].sort((a, b) => a - b);
898
- histograms.set(k, {
899
- labels,
900
- boundaries: sorted,
901
- data: { boundaries: sorted, counts: new Array(sorted.length + 1).fill(0), sum: 0, count: 0, min: Infinity, max: -Infinity }
902
- });
581
+ // src/core/runtime/loggerSink.ts
582
+ function consoleJsonLogger() {
583
+ return {
584
+ emit(ev, ctx) {
585
+ if (ev.type !== "log") return;
586
+ const wallTs = Date.now();
587
+ const out = {
588
+ level: ev.level,
589
+ msg: ev.message,
590
+ wallTs,
591
+ fiberId: ctx.fiberId,
592
+ scopeId: ctx.scopeId,
593
+ traceId: ctx.traceId,
594
+ spanId: ctx.spanId,
595
+ parentSpanId: ctx.parentSpanId,
596
+ ...ev.fields ?? {}
597
+ };
598
+ if (ev.level === "error") console.error(JSON.stringify(out));
599
+ else console.log(JSON.stringify(out));
903
600
  }
904
- const entry = histograms.get(k);
905
- return {
906
- observe: (value) => {
907
- entry.data.sum += value;
908
- entry.data.count++;
909
- entry.data.min = Math.min(entry.data.min, value);
910
- entry.data.max = Math.max(entry.data.max, value);
911
- let placed = false;
912
- for (let i = 0; i < entry.boundaries.length; i++) {
913
- if (value <= entry.boundaries[i]) {
914
- entry.data.counts[i]++;
915
- placed = true;
916
- break;
917
- }
601
+ };
602
+ }
603
+
604
+ // src/core/runtime/registry.ts
605
+ var RuntimeRegistry = class {
606
+ fibers = /* @__PURE__ */ new Map();
607
+ scopes = /* @__PURE__ */ new Map();
608
+ seq = 1;
609
+ recent = [];
610
+ recentCap = 2e3;
611
+ emit(ev, ctx) {
612
+ const rec = makeRuntimeEventRecord(ev, ctx, this.seq++);
613
+ this.recent.push(rec);
614
+ if (this.recent.length > this.recentCap) this.recent.shift();
615
+ switch (rec.type) {
616
+ case "fiber.start": {
617
+ const id = rec.fiberId;
618
+ this.fibers.set(id, {
619
+ fiberId: id,
620
+ parentFiberId: rec.parentFiberId,
621
+ name: rec.name,
622
+ runState: "Running",
623
+ status: "Running",
624
+ createdAt: rec.wallTs,
625
+ lastActiveAt: rec.wallTs,
626
+ scopeId: rec.scopeId,
627
+ traceId: rec.traceId,
628
+ spanId: rec.spanId
629
+ });
630
+ break;
631
+ }
632
+ case "fiber.suspend": {
633
+ const f = this.fibers.get(rec.fiberId);
634
+ if (f) {
635
+ f.runState = "Suspended";
636
+ f.lastActiveAt = rec.wallTs;
637
+ f.awaiting = { reason: rec.reason ?? "unknown" };
918
638
  }
919
- if (!placed) entry.data.counts[entry.boundaries.length]++;
920
- },
921
- buckets: () => ({ ...entry.data }),
922
- percentile: (p) => {
923
- const target = Math.ceil(entry.data.count * (p / 100));
924
- let cumulative = 0;
925
- for (let i = 0; i < entry.boundaries.length; i++) {
926
- cumulative += entry.data.counts[i];
927
- if (cumulative >= target) return entry.boundaries[i];
639
+ break;
640
+ }
641
+ case "fiber.resume": {
642
+ const f = this.fibers.get(rec.fiberId);
643
+ if (f) {
644
+ f.runState = "Running";
645
+ f.lastActiveAt = rec.wallTs;
646
+ f.awaiting = void 0;
928
647
  }
929
- return entry.data.max;
648
+ break;
649
+ }
650
+ case "fiber.end": {
651
+ const f = this.fibers.get(rec.fiberId);
652
+ if (f) {
653
+ f.runState = "Done";
654
+ f.lastActiveAt = rec.wallTs;
655
+ f.status = rec.status === "interrupted" ? "Interrupted" : "Done";
656
+ f.lastEnd = { status: rec.status, error: rec.error };
657
+ }
658
+ break;
659
+ }
660
+ case "scope.open": {
661
+ const sid = rec.scopeId;
662
+ this.scopes.set(sid, {
663
+ scopeId: sid,
664
+ parentScopeId: rec.parentScopeId,
665
+ ownerFiberId: rec.fiberId,
666
+ openAt: rec.wallTs,
667
+ finalizers: []
668
+ });
669
+ break;
670
+ }
671
+ case "scope.close": {
672
+ const s = this.scopes.get(rec.scopeId);
673
+ if (s) s.closedAt = rec.wallTs;
674
+ break;
930
675
  }
931
- };
932
- };
933
- return {
934
- counter,
935
- gauge,
936
- histogram,
937
- snapshot: () => ({
938
- counters: Array.from(counters.entries()).map(([k, v]) => ({ name: k.split("|")[0], labels: v.labels, value: v.value })),
939
- gauges: Array.from(gauges.entries()).map(([k, v]) => ({ name: k.split("|")[0], labels: v.labels, value: v.value })),
940
- histograms: Array.from(histograms.entries()).map(([k, v]) => ({ name: k.split("|")[0], labels: v.labels, buckets: v.data }))
941
- }),
942
- reset: () => {
943
- counters.clear();
944
- gauges.clear();
945
- histograms.clear();
946
676
  }
947
- };
677
+ }
678
+ getRecentEvents() {
679
+ return this.recent.slice();
680
+ }
681
+ };
682
+
683
+ // src/core/runtime/dump.ts
684
+ function dumpAllFibers(reg) {
685
+ const fibers = Array.from(reg.fibers.values());
686
+ fibers.sort((a, b) => a.runState === b.runState ? b.lastActiveAt - a.lastActiveAt : a.runState.localeCompare(b.runState));
687
+ const lines = [];
688
+ lines.push(`=== Fiber Dump (${(/* @__PURE__ */ new Date()).toISOString()}) ===`);
689
+ for (const f of fibers) {
690
+ lines.push(
691
+ `fiber#${f.fiberId} ${f.name ?? ""} run=${f.runState} status=${f.status} scope=${f.scopeId ?? "-"} trace=${f.traceId ?? "-"} span=${f.spanId ?? "-"} last=${new Date(f.lastActiveAt).toISOString()}`
692
+ );
693
+ if (f.awaiting) lines.push(` awaiting: ${f.awaiting.reason}${f.awaiting.detail ? ` (${f.awaiting.detail})` : ""}`);
694
+ if (f.lastEnd?.error) lines.push(` end.error: ${f.lastEnd.error}`);
695
+ }
696
+ lines.push(`=== Recent Events ===`);
697
+ for (const ev of reg.getRecentEvents().slice(-80)) {
698
+ lines.push(
699
+ `${ev.seq} ${new Date(ev.wallTs).toISOString()} ${ev.type} fiber=${ev.fiberId ?? "-"} scope=${ev.scopeId ?? "-"} trace=${ev.traceId ?? "-"}`
700
+ );
701
+ }
702
+ return lines.join("\n");
948
703
  }
949
704
 
705
+ // src/core/runtime/tracer.ts
706
+ var defaultTracer = {
707
+ newTraceId: () => crypto.randomUUID(),
708
+ newSpanId: () => crypto.randomUUID()
709
+ };
710
+
950
711
  // src/core/types/typedError.ts
951
712
  function catchTag(effect, tag, handler) {
952
713
  return asyncFold(
@@ -990,30 +751,329 @@ function orElse(effect, fallback) {
990
751
  return asyncFold(effect, fallback, asyncSucceed);
991
752
  }
992
753
 
754
+ // src/core/runtime/supervisor.ts
755
+ var nextSupervisorId = 1;
756
+ var nextChildId = 1;
757
+ var Supervisor = class {
758
+ constructor(runtime, config = {}) {
759
+ this.runtime = runtime;
760
+ this.strategy = config.strategy ?? "one-for-one";
761
+ this.restart = resolveRestartPolicy(config.restart ?? "on-failure");
762
+ this.escalation = config.escalation ?? "shutdown";
763
+ this.clock = config.clock ?? Date.now;
764
+ this.onEvent = config.onEvent;
765
+ }
766
+ runtime;
767
+ id = nextSupervisorId++;
768
+ records = /* @__PURE__ */ new Map();
769
+ strategy;
770
+ restart;
771
+ escalation;
772
+ clock;
773
+ onEvent;
774
+ closed = false;
775
+ start(spec) {
776
+ if (this.closed) throw new Error("Supervisor is shut down");
777
+ const record = {
778
+ id: nextChildId++,
779
+ name: spec.name,
780
+ spec,
781
+ restart: resolveRestartPolicy(spec.restart ?? this.restart),
782
+ status: "running",
783
+ restartCount: 0,
784
+ restartTimes: [],
785
+ generation: 0,
786
+ plannedRestart: false,
787
+ joiners: []
788
+ };
789
+ this.records.set(record.id, record);
790
+ this.launch(record);
791
+ return this.handle(record);
792
+ }
793
+ startAll(specs) {
794
+ return specs.map((spec) => this.start(spec));
795
+ }
796
+ shutdown() {
797
+ return asyncEffect((_env, cb) => {
798
+ this.closed = true;
799
+ this.emit({ type: "shutdown", supervisorId: this.id });
800
+ const records = [...this.records.values()];
801
+ let remaining = records.filter((record) => record.current !== void 0).length;
802
+ for (const record of records) {
803
+ if (record.timer) {
804
+ clearTimeout(record.timer);
805
+ record.timer = void 0;
806
+ }
807
+ record.plannedRestart = false;
808
+ if (record.current) {
809
+ record.status = "interrupted";
810
+ record.current.interrupt();
811
+ } else if (!record.terminalExit) {
812
+ this.finish(record, interruptExit());
813
+ }
814
+ }
815
+ if (remaining === 0) {
816
+ cb({ _tag: "Success", value: void 0 });
817
+ return;
818
+ }
819
+ for (const record of records) {
820
+ record.current?.join(() => {
821
+ remaining--;
822
+ if (remaining === 0) cb({ _tag: "Success", value: void 0 });
823
+ });
824
+ }
825
+ });
826
+ }
827
+ handle(record) {
828
+ return {
829
+ id: record.id,
830
+ name: record.name,
831
+ current: () => record.current,
832
+ status: () => record.status,
833
+ restartCount: () => record.restartCount,
834
+ interrupt: () => {
835
+ record.plannedRestart = false;
836
+ if (record.timer) {
837
+ clearTimeout(record.timer);
838
+ record.timer = void 0;
839
+ }
840
+ if (record.current) {
841
+ record.current.interrupt();
842
+ } else if (!record.terminalExit) {
843
+ this.finish(record, interruptExit());
844
+ }
845
+ },
846
+ join: (cb) => {
847
+ if (record.terminalExit) cb(record.terminalExit);
848
+ else record.joiners.push(cb);
849
+ }
850
+ };
851
+ }
852
+ launch(record) {
853
+ if (this.closed) return;
854
+ record.generation++;
855
+ record.status = "running";
856
+ record.plannedRestart = false;
857
+ record.terminalExit = void 0;
858
+ const generation = record.generation;
859
+ const effect = typeof record.spec.effect === "function" ? record.spec.effect() : record.spec.effect;
860
+ const fiber = this.runtime.fork(effect);
861
+ record.current = fiber;
862
+ this.emit({
863
+ type: "child-start",
864
+ supervisorId: this.id,
865
+ childId: record.id,
866
+ name: record.name,
867
+ status: "running",
868
+ restartCount: record.restartCount
869
+ });
870
+ fiber.join((exit) => this.onChildExit(record, generation, exit));
871
+ }
872
+ onChildExit(record, generation, exit) {
873
+ if (generation !== record.generation) return;
874
+ record.current = void 0;
875
+ if (this.closed) {
876
+ this.finish(record, exit);
877
+ return;
878
+ }
879
+ if (record.plannedRestart) {
880
+ this.scheduleRestart(record, exit, "all-for-one");
881
+ return;
882
+ }
883
+ this.emit({
884
+ type: "child-end",
885
+ supervisorId: this.id,
886
+ childId: record.id,
887
+ name: record.name,
888
+ status: statusFromExit(exit),
889
+ restartCount: record.restartCount,
890
+ exit
891
+ });
892
+ if (!shouldRestart(record.restart.mode, exit)) {
893
+ this.finish(record, exit);
894
+ return;
895
+ }
896
+ if (this.strategy === "all-for-one") {
897
+ this.restartAll(record, exit);
898
+ return;
899
+ }
900
+ this.scheduleRestart(record, exit, "one-for-one");
901
+ }
902
+ restartAll(failed, exit) {
903
+ for (const record of this.records.values()) {
904
+ if (record.id === failed.id) continue;
905
+ if (!record.current) continue;
906
+ record.plannedRestart = true;
907
+ record.status = "restarting";
908
+ record.current.interrupt();
909
+ }
910
+ this.scheduleRestart(failed, exit, "all-for-one");
911
+ }
912
+ scheduleRestart(record, exit, reason) {
913
+ const context = this.restartContext(record, exit);
914
+ const delay = this.nextDelay(record, context);
915
+ if (delay === void 0) {
916
+ this.escalate(record, exit, "restart policy exhausted");
917
+ return;
918
+ }
919
+ record.status = "restarting";
920
+ record.restartCount++;
921
+ this.emit({
922
+ type: "child-restart",
923
+ supervisorId: this.id,
924
+ childId: record.id,
925
+ name: record.name,
926
+ status: "restarting",
927
+ restartCount: record.restartCount,
928
+ delayMs: delay,
929
+ exit,
930
+ reason
931
+ });
932
+ record.timer = setTimeout(() => {
933
+ record.timer = void 0;
934
+ this.launch(record);
935
+ }, delay);
936
+ }
937
+ nextDelay(record, context) {
938
+ const now = this.clock();
939
+ const windowStart = record.restart.withinMs === void 0 ? Number.NEGATIVE_INFINITY : now - record.restart.withinMs;
940
+ record.restartTimes = record.restartTimes.filter((ts) => ts >= windowStart);
941
+ if (record.restart.maxRestarts !== void 0 && record.restartTimes.length >= record.restart.maxRestarts) {
942
+ return void 0;
943
+ }
944
+ record.restartTimes.push(now);
945
+ 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;
949
+ return decision.continue ? Math.max(0, Math.floor(decision.delayMs)) : void 0;
950
+ }
951
+ const raw = typeof record.restart.delayMs === "function" ? record.restart.delayMs(context) : record.restart.delayMs ?? 0;
952
+ return Number.isFinite(raw) ? Math.max(0, Math.floor(raw)) : 0;
953
+ }
954
+ escalate(record, exit, reason) {
955
+ this.emit({
956
+ type: "child-escalate",
957
+ supervisorId: this.id,
958
+ childId: record.id,
959
+ name: record.name,
960
+ status: statusFromExit(exit),
961
+ restartCount: record.restartCount,
962
+ exit,
963
+ reason
964
+ });
965
+ if (this.escalation === "shutdown") {
966
+ for (const child of this.records.values()) {
967
+ if (child.id !== record.id) child.current?.interrupt();
968
+ }
969
+ }
970
+ this.finish(record, exit);
971
+ }
972
+ finish(record, exit) {
973
+ record.status = statusFromExit(exit);
974
+ record.terminalExit = exit;
975
+ const joiners = record.joiners.splice(0);
976
+ for (const joiner of joiners) joiner(exit);
977
+ }
978
+ restartContext(record, exit) {
979
+ return {
980
+ supervisorId: this.id,
981
+ childId: record.id,
982
+ name: record.name,
983
+ restartCount: record.restartCount,
984
+ exit
985
+ };
986
+ }
987
+ emit(event) {
988
+ this.onEvent?.(event);
989
+ this.runtime.emit(toRuntimeEvent(event));
990
+ }
991
+ };
992
+ function makeSupervisor(runtime, config = {}) {
993
+ return new Supervisor(runtime, config);
994
+ }
995
+ function supervise(runtime, spec, config = {}) {
996
+ return new Supervisor(runtime, config).start(spec);
997
+ }
998
+ function joinSupervised(fiber) {
999
+ return asyncEffect((_env, cb) => fiber.join(cb));
1000
+ }
1001
+ function resolveRestartPolicy(policy) {
1002
+ if (typeof policy === "string") return { mode: policy };
1003
+ return {
1004
+ mode: policy.mode ?? "on-failure",
1005
+ maxRestarts: policy.maxRestarts,
1006
+ withinMs: policy.withinMs,
1007
+ delayMs: policy.delayMs,
1008
+ schedule: policy.schedule
1009
+ };
1010
+ }
1011
+ function shouldRestart(mode, exit) {
1012
+ if (exit._tag === "Failure" && exit.cause._tag === "Interrupt") return false;
1013
+ if (mode === "never") return false;
1014
+ if (mode === "always") return true;
1015
+ return exit._tag === "Failure";
1016
+ }
1017
+ function statusFromExit(exit) {
1018
+ if (exit._tag === "Success") return "succeeded";
1019
+ return exit.cause._tag === "Interrupt" ? "interrupted" : "failed";
1020
+ }
1021
+ function toRuntimeEvent(event) {
1022
+ switch (event.type) {
1023
+ case "child-start":
1024
+ return {
1025
+ type: "supervisor.child.start",
1026
+ supervisorId: event.supervisorId,
1027
+ childId: event.childId,
1028
+ name: event.name,
1029
+ restartCount: event.restartCount ?? 0
1030
+ };
1031
+ case "child-end":
1032
+ return {
1033
+ type: "supervisor.child.end",
1034
+ supervisorId: event.supervisorId,
1035
+ childId: event.childId,
1036
+ name: event.name,
1037
+ status: event.status === "succeeded" ? "success" : event.status === "interrupted" ? "interrupted" : "failure",
1038
+ error: event.exit?._tag === "Failure" ? event.exit.cause : void 0
1039
+ };
1040
+ case "child-restart":
1041
+ return {
1042
+ type: "supervisor.child.restart",
1043
+ supervisorId: event.supervisorId,
1044
+ childId: event.childId,
1045
+ name: event.name,
1046
+ restartCount: event.restartCount ?? 0,
1047
+ delayMs: event.delayMs ?? 0,
1048
+ reason: event.reason
1049
+ };
1050
+ case "child-escalate":
1051
+ return {
1052
+ type: "supervisor.child.escalate",
1053
+ supervisorId: event.supervisorId,
1054
+ childId: event.childId,
1055
+ name: event.name,
1056
+ reason: event.reason,
1057
+ error: event.exit?._tag === "Failure" ? event.exit.cause : void 0
1058
+ };
1059
+ case "shutdown":
1060
+ return {
1061
+ type: "supervisor.shutdown",
1062
+ supervisorId: event.supervisorId
1063
+ };
1064
+ }
1065
+ }
1066
+ function interruptExit() {
1067
+ return { _tag: "Failure", cause: { _tag: "Interrupt" } };
1068
+ }
1069
+
993
1070
  export {
994
1071
  makeCancelToken,
995
1072
  linkAbortController,
996
- bracket,
997
- ensuring,
998
- managed,
999
- useManaged,
1000
- managedAll,
1001
1073
  LinkedQueue,
1002
1074
  makeSemaphore,
1003
1075
  makeRef,
1004
1076
  derivedRef,
1005
- recurs,
1006
- fixed,
1007
- exponential,
1008
- jittered,
1009
- elapsed,
1010
- whileInput,
1011
- take,
1012
- andThen,
1013
- intersect,
1014
- union,
1015
- retryWithSchedule,
1016
- repeatWithSchedule,
1017
1077
  gracefulShutdown,
1018
1078
  registerShutdownHooks,
1019
1079
  makeTestRuntime,
@@ -1034,10 +1094,17 @@ export {
1034
1094
  provideLayer,
1035
1095
  makeWorkerPool,
1036
1096
  makeTracer,
1037
- makeMetrics,
1097
+ consoleJsonLogger,
1098
+ RuntimeRegistry,
1099
+ dumpAllFibers,
1100
+ defaultTracer,
1038
1101
  catchTag,
1039
1102
  catchTags,
1040
1103
  mapError,
1041
1104
  tagError,
1042
- orElse
1105
+ orElse,
1106
+ Supervisor,
1107
+ makeSupervisor,
1108
+ supervise,
1109
+ joinSupervised
1043
1110
  };