brass-runtime 1.16.0 → 1.17.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 (219) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +287 -23
  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-7XOPAB5Q.js → chunk-4P2HHGAX.mjs} +83 -5
  16. package/dist/{chunk-N6VHMOWB.cjs → chunk-4ROBZFL6.cjs} +128 -128
  17. package/dist/{chunk-NC5SDRYE.js → chunk-52OB2ROS.js} +4 -4
  18. package/dist/{chunk-JX3LZQJH.cjs → chunk-52PPNNI4.cjs} +82 -20
  19. package/dist/{chunk-5YOQOXEQ.cjs → chunk-5EC274J5.cjs} +676 -293
  20. package/dist/chunk-5QC7LRZ3.js +229 -0
  21. package/dist/{chunk-7TL2LHQJ.js → chunk-5VRJNBLZ.mjs} +524 -141
  22. package/dist/chunk-62AZW6UT.cjs +313 -0
  23. package/dist/chunk-6IXXWIUM.js +683 -0
  24. package/dist/chunk-6RY2FFN4.mjs +2024 -0
  25. package/dist/chunk-74ZTY6CP.js +2871 -0
  26. package/dist/chunk-7CMJS3QE.mjs +2871 -0
  27. package/dist/{chunk-2WC63LJK.mjs → chunk-7JIJOVCT.js} +20 -10
  28. package/dist/chunk-7X3K5RMS.js +2024 -0
  29. package/dist/chunk-7ZPEZ57L.cjs +2024 -0
  30. package/dist/{chunk-FM4W4QPL.js → chunk-A2OM6NEH.mjs} +5 -4
  31. package/dist/chunk-AGR5B2BC.cjs +683 -0
  32. package/dist/chunk-B33ICAKP.js +313 -0
  33. package/dist/{chunk-J3H54ZRV.mjs → chunk-B5JD23U7.mjs} +1 -1
  34. package/dist/{chunk-F5EUMJL7.mjs → chunk-BKK77SBA.js} +83 -5
  35. package/dist/{chunk-U5KWK3PX.mjs → chunk-C3MDXTRZ.js} +11 -0
  36. package/dist/{chunk-SPUEME2B.cjs → chunk-CZIVE6NT.cjs} +12 -1
  37. package/dist/{chunk-TDVMADDN.js → chunk-DNFJLJMW.mjs} +11 -0
  38. package/dist/{chunk-XDZOO4L5.js → chunk-EJ6BPYVR.mjs} +79 -17
  39. package/dist/chunk-EOC4UHBS.mjs +229 -0
  40. package/dist/chunk-F6XWZQY4.cjs +777 -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-TVN5I4U6.cjs → chunk-JF5WGYJJ.cjs} +25 -24
  46. package/dist/{chunk-CY33PGEX.mjs → chunk-KH4SYAOS.mjs} +570 -51
  47. package/dist/chunk-KN32XNTH.mjs +313 -0
  48. package/dist/chunk-KQLYONSE.cjs +2871 -0
  49. package/dist/{chunk-7HUOJA4W.cjs → chunk-KZJQ723N.cjs} +90 -80
  50. package/dist/{chunk-CCKHV5BT.mjs → chunk-L2SYFEBS.js} +5 -4
  51. package/dist/{chunk-IJT6RRQ5.cjs → chunk-L6VB5N7Q.cjs} +20 -9
  52. package/dist/{chunk-ZGLD4TVZ.mjs → chunk-MBEJI5HF.mjs} +4 -4
  53. package/dist/{chunk-PRWCB3QL.mjs → chunk-MIIYDLGM.js} +524 -141
  54. package/dist/{chunk-H55LI6WY.js → chunk-MOO4L7F4.mjs} +15 -4
  55. package/dist/chunk-MVGUEJ5Z.cjs +370 -0
  56. package/dist/chunk-PD4EJTQC.cjs +229 -0
  57. package/dist/chunk-PWC3RBQE.mjs +300 -0
  58. package/dist/{chunk-MWXMNYJS.cjs → chunk-Q2I37RP3.cjs} +643 -124
  59. package/dist/{chunk-VFIUZG7J.mjs → chunk-RKGKFN2A.js} +79 -17
  60. package/dist/{chunk-NYL4D7SK.cjs → chunk-SA6HUJVI.cjs} +5 -5
  61. package/dist/chunk-SK7UZRNI.mjs +777 -0
  62. package/dist/{chunk-K2T3DV26.mjs → chunk-TRM4JUZQ.js} +15 -4
  63. package/dist/chunk-UB4B6OFY.js +370 -0
  64. package/dist/{chunk-G3XGCZDQ.js → chunk-UCUBNWM2.js} +1 -1
  65. package/dist/chunk-VWIPB6I5.js +777 -0
  66. package/dist/{chunk-JNFRRJYH.cjs → chunk-WBGRHGBP.cjs} +270 -192
  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 +18 -531
  89. package/dist/observability/index.d.ts +81 -8
  90. package/dist/observability/index.js +25 -538
  91. package/dist/observability/index.mjs +25 -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-D6JZ15_e.d.ts} +16 -4
  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 +65 -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 +339 -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/framework-integrations.md +38 -0
  165. package/docs/frameworks/angular.md +153 -0
  166. package/docs/frameworks/express.md +125 -0
  167. package/docs/frameworks/fastify.md +124 -0
  168. package/docs/frameworks/nestjs.md +282 -0
  169. package/docs/frameworks/nextjs.md +147 -0
  170. package/docs/frameworks/react.md +139 -0
  171. package/docs/frameworks/vanilla.md +224 -0
  172. package/docs/getting-started.md +159 -0
  173. package/docs/guides/README.md +40 -0
  174. package/docs/guides/circuit-breaker.md +89 -0
  175. package/docs/guides/error-handling.md +91 -0
  176. package/docs/guides/getting-started.md +107 -0
  177. package/docs/guides/layers.md +189 -0
  178. package/docs/guides/metrics.md +101 -0
  179. package/docs/guides/resource-management.md +141 -0
  180. package/docs/guides/retry.md +215 -0
  181. package/docs/guides/semaphore.md +66 -0
  182. package/docs/guides/streams.md +117 -0
  183. package/docs/guides/supervisors.md +98 -0
  184. package/docs/guides/testing.md +162 -0
  185. package/docs/guides/tracing.md +71 -0
  186. package/docs/http-recipes.md +399 -0
  187. package/docs/http.md +749 -0
  188. package/docs/modules.md +285 -0
  189. package/docs/nestjs.md +6 -0
  190. package/docs/observability-collector-smoke.md +31 -0
  191. package/docs/observability-framework-examples.md +110 -0
  192. package/docs/observability.md +649 -0
  193. package/docs/otel-collector-smoke.yaml +27 -0
  194. package/docs/performance-profiler.md +199 -0
  195. package/docs/production-readiness.md +73 -0
  196. package/docs/recipes/README.md +12 -0
  197. package/docs/recipes/http-server.md +45 -0
  198. package/docs/recipes/layers.md +44 -0
  199. package/docs/recipes/performance.md +47 -0
  200. package/docs/recipes/runtime.md +41 -0
  201. package/docs/recipes/testing.md +41 -0
  202. package/docs/release.md +53 -0
  203. package/docs/wasm-bounded-queues.md +44 -0
  204. package/docs/wasm-engine-observability-benchmarks.md +85 -0
  205. package/docs/wasm-fiber-engine.md +117 -0
  206. package/docs/wasm-scheduler-state-machine.md +122 -0
  207. package/docs/wasm-stream-chunks.md +54 -0
  208. package/package.json +22 -2
  209. package/dist/chunk-45F7OKGT.cjs +0 -104
  210. package/dist/chunk-7V4KY4RL.mjs +0 -104
  211. package/dist/chunk-DJQ7OMMB.cjs +0 -144
  212. package/dist/chunk-GOV47PPB.mjs +0 -552
  213. package/dist/chunk-JF4XXPZ5.cjs +0 -552
  214. package/dist/chunk-KCPT2D6G.js +0 -552
  215. package/dist/chunk-NOYZIMUJ.mjs +0 -144
  216. package/dist/chunk-PNVFW245.js +0 -144
  217. package/dist/chunk-ROJC3NBJ.js +0 -104
  218. package/dist/effectRunner-3ZHAD3LE.cjs +0 -8
  219. package/dist/schedule-Fque9Abz.d.ts +0 -70
@@ -0,0 +1,117 @@
1
+ # Brass Runtime WASM Fiber Engine
2
+
3
+ Este repo mantiene la estructura original de `brass-runtime`: el package se publica desde la raíz del repositorio, no desde `packages/brass-runtime`.
4
+
5
+ ## Qué cambió
6
+
7
+ Se agregó un engine interno experimental para mover el estado de suspensión de las fibras hacia un bridge WASM:
8
+
9
+ - `src/core/runtime/engine/types.ts`: contrato `FiberEngine`.
10
+ - `src/core/runtime/engine/JsFiberEngine.ts`: fallback JS que usa el `RuntimeFiber` actual.
11
+ - `src/core/runtime/engine/WasmFiberEngine.ts`: engine experimental JS/WASM.
12
+ - `src/core/runtime/engine/opcodes.ts`: representación interna del `Async` actual como plan/opcodes.
13
+ - `src/core/runtime/engine/bridge/ReferenceWasmBridge.ts`: bridge de referencia, sin compilar Rust, útil para tests locales.
14
+ - `src/core/runtime/engine/bridge/WasmPackFiberBridge.ts`: adapter para el output de `wasm-pack`.
15
+ - `src/core/runtime/hostAction.ts`: efectos host declarativos HTTP/DB/Queue/custom.
16
+ - `crates/brass-runtime-wasm-engine`: crate Rust/WASM.
17
+ - `src/core/runtime/bench/heap-per-suspended-fiber.ts`: benchmark de heap por fibra suspendida.
18
+
19
+ ## Uso
20
+
21
+ El modo por defecto sigue siendo JS:
22
+
23
+ ```ts
24
+ const runtime = Runtime.make({});
25
+ ```
26
+
27
+ Modo experimental sin compilar Rust:
28
+
29
+ ```ts
30
+ const runtime = new Runtime({
31
+ env: {},
32
+ engine: "wasm-reference",
33
+ });
34
+ ```
35
+
36
+ Modo WASM real:
37
+
38
+ ```ts
39
+ const runtime = new Runtime({
40
+ env: {},
41
+ engine: "wasm",
42
+ });
43
+ ```
44
+
45
+ ## Host actions declarativas
46
+
47
+ ```ts
48
+ import { Runtime, fromHostAction } from "brass-runtime";
49
+
50
+ const runtime = new Runtime({
51
+ env: {},
52
+ engine: "wasm-reference",
53
+ hostExecutor: {
54
+ async execute(action, ctx) {
55
+ // Acá vive fetch/axios/db/queue real.
56
+ return { kind: "ok", value: { ok: true } };
57
+ },
58
+ },
59
+ });
60
+
61
+ const result = await runtime.toPromise(
62
+ fromHostAction({ kind: "custom", target: "my-side-effect" })
63
+ );
64
+ ```
65
+
66
+ Con `fromPromiseAbortable`, la closure host sigue viviendo en JS porque Axios/fetch/DB viven en Node. El engine WASM reduce la parte de continuación/scheduler/fiber state de Brass. Para reducir más heap por suspensión, usá `fromHostAction`, que representa el efecto como acción declarativa y deja el side effect real en el `HostExecutor`.
67
+
68
+ ## Publicación desde este repo
69
+
70
+ ```bash
71
+ npm install
72
+ npm run build
73
+ npm pack --dry-run
74
+ npm publish
75
+ ```
76
+
77
+ Si querés publicar con el `.wasm` real incluido:
78
+
79
+ ```bash
80
+ cargo --version
81
+ wasm-pack --version
82
+ npm run build:wasm
83
+ npm run build
84
+ npm pack --dry-run
85
+ npm publish
86
+ ```
87
+
88
+ ## Benchmark
89
+
90
+ ```bash
91
+ npm run benchmark heap-suspended-fiber
92
+ ```
93
+
94
+ The direct benchmark script is still available when you want a specific engine
95
+ or longer suspension window:
96
+
97
+ ```bash
98
+ node --expose-gc ./node_modules/.bin/tsx src/core/runtime/bench/heap-per-suspended-fiber.ts \
99
+ --engine wasm \
100
+ --mode host-action \
101
+ --fibers 10000 \
102
+ --delayMs 2000
103
+ node --expose-gc ./node_modules/.bin/tsx src/core/runtime/bench/heap-per-suspended-fiber.ts --engine ts --mode closure
104
+ ```
105
+
106
+ ## WASM fiber registry + wakeups
107
+
108
+ The WASM engine now also owns a compact `BrassWasmFiberRegistry` used by the TS engine facade to track fiber lifecycle metadata and coalesce wakeups.
109
+
110
+ The public TypeScript `Runtime` API is unchanged. When `engine: "wasm"` is selected:
111
+
112
+ - TS still executes host callbacks, promises, finalizers and Node integrations.
113
+ - Rust/WASM tracks fiber states: queued, running, suspended, done, failed and interrupted.
114
+ - Rust/WASM owns a wakeup queue so repeated wakeups for the same fiber are coalesced before TS schedules more work.
115
+ - `runtime.stats().fiberRegistry` exposes registry counters for observability.
116
+
117
+ This keeps the high-level Brass API ergonomic while moving another hot coordination path into a compact Rust state machine.
@@ -0,0 +1,122 @@
1
+ # WASM scheduler state machine
2
+
3
+ The scheduler bookkeeping hot path lives in `brass-runtime-wasm-engine` when the scheduler is created with `engine: "wasm"` or `engine: "auto"` and the WASM package is available. The public TypeScript scheduler API remains unchanged.
4
+
5
+ ## What moved to Rust/WASM
6
+
7
+ - scheduler phase: `idle`, `scheduledMicro`, `scheduledMacro`, `flushing`
8
+ - inferred caller lanes from task tags
9
+ - one bounded task-ref queue per lane
10
+ - round-robin lane rotation
11
+ - per-lane execution budget
12
+ - global flush budget accounting
13
+ - micro vs macro scheduling policy
14
+ - global counters for enqueued/executed/dropped tasks
15
+ - per-lane counters for enqueued/executed/dropped tasks
16
+ - counters for budget yields and flush cycles
17
+
18
+ ## What stays in TypeScript
19
+
20
+ - the actual task callbacks
21
+ - `queueMicrotask`, `setImmediate`, `MessageChannel`, `setTimeout`
22
+ - error logging
23
+ - Node/browser integration
24
+
25
+ This is intentional: WASM coordinates scheduler state and returns task refs, but JS still owns and executes side effects.
26
+
27
+ ## Lane inference
28
+
29
+ Rust and TypeScript now use the same tag convention:
30
+
31
+ ```txt
32
+ lane:<caller>|<task-label>
33
+ caller:<caller>|<task-label>
34
+ <fallback-prefix>#<id>.<task-label>
35
+ ```
36
+
37
+ `Runtime.withLane("bff-security/generate")` ultimately schedules fibers with a `lane:` tag, so Brass does not need to know anything about the BFF implementation. It only sees a stable caller key.
38
+
39
+ When no explicit lane is set, the TypeScript runtime infers a lane once from the first non-Brass callsite and then passes that stable lane to JS or WASM scheduler state. Child fibers inherit their parent lane.
40
+
41
+ ## Bounded queues and budgets
42
+
43
+ ```ts
44
+ const scheduler = new Scheduler({
45
+ engine: "wasm",
46
+ laneCapacity: 512,
47
+ laneBudget: 32,
48
+ flushBudget: 1024,
49
+ maxLanes: 256,
50
+ });
51
+ ```
52
+
53
+ Semantics:
54
+
55
+ - `laneCapacity`: max queued task refs per lane. When a lane is full, the newly enqueued task is dropped, `droppedTasks` increments, and TS maps the Rust policy to `Scheduler.schedule() === "dropped"`. Runtime fibers fail fast rather than hanging.
56
+ - `laneBudget`: max tasks a single lane may run before the scheduler rotates to the next non-empty lane.
57
+ - `flushBudget`: max tasks per flush before yielding back to the event loop.
58
+ - `maxLanes`: max distinct inferred lanes before new unknown callers are grouped into `overflow`.
59
+
60
+ ## Usage
61
+
62
+ ```ts
63
+ const scheduler = new Scheduler({ engine: "wasm" });
64
+ const runtime = Runtime.make({}, scheduler).withLane("bff-security/validate");
65
+
66
+ await runtime.toPromise(effect);
67
+ ```
68
+
69
+ For production-safe behavior use auto mode:
70
+
71
+ ```ts
72
+ const scheduler = new Scheduler({ engine: "auto" });
73
+ ```
74
+
75
+ `auto` uses WASM when `wasm/pkg/brass_runtime_wasm_engine.js` is available and falls back to the JS implementation otherwise.
76
+
77
+ ## Introspection
78
+
79
+ ```ts
80
+ console.log(scheduler.stats());
81
+ ```
82
+
83
+ The WASM path returns counters from the Rust state machine:
84
+
85
+ ```ts
86
+ {
87
+ engine: "wasm",
88
+ fallbackUsed: false,
89
+ data: {
90
+ phase: "idle",
91
+ len: 0,
92
+ capacity: 512,
93
+ scheduledFlushes: 10,
94
+ completedFlushes: 10,
95
+ enqueuedTasks: 1000,
96
+ executedTasks: 1000,
97
+ droppedTasks: 0,
98
+ yieldedByBudget: 0,
99
+ lanes: [
100
+ {
101
+ key: "bff-security/validate",
102
+ len: 0,
103
+ capacity: 512,
104
+ enqueuedTasks: 1000,
105
+ executedTasks: 1000,
106
+ droppedTasks: 0
107
+ }
108
+ ]
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## Build
114
+
115
+ After changing Rust, rebuild the WASM package locally:
116
+
117
+ ```bash
118
+ npm run build:wasm
119
+ npm run build
120
+ ```
121
+
122
+ The TypeScript changes compile without requiring the WASM artifact, but `engine: "wasm"` requires the generated `wasm/pkg` output at runtime.
@@ -0,0 +1,54 @@
1
+ # WASM stream chunks
2
+
3
+ This adds an optional WASM-backed chunk buffer for `ZStream`.
4
+
5
+ The goal is not to move all stream semantics to Rust. The goal is to make the hot path chunk-aware so downstream operators can process arrays/batches instead of one element at a time.
6
+
7
+ ## API
8
+
9
+ ```ts
10
+ import { chunks, chunksP, mapChunksEffectP } from "brass-runtime";
11
+ ```
12
+
13
+ ### Direct stream API
14
+
15
+ ```ts
16
+ const chunked = chunks(stream, 1024, { engine: "wasm" });
17
+ ```
18
+
19
+ `chunked` has type:
20
+
21
+ ```ts
22
+ ZStream<R, E, readonly A[]>
23
+ ```
24
+
25
+ ### Pipeline API
26
+
27
+ ```ts
28
+ const batched = via(stream, chunksP<number>(1024, { engine: "wasm" }));
29
+ ```
30
+
31
+ ### Effectful chunk mapping
32
+
33
+ ```ts
34
+ const processed = via(
35
+ stream,
36
+ mapChunksEffectP(1024, (chunk) =>
37
+ asyncSucceed(chunk.map((n) => n * 2))
38
+ , { engine: "wasm" })
39
+ );
40
+ ```
41
+
42
+ ## Engine modes
43
+
44
+ ```ts
45
+ { engine: "js" } // always JS arrays
46
+ { engine: "wasm" } // require wasm/pkg
47
+ { engine: "auto" } // WASM when available, otherwise JS fallback
48
+ ```
49
+
50
+ ## Why this shape
51
+
52
+ Crossing Node/JS ↔ WASM for every element can be slower than plain JS. This implementation makes WASM useful as a chunk boundary and gives the rest of the stream pipeline a batch-oriented representation.
53
+
54
+ The next optimization step would be to add specialized Rust operations for primitive chunks, for example numeric `sum`, `min`, `max`, `filterGt`, or binary/typed-array transforms. Generic JS callbacks should stay in TS.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brass-runtime",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Effect runtime utilities for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Augusto Vivaldelli",
@@ -38,6 +38,11 @@
38
38
  "import": "./dist/observability/index.mjs",
39
39
  "require": "./dist/observability/index.cjs"
40
40
  },
41
+ "./perf": {
42
+ "types": "./dist/perf/index.d.ts",
43
+ "import": "./dist/perf/index.mjs",
44
+ "require": "./dist/perf/index.cjs"
45
+ },
41
46
  "./agent": {
42
47
  "types": "./dist/agent/index.d.ts",
43
48
  "import": "./dist/agent/index.mjs",
@@ -51,6 +56,8 @@
51
56
  "dist",
52
57
  "wasm/pkg",
53
58
  "README.md",
59
+ "CHANGELOG.md",
60
+ "docs",
54
61
  "LICENSE",
55
62
  "package.json"
56
63
  ],
@@ -106,13 +113,25 @@
106
113
  "agent:vscode:reinstall:global": "npm run agent:vscode:uninstall:global && npm run agent:vscode:install:global",
107
114
  "benchmark": "tsx src/benchmarks/runner.ts",
108
115
  "benchmark:json": "tsx src/benchmarks/runner.ts --json",
116
+ "benchmark:runtime": "tsx src/benchmarks/runner.ts runtime-performance-track",
117
+ "benchmark:runtime:budget": "node scripts/check-runtime-benchmark-budget.mjs",
109
118
  "benchmark:http": "tsx src/benchmarks/runner.ts http-concurrent",
110
119
  "benchmark:http:soak": "BRASS_HTTP_BENCH_MODE=soak tsx src/benchmarks/runner.ts http-concurrent",
111
120
  "benchmark:http:budget": "node scripts/check-http-benchmark-budget.mjs",
121
+ "benchmark:http:ramp": "tsx src/benchmarks/runner.ts http-ramp-tps",
112
122
  "benchmark:adaptive": "tsx src/benchmarks/runner.ts adaptive-limiter-soak",
113
123
  "benchmark:adaptive:soak": "BRASS_ADAPTIVE_BENCH_MODE=soak tsx src/benchmarks/runner.ts adaptive-limiter-soak",
114
124
  "benchmark:observability": "tsx src/benchmarks/runner.ts observability-overhead",
115
125
  "benchmark:observability:budget": "node scripts/check-observability-benchmark-budget.mjs",
126
+ "benchmark:perf": "tsx src/perf/cli.ts --json",
127
+ "perf": "tsx src/perf/cli.ts",
128
+ "perf:json": "tsx src/perf/cli.ts --json",
129
+ "perf:runtime:ab": "tsx src/perf/cli.ts --profile runtime-ab",
130
+ "perf:runtime:soak": "tsx src/perf/cli.ts --profile runtime-soak",
131
+ "perf:runtime:budget": "tsx src/perf/budgetCli.ts",
132
+ "perf:http:memory": "tsx src/perf/cli.ts --profile http-memory",
133
+ "perf:history": "tsx src/perf/cli.ts --record-history",
134
+ "release:check": "npm run test:types && npm test && npm run build:ts && npm run validate:cjs && npm run perf:runtime:budget && npm run benchmark:runtime:budget && npm run benchmark:http:budget && npm run benchmark:observability:budget",
116
135
  "example:observability:express": "tsx src/examples/observabilityExpress.ts",
117
136
  "example:observability:fastify": "tsx src/examples/observabilityFastify.ts",
118
137
  "example:observability:nest": "tsx src/examples/observabilityNest.ts",
@@ -143,6 +162,7 @@
143
162
  },
144
163
  "homepage": "https://github.com/BaldrVivaldelli/brass-runtime#readme",
145
164
  "bin": {
146
- "brass-agent": "dist/agent/cli/main.cjs"
165
+ "brass-agent": "dist/agent/cli/main.cjs",
166
+ "brass-perf": "dist/perf/cli.cjs"
147
167
  }
148
168
  }
@@ -1,104 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
-
3
- var _chunkWQ5QNU5Rcjs = require('./chunk-WQ5QNU5R.cjs');
4
-
5
-
6
-
7
-
8
-
9
-
10
- var _chunkDJQ7OMMBcjs = require('./chunk-DJQ7OMMB.cjs');
11
-
12
- // src/core/runtime/combinators.ts
13
- function sleep(ms) {
14
- return _chunkDJQ7OMMBcjs.asyncEffect.call(void 0, (_env, cb) => {
15
- const delay = Math.max(0, Math.floor(ms));
16
- const id = setTimeout(() => cb({ _tag: "Success", value: void 0 }), delay);
17
- return () => clearTimeout(id);
18
- });
19
- }
20
- function timeout(effect, ms) {
21
- return _chunkDJQ7OMMBcjs.asyncEffect.call(void 0, (env, cb) => {
22
- let done = false;
23
- let timerId;
24
- let effectRunning = true;
25
- timerId = setTimeout(() => {
26
- if (done) return;
27
- done = true;
28
- effectRunning = false;
29
- cb({
30
- _tag: "Failure",
31
- cause: { _tag: "Fail", error: { _tag: "TimeoutError", ms } }
32
- });
33
- }, Math.max(0, Math.floor(ms)));
34
- const runtime = _chunkWQ5QNU5Rcjs.unsafeGetCurrentRuntime.call(void 0, );
35
- const fiber = runtime.fork(effect);
36
- fiber.join((exit) => {
37
- if (done) return;
38
- done = true;
39
- clearTimeout(timerId);
40
- cb(exit);
41
- });
42
- return () => {
43
- if (done) return;
44
- done = true;
45
- clearTimeout(timerId);
46
- fiber.interrupt();
47
- };
48
- });
49
- }
50
- function retry(effect, policy) {
51
- const shouldRetry = _nullishCoalesce(policy.shouldRetry, () => ( (() => true)));
52
- const jitter = _nullishCoalesce(policy.jitter, () => ( "full"));
53
- const maxElapsedMs = policy.maxElapsedMs;
54
- const computeDelay = (attempt) => {
55
- const exp = policy.baseDelayMs * Math.pow(2, attempt);
56
- const capped = Math.min(exp, policy.maxDelayMs);
57
- if (jitter === "none") return capped;
58
- return Math.floor(Math.random() * capped);
59
- };
60
- const loop = (attempt, startedAt) => _chunkDJQ7OMMBcjs.asyncFold.call(void 0,
61
- effect,
62
- (error) => {
63
- if (attempt >= policy.maxRetries) return _chunkDJQ7OMMBcjs.asyncFail.call(void 0, error);
64
- if (!shouldRetry(error, attempt)) return _chunkDJQ7OMMBcjs.asyncFail.call(void 0, error);
65
- if (maxElapsedMs !== void 0) {
66
- const elapsed = performance.now() - startedAt;
67
- if (elapsed >= maxElapsedMs) return _chunkDJQ7OMMBcjs.asyncFail.call(void 0, error);
68
- }
69
- const delay = computeDelay(attempt);
70
- return _chunkDJQ7OMMBcjs.asyncFlatMap.call(void 0, sleep(delay), () => loop(attempt + 1, startedAt));
71
- },
72
- (value) => _chunkDJQ7OMMBcjs.asyncSucceed.call(void 0, value)
73
- );
74
- return _chunkDJQ7OMMBcjs.asyncFlatMap.call(void 0,
75
- { _tag: "Sync", thunk: () => performance.now() },
76
- (startedAt) => loop(0, startedAt)
77
- );
78
- }
79
- function retryN(effect, n) {
80
- return retry(effect, {
81
- maxRetries: n,
82
- baseDelayMs: 0,
83
- maxDelayMs: 0,
84
- jitter: "none"
85
- });
86
- }
87
- function retryWithBackoff(effect, opts = {}) {
88
- return retry(effect, {
89
- maxRetries: _nullishCoalesce(opts.maxRetries, () => ( 3)),
90
- baseDelayMs: _nullishCoalesce(opts.baseDelayMs, () => ( 100)),
91
- maxDelayMs: _nullishCoalesce(opts.maxDelayMs, () => ( 1e4)),
92
- maxElapsedMs: opts.maxElapsedMs,
93
- shouldRetry: opts.shouldRetry,
94
- jitter: "full"
95
- });
96
- }
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
- exports.sleep = sleep; exports.timeout = timeout; exports.retry = retry; exports.retryN = retryN; exports.retryWithBackoff = retryWithBackoff;
@@ -1,104 +0,0 @@
1
- import {
2
- unsafeGetCurrentRuntime
3
- } from "./chunk-G6IQOE4P.mjs";
4
- import {
5
- asyncEffect,
6
- asyncFail,
7
- asyncFlatMap,
8
- asyncFold,
9
- asyncSucceed
10
- } from "./chunk-NOYZIMUJ.mjs";
11
-
12
- // src/core/runtime/combinators.ts
13
- function sleep(ms) {
14
- return asyncEffect((_env, cb) => {
15
- const delay = Math.max(0, Math.floor(ms));
16
- const id = setTimeout(() => cb({ _tag: "Success", value: void 0 }), delay);
17
- return () => clearTimeout(id);
18
- });
19
- }
20
- function timeout(effect, ms) {
21
- return asyncEffect((env, cb) => {
22
- let done = false;
23
- let timerId;
24
- let effectRunning = true;
25
- timerId = setTimeout(() => {
26
- if (done) return;
27
- done = true;
28
- effectRunning = false;
29
- cb({
30
- _tag: "Failure",
31
- cause: { _tag: "Fail", error: { _tag: "TimeoutError", ms } }
32
- });
33
- }, Math.max(0, Math.floor(ms)));
34
- const runtime = unsafeGetCurrentRuntime();
35
- const fiber = runtime.fork(effect);
36
- fiber.join((exit) => {
37
- if (done) return;
38
- done = true;
39
- clearTimeout(timerId);
40
- cb(exit);
41
- });
42
- return () => {
43
- if (done) return;
44
- done = true;
45
- clearTimeout(timerId);
46
- fiber.interrupt();
47
- };
48
- });
49
- }
50
- function retry(effect, policy) {
51
- const shouldRetry = policy.shouldRetry ?? (() => true);
52
- const jitter = policy.jitter ?? "full";
53
- const maxElapsedMs = policy.maxElapsedMs;
54
- const computeDelay = (attempt) => {
55
- const exp = policy.baseDelayMs * Math.pow(2, attempt);
56
- const capped = Math.min(exp, policy.maxDelayMs);
57
- if (jitter === "none") return capped;
58
- return Math.floor(Math.random() * capped);
59
- };
60
- const loop = (attempt, startedAt) => asyncFold(
61
- effect,
62
- (error) => {
63
- if (attempt >= policy.maxRetries) return asyncFail(error);
64
- if (!shouldRetry(error, attempt)) return asyncFail(error);
65
- if (maxElapsedMs !== void 0) {
66
- const elapsed = performance.now() - startedAt;
67
- if (elapsed >= maxElapsedMs) return asyncFail(error);
68
- }
69
- const delay = computeDelay(attempt);
70
- return asyncFlatMap(sleep(delay), () => loop(attempt + 1, startedAt));
71
- },
72
- (value) => asyncSucceed(value)
73
- );
74
- return asyncFlatMap(
75
- { _tag: "Sync", thunk: () => performance.now() },
76
- (startedAt) => loop(0, startedAt)
77
- );
78
- }
79
- function retryN(effect, n) {
80
- return retry(effect, {
81
- maxRetries: n,
82
- baseDelayMs: 0,
83
- maxDelayMs: 0,
84
- jitter: "none"
85
- });
86
- }
87
- function retryWithBackoff(effect, opts = {}) {
88
- return retry(effect, {
89
- maxRetries: opts.maxRetries ?? 3,
90
- baseDelayMs: opts.baseDelayMs ?? 100,
91
- maxDelayMs: opts.maxDelayMs ?? 1e4,
92
- maxElapsedMs: opts.maxElapsedMs,
93
- shouldRetry: opts.shouldRetry,
94
- jitter: "full"
95
- });
96
- }
97
-
98
- export {
99
- sleep,
100
- timeout,
101
- retry,
102
- retryN,
103
- retryWithBackoff
104
- };