@saykit/config 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Configuration schema and CLI for [SayKit](https://saykit.js.org).
4
4
 
5
+ [![Coverage](https://codecov.io/gh/k0d13/saykit/graph/badge.svg?flag=config)](https://codecov.io/gh/k0d13/saykit?flags%5B0%5D=config)
6
+
5
7
  Provides the `defineConfig` helper, the Zod-validated schema for `saykit.config.ts`, and the `saykit` CLI used to extract messages from your source.
6
8
 
7
9
  ## Install
@@ -19,6 +21,12 @@ import js from '@saykit/transform-js';
19
21
 
20
22
  export default defineConfig({
21
23
  locales: ['en', 'fr'],
24
+ // Optional per-locale fallback chains, most specific first. The source locale
25
+ // (the first entry in `locales`) is always the final fallback.
26
+ fallbackLocales: {
27
+ 'en-NZ': ['en-GB'],
28
+ 'es-MX': 'es',
29
+ },
22
30
  buckets: [
23
31
  {
24
32
  include: ['src/**/*.ts'],
@@ -35,8 +43,22 @@ export default defineConfig({
35
43
  ```sh
36
44
  saykit extract # extract messages once
37
45
  saykit extract --watch # extract and watch for changes
46
+ saykit clean # reconcile other locales against the source
38
47
  ```
39
48
 
49
+ Extraction only writes the **source** locale (the first entry in `locales`). Locales that don't
50
+ have a file yet are bootstrapped with an empty, header-only catalogue so a TMS can register them;
51
+ existing translation files are left untouched. Translated content is owned by your TMS.
52
+
53
+ At load time (via the unplugin or Babel plugin), each locale module is filled from its fallback
54
+ chain, so an untranslated key resolves to a fallback locale and ultimately the source string — the
55
+ runtime still loads a single locale.
56
+
57
+ `saykit clean` reconciles every non-source locale against the current source catalogue: it adds
58
+ missing keys (untranslated), drops entries that no longer exist in the source, and preserves any
59
+ existing translations. Use it when you want to propagate source changes into locale files yourself
60
+ rather than leaving it to your TMS.
61
+
40
62
  ## Documentation
41
63
 
42
64
  Full configuration guide and CLI reference at [saykit.js.org](https://saykit.js.org).
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  require("../index.cjs");
3
- const require_loader = require("../loader-DnWt_9U2.cjs");
4
- const require_hash = require("../hash-CDfMT76E.cjs");
3
+ const require_storage = require("../storage-3BTY8yws.cjs");
4
+ const require_loader = require("../loader-B25O-vF6.cjs");
5
5
  let _commander_js_extra_typings = require("@commander-js/extra-typings");
6
+ let node_fs_promises = require("node:fs/promises");
6
7
  let node_path = require("node:path");
7
8
  let node_async_hooks = require("node:async_hooks");
8
- let node_fs_promises = require("node:fs/promises");
9
9
  //#region src/features/logger.ts
10
10
  const RESET = "\x1B[0m";
11
11
  const DIM = "\x1B[2m";
@@ -45,6 +45,23 @@ var Logger = class {
45
45
  };
46
46
  new node_async_hooks.AsyncLocalStorage({ defaultValue: new Logger() });
47
47
  //#endregion
48
+ //#region src/commands/clean.ts
49
+ var clean_default = new _commander_js_extra_typings.Command("clean").description("Reconcile non-source locale files against the source locale").option("-v, --verbose", "enable verbose logging", false).option("-q, --quiet", "suppress all logging", false).action(async (options) => {
50
+ const config = require_loader.resolveConfig();
51
+ const logger = new Logger(options);
52
+ logger.header("🧹 Cleaning Locales");
53
+ const [sourceLocale, ...otherLocales] = config.locales;
54
+ for (const bucket of config.buckets) {
55
+ const sourceMessages = await require_storage.readCatalogueMessages(bucket, sourceLocale);
56
+ logger.info(`Reconciling ${otherLocales.length} locale(s) against ${sourceLocale}`);
57
+ for (const locale of otherLocales) {
58
+ logger.step(`Reconciling ${locale}`);
59
+ await require_storage.writeCatalogueMessages(bucket, locale, require_storage.reconcileLocaleMessages(await require_storage.readCatalogueMessages(bucket, locale), sourceMessages));
60
+ }
61
+ }
62
+ logger.success("Locales cleaned");
63
+ });
64
+ //#endregion
48
65
  //#region src/features/catalogue/extractor.ts
49
66
  async function extractMessagesFromFile(path, bucket) {
50
67
  const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
@@ -56,86 +73,6 @@ async function extractMessagesFromFile(path, bucket) {
56
73
  }));
57
74
  }
58
75
  //#endregion
59
- //#region src/features/catalogue/merge.ts
60
- function mergeUnique(...items) {
61
- return Array.from(new Set(items.flat()));
62
- }
63
- function getMessageKey(message) {
64
- return message.id ?? require_hash.generateHash(message.message, message.context);
65
- }
66
- function mergeExtractedMessages(messages) {
67
- const mergedMessages = messages.reduce((map, message) => {
68
- const key = getMessageKey(message);
69
- const existing = map.get(key) ?? message;
70
- map.set(key, {
71
- ...existing,
72
- comments: mergeUnique(...existing.comments, ...message.comments),
73
- references: mergeUnique(...existing.references, ...message.references)
74
- });
75
- return map;
76
- }, /* @__PURE__ */ new Map());
77
- return Array.from(mergedMessages.values());
78
- }
79
- function reconcileLocaleMessages(existingMessages, nextMessages) {
80
- const existingMessagesByKey = existingMessages.reduce((map, message) => {
81
- map.set(getMessageKey(message), message);
82
- return map;
83
- }, /* @__PURE__ */ new Map());
84
- const reconciledMessages = nextMessages.reduce((map, message) => {
85
- const key = getMessageKey(message);
86
- const existingMessage = existingMessagesByKey.get(key);
87
- map.set(key, {
88
- message: message.message,
89
- translation: void 0,
90
- ...existingMessage,
91
- id: message.id,
92
- context: message.context,
93
- comments: message.comments,
94
- references: message.references
95
- });
96
- return map;
97
- }, /* @__PURE__ */ new Map());
98
- return Array.from(reconciledMessages.values());
99
- }
100
- //#endregion
101
- //#region src/features/catalogue/path.ts
102
- function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
103
- return (0, node_path.resolve)(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
104
- }
105
- function expandBucketOutputIgnoreDirectory(bucket) {
106
- const [prefix] = bucket.output.split("{locale}");
107
- return (0, node_path.resolve)(prefix || ".");
108
- }
109
- //#endregion
110
- //#region src/features/catalogue/storage.ts
111
- const DECLARATION_CONTENT = `
112
- declare const translations: Record<string, string>;
113
- export default translations;
114
- `.trim();
115
- async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
116
- const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
117
- if (!content) return [];
118
- return bucket.formatter.parse(content);
119
- }
120
- async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
121
- const existingContent = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => void 0);
122
- const catalogueContent = bucket.formatter.stringify(messages, {
123
- locale,
124
- existingContent
125
- });
126
- const declarationPath = `${path}.d.ts`;
127
- const ignoreDirectory = expandBucketOutputIgnoreDirectory(bucket);
128
- const ignorePath = (0, node_path.join)(ignoreDirectory, ".gitignore");
129
- const ignoreContent = `.gitignore\n*.${bucket.formatter.extension.slice(1)}.d.ts`;
130
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
131
- await (0, node_fs_promises.mkdir)(ignoreDirectory, { recursive: true });
132
- await Promise.all([
133
- (0, node_fs_promises.writeFile)(path, catalogueContent),
134
- (0, node_fs_promises.writeFile)(declarationPath, DECLARATION_CONTENT),
135
- (0, node_fs_promises.writeFile)(ignorePath, ignoreContent)
136
- ]);
137
- }
138
- //#endregion
139
76
  //#region src/features/watch.ts
140
77
  /**
141
78
  * Expand a buckets include and exclude patterns into a flat list of file paths.
@@ -195,6 +132,9 @@ var BucketWorker = class {
195
132
  };
196
133
  //#endregion
197
134
  //#region src/features/workers/extract-worker.ts
135
+ function exists(path) {
136
+ return (0, node_fs_promises.access)(path).then(() => true, () => false);
137
+ }
198
138
  var BucketExtractWorker = class extends BucketWorker {
199
139
  #indexedMessagesByPath = /* @__PURE__ */ new Map();
200
140
  get #indexedMessages() {
@@ -224,13 +164,18 @@ var BucketExtractWorker = class extends BucketWorker {
224
164
  this.logger.info(`Total extracted messages: ${this.#indexedMessages.length}`);
225
165
  }
226
166
  async write() {
227
- const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
228
- this.logger.info(`Writing ${mergedMessages.length} messages to locales`);
229
- for (const locale of this.config.locales) {
230
- this.logger.step(`Writing locale file for ${locale} to disk`);
231
- const existingMessages = await readCatalogueMessages(this.bucket, locale);
232
- const nextMessages = locale === this.config.locales[0] ? mergedMessages : reconcileLocaleMessages(existingMessages, mergedMessages);
233
- await writeCatalogueMessages(this.bucket, locale, nextMessages);
167
+ const mergedMessages = require_storage.mergeExtractedMessages(this.#indexedMessages);
168
+ const [sourceLocale, ...otherLocales] = this.config.locales;
169
+ this.logger.info(`Writing ${mergedMessages.length} messages to ${sourceLocale}`);
170
+ this.logger.step(`Writing locale file for ${sourceLocale} to disk`);
171
+ await require_storage.writeCatalogueMessages(this.bucket, sourceLocale, mergedMessages);
172
+ for (const locale of otherLocales) {
173
+ if (await exists(require_storage.expandBucketOutputPath(this.bucket, locale))) {
174
+ this.logger.step(`Skipping existing locale file for ${locale}`);
175
+ continue;
176
+ }
177
+ this.logger.step(`Creating empty locale file for ${locale}`);
178
+ await require_storage.writeCatalogueMessages(this.bucket, locale, []);
234
179
  }
235
180
  this.logger.success(`Extraction complete for bucket: ${this.bucket.include}`);
236
181
  }
@@ -260,5 +205,5 @@ var extract_default = new _commander_js_extra_typings.Command("extract").descrip
260
205
  });
261
206
  //#endregion
262
207
  //#region src/commands/index.ts
263
- _commander_js_extra_typings.program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).parse();
208
+ _commander_js_extra_typings.program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).addCommand(clean_default).parse();
264
209
  //#endregion
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { t as resolveConfig } from "../loader-CGuahnrc.mjs";
3
- import { t as generateHash } from "../hash-Cs0dRdGf.mjs";
2
+ import { a as reconcileLocaleMessages, i as mergeExtractedMessages, n as writeCatalogueMessages, r as expandBucketOutputPath, t as readCatalogueMessages } from "../storage-C9nzxPtZ.mjs";
3
+ import { t as resolveConfig } from "../loader-C2eolE_1.mjs";
4
4
  import { Command, program } from "@commander-js/extra-typings";
5
- import { dirname, join, relative, resolve } from "node:path";
5
+ import { access, glob, readFile, watch } from "node:fs/promises";
6
+ import { join, relative } from "node:path";
6
7
  import { AsyncLocalStorage } from "node:async_hooks";
7
- import { glob, mkdir, readFile, watch, writeFile } from "node:fs/promises";
8
8
  //#region src/features/logger.ts
9
9
  const RESET = "\x1B[0m";
10
10
  const DIM = "\x1B[2m";
@@ -44,6 +44,23 @@ var Logger = class {
44
44
  };
45
45
  new AsyncLocalStorage({ defaultValue: new Logger() });
46
46
  //#endregion
47
+ //#region src/commands/clean.ts
48
+ var clean_default = new Command("clean").description("Reconcile non-source locale files against the source locale").option("-v, --verbose", "enable verbose logging", false).option("-q, --quiet", "suppress all logging", false).action(async (options) => {
49
+ const config = resolveConfig();
50
+ const logger = new Logger(options);
51
+ logger.header("🧹 Cleaning Locales");
52
+ const [sourceLocale, ...otherLocales] = config.locales;
53
+ for (const bucket of config.buckets) {
54
+ const sourceMessages = await readCatalogueMessages(bucket, sourceLocale);
55
+ logger.info(`Reconciling ${otherLocales.length} locale(s) against ${sourceLocale}`);
56
+ for (const locale of otherLocales) {
57
+ logger.step(`Reconciling ${locale}`);
58
+ await writeCatalogueMessages(bucket, locale, reconcileLocaleMessages(await readCatalogueMessages(bucket, locale), sourceMessages));
59
+ }
60
+ }
61
+ logger.success("Locales cleaned");
62
+ });
63
+ //#endregion
47
64
  //#region src/features/catalogue/extractor.ts
48
65
  async function extractMessagesFromFile(path, bucket) {
49
66
  const content = await readFile(path, "utf8").catch(() => "");
@@ -55,86 +72,6 @@ async function extractMessagesFromFile(path, bucket) {
55
72
  }));
56
73
  }
57
74
  //#endregion
58
- //#region src/features/catalogue/merge.ts
59
- function mergeUnique(...items) {
60
- return Array.from(new Set(items.flat()));
61
- }
62
- function getMessageKey(message) {
63
- return message.id ?? generateHash(message.message, message.context);
64
- }
65
- function mergeExtractedMessages(messages) {
66
- const mergedMessages = messages.reduce((map, message) => {
67
- const key = getMessageKey(message);
68
- const existing = map.get(key) ?? message;
69
- map.set(key, {
70
- ...existing,
71
- comments: mergeUnique(...existing.comments, ...message.comments),
72
- references: mergeUnique(...existing.references, ...message.references)
73
- });
74
- return map;
75
- }, /* @__PURE__ */ new Map());
76
- return Array.from(mergedMessages.values());
77
- }
78
- function reconcileLocaleMessages(existingMessages, nextMessages) {
79
- const existingMessagesByKey = existingMessages.reduce((map, message) => {
80
- map.set(getMessageKey(message), message);
81
- return map;
82
- }, /* @__PURE__ */ new Map());
83
- const reconciledMessages = nextMessages.reduce((map, message) => {
84
- const key = getMessageKey(message);
85
- const existingMessage = existingMessagesByKey.get(key);
86
- map.set(key, {
87
- message: message.message,
88
- translation: void 0,
89
- ...existingMessage,
90
- id: message.id,
91
- context: message.context,
92
- comments: message.comments,
93
- references: message.references
94
- });
95
- return map;
96
- }, /* @__PURE__ */ new Map());
97
- return Array.from(reconciledMessages.values());
98
- }
99
- //#endregion
100
- //#region src/features/catalogue/path.ts
101
- function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
102
- return resolve(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
103
- }
104
- function expandBucketOutputIgnoreDirectory(bucket) {
105
- const [prefix] = bucket.output.split("{locale}");
106
- return resolve(prefix || ".");
107
- }
108
- //#endregion
109
- //#region src/features/catalogue/storage.ts
110
- const DECLARATION_CONTENT = `
111
- declare const translations: Record<string, string>;
112
- export default translations;
113
- `.trim();
114
- async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
115
- const content = await readFile(path, "utf8").catch(() => "");
116
- if (!content) return [];
117
- return bucket.formatter.parse(content);
118
- }
119
- async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
120
- const existingContent = await readFile(path, "utf8").catch(() => void 0);
121
- const catalogueContent = bucket.formatter.stringify(messages, {
122
- locale,
123
- existingContent
124
- });
125
- const declarationPath = `${path}.d.ts`;
126
- const ignoreDirectory = expandBucketOutputIgnoreDirectory(bucket);
127
- const ignorePath = join(ignoreDirectory, ".gitignore");
128
- const ignoreContent = `.gitignore\n*.${bucket.formatter.extension.slice(1)}.d.ts`;
129
- await mkdir(dirname(path), { recursive: true });
130
- await mkdir(ignoreDirectory, { recursive: true });
131
- await Promise.all([
132
- writeFile(path, catalogueContent),
133
- writeFile(declarationPath, DECLARATION_CONTENT),
134
- writeFile(ignorePath, ignoreContent)
135
- ]);
136
- }
137
- //#endregion
138
75
  //#region src/features/watch.ts
139
76
  /**
140
77
  * Expand a buckets include and exclude patterns into a flat list of file paths.
@@ -194,6 +131,9 @@ var BucketWorker = class {
194
131
  };
195
132
  //#endregion
196
133
  //#region src/features/workers/extract-worker.ts
134
+ function exists(path) {
135
+ return access(path).then(() => true, () => false);
136
+ }
197
137
  var BucketExtractWorker = class extends BucketWorker {
198
138
  #indexedMessagesByPath = /* @__PURE__ */ new Map();
199
139
  get #indexedMessages() {
@@ -224,12 +164,17 @@ var BucketExtractWorker = class extends BucketWorker {
224
164
  }
225
165
  async write() {
226
166
  const mergedMessages = mergeExtractedMessages(this.#indexedMessages);
227
- this.logger.info(`Writing ${mergedMessages.length} messages to locales`);
228
- for (const locale of this.config.locales) {
229
- this.logger.step(`Writing locale file for ${locale} to disk`);
230
- const existingMessages = await readCatalogueMessages(this.bucket, locale);
231
- const nextMessages = locale === this.config.locales[0] ? mergedMessages : reconcileLocaleMessages(existingMessages, mergedMessages);
232
- await writeCatalogueMessages(this.bucket, locale, nextMessages);
167
+ const [sourceLocale, ...otherLocales] = this.config.locales;
168
+ this.logger.info(`Writing ${mergedMessages.length} messages to ${sourceLocale}`);
169
+ this.logger.step(`Writing locale file for ${sourceLocale} to disk`);
170
+ await writeCatalogueMessages(this.bucket, sourceLocale, mergedMessages);
171
+ for (const locale of otherLocales) {
172
+ if (await exists(expandBucketOutputPath(this.bucket, locale))) {
173
+ this.logger.step(`Skipping existing locale file for ${locale}`);
174
+ continue;
175
+ }
176
+ this.logger.step(`Creating empty locale file for ${locale}`);
177
+ await writeCatalogueMessages(this.bucket, locale, []);
233
178
  }
234
179
  this.logger.success(`Extraction complete for bucket: ${this.bucket.include}`);
235
180
  }
@@ -259,6 +204,6 @@ var extract_default = new Command("extract").description("Extract messages from
259
204
  });
260
205
  //#endregion
261
206
  //#region src/commands/index.ts
262
- program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).parse();
207
+ program.name("saykit").helpOption("-h, --help", "Display help for command").helpCommand("help [command]", "Display help for command").addCommand(extract_default).addCommand(clean_default).parse();
263
208
  //#endregion
264
209
  export {};
@@ -0,0 +1,60 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../../index.cjs");
3
+ const require_hash = require("../../hash-8It7TJpB.cjs");
4
+ const require_storage = require("../../storage-3BTY8yws.cjs");
5
+ let node_path = require("node:path");
6
+ //#region src/features/catalogue/record.ts
7
+ /**
8
+ * Resolve the fallback chain for a locale, most specific first. The source
9
+ * locale (the first configured locale) is always the final fallback, so an
10
+ * untranslated key ultimately resolves to the source string.
11
+ */
12
+ function resolveFallbackChain(config, locale) {
13
+ const source = config.locales[0];
14
+ const configured = config.fallbackLocales?.[locale];
15
+ const fallbacks = configured ? Array.isArray(configured) ? configured : [configured] : [];
16
+ return Array.from(new Set([
17
+ locale,
18
+ ...fallbacks,
19
+ source
20
+ ]));
21
+ }
22
+ /**
23
+ * Resolve the catalogue files that contribute to a locale module, most specific
24
+ * first. When the path does not map to a configured locale it is loaded on its
25
+ * own.
26
+ */
27
+ function resolveCatalogueSources(config, bucket, path) {
28
+ const resolved = (0, node_path.resolve)(path);
29
+ const locale = config.locales.find((l) => require_storage.expandBucketOutputPath(bucket, l) === resolved);
30
+ return {
31
+ locale,
32
+ sources: locale ? resolveFallbackChain(config, locale).map((l) => require_storage.expandBucketOutputPath(bucket, l)) : [resolved]
33
+ };
34
+ }
35
+ /**
36
+ * Assemble a `{ id: string }` record from catalogue file contents. `contents`
37
+ * must be aligned with the `sources` returned by {@link resolveCatalogueSources}
38
+ * (most specific first): more specific locales override their fallbacks, and any
39
+ * key still untranslated falls back to its source message.
40
+ */
41
+ function assembleCatalogueRecord(bucket, contents) {
42
+ const record = {};
43
+ for (const content of [...contents].reverse()) {
44
+ if (!content) continue;
45
+ for (const message of bucket.formatter.parse(content)) {
46
+ const key = message.id || require_hash.generateHash(message.message, message.context);
47
+ record[key] = message.translation || message.message;
48
+ }
49
+ }
50
+ return record;
51
+ }
52
+ //#endregion
53
+ exports.assembleCatalogueRecord = assembleCatalogueRecord;
54
+ exports.expandBucketOutputPath = require_storage.expandBucketOutputPath;
55
+ exports.mergeExtractedMessages = require_storage.mergeExtractedMessages;
56
+ exports.readCatalogueMessages = require_storage.readCatalogueMessages;
57
+ exports.reconcileLocaleMessages = require_storage.reconcileLocaleMessages;
58
+ exports.resolveCatalogueSources = resolveCatalogueSources;
59
+ exports.resolveFallbackChain = resolveFallbackChain;
60
+ exports.writeCatalogueMessages = require_storage.writeCatalogueMessages;
@@ -0,0 +1,59 @@
1
+ import { i as Message, n as Config, t as Bucket } from "../../shapes-Ba3xS10n.cjs";
2
+
3
+ //#region src/features/catalogue/merge.d.ts
4
+ declare function mergeExtractedMessages(messages: Message[]): {
5
+ message: string;
6
+ comments: string[];
7
+ references: string[];
8
+ translation?: string | undefined;
9
+ id?: string | undefined;
10
+ context?: string | undefined;
11
+ }[];
12
+ declare function reconcileLocaleMessages(existingMessages: Message[], nextMessages: Message[]): {
13
+ message: string;
14
+ comments: string[];
15
+ references: string[];
16
+ translation?: string | undefined;
17
+ id?: string | undefined;
18
+ context?: string | undefined;
19
+ }[];
20
+ //#endregion
21
+ //#region src/features/catalogue/path.d.ts
22
+ declare function expandBucketOutputPath(bucket: Bucket, locale: string, extension?: `.${string}`): string;
23
+ //#endregion
24
+ //#region src/features/catalogue/record.d.ts
25
+ /**
26
+ * Resolve the fallback chain for a locale, most specific first. The source
27
+ * locale (the first configured locale) is always the final fallback, so an
28
+ * untranslated key ultimately resolves to the source string.
29
+ */
30
+ declare function resolveFallbackChain(config: Config, locale: string): string[];
31
+ /**
32
+ * Resolve the catalogue files that contribute to a locale module, most specific
33
+ * first. When the path does not map to a configured locale it is loaded on its
34
+ * own.
35
+ */
36
+ declare function resolveCatalogueSources(config: Config, bucket: Bucket, path: string): {
37
+ locale: string | undefined;
38
+ sources: string[];
39
+ };
40
+ /**
41
+ * Assemble a `{ id: string }` record from catalogue file contents. `contents`
42
+ * must be aligned with the `sources` returned by {@link resolveCatalogueSources}
43
+ * (most specific first): more specific locales override their fallbacks, and any
44
+ * key still untranslated falls back to its source message.
45
+ */
46
+ declare function assembleCatalogueRecord(bucket: Bucket, contents: string[]): Record<string, string>;
47
+ //#endregion
48
+ //#region src/features/catalogue/storage.d.ts
49
+ declare function readCatalogueMessages(bucket: Bucket, locale: string, path?: string): Promise<{
50
+ message: string;
51
+ comments: string[];
52
+ references: string[];
53
+ translation?: string | undefined;
54
+ id?: string | undefined;
55
+ context?: string | undefined;
56
+ }[]>;
57
+ declare function writeCatalogueMessages(bucket: Bucket, locale: string, messages: Message[], path?: string): Promise<void>;
58
+ //#endregion
59
+ export { assembleCatalogueRecord, expandBucketOutputPath, mergeExtractedMessages, readCatalogueMessages, reconcileLocaleMessages, resolveCatalogueSources, resolveFallbackChain, writeCatalogueMessages };
@@ -0,0 +1,59 @@
1
+ import { i as Message, n as Config, t as Bucket } from "../../shapes-NPfwlInG.mjs";
2
+
3
+ //#region src/features/catalogue/merge.d.ts
4
+ declare function mergeExtractedMessages(messages: Message[]): {
5
+ message: string;
6
+ comments: string[];
7
+ references: string[];
8
+ translation?: string | undefined;
9
+ id?: string | undefined;
10
+ context?: string | undefined;
11
+ }[];
12
+ declare function reconcileLocaleMessages(existingMessages: Message[], nextMessages: Message[]): {
13
+ message: string;
14
+ comments: string[];
15
+ references: string[];
16
+ translation?: string | undefined;
17
+ id?: string | undefined;
18
+ context?: string | undefined;
19
+ }[];
20
+ //#endregion
21
+ //#region src/features/catalogue/path.d.ts
22
+ declare function expandBucketOutputPath(bucket: Bucket, locale: string, extension?: `.${string}`): string;
23
+ //#endregion
24
+ //#region src/features/catalogue/record.d.ts
25
+ /**
26
+ * Resolve the fallback chain for a locale, most specific first. The source
27
+ * locale (the first configured locale) is always the final fallback, so an
28
+ * untranslated key ultimately resolves to the source string.
29
+ */
30
+ declare function resolveFallbackChain(config: Config, locale: string): string[];
31
+ /**
32
+ * Resolve the catalogue files that contribute to a locale module, most specific
33
+ * first. When the path does not map to a configured locale it is loaded on its
34
+ * own.
35
+ */
36
+ declare function resolveCatalogueSources(config: Config, bucket: Bucket, path: string): {
37
+ locale: string | undefined;
38
+ sources: string[];
39
+ };
40
+ /**
41
+ * Assemble a `{ id: string }` record from catalogue file contents. `contents`
42
+ * must be aligned with the `sources` returned by {@link resolveCatalogueSources}
43
+ * (most specific first): more specific locales override their fallbacks, and any
44
+ * key still untranslated falls back to its source message.
45
+ */
46
+ declare function assembleCatalogueRecord(bucket: Bucket, contents: string[]): Record<string, string>;
47
+ //#endregion
48
+ //#region src/features/catalogue/storage.d.ts
49
+ declare function readCatalogueMessages(bucket: Bucket, locale: string, path?: string): Promise<{
50
+ message: string;
51
+ comments: string[];
52
+ references: string[];
53
+ translation?: string | undefined;
54
+ id?: string | undefined;
55
+ context?: string | undefined;
56
+ }[]>;
57
+ declare function writeCatalogueMessages(bucket: Bucket, locale: string, messages: Message[], path?: string): Promise<void>;
58
+ //#endregion
59
+ export { assembleCatalogueRecord, expandBucketOutputPath, mergeExtractedMessages, readCatalogueMessages, reconcileLocaleMessages, resolveCatalogueSources, resolveFallbackChain, writeCatalogueMessages };
@@ -0,0 +1,51 @@
1
+ import { t as generateHash } from "../../hash-DyC8GzMa.mjs";
2
+ import { a as reconcileLocaleMessages, i as mergeExtractedMessages, n as writeCatalogueMessages, r as expandBucketOutputPath, t as readCatalogueMessages } from "../../storage-C9nzxPtZ.mjs";
3
+ import { resolve } from "node:path";
4
+ //#region src/features/catalogue/record.ts
5
+ /**
6
+ * Resolve the fallback chain for a locale, most specific first. The source
7
+ * locale (the first configured locale) is always the final fallback, so an
8
+ * untranslated key ultimately resolves to the source string.
9
+ */
10
+ function resolveFallbackChain(config, locale) {
11
+ const source = config.locales[0];
12
+ const configured = config.fallbackLocales?.[locale];
13
+ const fallbacks = configured ? Array.isArray(configured) ? configured : [configured] : [];
14
+ return Array.from(new Set([
15
+ locale,
16
+ ...fallbacks,
17
+ source
18
+ ]));
19
+ }
20
+ /**
21
+ * Resolve the catalogue files that contribute to a locale module, most specific
22
+ * first. When the path does not map to a configured locale it is loaded on its
23
+ * own.
24
+ */
25
+ function resolveCatalogueSources(config, bucket, path) {
26
+ const resolved = resolve(path);
27
+ const locale = config.locales.find((l) => expandBucketOutputPath(bucket, l) === resolved);
28
+ return {
29
+ locale,
30
+ sources: locale ? resolveFallbackChain(config, locale).map((l) => expandBucketOutputPath(bucket, l)) : [resolved]
31
+ };
32
+ }
33
+ /**
34
+ * Assemble a `{ id: string }` record from catalogue file contents. `contents`
35
+ * must be aligned with the `sources` returned by {@link resolveCatalogueSources}
36
+ * (most specific first): more specific locales override their fallbacks, and any
37
+ * key still untranslated falls back to its source message.
38
+ */
39
+ function assembleCatalogueRecord(bucket, contents) {
40
+ const record = {};
41
+ for (const content of [...contents].reverse()) {
42
+ if (!content) continue;
43
+ for (const message of bucket.formatter.parse(content)) {
44
+ const key = message.id || generateHash(message.message, message.context);
45
+ record[key] = message.translation || message.message;
46
+ }
47
+ }
48
+ return record;
49
+ }
50
+ //#endregion
51
+ export { assembleCatalogueRecord, expandBucketOutputPath, mergeExtractedMessages, readCatalogueMessages, reconcileLocaleMessages, resolveCatalogueSources, resolveFallbackChain, writeCatalogueMessages };
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_loader = require("../../loader-DnWt_9U2.cjs");
2
+ const require_loader = require("../../loader-B25O-vF6.cjs");
3
3
  exports.resolveConfig = require_loader.resolveConfig;
@@ -1,4 +1,4 @@
1
- import { n as Config } from "../../shapes-CJyTfZXd.cjs";
1
+ import { n as Config } from "../../shapes-Ba3xS10n.cjs";
2
2
 
3
3
  //#region src/features/loader/resolve.d.ts
4
4
  declare function resolveConfig(name?: string): Config;
@@ -1,4 +1,4 @@
1
- import { n as Config } from "../../shapes-DETrtvZf.mjs";
1
+ import { n as Config } from "../../shapes-NPfwlInG.mjs";
2
2
 
3
3
  //#region src/features/loader/resolve.d.ts
4
4
  declare function resolveConfig(name?: string): Config;
@@ -1,2 +1,2 @@
1
- import { t as resolveConfig } from "../../loader-CGuahnrc.mjs";
1
+ import { t as resolveConfig } from "../../loader-C2eolE_1.mjs";
2
2
  export { resolveConfig };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_hash = require("../../hash-CDfMT76E.cjs");
2
+ const require_hash = require("../../hash-8It7TJpB.cjs");
3
3
  //#region src/features/messages/identifier.ts
4
4
  const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
5
5
  function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
@@ -54,13 +54,14 @@ var ChoiceMessage = class extends Base {
54
54
  }
55
55
  };
56
56
  var CompositeMessage = class extends Base {
57
- constructor(descriptor, comments, references, children, accessor) {
57
+ constructor(descriptor, comments, references, children, accessor, whitespace) {
58
58
  super();
59
59
  this.descriptor = descriptor;
60
60
  this.comments = comments;
61
61
  this.references = references;
62
62
  this.children = children;
63
63
  this.accessor = accessor;
64
+ this.whitespace = whitespace;
64
65
  }
65
66
  };
66
67
  //#endregion
@@ -46,10 +46,11 @@ declare class CompositeMessage extends Base {
46
46
  readonly references: string[];
47
47
  readonly children: Message[];
48
48
  readonly accessor: any;
49
+ readonly whitespace?: boolean | undefined;
49
50
  constructor(descriptor: {
50
51
  id?: string;
51
52
  context?: string;
52
- }, comments: string[], references: string[], children: Message[], accessor: any);
53
+ }, comments: string[], references: string[], children: Message[], accessor: any, whitespace?: boolean | undefined);
53
54
  }
54
55
  type Message = LiteralMessage | ArgumentMessage | ElementMessage | ChoiceMessage | CompositeMessage;
55
56
  //#endregion
@@ -46,10 +46,11 @@ declare class CompositeMessage extends Base {
46
46
  readonly references: string[];
47
47
  readonly children: Message[];
48
48
  readonly accessor: any;
49
+ readonly whitespace?: boolean | undefined;
49
50
  constructor(descriptor: {
50
51
  id?: string;
51
52
  context?: string;
52
- }, comments: string[], references: string[], children: Message[], accessor: any);
53
+ }, comments: string[], references: string[], children: Message[], accessor: any, whitespace?: boolean | undefined);
53
54
  }
54
55
  type Message = LiteralMessage | ArgumentMessage | ElementMessage | ChoiceMessage | CompositeMessage;
55
56
  //#endregion
@@ -1,4 +1,4 @@
1
- import { t as generateHash } from "../../hash-Cs0dRdGf.mjs";
1
+ import { t as generateHash } from "../../hash-DyC8GzMa.mjs";
2
2
  //#region src/features/messages/identifier.ts
3
3
  const AUTO_INCREMENT_IDENTIFIER = Symbol("auto-increment");
4
4
  function assignSequenceIdentifiers(message, sequence = { current: 0 }) {
@@ -53,13 +53,14 @@ var ChoiceMessage = class extends Base {
53
53
  }
54
54
  };
55
55
  var CompositeMessage = class extends Base {
56
- constructor(descriptor, comments, references, children, accessor) {
56
+ constructor(descriptor, comments, references, children, accessor, whitespace) {
57
57
  super();
58
58
  this.descriptor = descriptor;
59
59
  this.comments = comments;
60
60
  this.references = references;
61
61
  this.children = children;
62
62
  this.accessor = accessor;
63
+ this.whitespace = whitespace;
63
64
  }
64
65
  };
65
66
  //#endregion
package/dist/index.cjs CHANGED
@@ -65,6 +65,13 @@ const Bucket = zod.object({
65
65
  }));
66
66
  const Config = zod.object({
67
67
  locales: zod.tuple([zod.string()], zod.string()),
68
+ /**
69
+ * Per-locale fallback chains, most specific first, e.g.
70
+ * `{ 'en-NZ': ['en-GB'], 'es-MX': 'es' }`. The source locale (the first entry
71
+ * in {@link Config.locales}) is always appended as the final fallback, so an
72
+ * untranslated key ultimately resolves to the source string.
73
+ */
74
+ fallbackLocales: zod.record(zod.string(), zod.string().or(zod.string().array())).optional(),
68
75
  buckets: Bucket.array()
69
76
  });
70
77
  //#endregion
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-CJyTfZXd.cjs";
1
+ import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-Ba3xS10n.cjs";
2
2
  import { input } from "zod";
3
3
  import * as _$picomatch_lib_picomatch_js0 from "picomatch/lib/picomatch.js";
4
4
 
@@ -33,6 +33,7 @@ declare function defineConfig<C extends input<typeof Config>>(config: C): {
33
33
  };
34
34
  exclude?: string[] | undefined;
35
35
  }[];
36
+ fallbackLocales?: Record<string, string | string[]> | undefined;
36
37
  };
37
38
  //#endregion
38
39
  export { Bucket, Config, Formatter, Message, Transformer, defineConfig };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-DETrtvZf.mjs";
1
+ import { a as Transformer, i as Message, n as Config, r as Formatter, t as Bucket } from "./shapes-NPfwlInG.mjs";
2
2
  import { input } from "zod";
3
3
  import * as _$picomatch_lib_picomatch_js0 from "picomatch/lib/picomatch.js";
4
4
 
@@ -33,6 +33,7 @@ declare function defineConfig<C extends input<typeof Config>>(config: C): {
33
33
  };
34
34
  exclude?: string[] | undefined;
35
35
  }[];
36
+ fallbackLocales?: Record<string, string | string[]> | undefined;
36
37
  };
37
38
  //#endregion
38
39
  export { Bucket, Config, Formatter, Message, Transformer, defineConfig };
package/dist/index.mjs CHANGED
@@ -40,6 +40,13 @@ const Bucket = z.object({
40
40
  }));
41
41
  const Config = z.object({
42
42
  locales: z.tuple([z.string()], z.string()),
43
+ /**
44
+ * Per-locale fallback chains, most specific first, e.g.
45
+ * `{ 'en-NZ': ['en-GB'], 'es-MX': 'es' }`. The source locale (the first entry
46
+ * in {@link Config.locales}) is always appended as the final fallback, so an
47
+ * untranslated key ultimately resolves to the source string.
48
+ */
49
+ fallbackLocales: z.record(z.string(), z.string().or(z.string().array())).optional(),
43
50
  buckets: Bucket.array()
44
51
  });
45
52
  //#endregion
@@ -131,6 +131,7 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
131
131
  type Bucket = z.infer<typeof Bucket>;
132
132
  declare const Config: z.ZodObject<{
133
133
  locales: z.ZodTuple<[z.ZodString], z.ZodString>;
134
+ fallbackLocales: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>>;
134
135
  buckets: z.ZodArray<z.ZodPipe<z.ZodObject<{
135
136
  include: z.ZodArray<z.ZodString>;
136
137
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -131,6 +131,7 @@ declare const Bucket: z.ZodPipe<z.ZodObject<{
131
131
  type Bucket = z.infer<typeof Bucket>;
132
132
  declare const Config: z.ZodObject<{
133
133
  locales: z.ZodTuple<[z.ZodString], z.ZodString>;
134
+ fallbackLocales: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>>;
134
135
  buckets: z.ZodArray<z.ZodPipe<z.ZodObject<{
135
136
  include: z.ZodArray<z.ZodString>;
136
137
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -0,0 +1,97 @@
1
+ require("./index.cjs");
2
+ const require_hash = require("./hash-8It7TJpB.cjs");
3
+ let node_fs_promises = require("node:fs/promises");
4
+ let node_path = require("node:path");
5
+ //#region src/features/catalogue/merge.ts
6
+ function mergeUnique(...items) {
7
+ return Array.from(new Set(items.flat()));
8
+ }
9
+ function getMessageKey(message) {
10
+ return message.id ?? require_hash.generateHash(message.message, message.context);
11
+ }
12
+ function mergeExtractedMessages(messages) {
13
+ const mergedMessages = messages.reduce((map, message) => {
14
+ const key = getMessageKey(message);
15
+ const existing = map.get(key) ?? message;
16
+ map.set(key, {
17
+ ...existing,
18
+ comments: mergeUnique(...existing.comments, ...message.comments),
19
+ references: mergeUnique(...existing.references, ...message.references)
20
+ });
21
+ return map;
22
+ }, /* @__PURE__ */ new Map());
23
+ return Array.from(mergedMessages.values());
24
+ }
25
+ function reconcileLocaleMessages(existingMessages, nextMessages) {
26
+ const existingMessagesByKey = existingMessages.reduce((map, message) => {
27
+ map.set(getMessageKey(message), message);
28
+ return map;
29
+ }, /* @__PURE__ */ new Map());
30
+ const reconciledMessages = nextMessages.reduce((map, message) => {
31
+ const key = getMessageKey(message);
32
+ const existingMessage = existingMessagesByKey.get(key);
33
+ map.set(key, {
34
+ ...message,
35
+ translation: existingMessage?.translation
36
+ });
37
+ return map;
38
+ }, /* @__PURE__ */ new Map());
39
+ return Array.from(reconciledMessages.values());
40
+ }
41
+ //#endregion
42
+ //#region src/features/catalogue/path.ts
43
+ function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
44
+ return (0, node_path.resolve)(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
45
+ }
46
+ //#endregion
47
+ //#region src/features/catalogue/storage.ts
48
+ const DECLARATION_CONTENT = `
49
+ declare const messages: Record<string, string>;
50
+ export default messages;
51
+ `.trim();
52
+ async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
53
+ const content = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => "");
54
+ if (!content) return [];
55
+ return bucket.formatter.parse(content);
56
+ }
57
+ async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
58
+ const existingContent = await (0, node_fs_promises.readFile)(path, "utf8").catch(() => void 0);
59
+ const catalogueContent = bucket.formatter.stringify(messages, {
60
+ locale,
61
+ existingContent
62
+ });
63
+ const declarationPath = `${path}.d.ts`;
64
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(path), { recursive: true });
65
+ await Promise.all([(0, node_fs_promises.writeFile)(path, catalogueContent), (0, node_fs_promises.writeFile)(declarationPath, DECLARATION_CONTENT)]);
66
+ }
67
+ //#endregion
68
+ Object.defineProperty(exports, "expandBucketOutputPath", {
69
+ enumerable: true,
70
+ get: function() {
71
+ return expandBucketOutputPath;
72
+ }
73
+ });
74
+ Object.defineProperty(exports, "mergeExtractedMessages", {
75
+ enumerable: true,
76
+ get: function() {
77
+ return mergeExtractedMessages;
78
+ }
79
+ });
80
+ Object.defineProperty(exports, "readCatalogueMessages", {
81
+ enumerable: true,
82
+ get: function() {
83
+ return readCatalogueMessages;
84
+ }
85
+ });
86
+ Object.defineProperty(exports, "reconcileLocaleMessages", {
87
+ enumerable: true,
88
+ get: function() {
89
+ return reconcileLocaleMessages;
90
+ }
91
+ });
92
+ Object.defineProperty(exports, "writeCatalogueMessages", {
93
+ enumerable: true,
94
+ get: function() {
95
+ return writeCatalogueMessages;
96
+ }
97
+ });
@@ -0,0 +1,67 @@
1
+ import { t as generateHash } from "./hash-DyC8GzMa.mjs";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+ //#region src/features/catalogue/merge.ts
5
+ function mergeUnique(...items) {
6
+ return Array.from(new Set(items.flat()));
7
+ }
8
+ function getMessageKey(message) {
9
+ return message.id ?? generateHash(message.message, message.context);
10
+ }
11
+ function mergeExtractedMessages(messages) {
12
+ const mergedMessages = messages.reduce((map, message) => {
13
+ const key = getMessageKey(message);
14
+ const existing = map.get(key) ?? message;
15
+ map.set(key, {
16
+ ...existing,
17
+ comments: mergeUnique(...existing.comments, ...message.comments),
18
+ references: mergeUnique(...existing.references, ...message.references)
19
+ });
20
+ return map;
21
+ }, /* @__PURE__ */ new Map());
22
+ return Array.from(mergedMessages.values());
23
+ }
24
+ function reconcileLocaleMessages(existingMessages, nextMessages) {
25
+ const existingMessagesByKey = existingMessages.reduce((map, message) => {
26
+ map.set(getMessageKey(message), message);
27
+ return map;
28
+ }, /* @__PURE__ */ new Map());
29
+ const reconciledMessages = nextMessages.reduce((map, message) => {
30
+ const key = getMessageKey(message);
31
+ const existingMessage = existingMessagesByKey.get(key);
32
+ map.set(key, {
33
+ ...message,
34
+ translation: existingMessage?.translation
35
+ });
36
+ return map;
37
+ }, /* @__PURE__ */ new Map());
38
+ return Array.from(reconciledMessages.values());
39
+ }
40
+ //#endregion
41
+ //#region src/features/catalogue/path.ts
42
+ function expandBucketOutputPath(bucket, locale, extension = bucket.formatter.extension) {
43
+ return resolve(bucket.output.replaceAll("{locale}", locale).replaceAll("{extension}", extension.slice(1)));
44
+ }
45
+ //#endregion
46
+ //#region src/features/catalogue/storage.ts
47
+ const DECLARATION_CONTENT = `
48
+ declare const messages: Record<string, string>;
49
+ export default messages;
50
+ `.trim();
51
+ async function readCatalogueMessages(bucket, locale, path = expandBucketOutputPath(bucket, locale)) {
52
+ const content = await readFile(path, "utf8").catch(() => "");
53
+ if (!content) return [];
54
+ return bucket.formatter.parse(content);
55
+ }
56
+ async function writeCatalogueMessages(bucket, locale, messages, path = expandBucketOutputPath(bucket, locale)) {
57
+ const existingContent = await readFile(path, "utf8").catch(() => void 0);
58
+ const catalogueContent = bucket.formatter.stringify(messages, {
59
+ locale,
60
+ existingContent
61
+ });
62
+ const declarationPath = `${path}.d.ts`;
63
+ await mkdir(dirname(path), { recursive: true });
64
+ await Promise.all([writeFile(path, catalogueContent), writeFile(declarationPath, DECLARATION_CONTENT)]);
65
+ }
66
+ //#endregion
67
+ export { reconcileLocaleMessages as a, mergeExtractedMessages as i, writeCatalogueMessages as n, expandBucketOutputPath as r, readCatalogueMessages as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saykit/config",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI and configuration tooling for saykit",
5
5
  "keywords": [
6
6
  "cli",
@@ -37,6 +37,16 @@
37
37
  "default": "./dist/index.mjs"
38
38
  }
39
39
  },
40
+ "./features/catalogue": {
41
+ "require": {
42
+ "types": "./dist/features/catalogue/index.d.cts",
43
+ "default": "./dist/features/catalogue/index.cjs"
44
+ },
45
+ "import": {
46
+ "types": "./dist/features/catalogue/index.d.mts",
47
+ "default": "./dist/features/catalogue/index.mjs"
48
+ }
49
+ },
40
50
  "./features/loader": {
41
51
  "require": {
42
52
  "types": "./dist/features/loader/index.d.cts",
@@ -66,7 +76,7 @@
66
76
  "@commander-js/extra-typings": "^14.0.0",
67
77
  "commander": "^14.0.3",
68
78
  "js-sha256": "^0.11.1",
69
- "picomatch": "^4.0.4",
79
+ "picomatch": "^4.0.5",
70
80
  "zod": "^4.4.3"
71
81
  },
72
82
  "devDependencies": {
@@ -82,8 +92,6 @@
82
92
  },
83
93
  "scripts": {
84
94
  "check": "tsc --noEmit",
85
- "test": "vitest",
86
- "coverage": "vitest run --coverage",
87
95
  "build": "tsdown"
88
96
  }
89
97
  }
File without changes
File without changes