brass-runtime 1.15.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +673 -136
- package/dist/agent/cli/main.cjs +40 -35
- package/dist/agent/cli/main.js +9 -4
- package/dist/agent/cli/main.mjs +9 -4
- package/dist/agent/index.cjs +8 -4
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +7 -3
- package/dist/agent/index.mjs +7 -3
- package/dist/chunk-2HQTDLHF.mjs +683 -0
- package/dist/chunk-36I3M4UC.mjs +370 -0
- package/dist/chunk-3AYM6WPJ.js +1629 -0
- package/dist/chunk-3LOYJFRR.cjs +300 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-3Y2RIUMM.js +300 -0
- package/dist/{chunk-VEZNF5GZ.cjs → chunk-4ROBZFL6.cjs} +130 -126
- package/dist/{chunk-3QMOKAS5.js → chunk-52OB2ROS.js} +9 -5
- package/dist/chunk-52PPNNI4.cjs +416 -0
- package/dist/chunk-5EC274J5.cjs +2874 -0
- package/dist/chunk-5QC7LRZ3.js +229 -0
- package/dist/chunk-5VRJNBLZ.mjs +2874 -0
- package/dist/chunk-62AZW6UT.cjs +313 -0
- package/dist/chunk-6IXXWIUM.js +683 -0
- package/dist/chunk-74ZTY6CP.js +2871 -0
- package/dist/chunk-76YMRMH2.cjs +777 -0
- package/dist/chunk-7CMJS3QE.mjs +2871 -0
- package/dist/{chunk-4NHES7VK.mjs → chunk-7JIJOVCT.js} +27 -13
- package/dist/chunk-A2OM6NEH.mjs +194 -0
- package/dist/chunk-AGR5B2BC.cjs +683 -0
- package/dist/chunk-AVNQLJ5V.js +777 -0
- package/dist/chunk-B33ICAKP.js +313 -0
- package/dist/{chunk-ELOOF35R.mjs → chunk-B5JD23U7.mjs} +1 -1
- package/dist/chunk-BABBZK4Y.js +2024 -0
- package/dist/chunk-C3MDXTRZ.js +354 -0
- package/dist/chunk-CIZFIMK5.js +2193 -0
- package/dist/chunk-CZIVE6NT.cjs +354 -0
- package/dist/chunk-DNFJLJMW.mjs +354 -0
- package/dist/chunk-DNFO2EIZ.mjs +777 -0
- package/dist/chunk-EJ6BPYVR.mjs +416 -0
- package/dist/chunk-ENKODRU3.cjs +2193 -0
- package/dist/chunk-EOC4UHBS.mjs +229 -0
- package/dist/{chunk-BMH5AV44.js → chunk-FH2X7BVP.js} +756 -440
- package/dist/{chunk-PPUXIH5R.js → chunk-FHQGHPMO.mjs} +27 -13
- package/dist/{chunk-TGIFUAK4.cjs → chunk-GLE2WY7Z.cjs} +951 -635
- package/dist/{chunk-BDF4AMWX.mjs → chunk-GYM3LLGS.mjs} +756 -440
- package/dist/chunk-HLWLMW2F.mjs +2024 -0
- package/dist/chunk-JF5WGYJJ.cjs +194 -0
- package/dist/chunk-KH4SYAOS.mjs +1629 -0
- package/dist/chunk-KN32XNTH.mjs +313 -0
- package/dist/chunk-KQLYONSE.cjs +2871 -0
- package/dist/{chunk-STVLQ3XD.cjs → chunk-KZJQ723N.cjs} +92 -78
- package/dist/chunk-L2SYFEBS.js +194 -0
- package/dist/chunk-L6VB5N7Q.cjs +104 -0
- package/dist/{chunk-K6M7MDZ4.mjs → chunk-MBEJI5HF.mjs} +9 -5
- package/dist/chunk-MIIYDLGM.js +2874 -0
- package/dist/chunk-MOO4L7F4.mjs +104 -0
- package/dist/chunk-MT3OWDPC.mjs +2193 -0
- package/dist/chunk-MVGUEJ5Z.cjs +370 -0
- package/dist/chunk-OBGZSXTJ.cjs +10 -0
- package/dist/chunk-PD4EJTQC.cjs +229 -0
- package/dist/chunk-PWC3RBQE.mjs +300 -0
- package/dist/chunk-Q2I37RP3.cjs +1629 -0
- package/dist/chunk-RKGKFN2A.js +416 -0
- package/dist/{chunk-R3R2FVLG.cjs → chunk-SA6HUJVI.cjs} +5 -5
- package/dist/chunk-TRM4JUZQ.js +104 -0
- package/dist/chunk-UB4B6OFY.js +370 -0
- package/dist/{chunk-TO7IKXYT.js → chunk-UCUBNWM2.js} +1 -1
- package/dist/chunk-VN44DYYT.cjs +2024 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/client-CZHU674n.d.ts +820 -0
- package/dist/core/index.cjs +198 -4
- package/dist/core/index.d.ts +311 -212
- package/dist/core/index.js +237 -43
- package/dist/core/index.mjs +237 -43
- package/dist/{effect-CMOQKX8y.d.ts → effect-DIUHZ9IN.d.ts} +195 -1
- package/dist/effectRunner-CFLC32IK.cjs +8 -0
- package/dist/effectRunner-L4S7IPT3.js +8 -0
- package/dist/effectRunner-NNGG75QA.mjs +8 -0
- package/dist/http/index.cjs +1227 -2971
- package/dist/http/index.d.ts +826 -280
- package/dist/http/index.js +1089 -2833
- package/dist/http/index.mjs +1089 -2833
- package/dist/http/testing.cjs +161 -0
- package/dist/http/testing.d.ts +43 -0
- package/dist/http/testing.js +161 -0
- package/dist/http/testing.mjs +161 -0
- package/dist/index.cjs +486 -250
- package/dist/index.d.ts +87 -95
- package/dist/index.js +391 -155
- package/dist/index.mjs +391 -155
- package/dist/observability/index.cjs +162 -0
- package/dist/observability/index.d.ts +152 -0
- package/dist/observability/index.js +162 -0
- package/dist/observability/index.mjs +162 -0
- package/dist/perf/cli.cjs +401 -0
- package/dist/perf/cli.d.ts +1 -0
- package/dist/perf/cli.js +401 -0
- package/dist/perf/cli.mjs +401 -0
- package/dist/perf/index.cjs +141 -0
- package/dist/perf/index.d.ts +483 -0
- package/dist/perf/index.js +141 -0
- package/dist/perf/index.mjs +141 -0
- package/dist/schedule-CK3Ml_7p.d.ts +259 -0
- package/dist/schema/index.cjs +29 -0
- package/dist/schema/index.d.ts +179 -0
- package/dist/schema/index.js +29 -0
- package/dist/schema/index.mjs +29 -0
- package/dist/server-GJPg8ZSG.d.ts +675 -0
- package/dist/{stream-FQm9h4Mg.d.ts → stream-B4oK9JFP.d.ts} +1 -1
- package/dist/tracer-Hwt1cl7h.d.ts +189 -0
- package/dist/tracing-DqbTKGcf.d.ts +148 -0
- package/docs/ARCHITECTURE.md +292 -0
- package/docs/README.md +63 -0
- package/docs/adr/0001-ai-context-pack.md +32 -0
- package/docs/agent-apply-mode.md +104 -0
- package/docs/agent-approvals.md +110 -0
- package/docs/agent-batch.md +185 -0
- package/docs/agent-boundaries.md +112 -0
- package/docs/agent-chat-sessions.md +160 -0
- package/docs/agent-ci.md +17 -0
- package/docs/agent-cli.md +405 -0
- package/docs/agent-config.md +480 -0
- package/docs/agent-context-discovery.md +159 -0
- package/docs/agent-copilot-like-dx.md +126 -0
- package/docs/agent-declarative-optimized-planning.md +138 -0
- package/docs/agent-dx.md +224 -0
- package/docs/agent-env-files.md +126 -0
- package/docs/agent-follow-up-context.md +43 -0
- package/docs/agent-global-usage.md +180 -0
- package/docs/agent-init.md +109 -0
- package/docs/agent-install-and-configure.md +516 -0
- package/docs/agent-language-workspace-ux.md +99 -0
- package/docs/agent-llm-adapters.md +123 -0
- package/docs/agent-local-install.md +190 -0
- package/docs/agent-local-tests.md +51 -0
- package/docs/agent-observability.md +155 -0
- package/docs/agent-patch-quality-loop.md +162 -0
- package/docs/agent-presets.md +22 -0
- package/docs/agent-project-commands.md +237 -0
- package/docs/agent-project-intelligence.md +156 -0
- package/docs/agent-redaction.md +18 -0
- package/docs/agent-release-readiness.md +76 -0
- package/docs/agent-rollback-safety.md +162 -0
- package/docs/agent-rollback.md +23 -0
- package/docs/agent-run-artifacts.md +16 -0
- package/docs/agent-vscode-auto-discovery.md +137 -0
- package/docs/agent-vscode-batch-runner.md +100 -0
- package/docs/agent-vscode-chat-layout.md +90 -0
- package/docs/agent-vscode-clean-install.md +147 -0
- package/docs/agent-vscode-code-actions.md +70 -0
- package/docs/agent-vscode-diff-preview.md +45 -0
- package/docs/agent-vscode-inline-assist.md +56 -0
- package/docs/agent-vscode-install.md +186 -0
- package/docs/agent-vscode-model-setup.md +97 -0
- package/docs/agent-vscode-patch-preview.md +92 -0
- package/docs/agent-vscode-problems.md +79 -0
- package/docs/agent-vscode-project-dashboard.md +106 -0
- package/docs/agent-vscode-run-history.md +92 -0
- package/docs/agent-vscode-ux.md +73 -0
- package/docs/ai/INVARIANTS.md +84 -0
- package/docs/ai/PROJECT_MAP.md +338 -0
- package/docs/ai/PUBLIC_API.md +336 -0
- package/docs/ai/VALIDATION_MATRIX.md +67 -0
- package/docs/api-polish.md +37 -0
- package/docs/cancellation.md +162 -0
- package/docs/coverage.md +46 -0
- package/docs/getting-started.md +159 -0
- package/docs/guides/README.md +40 -0
- package/docs/guides/circuit-breaker.md +89 -0
- package/docs/guides/error-handling.md +91 -0
- package/docs/guides/getting-started.md +107 -0
- package/docs/guides/layers.md +189 -0
- package/docs/guides/metrics.md +101 -0
- package/docs/guides/resource-management.md +141 -0
- package/docs/guides/retry.md +215 -0
- package/docs/guides/semaphore.md +66 -0
- package/docs/guides/streams.md +117 -0
- package/docs/guides/supervisors.md +98 -0
- package/docs/guides/testing.md +162 -0
- package/docs/guides/tracing.md +71 -0
- package/docs/http-recipes.md +399 -0
- package/docs/http.md +749 -0
- package/docs/modules.md +285 -0
- package/docs/observability-collector-smoke.md +31 -0
- package/docs/observability-framework-examples.md +98 -0
- package/docs/observability.md +542 -0
- package/docs/otel-collector-smoke.yaml +27 -0
- package/docs/performance-profiler.md +199 -0
- package/docs/production-readiness.md +73 -0
- package/docs/recipes/README.md +12 -0
- package/docs/recipes/http-server.md +45 -0
- package/docs/recipes/layers.md +44 -0
- package/docs/recipes/performance.md +47 -0
- package/docs/recipes/runtime.md +41 -0
- package/docs/recipes/testing.md +41 -0
- package/docs/release.md +53 -0
- package/docs/wasm-bounded-queues.md +44 -0
- package/docs/wasm-engine-observability-benchmarks.md +85 -0
- package/docs/wasm-fiber-engine.md +117 -0
- package/docs/wasm-scheduler-state-machine.md +122 -0
- package/docs/wasm-stream-chunks.md +54 -0
- package/package.json +48 -2
- package/dist/chunk-AR22SXML.js +0 -1043
- package/dist/chunk-BDYEENHT.js +0 -224
- package/dist/chunk-JFPU5GQI.mjs +0 -1043
- package/dist/chunk-MS34J5LY.cjs +0 -224
- package/dist/chunk-UMAZLXAB.mjs +0 -224
- package/dist/chunk-XPZNXSVN.cjs +0 -1043
- package/dist/tracing-DNT9jEbr.d.ts +0 -106
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Brass Performance Profiler
|
|
2
|
+
|
|
3
|
+
`brass-runtime/perf` is the built-in profiling surface for runtime and HTTP
|
|
4
|
+
performance work. It is intentionally dependency-free and runs against a local
|
|
5
|
+
Node HTTP server so it can be used in CI, during local optimization, and before
|
|
6
|
+
changing runtime internals.
|
|
7
|
+
|
|
8
|
+
## Run it
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm run perf
|
|
12
|
+
npm run perf:json
|
|
13
|
+
npm run benchmark:perf
|
|
14
|
+
npm run perf:runtime:ab
|
|
15
|
+
npm run perf:runtime:soak
|
|
16
|
+
npm run perf:runtime:budget
|
|
17
|
+
npm run perf:http:memory
|
|
18
|
+
npm run perf:history
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For memory-sensitive runs, expose GC:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
node --expose-gc --import tsx src/perf/cli.ts --force-gc
|
|
25
|
+
node --expose-gc --import tsx src/perf/cli.ts --profile http-memory --calls 100000 --concurrency 512 --delay-ms 2 --force-gc
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Focused examples:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run perf -- --profile runtime --runtime-iterations 10000
|
|
32
|
+
npm run perf -- --profile runtime-ab --baseline fiber-only --candidate default
|
|
33
|
+
npm run perf -- --profile runtime-soak --rounds 10 --runtime-iterations 100000
|
|
34
|
+
npm run perf -- --profile http --calls 20000 --concurrency 512 --delay-ms 2 --force-gc
|
|
35
|
+
npm run perf -- --profile http --variants default-json,default-json-observed --json
|
|
36
|
+
npm run perf -- --profile http-memory --calls 20000 --concurrency 512 --rounds 2 --force-gc
|
|
37
|
+
npm run perf -- --profile runtime-ab --record-history --save-baseline runtime-main
|
|
38
|
+
npm run perf -- --profile runtime-ab --compare-baseline runtime-main --fail-on-baseline-regression
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Environment variables mirror the CLI flags:
|
|
42
|
+
|
|
43
|
+
- `BRASS_PERF_PROFILE=all|runtime|http`
|
|
44
|
+
- `BRASS_PERF_CALLS`
|
|
45
|
+
- `BRASS_PERF_CONCURRENCY`
|
|
46
|
+
- `BRASS_PERF_DELAY_MS`
|
|
47
|
+
- `BRASS_PERF_WARMUP_CALLS`
|
|
48
|
+
- `BRASS_PERF_VARIANTS`
|
|
49
|
+
- `BRASS_PERF_RUNTIME_ITERATIONS`
|
|
50
|
+
- `BRASS_PERF_RUNTIME_CHAIN_DEPTH`
|
|
51
|
+
- `BRASS_PERF_RUNTIME_VARIANT`
|
|
52
|
+
- `BRASS_PERF_BASELINE`
|
|
53
|
+
- `BRASS_PERF_CANDIDATE`
|
|
54
|
+
- `BRASS_PERF_ROUNDS`
|
|
55
|
+
- `BRASS_PERF_FORCE_GC=true`
|
|
56
|
+
- `BRASS_PERF_JSON=true`
|
|
57
|
+
- `BRASS_PERF_RECORD_HISTORY=true`
|
|
58
|
+
- `BRASS_PERF_HISTORY_DIR`
|
|
59
|
+
- `BRASS_PERF_SAVE_BASELINE`
|
|
60
|
+
- `BRASS_PERF_COMPARE_BASELINE`
|
|
61
|
+
- `BRASS_PERF_FAIL_ON_BASELINE_REGRESSION=true`
|
|
62
|
+
|
|
63
|
+
## Import it
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import {
|
|
67
|
+
comparePerfToBaseline,
|
|
68
|
+
formatPerformanceReport,
|
|
69
|
+
loadPerfBaseline,
|
|
70
|
+
profileHttpLayers,
|
|
71
|
+
profileRuntimePrimitives,
|
|
72
|
+
runBrassPerformanceProfile,
|
|
73
|
+
savePerfBaseline,
|
|
74
|
+
createPerfHistoryEntry,
|
|
75
|
+
} from "brass-runtime/perf";
|
|
76
|
+
|
|
77
|
+
const report = await runBrassPerformanceProfile({
|
|
78
|
+
http: {
|
|
79
|
+
calls: 20_000,
|
|
80
|
+
concurrency: 512,
|
|
81
|
+
delayMs: 2,
|
|
82
|
+
forceGc: true,
|
|
83
|
+
variants: ["default-json", "default-json-observed"],
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log(formatPerformanceReport(report));
|
|
88
|
+
|
|
89
|
+
const entry = createPerfHistoryEntry("all", report);
|
|
90
|
+
await savePerfBaseline("daily-main", entry);
|
|
91
|
+
const baseline = await loadPerfBaseline("daily-main");
|
|
92
|
+
if (baseline) {
|
|
93
|
+
console.log(comparePerfToBaseline(entry, baseline));
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## What it measures
|
|
98
|
+
|
|
99
|
+
Runtime profile:
|
|
100
|
+
|
|
101
|
+
- top-level `asyncSucceed`
|
|
102
|
+
- top-level `asyncFail`
|
|
103
|
+
- top-level `asyncSync`
|
|
104
|
+
- deep `flatMap` chains
|
|
105
|
+
- `FiberRef` update/get chains
|
|
106
|
+
- fibers started per primitive
|
|
107
|
+
- ns/op and operations per second
|
|
108
|
+
|
|
109
|
+
Runtime A/B profile:
|
|
110
|
+
|
|
111
|
+
- `fiber-only` baseline for forced fiber execution
|
|
112
|
+
- `default` candidate with native top-level fast path for no-hooks/no-lane
|
|
113
|
+
`Succeed`, `Fail`, `Sync`, synchronous continuations, `FiberRef` locals, and
|
|
114
|
+
synchronous/asynchronous callback effects
|
|
115
|
+
- `active-hooks` to measure EventBus overhead
|
|
116
|
+
- `recorder` to measure bounded flight recorder overhead
|
|
117
|
+
- `wide-scheduler` to measure larger single-lane queue settings
|
|
118
|
+
|
|
119
|
+
Runtime soak profile:
|
|
120
|
+
|
|
121
|
+
- repeated runtime-only rounds
|
|
122
|
+
- throughput trend from first to last round
|
|
123
|
+
- heap/rss trend across rounds
|
|
124
|
+
- hottest primitive per round
|
|
125
|
+
|
|
126
|
+
HTTP layer profile:
|
|
127
|
+
|
|
128
|
+
- `node-http-text`
|
|
129
|
+
- `wire-raw`
|
|
130
|
+
- `default-minimal-json`
|
|
131
|
+
- `default-balanced-no-adaptive-json`
|
|
132
|
+
- `default-balanced-json`
|
|
133
|
+
- `default-json`
|
|
134
|
+
- `default-json-observed`
|
|
135
|
+
|
|
136
|
+
Each HTTP variant reports throughput, latency percentiles, client/server
|
|
137
|
+
in-flight peaks, queue depth, adaptive limiter state when present, span
|
|
138
|
+
retention when observability is enabled, and memory deltas.
|
|
139
|
+
|
|
140
|
+
HTTP long-run memory lab:
|
|
141
|
+
|
|
142
|
+
- defaults to `forceGc: true`, so use `node --expose-gc` for the strongest
|
|
143
|
+
retained-memory signal
|
|
144
|
+
- compares node transport, wire raw, minimal, balanced without adaptive,
|
|
145
|
+
balanced, default, and default+observability variants
|
|
146
|
+
- reports heap/rss totals, max p99, mean throughput, errors, and
|
|
147
|
+
`heapDeltaPer10kRequestsMb`
|
|
148
|
+
- highlights whether memory is `ok`, `watch`, `critical`, or `unknown-gc`
|
|
149
|
+
- compares default+observability against default for heap/10k and throughput
|
|
150
|
+
|
|
151
|
+
Memory profile:
|
|
152
|
+
|
|
153
|
+
- before/after snapshots
|
|
154
|
+
- heap/rss/external/array-buffer deltas
|
|
155
|
+
- GC availability
|
|
156
|
+
- optional forced GC via `--force-gc`
|
|
157
|
+
|
|
158
|
+
## History and baselines
|
|
159
|
+
|
|
160
|
+
Perf history is local and dependency-free:
|
|
161
|
+
|
|
162
|
+
- history appends compact JSONL entries to `.brass/perf-history/runs.jsonl`
|
|
163
|
+
- baselines are stored under `.brass/perf-history/baselines/*.json`
|
|
164
|
+
- entries keep normalized metrics rather than full reports by default
|
|
165
|
+
- `--record-history` writes the current run
|
|
166
|
+
- `--save-baseline NAME` stores the current run as a named baseline
|
|
167
|
+
- `--compare-baseline NAME` compares matching metrics by name/tags
|
|
168
|
+
- `--fail-on-baseline-regression` turns a failed baseline comparison into a
|
|
169
|
+
non-zero CLI exit code
|
|
170
|
+
|
|
171
|
+
Thresholds are intentionally simple:
|
|
172
|
+
|
|
173
|
+
- throughput and ops/s are higher-is-better
|
|
174
|
+
- latency, heap/rss, ns/op, and errors are lower-is-better
|
|
175
|
+
- `--baseline-max-regression-percent` defaults to `10`
|
|
176
|
+
- `--baseline-max-heap-regression-percent` defaults to `25`
|
|
177
|
+
- `--baseline-warn-at-ratio` defaults to `0.5`
|
|
178
|
+
|
|
179
|
+
## Recommendations
|
|
180
|
+
|
|
181
|
+
`recommendPerformance` turns raw numbers into actionable warnings:
|
|
182
|
+
|
|
183
|
+
- default client throughput compared to `node:http`
|
|
184
|
+
- observability overhead compared to unobserved default client
|
|
185
|
+
- retained heap per HTTP variant and full profile
|
|
186
|
+
- queueing and adaptive limiter pressure
|
|
187
|
+
- high HTTP p99 latency
|
|
188
|
+
- runtime primitives below local thresholds
|
|
189
|
+
|
|
190
|
+
These recommendations are heuristics, not release gates. Use the focused
|
|
191
|
+
benchmark budgets for stable regression checks:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm run perf:runtime:budget
|
|
195
|
+
npm run perf:http:memory
|
|
196
|
+
npm run benchmark:runtime:budget
|
|
197
|
+
npm run benchmark:http:budget
|
|
198
|
+
npm run benchmark:observability:budget
|
|
199
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Production Readiness
|
|
2
|
+
|
|
3
|
+
This checklist is the release gate for using `brass-runtime` in production-like
|
|
4
|
+
services.
|
|
5
|
+
|
|
6
|
+
## HTTP Defaults
|
|
7
|
+
|
|
8
|
+
Use `makeDefaultHttpClient` for application clients. The `default` preset enables
|
|
9
|
+
timeout, deduplication, priority scheduling, retry, conservative adaptive
|
|
10
|
+
concurrency, safe-method cache, compression, stats, cache controls, and
|
|
11
|
+
`cancelAll`.
|
|
12
|
+
|
|
13
|
+
The adaptive limiter is intentionally conservative in presets:
|
|
14
|
+
|
|
15
|
+
- `minSamples` prevents cold-start latency from changing limits too early.
|
|
16
|
+
- `decreaseThreshold` creates a deadband for normal latency jitter.
|
|
17
|
+
- `maxDecreaseRatio` caps one-step decreases so a noisy sample cannot collapse
|
|
18
|
+
concurrency.
|
|
19
|
+
- Presets avoid `minLimit: 1`; use that only when a caller explicitly wants a
|
|
20
|
+
nearly-closed circuit under pressure.
|
|
21
|
+
|
|
22
|
+
When debugging throughput, compare requested concurrency with
|
|
23
|
+
`serverMaxInFlight`, `adaptiveFinalLimit`, and `adaptiveMaxQueueDepth` before
|
|
24
|
+
assuming a memory leak.
|
|
25
|
+
|
|
26
|
+
## Validation
|
|
27
|
+
|
|
28
|
+
Baseline gate:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run test:types
|
|
32
|
+
npm test
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Public package gate:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm run build
|
|
39
|
+
npm run validate:cjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Benchmark gates:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm run benchmark:observability:budget
|
|
46
|
+
npm run benchmark:http:budget
|
|
47
|
+
npm run benchmark:adaptive
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Soak gate:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm run benchmark:http:soak
|
|
54
|
+
npm run benchmark:adaptive:soak
|
|
55
|
+
BRASS_HTTP_BENCH_CALLS=100000 node --expose-gc --import tsx src/benchmarks/runner.ts http-concurrent
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Treat sustained positive `heapDeltaMb` after explicit GC as leak evidence. Treat
|
|
59
|
+
RSS-only growth as a signal to investigate, not proof of a leak.
|
|
60
|
+
|
|
61
|
+
## Operational Notes
|
|
62
|
+
|
|
63
|
+
- Attach HTTP observability with `middleware: [withHttpObservability(obs)]`.
|
|
64
|
+
- HTTP observability records adaptive limiter gauges when the wrapped client
|
|
65
|
+
owns a limiter; keep the optional limiter key label disabled unless keys are
|
|
66
|
+
low-cardinality.
|
|
67
|
+
- Always call `observability.shutdown()` during service shutdown.
|
|
68
|
+
- Call HTTP `shutdown()` as part of graceful shutdown when using default or
|
|
69
|
+
adaptive-limiter clients so queue/TTL timers and waiters are cleaned up.
|
|
70
|
+
- Keep exporter queues bounded and use `flush()` before process exit.
|
|
71
|
+
- Prefer local benchmark servers over public demo APIs for repeatable numbers.
|
|
72
|
+
- Keep benchmark budgets advisory in developer machines and mandatory in CI once
|
|
73
|
+
the CI hardware profile is stable.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Brass Recipes
|
|
2
|
+
|
|
3
|
+
Copyable happy paths for the first release.
|
|
4
|
+
|
|
5
|
+
- [Runtime](./runtime.md)
|
|
6
|
+
- [Layers](./layers.md)
|
|
7
|
+
- [HTTP server](./http-server.md)
|
|
8
|
+
- [Testing](./testing.md)
|
|
9
|
+
- [Performance](./performance.md)
|
|
10
|
+
|
|
11
|
+
These recipes use the public exports a new user should reach for first. Deeper
|
|
12
|
+
guides still live under `docs/guides/`.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# HTTP Server Recipe
|
|
2
|
+
|
|
3
|
+
Use `HttpServer` for the discoverable happy path: define routes, build a
|
|
4
|
+
router, and manage the Node listener as a resource.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { asyncSucceed, asyncSync, runPromise, useResource } from "brass-runtime";
|
|
8
|
+
import { HttpServer, s } from "brass-runtime/http";
|
|
9
|
+
|
|
10
|
+
const User = s.object({
|
|
11
|
+
id: s.nonEmptyString(),
|
|
12
|
+
name: s.nonEmptyString(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const routes = [
|
|
16
|
+
HttpServer.route("GET", "/users/:id", {
|
|
17
|
+
params: s.object({ id: s.nonEmptyString() }),
|
|
18
|
+
response: User,
|
|
19
|
+
}, (ctx) =>
|
|
20
|
+
asyncSucceed(HttpServer.json({
|
|
21
|
+
id: ctx.params.id,
|
|
22
|
+
name: "Ada",
|
|
23
|
+
})),
|
|
24
|
+
),
|
|
25
|
+
HttpServer.healthRoute(),
|
|
26
|
+
HttpServer.readinessRoute(),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const router = HttpServer.router(routes, {
|
|
30
|
+
middleware: [HttpServer.middleware.header("x-powered-by", "brass-runtime")],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await runPromise(
|
|
34
|
+
useResource(
|
|
35
|
+
router.listen({ port: 3000 }),
|
|
36
|
+
(server) => asyncSync(() => {
|
|
37
|
+
console.log(server.url());
|
|
38
|
+
}),
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Validation failures are returned as JSON responses. Handler failures are mapped
|
|
44
|
+
to server error responses by the router, while listener failures use typed
|
|
45
|
+
`NodeHttpServerError` values.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Layers Recipe
|
|
2
|
+
|
|
3
|
+
Use `defineService` for typed service tags, `Layer.effect` or `Layer.value` to
|
|
4
|
+
provide them, and `provideContext` to run a program with a scoped dependency
|
|
5
|
+
graph.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
Layer,
|
|
10
|
+
LayerContext,
|
|
11
|
+
asyncSucceed,
|
|
12
|
+
defineService,
|
|
13
|
+
provideContext,
|
|
14
|
+
runPromise,
|
|
15
|
+
} from "brass-runtime";
|
|
16
|
+
|
|
17
|
+
type Config = { readonly baseUrl: string };
|
|
18
|
+
type Repo = { readonly findUser: (id: string) => string };
|
|
19
|
+
|
|
20
|
+
const Config = defineService<Config>("Config");
|
|
21
|
+
const Repo = defineService<Repo>("Repo");
|
|
22
|
+
|
|
23
|
+
const ConfigLayer = Layer.value(Config, { baseUrl: "https://api.example.com" });
|
|
24
|
+
|
|
25
|
+
const RepoLayer = Layer.effect(Repo, (ctx: LayerContext) => {
|
|
26
|
+
const config = ctx.unsafeGet(Config);
|
|
27
|
+
return asyncSucceed({
|
|
28
|
+
findUser: (id) => `${config.baseUrl}/users/${id}`,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const AppLayer = Layer.compose(ConfigLayer, RepoLayer);
|
|
33
|
+
|
|
34
|
+
const userUrl = await runPromise(
|
|
35
|
+
provideContext(AppLayer, (ctx) =>
|
|
36
|
+
asyncSucceed(ctx.unsafeGet(Repo).findUser("u1")),
|
|
37
|
+
),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log(userUrl);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Missing services throw `MissingLayerServiceError`; use `formatLayerError` when
|
|
44
|
+
surfacing the message.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Performance Recipe
|
|
2
|
+
|
|
3
|
+
Use the profiler before and after runtime, HTTP, scheduler, layer, schedule, or
|
|
4
|
+
observability changes.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm run perf -- --profile runtime-ab
|
|
8
|
+
npm run perf -- --profile runtime-soak
|
|
9
|
+
npm run perf:http:memory -- --calls 1000 --concurrency 64 --variants default-json,default-json-observed
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Save a local baseline when the machine is stable:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm run perf -- --profile runtime-ab --record-history --save-baseline runtime-main
|
|
16
|
+
npm run perf -- --profile runtime-ab --compare-baseline runtime-main --fail-on-baseline-regression
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For memory-sensitive HTTP work, expose GC:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node --expose-gc --import tsx src/perf/cli.ts --profile http-memory --calls 100000 --concurrency 512 --delay-ms 2 --force-gc
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Programmatic API:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import {
|
|
29
|
+
comparePerfToBaseline,
|
|
30
|
+
createPerfHistoryEntry,
|
|
31
|
+
loadPerfBaseline,
|
|
32
|
+
runBrassPerformanceProfile,
|
|
33
|
+
savePerfBaseline,
|
|
34
|
+
} from "brass-runtime/perf";
|
|
35
|
+
|
|
36
|
+
const report = await runBrassPerformanceProfile({ http: false });
|
|
37
|
+
const entry = createPerfHistoryEntry("runtime", report);
|
|
38
|
+
await savePerfBaseline("runtime-main", entry);
|
|
39
|
+
|
|
40
|
+
const baseline = await loadPerfBaseline("runtime-main");
|
|
41
|
+
if (baseline) {
|
|
42
|
+
console.log(comparePerfToBaseline(entry, baseline));
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Treat non-GC heap deltas as allocator churn until a GC-aware run confirms
|
|
47
|
+
retained memory.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Runtime Recipe
|
|
2
|
+
|
|
3
|
+
Use `runPromise` for the short path and `runExit` when you want the complete
|
|
4
|
+
`Exit`/`Cause` value.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { asyncFlatMap, asyncSucceed, runExit, runPromise } from "brass-runtime";
|
|
8
|
+
|
|
9
|
+
const program = asyncFlatMap(asyncSucceed(41), (n) => asyncSucceed(n + 1));
|
|
10
|
+
|
|
11
|
+
const value = await runPromise(program);
|
|
12
|
+
const exit = await runExit(program);
|
|
13
|
+
|
|
14
|
+
console.log(value); // 42
|
|
15
|
+
console.log(exit);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use `makeRuntime` when the app owns runtime configuration.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { makeRuntime, asyncSync } from "brass-runtime";
|
|
22
|
+
|
|
23
|
+
const runtime = makeRuntime({ config: { port: 3000 } }, { inferLane: false });
|
|
24
|
+
|
|
25
|
+
const port = await runtime.toPromise(
|
|
26
|
+
asyncSync((env: { config: { port: number } }) => env.config.port),
|
|
27
|
+
);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If you care about typed failures, prefer `runExit` at the boundary and format
|
|
31
|
+
the cause in logs or HTTP responses.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { Cause, asyncFail, runExit } from "brass-runtime";
|
|
35
|
+
|
|
36
|
+
const exit = await runExit(asyncFail({ _tag: "NotFound", id: "u1" }));
|
|
37
|
+
|
|
38
|
+
if (exit._tag === "Failure") {
|
|
39
|
+
console.error(Cause.pretty(exit.cause));
|
|
40
|
+
}
|
|
41
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Testing Recipe
|
|
2
|
+
|
|
3
|
+
Use `makeTestRuntime` for deterministic clocks and scheduler control.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { asyncFlatMap, asyncSucceed, sleep, timeout } from "brass-runtime";
|
|
7
|
+
import { makeTestRuntime } from "brass-runtime";
|
|
8
|
+
|
|
9
|
+
const test = makeTestRuntime();
|
|
10
|
+
|
|
11
|
+
const program = asyncFlatMap(sleep(1000), () => asyncSucceed("done"));
|
|
12
|
+
|
|
13
|
+
const result = test.run(program);
|
|
14
|
+
await test.advance(1000);
|
|
15
|
+
|
|
16
|
+
console.log(await result); // done
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use HTTP testing helpers for client code without touching the network.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import {
|
|
23
|
+
makeJsonHttpResponse,
|
|
24
|
+
makeMockHttpClient,
|
|
25
|
+
runHttpEffect,
|
|
26
|
+
} from "brass-runtime/http/testing";
|
|
27
|
+
|
|
28
|
+
const client = makeMockHttpClient((req) =>
|
|
29
|
+
makeJsonHttpResponse({ ok: true, url: req.url }),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const response = await runHttpEffect(client({ method: "GET", url: "/health" }));
|
|
33
|
+
console.log(response.status);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For typed failures, assert on `Exit` instead of catching thrown values.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
const exit = await test.runExit(timeout(asyncSucceed("ok"), 1));
|
|
40
|
+
console.log(exit._tag);
|
|
41
|
+
```
|
package/docs/release.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# First Release Checklist
|
|
2
|
+
|
|
3
|
+
This is the release gate for the first public `brass-runtime` release.
|
|
4
|
+
|
|
5
|
+
## Release command
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run release:check
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`release:check` covers:
|
|
12
|
+
|
|
13
|
+
- TypeScript API/type checks.
|
|
14
|
+
- Full Vitest suite.
|
|
15
|
+
- TS bundle build.
|
|
16
|
+
- CJS compatibility validation.
|
|
17
|
+
- Runtime profiler budget.
|
|
18
|
+
- Runtime benchmark budget.
|
|
19
|
+
- HTTP benchmark budget.
|
|
20
|
+
- Observability benchmark budget.
|
|
21
|
+
|
|
22
|
+
## Manual release notes
|
|
23
|
+
|
|
24
|
+
Before publishing:
|
|
25
|
+
|
|
26
|
+
- Review `README.md` and `docs/recipes/`.
|
|
27
|
+
- Run a GC-aware HTTP memory lab on the release machine:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node --expose-gc --import tsx src/perf/cli.ts --profile http-memory --calls 100000 --concurrency 512 --delay-ms 2 --force-gc
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- Save a local perf baseline:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run perf -- --profile runtime-ab --record-history --save-baseline first-release-runtime
|
|
37
|
+
npm run perf -- --profile http-memory --calls 20000 --concurrency 512 --record-history --save-baseline first-release-http-memory
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- Confirm `npm pack --dry-run` includes only package files expected by
|
|
41
|
+
`package.json`.
|
|
42
|
+
|
|
43
|
+
## First-release scope
|
|
44
|
+
|
|
45
|
+
- Core runtime, fibers, scopes, `Cause`, interruptibility, `FiberRef`.
|
|
46
|
+
- Layer 2.0 and Schedule 2.0.
|
|
47
|
+
- Streams and pipelines.
|
|
48
|
+
- HTTP client/server, schema validation, lifecycle middleware.
|
|
49
|
+
- Observability and runtime health/readiness.
|
|
50
|
+
- Performance profiler, budgets, history, and baselines.
|
|
51
|
+
- Brass Agent CLI/library surface.
|
|
52
|
+
|
|
53
|
+
Do not publish `.brass/perf-history`; it is intentionally local evidence.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# WASM bounded queues / ring buffers
|
|
2
|
+
|
|
3
|
+
`brass-runtime` now has an engine-selectable bounded ring buffer abstraction.
|
|
4
|
+
|
|
5
|
+
The public queue API remains backward compatible:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
bounded<number>(1024, "backpressure");
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
You can force the ring storage engine when benchmarking:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
bounded<number>(1024, "backpressure", { engine: "wasm" });
|
|
15
|
+
bounded<number>(1024, "sliding", { engine: "wasm" });
|
|
16
|
+
bounded<number>(1024, "dropping", { engine: "wasm" });
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or use the lower-level ring buffer factory:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { makeBoundedRingBuffer } from "brass-runtime";
|
|
23
|
+
|
|
24
|
+
const ring = makeBoundedRingBuffer<number>(1024, 1024, { engine: "wasm" });
|
|
25
|
+
ring.push(1);
|
|
26
|
+
ring.shift();
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Engine semantics
|
|
30
|
+
|
|
31
|
+
- `engine: "js"`: always uses the TypeScript `RingBuffer`.
|
|
32
|
+
- `engine: "wasm"`: requires `wasm/pkg` to expose `BrassWasmRingBuffer`; throws if the WASM package was not rebuilt.
|
|
33
|
+
- `engine: "auto"`: uses WASM when available, otherwise falls back to the JS ring buffer.
|
|
34
|
+
|
|
35
|
+
## Performance note
|
|
36
|
+
|
|
37
|
+
This is intentionally selective. WASM helps most when the queue is hot, bounded, and dominated by ring-buffer state management. If every operation crosses the JS/WASM boundary with large object payloads, JS can still win. Benchmark both modes before making WASM the default in production.
|
|
38
|
+
|
|
39
|
+
After changing Rust, rebuild the package:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm run build:wasm
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# WASM engine observability and benchmarks
|
|
2
|
+
|
|
3
|
+
This layer makes `auto` observable instead of implicit.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
type EngineStats<T> = {
|
|
7
|
+
engine: "js" | "wasm";
|
|
8
|
+
data: T;
|
|
9
|
+
fallbackUsed: boolean;
|
|
10
|
+
};
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Capability detection
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { runtimeCapabilities } from "brass-runtime";
|
|
17
|
+
|
|
18
|
+
console.log(runtimeCapabilities());
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
{
|
|
25
|
+
wasmAvailable: boolean;
|
|
26
|
+
wasmFiberEngine: boolean;
|
|
27
|
+
wasmRingBuffer: boolean;
|
|
28
|
+
wasmScheduler: boolean;
|
|
29
|
+
wasmFiberRegistry: boolean;
|
|
30
|
+
wasmStreamChunks: boolean;
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Runtime stats
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const runtime = Runtime.makeWithEngine({}, "auto");
|
|
38
|
+
console.log(runtime.capabilities());
|
|
39
|
+
console.log(runtime.stats());
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`runtime.stats()` now returns `EngineStats<FiberEngineStats>`.
|
|
43
|
+
|
|
44
|
+
## Scheduler stats
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const scheduler = new Scheduler({ engine: "auto" });
|
|
48
|
+
console.log(scheduler.stats());
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`fallbackUsed: true` means `auto` selected JS because the corresponding WASM capability was not available.
|
|
52
|
+
|
|
53
|
+
## Ring buffer stats
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const q = makeBoundedRingBuffer<number>(1024, 1024, { engine: "auto" });
|
|
57
|
+
q.push(1);
|
|
58
|
+
q.shift();
|
|
59
|
+
console.log(q.stats());
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Stream chunk stats
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const chunker = makeStreamChunker<number>(256, { engine: "auto" });
|
|
66
|
+
console.log(chunker.stats());
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Benchmarks
|
|
70
|
+
|
|
71
|
+
Run:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm run build:wasm
|
|
75
|
+
npm run benchmark wasm-engines
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
JSON output:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run build:wasm
|
|
82
|
+
npm run benchmark:json wasm-engines
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The benchmark report includes the runtime capability snapshot, so results can be compared against whether WASM was actually available.
|