New:Socket for Asana Is Now Available.Learn more
Get Started

@ultimat3/core

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ultimat3/core - npm Package Compare versions

Comparing version
17.0.0
to
18.0.0
+118
src/config-pwa.ts
// Single responsibility: the `pwa` block of `app.config.ts` — what an app must say before a
// browser will offer to install it, and the boot-time refusal when it has not.
//
// SPLIT OUT OF `config.ts` because that file reached its 500-line ceiling, and this is the seam
// that costs nothing to cross: `enabled` turns four other requirements on, so the shape, the screen
// and the remedy are one subject. `config.ts` keeps the one call.
//
// WHY THE SCREEN IS HERE AND NOT AT EMIT. `@ultimat3/pwa`'s `generateWebManifest` refuses a blank
// title too, and that refusal arrives at `x build` — which is the wrong moment: an agent that ran
// `x dev`, saw a served app and shipped it would meet the missing title in CI. Two checks, two
// subjects (a config file, and a function argument a library caller supplies directly).
import { describeValue } from './error-render';
// `app.config.ts` CONSUMES the route vocabulary; it does not own it — `config.ts`'s rule, and the
// reason `OfflineStrategy` is imported rather than restated.
import type { OfflineStrategy } from './route-vocabulary';
/**
* `installPrompt` was removed 2026-08, same rule: `@ultimat3/pwa`'s `createInstallController` is
* real and complete, nothing ever threaded the flag into it, and both tracked apps plus every
* scaffolded app set a switch with no wire. Call the controller from your own affordance instead.
*/
export interface PwaConfig {
readonly enabled: boolean;
readonly offline: OfflineStrategy;
readonly backgroundSync: boolean;
readonly push: boolean;
/**
* The install title, and the one manifest member nothing can derive. `AppConfig.name` is a slug
* (`^[a-z][a-z0-9-]{1,63}$`) and an install prompt shows a person a title, so `ledger-demo` is
* the wrong answer rather than a rough one. Required once `enabled` is true; `''` is what a
* disabled block resolves to.
*/
readonly name: string;
/**
* `theme_color` and `background_color`, per scheme. Required once `enabled` is true, and
* `undefined` otherwise — never a colour the framework picked. There is no defensible default:
* an install splash painted in a colour nobody chose is a wrong-looking app that boots, which is
* worse than a boot that names the four values it needs.
*/
readonly colors: PwaColors | undefined;
}
/**
* The install chrome's two colours for one scheme, as CSS colour strings.
*
* ONE OF THE TWO PLACES A RAW COLOUR IS LEGAL, alongside `ThemeConfig.tokens` one section up, and
* for a stronger reason than that one has: a browser paints the install splash and the address bar
* from these before a single stylesheet has loaded, so there is no token to resolve them against
* and no component anywhere in the loop.
*/
export interface PwaSchemeColors {
readonly themeColor: string;
readonly backgroundColor: string;
}
/**
* Both schemes, because a web manifest carries exactly one `theme_color` and `<head>` carries a
* media-scoped `<meta name="theme-color">` per scheme. One value would make the dark answer the
* light one's, on the surface a reader sees before the app has painted anything.
*/
export interface PwaColors {
readonly light: PwaSchemeColors;
readonly dark: PwaSchemeColors;
}
/**
* The two schemes and the two colours `validate` screens, for `INBOX_RETENTION_KEYS`' reason: a
* third member added to `PwaColors` or `PwaSchemeColors` without a row here is a value an app can
* leave blank, and `config.test.ts` asserts both lists against the types so the omission is a red
* test rather than a manifest with an empty `theme_color`.
*/
export const PWA_SCHEMES = ['light', 'dark'] as const;
export const PWA_COLOR_KEYS = ['themeColor', 'backgroundColor'] as const;
/**
* Appended only when the `pwa` block is what failed, and it carries the whole block rather than
* the missing key: `pwa.enabled` is what turns four other requirements on, so a reader who set one
* boolean needs to see the complete shape and the opt-out in the same line.
*/
export const PWA_FIX =
"in app.config.ts, complete the pwa block: pwa: { enabled: true, offline: 'runtime', name: 'My App', colors: { light: { themeColor: '#1b1f3b', backgroundColor: '#ffffff' }, dark: { themeColor: '#1b1f3b', backgroundColor: '#0b0d1a' } } } — a browser paints the install splash and the address bar from those four values before any stylesheet loads, so there is nothing for the framework to derive them from; or set pwa.enabled: false";
/**
* Every rule `validate` applies to the block, appended to the caller's own issue list. Answers
* whether IT found anything, so the pwa remedy rides only on a pwa finding: `issues` may already
* hold a bad locale, and a fix line naming the install block for that is axiom 4 broken.
*
* `describeValue` for the reason the retention windows use it — this is where an untyped config
* object crosses into the framework, so every value here is `unknown` however the interface types it.
*/
export function pwaIssues(pwa: PwaConfig, issues: string[]): boolean {
if (!pwa.enabled) return false;
const before = issues.length;
const { name, colors } = pwa;
if (typeof name !== 'string' || name.trim() === '') {
issues.push(`pwa.name is required when pwa.enabled is true, and is ${describeValue(name)}`);
}
// `typeof !== 'object' || null`, never `=== undefined`: an untyped `app.config.ts` writing
// `pwa.colors: null` reached `null[scheme]` one line down and took the boot out with a native
// `TypeError`, from the validator whose whole job is producing an instruction instead of one.
if (colors === null || typeof colors !== 'object') {
issues.push(
`pwa.colors is required when pwa.enabled is true, for both light and dark, and is ${describeValue(colors)}`,
);
} else {
for (const scheme of PWA_SCHEMES) {
for (const key of PWA_COLOR_KEYS) {
const value: unknown = colors[scheme]?.[key];
if (typeof value === 'string' && value.trim() !== '') continue;
issues.push(
`pwa.colors.${scheme}.${key} must be a CSS colour, not ${describeValue(value)}`,
);
}
}
}
return issues.length > before;
}
+30
-17

@@ -80,13 +80,24 @@ # @ultimat3/core — agent notes

escape, so a cause could repaint the screen or hide the line above it. `@ultimat3/schema` carries a
deliberate duplicate for the tier-0 reason below, pinned behaviourally by
`single-line-pin.test.ts` in `@ultimat3/cli`.
deliberate duplicate — `schema -> core` stays forbidden, so that direction cannot be collapsed —
pinned behaviourally by `single-line-pin.test.ts`, which lives HERE `As of 2026-08-27`, at the tier
the invariant belongs to, and pins `ERROR_DOCS_URL` and the brand key beside it.
`describeValue` in `error-render.ts` is a deliberate duplicate of `describeValue` in
`packages/schema/src/describe-value.ts`, for the same tier-0 reason `SCHEMA_ERROR_CODE_TITLES` is
one: schema and core are both tier 0 and `core → schema` is **not** a declared edge in
`scripts/lib/tiers.ts`, so neither may import the other. Keep the two ANSWERING identically — that
is the contract, and the source is no longer character-for-character: schema counts characters
through `char-count.ts`, which core copies privately. A pin test in
`@ultimat3/cli` (which may legally import both) is the mechanical half, the same shape as
`schema-error-codes-pin.test.ts`. **A string's length is CODE POINTS in both, `As of 2026-08-22`**
**`core -> schema` is a declared edge `As of 2026-08-27`, and FIVE copies went with it.**
`describeValue`, `charCount`, `CURRENCY_CODE_PATTERN`, `SCHEMA_ERROR_CODES` and `isIanaZoneName`
were all restated here because both packages are tier 0 and neither could import the other; they
were held equal by 394 lines of pin test in `@ultimat3/cli`, a TIER-5 package pinning a tier-0
invariant that no rule required to exist. `describeValue` is the one that made it urgent — it
prints INSTEAD of a rejected password, so the safety property of the framework's most
security-sensitive renderer rested on a 63-line behavioural pin at tier 5. `error-render.ts`
re-exports schema's now, and the four pin files are deleted.
The edge cost was MEASURED before it was declared, because axiom 6 makes it a measurement and not
an argument — `docs/architecture/01-package-map.md` carries the table. Short version: the edge
alone TRIPLED a core-only browser chunk (6,362 → 19,018 B), because importing schema's barrel with
no `sideEffects` field forces a bundler to keep every module it reaches; `@ultimat3/schema` now
declares `sideEffects: false`, which `bun run side-effects` had already measured as true of it, and
the cost drops to ~1 kB — while `moneyText` from `@ultimat3/ui`, which always carried schema, comes
out 7.8 kB SMALLER.
**A string's length is CODE POINTS, `As of 2026-08-22`**
— `validators.ts` rejects in that unit and `json-schema.ts` publishes `minLength` in it, so

@@ -129,3 +140,3 @@ `.length` made `t.string.min(3).safeParse('👍a')` say "at least 3 chars, received a string of 3

| a value that must not be printed | `secret.ts` | redacted by VALUE; `revealSecret()` is the one way out, on purpose greppable |
| an `Intl` formatter cache, and the screen in front of it | `intl-cache.ts` (`cachedFormatter`, `canonicalLocale`, `assertLocale`, `MAX_CACHED_FORMATTERS`) | a locale and a zone arrive from a request header, so the tag must be REFUSED when it is not a tag (`X_LOCALE_INVALID`), the key must be canonical AND the cache bounded — never a second copy of any of the three |
| 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 and a zone arrive from a request header, so the tag must be REFUSED when it is not a tag (`X_LOCALE_INVALID`), the key must be canonical AND the cache bounded — never a second copy of any of the three. The refusal is bounded too: the `cause` quotes back at most `MAX_LOCALE_EXCERPT` (35, RFC 5646 §4.4.1) code points and says so when it cut, and the whole tag rides in `meta.locale` — a `cause` reaches the 400 body and the log line, where a value with no key has nothing a redactor can address |
| 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()` |

@@ -167,8 +178,10 @@

`schema-error-codes.ts` is the same shape a second time, for codes this package does not even own.
`@ultimat3/schema` is tier 0 like `core` and so can neither call `registerErrorCodes()` itself nor
import core to reach it — the four codes' titles are a deliberate, tested duplicate of
`SCHEMA_ERROR_CODES` in `packages/schema/src/errors.ts`, registered unconditionally at import time
so any process that imports core (not just `@ultimat3/cli`, which used to be the only registrant)
renders schema's real titles. Neither tier-0 package can check the duplicate against its source, so
the pin (`schema-error-codes-pin.test.ts`) lives in `@ultimat3/cli`, which may legally import both.
`@ultimat3/schema` is tier 0 like `core` and cannot call `registerErrorCodes()` itself — that would
be `schema -> core`, which stays forbidden. So core READS `SCHEMA_ERROR_CODES` over the declared
edge and registers it unconditionally at import time, and any process that imports core (not just
`@ultimat3/cli`, which used to be the only registrant) renders schema's real titles. The retry
classification is derived from the same set rather than typed out beside it: a fifth code would
have been silently absent from a hand-written list, and an unregistered code reads as UNCLASSIFIED,
so a schema refusal inside a job body burns the whole retry policy re-proving an answer no attempt
can change.

@@ -175,0 +188,0 @@ `timing-safe-equal.ts` holds the one constant-time string comparison `@ultimat3/auth` and

{
"name": "@ultimat3/core",
"version": "17.0.0",
"version": "18.0.0",
"description": "Ultimate's foundation: errors, context, env, config, clock, ids, logging, telemetry, lifecycle",

@@ -33,3 +33,3 @@ "license": "MIT",

"engines": {
"bun": ">=1.3.0"
"bun": ">=1.4.0"
},

@@ -40,3 +40,5 @@ "scripts": {

},
"dependencies": {}
"dependencies": {
"@ultimat3/schema": "18.0.0"
}
}

@@ -33,2 +33,3 @@ # 🧱 @ultimat3/core

| `defineConfig()` for `app.config.ts` | `config.ts` |
| the `pwa` block — what an install needs, and the boot refusal when it is not there | `config-pwa.ts` |
| the closed route vocabulary every renderer names | `route-vocabulary.ts` |

@@ -491,2 +492,13 @@ | runtime roles + `ROLE` resolution | `roles.ts` |

The **refusal** is bounded for the same reason the cache is. `X_LOCALE_INVALID` answers 400 and
`@ultimat3/http`'s `toProblem` copies a `cause` into the response `detail` **and** into the log
line, so a tag echoed verbatim is a stranger-chosen string of unbounded length in a shared log
index. `localeInvalid` quotes back the first `MAX_LOCALE_EXCERPT` (35) code points — RFC 5646
§4.4.1's own figure for the longest tag the registry can form — and appends
`(truncated at 35 characters)` when there were more, so a cut value is never read as a whole one.
`describeValue` is deliberately **not** used here: "a 5-character string" deletes the only
actionable content in a sentence whose job is to name the tag. The whole tag rides in
`meta.locale`, which is the half that gives a redactor a key to address; a value spliced into
prose has none.
## One flight layer — wait, classify, share, bound, fence

@@ -493,0 +505,0 @@

@@ -5,2 +5,3 @@ // Single responsibility: `app.config.ts` — the one config file. Deeply optional with real

import { CURRENCY_CODE_PATTERN } from '@ultimat3/schema';
// Same rule for the same reason: `app.config.ts` CONSUMES the cache tier names, it does not own

@@ -10,8 +11,7 @@ // them. Declaring them here is what let `cache.tiers` and the ladder `@ultimat3/cache` orders by

import { CACHE_TIERS, type CacheTierName } from './cache-vocabulary';
import type { PwaConfig } from './config-pwa';
import { PWA_FIX, pwaIssues } from './config-pwa';
import { describeValue } from './error-render';
import { ConfigInvalidError } from './errors';
import { ROLES, type Role } from './roles';
// `app.config.ts` CONSUMES the route vocabulary; it does not own it. Declaring `OfflineStrategy`
// here is what made it copyable — `render`, `manifest` and `pwa` each wrote their own rather than
// import a name that reads like a config key.
import type { OfflineStrategy } from './route-vocabulary';
import { isIanaZoneName } from './time-zone-name';

@@ -46,14 +46,34 @@

/**
* `installPrompt` was removed 2026-08, same rule: `@ultimat3/pwa`'s `createInstallController` is
* real and complete, nothing ever threaded the flag into it, and both tracked apps plus every
* scaffolded app set a switch with no wire. Call the controller from your own affordance instead.
* Retention for `x_notify_inbox`, and the one framework table whose window the framework may not
* pick. Every other one holds bookkeeping whose job ENDS — an idempotency key, a rate-limit
* bucket, an auth challenge, a delivery claim — and a sweep is unambiguously right. An inbox row
* is a message a person has not read yet, so when it disappears is a product decision, which is
* axiom 8: Ultimate ships mechanism, your app ships convention.
*
* BOTH DEFAULT TO `undefined`, meaning never swept, and that is the only safe default here: the
* failure mode of keeping rows is a table that grows, and the failure mode of guessing a number is
* a notification the recipient never got to read.
*
* TWO WINDOWS RATHER THAN ONE, because the axiom-8 objection is only about UNREAD messages. An app
* that wants read notices gone in a month and unread ones kept forever must be able to say exactly
* that, and `SQL_NOTIFY_INBOX_MARK_READ` already stamps the time that makes it expressible.
*
* Milliseconds and not a `DurationInput`: that type lives in `@ultimat3/jobs` (tier 3) and this
* package is tier 0, which is the same reason `cache.defaultTtlMs` and `jobs.visibilityTimeoutMs`
* are spelled this way.
*/
export interface PwaConfig {
readonly enabled: boolean;
readonly offline: OfflineStrategy;
readonly backgroundSync: boolean;
readonly push: boolean;
export interface NotifyConfig {
/** Age of a row's `read_at`, not its `created_at` — a row ages from when it was READ. */
readonly inboxReadRetentionMs: number | undefined;
/** Age of an unread row's `created_at`. Setting this deletes messages nobody has read. */
readonly inboxUnreadRetentionMs: number | undefined;
}
/**
* Every window `validate` screens, derived from nothing — a third one added to `NotifyConfig`
* without a row here is a window an app can set to `-1`, and `config.test.ts` asserts the two
* lists agree so the omission is a red test rather than a silent hole.
*/
export const INBOX_RETENTION_KEYS = ['inboxReadRetentionMs', 'inboxUnreadRetentionMs'] as const;
/**
* Deliberately thin. `urlEnv`, `poolSize` and `schema` were removed 2026-08 because **nothing

@@ -172,2 +192,3 @@ * read them** — the only reader of any `config.database.*` field in the repo was this file's own

readonly realtime: RealtimeConfig;
readonly notify: NotifyConfig;
readonly ai: AiConfig;

@@ -197,2 +218,3 @@ }

readonly realtime?: Input<RealtimeConfig> | undefined;
readonly notify?: Input<NotifyConfig> | undefined;
readonly ai?: AiConfigInput | undefined;

@@ -220,14 +242,9 @@ }

/**
* A deliberate duplicate of `CURRENCY_CODE_PATTERN` in `packages/schema/src/money-value.ts`, which
* is the framework's ONE declaration of what an ISO 4217 code looks like and the source
* `isCurrencyCode`, the published OpenAPI `pattern` and `@ultimat3/entity`'s Postgres CHECK all
* derive from. This file cannot import it: `core` and `schema` are both tier 0 and `core → schema`
* is not in `SIDEWAYS_ALLOW` (`scripts/lib/tiers.ts`), the same wall that makes `describeValue` a
* character-for-character copy in `error-render.ts`.
*
* So keep the two identical, and keep the pattern inside the syntax ECMAScript, JSON Schema and
* POSIX ERE spell identically — a `defaultCurrency` this accepts and `t.money` refuses is an app
* whose configured currency cannot be written to a row.
* Built from `@ultimat3/schema`'s `CURRENCY_CODE_PATTERN`, the framework's ONE declaration of what
* an ISO 4217 code looks like — the same source `isCurrencyCode`, the published OpenAPI `pattern`
* and `@ultimat3/entity`'s Postgres CHECK all derive from. It was a character-for-character copy
* here until the `core -> schema` edge was declared (`scripts/lib/tiers.ts`), held equal only by a
* pin test in `@ultimat3/cli`.
*/
const CURRENCY_RE = /^[A-Z]{3}$/;
const CURRENCY_RE = new RegExp(CURRENCY_CODE_PATTERN);

@@ -250,3 +267,10 @@ function isLocale(value: string): boolean {

auth: { signInPath: null },
pwa: { enabled: false, offline: 'network-only', backgroundSync: false, push: false },
pwa: {
enabled: false,
offline: 'network-only',
backgroundSync: false,
push: false,
name: '',
colors: undefined,
},
roles: [...ROLES],

@@ -263,2 +287,3 @@ database: { driver: 'postgres', ssl: false },

realtime: { enabled: false, transport: 'memory', urlEnv: undefined },
notify: { inboxReadRetentionMs: undefined, inboxUnreadRetentionMs: undefined },
ai: { mcp: { expose: true, path: '/mcp' } },

@@ -295,2 +320,4 @@ };

const tierFix: string[] = [];
// Same shape again: carried only when an installable app is missing what an install needs.
const pwaFix: string[] = [];

@@ -325,2 +352,23 @@ if (!NAME_RE.test(config.name)) {

}
// BOTH RETENTION WINDOWS OR NEITHER — `undefined` is a real value here (never swept) and the
// only other legal one is a positive, finite count of milliseconds. Zero is refused rather than
// read as "immediately": a sweep at age 0 deletes every row the instant it is written, which is
// an inbox that silently receives nothing, and nobody types it on purpose. `describeValue`
// rather than `${…}`: this validator is the boundary a JS app's untyped config crosses, so the
// value here is `unknown` in practice however the interface types it.
for (const key of INBOX_RETENTION_KEYS) {
const ms: unknown = config.notify[key];
if (ms === undefined) continue;
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) {
issues.push(
`notify.${key} must be a positive number of milliseconds, not ${describeValue(ms)}`,
);
}
}
// What an install needs, asked at BOOT and not at emit — `config-pwa.ts` owns the rules and the
// remedy, because `pwa.enabled` turning four other requirements on is a question about that block
// and nothing else here.
if (pwaIssues(config.pwa, issues)) pwaFix.push(PWA_FIX);
// A rung the ladder cannot build is the defect this key had: `sortTiers` places a name by its

@@ -340,3 +388,3 @@ // index in `CACHE_TIERS`, and a name missing from it sorts to `-1` — AHEAD of the request memo.

// pasted — a trailing `.` after `x verify` is a command nobody can run.
fix: [...zoneFix, ...tierFix, BASE_FIX].join('. '),
fix: [...zoneFix, ...tierFix, ...pwaFix, BASE_FIX].join('. '),
meta: { issues },

@@ -377,2 +425,3 @@ });

realtime: section(base.realtime, merged.realtime),
notify: section(base.notify, merged.notify),
ai: { mcp: section(base.ai.mcp, merged.ai?.mcp) },

@@ -379,0 +428,0 @@ };

@@ -255,55 +255,12 @@ // Single responsibility: turn a value the framework does not control into text for an error's

*
* A deliberate duplicate of `describeValue` in `packages/schema/src/describe-value.ts`, for the
* reason `SCHEMA_ERROR_CODE_TITLES` is one: `@ultimat3/schema` is tier 0 alongside this package,
* so neither may import the other. Keep the two answering IDENTICALLY — that is what
* `packages/cli/src/describe-value-pin.test.ts` holds — and changing one alone is the bug.
* DECLARED IN `@ultimat3/schema` and re-exported here, `As of 2026-08-27`. It was a
* character-for-character copy of `packages/schema/src/describe-value.ts` — the two tier-0
* packages could not import each other — held equal by a 63-line behavioural pin in
* `@ultimat3/cli`, a TIER-5 package, that no rule required to exist. Whose function it is has not
* changed: schema's `expected()` is the other reader, and a widening that leaks a value is now one
* edit in one file rather than two files that can drift apart in silence.
*
* `charCount` went with it — the same duplication, one call deep — so the unit a message quotes
* and the unit a length rule counts in are the same code, not two files that agree today.
*/
export function describeValue(value: unknown): string {
if (value === undefined) return 'undefined';
if (value === null) return 'null';
switch (typeof value) {
case 'string':
return countOf(charCount(value), 'string', 'character');
case 'number':
return describeNumber(value);
case 'boolean':
return 'a boolean';
case 'bigint':
return 'a bigint';
case 'symbol':
return 'a symbol';
case 'function':
return 'a function';
default:
break;
}
if (Array.isArray(value)) return countOf(value.length, 'array', 'item');
// `getTime()` rather than a value: an invalid Date is the one Date fact a caller can act on.
if (value instanceof Date) return Number.isNaN(value.getTime()) ? 'an invalid Date' : 'a Date';
return 'an object';
}
function describeNumber(value: number): string {
if (Number.isNaN(value)) return 'NaN';
if (value === Number.POSITIVE_INFINITY) return 'Infinity';
if (value === Number.NEGATIVE_INFINITY) return '-Infinity';
return 'a number';
}
function countOf(size: number, noun: string, unit: string): string {
if (size === 0) return `an empty ${noun}`;
const article = noun === 'array' ? 'an' : 'a';
return `${article} ${noun} of ${size} ${unit}${size === 1 ? '' : 's'}`;
}
/**
* The twin of `@ultimat3/schema`'s `char-count.ts`, duplicated for the same reason `describeValue`
* is: both packages are tier 0 and neither may import the other. Code points, because the rules
* that reject a string count in them and the message must quote the same unit — `'👍'.length` is 2.
* Only a surrogate makes the two counts differ, so every ASCII value keeps the O(1) read.
*/
const HAS_SURROGATE = /[\uD800-\uDBFF]/;
function charCount(value: string): number {
return HAS_SURROGATE.test(value) ? [...value].length : value.length;
}
export { describeValue } from '@ultimat3/schema';

@@ -68,3 +68,3 @@ // Single responsibility: the public API of @ultimat3/core. Explicit named exports only —

McpConfig,
PwaConfig,
NotifyConfig,
RealtimeConfig,

@@ -75,3 +75,5 @@ RealtimeTransport,

} from './config';
export { defineConfig } from './config';
export { defineConfig, INBOX_RETENTION_KEYS } from './config';
export type { PwaColors, PwaConfig, PwaSchemeColors } from './config-pwa';
export { PWA_COLOR_KEYS, PWA_SCHEMES } from './config-pwa';
export type { Ctx, CtxFacts, CtxInit, CtxPatch, CtxServices, ServiceBag } from './context';

@@ -466,2 +468,3 @@ export {

MAX_CACHED_FORMATTERS,
MAX_LOCALE_EXCERPT,
} from './intl-cache';

@@ -468,0 +471,0 @@ export { isJsonObject } from './json-object';

@@ -31,2 +31,12 @@ // One bounded cache, on one canonical key, and one screen, for every `Intl` formatter the

/**
* The cap on the tag an `X_LOCALE_INVALID` cause quotes back, in code points.
*
* 35 is RFC 5646 §4.4.1's own number — Figure 7 derives it as the longest tag the registry can
* form (language 8 + script 5 + region 4 + two variants 9+9), and the same section says a protocol
* with a fixed buffer "MUST allow for language tags of at least 35 characters". So every tag a
* caller could legitimately have meant fits, and nothing longer is a tag being debugged.
*/
export const MAX_LOCALE_EXCERPT = 35;
/**
* The canonical BCP 47 spelling, or `undefined` when the tag is not structurally valid at all

@@ -57,4 +67,10 @@ * (`en_US`, `''`, `not a locale`). Well-formed but unknown to ICU (`zz`) is a locale — `Intl`

code: 'X_LOCALE_INVALID',
cause: `"${locale}" is not a well-formed BCP 47 language tag`,
cause: `${localeExcerpt(locale)} is not a well-formed BCP 47 language tag`,
fix: "pass a tag like 'en', 'en-GB' or 'de-DE' — screen a header-supplied value with Intl.DateTimeFormat.supportedLocalesOf([tag]) before it reaches a formatter",
// The raw tag, under a NAME. A `cause` is copied into the 400 body and into the log line by
// `@ultimat3/http`'s `toProblem`, and a logger redacts by key — so a caller's value spliced
// into prose has no key left to redact, which is the whole argument `describeValue` rests on.
// `meta` is the key, and it is machine-read, so it carries the value WHOLE: an excerpt here
// would be a value a redactor or a bug report reads as complete when it is not.
meta: { locale },
});

@@ -64,2 +80,32 @@ }

/**
* The tag as a reader can act on it: the first `MAX_LOCALE_EXCERPT` code points, quoted, and SAID
* to be cut when there were more.
*
* `describeValue` is the usual answer for a caller-supplied value in a `cause` and it is the wrong
* one here — it renders `en_US` as "a 5-character string", deleting the only actionable content in
* a sentence whose entire job is to say WHICH tag was refused. Bounding it keeps the diagnostic and
* removes the part a stranger chooses: without a cap the whole of an `Accept-Language` value —
* megabytes, at the caller's option — became the error's `message`, its 400 body and its log line.
*
* Code points, never `slice`: cutting between a surrogate pair leaves a lone surrogate, which is
* not text and survives no encoder between here and the log index. The string iterator is lazy, so
* a megabyte tag costs 35 steps rather than a megabyte-long array.
*
* No escaping here: `UltimateError`'s constructor runs `singleLine` over every line-bearing field
* exactly once, which is what keeps a newline in a tag from writing a second line an operator reads
* as a genuine framework message. A second pass at this call site would be a second place that has
* to be right.
*/
function localeExcerpt(locale: string): string {
let head = '';
let taken = 0;
for (const char of locale) {
if (taken === MAX_LOCALE_EXCERPT) return `"${head}" (truncated at ${taken} characters)`;
head += char;
taken += 1;
}
return `"${head}"`;
}
/**
* The canonical spelling of a well-formed tag, or `X_LOCALE_INVALID`. The ONE screen a

@@ -66,0 +112,0 @@ * caller-supplied BCP 47 tag passes before it reaches an `Intl` constructor.

// Single responsibility: register `@ultimat3/schema`'s error codes so their titles render for any
// process that imports `@ultimat3/core` — not just the CLI. `@ultimat3/schema` is tier 0 alongside
// this package, so it cannot call `registerErrorCodes()` itself (that would mean importing core,
// a same-tier import) and this package cannot import schema to read its declarations back (same
// reason, the other direction). The codes below are a deliberate, tested duplicate of
// `SCHEMA_ERROR_CODES` in `packages/schema/src/errors.ts` — `schema-error-codes-pin.test.ts`, in a
// package that may legally import both (`@ultimat3/cli`), asserts them equal so a title edited in
// one place and not the other fails the build instead of quietly disagreeing at runtime.
// this package and cannot call `registerErrorCodes()` itself: that would be `schema -> core`, an
// import this package's own dependency-free promise forbids in that direction and always will.
// The other direction is DECLARED (`core -> schema`, `scripts/lib/tiers.ts`), so the titles are
// READ from schema rather than restated here.
//
// They were a hand-kept duplicate until 2026-08-27, held equal by `schema-error-codes-pin.test.ts`
// in `@ultimat3/cli` — a tier-5 package pinning a tier-0 invariant, which nothing required to
// exist. It is deleted with this change.
import { SCHEMA_ERROR_CODES } from '@ultimat3/schema';
import { registerErrorCodes } from './error-codes';
import { registerErrorRetry } from './error-retry';
/** Mirrors `SCHEMA_ERROR_CODES` in `packages/schema/src/errors.ts`. Keep the titles identical. */
export const SCHEMA_ERROR_CODE_TITLES: Readonly<Record<string, string>> = Object.freeze({
X_VALIDATION_FAILED: 'value did not match its schema',
X_SCHEMA_UNSUPPORTED: 'the active schema provider cannot do this',
X_SCHEMA_DISCRIMINANT_INVALID: 'a discriminated union member can never be dispatched to',
X_SCHEMA_DEFAULT_UNSHAREABLE: 'a schema default cannot be copied per parse',
});
/**
* Schema's own declarations, projected to titles. Kept as an export because it is public API
* shipped since 1.0; it is now DERIVED and can no longer disagree with its source.
*/
export const SCHEMA_ERROR_CODE_TITLES: Readonly<Record<string, string>> = Object.freeze(
Object.fromEntries(
Object.entries(SCHEMA_ERROR_CODES).map(([code, declared]) => [code, declared.title]),
),
);

@@ -24,8 +29,4 @@ // Registered here rather than in `error-codes.ts`'s `CORE_CODE_TITLES` because core does not own

// raises `X_ERROR_CODE_DUPLICATE` if a package that DOES own one of them ever tries to register it
// too, which pins ownership even though the titles live in two files.
registerErrorCodes(
Object.fromEntries(
Object.entries(SCHEMA_ERROR_CODE_TITLES).map(([code, title]) => [code, { title }]),
),
);
// too, which pins ownership even though the registration happens here.
registerErrorCodes(SCHEMA_ERROR_CODES);

@@ -45,7 +46,8 @@ // And how each is RETRIED, here for the same tier reason the titles are here.

// A value that does not match its schema does not match it on attempt five either.
registerErrorRetry({
X_VALIDATION_FAILED: 'terminal',
X_SCHEMA_UNSUPPORTED: 'terminal',
X_SCHEMA_DISCRIMINANT_INVALID: 'terminal',
X_SCHEMA_DEFAULT_UNSHAREABLE: 'terminal',
});
//
// DERIVED from the same set, so a code schema adds cannot arrive unclassified: the list was typed
// out here and a fifth code would have been silently missing from it, which is precisely the
// "unregistered reads as unclassified" failure the paragraph above describes.
registerErrorRetry(
Object.fromEntries(Object.keys(SCHEMA_ERROR_CODES).map((code) => [code, 'terminal' as const])),
);

@@ -1,43 +0,14 @@

// Single responsibility: is a string an IANA zone NAME? Tier 0's statement of the one rule
// `@ultimat3/time` enforces everywhere above it — a zone is `Area/Location`, and `UTC` is the one
// exception. It is stated twice because `core` is tier 0 and may not import `@ultimat3/time`;
// `packages/time/src/zone-canonical.ts` is where the rule and its reasoning are written down.
// Single responsibility: re-export `@ultimat3/schema`'s IANA zone-name predicate, so `app.config.ts`
// and `t.timezone` judge a zone with ONE function.
//
// This file held a character-for-character copy of it until 2026-08-27 — `core` and `schema` are
// both tier 0 and neither could import the other — kept equal by a 123-line pin test in
// `@ultimat3/cli`, a tier-5 package pinning a tier-0 invariant that no rule required to exist.
// `core -> schema` is now a declared edge (`scripts/lib/tiers.ts`), so the copy is gone.
//
// The rule itself, and why a bare `new Intl.DateTimeFormat(…)` probe is not it — ICU 78 resolves
// `CET`, `EST`, `Japan`, `GMT` and `Zulu`, so `defaultTimeZone: 'CET'` passed validation at boot
// and threw `X_TIMEZONE_INVALID` on the first `format` call (issue #257) — is written down in
// `packages/schema/src/time-zone-name.ts` and in `packages/time/src/zone-canonical.ts`.
/**
* A LEADING sign is a fixed offset, which carries no DST rules. `Etc/GMT+2` keeps its `+`.
*
* Unobservable on ICU 78 — `+01:00` resolves to itself, so the slash rule below already refuses it,
* and deleting this line changes no answer this package can currently produce. It stays because it
* guards the runtime that folds an offset into `Etc/GMT-1`, which WOULD carry a slash, and because
* `packages/time/src/zone-canonical.ts` carries the same line: two statements of one rule may not
* differ, least of all in the half that is hard to test.
*/
const NUMERIC_OFFSET = /^[+-]/;
/**
* Structural, and never delegated to `Intl` — the reasoning, and why a denylist is not the
* alternative, is `packages/time/src/zone-canonical.ts`'s and is not re-derived here. What this
* file enforces is that same rule for `app.config.ts`: an identifier is `Area/Location`, `UTC` is
* the one legal exception, and a leading sign is an offset rather than a name.
*
* It exists because a bare `new Intl.DateTimeFormat(…)` probe was the second answer to that
* question, and the two stopped agreeing: ICU 78 (Bun 1.4) resolves `CET`, `EST`, `Japan`, `GMT`,
* `Zulu` and the whole `backward`/abbreviation family, so `defaultTimeZone: 'CET'` passed validation
* at boot and threw `X_TIMEZONE_INVALID` on the first `format` call — a config file accepting a
* value nothing downstream can use (issue #257).
*
* The judgement is made on the RESOLVED name, not the input, so a casing (`utc`) and a runtime that
* folds an alias into its target (`Etc/UTC` → `UTC`) both answer correctly. No cache: this is read
* once per process at config validation, never off a request header — a zone that arrives from a
* caller goes through `@ultimat3/time`'s `canonicalTimeZone`, which is bounded and canonicalizing.
*/
export function isIanaZoneName(value: string): boolean {
if (value === '' || NUMERIC_OFFSET.test(value)) return false;
try {
const resolved = new Intl.DateTimeFormat('en-US', { timeZone: value }).resolvedOptions()
.timeZone;
return resolved === 'UTC' || resolved.includes('/');
} catch {
return false;
}
}
export { isIanaZoneName } from '@ultimat3/schema';