@voltro/i18n 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -39,6 +39,56 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.11.1] — 2026-07-23
43
+
44
+ ### Added
45
+
46
+ - **@voltro/runtime** — `defineExecutor(descriptor, fn)` type-checks a query/mutation/action handler's return against the descriptor's `output` schema Type — so returning a `number` where `output` is `timestampMs` (Type = `Date`) is a COMPILE error at the handler, not a runtime encode failure that Dies the subscription. The gap it closes: the executor is a separate default export whose return was never tied to `output`, so a handler that builds a plain object with a leftover `.getTime()` compiled green and only threw `Expected DateFromSelf, actual 1784…` at encode time — invisible while a nullable date was null, exploding the instant it became non-null.
47
+
48
+ Opt-in and zero-cost: it's a runtime identity (returns `fn` unchanged, so the codegen wires it exactly as the bare default export), and the compile check is the whole value. Wrap the handler and import the descriptor into its `*.server.ts`:
49
+
50
+ ```ts
51
+ export default defineExecutor(getRoadmapsByYear, (input, ctx) => …)
52
+ ```
53
+
54
+ The Effect error and requirement channels stay inferred from the handler; only the success value is constrained. A reactive query that returns a `{ descriptor }` builder is allowed through unchecked — the store produces its rows, so a value-level return type can't express that row-vs-output check.
55
+ - **@voltro/database, @voltro/runtime** — An `.encrypted()` column that can't be decrypted with the active key now fails as a typed, readable `FieldDecryptionError` naming the `table.column`, instead of a raw `Error: field cipher: malformed ciphertext` with no context. It carries a `_tag` (the same tagged shape `storeErrors` uses, so `Effect.catchTag` matches), and never includes the ciphertext. The common trigger is restoring a prod/staging snapshot into a dev DB whose `VOLTRO_FIELD_ENCRYPTION_KEY` differs.
56
+
57
+ New dev/migration escape hatch: `VOLTRO_FIELD_DECRYPT_ON_ERROR=null` degrades an undecryptable column to `null` (with one deduped warning per `table.column`, scope `store.fieldEncryption`) instead of letting one bad row nuke the whole read — its readable siblings still decrypt. Default stays `'throw'`; never set `null` in production, where a key mismatch must fail loud. `decryptFieldsOnRead` gains an optional `{ onError, warn }` argument (additive); the raw throw is replaced by the typed one, which existing `catch (e: Error)` handlers still catch.
58
+ - **@voltro/i18n** — `assertCatalogParity({ en, de })` checks every locale uses the SAME ICU `{var}` set per key. `defineLocale` enforces KEY parity but not PLACEHOLDER parity — a translation that drops or renames a `{var}` (`'Published on {date}'` → `'Veröffentlicht'`) compiles and boots, then throws `The intl string context variable "date" was not provided` only in that locale, only when the message renders. Call it in a test or at boot; it throws listing every drift (or warns with `onMismatch: 'warn'`). Plural argument names are extracted; a plural's inner `{# item}` branches are not mistaken for placeholders.
59
+ - **@voltro/i18n** — `createTypedMessages<typeof en>()` binds a catalog's LITERAL message types to `useT` / `useTFn` / `<T>`, so a missing ICU placeholder is a COMPILE error instead of a runtime throw at format time. Until now `defineCatalog` / `defineLocale` enforced key PARITY across locales, but the call site `t('key', values)` was untyped — a message like `'Published on {date}'` called as `t('roadmap.publishedAt')` (or via the `t('key').replace('{{date}}', …)` idiom from other i18n systems) threw `The intl string context variable "date" was not provided` only when it rendered. Now `useT('roadmap.publishedAt')` demands `{ date }` at compile time, and a wrong/missing key is caught too.
60
+
61
+ Opt-in and purely additive: call `createTypedMessages` once with your base catalog (`as const`) and re-export the returned `useT`/`useTFn`/`T`; the bare hooks keep their existing loose signatures. Scope: simple `{name}` and single-argument `{count, number}` forms are extracted; messages with nested inline ICU (`{n, plural, one {…} other {…}}` / `select`) accept a loose values bag rather than a wrong strict one — apps that pluralise in JS over simple `{count}` messages stay fully typed. `<T>` gets a typed key with loose values, because its rich-text `<tag>` renderers can't be modelled by `{var}` extraction.
62
+ - **@voltro/plugin-ai-flows** — An AI-flow `MediaGenerator` (and the `makeMediaGenerator` persistence seam) now receives the run it executes within — `run: { runId, tenantId }` — resolved from the durable run row rather than the caller subject. This is what a resume needs: a BOOTSTRAP/crash resume runs under a tenant-less `SYSTEM_SUBJECT`, so a host that persists artifacts per tenant could not read the tenant from `ctx.request.subject` (it isn't there) and would either fail closed or, under an old anonymous fallback, write into the wrong tenant. The engine already loads the run row (for `ownerId`); it now reads `tenantId` from the same row and hands it down to both the deterministic and agentic media steps, and `MediaPersist.put` / `ingestUrl` forward it to `storage.put` / `ingestUrl` so persistence continues in the run's own tenant on replay.
63
+
64
+ Additive: `run` is appended to the generator/persist arguments, so a host that ignores it keeps compiling; the tenant is simply available when it doesn't. No `@voltro/web` change — the engine is server-only and not re-exported to the browser surface.
65
+ - **@voltro/client, @voltro/web** — A sequence step's `undo` now receives the accumulated context as a second argument — `undo: (result, ctx) => …` — alongside the step's own result. An inverse usually needs an id from an EARLIER step as well as this one's (`deleteJiraDraftTicket({ jiraKey: created.key, draftId: ctx.draft.id })`), and until now the only way to reach it was to re-return that id from the step purely so the undo could read it back. `ctx` is typed as of the step's definition — the steps before it, the same context `covers` and `when` already see — so a later step's result is deliberately not visible (it is rolled back before this one).
66
+
67
+ Additive, not breaking: `StepUndo<Result>` became `StepUndo<Result, Ctx = Record<string, unknown>>` with the context parameter defaulted and appended, so a named `StepUndo<T>` still resolves and an existing single-argument `undo: (r) => …` stays assignable. `@voltro/web` re-exports the client surface, which is why it moves too.
68
+ - **@voltro/cli** — SSR `ctx.query` now has a first-class, server-only api origin for split web/api deployments (#18). Previously, a `renderMode:'ssr'` page reloaded in a split deployment 500'd: the web pod's `POST <origin>/rpc` fell back to the DEV proxy target (`http://localhost:4000`), which nothing serves in production → `ECONNREFUSED` buried in a render error. Browser-reachability and SSR-reachability were conflated into the one `url` field.
69
+
70
+ New `apis.<name>.serverUrl` (and env overrides `VOLTRO_API_ORIGIN_<NAME>` / `VOLTRO_API_ORIGIN`) set the origin the WEB POD uses for SSR — the api's internal cluster DNS (`http://api.<ns>.svc.cluster.local`) — distinct from the browser's relative wsPath, and NEVER emitted into the browser bundle. Resolution: env > `serverUrl` > (dev only) the vite proxy target > an external api's absolute url. Under `voltro start` a package api with none resolves to `undefined` and the loader query FAILS LOUD naming the api and the config to set — it never dials the dev localhost port. A transport failure is wrapped naming the api and the origin attempted, instead of a bare `fetch failed`.
71
+ - **@voltro/database, @voltro/runtime** — `store.insert` / `upsert` / `insertIgnore` now raise a clear, typed `TableValidationFailed` naming the column when the payload omits one that is NOT NULL, has no default, and isn't auto-stamped — instead of a raw dialect `SqlError: Failed to execute statement` (`Field '…' doesn't have a default value`) surfaced only on the INSERT path (so it lay dormant until the first row with no existing cache entry). An upsert / insertIgnore whose payload is missing one of its own `conflictColumns` is likewise named at the call (an absent conflict key can't match its target). The check runs AFTER stamping, so auto-id / tenant / audit columns never trip it, and skips nullable, defaulted, and id (`idScheme`) columns — exactly the ones a caller may legitimately omit.
72
+
73
+ Two pure helpers back it — `missingRequiredColumns(table, row)` and `missingConflictColumns(conflictColumns, row)` (exported from `@voltro/database`). This is the runtime half of the "handler data silently disagrees with the schema" class; a compile-time payload type needs the column DSL to track `hasDefault` at the type level, which is a separate change.
74
+ - **@voltro/database, @voltro/plugin-ai-flows, @voltro/plugin-audit, @voltro/plugin-deactivation, @voltro/plugin-soft-delete** — Compile-time payload typing for writes (#15) — the type-level half that the runtime `TableValidationFailed` guard flagged as a separate change. `insertRow` / `upsertRow` take the TABLE OBJECT (not a string name), so the payload is checked against `InferInsertRow<T>`: every column is required EXCEPT nullable ones, columns with a default, and the framework-filled id/tenant/audit columns. A missing NOT-NULL-no-default column — the exact `lastRefreshedAt` / `teamId` omission from the report — is now a COMPILE error at the call, not a runtime SqlError only on the INSERT path; `upsertRow`'s `conflictColumns` are constrained to the table's own columns too.
75
+
76
+ import { insertRow } from '@voltro/database' await insertRow(ctx.store, roadmapEpicStats, { teamId, lastRefreshedAt: new Date() })
77
+
78
+ Enabled by a type-level default flag: `ColumnDefinition` / `ColumnBuilder` gained a third `HasDefault` parameter that `.default()` narrows to `true`. It defaults to `boolean`, so every existing `ColumnDefinition<unknown>` (mixins, query builder, migrate, plugins) is unaffected — the only golden churn is the additive third parameter rendering (e.g. an audit mixin's defaulted `createdAt` now shows `ColumnDefinition<Date, "timestamp", true>`). The string-keyed `store.insert` / `upsert` are unchanged; the typed seam is opt-in.
79
+ - **@voltro/runtime, @voltro/cli** — `voltro dev` now warns, once per tenant-scoped table, when a request reads it with the empty-string tenant sentinel — an authenticated subject that has no resolved org (`tenantId === ''`). The auto-merged tenant filter becomes `eq('tenantId', '')`, which matches no real row, so every such read returns empty WITH NO ERROR — indistinguishable, from the response alone, between "no such row", "filtered by an empty tenant", and "auth half-resolved". The warning names the cause and the fix. `voltro serve` deliberately stays silent (a prod diagnostic on every scoped read is noise). The decision is a pure predicate, `isEmptyTenantScopedRead`, exported from `@voltro/runtime` so it is unit-tested apart from the 6k-line dev boot; `applyTenantScope` itself stays pure.
80
+ - **@voltro/runtime, @voltro/cli** — `voltro dev` now warns once, the first time an authenticated subject resolves with NO active org (a `user` carrying the empty-string tenant sentinel) — "authenticated, but no active org → all tenant-scoped reads will be empty". Broader and earlier than the per-table empty-tenant read warning: it catches the whole class at the door instead of on a specific read. Backed by the pure `isOrglessUserSubject` predicate in `@voltro/runtime`; dev owns the one-time log.
81
+ - **@voltro/cli** — `voltro start` (web) now reports boot timing, matching `voltro serve`. It always logs a structured `start: ready in <n>ms` line (with `bootMs`), counted from PROCESS start so the module-graph load — the phase that dominates a scale-to-zero cold start — is included instead of being missed by a mid-boot baseline; the banner's `bootMs` uses the same total. Under `VOLTRO_BOOT_TIMING=1` the line also carries a per-phase `phases` breakdown (`modules`, `config`, `scan`, `provider`, `routes`, `cdc`, `ready`). Because the total is a structured log record, it is retrievable from the container's `/_voltro/inspect/logs` endpoint, not only from stdout.
82
+ - **@voltro/cli** — Production web images are now self-contained and dramatically smaller. `voltro build` makes every runtime artefact framework-inlined + tree-shaken — the SSR bundle (vite `noExternal: true`), the start bundle, and the precompiled `appConfig` — so a booted `voltro start` needs from `node_modules` only the runtime-external NATIVE leaves it actually reaches (a SQL driver an ISR/config path touches). A new hidden `voltro prune-runtime <deploy-dir>` command (`@vercel/nft`, a new build-time optionalDependency) traces the real reachable set from those bundles + the app's installed native leaves and drops the rest — the whole `@voltro`/effect/react tree that's now dead weight. The standalone web Dockerfiles run it after `pnpm --prod deploy`; for a static/SSR site with no native runtime dep, `node_modules` collapses to nothing. Measured on a real marketing app: the app tree drops from ~210 MB to ~65 MB (node_modules ~145 MB → 0) and still boots + renders. Fully automatic — a used native driver is traced + kept, an unused one dropped, no per-app allow-list — and non-fatal: any trace failure keeps the fuller tree (a bigger image, never a broken one). The appConfig `@voltro/*` imports are now inlined (were external); this is safe because `runEnvGate` consumes the env schema structurally (`isEnvContract` duck-types `{ vars }`, no `instanceof`/Symbol), so a schema built by the config's inlined `@voltro/env` still validates.
83
+ - **@voltro/cli** — `voltro build` now precompiles the web START runtime into a single bundle (`.framework/dist-web/startBundle/startEntry.js`, framework inlined) — the web counterpart to the api serve bundle. `bin/voltro.mjs`'s `voltro start` fast path prefers it, so a cold scale-from-zero web boot loads ONE artefact instead of resolving the whole `@voltro`/effect module graph. Measured on the web-spa-shell fixture: the `modules` boot phase collapses from ~1130 ms to ~67 ms (~17×), directly cutting the phase measured to dominate a scale-to-zero web cold start. Non-fatal — if the bundle build or its import fails, `voltro start` falls back to the per-module CLI entry (correct, just slower). No app or Dockerfile change is needed; the bundle rides in `.framework`, which `pnpm deploy` already copies.
84
+
85
+ ### Fixed
86
+
87
+ - **@voltro/runtime** — An undeclared infra error no longer reaches the browser as an `ExitEncoded` schema-tree dump (a wall of text that leaks internals and no client can pattern-match). `FieldDecryptionError` — a `.encrypted()` column that can't be decrypted with the active key — now collapses to a generic `InternalError` on both the unary and the subscription paths (it was previously only `SqlError` / `ResultLengthMismatch`, and only on the unary path). Its message names an internal `table.column`, so this also stops that leak; the real cause is logged server-side with the traceId. `TableValidationFailed` is deliberately left through — its summary/issues are meant to be shown to a user. Typed app errors are untouched.
88
+ - **@voltro/cli** — Security: the `/_voltro/inspect/logs` endpoint no longer bypasses the inspect gate. It dumps the process LogBuffer (request URLs + error payloads), but it returned early — above the `isInspectDisabled` / `VOLTRO_INSPECT_TOKEN` checks — so `VOLTRO_INSPECT=off` closed the manifest and metrics (503) while leaving the log buffer publicly readable, and a configured `VOLTRO_INSPECT_TOKEN` was ignored for it. The gate now lives inside `handleLogsRequest`, single-sourced across the three call sites (`voltro start` / `voltro dev` / the web-dev server) that each drifted (start ungated, dev gated nothing, web-dev gated disabled-but-not-token): disabled → 503, missing/bad bearer → 401.
89
+
90
+ ---
91
+
42
92
  ## [0.11.0] — 2026-07-22
43
93
 
44
94
  ### ⚠ BREAKING
package/dist/index.d.ts CHANGED
@@ -3,6 +3,28 @@ import { FormattedMessage } from 'react-intl';
3
3
  import { IntlConfig } from 'react-intl';
4
4
  import { ReactNode } from 'react';
5
5
 
6
+ /**
7
+ * Assert every locale uses the SAME ICU `{var}` set per key. `defineLocale`
8
+ * enforces KEY parity, but not PLACEHOLDER parity — a translation that drops or
9
+ * renames a `{var}` (`'Published on {date}'` → `'Veröffentlicht'`) compiles and
10
+ * boots, then throws `The intl string context variable "date" was not provided`
11
+ * ONLY in that locale, only when that message renders. This catches it up front.
12
+ *
13
+ * Call it in a test or at boot over your catalog map:
14
+ *
15
+ * import en from './locales/en'
16
+ * import de from './locales/de'
17
+ * assertCatalogParity({ en, de }) // throws listing every drift
18
+ *
19
+ * Keys missing from a non-base locale are left to `defineLocale` (this only
20
+ * compares the placeholder sets of keys present in both). `baseLocale` defaults
21
+ * to the first entry; `onMismatch: 'warn'` logs instead of throwing.
22
+ */
23
+ export declare const assertCatalogParity: (catalogs: Record<string, MessageCatalog>, options?: {
24
+ readonly baseLocale?: string;
25
+ readonly onMismatch?: "throw" | "warn";
26
+ }) => void;
27
+
6
28
  export declare type CatalogLoader = () => Promise<CatalogModule>;
7
29
 
8
30
  /** A locale's catalog module. Supports both `export default` and a bare map. */
@@ -10,6 +32,14 @@ export declare type CatalogModule = MessageCatalog | {
10
32
  readonly default: MessageCatalog;
11
33
  };
12
34
 
35
+ /**
36
+ * Bind the typed message API to a catalog's literal types. Pure type
37
+ * refinement: the runtime is exactly `useT` / `useTFn` / `<T>` — this only
38
+ * narrows their signatures to `C`, so there is no behaviour change and no
39
+ * runtime cost. See the module header for the call-once pattern and scope.
40
+ */
41
+ export declare const createTypedMessages: <C extends MessageCatalog>() => TypedMessages<C>;
42
+
13
43
  export declare type DateInput = Date | number | string;
14
44
 
15
45
  /**
@@ -61,6 +91,18 @@ declare interface I18nProviderProps {
61
91
  readonly children: ReactNode;
62
92
  }
63
93
 
94
+ /** The declared NAME of an ICU argument: the identifier before the first comma
95
+ * (`{count, number}` → `count`), or the whole body for a bare `{name}`. */
96
+ declare type ICUArgName<Body extends string> = Body extends `${infer Name},${string}` ? Trim<Name> : Trim<Body>;
97
+
98
+ /**
99
+ * The union of ICU placeholder names in a message template literal — `never`
100
+ * when it has none, or when the message type is a non-literal `string` (the
101
+ * untyped-catalog fallback). Simple `{var}` and top-level `{var, type}` only;
102
+ * nested ICU is out of scope (see the module header).
103
+ */
104
+ export declare type ICUVars<S extends string> = string extends S ? never : S extends `${string}{${infer Body}}${infer Rest}` ? ICUArgName<Body> | ICUVars<Rest> : never;
105
+
64
106
  export declare interface LazyCatalogs<L extends string = string> {
65
107
  /** Locales this app declares, in declaration order. */
66
108
  readonly locales: ReadonlyArray<L>;
@@ -116,8 +158,20 @@ declare interface LazyI18nProviderProps {
116
158
  */
117
159
  export declare const makeSsgWrap: (locale: string, messages: MessageCatalog, defaultLocale: string) => ((children: ReactNode) => ReactNode);
118
160
 
161
+ /**
162
+ * The positional `values` a message key requires: none for a plain message, a
163
+ * required record for one with placeholders, and a loose optional bag when the
164
+ * catalog value is a non-literal `string` (so an untyped catalog still works).
165
+ */
166
+ export declare type MessageArgs<Msg extends string> = string extends Msg ? [values?: Readonly<Record<string, MessageValue>>] : ICUVars<Msg> extends never ? [] : [values: {
167
+ readonly [K in ICUVars<Msg>]: MessageValue;
168
+ }];
169
+
119
170
  export declare type MessageCatalog = Readonly<Record<string, string>>;
120
171
 
172
+ /** The value types an ICU placeholder accepts. */
173
+ export declare type MessageValue = string | number | boolean | Date | null | undefined;
174
+
121
175
  /**
122
176
  * Pick the catalog for a locale OUTSIDE React — for `meta({ locale })` and other
123
177
  * non-hook call sites that resolve a message for an ARBITRARY locale, where
@@ -166,6 +220,26 @@ declare type TProps = Omit<ComponentProps<typeof FormattedMessage>, 'id'> & {
166
220
  readonly id: string;
167
221
  };
168
222
 
223
+ declare type Trim<S extends string> = S extends ` ${infer R}` ? Trim<R> : S extends `${infer R} ` ? Trim<R> : S;
224
+
225
+ export declare interface TypedMessages<C extends MessageCatalog> {
226
+ /** `useT(id, …values)` — a string, with the key and ICU params type-checked. */
227
+ readonly useT: TypedTFunction<C>;
228
+ /** The captured-once function form; same typing as `useT`. */
229
+ readonly useTFn: () => TypedTFunction<C>;
230
+ /** `<T id=… />` with a catalog-typed key (values stay loose for rich text). */
231
+ readonly T: (props: TypedTProps<C>) => ReactNode;
232
+ }
233
+
234
+ /** The typed key + params surface of a bound catalog. */
235
+ export declare type TypedTFunction<C extends MessageCatalog> = <K extends keyof C & string>(id: K, ...args: MessageArgs<C[K]>) => string;
236
+
237
+ /** `<T>` prop shape: a catalog-typed `id` with react-intl's own (loose) values —
238
+ * `<T>` supports rich-text tag renderers that `{var}` extraction can't model. */
239
+ export declare type TypedTProps<C extends MessageCatalog> = Omit<ComponentProps<typeof FormattedMessage>, 'id'> & {
240
+ readonly id: keyof C & string;
241
+ };
242
+
169
243
  /** Convenience: currency formatting with the ISO code up front. */
170
244
  export declare const useFormatCurrency: (currency: string) => ((value: number, options?: Intl.NumberFormatOptions) => string);
171
245
 
package/dist/index.js CHANGED
@@ -93,14 +93,46 @@ var l = ({ locale: e, messages: n, defaultLocale: i, silenceMissingTranslations:
93
93
  r,
94
94
  i
95
95
  ]);
96
- }, w = (e) => e, T = () => (e) => e, E = (e, t, n) => e[t ?? n] ?? e[n], D = (e) => "default" in e && typeof e.default == "object" ? e.default : e, O = (e, t) => {
96
+ }, w = (e) => e, T = () => (e) => e, E = (e, t, n) => e[t ?? n] ?? e[n], D = /\{\s*([A-Za-z0-9_]+)/g, O = (e) => {
97
+ let t = /* @__PURE__ */ new Set();
98
+ for (let n of e.matchAll(D)) t.add(n[1]);
99
+ return t;
100
+ }, k = (e, t) => {
101
+ let n = Object.keys(e);
102
+ if (n.length < 2) return;
103
+ let r = t?.baseLocale ?? n[0], i = e[r] ?? {}, a = [];
104
+ for (let [t, o] of Object.entries(i)) {
105
+ let i = O(o);
106
+ for (let o of n) {
107
+ if (o === r) continue;
108
+ let n = e[o]?.[t];
109
+ if (n === void 0) continue;
110
+ let s = O(n), c = [...i].filter((e) => !s.has(e)), l = [...s].filter((e) => !i.has(e));
111
+ if (c.length > 0 || l.length > 0) {
112
+ let e = [c.length > 0 ? `missing {${c.join("}, {")}}` : "", l.length > 0 ? `unexpected {${l.join("}, {")}}` : ""].filter(Boolean);
113
+ a.push(`${o} '${t}': ${e.join("; ")} (base ${r} has {${[...i].join("}, {")}})`);
114
+ }
115
+ }
116
+ }
117
+ if (a.length === 0) return;
118
+ let o = `i18n placeholder parity — a translation dropped or renamed an ICU {var}; it would throw only in that locale, only when the message renders:\n ${a.join("\n ")}`;
119
+ if (t?.onMismatch === "warn") {
120
+ console.warn(o);
121
+ return;
122
+ }
123
+ throw Error(o);
124
+ }, A = () => ({
125
+ useT: d,
126
+ useTFn: m,
127
+ T: u
128
+ }), j = (e) => "default" in e && typeof e.default == "object" ? e.default : e, M = (e, t) => {
97
129
  let n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map(), i = (n) => n && n in e ? n : t, a = (t) => {
98
130
  let a = i(t), o = n.get(a);
99
131
  if (o) return Promise.resolve(o);
100
132
  let s = r.get(a);
101
133
  if (s) return s;
102
134
  let c = e[a], l = c().then((e) => {
103
- let t = D(e);
135
+ let t = j(e);
104
136
  return n.set(a, t), r.delete(a), t;
105
137
  }).catch((e) => {
106
138
  throw r.delete(a), e;
@@ -116,7 +148,7 @@ var l = ({ locale: e, messages: n, defaultLocale: i, silenceMissingTranslations:
116
148
  a(e).catch(() => {});
117
149
  }
118
150
  };
119
- }, k = ({ catalogs: e, locale: t, fallback: n = null, silenceMissingTranslations: i, children: a }) => {
151
+ }, N = ({ catalogs: e, locale: t, fallback: n = null, silenceMissingTranslations: i, children: a }) => {
120
152
  let [s, u] = c(() => e.peek(t));
121
153
  return o(() => {
122
154
  let n = e.peek(t);
@@ -137,7 +169,7 @@ var l = ({ locale: e, messages: n, defaultLocale: i, silenceMissingTranslations:
137
169
  ...i === void 0 ? {} : { silenceMissingTranslations: i },
138
170
  children: a
139
171
  }) : n;
140
- }, A = (e, t, n) => (r) => i(l, {
172
+ }, P = (e, t, n) => (r) => i(l, {
141
173
  locale: e,
142
174
  messages: t,
143
175
  defaultLocale: n,
@@ -145,4 +177,4 @@ var l = ({ locale: e, messages: n, defaultLocale: i, silenceMissingTranslations:
145
177
  children: r
146
178
  });
147
179
  //#endregion
148
- export { l as I18nProvider, k as LazyI18nProvider, u as T, w as defineCatalog, O as defineCatalogs, T as defineLocale, A as makeSsgWrap, E as pickCatalog, h as plural, S as useFormatCurrency, v as useFormatDate, x as useFormatNumber, C as useFormatters, f as useLocale, p as useMessages, g as usePlural, b as useRelativeTime, d as useT, m as useTFn };
180
+ export { l as I18nProvider, N as LazyI18nProvider, u as T, k as assertCatalogParity, A as createTypedMessages, w as defineCatalog, M as defineCatalogs, T as defineLocale, P as makeSsgWrap, E as pickCatalog, h as plural, S as useFormatCurrency, v as useFormatDate, x as useFormatNumber, C as useFormatters, f as useLocale, p as useMessages, g as usePlural, b as useRelativeTime, d as useT, m as useTFn };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/i18n",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Voltro's i18n layer — thin opinionated wrap over react-intl. Provides the framework's locale-resolution conventions (voltro:lang cookie + Accept-Language fallback), a typed message-catalog helper, and a slim provider component. Power users can import directly from react-intl for advanced APIs.",
5
5
  "keywords": [
6
6
  "voltro",