brass-runtime 1.13.8 → 1.15.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 (49) hide show
  1. package/README.md +6 -3
  2. package/dist/agent/cli/main.cjs +44 -43
  3. package/dist/agent/cli/main.js +5 -4
  4. package/dist/agent/cli/main.mjs +5 -4
  5. package/dist/agent/index.cjs +4 -3
  6. package/dist/agent/index.d.ts +1 -1
  7. package/dist/agent/index.js +3 -2
  8. package/dist/agent/index.mjs +3 -2
  9. package/dist/{chunk-3R7ZYRK2.mjs → chunk-3QMOKAS5.js} +9 -7
  10. package/dist/{chunk-ATHSSDUF.js → chunk-4NHES7VK.mjs} +113 -31
  11. package/dist/chunk-AR22SXML.js +1043 -0
  12. package/dist/chunk-BDF4AMWX.mjs +3773 -0
  13. package/dist/chunk-BDYEENHT.js +224 -0
  14. package/dist/chunk-BMH5AV44.js +3773 -0
  15. package/dist/chunk-ELOOF35R.mjs +131 -0
  16. package/dist/chunk-JFPU5GQI.mjs +1043 -0
  17. package/dist/{chunk-INZBKOHY.js → chunk-K6M7MDZ4.mjs} +9 -7
  18. package/dist/chunk-MS34J5LY.cjs +224 -0
  19. package/dist/{chunk-XNOTJSMZ.mjs → chunk-PPUXIH5R.js} +113 -31
  20. package/dist/chunk-R3R2FVLG.cjs +131 -0
  21. package/dist/{chunk-ZTDK2DLG.cjs → chunk-STVLQ3XD.cjs} +169 -87
  22. package/dist/chunk-TGIFUAK4.cjs +3773 -0
  23. package/dist/chunk-TO7IKXYT.js +131 -0
  24. package/dist/chunk-UMAZLXAB.mjs +224 -0
  25. package/dist/{chunk-XDINDYNA.cjs → chunk-VEZNF5GZ.cjs} +136 -134
  26. package/dist/chunk-XPZNXSVN.cjs +1043 -0
  27. package/dist/core/index.cjs +216 -0
  28. package/dist/core/index.d.ts +673 -0
  29. package/dist/core/index.js +216 -0
  30. package/dist/core/index.mjs +216 -0
  31. package/dist/{effect-ISvXPLgc.d.ts → effect-CMOQKX8y.d.ts} +202 -31
  32. package/dist/http/index.cjs +3177 -187
  33. package/dist/http/index.d.ts +1692 -9
  34. package/dist/http/index.js +3164 -174
  35. package/dist/http/index.mjs +3164 -174
  36. package/dist/index.cjs +936 -219
  37. package/dist/index.d.ts +313 -36
  38. package/dist/index.js +830 -113
  39. package/dist/index.mjs +830 -113
  40. package/dist/{stream-BvukHxCv.d.ts → stream-FQm9h4Mg.d.ts} +12 -4
  41. package/dist/tracing-DNT9jEbr.d.ts +106 -0
  42. package/package.json +11 -3
  43. package/wasm/pkg/brass_runtime_wasm_engine.d.ts +95 -16
  44. package/wasm/pkg/brass_runtime_wasm_engine.js +715 -15
  45. package/wasm/pkg/brass_runtime_wasm_engine_bg.wasm +0 -0
  46. package/wasm/pkg/brass_runtime_wasm_engine_bg.wasm.d.ts +78 -7
  47. package/dist/chunk-2P4PD6D7.cjs +0 -2557
  48. package/dist/chunk-7F2R7A2V.mjs +0 -2557
  49. package/dist/chunk-L6KKKM66.js +0 -2557
package/dist/index.mjs CHANGED
@@ -1,3 +1,54 @@
1
+ import {
2
+ LinkedQueue,
3
+ andThen,
4
+ assertCompletesWithin,
5
+ assertFails,
6
+ assertFailsWith,
7
+ assertSucceeds,
8
+ bracket,
9
+ catchTag,
10
+ catchTags,
11
+ compose,
12
+ delayedEffect,
13
+ derivedRef,
14
+ elapsed,
15
+ ensuring,
16
+ exponential,
17
+ fixed,
18
+ flakyEffect,
19
+ gracefulShutdown,
20
+ intersect,
21
+ jittered,
22
+ layer,
23
+ layerFail,
24
+ layerFrom,
25
+ layerSucceed,
26
+ linkAbortController,
27
+ makeCancelToken,
28
+ makeMetrics,
29
+ makeRef,
30
+ makeSemaphore,
31
+ makeTestRuntime,
32
+ makeTracer,
33
+ makeWorkerPool,
34
+ managed,
35
+ managedAll,
36
+ mapError as mapError2,
37
+ mapLayer,
38
+ merge,
39
+ neverEffect,
40
+ orElse,
41
+ provideLayer,
42
+ recurs,
43
+ registerShutdownHooks,
44
+ repeatWithSchedule,
45
+ retryWithSchedule,
46
+ tagError,
47
+ take,
48
+ union,
49
+ useManaged,
50
+ whileInput
51
+ } from "./chunk-JFPU5GQI.mjs";
1
52
  import {
2
53
  assertNever,
3
54
  collectStream,
@@ -10,7 +61,7 @@ import {
10
61
  fromPull,
11
62
  managedStream,
12
63
  mapStream,
13
- merge,
64
+ merge as merge2,
14
65
  mergeStream,
15
66
  rangeStream,
16
67
  streamFromReadableStream,
@@ -18,19 +69,37 @@ import {
18
69
  unwrapScoped,
19
70
  widenOpt,
20
71
  zip
21
- } from "./chunk-XNOTJSMZ.mjs";
72
+ } from "./chunk-4NHES7VK.mjs";
22
73
  import {
74
+ makeCircuitBreaker,
75
+ retry,
76
+ retryN,
77
+ retryWithBackoff,
78
+ sleep,
79
+ timeout
80
+ } from "./chunk-UMAZLXAB.mjs";
81
+ import {
82
+ collectAllPar,
83
+ race,
84
+ raceWith,
85
+ zipPar
86
+ } from "./chunk-ELOOF35R.mjs";
87
+ import {
88
+ ABI_VERSION,
23
89
  Async,
24
90
  Cause,
25
91
  DefaultHostExecutor,
92
+ EVENT_WORDS,
26
93
  EngineFiberHandle,
94
+ EventKindCode,
27
95
  Exit,
28
96
  HostRegistry,
29
97
  JsFiberEngine,
98
+ NONE_U32,
30
99
  NoopHooks,
100
+ OpcodeTagCode,
31
101
  ProgramBuilder,
32
102
  PushStatus,
33
- ReferenceWasmBridge,
34
103
  RingBuffer,
35
104
  Runtime,
36
105
  RuntimeFiber,
@@ -39,9 +108,10 @@ import {
39
108
  WasmFiberEngine,
40
109
  WasmFiberRegistryBridge,
41
110
  WasmPackFiberBridge,
111
+ abortablePromiseStats,
42
112
  acquireRelease,
43
- async,
44
113
  asyncCatchAll,
114
+ asyncEffect,
45
115
  asyncFail,
46
116
  asyncFlatMap,
47
117
  asyncFold,
@@ -52,7 +122,10 @@ import {
52
122
  asyncSync,
53
123
  asyncTotal,
54
124
  catchAll,
55
- collectAllPar,
125
+ decodeEvent,
126
+ decodeEventBatch,
127
+ encodeOpcodeNodes,
128
+ encodeOpcodeProgram,
56
129
  end,
57
130
  fail,
58
131
  flatMap,
@@ -61,22 +134,27 @@ import {
61
134
  getBenchmarkBudget,
62
135
  getCurrentFiber,
63
136
  globalScheduler,
137
+ inferCallerLaneFromStack,
138
+ laneTag,
64
139
  makeBoundedRingBuffer,
140
+ makeFiberReadyQueue,
65
141
  map,
66
142
  mapAsync,
67
143
  mapError,
68
144
  mapTryAsync,
69
145
  none,
70
146
  orElseOptional,
71
- race,
72
- raceWith,
147
+ resetAbortablePromiseStats,
73
148
  resolveWasmModule,
74
149
  runtimeCapabilities,
150
+ runtimeForCaller,
151
+ sanitizeLaneKey,
75
152
  setBenchmarkBudget,
76
153
  some,
77
154
  succeed,
78
155
  sync,
79
156
  toPromise,
157
+ toPromiseByCaller,
80
158
  unit,
81
159
  unsafeGetCurrentRuntime,
82
160
  unsafeRunAsync,
@@ -84,84 +162,8 @@ import {
84
162
  withAsyncPromise,
85
163
  withCurrentFiber,
86
164
  withScope,
87
- withScopeAsync,
88
- zipPar
89
- } from "./chunk-7F2R7A2V.mjs";
90
-
91
- // src/core/types/cancel.ts
92
- function makeCancelToken() {
93
- let cancelled = false;
94
- const listeners = /* @__PURE__ */ new Set();
95
- const cancel = () => {
96
- if (cancelled) return;
97
- cancelled = true;
98
- listeners.forEach((f) => f());
99
- listeners.clear();
100
- };
101
- return {
102
- isCancelled: () => cancelled,
103
- onCancel: (f) => {
104
- if (cancelled) {
105
- try {
106
- f();
107
- } catch {
108
- }
109
- return () => {
110
- };
111
- }
112
- listeners.add(f);
113
- return () => {
114
- listeners.delete(f);
115
- };
116
- },
117
- cancel
118
- };
119
- }
120
- function linkAbortController(token, ac) {
121
- return token.onCancel(() => ac.abort());
122
- }
123
-
124
- // src/core/runtime/linkedQueue.ts
125
- var LinkedQueue = class {
126
- head = null;
127
- tail = null;
128
- len = 0;
129
- get length() {
130
- return this.len;
131
- }
132
- isEmpty() {
133
- return this.len === 0;
134
- }
135
- push(value) {
136
- const node = { value, next: null, prev: this.tail, removed: false };
137
- if (this.tail) this.tail.next = node;
138
- else this.head = node;
139
- this.tail = node;
140
- this.len++;
141
- return node;
142
- }
143
- shift() {
144
- const h = this.head;
145
- if (!h) return void 0;
146
- this.unlink(h);
147
- return h.value;
148
- }
149
- remove(node) {
150
- if (node.removed) return;
151
- this.unlink(node);
152
- }
153
- unlink(node) {
154
- node.removed = true;
155
- const { prev, next } = node;
156
- if (prev) prev.next = next;
157
- else this.head = next;
158
- if (next) next.prev = prev;
159
- else this.tail = prev;
160
- node.next = null;
161
- node.prev = null;
162
- this.len--;
163
- }
164
- };
165
+ withScopeAsync
166
+ } from "./chunk-BDF4AMWX.mjs";
165
167
 
166
168
  // src/core/stream/queue.ts
167
169
  function bounded(capacity, strategy = "backpressure", options = {}) {
@@ -189,7 +191,7 @@ function makeQueue(capacity, strategy, options) {
189
191
  return {
190
192
  size: () => items.length,
191
193
  shutdown,
192
- offer: (a) => async((_env, cb) => {
194
+ offer: (a) => asyncEffect((_env, cb) => {
193
195
  if (closed) {
194
196
  cb({ _tag: "Success", value: false });
195
197
  return;
@@ -224,7 +226,7 @@ function makeQueue(capacity, strategy, options) {
224
226
  };
225
227
  return canceler;
226
228
  }),
227
- take: () => async((_env, cb) => {
229
+ take: () => asyncEffect((_env, cb) => {
228
230
  if (items.length > 0) {
229
231
  const a = items.shift();
230
232
  cb({ _tag: "Success", value: a });
@@ -250,6 +252,61 @@ function makeQueue(capacity, strategy, options) {
250
252
  takers.remove(node);
251
253
  };
252
254
  return canceler;
255
+ }),
256
+ offerBatch: (values) => asyncSync(() => {
257
+ const results = [];
258
+ for (let i = 0; i < values.length; i++) {
259
+ if (closed) {
260
+ results.push(false);
261
+ continue;
262
+ }
263
+ const a = values[i];
264
+ if (takers.length > 0) {
265
+ const t = takers.shift();
266
+ t({ _tag: "Success", value: a });
267
+ results.push(true);
268
+ continue;
269
+ }
270
+ if (items.length < capacity) {
271
+ items.push(a);
272
+ results.push(true);
273
+ continue;
274
+ }
275
+ if (strategy === "dropping") {
276
+ results.push(false);
277
+ } else if (strategy === "sliding") {
278
+ items.shift();
279
+ items.push(a);
280
+ results.push(true);
281
+ } else {
282
+ results.push(false);
283
+ }
284
+ }
285
+ return results;
286
+ }),
287
+ takeBatch: (n) => asyncSync(() => {
288
+ const results = [];
289
+ const count = Math.min(n, items.length + offerWaiters.length);
290
+ for (let i = 0; i < count; i++) {
291
+ if (items.length > 0) {
292
+ results.push(items.shift());
293
+ if (offerWaiters.length > 0 && items.length < capacity) {
294
+ const w = offerWaiters.shift();
295
+ items.push(w.a);
296
+ w.cb(true);
297
+ }
298
+ } else if (offerWaiters.length > 0) {
299
+ const w = offerWaiters.shift();
300
+ w.cb(true);
301
+ results.push(w.a);
302
+ } else {
303
+ break;
304
+ }
305
+ }
306
+ if (results.length === 0 && closed) {
307
+ throw QueueClosedErr;
308
+ }
309
+ return results;
253
310
  })
254
311
  };
255
312
  }
@@ -431,14 +488,12 @@ function resolveWasmChunkBuffer() {
431
488
  cachedWasmChunkCtor = mod?.BrassWasmChunkBuffer ?? null;
432
489
  return cachedWasmChunkCtor;
433
490
  }
434
- var JsChunker = class {
435
- constructor(maxChunkSize, fallbackUsed = false) {
491
+ var TsChunker = class {
492
+ constructor(maxChunkSize) {
436
493
  this.maxChunkSize = maxChunkSize;
437
- this.fallbackUsed = fallbackUsed;
438
494
  }
439
495
  maxChunkSize;
440
- fallbackUsed;
441
- engine = "js";
496
+ engine = "ts";
442
497
  values = [];
443
498
  emittedChunks = 0;
444
499
  emittedItems = 0;
@@ -472,8 +527,8 @@ var JsChunker = class {
472
527
  }
473
528
  stats() {
474
529
  return {
475
- engine: "js",
476
- fallbackUsed: this.fallbackUsed,
530
+ engine: "ts",
531
+ fallbackUsed: false,
477
532
  data: {
478
533
  len: this.values.length,
479
534
  maxChunkSize: this.maxChunkSize,
@@ -522,10 +577,10 @@ var WasmChunker = class {
522
577
  };
523
578
  function makeStreamChunker(chunkSize, options = {}) {
524
579
  const size = Math.max(1, chunkSize | 0);
525
- const engine = options.engine ?? "auto";
526
- if (engine === "js") return new JsChunker(size, false);
580
+ const engine = options.engine ?? "ts";
581
+ if (engine === "ts") return new TsChunker(size);
527
582
  if (engine === "wasm") return new WasmChunker(size);
528
- return resolveWasmChunkBuffer() ? new WasmChunker(size) : new JsChunker(size, true);
583
+ throw new Error(`brass-runtime stream chunk engine must be 'ts' or 'wasm'; received '${String(engine)}'`);
529
584
  }
530
585
  function chunks(input, chunkSize, options = {}) {
531
586
  const size = Math.max(1, chunkSize | 0);
@@ -591,21 +646,430 @@ function mapChunksEffect(chunkSize, f, options = {}) {
591
646
  });
592
647
  }
593
648
 
649
+ // src/core/stream/fusion.ts
650
+ var _fusionEnabled = true;
651
+ var _fusionVerbose = false;
652
+ function setFusionEnabled(enabled) {
653
+ _fusionEnabled = enabled;
654
+ }
655
+ function isFusionEnabled() {
656
+ return _fusionEnabled;
657
+ }
658
+ function setFusionVerbose(verbose) {
659
+ _fusionVerbose = verbose;
660
+ }
661
+ function isFusionVerbose() {
662
+ return _fusionVerbose;
663
+ }
664
+ function getStats(pipeline) {
665
+ const p = pipeline;
666
+ if (p._fusedSteps && Array.isArray(p._fusedSteps)) {
667
+ const metas = p._fusedSteps;
668
+ return buildStats(metas);
669
+ }
670
+ if (p[PURE_PIPELINE_TAG]) {
671
+ return null;
672
+ }
673
+ return null;
674
+ }
675
+ var PURE_PIPELINE_TAG = /* @__PURE__ */ Symbol("brass:pure-pipeline");
676
+ function makeMapStep(fn) {
677
+ return (a, _state) => ({ tag: "emit", value: fn(a) });
678
+ }
679
+ function makeFilterStep(pred) {
680
+ return (a, _state) => pred(a) ? { tag: "emit", value: a } : { tag: "skip" };
681
+ }
682
+ function makeTakeStep(n, counterIndex) {
683
+ return (a, state) => {
684
+ if (state.counters[counterIndex] >= n) {
685
+ return { tag: "halt" };
686
+ }
687
+ state.counters[counterIndex]++;
688
+ return { tag: "emit", value: a };
689
+ };
690
+ }
691
+ function makeDropStep(n, counterIndex) {
692
+ return (a, state) => {
693
+ if (state.counters[counterIndex] < n) {
694
+ state.counters[counterIndex]++;
695
+ return { tag: "skip" };
696
+ }
697
+ return { tag: "emit", value: a };
698
+ };
699
+ }
700
+ function initState(counterCount) {
701
+ return () => ({
702
+ counters: new Array(counterCount).fill(0)
703
+ });
704
+ }
705
+ function composeSteps(step1, step2) {
706
+ return (a, state) => {
707
+ const r1 = step1(a, state);
708
+ if (r1.tag !== "emit") return r1;
709
+ return step2(r1.value, state);
710
+ };
711
+ }
712
+ function getPureTag(pipeline) {
713
+ const tagged = pipeline;
714
+ return tagged[PURE_PIPELINE_TAG] ?? null;
715
+ }
716
+ function getFusedSteps(pipeline) {
717
+ const p = pipeline;
718
+ if (p._fusedSteps && Array.isArray(p._fusedSteps)) {
719
+ return p._fusedSteps;
720
+ }
721
+ return null;
722
+ }
723
+ function buildStepFromMetadata(meta, counterIndex) {
724
+ switch (meta.kind) {
725
+ case "map":
726
+ return makeMapStep(meta.fn);
727
+ case "filter":
728
+ return makeFilterStep(meta.pred);
729
+ case "take":
730
+ return makeTakeStep(meta.n, counterIndex);
731
+ case "drop":
732
+ return makeDropStep(meta.n, counterIndex);
733
+ }
734
+ }
735
+ function buildStats(metas) {
736
+ const steps = metas.map((m) => {
737
+ switch (m.kind) {
738
+ case "map":
739
+ return { kind: "map" };
740
+ case "filter":
741
+ return { kind: "filter" };
742
+ case "take":
743
+ return { kind: "take", n: m.n };
744
+ case "drop":
745
+ return { kind: "drop", n: m.n };
746
+ }
747
+ });
748
+ return {
749
+ fusedSteps: steps.length,
750
+ steps,
751
+ hasTake: metas.some((m) => m.kind === "take"),
752
+ hasDrop: metas.some((m) => m.kind === "drop")
753
+ };
754
+ }
755
+ function countCounters(metas) {
756
+ let count = 0;
757
+ for (const m of metas) {
758
+ if (m.kind === "take" || m.kind === "drop") {
759
+ count++;
760
+ }
761
+ }
762
+ return count;
763
+ }
764
+ function fuse(pipeline, options) {
765
+ const verbose = options?.verbose ?? _fusionVerbose;
766
+ if (options?.enabled === false || !_fusionEnabled && options?.enabled !== true) {
767
+ if (verbose) {
768
+ console.log("[FusionEngine] Fusion disabled by options");
769
+ }
770
+ return null;
771
+ }
772
+ const metas = collectMetadata(pipeline);
773
+ if (!metas || metas.length === 0) {
774
+ if (verbose) {
775
+ console.log("[FusionEngine] Pipeline is not fusable (no pure tags detected)");
776
+ }
777
+ return null;
778
+ }
779
+ if (verbose) {
780
+ console.log(
781
+ `[FusionEngine] Fusing ${metas.length} steps: ${metas.map((m) => m.kind).join(" \u2192 ")}`
782
+ );
783
+ }
784
+ let counterIndex = 0;
785
+ let composedStep = buildStepFromMetadata(
786
+ metas[0],
787
+ metas[0].kind === "take" || metas[0].kind === "drop" ? counterIndex++ : -1
788
+ );
789
+ for (let i = 1; i < metas.length; i++) {
790
+ const meta = metas[i];
791
+ const idx = meta.kind === "take" || meta.kind === "drop" ? counterIndex++ : -1;
792
+ composedStep = composeSteps(composedStep, buildStepFromMetadata(meta, idx));
793
+ }
794
+ const counterCount = countCounters(metas);
795
+ const stats = buildStats(metas);
796
+ return {
797
+ _tag: "FusedPipeline",
798
+ step: composedStep,
799
+ initState: initState(counterCount),
800
+ stats
801
+ };
802
+ }
803
+ function collectMetadata(pipeline) {
804
+ const fusedSteps = getFusedSteps(pipeline);
805
+ if (fusedSteps) {
806
+ return fusedSteps;
807
+ }
808
+ const tag = getPureTag(pipeline);
809
+ if (tag) {
810
+ return [tag];
811
+ }
812
+ return null;
813
+ }
814
+ function runFusedArray(input, fused) {
815
+ const state = fused.initState();
816
+ const output = [];
817
+ for (let i = 0; i < input.length; i++) {
818
+ const result = fused.step(input[i], state);
819
+ switch (result.tag) {
820
+ case "emit":
821
+ output.push(result.value);
822
+ break;
823
+ case "skip":
824
+ break;
825
+ case "halt":
826
+ return output;
827
+ }
828
+ }
829
+ return output;
830
+ }
831
+ function drainStreamSync(stream) {
832
+ const result = [];
833
+ let cur = stream;
834
+ while (true) {
835
+ switch (cur._tag) {
836
+ case "Empty":
837
+ return result;
838
+ case "FromArray": {
839
+ const arr = cur.values;
840
+ for (let i = 0; i < arr.length; i++) {
841
+ result.push(arr[i]);
842
+ }
843
+ return result;
844
+ }
845
+ case "Emit": {
846
+ const zio = cur.value;
847
+ if (zio._tag === "Succeed") {
848
+ result.push(zio.value);
849
+ return result;
850
+ }
851
+ return null;
852
+ }
853
+ case "Concat": {
854
+ const leftItems = drainStreamSync(cur.left);
855
+ if (leftItems === null) return null;
856
+ for (let i = 0; i < leftItems.length; i++) {
857
+ result.push(leftItems[i]);
858
+ }
859
+ cur = cur.right;
860
+ break;
861
+ }
862
+ default:
863
+ return null;
864
+ }
865
+ }
866
+ }
867
+ function applyFused(stream, fused) {
868
+ const inputArray = drainStreamSync(stream);
869
+ if (inputArray !== null) {
870
+ const outputArray = runFusedArray(inputArray, fused);
871
+ return arrayToStream(outputArray);
872
+ }
873
+ const state = fused.initState();
874
+ const loop = (cur) => fromPull(
875
+ asyncFold(
876
+ uncons(cur),
877
+ // End-of-stream or error: propagate
878
+ (opt) => asyncFail(opt),
879
+ ([a, tail]) => {
880
+ const result = fused.step(a, state);
881
+ switch (result.tag) {
882
+ case "emit":
883
+ return asyncSucceed([result.value, loop(tail)]);
884
+ case "skip":
885
+ return uncons(loop(tail));
886
+ case "halt":
887
+ return asyncFail(none);
888
+ }
889
+ }
890
+ )
891
+ );
892
+ return loop(stream);
893
+ }
894
+ function arrayToStream(values) {
895
+ if (values.length === 0) return { _tag: "Empty" };
896
+ return { _tag: "FromArray", values };
897
+ }
898
+ function serializeFusedPipeline(pipeline) {
899
+ const p = pipeline;
900
+ let metas = null;
901
+ if (p._fusedSteps && Array.isArray(p._fusedSteps)) {
902
+ metas = p._fusedSteps;
903
+ } else if (p[PURE_PIPELINE_TAG]) {
904
+ return null;
905
+ }
906
+ if (!metas || metas.length === 0) {
907
+ return null;
908
+ }
909
+ const steps = metas.map((meta) => {
910
+ switch (meta.kind) {
911
+ case "map":
912
+ return { kind: "map", fnSource: meta.fn.toString() };
913
+ case "filter":
914
+ return { kind: "filter", predSource: meta.pred.toString() };
915
+ case "take":
916
+ return { kind: "take", n: meta.n };
917
+ case "drop":
918
+ return { kind: "drop", n: meta.n };
919
+ }
920
+ });
921
+ return { version: 1, steps };
922
+ }
923
+ function deserializeFusedPipeline(serialized) {
924
+ try {
925
+ if (serialized.version !== 1) {
926
+ return null;
927
+ }
928
+ if (!serialized.steps || serialized.steps.length === 0) {
929
+ return null;
930
+ }
931
+ const metas = [];
932
+ for (const step of serialized.steps) {
933
+ switch (step.kind) {
934
+ case "map": {
935
+ const fn = new Function("return " + step.fnSource)();
936
+ if (typeof fn !== "function") return null;
937
+ metas.push({ kind: "map", fn });
938
+ break;
939
+ }
940
+ case "filter": {
941
+ const pred = new Function("return " + step.predSource)();
942
+ if (typeof pred !== "function") return null;
943
+ metas.push({ kind: "filter", pred });
944
+ break;
945
+ }
946
+ case "take": {
947
+ if (typeof step.n !== "number" || step.n < 0) return null;
948
+ metas.push({ kind: "take", n: step.n });
949
+ break;
950
+ }
951
+ case "drop": {
952
+ if (typeof step.n !== "number" || step.n < 0) return null;
953
+ metas.push({ kind: "drop", n: step.n });
954
+ break;
955
+ }
956
+ default:
957
+ return null;
958
+ }
959
+ }
960
+ const carrier = createDeserializedCarrier(metas);
961
+ return carrier;
962
+ } catch {
963
+ return null;
964
+ }
965
+ }
966
+ function createDeserializedCarrier(metas) {
967
+ let counterIndex = 0;
968
+ let composedStep = buildStepFromMetadata(
969
+ metas[0],
970
+ metas[0].kind === "take" || metas[0].kind === "drop" ? counterIndex++ : -1
971
+ );
972
+ for (let i = 1; i < metas.length; i++) {
973
+ const meta = metas[i];
974
+ const idx = meta.kind === "take" || meta.kind === "drop" ? counterIndex++ : -1;
975
+ composedStep = composeSteps(composedStep, buildStepFromMetadata(meta, idx));
976
+ }
977
+ const counterCount = countCounters(metas);
978
+ const stats = buildStats(metas);
979
+ const fusedRepr = {
980
+ _tag: "FusedPipeline",
981
+ step: composedStep,
982
+ initState: initState(counterCount),
983
+ stats
984
+ };
985
+ const pipeline = ((input) => {
986
+ return applyFused(input, fusedRepr);
987
+ });
988
+ pipeline._fusedSteps = metas;
989
+ return pipeline;
990
+ }
991
+
594
992
  // src/core/stream/pipeline.ts
595
993
  function via(stream, pipeline) {
994
+ if (isFusionEnabled()) {
995
+ const p = pipeline;
996
+ if (p._fusedSteps || p[PURE_PIPELINE_TAG]) {
997
+ let fusedRepr = p._cachedFusedRepr;
998
+ if (!fusedRepr) {
999
+ fusedRepr = fuse(pipeline);
1000
+ if (fusedRepr) p._cachedFusedRepr = fusedRepr;
1001
+ }
1002
+ if (fusedRepr) {
1003
+ return applyFused(stream, fusedRepr);
1004
+ }
1005
+ }
1006
+ }
596
1007
  return pipeline(stream);
597
1008
  }
598
- function andThen(p1, p2) {
1009
+ function andThen2(p1, p2) {
1010
+ const fused = tryFuse(p1, p2);
1011
+ if (fused) return fused;
599
1012
  return ((input) => p2(p1(input)));
600
1013
  }
601
- function compose(p2, p1) {
602
- return andThen(p1, p2);
1014
+ function collectPureMetas(pipeline) {
1015
+ const p = pipeline;
1016
+ if (p._fusedSteps && Array.isArray(p._fusedSteps)) {
1017
+ return p._fusedSteps;
1018
+ }
1019
+ const tag = p[PURE_PIPELINE_TAG];
1020
+ if (tag) {
1021
+ return [tag];
1022
+ }
1023
+ return null;
1024
+ }
1025
+ function tryFuse(p1, p2) {
1026
+ if (!isFusionEnabled()) {
1027
+ if (isFusionVerbose()) {
1028
+ console.log("[FusionEngine] andThen: fusion globally disabled, skipping");
1029
+ }
1030
+ return null;
1031
+ }
1032
+ const metas1 = collectPureMetas(p1);
1033
+ const metas2 = collectPureMetas(p2);
1034
+ if (!metas1 || !metas2) {
1035
+ if (isFusionVerbose()) {
1036
+ const reason = !metas1 && !metas2 ? "neither operand is pure" : !metas1 ? "left operand is not pure" : "right operand is not pure";
1037
+ console.log(`[FusionEngine] andThen: cannot fuse \u2014 ${reason}`);
1038
+ }
1039
+ return null;
1040
+ }
1041
+ const combinedMetas = [...metas1, ...metas2];
1042
+ const fusedRepr = fuse(
1043
+ createMetadataCarrier(combinedMetas)
1044
+ );
1045
+ if (!fusedRepr) {
1046
+ return null;
1047
+ }
1048
+ if (isFusionVerbose()) {
1049
+ console.log(
1050
+ `[FusionEngine] andThen: fused ${combinedMetas.length} steps: ${combinedMetas.map((m) => m.kind).join(" \u2192 ")}`
1051
+ );
1052
+ }
1053
+ const fusedPipeline = ((input) => {
1054
+ return applyFused(input, fusedRepr);
1055
+ });
1056
+ fusedPipeline._fusedSteps = combinedMetas;
1057
+ return fusedPipeline;
1058
+ }
1059
+ function createMetadataCarrier(metas) {
1060
+ const carrier = (() => {
1061
+ });
1062
+ carrier._fusedSteps = metas;
1063
+ return carrier;
1064
+ }
1065
+ function compose2(p2, p1) {
1066
+ return andThen2(p1, p2);
603
1067
  }
604
1068
  function identity() {
605
1069
  return ((input) => input);
606
1070
  }
607
1071
  function mapP(f) {
608
- return ((input) => {
1072
+ const pipeline = ((input) => {
609
1073
  const onError = (opt) => asyncFail(opt);
610
1074
  const onSuccess = ([a, tail]) => asyncSucceed([f(a), loop(tail)]);
611
1075
  const loop = (cur) => fromPull(
@@ -613,15 +1077,19 @@ function mapP(f) {
613
1077
  );
614
1078
  return loop(input);
615
1079
  });
1080
+ pipeline[PURE_PIPELINE_TAG] = { kind: "map", fn: f };
1081
+ return pipeline;
616
1082
  }
617
1083
  function filterP(pred) {
618
- return ((input) => {
1084
+ const pipeline = ((input) => {
619
1085
  const onError = (opt) => asyncFail(opt);
620
1086
  const onSuccess = ([a, tail]) => pred(a) ? asyncSucceed([a, loop(tail)]) : next(tail);
621
1087
  const next = (cur) => asyncFold(uncons(cur), onError, onSuccess);
622
1088
  const loop = (cur) => fromPull(next(cur));
623
1089
  return loop(input);
624
1090
  });
1091
+ pipeline[PURE_PIPELINE_TAG] = { kind: "filter", pred };
1092
+ return pipeline;
625
1093
  }
626
1094
  function filterMapP(f) {
627
1095
  return ((input) => {
@@ -640,7 +1108,7 @@ function filterMapP(f) {
640
1108
  }
641
1109
  function takeP(n) {
642
1110
  const m = Math.max(0, n | 0);
643
- return ((input) => {
1111
+ const pipeline = ((input) => {
644
1112
  const loop = (cur, remaining) => {
645
1113
  if (remaining <= 0) return emptyStream();
646
1114
  return fromPull(
@@ -653,10 +1121,12 @@ function takeP(n) {
653
1121
  };
654
1122
  return loop(input, m);
655
1123
  });
1124
+ pipeline[PURE_PIPELINE_TAG] = { kind: "take", n: m };
1125
+ return pipeline;
656
1126
  }
657
1127
  function dropP(n) {
658
1128
  const m = Math.max(0, n | 0);
659
- return ((input) => {
1129
+ const pipeline = ((input) => {
660
1130
  const skip = (cur, remaining) => {
661
1131
  if (remaining <= 0) return cur;
662
1132
  return fromPull(
@@ -669,6 +1139,8 @@ function dropP(n) {
669
1139
  };
670
1140
  return skip(input, m);
671
1141
  });
1142
+ pipeline[PURE_PIPELINE_TAG] = { kind: "drop", n: m };
1143
+ return pipeline;
672
1144
  }
673
1145
  function mapEffectP(f) {
674
1146
  return ((input) => {
@@ -731,25 +1203,187 @@ function groupedP(n) {
731
1203
  });
732
1204
  }
733
1205
 
1206
+ // src/core/stream/operators.ts
1207
+ function throttle(stream, intervalMs) {
1208
+ let lastEmitTime = 0;
1209
+ const loop = (cur) => fromPull(
1210
+ asyncFold(
1211
+ uncons(cur),
1212
+ (opt) => asyncFail(opt),
1213
+ ([a, tail]) => {
1214
+ const now = Date.now();
1215
+ if (now - lastEmitTime >= intervalMs) {
1216
+ lastEmitTime = now;
1217
+ return asyncSucceed([a, loop(tail)]);
1218
+ }
1219
+ return uncons(loop(tail));
1220
+ }
1221
+ )
1222
+ );
1223
+ return loop(stream);
1224
+ }
1225
+ function debounce(stream, delayMs) {
1226
+ return fromPull(
1227
+ asyncEffect((_env, cb) => {
1228
+ let lastValue;
1229
+ let hasValue = false;
1230
+ let timerId;
1231
+ let done = false;
1232
+ let tail = stream;
1233
+ const pullNext = () => {
1234
+ if (done) return;
1235
+ const pull = uncons(tail);
1236
+ unsafeRunAsync(pull, _env, (exit) => {
1237
+ if (done) return;
1238
+ if (exit._tag === "Failure") {
1239
+ if (hasValue) {
1240
+ const value = lastValue;
1241
+ hasValue = false;
1242
+ clearTimeout(timerId);
1243
+ cb({ _tag: "Success", value: [value, emptyStream()] });
1244
+ } else {
1245
+ cb(exit);
1246
+ }
1247
+ return;
1248
+ }
1249
+ const [a, nextTail] = exit.value;
1250
+ tail = nextTail;
1251
+ lastValue = a;
1252
+ hasValue = true;
1253
+ clearTimeout(timerId);
1254
+ timerId = setTimeout(() => {
1255
+ if (done) return;
1256
+ const value = lastValue;
1257
+ hasValue = false;
1258
+ done = true;
1259
+ cb({ _tag: "Success", value: [value, debounce(tail, delayMs)] });
1260
+ }, delayMs);
1261
+ pullNext();
1262
+ });
1263
+ };
1264
+ pullNext();
1265
+ return () => {
1266
+ done = true;
1267
+ clearTimeout(timerId);
1268
+ };
1269
+ })
1270
+ );
1271
+ }
1272
+ function zip2(left, right) {
1273
+ const loop = (l, r) => fromPull(
1274
+ asyncFold(
1275
+ uncons(l),
1276
+ (opt) => asyncFail(opt),
1277
+ ([a, lTail]) => asyncFold(
1278
+ uncons(r),
1279
+ (opt) => asyncFail(opt),
1280
+ ([b, rTail]) => asyncSucceed([[a, b], loop(lTail, rTail)])
1281
+ )
1282
+ )
1283
+ );
1284
+ return loop(left, right);
1285
+ }
1286
+ function zipWith(left, right, f) {
1287
+ const loop = (l, r) => fromPull(
1288
+ asyncFold(
1289
+ uncons(l),
1290
+ (opt) => asyncFail(opt),
1291
+ ([a, lTail]) => asyncFold(
1292
+ uncons(r),
1293
+ (opt) => asyncFail(opt),
1294
+ ([b, rTail]) => asyncSucceed([f(a, b), loop(lTail, rTail)])
1295
+ )
1296
+ )
1297
+ );
1298
+ return loop(left, right);
1299
+ }
1300
+ function scan(stream, initial, f) {
1301
+ const loop = (cur, acc) => fromPull(
1302
+ asyncFold(
1303
+ uncons(cur),
1304
+ (opt) => asyncFail(opt),
1305
+ ([a, tail]) => {
1306
+ const next = f(acc, a);
1307
+ return asyncSucceed([next, loop(tail, next)]);
1308
+ }
1309
+ )
1310
+ );
1311
+ return concatStream(
1312
+ emitStream(succeed(initial)),
1313
+ loop(stream, initial)
1314
+ );
1315
+ }
1316
+ function interleave(left, right) {
1317
+ const loop = (l, r, pickLeft) => fromPull(
1318
+ asyncFold(
1319
+ uncons(pickLeft ? l : r),
1320
+ // Current stream ended — drain the other
1321
+ (_opt) => uncons(pickLeft ? r : l),
1322
+ ([a, tail]) => {
1323
+ const nextL = pickLeft ? tail : l;
1324
+ const nextR = pickLeft ? r : tail;
1325
+ return asyncSucceed([a, loop(nextL, nextR, !pickLeft)]);
1326
+ }
1327
+ )
1328
+ );
1329
+ return loop(left, right, true);
1330
+ }
1331
+ function take2(stream, n) {
1332
+ if (n <= 0) return emptyStream();
1333
+ const loop = (cur, remaining) => {
1334
+ if (remaining <= 0) return emptyStream();
1335
+ return fromPull(
1336
+ asyncFold(
1337
+ uncons(cur),
1338
+ (opt) => asyncFail(opt),
1339
+ ([a, tail]) => asyncSucceed([a, loop(tail, remaining - 1)])
1340
+ )
1341
+ );
1342
+ };
1343
+ return loop(stream, n);
1344
+ }
1345
+ function drop(stream, n) {
1346
+ if (n <= 0) return stream;
1347
+ const skip = (cur, remaining) => {
1348
+ if (remaining <= 0) return cur;
1349
+ return fromPull(
1350
+ asyncFold(
1351
+ uncons(cur),
1352
+ (opt) => asyncFail(opt),
1353
+ ([_a, tail]) => uncons(skip(tail, remaining - 1))
1354
+ )
1355
+ );
1356
+ };
1357
+ return skip(stream, n);
1358
+ }
1359
+
734
1360
  // src/core/runtime/engineStats.ts
735
- function engineStats(engine, data, fallbackUsed = false) {
736
- return { engine, data, fallbackUsed };
1361
+ function engineStats(engine, data) {
1362
+ return { engine, data, fallbackUsed: false };
737
1363
  }
738
1364
  function selectedEngineStats(requested, engine, data) {
739
- return { requested, engine, data, fallbackUsed: requested === "auto" && engine !== "wasm" };
1365
+ if (requested !== engine) {
1366
+ throw new Error(`brass-runtime strict engine mismatch: requested '${requested}', got '${engine}'`);
1367
+ }
1368
+ return { requested, engine, data, fallbackUsed: false };
740
1369
  }
741
1370
  export {
1371
+ ABI_VERSION,
742
1372
  Async,
743
1373
  Cause,
744
1374
  DefaultHostExecutor,
1375
+ EVENT_WORDS,
745
1376
  EngineFiberHandle,
1377
+ EventKindCode,
746
1378
  Exit,
747
1379
  HostRegistry,
748
1380
  JsFiberEngine,
1381
+ NONE_U32,
749
1382
  NoopHooks,
1383
+ OpcodeTagCode,
1384
+ PURE_PIPELINE_TAG,
750
1385
  ProgramBuilder,
751
1386
  PushStatus,
752
- ReferenceWasmBridge,
753
1387
  RingBuffer,
754
1388
  Runtime,
755
1389
  RuntimeFiber,
@@ -758,10 +1392,17 @@ export {
758
1392
  WasmFiberEngine,
759
1393
  WasmFiberRegistryBridge,
760
1394
  WasmPackFiberBridge,
1395
+ abortablePromiseStats,
761
1396
  acquireRelease,
762
- andThen,
1397
+ andThen2 as andThen,
1398
+ andThen as andThenSchedule,
1399
+ applyFused,
1400
+ assertCompletesWithin,
1401
+ assertFails,
1402
+ assertFailsWith,
763
1403
  assertNever,
764
- async,
1404
+ assertSucceeds,
1405
+ asyncEffect as async,
765
1406
  asyncCatchAll,
766
1407
  asyncFail,
767
1408
  asyncFlatMap,
@@ -773,25 +1414,43 @@ export {
773
1414
  asyncSync,
774
1415
  asyncTotal,
775
1416
  bounded,
1417
+ bracket,
776
1418
  broadcast,
777
1419
  broadcastToHub,
778
1420
  buffer,
779
1421
  bufferP,
780
1422
  catchAll,
1423
+ catchTag,
1424
+ catchTags,
781
1425
  chunks,
782
1426
  chunksP,
783
1427
  collectAllPar,
784
1428
  collectStream,
785
- compose,
1429
+ compose2 as compose,
1430
+ compose as composeLayer,
786
1431
  concatStream,
1432
+ debounce,
1433
+ decodeEvent,
1434
+ decodeEventBatch,
1435
+ delayedEffect,
1436
+ derivedRef,
1437
+ deserializeFusedPipeline,
787
1438
  dropP,
1439
+ drop as dropStream,
1440
+ elapsed,
788
1441
  emitStream,
789
1442
  emptyStream,
1443
+ encodeOpcodeNodes,
1444
+ encodeOpcodeProgram,
790
1445
  end,
791
1446
  engineStats,
1447
+ ensuring,
1448
+ exponential,
792
1449
  fail,
793
1450
  filterMapP,
794
1451
  filterP,
1452
+ fixed,
1453
+ flakyEffect,
795
1454
  flatMap,
796
1455
  flattenStream,
797
1456
  foreachStream,
@@ -800,16 +1459,41 @@ export {
800
1459
  fromHub,
801
1460
  fromPromiseAbortable,
802
1461
  fromPull,
1462
+ fuse,
803
1463
  getBenchmarkBudget,
804
1464
  getCurrentFiber,
1465
+ getStats,
805
1466
  globalScheduler,
1467
+ gracefulShutdown,
806
1468
  groupedP,
807
1469
  identity,
1470
+ inferCallerLaneFromStack,
1471
+ initState,
1472
+ interleave,
1473
+ intersect,
1474
+ isFusionEnabled,
1475
+ isFusionVerbose,
1476
+ jittered,
1477
+ laneTag,
1478
+ layer,
1479
+ layerFail,
1480
+ layerFrom,
1481
+ layerSucceed,
808
1482
  linkAbortController,
809
1483
  makeBoundedRingBuffer,
810
1484
  makeCancelToken,
1485
+ makeCircuitBreaker,
1486
+ makeFiberReadyQueue,
811
1487
  makeHub,
1488
+ makeMetrics,
1489
+ makeRef,
1490
+ makeSemaphore,
812
1491
  makeStreamChunker,
1492
+ makeTestRuntime,
1493
+ makeTracer,
1494
+ makeWorkerPool,
1495
+ managed,
1496
+ managedAll,
813
1497
  managedStream,
814
1498
  map,
815
1499
  mapAsync,
@@ -818,38 +1502,71 @@ export {
818
1502
  mapChunksEffectP,
819
1503
  mapEffectP,
820
1504
  mapError,
1505
+ mapError2 as mapErrorTyped,
1506
+ mapLayer,
821
1507
  mapP,
822
1508
  mapStream,
823
1509
  mapTryAsync,
824
- merge,
1510
+ merge2 as merge,
1511
+ merge as mergeLayer,
825
1512
  mergeStream,
1513
+ neverEffect,
826
1514
  none,
1515
+ orElse,
827
1516
  orElseOptional,
1517
+ provideLayer,
828
1518
  race,
829
1519
  raceWith,
830
1520
  rangeStream,
1521
+ recurs,
1522
+ registerShutdownHooks,
1523
+ repeatWithSchedule,
1524
+ resetAbortablePromiseStats,
1525
+ retry,
1526
+ retryN,
1527
+ retryWithBackoff,
1528
+ retryWithSchedule,
1529
+ runFusedArray,
831
1530
  runtimeCapabilities,
1531
+ runtimeForCaller,
1532
+ sanitizeLaneKey,
1533
+ scan,
832
1534
  selectedEngineStats,
1535
+ serializeFusedPipeline,
833
1536
  setBenchmarkBudget,
1537
+ setFusionEnabled,
1538
+ setFusionVerbose,
1539
+ sleep,
834
1540
  some,
835
1541
  streamFromReadableStream,
836
1542
  succeed,
837
1543
  sync,
1544
+ tagError,
838
1545
  takeP,
1546
+ take as takeSchedule,
1547
+ take2 as takeStream,
839
1548
  tapEffectP,
1549
+ throttle,
1550
+ timeout,
840
1551
  toPromise,
1552
+ toPromiseByCaller,
841
1553
  uncons,
1554
+ union,
842
1555
  unit,
843
1556
  unsafeGetCurrentRuntime,
844
1557
  unsafeRunAsync,
845
1558
  unsafeRunFoldWithEnv,
846
1559
  unwrapScoped,
1560
+ useManaged,
847
1561
  via,
1562
+ whileInput,
848
1563
  widenOpt,
849
1564
  withAsyncPromise,
850
1565
  withCurrentFiber,
851
1566
  withScope,
852
1567
  withScopeAsync,
853
1568
  zip,
854
- zipPar
1569
+ zipPar,
1570
+ zip2 as zipStream,
1571
+ zipWith
855
1572
  };