@ultimat3/core 23.0.0 → 25.0.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.
- package/CLAUDE.md +29 -26
- package/README.md +98 -30
- package/package.json +4 -7
- package/src/actor.ts +9 -0
- package/src/address-class.ts +40 -4
- package/src/assert.ts +9 -5
- package/src/audit.ts +144 -0
- package/src/aws-sigv4.ts +275 -0
- package/src/backoff.ts +16 -0
- package/src/bunfs.ts +17 -0
- package/src/client-dispatch.ts +24 -3
- package/src/client-flight.ts +68 -13
- package/src/client-problem.ts +62 -6
- package/src/client-retry-after.ts +47 -0
- package/src/client-transport.ts +3 -1
- package/src/client-wire.ts +27 -3
- package/src/config-ai.ts +32 -0
- package/src/config-defaults.ts +53 -0
- package/src/config-fixes.ts +0 -10
- package/src/config-health.ts +9 -2
- package/src/config-jobs.ts +51 -0
- package/src/config-keys.ts +170 -0
- package/src/config-mail.ts +73 -0
- package/src/config-merge.ts +9 -1
- package/src/config-navigation.ts +1 -25
- package/src/config-pwa.ts +42 -5
- package/src/config-removed.ts +131 -0
- package/src/config-shape.ts +79 -0
- package/src/config-site.ts +14 -3
- package/src/config.ts +141 -173
- package/src/context.ts +29 -13
- package/src/cookie.ts +299 -0
- package/src/core-error-codes.ts +2 -0
- package/src/cursor-page.ts +41 -0
- package/src/cursor.ts +26 -5
- package/src/decimal-order.ts +5 -4
- package/src/deprecation.ts +77 -0
- package/src/dev-secrets.ts +18 -7
- package/src/drain-deadline.ts +43 -0
- package/src/env-example.ts +9 -29
- package/src/error-reporter-sentry.ts +7 -3
- package/src/errors.ts +18 -9
- package/src/exports/error-contract.ts +0 -1
- package/src/exports/observability.ts +1 -1
- package/src/exports/secrets.ts +3 -0
- package/src/finite-option.ts +1 -1
- package/src/flight-gate.ts +43 -16
- package/src/fnv1a.ts +19 -0
- package/src/generation-fence.ts +1 -1
- package/src/health-disclosure.ts +43 -0
- package/src/host-rules.ts +28 -1
- package/src/html-escape.ts +24 -0
- package/src/ids.ts +7 -7
- package/src/image/canvas.ts +76 -5
- package/src/image/errors.ts +3 -1
- package/src/image/pipeline.ts +17 -5
- package/src/image/png-pixels.ts +29 -6
- package/src/image/probe.ts +7 -2
- package/src/image/raster.ts +27 -2
- package/src/index.ts +99 -33
- package/src/iso-date.ts +1 -1
- package/src/lifecycle-errors.ts +1 -1
- package/src/lifecycle-readiness.ts +60 -2
- package/src/lifecycle-signals.ts +27 -2
- package/src/lifecycle-types.ts +96 -0
- package/src/lifecycle.ts +44 -133
- package/src/locale-direction.ts +1 -1
- package/src/logger.ts +92 -16
- package/src/mcp-exposure.ts +70 -8
- package/src/measurement-actor.ts +16 -1
- package/src/metric-errors.ts +32 -0
- package/src/metric-registry.ts +151 -0
- package/src/metric-series.ts +94 -0
- package/src/metrics.ts +9 -255
- package/src/nearest-name.ts +11 -2
- package/src/otlp-metric-exporter.ts +1 -1
- package/src/otlp-span-exporter.ts +1 -1
- package/src/otlp.ts +44 -13
- package/src/page.ts +4 -2
- package/src/pg-executor.ts +15 -0
- package/src/public-cause.ts +37 -0
- package/src/registrar.ts +22 -4
- package/src/retry.ts +40 -7
- package/src/route-rank.ts +36 -0
- package/src/same-origin.ts +1 -1
- package/src/sampler.ts +6 -2
- package/src/secrets-errors.ts +14 -3
- package/src/secrets-key-file.ts +139 -0
- package/src/secrets-store.ts +32 -16
- package/src/service.ts +5 -5
- package/src/single-flight.ts +1 -1
- package/src/source-mask.ts +14 -8
- package/src/store-mode.ts +23 -0
- package/src/telemetry.ts +1 -1
- package/src/theme-storage.ts +12 -0
- package/src/type-pins.ts +51 -1
- package/src/image/fixtures.ts +0 -263
- package/src/time-zone-name.ts +0 -14
package/CLAUDE.md
CHANGED
|
@@ -40,11 +40,10 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
40
40
|
- **`singleLine` keeps the 3-line contract to three lines, applied in the CONSTRUCTOR** so every
|
|
41
41
|
door (`format()`, `.message`, `.cause`, `toJSON()`) is covered once. It touches only C0 controls
|
|
42
42
|
and DEL — a cause keeps its quotes and backslashes. `@ultimat3/schema` carries a deliberate
|
|
43
|
-
duplicate, pinned by `single-line-pin.test.ts` HERE, with `ERROR_DOCS_URL
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`sideEffects: false`.
|
|
43
|
+
duplicate, pinned by `single-line-pin.test.ts` HERE, with `ERROR_DOCS_URL`. The brand symbol is
|
|
44
|
+
NOT a copy: `ULTIMATE_ERROR_BRAND` is schema's one declaration, imported by `errors.ts`.
|
|
45
|
+
- **`core → schema` is declared: import a schema value, never copy it.** Bundle cost:
|
|
46
|
+
`docs/architecture/01-package-map.md`; it depends on `@ultimat3/schema` keeping `sideEffects: false`.
|
|
48
47
|
- **A string's length is CODE POINTS** — `validators.ts` rejects in that unit and
|
|
49
48
|
`json-schema.ts` publishes `minLength` in it. `parseId`/`uuidTimestamp` describe a rejected id and
|
|
50
49
|
never echo it: a value baked into a message has no log field key to redact.
|
|
@@ -60,9 +59,11 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
60
59
|
| Concept | Owner | Note |
|
|
61
60
|
|---|---|---|
|
|
62
61
|
| which deploy this is | `environment.ts` (`ULTIMATE_ENV`) | the twin of `ROLE`; never a second env var |
|
|
62
|
+
| one-home helpers | `store-mode` `html-escape` `cookie` `fnv1a` `pg-executor` `deprecation` `aws-sigv4` | never copied (`X_HELPER_COPY`) |
|
|
63
63
|
| what this process does | `roles.ts` (`ROLE`) | |
|
|
64
64
|
| how a route renders, caches offline and hydrates | `route-vocabulary.ts` (`RENDER_MODES`, `OFFLINE_STRATEGIES`, `HYDRATE_STRATEGIES`) | every union is `(typeof ARRAY)[number]`, pinned in `type-pins.ts`; `scripts/render-modes.test.ts` refuses a second declaration. Re-export it, never restate it |
|
|
65
|
-
| which
|
|
65
|
+
| which of two route patterns wins a pathname | `route-rank.ts` (`routeRank`) | the request router's order as one integer: segment by segment, literal 3 > `:param` 2 > `*catch-all` 1, ENDED 4, packed base 5 over 22 segments. Read by `@ultimat3/render`'s `compilePattern` and `@ultimat3/pwa`'s rule order — both tier 4, so the one copy lives here. `@ultimat3/http`'s trie encodes the same order by its walk, not by this number. Never a sum: 100/10/1 ranked `/:a/b/c` above `/a/:x/:y` |
|
|
66
|
+
| which rungs a cache ladder has | `cache-vocabulary.ts` (`CACHE_TIERS`) | `@ultimat3/cache`'s read order, imported (no `TIER_ORDER` alias). `isr` is a `RenderMode`, never a tier |
|
|
66
67
|
| which build of the APP this is | `app-version.ts` (`APP_VERSION`) | one reader, `dev` by default |
|
|
67
68
|
| the values | `env.ts` | `checkEnv().values` holds REAL secrets — printing goes through `maskedEnvValues()` |
|
|
68
69
|
| `.env.example` | `env-example.ts` | a projection of the schema, never hand-maintained |
|
|
@@ -73,15 +74,19 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
73
74
|
| whether an answer still applies | `generation-fence.ts` | `X_SUPERSEDED` / `isSuperseded` |
|
|
74
75
|
| which HTTP statuses are worth repeating | `retryable-status.ts` | `>= 500` plus 408, 409, 425, 429 |
|
|
75
76
|
| how long this request has left | `request-budget.ts` (`Ctx.deadlineAt`, `REQUEST_TIMEOUT_HEADER`) | `@ultimat3/http`'s `startDeadline` is the one writer of the instant; `traceHeaders()` the one writer of the header. A spent budget sends nothing, never `0` |
|
|
76
|
-
| the above, composed into one typed-client call | `client-flight.ts` + `client-wire.ts` | shared by `@ultimat3/action` and `@ultimat3/query` (both tier 3), re-exported by both. Declares no code of its own |
|
|
77
|
-
| the browser's one HTTP function, records envelope, per-tab handle and principal fence | `client-transport.ts`, `client-dispatch.ts`, `client-problem.ts`, `client-paths.ts`, `record-envelope.ts`, `record-sink.ts`, `client-scope.ts` | `pageClient()` is the ONE `globalThis` write (`Symbol.for('ultimate.client')`); the scope's listeners live ON the handle. `clientTransport` never value-imports `
|
|
77
|
+
| the above, composed into one typed-client call | `client-flight.ts` + `client-wire.ts` | shared by `@ultimat3/action` and `@ultimat3/query` (both tier 3), re-exported by both. Declares no code of its own. A gate slot per ATTEMPT; the wait holds none, ends on `bump()` or the caller's abort |
|
|
78
|
+
| the browser's one HTTP function, records envelope, per-tab handle and principal fence | `client-transport.ts`, `client-dispatch.ts`, `client-problem.ts`, `client-retry-after.ts`, `client-paths.ts`, `record-envelope.ts`, `record-sink.ts`, `client-scope.ts` | `pageClient()` is the ONE `globalThis` write (`Symbol.for('ultimate.client')`); the scope's listeners live ON the handle. `clientTransport` never value-imports `clientFlight` or `traceHeaders()`: trace/budget headers reach it through the page handle's OUTBOUND SLOT (`outbound-headers.ts`). `isSuperseded` answers both `X_SUPERSEDED` and `X_CLIENT_SCOPE_CHANGED`. The fence never `bump()`s a caller's flight. A write never dedupes; a read dedupes only with a caller's `flight`. `pageClient()` reads `<meta name="ultimate-scope">` (`CLIENT_SCOPE_META`) once: content = principal, empty = anonymous (`null`), absent = UNSCOPED (`undefined`, nothing persisted). `actionPath(name)` with no `style` reads the document's `<meta name="ultimate-path-style">` (`renderedActionPathStyle()`; `CLIENT_PATH_STYLE_META` in `page-meta.ts`) and falls back to `'resource'`. A refusal's `Retry-After` and body `title` are read HERE (`client-retry-after.ts`, `remoteTitleOf`). Bytes: `page-bundle.test.ts`; `rpc`'s and `queryClient`'s in `action`'s and `query`'s CLAUDE.md |
|
|
78
79
|
| a write's public name | `write-digest.ts` (`writeDigest`, `isWriteDigest`, also on `./page`) + `write-origin.ts` (`withWriteOrigin`, `currentWriteOrigin`, `WRITE_ORIGIN_WAL_PREFIX`, server-only) | SHA-256 of an idempotency key, 32 hex; carried action → entity → WAL → realtime `records` frame. A malformed value runs the work unnamed: a label, never a gate |
|
|
79
80
|
| which row survives a conflict | `conflict-policy.ts` (`ConflictPolicy`, `resolveConflict`, `Row`) | read by `action`'s mutator and `realtime`'s rebase |
|
|
81
|
+
| where a visitor's light/dark choice is stored | `theme-storage.ts` (`THEME_STORAGE_KEY`, also on `./page`) | render's boot script reads it, ui's toggle writes it — both tier 4, so the one literal lives here. Persisted in browsers: never respelled |
|
|
80
82
|
| the four shapes of an async region | `async-state.ts` (`AsyncState`) | `realtime` returns it, `ui` renders it. `bun run render-modes` refuses a second status union sharing three members |
|
|
83
|
+
| the audit record + one sink | `audit.ts` | `action` + `query`; `AUDIT_RECORD_FIELDS` pins both |
|
|
81
84
|
| is this `unknown` a keyed record? | `json-object.ts` (`isJsonObject`) | narrows a shape; does not certify provenance |
|
|
85
|
+
| may a caller read this 5xx `cause`? | `public-cause.ts` (`hasPublicCause`) | one table for http, mcp, ai. `registerPublicCause` is `@ultimat3/http`'s `registerProblemMeta` writing it — never an app's door |
|
|
86
|
+
| is this field a credential? | `logger.ts` (`isRedactedKey`) | exact keys + `CREDENTIAL_NAME`; a bare `token` suffix is NOT one (`idempotencyToken`, `maxTokens`). Log line, monitor envelope and `action`'s audit ask it |
|
|
82
87
|
| a value that must not be printed | `secret.ts` | redacted by VALUE; `revealSecret()` is the one, greppable, way out |
|
|
83
88
|
| an `Intl` formatter cache, and the screen in front of it | `intl-cache.ts` (`cachedFormatter`, `canonicalLocale`, `assertLocale`, `MAX_CACHED_FORMATTERS`, `MAX_LOCALE_EXCERPT`) | a locale arrives from a header: refuse a non-tag (`X_LOCALE_INVALID`), key canonically AND bound the cache — never a copy of any of the three. The cause quotes at most `MAX_LOCALE_EXCERPT` (35) code points; the whole tag rides in `meta.locale` |
|
|
84
|
-
| the text direction of a locale | `locale-direction.ts` (`directionOf`, `isRtl`, `Direction`) |
|
|
89
|
+
| the text direction of a locale | `locale-direction.ts` (`directionOf`, `isRtl`, `Direction`) | import from here, never `@ultimat3/i18n`; here so `@ultimat3/ui` skips the i18n barrel |
|
|
85
90
|
| the committed encrypted values | `secrets.ts` (envelope) + `secrets-store.ts` (files, `installSecrets`) | plaintext is a flat map of ENV NAMES; there is no `secrets.get()` |
|
|
86
91
|
| ONE sealed value | `seal.ts` (`seal`, `open`, `openText`, `sealAll`) + `seal-keys.ts` (the ring) + `seal-errors.ts` | the framework's one AES call above the envelope: `entity`'s sealed column and `scraping`'s stored session call it, never WebCrypto. `purpose` is required and is AAD |
|
|
87
92
|
|
|
@@ -105,7 +110,7 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
105
110
|
is `x2`, never an edit.
|
|
106
111
|
- **`schema-error-codes.ts` registers `@ultimat3/schema`'s codes** (schema cannot call core), and
|
|
107
112
|
derives their retry classification from the same set. It is a `SIDE_EFFECTS_ANCHORS` entry.
|
|
108
|
-
- `timing-safe-equal.ts` is the one constant-time comparison (
|
|
113
|
+
- `timing-safe-equal.ts` is the one constant-time comparison (`auth`, `storage`).
|
|
109
114
|
- **`canonical-json.ts`: `canonicalJson` is INJECTIVE and `fingerprint` is SHA-256/16 of it** — the
|
|
110
115
|
hash every in-memory sharing key is taken over (`query`'s `queryHash`, `realtime`'s `qid`).
|
|
111
116
|
`NaN`, `±Infinity` and `-0` are bare tokens; `Date`, `Map` and `Set` are TAGGED. Never a
|
|
@@ -117,8 +122,7 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
117
122
|
caller that knows the column's kind may ask (`@ultimat3/entity`'s `compareByKind`) — never
|
|
118
123
|
`@ultimat3/query`, whose `OrderKey` has no kind.
|
|
119
124
|
- **`format-bytes.ts`: one `formatBytes(bytes)`, 1024-base, `b|kb|mb|gb`** for byte counts in error
|
|
120
|
-
messages. Not `@ultimat3/ui`'s locale-formatted decimal one.
|
|
121
|
-
third copy.
|
|
125
|
+
messages. Not `@ultimat3/ui`'s locale-formatted decimal one. Unmechanised: review catches a third.
|
|
122
126
|
- **The flight layer** (`backoff.ts`, `retry.ts`, `single-flight.ts`, `flight-gate.ts`,
|
|
123
127
|
`generation-fence.ts`, `retryable-status.ts`, `client-flight.ts`, `client-wire.ts`) imports
|
|
124
128
|
nothing but this package, runs nothing at import, and injects every source of non-determinism
|
|
@@ -130,7 +134,7 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
130
134
|
option.** `finiteCount(subject, option, value, min)` takes `min: 0 | 1` because only the caller
|
|
131
135
|
knows what zero means. `bun run finite-bounds` recognises a repair by the call's shape, so a
|
|
132
136
|
package screen carries `Finite` in its name.
|
|
133
|
-
- **`
|
|
137
|
+
- **`flightGate` HANDS its slot to a waiter** rather than releasing it. `X_FLIGHT_GATE_OVERLOADED`
|
|
134
138
|
is core's own code (tier 0 cannot borrow http's `X_OVERLOADED`); `overflow:` lets a caller throw its own.
|
|
135
139
|
- **`client-flight.ts` INVERTS `retryDecision`'s unclassified default** through its `transient:`
|
|
136
140
|
predicate: a bare `TypeError` (dead network) and an `AbortError` (the caller's cancellation) are
|
|
@@ -143,13 +147,11 @@ top-level `UltimateError` use in `error-codes.ts`.
|
|
|
143
147
|
|
|
144
148
|
## Metrics, tracing, reporting
|
|
145
149
|
|
|
146
|
-
`metrics.ts
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
— it reads `process`, so it is never exported from `page.ts`. One call site per package; a second
|
|
152
|
-
is the bug:
|
|
150
|
+
`metrics.ts`: counters (spans are `telemetry.ts`), always on, no-op exporter by default.
|
|
151
|
+
`runtime-metrics.ts` alone names a series the chart reads (`http_requests_total`, `connections`,
|
|
152
|
+
`queue_depth`), keyed by `ScalingSignal` in `SCALING_METRICS`. `process-metrics.ts`: `process_*`
|
|
153
|
+
(resident memory, heap, external, CPU seconds, event-loop lag, start time, `process_info{role}`);
|
|
154
|
+
server-only, never on `page.ts`. One call site per recorder; a second is the bug:
|
|
153
155
|
|
|
154
156
|
| Recorder | The one caller |
|
|
155
157
|
|---|---|
|
|
@@ -192,6 +194,8 @@ is the bug:
|
|
|
192
194
|
WHOLE drain's; `DEFAULT_DEADLINE_MS` (25 s) always applies; it is real monotonic time
|
|
193
195
|
(`systemClock`), never the injected `clock`. `drainDeadlineMs()` is the one decision point.
|
|
194
196
|
`settleWithin` attaches a rejection handler unconditionally.
|
|
197
|
+
- **Who a health endpoint tells what is ONE rule, here** (`health-disclosure.ts`): `healthBody` +
|
|
198
|
+
`healthPeerListed`, called by http and by realtime's sync listener. Never a second copy.
|
|
195
199
|
- **A readiness grace runs before the `accept` phase** (`lifecycle-grace.ts`): `/readyz` answers 503
|
|
196
200
|
with the socket still open for `drain.readinessGraceMs`, ADDED to `deadlineMs` (a chart's
|
|
197
201
|
`terminationGracePeriodSeconds` must exceed the sum — 5 s + 25 s by default). Unset: 0 in
|
|
@@ -212,8 +216,8 @@ bun test packages/core/src # from the REPO ROOT, never from packages/core
|
|
|
212
216
|
bun run typecheck
|
|
213
217
|
```
|
|
214
218
|
|
|
215
|
-
The root is
|
|
216
|
-
|
|
219
|
+
The root is load-bearing: Bun reads `bunfig.toml` (its preload installs `@ultimat3/testing`'s
|
|
220
|
+
matchers) from the cwd.
|
|
217
221
|
|
|
218
222
|
Gotchas:
|
|
219
223
|
- `exactOptionalPropertyTypes` is on — declare optional fields as `x?: T | undefined`.
|
|
@@ -221,12 +225,11 @@ Gotchas:
|
|
|
221
225
|
- `Ctx` carries a string index signature so apps can augment `CtxServices`; the cost is that
|
|
222
226
|
`ctx.anything` type-checks as `unknown`. Deleting it is a breaking change, measured to compile
|
|
223
227
|
core clean; land it alone, with a full `bun run verify`.
|
|
224
|
-
- **`Ctx extends CtxFacts, CtxServices`, and `
|
|
228
|
+
- **`Ctx extends CtxFacts, CtxServices`, and `ctxOf` holds the framework's ONE irreducible
|
|
225
229
|
`as Ctx`.** `CtxFacts` is what the framework sets (and what a `ServiceFactory` receives); an
|
|
226
230
|
augmentation's named members are required of every `Ctx`, and no framework function can obtain
|
|
227
|
-
them. `@ultimat3/http`'s `
|
|
228
|
-
|
|
229
|
-
a major, alongside the index-signature deletion.
|
|
231
|
+
them. `@ultimat3/http`'s `requestContext` composes `ctxOf()` and has no assertion.
|
|
232
|
+
Refused alternatives: the file header. The repair is a major, with the index-signature deletion.
|
|
230
233
|
- Tests that touch the registry, the lifecycle or the listener table call `resetErrorCodes()` /
|
|
231
234
|
`resetLifecycle()` / `resetListeners()` — and a registry reset takes `errorCodeSnapshot()` first
|
|
232
235
|
and restores it in `afterAll`, or every earlier package's titles render humanised for the run.
|
package/README.md
CHANGED
|
@@ -28,20 +28,28 @@ Zero dependencies, zero `@ultimat3/*` imports.
|
|
|
28
28
|
| which principal the page acts for, and the epoch that moves when it changes | `client-scope.ts` |
|
|
29
29
|
| which row survives a conflict — `server-wins`, `last-write-wins`, `custom` | `conflict-policy.ts` |
|
|
30
30
|
| the four shapes an async region can be in | `async-state.ts` |
|
|
31
|
+
| the audit seam `action` and `query` share — `AuditRecord` (`name`, `primitive`, `surface`, `outcome`, the parsed `input`; `action` is a deprecated alias of `name` until 25.0.0), `AuditSink`, the ONE installed sink (`setAuditSink` / `getAuditSink` / `resetAuditSink`, re-exported by `@ultimat3/action`), and `AUDIT_RECORD_FIELDS`, the field list both primitives' tests pin their records to | `audit.ts` |
|
|
31
32
|
| is this `unknown` a keyed record? | `json-object.ts` |
|
|
32
33
|
| typed env validated at boot | `env.ts` |
|
|
33
34
|
| `.env.example` rendered from that schema, and its drift check | `env-example.ts` |
|
|
34
35
|
| named environments + `ULTIMATE_ENV` resolution | `environment.ts` |
|
|
36
|
+
| which store backs a seam — `storeMode(env)`: `memory` under `test`, `database` everywhere else | `store-mode.ts` |
|
|
35
37
|
| the boot refusal of a shipped dev signing secret outside development/test — `X_CURSOR_SECRET_DEV` | `dev-secrets.ts` |
|
|
36
38
|
| a value that cannot be printed by accident | `secret.ts` |
|
|
37
39
|
| the committed encrypted secrets envelope, AES-256-GCM | `secrets.ts` |
|
|
38
40
|
| the two secrets files, and decrypted values → `defineEnv` | `secrets-store.ts` |
|
|
41
|
+
| a key file only its owner can read — 0600, and an `icacls` ACL on Windows — written via temp + rename, the rename retried on EPERM/EBUSY (`writeMasterKeyFile`, `stageMasterKeyFile`, `promoteStagedMasterKey`) | `secrets-key-file.ts` |
|
|
39
42
|
| one value sealed under the master key — `seal()` / `open()` | `seal.ts` |
|
|
40
43
|
| the key ring those work under: the current key plus retired ones | `seal-keys.ts` |
|
|
41
44
|
| `defineConfig()` for `app.config.ts` | `config.ts` |
|
|
42
45
|
| how overlays layer onto it — per section, key by key | `config-merge.ts` |
|
|
46
|
+
| what each key is when no layer says | `config-defaults.ts` |
|
|
47
|
+
| the shape screens that run before any rule reads a value — section, list, boolean, closed set, path | `config-shape.ts` |
|
|
48
|
+
| the keys a major deleted (`locales`, `defaultLocale`, `defaultTimeZone`, `defaultCurrency`, `theme.tokens` in 25.0.0; `jobs.driver`, deleted in 5.0.0 and refused since 25.0.0) — each REFUSED by name with `X_CONFIG_INVALID` and its replacement, never ignored | `config-removed.ts` |
|
|
43
49
|
| the `pwa` block — what an install needs, and the boot refusal when it is not there | `config-pwa.ts` |
|
|
50
|
+
| `isSameOriginPath(value)` — a path on THIS origin as a browser resolves it: refuses `//host`, `/\host`, a C0 control or DEL (`/\t/evil.example` parses to `//evil.example`) and a dot segment leaving a `//` pathname. The one predicate for every URL precached as an offline answer, `@ultimat3/pwa`'s build included | `config-pwa.ts` |
|
|
44
51
|
| the closed route vocabulary every renderer names | `route-vocabulary.ts` |
|
|
52
|
+
| which of two route patterns wins a pathname (`routeRank`) | `route-rank.ts` |
|
|
45
53
|
| runtime roles + `ROLE` resolution | `roles.ts` |
|
|
46
54
|
| `Clock` — the only source of "now" | `clock.ts` |
|
|
47
55
|
| UUIDv7, nanoid, branded ids | `ids.ts` |
|
|
@@ -51,6 +59,7 @@ Zero dependencies, zero `@ultimat3/*` imports.
|
|
|
51
59
|
| OTLP/HTTP JSON: endpoint, headers, value encoding | `otlp.ts` |
|
|
52
60
|
| `SpanExporter` on the wire, batched | `otlp-span-exporter.ts` |
|
|
53
61
|
| `MetricExporter` on the wire | `otlp-metric-exporter.ts` |
|
|
62
|
+
| may a caller read this 5xx code's `cause`? `hasPublicCause(code)` — one predicate for the HTTP problem document, MCP error data and an agent `tool_result` | `public-cause.ts` |
|
|
54
63
|
| `reportError` + the `ErrorReporter` seam, no-op by default | `error-reporter.ts` |
|
|
55
64
|
| that seam on the wire, Sentry's envelope and DSN | `error-reporter-sentry.ts` |
|
|
56
65
|
| OTel-shaped counter / gauge / histogram, same seam | `metrics.ts` |
|
|
@@ -58,14 +67,29 @@ Zero dependencies, zero `@ultimat3/*` imports.
|
|
|
58
67
|
| the series every process emits, incl. what the chart scales on | `runtime-metrics.ts` |
|
|
59
68
|
| what the process itself costs: `process_resident_memory_bytes`, `process_heap_used_bytes`, `process_heap_total_bytes`, `process_external_memory_bytes`, `process_cpu_seconds_total`, `process_event_loop_lag_seconds`, `process_start_time_seconds`, `process_info{role}` — **server-only**, never on `@ultimat3/core/page` | `process-metrics.ts` (`startProcessMetrics`, `readProcess`) |
|
|
60
69
|
| graceful drain, `/healthz`, `/readyz` | `lifecycle.ts` |
|
|
70
|
+
| what a health endpoint tells whom — `healthBody(report, role, detailed)`, `healthPeerListed(peers, address)`, `DEFAULT_HEALTH_DETAIL_PEERS`; the one rule `@ultimat3/http` and the sync node's own listener both call | `health-disclosure.ts` |
|
|
61
71
|
| the readiness grace between `/readyz` → 503 and the listener closing (`drain.readinessGraceMs`) | `lifecycle-grace.ts` |
|
|
62
|
-
|
|
|
72
|
+
| the drain budget's default and domain (`drain.deadlineMs`, 25 s, 1–3600000 ms) — `DRAIN_DEADLINE_DEFAULT_MS`, `DRAIN_DEADLINE_MAX_MS` | `drain-deadline.ts` |
|
|
73
|
+
| `jobs.concurrency`'s default and domain — one slot count for every queue a worker serves, or a table per queue (`{ banks: 4, 'banks-long': 2 }`; a queue the table does not name runs at the default). `JOBS_CONCURRENCY_DEFAULT` (8), `type JobsConcurrency` | `config-jobs.ts` |
|
|
74
|
+
| SIGTERM/SIGINT → the one drain; on Windows also SIGHUP (console close) and SIGBREAK (Ctrl-Break) — `drainSignals(platform)` | `lifecycle-signals.ts` |
|
|
75
|
+
| is this directory inside a `bun build --compile` binary? `isCompiledBundle(import.meta.dir)` — `/$bunfs/` and Windows' `B:\~BUN\` | `bunfs.ts` |
|
|
63
76
|
| which network an IP literal belongs to — `classifyAddress`, for SSRF screens | `address-class.ts` |
|
|
77
|
+
| the network a caller's address keys a per-caller budget on — `addressNetwork`: IPv4 exact, IPv4-mapped as IPv4, IPv6 as its /64 | `address-class.ts` |
|
|
64
78
|
| the sockets this process opened, so a self-request is not egress | `listeners.ts` |
|
|
65
79
|
| `defineService('orgs', …)` → `ctx.orgs`, rebuilt per actor | `service.ts` |
|
|
66
80
|
| the registrar table one same-tier package reaches another through | `registrar.ts` |
|
|
67
81
|
| decode → resize → encode, the one image pipeline (over `Bun.Image`) | `image/` |
|
|
68
|
-
| `assertNever`, `
|
|
82
|
+
| `assertNever`, `assertCoded`, `assert` | `assert.ts` |
|
|
83
|
+
| the one HTML character table — `escapeHtml`, text and attributes alike (`& < > " '`) | `html-escape.ts` |
|
|
84
|
+
| the one `Cookie:` reader — `readCookie(header, name)`, `null` when absent, never a throw | `cookie.ts` |
|
|
85
|
+
| the one `Set-Cookie` writer — `serializeSetCookie(name, value, options?)`; defaults `Path=/; HttpOnly; Secure; SameSite=Lax`, the value percent-encoded so `readCookie` returns it exactly, `X_COOKIE_INVALID` for a non-token name, a pair over 4096 octets, `SameSite=None`/`Partitioned` without `Secure`, a broken `__Secure-`/`__Host-` prefix rule, or an injectable `Path`/`Domain`. `Expires` is an IMF-fixdate in UTC | `cookie.ts` |
|
|
86
|
+
| the one AWS Signature V4 signer — `signAwsRequest({ method, url, headers?, payload?, credentials, region, service, clock? })` → the URL, every header to send (`authorization`, `x-amz-date`, `x-amz-security-token`, `x-amz-content-sha256` by default on `s3`), the canonical request and string-to-sign. `payload`: `{ body }`, a pre-taken `{ sha256Hex }`, or `UNSIGNED_PAYLOAD`. S3 encodes the path once, every other service twice. Web Crypto, no SDK; proven on the aws-c-auth SigV4 suite and S3's worked examples. Shared by `storage`'s s3 disk and `mail`'s SES driver | `aws-sigv4.ts` |
|
|
87
|
+
| 32-bit FNV-1a — a BUCKET (rollouts, factory seeds), never a sharing key (`fingerprint` is) | `fnv1a.ts` |
|
|
88
|
+
| `PgExecutor` — the structural `query(text, values)` seam every Postgres store takes | `pg-executor.ts` |
|
|
89
|
+
| a declared retirement as headers — `renderDeprecation` (RFC 9745 `Deprecation`, RFC 8594 `Sunset`, the `successor-version` link) and `recordDeprecatedCall` on the one `deprecated_calls_total`; `action` and `query` both project through it | `deprecation.ts` |
|
|
90
|
+
|
|
91
|
+
Each of the five, and `fingerprint`, `storeMode` and render's `contentHash`, has ONE implementation:
|
|
92
|
+
`bun run flight-copies` refuses a second by its shape (`X_HELPER_COPY`), whatever it is named.
|
|
69
93
|
|
|
70
94
|
## Errors are instructions
|
|
71
95
|
|
|
@@ -179,9 +203,9 @@ a job boundary the class is gone and the `code` is what survives — match on th
|
|
|
179
203
|
| Class | Code | Declared in |
|
|
180
204
|
|---|---|---|
|
|
181
205
|
| `ConfigInvalidError` | `X_CONFIG_INVALID` | `src/errors.ts` |
|
|
206
|
+
| `CookieInvalidError` | `X_COOKIE_INVALID` | `src/cookie.ts` |
|
|
182
207
|
| `CursorInvalidError` | `X_CURSOR_INVALID` | `src/cursor.ts` |
|
|
183
208
|
| `CursorSecretDevError` | `X_CURSOR_SECRET_DEV` | `src/dev-secrets.ts` |
|
|
184
|
-
| `EnvExampleDriftError` | `X_ENV_EXAMPLE_DRIFT` | `src/env-example.ts` |
|
|
185
209
|
| `EnvironmentInvalidError` | `X_ENVIRONMENT_INVALID` | `src/environment.ts` |
|
|
186
210
|
| `EnvMissingError` | `X_ENV_MISSING` | `src/errors.ts` |
|
|
187
211
|
| `ErrorReporterDsnInvalidError` | `X_ERROR_REPORTER_DSN_INVALID` | `src/error-reporter-sentry.ts` |
|
|
@@ -201,6 +225,7 @@ a job boundary the class is gone and the `code` is what survives — match on th
|
|
|
201
225
|
| `SealKeyUnknownError` | `X_SEAL_KEY_UNKNOWN` | `src/seal-errors.ts` |
|
|
202
226
|
| `SecretsFileInvalidError` | `X_SECRETS_FILE_INVALID` | `src/secrets-errors.ts` |
|
|
203
227
|
| `SecretsFileMissingError` | `X_SECRETS_FILE_MISSING` | `src/secrets-errors.ts` |
|
|
228
|
+
| `SecretsKeyAclError` | `X_SECRETS_KEY_ACL_FAILED` — Windows only: `icacls` could not restrict a new key file, so none was written | `src/secrets-key-file.ts` |
|
|
204
229
|
| `SecretsKeyInvalidError` | `X_SECRETS_KEY_INVALID` | `src/secrets-errors.ts` |
|
|
205
230
|
| `SecretsRingKeyInvalidError` | `X_SECRETS_KEY_INVALID` — a malformed entry of `ULTIMATE_SECRETS_RETIRED_KEYS` | `src/secrets-errors.ts` |
|
|
206
231
|
| `SecretsKeyMismatchError` | `X_SECRETS_KEY_MISMATCH` | `src/secrets-errors.ts` |
|
|
@@ -212,7 +237,7 @@ a job boundary the class is gone and the `code` is what survives — match on th
|
|
|
212
237
|
## Context
|
|
213
238
|
|
|
214
239
|
```ts
|
|
215
|
-
const ctx =
|
|
240
|
+
const ctx = ctxOf({ actor: agentActor({ id: 'mcp-1', scopes: ['post:publish'] }) });
|
|
216
241
|
await runWithContext(ctx, async () => {
|
|
217
242
|
const { actor, locale, tz, logger } = useContext(); // throws X_NO_CONTEXT outside
|
|
218
243
|
await withChildContext({ locale: 'es' }, () => render());
|
|
@@ -224,13 +249,13 @@ automatically; so does the root `logger` while a context is active. Add typed se
|
|
|
224
249
|
augmenting `CtxServices`; reach late-bound ones with `useService<T>('mail')`.
|
|
225
250
|
|
|
226
251
|
A service that reads the actor (`ctx.posts`, scoped to `ctx.actor.orgId`) registers once with
|
|
227
|
-
`defineService('posts', (ctx) => ({ ... }))`, at import time. `
|
|
252
|
+
`defineService('posts', (ctx) => ({ ... }))`, at import time. `ctxOf` and
|
|
228
253
|
`withChildContext` then build it fresh, bound to whichever actor they are constructing a ctx
|
|
229
254
|
for — importing the module that calls `defineService` is the registration, the same convention
|
|
230
|
-
`registerActions` uses. Passing `services: { posts: ... }` to `
|
|
255
|
+
`registerActions` uses. Passing `services: { posts: ... }` to `ctxOf` still works and
|
|
231
256
|
wins over a registered factory of the same name, for a test that wants to hand in a mock.
|
|
232
257
|
|
|
233
|
-
A factory runs again on **every** `
|
|
258
|
+
A factory runs again on **every** `ctxOf` / `withChildContext` call and is never cached,
|
|
234
259
|
because it closes over the ctx (actor, clock, tz) it was built for. `withChildContext` drops a
|
|
235
260
|
factory-managed name from what it carries forward on purpose: only an ad hoc service nobody
|
|
236
261
|
registered survives an actor swap unrebuilt.
|
|
@@ -283,9 +308,11 @@ safe for `x.manifest.json`. Omit `required` for required — `required: false` i
|
|
|
283
308
|
loosening. Never declare an env var for *which deploy this is* — that is `ULTIMATE_ENV`, below.
|
|
284
309
|
|
|
285
310
|
`.env.example` is a **projection** of that schema, never a second list:
|
|
286
|
-
`renderEnvExample(schema)` writes it
|
|
287
|
-
`
|
|
288
|
-
somebody else's `X_ENV_MISSING` on a
|
|
311
|
+
`renderEnvExample(schema)` writes it and `checkEnvExample(schema, text)` reports `missing` /
|
|
312
|
+
`extra` as data. The gate is `x verify`'s `manifest` step (`X_ENV_EXAMPLE_DRIFT`, fixed by
|
|
313
|
+
`x env example`) — the failure that otherwise arrives as somebody else's `X_ENV_MISSING` on a
|
|
314
|
+
variable nobody documented. `assertEnvExample` (key presence only, called by nothing) was deleted
|
|
315
|
+
in 25.0.0.
|
|
289
316
|
|
|
290
317
|
Loading `.env` is **Bun's**, not ours. `envFileCandidates()` states what it does, measured:
|
|
291
318
|
`.env` → `.env.<mode>` → `.env.local`, with `.env.local` skipped under test, and the mode being
|
|
@@ -312,6 +339,10 @@ fallback of its own; the caller does.
|
|
|
312
339
|
is not our key. This is the twin of `roles.ts` — `ROLE` says what the process does,
|
|
313
340
|
`ULTIMATE_ENV` says which deploy it belongs to.
|
|
314
341
|
|
|
342
|
+
`storeMode(Bun.env)` is the one answer to "memory store or database store?" for a seam with both —
|
|
343
|
+
`memory` under `test` (no database client is installed there), `database` everywhere else, `x dev`'s
|
|
344
|
+
embedded PGlite included. Never a `DATABASE_URL` truthiness check: `x dev` sets none.
|
|
345
|
+
|
|
315
346
|
## A secret is redacted by value, not by name
|
|
316
347
|
|
|
317
348
|
```ts
|
|
@@ -320,7 +351,24 @@ logger.info('boot', { dsn }); // {"dsn":"[redacted]"}
|
|
|
320
351
|
connect(revealSecret(dsn)); // the one greppable way out
|
|
321
352
|
```
|
|
322
353
|
|
|
323
|
-
`
|
|
354
|
+
`isRedactedKey(key)` is the one answer to "is this field a credential?" — the log line, the error
|
|
355
|
+
monitor's envelope and `@ultimat3/action`'s audit row all ask it. It matches the exact names
|
|
356
|
+
`redactKeys()` holds (`defineEnv` adds every `secret: true` variable) **and** a credential-bearing
|
|
357
|
+
name it was never told about: `password` / `passphrase` anywhere, `secret` as the last word, any
|
|
358
|
+
`…token` that is not a dedupe or paging key (`resetToken`, `githubToken`, `NPM_TOKEN`), key
|
|
359
|
+
material by its qualifier (`signingKey`, `masterKey`, `accessKeyId`), a value that embeds a
|
|
360
|
+
credential (`connectionString`, `dsn`, `databaseUrl`), the one-time codes
|
|
361
|
+
(`totpCode`, `recoveryCode`), session and bearer material (`credentials`, `jwt`, `bearer`,
|
|
362
|
+
`cookie`, `sessionId`, `sessionKey`, `privateKeyPem`), card data (`cvv`, `cvc`, a whole-word `pin`
|
|
363
|
+
such as `cardPin` or `pinCode`) and a stored hash of any of them (`passwordHash`, `tokenHash`,
|
|
364
|
+
`keyHash`). `@ultimat3/action` also asks it before storing an idempotent answer. It deliberately
|
|
365
|
+
leaves `idempotencyToken`, a paging token, `maxTokens`, an error `code`, `spinner`, `isPinned`,
|
|
366
|
+
`sessionStart` and `cookieName` readable — a redacted field is one an operator cannot correlate on.
|
|
367
|
+
|
|
368
|
+
`LOG_LEVEL` is refused when it is not one of `LOG_LEVELS` (lowercase), exactly as
|
|
369
|
+
`structuredLogger({ level })` refuses it; unset or empty is `info`.
|
|
370
|
+
|
|
371
|
+
A `Secret`
|
|
324
372
|
box catches the other case: `String()`, template literals, `+`, `JSON.stringify`, `console.log`,
|
|
325
373
|
the logger and an error's `meta` all render `[redacted]`, whatever key it sits under. It is
|
|
326
374
|
frozen and everything but `label` is non-enumerable, so `{ ...dsn }` cannot spread the value back
|
|
@@ -410,11 +458,11 @@ match with `sealAll()`; uniqueness cannot be held across keys.
|
|
|
410
458
|
## Time, ids, telemetry, drain
|
|
411
459
|
|
|
412
460
|
- Never call `Date.now()`. Take a `Clock`; tests pass `frozenClock('2026-07-26T10:00:00Z')`.
|
|
413
|
-
- `
|
|
461
|
+
- `uuidV7()` is UUIDv7: time-prefixed, monotonic within a millisecond, never backwards on clock
|
|
414
462
|
skew. `typedId<'post'>()` brands it so a post id cannot be passed where a user id is wanted.
|
|
415
463
|
- `withSpan('action.publishPost', fn)` is free until `configureTelemetry({ exporter })`.
|
|
416
464
|
Traces cross process boundaries via `traceparent()` / `parseTraceparent()`, whose ids come from
|
|
417
|
-
`traceId()` / `spanId()` — **never `
|
|
465
|
+
`traceId()` / `spanId()` — **never `uuidV7()`**, whose dashed 36 characters every collector
|
|
418
466
|
rejects. `isTraceId()` / `isSpanId()` are the one definition of the valid shape.
|
|
419
467
|
- **Sampling is honoured, not just propagated.** `startSpan` takes the parent's bit when there is
|
|
420
468
|
one, else asks the `Sampler`; `span.end()` exports nothing when the bit is 0. The default reads
|
|
@@ -554,12 +602,28 @@ never a silently wrong page.
|
|
|
554
602
|
| | |
|
|
555
603
|
|---|---|
|
|
556
604
|
| Signature | truncated HMAC-SHA256, compared in constant time |
|
|
557
|
-
| Secret | `configureCursorSigning()` at boot, else `ULTIMATE_CURSOR_SECRET`. **Read when a cursor is signed, never at import** — an app whose `openSecrets()` sets the variable during boot would otherwise sign every cursor with the dev key. Rotating it invalidates every open cursor |
|
|
605
|
+
| Secret | `configureCursorSigning()` at boot, else `ULTIMATE_CURSOR_SECRET`. An EMPTY value is unset — never an empty HMAC key — so `usesDevCursorSecret()` reports it and the boot refuses it outside a local environment. **Read when a cursor is signed, never at import** — an app whose `openSecrets()` sets the variable during boot would otherwise sign every cursor with the dev key. Rotating it invalidates every open cursor |
|
|
558
606
|
| Also keys | `keyedFingerprint(value, purpose)` — `h1:<key id>:<HMAC>` over `canonicalJson`, under a per-purpose key derived from this secret; the fingerprint to PERSIST (`@ultimat3/action`'s idempotency `requestHash`). `compareFingerprint` answers `match` / `mismatch` / `unverifiable` (other key), and still checks a legacy bare `fingerprint()` exactly. Rotating the secret makes in-window stored fingerprints `unverifiable` |
|
|
559
607
|
| Signed, not encrypted | the client already has these rows; what it must not do is *invent* a position |
|
|
560
|
-
| `usesDevCursorSecret()` | true while the shipped dev key is in use |
|
|
608
|
+
| `usesDevCursorSecret({ env? })` | true while the shipped dev key is in use — this process, or the table `env` as signing would read it (empty and the published key count as unset) |
|
|
609
|
+
| `CURSOR_SECRET_KEY` / `CURSOR_SECRET_FIX` | `'ULTIMATE_CURSOR_SECRET'` and the one `fix:` for `X_CURSOR_SECRET_DEV` (`export ULTIMATE_CURSOR_SECRET="$(openssl rand -hex 32)"`) — the boot refusal and `@ultimat3/cli`'s deploy contract (`.env.example`, `x env check`, `x doctor`) read both |
|
|
610
|
+
| `devSecretsRefused({ env? })` | THE rule for refusing a shipped dev secret: anything but `development`/`test`, and no named environment counts as production. The boot (`assertNoDevSecretsOutsideLocal`), `@ultimat3/storage`'s disk and the CLI's diagnostics all ask it |
|
|
561
611
|
| `resetCursorSigning()` | test seam: forget `configureCursorSigning` and fall back to the environment |
|
|
562
612
|
|
|
613
|
+
### One page shape
|
|
614
|
+
|
|
615
|
+
`Page<Row>` (`src/cursor-page.ts`) is what every cursor page answers — an entity `findMany`, a
|
|
616
|
+
`query`'s `.page()`, the `?_first=` HTTP envelope and the typed client: `{ rows, nextCursor,
|
|
617
|
+
hasMore }`, with ONE meaning. `nextCursor` is the cursor for the next page and `null` exactly when
|
|
618
|
+
`hasMore` is false, so `while (page.hasMore)` and `while (page.nextCursor !== null)` are the same
|
|
619
|
+
loop and both stop on the last page without fetching an empty one.
|
|
620
|
+
|
|
621
|
+
| | |
|
|
622
|
+
|---|---|
|
|
623
|
+
| The type | a union of `{ nextCursor: string; hasMore: true }` and `{ nextCursor: null; hasMore: false }` — a literal that disagrees is a build error (`type-pins.ts`), and `if (page.hasMore)` narrows `nextCursor` to `string` |
|
|
624
|
+
| `pageOf(rows, nextCursor)` | the only constructor: `hasMore` is derived from the cursor, never passed beside it. `null` (or `''`) is the last page |
|
|
625
|
+
| Re-exported | by `@ultimat3/query` as its `Page`; `@ultimat3/entity` exports none — import it from here |
|
|
626
|
+
|
|
563
627
|
## One bounded cache for every `Intl` formatter
|
|
564
628
|
|
|
565
629
|
```ts
|
|
@@ -607,16 +671,16 @@ prose has none.
|
|
|
607
671
|
```ts
|
|
608
672
|
import {
|
|
609
673
|
backoffDelay,
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
674
|
+
generationFence,
|
|
675
|
+
flightGate,
|
|
676
|
+
singleFlight,
|
|
613
677
|
isRetryableStatus,
|
|
614
678
|
} from '@ultimat3/core';
|
|
615
679
|
|
|
616
680
|
// One ceiling on work whose cost is memory, one run per key, one fence over the answer.
|
|
617
|
-
const gate =
|
|
618
|
-
const flights =
|
|
619
|
-
const fence =
|
|
681
|
+
const gate = flightGate({ maxConcurrent: 8, maxQueued: 64 }, { subject: 'jwks fetches' });
|
|
682
|
+
const flights = singleFlight({ deadlineMs: 30_000 });
|
|
683
|
+
const fence = generationFence('the jwks cache');
|
|
620
684
|
|
|
621
685
|
export async function jwks(url: string): Promise<Response> {
|
|
622
686
|
const issued = fence.generation();
|
|
@@ -648,14 +712,18 @@ nothing consulted it before deciding to try again. `As of 2026-08-23`.
|
|
|
648
712
|
| Export | The one answer | The question it settles |
|
|
649
713
|
|---|---|---|
|
|
650
714
|
| `backoffDelay({ attempt, base, max, factor?, curve?, jitter?, random? })` | one curve — `exponential \| linear \| fixed`, `full \| equal \| none` — 1-based `attempt`, clamped to `max` **before** jitter, rounded, and `0` rather than `NaN` | how long to wait. `random` is injectable, so a schedule is a unit test rather than a range |
|
|
651
|
-
| `
|
|
652
|
-
| `
|
|
653
|
-
| `
|
|
715
|
+
| `singleFlight({ deadlineMs?, schedule? })` → `run(key, work, join?)`, `size` | N callers on one key are ONE run | who pays for a miss. Eviction is identity-checked, so a load that settles late never drops the load that replaced it; `deadlineMs` frees the KEY a wedged load would hold forever — it never cancels the work and never rejects a joiner |
|
|
716
|
+
| `flightGate({ maxConcurrent, maxQueued }, { subject?, overflow? })` | one bound, one queue, one refusal | how many at once. Past the queue the answer is `X_FLIGHT_GATE_OVERLOADED` (503) and never a longer queue; the slot is HANDED to a waiter, never released and re-acquired. Both limits are screened at construction (`finiteCount`, 0 allowed); a width of 0 refuses every caller rather than queueing for a slot that never frees. `run(work, signal?)`: an abort while QUEUED removes the waiter and rejects with the abort's reason |
|
|
717
|
+
| `generationFence(subject)` → `generation()`, `bump()`, `guard(issued)` | whether an answer still applies | `X_SUPERSEDED` (499) and `isSuperseded(error)` — the piece nothing in the tree had. `guard` compares `!==`, never `<` |
|
|
654
718
|
| `isRetryableStatus(status)`, `RETRYABLE_STATUSES` | `>= 500`, plus 408, 409, 425, 429 | which HTTP answers are worth repeating |
|
|
655
|
-
| `
|
|
656
|
-
| `
|
|
719
|
+
| `jitterStatedDelay(waitMs, capMs, random?)` | the ONE rule for a delay a responder named | the stated wait as a FLOOR plus a spread in `[0, min(wait / 2, cap))`, so a burst told `Retry-After: 1` does not replay in lockstep. `retryDecision`'s `retry-after` path (floor clamped to `max`; `jitter: 'none'` gets the bare floor) and `@ultimat3/jobs`' rate-limit deferral and webhook throttle all take it |
|
|
720
|
+
| `retry(work, policy, { sleep, now?, random? })`, `retryDecision(policy, attempt, error, random?)` | the executor and the pure decision behind the classification | whether to try again at all. `clientFlight` is its one caller in the framework; `jobs`, `ai` and `db` each keep their own loop and delegate only the arithmetic and the classification |
|
|
721
|
+
| `clientFlight({ principal?, retry?, deadlineMs?, limit?, … })` → `run(plan)`, `keyFor(url, opts?)`, `bump()`, `generation()` | the five above composed into one typed-client call | dedup, supersession, retry, one wall-clock deadline and a concurrency ceiling, for a call whose dispatch the caller supplies. `@ultimat3/action` and `@ultimat3/query` import it from here and re-export only its types — one file, because both are tier 3 and neither may import the other |
|
|
657
722
|
| `isTransientFailure(error)` | what a CLIENT may send again | a declared `retryable`/`retry-after`, plus a dispatch that produced no response at all. It **inverts** `retryDecision`'s unclassified default on purpose: a caller's own `AbortError` and a foreign `TypeError` are terminal |
|
|
658
|
-
| `traceHeaders()`, `problemOf(text)`, `retryForStatus(code, status)`, `FRAMEWORK_CODE` | what a typed client puts on the wire and reads back off it | the W3C header (nothing at all when the span context is incomplete), a total `problem+json` read, and the classification a STATUS is allowed to give when nobody declared one for the code |
|
|
723
|
+
| `traceHeaders()`, `problemOf(text)`, `retryForStatus(code, status, retryAfterSeconds?)`, `FRAMEWORK_CODE` | what a typed client puts on the wire and reads back off it | the W3C header (nothing at all when the span context is incomplete), a total `problem+json` read, and the classification a STATUS is allowed to give when nobody declared one for the code — `retry-after` for a 429 or 503 that stated a delay, unless the code is declared `terminal` |
|
|
724
|
+
| `retryAfterSecondsOf(header, date)`, `MAX_RETRY_AFTER_SECONDS` | the one `Retry-After` reader | delta-seconds, or an IMF-fixdate measured against the same response's `Date` header (never the client's clock), capped at a day; `0`, a date already past and anything else `undefined` — only a positive delay is a statement (`retryAfterOf`'s outbound rule), so unstated falls back to the jittered curve. `clientTransport` passes it to `problemError` and to a caller's `decodeError(status, text, retryAfterSeconds)`, which carry it as `meta.retryAfterSeconds` for `statedDelayMs` |
|
|
725
|
+
| `withStatedDelay(meta, retryAfterSeconds)` | a remote error's `meta`, for both decoders (`problemError`, `@ultimat3/action`'s `RemoteActionError`) | the copied wire keys minus any `retryAfterSeconds` — the server never writes one in a body, framework meta being operator-only — plus the header's value when there was one. A body can never drive the wait |
|
|
726
|
+
| `remoteTitleOf(value)`, `MAX_REMOTE_TITLE_LENGTH` | a problem document's `title`, as untrusted display text | a non-blank string cut at 120 characters, or nothing. Handed to `UltimateError` as `remoteTitle`, used only when this realm registered no title for the code |
|
|
659
727
|
|
|
660
728
|
`retry`'s `policy.jitter` is **required** where `backoffDelay`'s defaults to `none`: a loop retrying
|
|
661
729
|
without jitter IS the thundering herd, so the mode is a decision each caller makes rather than one
|
|
@@ -678,7 +746,7 @@ read; `@ultimat3/jobs` re-exports both rather than keeping a second pair.
|
|
|
678
746
|
| `resolveConflict(policy, local, server, { clockField? })`, `ConflictPolicy`, `Row` | one conflict vocabulary, over ROWS | `last-write-wins` keeps the local row only when its numeric clock field (default `updatedAt`) is newer; no provable clock = the server's row |
|
|
679
747
|
| `AsyncState<T>` | `pending \| refreshing \| ready \| failed` | the type `realtime` produces and `ui` renders; tier 0 because neither may import the other |
|
|
680
748
|
|
|
681
|
-
`clientTransport` does not value-import `
|
|
749
|
+
`clientTransport` does not value-import `clientFlight` or `traceHeaders()` — measured sizes
|
|
682
750
|
are in its file header. A server-side caller that propagates a trace passes `traceHeaders()` in
|
|
683
751
|
`headers` itself.
|
|
684
752
|
|
|
@@ -732,9 +800,9 @@ is read, mapping it onto `X_IMAGE_UNSUPPORTED` / `X_IMAGE_TOO_LARGE` / `X_IMAGE_
|
|
|
732
800
|
caller branches on a Bun code.
|
|
733
801
|
|
|
734
802
|
Two files in `image/` are past the 200-line target and neither splits without inventing a seam:
|
|
735
|
-
`probe.ts` is one algorithm per format over header bytes, and `
|
|
803
|
+
`probe.ts` is one algorithm per format over header bytes, and `image-fixture.ts` is data. The 500-line
|
|
736
804
|
hard ceiling applies to both. Everything else in `image/` is under the target — deleting the
|
|
737
805
|
hand-rolled JPEG and PNG codecs is what put it there.
|
|
738
806
|
|
|
739
|
-
`image/
|
|
807
|
+
`image/image-fixture.ts` is byte-exact output from Pillow and ffmpeg on purpose: a codec that only round
|
|
740
808
|
trips against itself proves nothing. Never regenerate a fixture with our own encoder.
|
package/package.json
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "25.0.0",
|
|
4
4
|
"description": "Ultimate's foundation: errors, context, env, config, clock, ids, logging, telemetry, lifecycle",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": [
|
|
8
|
-
"./src/context.ts",
|
|
9
8
|
"./src/core-error-codes.ts",
|
|
10
|
-
"./src/
|
|
11
|
-
"./src/schema-error-codes.ts",
|
|
12
|
-
"./src/secrets-errors.ts"
|
|
9
|
+
"./src/schema-error-codes.ts"
|
|
13
10
|
],
|
|
14
11
|
"repository": {
|
|
15
12
|
"type": "git",
|
|
@@ -33,13 +30,13 @@
|
|
|
33
30
|
"LICENSE"
|
|
34
31
|
],
|
|
35
32
|
"engines": {
|
|
36
|
-
"bun": ">=1.4.
|
|
33
|
+
"bun": ">=1.4.2"
|
|
37
34
|
},
|
|
38
35
|
"scripts": {
|
|
39
36
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
40
37
|
"test": "bun test"
|
|
41
38
|
},
|
|
42
39
|
"dependencies": {
|
|
43
|
-
"@ultimat3/schema": "
|
|
40
|
+
"@ultimat3/schema": "25.0.0"
|
|
44
41
|
}
|
|
45
42
|
}
|
package/src/actor.ts
CHANGED
|
@@ -185,6 +185,15 @@ export function isAnonymous(actor: Actor): boolean {
|
|
|
185
185
|
return actor.kind === 'anonymous';
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/**
|
|
189
|
+
* The context's actor as a policy reads it: core models "nobody" as the anonymous actor, policy as
|
|
190
|
+
* `null` — the value that turns a missing session into `X_UNAUTHENTICATED` instead of a bare
|
|
191
|
+
* denial. Declared here, once, so action, query, MCP and a page all map it the same way.
|
|
192
|
+
*/
|
|
193
|
+
export function actorOf(ctx: { readonly actor: Actor }): Actor | null {
|
|
194
|
+
return isAnonymous(ctx.actor) ? null : ctx.actor;
|
|
195
|
+
}
|
|
196
|
+
|
|
188
197
|
export function hasRole(actor: Actor, role: string): boolean {
|
|
189
198
|
return actor.roles.includes(role);
|
|
190
199
|
}
|
package/src/address-class.ts
CHANGED
|
@@ -119,16 +119,22 @@ function classifyV6(g: readonly number[]): AddressClass {
|
|
|
119
119
|
return 'public';
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/** The literal itself: trimmed, unbracketed, its IPv6 zone id dropped. */
|
|
123
|
+
function literalOf(address: string): string {
|
|
124
|
+
let text = address.trim();
|
|
125
|
+
if (text.startsWith('[') && text.endsWith(']')) text = text.slice(1, -1);
|
|
126
|
+
const zone = text.indexOf('%');
|
|
127
|
+
if (zone !== -1 && text.includes(':')) text = text.slice(0, zone);
|
|
128
|
+
return text;
|
|
129
|
+
}
|
|
130
|
+
|
|
122
131
|
/**
|
|
123
132
|
* The class of an IP address LITERAL — IPv4, IPv6, bracketed `[::1]`, a zone id `fe80::1%eth0`,
|
|
124
133
|
* and every IPv6 form carrying an IPv4 address. `undefined` means "not an address literal": a
|
|
125
134
|
* hostname must be resolved first and each resolved address classified, never this string.
|
|
126
135
|
*/
|
|
127
136
|
export function classifyAddress(address: string): AddressClass | undefined {
|
|
128
|
-
|
|
129
|
-
if (text.startsWith('[') && text.endsWith(']')) text = text.slice(1, -1);
|
|
130
|
-
const zone = text.indexOf('%');
|
|
131
|
-
if (zone !== -1 && text.includes(':')) text = text.slice(0, zone);
|
|
137
|
+
const text = literalOf(address);
|
|
132
138
|
if (text.includes(':')) {
|
|
133
139
|
const groups = parseV6(text);
|
|
134
140
|
return groups === undefined ? undefined : classifyV6(groups);
|
|
@@ -141,3 +147,33 @@ export function classifyAddress(address: string): AddressClass | undefined {
|
|
|
141
147
|
export function isPublicAddress(address: string): boolean {
|
|
142
148
|
return classifyAddress(address) === 'public';
|
|
143
149
|
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The NETWORK a caller's address names, for keying a per-caller budget: IPv4 as itself, an
|
|
153
|
+
* IPv4-mapped IPv6 (`::ffff:a.b.c.d`) as the IPv4 it carries, any other IPv6 as its /64 in RFC 5952
|
|
154
|
+
* form (`2001:db8:1:2::/64`). A /64 is the smallest block one subscriber is handed, so keying on
|
|
155
|
+
* the full IPv6 string gave one host ~2^64 budgets. Anything that is not an address literal is
|
|
156
|
+
* returned unchanged — the caller's key stays whatever it was, never a guessed network.
|
|
157
|
+
*/
|
|
158
|
+
export function addressNetwork(address: string): string {
|
|
159
|
+
const text = literalOf(address);
|
|
160
|
+
if (!text.includes(':')) {
|
|
161
|
+
const v4 = parseV4(text);
|
|
162
|
+
return v4 === undefined ? address : text;
|
|
163
|
+
}
|
|
164
|
+
const g = parseV6(text);
|
|
165
|
+
if (g === undefined) return address;
|
|
166
|
+
if (g.slice(0, 5).every((group) => group === 0) && g[5] === 0xffff) {
|
|
167
|
+
const v4 = embeddedV4(g);
|
|
168
|
+
return [24, 16, 8, 0].map((shift) => String(Math.floor(v4 / 2 ** shift) % 256)).join('.');
|
|
169
|
+
}
|
|
170
|
+
const prefix = g.slice(0, 4);
|
|
171
|
+
// RFC 5952: the zero run reaching into the (zero) interface half is always the longest, so
|
|
172
|
+
// compression starts at the first zero group from which the prefix stays zero.
|
|
173
|
+
let cut = 4;
|
|
174
|
+
while (cut > 0 && prefix[cut - 1] === 0) cut -= 1;
|
|
175
|
+
return `${prefix
|
|
176
|
+
.slice(0, cut)
|
|
177
|
+
.map((group) => group.toString(16))
|
|
178
|
+
.join(':')}::/64`;
|
|
179
|
+
}
|
package/src/assert.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { renderCauseValue } from './error-render';
|
|
5
5
|
import { UltimateError } from './errors';
|
|
6
6
|
|
|
7
|
-
export interface
|
|
7
|
+
export interface AssertCodedOptions {
|
|
8
8
|
readonly docs?: string | undefined;
|
|
9
9
|
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
10
10
|
}
|
|
@@ -25,12 +25,16 @@ export function assertNever(value: never, fix?: string): never {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Throw `code` with `cause` and `fix` unless `condition` holds — `assert` with a dedicated code.
|
|
30
|
+
* Not `invariant`: that name is `@ultimat3/entity`'s, the declaration of an entity invariant.
|
|
31
|
+
*/
|
|
32
|
+
export function assertCoded(
|
|
29
33
|
condition: unknown,
|
|
30
34
|
code: string,
|
|
31
35
|
cause: string,
|
|
32
36
|
fix: string,
|
|
33
|
-
options?:
|
|
37
|
+
options?: AssertCodedOptions,
|
|
34
38
|
): asserts condition {
|
|
35
39
|
if (condition) return;
|
|
36
40
|
throw new UltimateError({
|
|
@@ -42,7 +46,7 @@ export function invariant(
|
|
|
42
46
|
});
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
/** `
|
|
49
|
+
/** `assertCoded` with the generic code, for checks that have no dedicated code yet. */
|
|
46
50
|
export function assert(condition: unknown, cause: string, fix: string): asserts condition {
|
|
47
|
-
|
|
51
|
+
assertCoded(condition, 'X_INVARIANT', cause, fix);
|
|
48
52
|
}
|