@ultimat3/core 23.0.0 → 24.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/src/otlp.ts CHANGED
@@ -96,7 +96,10 @@ function parseEndpoint(signal: OtlpSignal, raw: string, perSignal: boolean, env:
96
96
  // The spec's own asymmetry, not ours: a per-signal endpoint is the full URL an operator chose,
97
97
  // while the generic one is a base the signal path is appended to.
98
98
  if (perSignal) return url.toString();
99
- return `${url.toString().replace(/\/+$/, '')}/v1/${signal}`;
99
+ // On the PATH, never on the string: `http://collector:4318?tenant=a` concatenated to
100
+ // `…/?tenant=a/v1/traces`, a request to `/` whose query merely ends in the receiver's path.
101
+ url.pathname = `${url.pathname.replace(/\/+$/, '')}/v1/${signal}`;
102
+ return url.toString();
100
103
  }
101
104
 
102
105
  /** The endpoint an operator configured, or `undefined` when they configured none. */
@@ -157,32 +160,43 @@ export function otlpEndpoint(
157
160
  * and no fix. Refused instead, naming the variable and the header KEY: the value is the
158
161
  * collector's credential and a `cause:` is folded into a log line.
159
162
  */
160
- function decodeHeaderValue(key: string, raw: string): string {
163
+ function decodeHeaderValue(variable: string, key: string, raw: string): string {
161
164
  try {
162
165
  return decodeURIComponent(raw);
163
166
  } catch {
164
167
  throw new OtlpHeadersInvalidError({
165
- cause: `${OTLP_HEADERS_KEY} carries a malformed percent-escape in the "${key}" value, so the header cannot be decoded`,
166
- fix: `set ${OTLP_HEADERS_KEY}=${key}=<encoded>, where <encoded> is what bun -e 'console.log(encodeURIComponent(process.argv[1]))' <value> prints — or drop the stray % from the "${key}" value if it was meant literally`,
168
+ cause: `${variable} carries a malformed percent-escape in the "${key}" value, so the header cannot be decoded`,
169
+ fix: `set ${variable}=${key}=<encoded>, where <encoded> is what bun -e 'console.log(encodeURIComponent(process.argv[1]))' <value> prints — or drop the stray % from the "${key}" value if it was meant literally`,
167
170
  meta: { header: key },
168
171
  });
169
172
  }
170
173
  }
171
174
 
172
- /** `key=value,key2=value2`, percent-decoded — the spec's format for collector auth headers. */
175
+ /**
176
+ * `key=value,key2=value2`, percent-decoded — the spec's format for collector auth headers.
177
+ *
178
+ * With a `signal`, `OTEL_EXPORTER_OTLP_<SIGNAL>_HEADERS` REPLACES the generic variable for that
179
+ * signal, which is the spec's rule and the one `ENDPOINT` and `PROTOCOL` already followed here:
180
+ * only the generic one was read, so a collector that authenticates traces and metrics with
181
+ * different keys got the same key on both and rejected one of them.
182
+ */
173
183
  export function otlpHeaders(
174
184
  explicit?: Readonly<Record<string, string>> | undefined,
175
185
  env: OtlpEnv = process.env,
186
+ signal?: OtlpSignal | undefined,
176
187
  ): Record<string, string> {
177
188
  const headers: Record<string, string> = { 'content-type': 'application/json' };
178
- const raw = env[OTLP_HEADERS_KEY];
189
+ const specific = signal === undefined ? undefined : signalKey(signal, 'HEADERS');
190
+ const variable =
191
+ specific !== undefined && (env[specific] ?? '').trim() !== '' ? specific : OTLP_HEADERS_KEY;
192
+ const raw = env[variable];
179
193
  if (raw !== undefined) {
180
194
  for (const pair of raw.split(',')) {
181
195
  const index = pair.indexOf('=');
182
196
  if (index <= 0) continue;
183
197
  const key = pair.slice(0, index).trim().toLowerCase();
184
198
  if (key === '') continue;
185
- headers[key] = decodeHeaderValue(key, pair.slice(index + 1).trim());
199
+ headers[key] = decodeHeaderValue(variable, key, pair.slice(index + 1).trim());
186
200
  }
187
201
  }
188
202
  for (const [key, value] of Object.entries(explicit ?? {})) headers[key.toLowerCase()] = value;
@@ -202,22 +216,39 @@ export interface OtlpKeyValue {
202
216
  readonly value: OtlpAnyValue;
203
217
  }
204
218
 
205
- function anyValue(value: AttributeValue): OtlpAnyValue {
219
+ /**
220
+ * `undefined` for a number the wire cannot spell. `NaN` and `±Infinity` serialise as
221
+ * `{"doubleValue":null}`, and a validating collector rejects the WHOLE batch for it — `postOtlp`
222
+ * only warns, so one bad gauge silently cost every span beside it. Dropped, as a missing
223
+ * attribute is the honest reading of "not a number".
224
+ */
225
+ function anyValue(value: AttributeValue): OtlpAnyValue | undefined {
206
226
  if (typeof value === 'string') return { stringValue: value };
207
227
  if (typeof value === 'boolean') return { boolValue: value };
208
228
  if (typeof value === 'number') {
229
+ if (!Number.isFinite(value)) return undefined;
209
230
  // `intValue` is a 64-bit field, so the JSON encoding spells it as a string. A float that
210
- // happens to be integral is still a double to whoever queries it; `Number.isInteger` is the
211
- // only signal available and matches what every other OTLP/JSON encoder does.
212
- return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
231
+ // happens to be integral is still a double to whoever queries it; SAFE-integer is the signal,
232
+ // because past 2^53 `String(value)` is `"1e+21"` — exponent notation is not an int64.
233
+ return Number.isSafeInteger(value) ? { intValue: String(value) } : { doubleValue: value };
234
+ }
235
+ const values: OtlpAnyValue[] = [];
236
+ for (const item of value) {
237
+ const encoded = anyValue(item);
238
+ if (encoded !== undefined) values.push(encoded);
213
239
  }
214
- return { arrayValue: { values: value.map((item) => anyValue(item)) } };
240
+ return { arrayValue: { values } };
215
241
  }
216
242
 
217
243
  export function otlpAttributes(
218
244
  attributes: Readonly<Record<string, AttributeValue>>,
219
245
  ): readonly OtlpKeyValue[] {
220
- return Object.entries(attributes).map(([key, value]) => ({ key, value: anyValue(value) }));
246
+ const out: OtlpKeyValue[] = [];
247
+ for (const [key, raw] of Object.entries(attributes)) {
248
+ const value = anyValue(raw);
249
+ if (value !== undefined) out.push({ key, value });
250
+ }
251
+ return out;
221
252
  }
222
253
 
223
254
  /** Epoch ms -> the string of nanoseconds OTLP/JSON wants, without losing precision to a float. */
@@ -0,0 +1,15 @@
1
+ // The one structural Postgres seam: `query(text, values)` answering rows. Declared at tier 0 so
2
+ // `@ultimat3/http`, `auth`, `action` and `jobs` share it while staying free of `@ultimat3/db` —
3
+ // four packages each declared it, and a copy is a fifth place for the contract to drift.
4
+
5
+ /**
6
+ * One method, positional parameters. **`Bun.sql` does not satisfy it** — `Bun.sql.query` is
7
+ * `undefined`; it is a tagged template whose positional form is `unsafe`, so `{ executor: Bun.sql }`
8
+ * would `TypeError` on the first statement. What satisfies it is a client that already speaks
9
+ * `(text, values)`, wrapped in one line — `@ultimat3/cli`'s `pgExecutorFor(client)` over
10
+ * `@ultimat3/db`'s `DbClient.query({ text, values })` is the framework's own — or a transaction
11
+ * handle, which is a client on its own connection. It answers rows, never a command tag.
12
+ */
13
+ export interface PgExecutor {
14
+ query<R>(sql: string, params: readonly unknown[]): Promise<readonly R[]>;
15
+ }
@@ -0,0 +1,37 @@
1
+ // Single responsibility: the ONE answer to "may a caller read this 5xx code's `cause`?". Moved down
2
+ // from `@ultimat3/http`'s `problem-meta.ts` because three renderers send an error off the box — the
3
+ // HTTP problem document, the MCP error data and the agent `tool_result` — and only the first asked.
4
+ // Tier 0, so `@ultimat3/mcp` and `@ultimat3/ai` (tier 4) can ask it without importing each other.
5
+
6
+ /**
7
+ * The 5xx codes whose `cause` a caller may read. Every other 5xx document carries the code and the
8
+ * request id and a fixed sentence: `X_DB_STATEMENT_FAILED` has a status row, so the old "blank only
9
+ * what nobody classified" rule served the Postgres message and the SQL statement in a production
10
+ * 500. The framework's four are refusals whose cause IS the instruction — back off, retry.
11
+ */
12
+ const FRAMEWORK_PUBLIC_CAUSE: ReadonlySet<string> = new Set([
13
+ 'X_DRAINING',
14
+ 'X_OVERLOADED',
15
+ 'X_FLIGHT_GATE_OVERLOADED',
16
+ 'X_TIMEOUT',
17
+ ]);
18
+ const APP_PUBLIC_CAUSE = new Set<string>();
19
+
20
+ /** Whether a 5xx document for `code` may carry its authored `cause`. */
21
+ export const hasPublicCause = (code: string): boolean =>
22
+ FRAMEWORK_PUBLIC_CAUSE.has(code) || APP_PUBLIC_CAUSE.has(code);
23
+
24
+ /**
25
+ * The WRITE half, and not an app's door: an app declares a public cause through
26
+ * `registerProblemMeta({ CODE: { publicCause: true } })` in `@ultimat3/http`, which refuses a
27
+ * framework-owned code first and then calls this. The set lives here only so the predicate above
28
+ * has one table to read whichever renderer asks.
29
+ */
30
+ export const registerPublicCause = (code: string): void => {
31
+ APP_PUBLIC_CAUSE.add(code);
32
+ };
33
+
34
+ /** Test seam. Production registers once at boot and never unregisters. */
35
+ export const resetPublicCauses = (): void => {
36
+ APP_PUBLIC_CAUSE.clear();
37
+ };
package/src/registrar.ts CHANGED
@@ -27,6 +27,23 @@ export const PRIMITIVE_KINDS = [
27
27
 
28
28
  export type PrimitiveKind = (typeof PRIMITIVE_KINDS)[number];
29
29
 
30
+ /**
31
+ * The package that ANNOUNCES each kind's registrar — a kind is not a package name. Both `fix:`
32
+ * lines below spliced `@ultimat3/${kind}`, so a missing `task` registrar told its reader to
33
+ * `bun add @ultimat3/task`, a package the registry has never had. A `Record` over the union, so a
34
+ * ninth kind fails to compile here before it can ship a fix that 404s.
35
+ */
36
+ export const PRIMITIVE_PACKAGES = Object.freeze<Record<PrimitiveKind, string>>({
37
+ action: '@ultimat3/action',
38
+ entity: '@ultimat3/entity',
39
+ job: '@ultimat3/jobs',
40
+ mutator: '@ultimat3/action',
41
+ policy: '@ultimat3/policy',
42
+ query: '@ultimat3/query',
43
+ route: '@ultimat3/render',
44
+ task: '@ultimat3/jobs',
45
+ });
46
+
30
47
  /** One factory over one primitive: the export's name, the package that ships it, what it returns. */
31
48
  export interface PrimitiveFactory {
32
49
  readonly factory: string;
@@ -103,9 +120,9 @@ export function registerPrimitiveRegistrar(kind: PrimitiveKind, registrar: Modul
103
120
  code: 'X_REGISTRAR_CONFLICT',
104
121
  cause: `two different ${kind} registrars are loaded, so ${kind} primitives would split across two registries`,
105
122
  // One command, because a `fix:` is pasted verbatim: collapsing every range on the package
106
- // to one resolved version is the repair. `bun pm why @ultimat3/<kind>` names the dependents
107
- // when a range genuinely disagrees and the update cannot converge on its own.
108
- fix: `bun update @ultimat3/${kind}`,
123
+ // to one resolved version is the repair. `bun pm why <package>` names the dependents when
124
+ // a range genuinely disagrees and the update cannot converge on its own.
125
+ fix: `bun update ${PRIMITIVE_PACKAGES[kind]}`,
109
126
  meta: { kind },
110
127
  });
111
128
  }
@@ -127,7 +144,7 @@ export function primitiveRegistrar(kind: PrimitiveKind): ModuleRegistrar {
127
144
  throw new UltimateError({
128
145
  code: 'X_REGISTRAR_MISSING',
129
146
  cause: `no ${kind} registrar is loaded, so ${kind} primitives cannot be registered`,
130
- fix: `bun add @ultimat3/${kind}`,
147
+ fix: `bun add ${PRIMITIVE_PACKAGES[kind]}`,
131
148
  meta: { kind },
132
149
  });
133
150
  }
package/src/retry.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  import { type BackoffCurve, backoffDelay, type JitterMode, type Random } from './backoff';
7
7
  import { systemClock } from './clock';
8
8
  import { classifyThrown, type ErrorRetry, statedDelayMs } from './error-retry';
9
+ import { finiteCount, finiteOption } from './finite-option';
9
10
 
10
11
  export interface RetryPolicy {
11
12
  /** Total attempts INCLUDING the first. `attempts: 1` means no retry. */
@@ -63,6 +64,9 @@ export function retryDecision(
63
64
  error: unknown,
64
65
  random?: Random,
65
66
  ): RetryDecision {
67
+ // Screened HERE and not only in `retry()`: this function is exported so a caller can write its
68
+ // own loop, and `attempt >= NaN` is false for every attempt — the loop that asks it never ends.
69
+ const attempts = finiteCount('a retry policy', 'attempts', policy.attempts);
66
70
  const classification = classifyThrown(error);
67
71
  const stop = (stoppedBy: RetryStopReason): RetryDecision => ({
68
72
  retry: false,
@@ -74,7 +78,7 @@ export function retryDecision(
74
78
  });
75
79
 
76
80
  if (classification === 'terminal') return stop('terminal');
77
- if (attempt >= policy.attempts) return stop('attempts-exhausted');
81
+ if (attempt >= attempts) return stop('attempts-exhausted');
78
82
 
79
83
  const computed = backoffDelay({
80
84
  attempt,
@@ -111,6 +115,16 @@ export async function retry<T>(
111
115
  policy: RetryPolicy,
112
116
  deps: RetryDeps,
113
117
  ): Promise<T> {
118
+ // Both bounds are refused BEFORE the first try: a policy that cannot stop the loop is a defect in
119
+ // the call, and running the work once first would report it as the work's own failure.
120
+ // `finiteOption` for the budget, not `finiteCount`: it is a duration a caller computes from a
121
+ // monotonic clock, so a fraction is real and a spent (negative) one means "do not wait at all".
122
+ // Zero stays legal and means what it always did — one try, no retry (`retry.test.ts` pins it).
123
+ finiteCount('a retry policy', 'attempts', policy.attempts);
124
+ const budget =
125
+ policy.timeBudgetMs === undefined
126
+ ? undefined
127
+ : finiteOption('a retry policy', 'timeBudgetMs', policy.timeBudgetMs);
114
128
  const now = deps.now ?? ((): number => systemClock.monotonic());
115
129
  // Read once even when no budget is set: a clock call per attempt would be a cost the common case
116
130
  // does not owe. `startedAt` is only compared against when `timeBudgetMs` is present.
@@ -122,7 +136,6 @@ export async function retry<T>(
122
136
  } catch (error) {
123
137
  const decision = retryDecision(policy, attempt, error, deps.random);
124
138
  if (!decision.retry) throw error;
125
- const budget = policy.timeBudgetMs;
126
139
  // Decided BEFORE the wait, never after: a loop that sleeps and then discovers it is out of
127
140
  // budget has already spent the caller's deadline on a wait nobody could use.
128
141
  if (budget !== undefined && now() - startedAt + decision.delayMs > budget) throw error;
@@ -0,0 +1,36 @@
1
+ // Single responsibility: the ONE precedence between route patterns, as an integer. Tier 0 because
2
+ // three tier-4 readers need it and may not import each other — `@ultimat3/render`'s
3
+ // `compilePattern` (ISR, sitemap extras, the admin), and `@ultimat3/pwa`'s worker rule order.
4
+
5
+ /**
6
+ * Per segment, how strongly it claims a pathname: a literal 3, a `:param` 2, a `*catch-all` 1, and
7
+ * 4 where the pattern has already ENDED — `/` outranks `/*rest` and `/docs` outranks `/docs/*path`
8
+ * at the bare prefix, as the request router's own terminal outranks its catch-all.
9
+ */
10
+ const SEGMENT_WEIGHT = { literal: 3, param: 2, catchAll: 1, ended: 4 } as const;
11
+ const WEIGHT_BASE = 5;
12
+ /** 5^22 < 2^53: every rank is an exact integer. A deeper pattern ties past its 22nd segment. */
13
+ export const ROUTE_RANK_SEGMENTS = 22;
14
+
15
+ /**
16
+ * The request router's precedence (`@ultimat3/http`'s trie) as ONE number, higher wins: segment by
17
+ * segment, the first segment where two patterns differ decides — literal over `:param` over
18
+ * `*catch-all`. Positional, never a sum: the 100/10/1 sum this replaced ranked `/:a/b/c` above
19
+ * `/a/:x/:y` for `/a/b/c`, where the trie takes the literal first segment. Compare two ranks only
20
+ * with each other; the value itself means nothing.
21
+ */
22
+ export function routeRank(pattern: string): number {
23
+ const weights = pattern
24
+ .split('/')
25
+ .filter((segment) => segment.length > 0)
26
+ .map((segment) => {
27
+ if (segment.startsWith('*')) return SEGMENT_WEIGHT.catchAll;
28
+ if (segment.startsWith(':')) return SEGMENT_WEIGHT.param;
29
+ return SEGMENT_WEIGHT.literal;
30
+ });
31
+ let rank = 0;
32
+ for (let i = 0; i < ROUTE_RANK_SEGMENTS; i += 1) {
33
+ rank = rank * WEIGHT_BASE + (weights[i] ?? SEGMENT_WEIGHT.ended);
34
+ }
35
+ return rank;
36
+ }
@@ -13,7 +13,7 @@ export interface OriginEvidence {
13
13
  readonly secFetchSite: string | null;
14
14
  /** An EXACT allowance for a sibling origin — never a wildcard, never a suffix match. */
15
15
  readonly listed: (origin: string) => boolean;
16
- /** Where the operator adds an origin, named in the refusal: `http.cors.origins`, `SYNC_ORIGINS`. */
16
+ /** Where the operator adds an origin, named in the refusal: `http.cors.origins`, `APP_URL`. */
17
17
  readonly listName: string;
18
18
  }
19
19
 
package/src/sampler.ts CHANGED
@@ -136,9 +136,13 @@ export function samplerFromEnv(
136
136
  return ratioSampler(ratio);
137
137
  case 'parentbased_always_off':
138
138
  return parentBasedRatioSampler(0);
139
+ case 'parentbased_always_on':
140
+ // Its own case because it takes NO arg: sharing the ratio branch let a leftover
141
+ // `OTEL_TRACES_SAMPLER_ARG=0.1` thin the roots of a sampler whose name says always.
142
+ return parentBasedRatioSampler(1);
139
143
  default:
140
- // `parentbased_always_on`, `parentbased_traceidratio` and the unset case are one sampler:
141
- // honour the parent, else the ratio — which is 1 when nothing set an arg.
144
+ // `parentbased_traceidratio` and the unset case are one sampler: honour the parent, else
145
+ // the ratio — which is 1 when nothing set an arg.
142
146
  return parentBasedRatioSampler(ratio);
143
147
  }
144
148
  }
@@ -77,8 +77,19 @@ export class SecretsKeyInvalidError extends UltimateError {
77
77
  constructor(input: { at: string; found: number; expected: number }) {
78
78
  super({
79
79
  code: 'X_SECRETS_KEY_INVALID',
80
- cause: `the master key in ${input.at} is ${input.found} character(s); an AES-256 key is ${input.expected} lowercase hex characters`,
81
- fix: `export ULTIMATE_SECRETS_KEY="$(cat .secrets.key)" # the key file holds the ${input.expected} characters verbatim, no newline of its own`,
80
+ // The lost-key sentence is CAUSE, not fix: a `fix:` is one command, and no command restores
81
+ // a key file — so the file branch says what cannot be done here and the fix measures it.
82
+ cause:
83
+ input.at === 'ULTIMATE_SECRETS_KEY'
84
+ ? `the master key in ${input.at} is ${input.found} character(s); an AES-256 key is ${input.expected} lowercase hex characters`
85
+ : `the master key in ${input.at} is ${input.found} character(s); an AES-256 key is ${input.expected} lowercase hex characters — the key FILE is what is wrong, so re-exporting it changes nothing: restore it from wherever the team keeps the key, because a lost key cannot be recovered or regenerated`,
86
+ // Branches on WHERE the bad key was read. From the variable, re-reading the file repairs
87
+ // it. From the FILE, that same line reads the truncated file into the variable and is
88
+ // refused again, so the command is the measurement that says when the restore worked.
89
+ fix:
90
+ input.at === 'ULTIMATE_SECRETS_KEY'
91
+ ? `export ULTIMATE_SECRETS_KEY="$(cat .secrets.key)" # the key file holds the ${input.expected} characters on one line`
92
+ : `wc -c ${renderFixShellArg(input.at, '<the key file the cause names>')} # ${input.expected + 1} is a whole key and its newline; any other count is the truncated or padded file to restore`,
82
93
  meta: { at: input.at },
83
94
  });
84
95
  }
@@ -99,7 +110,7 @@ export class SecretsRingKeyInvalidError extends UltimateError {
99
110
  super({
100
111
  code: 'X_SECRETS_KEY_INVALID',
101
112
  cause: `the master key in ${input.at} is ${input.found} character(s); an AES-256 key is ${input.expected} lowercase hex characters`,
102
- fix: `x secrets edit # ${variable} holds ${input.expected}-character lowercase hex keys separated by commas: correct or remove the entry the cause names`,
113
+ fix: `x secrets edit # ${variable} holds ${input.expected}-character lowercase hex keys separated by commas: correct or remove the entry the cause names — the variable is a line of secrets.enc.json, and a platform that ALSO sets it wins, so correct it there too`,
103
114
  meta: { at: input.at },
104
115
  });
105
116
  }
@@ -45,7 +45,11 @@ function endOfInterpolation(text: string, from: number): number {
45
45
  let i = from;
46
46
  while (i < text.length) {
47
47
  const ch = text[i] as string;
48
- if (ch === '/' && (text[i + 1] === '/' || text[i + 1] === '*')) {
48
+ // A regex before the comment test: in `${p.replace(/^\//, '')}` the `\//` is the regex's own
49
+ // escaped slash and its close, and read as a `//` comment it swallowed the `}` and the closing
50
+ // backtick — every declaration below masked as template text (`admin/src/errors.ts` hid one).
51
+ if (ch === '/' && opensRegex(text, i, from)) i = endOfRegex(text, i);
52
+ else if (ch === '/' && (text[i + 1] === '/' || text[i + 1] === '*')) {
49
53
  const line = text[i + 1] === '/';
50
54
  const end = line ? text.indexOf('\n', i) : text.indexOf('*/', i + 2);
51
55
  i = end === -1 ? text.length : line ? end : end + 2;
@@ -66,19 +70,21 @@ function endOfInterpolation(text: string, from: number): number {
66
70
  * Whether the `/` at `at` opens a regex rather than divides — the call no scanner without a parser
67
71
  * avoids. A regex cannot follow what ends an expression: an identifier that is not one of the words
68
72
  * above, a number, `)`, `]`, a string's closing quote. Every other position is an operator's and
69
- * opens one; `</` and `/>` are JSX delimiters. Read from the masked prefix, so a comment is space.
73
+ * opens one; `</` and `/>` are JSX delimiters; `//` and `/*` open comments. Read from the masked
74
+ * prefix, so a comment is space — or, inside a `${}`, from the raw body from `floor` on, where the
75
+ * placeholder's own start is an operator position.
70
76
  */
71
- function opensRegex(out: readonly string[], at: number): boolean {
72
- if (out[at + 1] === '>') return false;
77
+ function opensRegex(out: ArrayLike<string>, at: number, floor = 0): boolean {
78
+ if (out[at + 1] === '>' || out[at + 1] === '/' || out[at + 1] === '*') return false;
73
79
  let i = at - 1;
74
- while (i >= 0 && /\s/.test(out[i] as string)) i -= 1;
75
- if (i < 0) return true;
80
+ while (i >= floor && /\s/.test(out[i] as string)) i -= 1;
81
+ if (i < floor) return true;
76
82
  const ch = out[i] as string;
77
83
  if (ch === '<' || ch === ')' || ch === ']' || QUOTES.has(ch)) return false;
78
84
  if (!WORD.test(ch)) return true;
79
85
  let start = i;
80
- while (start >= 0 && WORD.test(out[start] as string)) start -= 1;
81
- return REGEX_AFTER_WORDS.has(out.slice(start + 1, i + 1).join(''));
86
+ while (start >= floor && WORD.test(out[start] as string)) start -= 1;
87
+ return REGEX_AFTER_WORDS.has(Array.prototype.slice.call(out, start + 1, i + 1).join(''));
82
88
  }
83
89
 
84
90
  /**
@@ -0,0 +1,23 @@
1
+ // Which store backs a framework seam in this process — the in-memory one or the database — decided
2
+ // once. Three apps each wrote the ternary with a different predicate, and under `x dev` one seam
3
+ // sat in memory while every repository read the embedded Postgres.
4
+
5
+ import { resolveEnvironment } from './environment';
6
+
7
+ export const STORE_MODES = ['memory', 'database'] as const;
8
+
9
+ export type StoreMode = (typeof STORE_MODES)[number];
10
+
11
+ /**
12
+ * `memory` under `test`, `database` everywhere else. The environment and never `DATABASE_URL`:
13
+ * `x dev` installs the embedded PGlite as the process client and sets no URL, and a container
14
+ * gets its pool from the URL — both are `database`. `bun test` installs no client at all, so a
15
+ * statement there would have nothing to reach; that is the one carve-out. An unknown
16
+ * `ULTIMATE_ENV` throws `X_ENVIRONMENT_INVALID`, as `resolveEnvironment` does.
17
+ *
18
+ * `env` is required: the store a module picks at load must be testable without mutating the
19
+ * process environment, so the caller passes `Bun.env`.
20
+ */
21
+ export function storeMode(env: Readonly<Record<string, string | undefined>>): StoreMode {
22
+ return resolveEnvironment({ env }) === 'test' ? 'memory' : 'database';
23
+ }