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

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,149 @@
1
+ # @secundus-studio/stift-generator
2
+
3
+ ## 0.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 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.
8
+ - Updated dependencies
9
+ - @secundus-studio/stift-core@0.4.2
10
+
11
+ ## 0.4.1
12
+
13
+ ### Minor Changes
14
+
15
+ - ec4a6c1: Replace the three per-concern persistence options with one persisted-state
16
+ config. `createStift` no longer takes `localePersistence`, `paramsPersistence`,
17
+ and `unitsPersistence`; the active locale, context-param overrides, and unit
18
+ overrides now live in one state store keyed `${prefix}${field}` (default
19
+ `__stift_locale`, `__stift_gender`, `__stift_length`, …) and backed by
20
+ `state.storage` (`'webStorage'` default, `'cookie'`, or `'none'`). Reads are
21
+ synchronous, so persisted values apply before the first render — no flash.
22
+ `state.prefix` and `state.storage` are configurable globally in
23
+ `stift.config.ts`, and per-field `persist: false` opts a param/unit out.
24
+
25
+ Add `createStiftApp()`: resolves a lazy persister factory and returns a
26
+ `boot()` that owns the offline-first `detectLocale → setLocale → initialize`
27
+ loop, so apps stop hand-writing the async singleton.
28
+
29
+ Detection gains a reserved `'persisted'` detector for the stored locale — rank
30
+ it in `detection.order` where the user's saved choice should sit; the
31
+ `detectors.custom` glue is no longer needed. `serializeStiftState`/`hydrate`
32
+ now also carry param/unit overrides so a backend-resolved value renders on the
33
+ first client frame.
34
+
35
+ BREAKING (pre-1.0): `createStift({ localePersistence | paramsPersistence |
36
+ unitsPersistence })` is removed — move to `state`.
37
+ `@secundus-studio/stift-tanstack-router-plugin`'s
38
+ `localStoragePersister`/`cookiePersister` now default to `__stift_locale`
39
+ (previously `stift:locale` / `locale`) so the router and the Stift instance
40
+ share one persisted choice.
41
+
42
+ ### Patch Changes
43
+
44
+ - Updated dependencies [ec4a6c1]
45
+ - @secundus-studio/stift-core@0.4.1
46
+
47
+ ## 0.4.0
48
+
49
+ ### Minor Changes
50
+
51
+ - d60faf6: Split the pack catalog into a slim `catalog.json` index plus one
52
+ content-addressed shard per locale. The index (namespaces, sizes,
53
+ coverage, shard URL + hash) is all that's inlined or fetched for an
54
+ update check; per-namespace URLs and hashes move to
55
+ `catalog/<locale>.<hash>.json`, fetched lazily and never re-downloaded
56
+ for locales that didn't change. Update checks short-circuit at the
57
+ locale level via the shard hash, so a one-key edit no longer invalidates
58
+ every namespace in a locale.
59
+
60
+ BREAKING (pre-1.0): `PackCatalog` is now an object with a `buildId` and a
61
+ `locales[]` array of index rows (not a bare entry array), and the
62
+ per-namespace `namespaces[]` detail lives in the new `LocaleShard`
63
+ type. Code that read `PackCatalogEntry.namespaces`/`.url` directly must
64
+ go through the runtime's lazy shard source or `parseLocaleShard`.
65
+
66
+ ### Patch Changes
67
+
68
+ - Updated dependencies [d60faf6]
69
+ - @secundus-studio/stift-core@0.4.0
70
+
71
+ ## 0.3.0
72
+
73
+ ### Minor Changes
74
+
75
+ - 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.
76
+
77
+ ### Patch Changes
78
+
79
+ - Updated dependencies [71e677d]
80
+ - @secundus-studio/stift-core@0.3.0
81
+
82
+ ## 0.2.0
83
+
84
+ ### Minor Changes
85
+
86
+ - 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`.
87
+ - 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.
88
+
89
+ ### Patch Changes
90
+
91
+ - 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.
92
+ - Updated dependencies [13824b6]
93
+ - Updated dependencies [1770b34]
94
+ - @secundus-studio/l10n-core@0.2.0
95
+
96
+ ## 0.1.0
97
+
98
+ ### Minor Changes
99
+
100
+ - 275184d: Pack truth, catalog v2, boot modes, SSR hydration, TTL, and a new Cache API persister.
101
+
102
+ **Core**
103
+
104
+ - `pack.rehydrateInstalled()` + `initialize()` seeds reactive pack status from
105
+ the persister — returning visits read `installed` from the first notification.
106
+ - Honest lifecycle: per-namespace `__meta` writes (a failed install/refresh
107
+ never corrupts the previous good pack), in-flight healing, `error` (not fake
108
+ `idle`) on partial wipes, `installLocale` failures via `hooks.onError`.
109
+ - Byte-true `bytesReceived`/`bytesTotal` from the catalog; unified "installed"
110
+ definition (`isInstalled`/`getPackSize`/status all key on `__meta`);
111
+ `isNamespaceAvailable` honors cache ∪ persister.
112
+ - `pack.durability` (`'durable' | 'memory' | 'none'`); background full-locale
113
+ installs skip catalog-matching namespaces.
114
+ - `pack.checkAllForUpdates()`: one catalog fetch diffed against every
115
+ persisted locale.
116
+ - `createI18n({ persisterTtlMs })`: expired packs stay usable, report
117
+ `reason: 'ttl-expired'`, and refresh in the background on `activate`.
118
+ - `serializeI18nState()` + `createI18n({ hydrate })`: SSR hydration without an
119
+ SSR boot mode. `scope()` single-tuple overload.
120
+ - Deprecated `interpolate`/`plainFormatterEngine` (ICU MF1 supersedes them).
121
+
122
+ **Catalog (generator + vite-plugin)**
123
+
124
+ - Content-hashed pack URLs (`<ns>.<hash>.dat`), identical in dev and prod.
125
+ - Conditional catalog ping (ETag/`If-None-Match`, `no-cache`, no cache-buster).
126
+ - Catalog entries gain `buildId`, `publishedAt`, `coverage` (authored fraction
127
+ from completion, key-presence fallback for direct `compileAll` use).
128
+
129
+ **React**
130
+
131
+ - `I18nProvider onBootError` (rejected `boot` reports instead of swallowing).
132
+ - Missing translator type exports (`MultiScopedTranslationResult`,
133
+ `BoundMultiT`, multi/rich/markdown variants); suspense single-flight keyed
134
+ per i18n instance.
135
+
136
+ **Devtools**: snapshot exposes `packDurability`; Pack state hides
137
+ install/delete affordances unless durable; memory-cache buckets relabeled.
138
+
139
+ **OPFS**: write queue self-heals after a transient failure.
140
+
141
+ **New package** `@secundus-studio/l10n-persisters`: OPFS, IndexedDB (idb-keyval), and
142
+ Cache Storage persisters (`/opfs`, `/idb-keyval`, `/cache-api` subpaths) plus
143
+ a `createBestEffortPersister()` fallback chain.
144
+
145
+ ### Patch Changes
146
+
147
+ - Updated dependencies [275184d]
148
+ - Updated dependencies [275184d]
149
+ - @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;