@secundus-studio/stift-generator 0.0.0-stage → 0.4.31

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 ADDED
@@ -0,0 +1,155 @@
1
+ # @secundus-studio/stift-generator
2
+
3
+ ## 0.4.31
4
+
5
+ ### Patch Changes
6
+
7
+ - @secundus-studio/stift-core@0.4.31
8
+
9
+ ## 0.4.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Republish 0.4.1 as 0.4.2 with installable tarballs: the 0.4.1 artifacts carried literal `workspace:*` dependency ranges and could not be installed by any package manager. No code changes.
14
+ - Updated dependencies
15
+ - @secundus-studio/stift-core@0.4.2
16
+
17
+ ## 0.4.1
18
+
19
+ ### Minor Changes
20
+
21
+ - ec4a6c1: Replace the three per-concern persistence options with one persisted-state
22
+ config. `createStift` no longer takes `localePersistence`, `paramsPersistence`,
23
+ and `unitsPersistence`; the active locale, context-param overrides, and unit
24
+ overrides now live in one state store keyed `${prefix}${field}` (default
25
+ `__stift_locale`, `__stift_gender`, `__stift_length`, …) and backed by
26
+ `state.storage` (`'webStorage'` default, `'cookie'`, or `'none'`). Reads are
27
+ synchronous, so persisted values apply before the first render — no flash.
28
+ `state.prefix` and `state.storage` are configurable globally in
29
+ `stift.config.ts`, and per-field `persist: false` opts a param/unit out.
30
+
31
+ Add `createStiftApp()`: resolves a lazy persister factory and returns a
32
+ `boot()` that owns the offline-first `detectLocale → setLocale → initialize`
33
+ loop, so apps stop hand-writing the async singleton.
34
+
35
+ Detection gains a reserved `'persisted'` detector for the stored locale — rank
36
+ it in `detection.order` where the user's saved choice should sit; the
37
+ `detectors.custom` glue is no longer needed. `serializeStiftState`/`hydrate`
38
+ now also carry param/unit overrides so a backend-resolved value renders on the
39
+ first client frame.
40
+
41
+ BREAKING (pre-1.0): `createStift({ localePersistence | paramsPersistence |
42
+ unitsPersistence })` is removed — move to `state`.
43
+ `@secundus-studio/stift-tanstack-router-plugin`'s
44
+ `localStoragePersister`/`cookiePersister` now default to `__stift_locale`
45
+ (previously `stift:locale` / `locale`) so the router and the Stift instance
46
+ share one persisted choice.
47
+
48
+ ### Patch Changes
49
+
50
+ - Updated dependencies [ec4a6c1]
51
+ - @secundus-studio/stift-core@0.4.1
52
+
53
+ ## 0.4.0
54
+
55
+ ### Minor Changes
56
+
57
+ - d60faf6: Split the pack catalog into a slim `catalog.json` index plus one
58
+ content-addressed shard per locale. The index (namespaces, sizes,
59
+ coverage, shard URL + hash) is all that's inlined or fetched for an
60
+ update check; per-namespace URLs and hashes move to
61
+ `catalog/<locale>.<hash>.json`, fetched lazily and never re-downloaded
62
+ for locales that didn't change. Update checks short-circuit at the
63
+ locale level via the shard hash, so a one-key edit no longer invalidates
64
+ every namespace in a locale.
65
+
66
+ BREAKING (pre-1.0): `PackCatalog` is now an object with a `buildId` and a
67
+ `locales[]` array of index rows (not a bare entry array), and the
68
+ per-namespace `namespaces[]` detail lives in the new `LocaleShard`
69
+ type. Code that read `PackCatalogEntry.namespaces`/`.url` directly must
70
+ go through the runtime's lazy shard source or `parseLocaleShard`.
71
+
72
+ ### Patch Changes
73
+
74
+ - Updated dependencies [d60faf6]
75
+ - @secundus-studio/stift-core@0.4.0
76
+
77
+ ## 0.3.0
78
+
79
+ ### Minor Changes
80
+
81
+ - 71e677d: Rename the library from l10n to Stift. Packages move to `@secundus-studio/stift-*`, the config file is `stift.config.ts`, the CLI is `stift`, and the API follows (`createStift`, `Stift`, `StiftProvider`, `StiftConfig`, `virtual:stift/runtime`). BREAKING (pre-1.0): update imports, the config filename, and pack tooling; old `.dat` packs (I18NKIT magic) are rejected by the new decoder and re-fetched, and renamed storage keys, cookie names, and devtools routes start fresh.
82
+
83
+ ### Patch Changes
84
+
85
+ - Updated dependencies [71e677d]
86
+ - @secundus-studio/stift-core@0.3.0
87
+
88
+ ## 0.2.0
89
+
90
+ ### Minor Changes
91
+
92
+ - 13824b6: Remove `timezones` and `calendars` from `l10n.config.ts` and make `numberingSystems` optional. Timezones and calendars are per-user runtime values from the browser's own ICU data, not something the project ships — the format call-sites take plain strings and `Intl` still throws on invalid values. BREAKING (pre-1.0): delete those keys from your config; the format option types widen to `string`.
93
+ - 1770b34: Add `security.encoding: "json"` for plain readable packs. Builds emit raw `.json` per locale×namespace with catalog URLs to match, and the runtime fetches and parses them with no codec, decompression, or key — offline persisters, update checks, and devtools live-edit keep working unchanged.
94
+
95
+ ### Patch Changes
96
+
97
+ - fe77724: Reject `messageFormat: "icu-mf2"` at config load. Only `"icu-mf1"` has a formatter engine, so selecting MF2 now fails fast instead of silently building MF1 packs.
98
+ - Updated dependencies [13824b6]
99
+ - Updated dependencies [1770b34]
100
+ - @secundus-studio/l10n-core@0.2.0
101
+
102
+ ## 0.1.0
103
+
104
+ ### Minor Changes
105
+
106
+ - 275184d: Pack truth, catalog v2, boot modes, SSR hydration, TTL, and a new Cache API persister.
107
+
108
+ **Core**
109
+
110
+ - `pack.rehydrateInstalled()` + `initialize()` seeds reactive pack status from
111
+ the persister — returning visits read `installed` from the first notification.
112
+ - Honest lifecycle: per-namespace `__meta` writes (a failed install/refresh
113
+ never corrupts the previous good pack), in-flight healing, `error` (not fake
114
+ `idle`) on partial wipes, `installLocale` failures via `hooks.onError`.
115
+ - Byte-true `bytesReceived`/`bytesTotal` from the catalog; unified "installed"
116
+ definition (`isInstalled`/`getPackSize`/status all key on `__meta`);
117
+ `isNamespaceAvailable` honors cache ∪ persister.
118
+ - `pack.durability` (`'durable' | 'memory' | 'none'`); background full-locale
119
+ installs skip catalog-matching namespaces.
120
+ - `pack.checkAllForUpdates()`: one catalog fetch diffed against every
121
+ persisted locale.
122
+ - `createI18n({ persisterTtlMs })`: expired packs stay usable, report
123
+ `reason: 'ttl-expired'`, and refresh in the background on `activate`.
124
+ - `serializeI18nState()` + `createI18n({ hydrate })`: SSR hydration without an
125
+ SSR boot mode. `scope()` single-tuple overload.
126
+ - Deprecated `interpolate`/`plainFormatterEngine` (ICU MF1 supersedes them).
127
+
128
+ **Catalog (generator + vite-plugin)**
129
+
130
+ - Content-hashed pack URLs (`<ns>.<hash>.dat`), identical in dev and prod.
131
+ - Conditional catalog ping (ETag/`If-None-Match`, `no-cache`, no cache-buster).
132
+ - Catalog entries gain `buildId`, `publishedAt`, `coverage` (authored fraction
133
+ from completion, key-presence fallback for direct `compileAll` use).
134
+
135
+ **React**
136
+
137
+ - `I18nProvider onBootError` (rejected `boot` reports instead of swallowing).
138
+ - Missing translator type exports (`MultiScopedTranslationResult`,
139
+ `BoundMultiT`, multi/rich/markdown variants); suspense single-flight keyed
140
+ per i18n instance.
141
+
142
+ **Devtools**: snapshot exposes `packDurability`; Pack state hides
143
+ install/delete affordances unless durable; memory-cache buckets relabeled.
144
+
145
+ **OPFS**: write queue self-heals after a transient failure.
146
+
147
+ **New package** `@secundus-studio/l10n-persisters`: OPFS, IndexedDB (idb-keyval), and
148
+ Cache Storage persisters (`/opfs`, `/idb-keyval`, `/cache-api` subpaths) plus
149
+ a `createBestEffortPersister()` fallback chain.
150
+
151
+ ### Patch Changes
152
+
153
+ - Updated dependencies [275184d]
154
+ - Updated dependencies [275184d]
155
+ - @secundus-studio/l10n-core@0.1.0
package/LICENSE ADDED
@@ -0,0 +1,41 @@
1
+ Copyright (c) 2026 Ali Alkhateeb. All rights reserved.
2
+
3
+ SECUNDUS L10N — PROPRIETARY LICENSE
4
+
5
+ The compiled software packages distributed under the @secundus-studio scope on
6
+ the public npm registry (the "Software") are proprietary to Ali Alkhateeb. They
7
+ are licensed, not sold.
8
+
9
+ 1. Grant. Subject to this license, Ali Alkhateeb grants you a non-exclusive,
10
+ non-transferable, royalty-free right to install and use the Software, in
11
+ object-code form, as a dependency of your own applications, including
12
+ commercial and closed-source applications.
13
+
14
+ 2. Restrictions. You may not:
15
+ (a) copy, publish, distribute, sublicense, or sell the Software, or make
16
+ it available to any third party, except as embedded in your own
17
+ application;
18
+ (b) modify, translate, reverse engineer, decompile, or disassemble the
19
+ Software, or attempt to derive its source code;
20
+ (c) remove or alter any copyright, license, or proprietary notices.
21
+
22
+ 3. No source code. This license grants no rights to the source code of the
23
+ Software. The source is confidential, is not distributed, and remains the
24
+ property of Ali Alkhateeb.
25
+
26
+ 4. Contributions. Any feedback, bug report, or suggestion you provide may be
27
+ used by Ali Alkhateeb without restriction or obligation.
28
+
29
+ 5. Termination. This license terminates automatically if you breach any of its
30
+ terms. On termination you must stop using the Software and delete all
31
+ copies in your possession or control.
32
+
33
+ 6. Disclaimer. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
36
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
37
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
38
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
39
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
40
+
41
+ For licensing inquiries, contact hello@secundus.studio.
package/README.md CHANGED
@@ -1,3 +1,51 @@
1
- # Temporary Holding Version
1
+ # @secundus-studio/stift-generator
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Bundler-agnostic build core: config loading/validation, namespace discovery, pack compiler, and typegen.
4
+
5
+ ## What it is
6
+
7
+ The shared build engine behind [`@secundus-studio/stift-cli`](https://www.npmjs.com/package/@secundus-studio/stift-cli) and [`@secundus-studio/stift-vite-plugin`](https://www.npmjs.com/package/@secundus-studio/stift-vite-plugin): config loading and validation (`stift.config.ts`), locale/namespace discovery, message-shape extraction, the pack compiler (`.dat` packs plus `catalog.json` and per-locale shards), and `Register` typegen (`stift.gen.d.ts`). Most users meet it through the CLI or the Vite plugin and never install it directly — reach for it when you need the pipeline programmatically (custom scripts, tests, or your own tooling).
8
+
9
+ ```bash
10
+ pnpm add @secundus-studio/stift-generator
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { Generator } from '@secundus-studio/stift-generator'
17
+
18
+ const generator = new Generator({ root: process.cwd() })
19
+
20
+ // Load stift.config.ts, discover locales/<locale>/<namespace>.json,
21
+ // extract param shapes, diff locales, emit src/stift.gen.d.ts.
22
+ await generator.generateAsync()
23
+
24
+ // Compile every (locale, namespace) into packs + catalog.json + shards.
25
+ await generator.compileAsync({ outDir: 'dist/packs' })
26
+ ```
27
+
28
+ `stift.config.ts` is authored with the `defineConfig` helper and resolved with `loadConfig` / `findConfigFile`.
29
+
30
+ ## API
31
+
32
+ - `Generator` — one-shot pipeline: `generateAsync()` (config → discovery → extraction → diff → `stift.gen.d.ts`) and `compileAsync()` (packs + catalog + shards, written to `outDir` or returned in memory).
33
+ - `DEFAULT_OUTPUT_REL` — default relative output path for the generated declaration file.
34
+ - `compileAll` — compile every (locale, namespace) source into pack bytes, the catalog index, and one shard per locale.
35
+ - `compileNamespace` — compile a single namespace message map into pack bytes.
36
+ - `resolveCompileSecurity` — resolve the effective compiler security config from a loaded config (with static-key fallback).
37
+ - `loadConfig` / `findConfigFile` — resolve and schema-validate `stift.config.ts`.
38
+ - `defineConfig` — typed helper for authoring `stift.config.ts`.
39
+ - `discoverConvention` — walk `locales/<locale>/<namespace>.json` (nested namespaces supported) into a stable discovery result.
40
+ - `generateTypegen` — emit the `stift.gen.d.ts` `Register` declaration from config plus extraction.
41
+ - `extractShapes` / `deriveLocaleShape` — infer union-across-locale param shapes from message sources.
42
+ - `diffLocales` — diff key coverage across supported locales against the default locale.
43
+ - `parseMessage` / `collectFunctions` — ICU message parsing and function collection (re-exported from core).
44
+ - `configSchema` — schema the loaded config is validated against.
45
+ - Type-only: `GenerateOptions`, `GenerateResult`, `CompileOptions`, `CompileResult`, `CompileAllOptions`, `CompileAllResult`, `CompileNamespaceOptions`, `CompiledNamespaceOutput`, `CompiledNamespaceCacheEntry`, `CompiledCache`, `LocaleShardOutput`, `CompileSecurityConfig`, `StiftConfig`, `ParamsConfig`, `ContextParamConfig`, `NamespacesConfig`, `DetectionConfig`, `LoadConfigOptions`, `LoadedConfig`, `DiscoveredNamespace`, `DiscoveryResult`, `ExtractResult`, `KeyShape`, `KeyLocaleShape`, `MessageSourceInput`, `ParamType`, `DiffResult`, `MissingDiffIssue`, `GenerateTypegenOptions`, `IcuNode`, `IcuOption`, `ParseResult`.
46
+
47
+ ## Docs
48
+
49
+ - Config file: https://stift.secundus.studio/docs/latest/vanilla/configuration/config-file
50
+ - Build pipeline: https://stift.secundus.studio/docs/latest/vanilla/tooling/build
51
+ - Authoring messages: https://stift.secundus.studio/docs/latest/vanilla/authoring/messages
package/dist/index.cjs ADDED
@@ -0,0 +1,37 @@
1
+ 'use strict';var N=require('fs/promises'),v=require('path'),q=require('fs'),jiti=require('jiti'),zod=require('zod'),stiftCore=require('@secundus-studio/stift-core'),msgpack=require('@secundus-studio/stift-core/codecs/msgpack'),cbor=require('@secundus-studio/stift-core/codecs/cbor'),node=require('@secundus-studio/stift-core/decompress/node');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var N__namespace=/*#__PURE__*/_interopNamespace(N);var v__namespace=/*#__PURE__*/_interopNamespace(v);var q__namespace=/*#__PURE__*/_interopNamespace(q);var Te=Object.defineProperty;var c=(e,t)=>Te(e,"name",{value:t,configurable:true});function Re(e){if(typeof e!="string"||e.trim()==="")return false;try{return new Intl.Locale(e),!0}catch{return false}}c(Re,"localeTagGuard");var G=zod.z.string().refine(Re);function Ne(e,t){if(typeof t!="string"||t.trim()==="")return false;if(e==="numberingSystem")try{return Intl.supportedValuesOf(e).includes(t)}catch{return true}try{return new Intl.NumberFormat("und",{style:"currency",currency:t}),!0}catch{return false}}c(Ne,"engineValueGuard");function ue(e,t){return zod.z.union([zod.z.literal("any"),zod.z.array(zod.z.string().min(1).refine(o=>Ne(e,o),{message:t})).min(1)])}c(ue,"engineList");var Ie=zod.z.object({name:zod.z.string().min(1),values:zod.z.array(zod.z.string().min(1)).min(1),defaults:zod.z.record(zod.z.string().min(1),zod.z.string()),persist:zod.z.boolean().optional()}),$e=zod.z.object({context:zod.z.array(Ie).optional()}).optional(),Oe=new Set(Object.values(stiftCore.UNITS_BY_CATEGORY).flat()),Z=zod.z.string().min(1).refine(e=>Oe.has(e),{message:"not a sanctioned ECMA-402 unit (docs/17-units.md)"}),Fe=zod.z.union([zod.z.literal("all"),zod.z.record(zod.z.enum(["length","mass","area","temperature","digital"]),zod.z.object({supportedValues:zod.z.union([zod.z.literal("all"),zod.z.array(Z).min(1)]),defaults:zod.z.object({all:Z}).catchall(Z),persist:zod.z.boolean().optional()}))]).optional(),Ee=zod.z.object({tier:zod.z.enum(["none","obfuscated","encrypted-static","encrypted-session"]),encoding:zod.z.enum(["msgpack","cbor","json"]),compression:zod.z.enum(["none","gzip","brotli"]),staticKey:zod.z.string().min(1).optional()}).superRefine((e,t)=>{e.encoding==="json"&&(e.tier!=="none"&&t.addIssue({code:zod.z.ZodIssueCode.custom,path:["tier"],message:`encoding "json" requires tier "none" (got "${e.tier}") \u2014 JSON packs are plain readable bytes`}),e.compression!=="none"&&t.addIssue({code:zod.z.ZodIssueCode.custom,path:["compression"],message:`encoding "json" requires compression "none" (got "${e.compression}") \u2014 JSON packs are served uncompressed`}));}).optional(),De=zod.z.object({maxCachedNamespaces:zod.z.number().int().positive().optional(),evictionPolicy:zod.z.enum(["lru","ttl"]).optional(),ttlMs:zod.z.number().positive().optional()}).optional(),Ke=zod.z.enum(["lazy-per-namespace","critical-then-full-background","boot-all"]).optional(),Ae=zod.z.object({order:zod.z.array(zod.z.string()).min(1),timeout:zod.z.union([zod.z.number(),zod.z.record(zod.z.string(),zod.z.number()).and(zod.z.object({default:zod.z.number()})),zod.z.function()]).optional(),url:zod.z.object({strategy:zod.z.enum(["path-prefix","subdomain","domain","query-param"]),paramName:zod.z.string().optional(),domainMap:zod.z.record(zod.z.string(),G).optional()}).optional(),cookie:zod.z.object({name:zod.z.string().optional(),maxAge:zod.z.number().optional(),path:zod.z.string().optional(),sameSite:zod.z.enum(["strict","lax","none"]).optional(),secure:zod.z.boolean().optional()}).optional(),fallback:G.optional()}).optional(),Ue=zod.z.object({prefix:zod.z.string().min(1).optional(),storage:zod.z.enum(["webStorage","cookie","none"]).optional()}).optional(),B=zod.z.object({locales:zod.z.object({default:G,supported:zod.z.array(G).min(1)}),numberingSystems:ue("numberingSystem","not a numbering system this engine recognizes").optional(),currencies:ue("currency","not a currency code this engine recognizes").optional(),namespaces:zod.z.object({dir:zod.z.string().min(1),discovery:zod.z.enum(["convention","manifest"]),default:zod.z.array(zod.z.string())}),messageFormat:zod.z.enum(["icu-mf1","icu-mf2"]).superRefine((e,t)=>{e==="icu-mf2"&&t.addIssue({code:zod.z.ZodIssueCode.custom,message:'messageFormat "icu-mf2" is not implemented yet \u2014 use "icu-mf1"'});}),params:$e,units:Fe,security:Ee,memory:De,loadingStrategy:Ke,bootExclude:zod.z.array(zod.z.string()).optional(),detection:Ae,state:Ue}).superRefine((e,t)=>{e.locales.supported.includes(e.locales.default)||t.addIssue({code:zod.z.ZodIssueCode.custom,path:["locales","supported"],message:`default locale "${e.locales.default}" must be listed in supported locales`});let o=e.params?.context;if(o){let n=e.locales.supported,a=new Set;o.forEach((i,g)=>{let l=["params","context",g];a.has(i.name)&&t.addIssue({code:zod.z.ZodIssueCode.custom,path:[...l,"name"],message:`context param name "${i.name}" is declared more than once`}),a.add(i.name),"all"in i.defaults||t.addIssue({code:zod.z.ZodIssueCode.custom,path:[...l,"defaults"],message:`context param "${i.name}" must declare an "all" fallback default`});let u=c((m,b)=>{i.values.includes(m)||t.addIssue({code:zod.z.ZodIssueCode.custom,path:b,message:`defaults value "${m}" for context param "${i.name}" is not one of its values [${i.values.join(", ")}]`});},"check");for(let[m,b]of Object.entries(i.defaults))u(b,[...l,"defaults",m]),m!=="all"&&!n.includes(m)&&t.addIssue({code:zod.z.ZodIssueCode.custom,path:[...l,"defaults",m],message:`defaults locale "${m}" is not in locales.supported`});});}let s=e.units;if(s&&s!=="all"){let n=e.locales.supported;for(let[a,i]of Object.entries(s)){let g=["units",a],l=i.supportedValues,u=i.defaults?.all;Array.isArray(l)&&u&&!l.includes(u)&&t.addIssue({code:zod.z.ZodIssueCode.custom,path:[...g,"defaults","all"],message:`default unit "${u}" for "${a}" is not in supportedValues [${l.join(", ")}]`});for(let[m]of Object.entries(i.defaults??{}))m!=="all"&&(n.includes(m)||t.addIssue({code:zod.z.ZodIssueCode.custom,path:[...g,"defaults",m],message:`units locale "${m}" is not in locales.supported`}));}}});var He=["stift.config.ts","stift.config.mts","stift.config.js"];function me(e){for(let t of He){let o=v__namespace.join(e,t);if(q__namespace.existsSync(o))return o}}c(me,"findConfigFile");async function H(e={}){let t=e.root??process.cwd(),o=e.configFile??me(t)??(()=>{throw new Error(`No stift.config.ts found under "${t}". Create one or pass configFile.`)})(),n=await jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)),{interopDefault:true,moduleCache:false}).import(o),a=Je(n),i=B.safeParse(a);if(!i.success){let g=i.error.issues.map(l=>` - ${l.path.join(".")}: ${l.message}`).join(`
2
+ `);throw new Error(`Invalid stift config at ${o}:
3
+ ${g}`)}return {config:i.data,file:o}}c(H,"loadConfig");function Je(e){return e&&typeof e=="object"&&"default"in e&&e.default?e.default:e}c(Je,"constructConfig");async function J(e,t="locales"){let o=v__namespace.join(e,t),s;try{s=await q__namespace.promises.readdir(o,{withFileTypes:!0});}catch{return {byLocale:new Map,all:[]}}let n=new Map,a=[];for(let i of s){if(!i.isDirectory())continue;let g=i.name,l=[];await de(v__namespace.join(o,g),g,v__namespace.join(o,g),l),l.length>0&&(n.set(g,l),a.push(...l));}return a.sort((i,g)=>(i.locale+"/"+i.namespace).localeCompare(g.locale+"/"+g.namespace)),{byLocale:n,all:a}}c(J,"discoverConvention");async function de(e,t,o,s){let n;try{n=await q__namespace.promises.readdir(e,{withFileTypes:!0});}catch{return}for(let a of n){let i=v__namespace.join(e,a.name);if(a.isDirectory())await de(i,t,o,s);else if(a.isFile()&&a.name.endsWith(".json")){let l=v__namespace.relative(o,i).split(v__namespace.sep).join("/").replace(/\.json$/,"");s.push({locale:t,namespace:l,file:i});}}}c(de,"walkNamespaceDir");function V(e){return typeof e=="string"?e:e.value}c(V,"messageValue");function ye(e){let{ast:t}=stiftCore.parseMessage(e),o={};return he(t,o),o}c(ye,"deriveLocaleShape");function he(e,t){for(let o of e){if(o.type!=="argument"&&o.type!=="function")continue;let s=o.name,n=t[s];if(o.argType&&o.argType.toLowerCase()==="select"){let a=o.type==="function"?ze(o):[],i=n?_e(n,a):{kind:"select",categories:a};t[s]=i;}else o.argType&&(o.argType.toLowerCase()==="plural"||o.argType.toLowerCase()==="selectordinal"||o.argType.toLowerCase()==="number")?t[s]={kind:"number"}:t[s]={kind:"primitive"};for(let a of o.options)he(a.value,t);}}c(he,"collectParams");function _e(e,t){return e.kind!=="select"?{kind:"select",categories:t}:{kind:"select",categories:Ce(e.categories,t)}}c(_e,"unionCategories");function ze(e){return e.options.map(t=>t.key)}c(ze,"optionKeys");function Ce(...e){let t=new Set;for(let o of e)for(let s of o)t.add(s);return [...t].sort()}c(Ce,"mergeStrings");function W(e,t){let o=new Map,s=[],n=new Map;for(let a of e){let i=`${a.namespace}\0${a.key}`,g=n.get(i);g?g.push(a):n.set(i,[a]);}for(let[a,i]of n){let[g,l]=a.split("\0"),u={},m={};for(let p of i){let f=ye(p.value);for(let[y,d]of Object.entries(f)){u[y]=Ze(u[y],d);let C=m[y]??(m[y]=[]);C.includes(p.locale)||C.push(p.locale);}}if(t?.context?.length){let p=new Map;for(let f of t.context)p.set(f.name,f);for(let[f,y]of Object.entries(u)){let d=p.get(f);d&&y.kind==="select"&&Be(s,g,l,f,y.categories,d,m[f]??[]);}}let b=o.get(g);b||(b=new Map,o.set(g,b));let L={};for(let[p,f]of Object.entries(u))L[p]=f.kind==="select"?{kind:"select",categories:[...f.categories].sort()}:f;b.set(l,{params:L,paramLocales:m});}return {byNamespace:o,warnings:s}}c(W,"extractShapes");function Ze(e,t){return e?e.kind==="number"&&t.kind==="number"||e.kind==="primitive"&&t.kind==="primitive"?e:e.kind==="select"&&t.kind==="select"?{kind:"select",categories:Ce(e.categories,t.categories)}:e.kind==="number"&&t.kind==="primitive"||e.kind==="primitive"&&t.kind==="number"?{kind:"number"}:t.kind==="select"?t:e:t}c(Ze,"unionParam");function Be(e,t,o,s,n,a,i){let g=new Set(a.values),l=n.filter(u=>u!=="other"&&!g.has(u));l.length!==0&&e.push(`[${t}:${o}] param "${s}" uses categories [${l.join(", ")}] outside the configured canonical set [${a.values.join(", ")}] (used by ${i.join(", ")})`);}c(Be,"warnOnCategoryMismatch");async function Y(e){let t=[];for(let o of e){let s=await N__namespace.readFile(o.file,"utf8"),n;try{n=JSON.parse(s);}catch(a){throw new Error(`Invalid JSON in ${o.file}: ${a.message}`)}if(typeof n!="object"||n===null||Array.isArray(n))throw new Error(`Namespace file ${o.file} must contain a JSON object`);for(let[a,i]of Object.entries(n)){if(typeof i!="string"&&!qe(i))throw new Error(`Message "${a}" in ${o.file} must be a string or { value: string }`);t.push({locale:o.locale,namespace:o.namespace,key:a,value:V(i)});}}return t}c(Y,"readNamespaceSources");function qe(e){return typeof e=="object"&&e!==null&&typeof e.value=="string"}c(qe,"isMessageSource");function We(e){let t=new Map;for(let o of e){let s=t.get(o.locale);s||(s=new Map,t.set(o.locale,s));let n=s.get(o.namespace);n||(n=new Set,s.set(o.namespace,n)),n.add(o.key);}return t}c(We,"presenceByLocale");function Q(e,t,o){let s=We(e),n=s.get(o);if(!n)return {missing:[]};let a=new Set(t),i=t.filter(l=>!Ye(l,a)),g=[];for(let l of i){if(l===o)continue;let u=s.get(l);for(let[m,b]of n){let L=u?.get(m);for(let p of b)L?.has(p)||g.push({namespace:m,key:p,locale:l});}}return g.sort((l,u)=>(l.namespace+"/"+l.key+"/"+l.locale).localeCompare(u.namespace+"/"+u.key+"/"+u.locale)),{missing:g}}c(Q,"diffLocales");function Ye(e,t){let o=e;for(;;){let s=o.lastIndexOf("-");if(s<=0||s===o.length-1)return false;if(o=o.slice(0,s),t.has(o))return true}}c(Ye,"hasParentWithin");function T(e){return JSON.stringify(e)}c(T,"quote");function Xe(e){return e.kind==="number"?"number":e.kind==="primitive"?"string | number":e.categories.map(T).join(" | ")}c(Xe,"paramToTs");var X={};function et(e){if(X[e])return X[e];let t=[];try{t=Intl.supportedValuesOf(e);}catch{t=[];}return X[e]=t,t}c(et,"engineValues");function xe(e,t){if(e==="any"){let s=et(t);return s.length===0?"string":s.map(T).join(" | ")}return e.map(T).join(" | ")||"string"}c(xe,"listOrAny");function tt(e,t){let o=e.units;if(o==="all"||!o)return stiftCore.UNITS_BY_CATEGORY[t];let s=o[t];if(!s)return null;let n=s.supportedValues;return !n||n==="all"?stiftCore.UNITS_BY_CATEGORY[t]:[...n]}c(tt,"unitsForCategory");function ee(e){let{config:t,extract:o}=e,s=[...o.byNamespace.keys()].sort(),n=new Map;for(let u of t.params?.context??[])n.set(u.name,u.values);let a=[];for(let u of s){let m=o.byNamespace.get(u),b=[];for(let L of [...m.keys()].sort()){let p=m.get(L),f=Object.keys(p.params).sort();if(f.length===0){b.push(` ${T(L)}: {}`);continue}let y=f.map(d=>{let C=n.get(d),x=C!==void 0,I=C?C.map(T).join(" | "):Xe(p.params[d]);return ` ${T(d)}${x?"?":""}: ${I}`}).join(`
4
+ `);b.push(` ${T(L)}: {
5
+ ${y}
6
+ }`);}a.push(` ${T(u)}: {
7
+ ${b.join(`
8
+ `)}
9
+ }`);}let i=[];if(n.size>0)for(let u of [...n.keys()].sort()){let m=n.get(u).map(T).join(" | ");i.push(` ${T(u)}: ${m}`);}let g=i.length>0?` context: {
10
+ ${i.join(`
11
+ `)}
12
+ }
13
+ `:"",l=stiftCore.UNIT_CATEGORIES.flatMap(u=>{let m=tt(t,u);return m?[` ${T(u)}: ${m.map(T).join(" | ")}`]:[]});return `/**
14
+ * Generated by @secundus-studio/stift-generator \u2014 do not edit by hand.
15
+ * Regenerate with \`stift generate\` (and in Phase 5, the dev plugin watches).
16
+ */
17
+ import "@secundus-studio/stift-core"
18
+ declare module "@secundus-studio/stift-core" {
19
+ interface Register {
20
+ locale: ${t.locales.supported.map(T).join(" | ")}
21
+ numberingSystems: ${xe(t.numberingSystems??"any","numberingSystem")}
22
+ currencies: ${xe(t.currencies??"any","currency")}
23
+ units: {
24
+ ${l.join(`
25
+ `)}
26
+ }
27
+ messages: {
28
+ ${a.join(`
29
+ `)}
30
+ }
31
+ ${g} }
32
+ }
33
+ `}c(ee,"generateTypegen");async function oe(e,t,o){let s=v__namespace.join(e,t),n=[];for(let a of o){let i=v__namespace.join(s,a);try{await N__namespace.access(i);}catch{await N__namespace.mkdir(i,{recursive:true}),n.push(i);}}return {createdDirs:n}}c(oe,"ensureLocaleDirectories");async function ot(e){let t=new Map;for(let o of e){let s=JSON.parse(await N__namespace.readFile(o.file,"utf8"));if(typeof s!="object"||s===null||Array.isArray(s))throw new Error(`Namespace file ${o.file} must contain a JSON object`);let n=t.get(o.locale);n||(n=new Map,t.set(o.locale,n)),n.set(o.namespace,s);}return t}c(ot,"readRawFiles");function nt(e){let t=Object.entries(e);return t.length===0?"{}":`{
34
+ ${t.map(([s,n])=>` ${JSON.stringify(s)}: ${JSON.stringify(n)}`).join(`,
35
+ `)}
36
+ }`}c(nt,"serializeNamespace");async function ne(e){let{root:t,localesDir:o,defaultLocale:s,locales:n}=e,a=await ot(e.discovery.all),i=new Map,g=a.get(s);if(g)for(let[h,S]of g)i.set(h,{order:Object.keys(S),values:S});let l=[];i.size===0&&l.push(`[stift] default locale "${s}" has no namespace files \u2014 nothing to complete`);let u=new Set(n);function m(h){let S=[],k=h;for(;;){let M=k.lastIndexOf("-");if(M<=0)break;k=k.slice(0,M),k!==s&&u.has(k)&&S.push(k);}return [...S,s]}c(m,"fillChain");function b(h,S,k,M){for(let F of k){let $=a.get(F)?.get(S)?.[h];if($!==void 0)return $}return M[h]}c(b,"resolveValue");let L=[],p=[],f=[],y=[],d=0,C=new Set,x={[s]:1};for(let h of n){if(h===s)continue;let S=a.get(h),k=m(h),M=0,F=0,$=c(w=>w!==s,"inheritedIsTranslated");for(let[w,P]of i){let A=S?.get(w),D=v__namespace.join(t,o,h,`${w}.json`),Me=S?.has(w)??false;C.add(`${h}\0${w}`);let je=new Set(P.order),K={};for(let j of P.order){let U=A?.[j];if(U!==void 0)K[j]=U,M++;else {K[j]=b(j,w,k,P.values),d++;for(let pe of k){if(!$(pe))break;if(a.get(pe)?.get(w)?.[j]!==void 0){M++;break}}}F++;}if(A)for(let j of Object.keys(A))je.has(j)||(K[j]=A[j],y.push({namespace:w,key:j,locale:h}));let ce=nt(K)+`
37
+ `,le=true;try{le=await N__namespace.readFile(D,"utf8")!==ce;}catch{}le&&(await N__namespace.mkdir(v__namespace.dirname(D),{recursive:true}),await N__namespace.writeFile(D,ce,"utf8"),p.push(D),Me||f.push(D));for(let[j,U]of Object.entries(K))L.push({locale:h,namespace:w,key:j,value:V(U)});}x[h]=F>0?Math.min(1,M/F):1;}let R=[...e.sources.filter(h=>!(h.locale!==s&&C.has(`${h.locale}\0${h.namespace}`))),...L];return R.sort((h,S)=>h.locale.localeCompare(S.locale)||h.namespace.localeCompare(S.namespace)||h.key.localeCompare(S.key)),{sources:R,writtenFiles:p,createdFiles:f,orphanKeys:y,filledKeys:d,authoredCoverage:x,warnings:l}}c(ne,"completeSources");var pt={tier:"obfuscated",encoding:"msgpack",compression:"gzip"};function re(e){let t=e.security??pt;return {tier:t.tier,encoding:t.encoding,compression:t.compression,staticKey:t.staticKey??process.env.STIFT_STATIC_KEY??void 0}}c(re,"resolveCompileSecurity");function ut(e){if(e==="cbor")return cbor.cborCodec;if(e==="msgpack")return msgpack.msgpackCodec;throw new Error(`[stift] encoding "${e}" has no binary codec \u2014 JSON packs are raw bytes, not a compiled container`)}c(ut,"codecFor");async function ve(e,t){let{tier:o,encoding:s,compression:n}=t.security;if(s==="json"){if(o!=="none")throw new Error(`[stift] encoding "json" requires tier "none" (got "${o}")`);if(n!=="none")throw new Error(`[stift] encoding "json" requires compression "none" (got "${n}")`);return stiftCore.encodeJsonPack(e)}let a;if(o==="encrypted-static"||o==="encrypted-session"){if(!t.security.staticKey)throw new Error("[stift] security.staticKey is required to compile encrypted tiers");a=await stiftCore.deriveStaticKey(t.security.staticKey);}return stiftCore.encodeCompiled(e,{codec:ut(s),compression:n,tier:o,compress:node.createNodeCompress(n),key:a})}c(ve,"compileNamespace");function ft(e){let t=new Map;for(let o of e){let s=t.get(o.locale);s||(s=new Map,t.set(o.locale,s));let n=s.get(o.namespace);n||(n={},s.set(o.namespace,n)),n[o.key]=o.value;}return t}c(ft,"groupMessages");async function ae(e){let t=ft(e.sources),o=[],s=[],n=[],a=new Map,i=0,g=0,l=new Map;for(let p of e.locales){let f=t.get(p);if(!f||f.size===0)continue;let y=mt(p,e.locales,e.defaultLocale),d=[],C=[];for(let[S,k]of f){let M=`${p}/${S}`,F=await stiftCore.sha256Hex(gt(JSON.stringify(k))),$=e.cache?.get(M),w,P;$&&$.sourceHash===F?(w=$.bytes,P=$.contentHash,g++):(w=await ve(k,{security:e.security}),P=await stiftCore.sha256Hex(w),i++),d.push({namespace:S,url:e.urlFor(p,S,P),persistKey:M,byteLength:w.byteLength,contentHash:P}),C.push(P),o.push({locale:p,namespace:S,persistKey:M,bytes:w,byteLength:w.byteLength,contentHash:P}),a.set(M,{sourceHash:F,bytes:w,byteLength:w.byteLength,contentHash:P});}d.sort((S,k)=>S.namespace.localeCompare(k.namespace));let x=t.get(e.defaultLocale),I=0,R=0;if(x)for(let[S,k]of x)R+=Object.keys(k).length,I+=f.has(S)?Object.keys(f.get(S)).length:0;let h=e.coverageByLocale?.[p];l.set(p,{baseLocale:y,namespaces:d,hashes:C,coverage:h??(R>0?Math.min(1,I/R):void 0)});}let u=ke([...l.values()].flatMap(p=>p.hashes)),m=Date.now(),b=e.shardUrlFor??((p,f)=>`catalog/${p}.${f}.json`);for(let[p,f]of [...l.entries()].sort(([y],[d])=>y.localeCompare(d))){let y={version:stiftCore.CATALOG_VERSION,locale:p,namespaces:f.namespaces},d=new TextEncoder().encode(JSON.stringify(y)),C=await stiftCore.sha256Hex(d),x=f.namespaces.reduce((R,h)=>R+h.byteLength,0);n.push({locale:p,shard:y,bytes:d,byteLength:d.byteLength,namespacesHash:C});let I={locale:p,namespacesUrl:b(p,C),namespacesHash:C,namespaces:f.namespaces.map(R=>R.namespace),packVersion:ke(f.hashes),totalBytes:x};f.baseLocale!==void 0&&(I.baseLocale=f.baseLocale),f.coverage!==void 0&&(I.coverage=f.coverage),s.push(I);}return {outputs:o,catalog:{version:stiftCore.CATALOG_VERSION,buildId:u,publishedAt:m,locales:s},shards:n,cache:a,compiledCount:i,reusedCount:g}}c(ae,"compileAll");function gt(e){return new TextEncoder().encode(e)}c(gt,"utf8Bytes");function mt(e,t,o){if(e===o)return;let n=e.split("-");for(let a=n.length;a>=2;a--){let i=n.slice(0,a-1).join("-");if(t.includes(i))return i}}c(mt,"directParent");function ke(e){let t=e.sort().join(""),o=0;for(let s=0;s<t.length;s++)o=o*31+t.charCodeAt(s)>>>0;return o.toString(16)}c(ke,"shortHash");var Le=v__namespace.join("src","stift.gen.d.ts"),ie=class{static{c(this,"Generator");}constructor(t={}){this.root=t.root??process.cwd();}root;async generateAsync(t={}){let o=t.root??this.root,n=(await H({root:o,configFile:t.configFile})).config,a=await oe(o,n.namespaces.dir,n.locales.supported),i=await J(o,n.namespaces.dir),g=await Y(i.all),l=await ne({root:o,localesDir:n.namespaces.dir,defaultLocale:n.locales.default,locales:[...n.locales.supported],sources:g,discovery:i}),u=l.sources,m=W(u,n.params),b=[...n.locales.supported],L=Q(u,b,n.locales.default),p=v__namespace.resolve(o,t.outputFile??Le),f=ee({config:n,extract:m});await N__namespace.mkdir(v__namespace.dirname(p),{recursive:true}),await N__namespace.writeFile(p,f,"utf8");let y=l.orphanKeys.map(({namespace:d,key:C,locale:x})=>`[stift] key "${C}" in locale "${x}" (namespace "${d}") is not part of the default locale's schema \u2014 orphan`);return {config:n,discovery:i,extract:m,diff:L,sources:u,scaffold:a,completion:l,outputFile:p,warnings:[...m.warnings,...y,...l.warnings]}}makeWatchFilter(t,o){let s=v__namespace.resolve(t,o);return n=>{let a=v__namespace.resolve(n);return a.startsWith(s+v__namespace.sep)&&a.endsWith(".json")}}async compileAsync(t={}){let o=t.root??this.root,n=(await H({root:o,configFile:t.configFile})).config;await oe(o,n.namespaces.dir,n.locales.supported);let a=await J(o,n.namespaces.dir),i=await Y(a.all),g=await ne({root:o,localesDir:n.namespaces.dir,defaultLocale:n.locales.default,locales:[...n.locales.supported],sources:i,discovery:a}),l=[...n.locales.supported],u=t.security??re(n),m=u.encoding==="json"?"json":"dat",b=t.urlFor??((y,d)=>`${y}/${d}.${m}`),L=t.shardUrlFor??((y,d)=>`catalog/${y}.${d}.json`),p=await ae({sources:g.sources,locales:l,defaultLocale:n.locales.default,security:u,urlFor:b,shardUrlFor:L,coverageByLocale:g.authoredCoverage}),f=[];if(t.outDir){let y=v__namespace.resolve(o,t.outDir);for(let C of p.outputs){let x=v__namespace.join(y,`${C.persistKey}.${m}`);await N__namespace.mkdir(v__namespace.dirname(x),{recursive:true}),await N__namespace.writeFile(x,C.bytes),f.push(x);}let d=v__namespace.join(y,"catalog.json");await N__namespace.mkdir(v__namespace.dirname(d),{recursive:true}),await N__namespace.writeFile(d,JSON.stringify(p.catalog,null,2),"utf8"),f.push(d);for(let C of p.shards){let x=v__namespace.join(y,"catalog",`${C.locale}.${C.namespacesHash}.json`);await N__namespace.mkdir(v__namespace.dirname(x),{recursive:true}),await N__namespace.writeFile(x,C.bytes),f.push(x);}}return {config:n,result:p,writtenFiles:f}}};function dt(e){return e}c(dt,"defineConfig");Object.defineProperty(exports,"collectFunctions",{enumerable:true,get:function(){return stiftCore.collectFunctions}});Object.defineProperty(exports,"parseMessage",{enumerable:true,get:function(){return stiftCore.parseMessage}});exports.DEFAULT_OUTPUT_REL=Le;exports.Generator=ie;exports.compileAll=ae;exports.compileNamespace=ve;exports.configSchema=B;exports.defineConfig=dt;exports.deriveLocaleShape=ye;exports.diffLocales=Q;exports.discoverConvention=J;exports.extractShapes=W;exports.findConfigFile=me;exports.generateTypegen=ee;exports.loadConfig=H;exports.resolveCompileSecurity=re;