@linguanahq/catalog 0.1.1 → 0.1.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.
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ export declare const contextProfileSchema: z.ZodObject<{
3
+ product: z.ZodDefault<z.ZodString>;
4
+ audience: z.ZodDefault<z.ZodString>;
5
+ tone: z.ZodDefault<z.ZodString>;
6
+ formality: z.ZodDefault<z.ZodEnum<{
7
+ neutral: "neutral";
8
+ formal: "formal";
9
+ informal: "informal";
10
+ }>>;
11
+ preferred: z.ZodDefault<z.ZodArray<z.ZodString>>;
12
+ discouraged: z.ZodDefault<z.ZodArray<z.ZodString>>;
13
+ conventions: z.ZodDefault<z.ZodString>;
14
+ localeOverrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
15
+ product: z.ZodOptional<z.ZodString>;
16
+ audience: z.ZodOptional<z.ZodString>;
17
+ tone: z.ZodOptional<z.ZodString>;
18
+ formality: z.ZodOptional<z.ZodEnum<{
19
+ neutral: "neutral";
20
+ formal: "formal";
21
+ informal: "informal";
22
+ }>>;
23
+ preferred: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
+ discouraged: z.ZodOptional<z.ZodArray<z.ZodString>>;
25
+ conventions: z.ZodOptional<z.ZodString>;
26
+ }, z.core.$strip>>>;
27
+ glossary: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
28
+ mode: z.ZodLiteral<"active">;
29
+ }, z.core.$strip>, z.ZodObject<{
30
+ mode: z.ZodLiteral<"pinned">;
31
+ versionId: z.ZodString;
32
+ }, z.core.$strip>], "mode">>;
33
+ }, z.core.$strip>;
34
+ export type ContextProfileContent = z.infer<typeof contextProfileSchema>;
35
+ export declare function resolveContextProfile(content: ContextProfileContent, locale: string): {
36
+ product: string;
37
+ audience: string;
38
+ tone: string;
39
+ formality: "neutral" | "formal" | "informal";
40
+ preferred: string[];
41
+ discouraged: string[];
42
+ conventions: string;
43
+ };
44
+ export declare const meaningSchema: z.ZodString;
45
+ export declare const runtimeItemSchema: z.ZodObject<{
46
+ source: z.ZodString;
47
+ format: z.ZodDefault<z.ZodEnum<{
48
+ plain: "plain";
49
+ icu: "icu";
50
+ }>>;
51
+ meaning: z.ZodDefault<z.ZodString>;
52
+ }, z.core.$strip>;
53
+ export type RuntimeItem = z.infer<typeof runtimeItemSchema>;
54
+ export declare const runtimeBatchSchema: z.ZodObject<{
55
+ projectId: z.ZodString;
56
+ environment: z.ZodString;
57
+ surface: z.ZodString;
58
+ sourceLocale: z.ZodString;
59
+ locale: z.ZodString;
60
+ items: z.ZodArray<z.ZodObject<{
61
+ source: z.ZodString;
62
+ format: z.ZodDefault<z.ZodEnum<{
63
+ plain: "plain";
64
+ icu: "icu";
65
+ }>>;
66
+ meaning: z.ZodDefault<z.ZodString>;
67
+ }, z.core.$strip>>;
68
+ }, z.core.$strip>;
69
+ export type RuntimeBatchInput = z.infer<typeof runtimeBatchSchema>;
70
+ export declare function runtimeContentKey(input: {
71
+ sourceLocale: string;
72
+ surface: string;
73
+ } & RuntimeItem): string;
@@ -0,0 +1,2 @@
1
+ /** Compare semantic requirements, not translated branch prose or locale-specific categories. */
2
+ export declare function validateIcuSemantics(source: string, target: string, targetLocale?: string): string[];
package/dist/index.d.ts CHANGED
@@ -81,7 +81,10 @@ export declare const diagnosticSchema: z.ZodObject<{
81
81
  remediation: z.ZodOptional<z.ZodString>;
82
82
  }, z.core.$strip>;
83
83
  export declare const manifestIntegrationSchema: z.ZodObject<{
84
- tool: z.ZodLiteral<"vite">;
84
+ tool: z.ZodEnum<{
85
+ vite: "vite";
86
+ next: "next";
87
+ }>;
85
88
  toolVersion: z.ZodString;
86
89
  framework: z.ZodEnum<{
87
90
  react: "react";
@@ -103,7 +106,10 @@ export declare const manifestSchema: z.ZodObject<{
103
106
  sourceLocale: z.ZodString;
104
107
  sourceRevision: z.ZodOptional<z.ZodString>;
105
108
  integration: z.ZodOptional<z.ZodObject<{
106
- tool: z.ZodLiteral<"vite">;
109
+ tool: z.ZodEnum<{
110
+ vite: "vite";
111
+ next: "next";
112
+ }>;
107
113
  toolVersion: z.ZodString;
108
114
  framework: z.ZodEnum<{
109
115
  react: "react";
@@ -199,3 +205,5 @@ export declare function stableMessageId(input: {
199
205
  export declare function manifestHash(manifest: Manifest): string;
200
206
  export declare function createCatalog(input: Omit<Catalog, "integrity">): Catalog;
201
207
  export declare function verifyCatalog(catalog: Catalog): boolean;
208
+ export * from "./context.ts";
209
+ export * from "./icu-validation.ts";
package/dist/index.js CHANGED
@@ -1,21 +1,170 @@
1
1
  // src/index.ts
2
2
  import {
3
- parse,
4
- TYPE
3
+ parse as parse2,
4
+ TYPE as TYPE2
5
5
  } from "@formatjs/icu-messageformat-parser";
6
6
  import { sha256 } from "@noble/hashes/sha2.js";
7
7
  import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
8
+ import { z as z2 } from "zod";
9
+
10
+ // src/context.ts
8
11
  import { z } from "zod";
12
+ var guidance = z.object({
13
+ product: z.string().max(2e3).default(""),
14
+ audience: z.string().max(1e3).default(""),
15
+ tone: z.string().max(1e3).default(""),
16
+ formality: z.enum(["neutral", "formal", "informal"]).default("neutral"),
17
+ preferred: z.array(z.string().max(512)).max(20).default([]),
18
+ discouraged: z.array(z.string().max(512)).max(20).default([]),
19
+ conventions: z.string().max(2e3).default("")
20
+ });
21
+ var contextProfileSchema = guidance.extend({
22
+ localeOverrides: z.record(
23
+ z.string(),
24
+ z.object({
25
+ product: guidance.shape.product.removeDefault().optional(),
26
+ audience: guidance.shape.audience.removeDefault().optional(),
27
+ tone: guidance.shape.tone.removeDefault().optional(),
28
+ formality: guidance.shape.formality.removeDefault().optional(),
29
+ preferred: guidance.shape.preferred.removeDefault().optional(),
30
+ discouraged: guidance.shape.discouraged.removeDefault().optional(),
31
+ conventions: guidance.shape.conventions.removeDefault().optional()
32
+ })
33
+ ).default({}),
34
+ glossary: z.discriminatedUnion("mode", [
35
+ z.object({ mode: z.literal("active") }),
36
+ z.object({ mode: z.literal("pinned"), versionId: z.string().min(1) })
37
+ ]).default({ mode: "active" })
38
+ }).superRefine((value, ctx) => {
39
+ if (new TextEncoder().encode(canonicalJson(value)).byteLength > 16 * 1024)
40
+ ctx.addIssue({
41
+ code: "custom",
42
+ message: "Context profile exceeds 16 KiB"
43
+ });
44
+ for (const locale of Object.keys(value.localeOverrides)) {
45
+ try {
46
+ if (Intl.getCanonicalLocales(locale)[0] !== locale) throw new Error();
47
+ } catch {
48
+ ctx.addIssue({
49
+ code: "custom",
50
+ message: `Use a canonical locale tag: ${locale}`
51
+ });
52
+ }
53
+ }
54
+ });
55
+ function resolveContextProfile(content, locale) {
56
+ const { localeOverrides, glossary: _glossary, ...base } = content;
57
+ const canonical = Intl.getCanonicalLocales(locale)[0];
58
+ return {
59
+ ...base,
60
+ ...localeOverrides[canonical] ?? localeOverrides[canonical.split("-")[0]] ?? {}
61
+ };
62
+ }
63
+ var meaningSchema = z.string().refine(
64
+ (value) => [...value].length <= 512,
65
+ "Meaning exceeds 512 code points"
66
+ );
67
+ var runtimeItemSchema = z.object({
68
+ source: z.string().min(1),
69
+ format: z.enum(["plain", "icu"]).default("plain"),
70
+ meaning: meaningSchema.default("")
71
+ });
72
+ var runtimeBatchSchema = z.object({
73
+ projectId: z.string().min(1),
74
+ environment: z.string().min(1),
75
+ surface: z.string().min(1),
76
+ sourceLocale: z.string().min(2),
77
+ locale: z.string().min(2),
78
+ items: z.array(runtimeItemSchema).min(1).max(50)
79
+ }).superRefine((value, ctx) => {
80
+ if (value.items.reduce((n, item) => n + [...item.source].length, 0) > 12e3)
81
+ ctx.addIssue({
82
+ code: "custom",
83
+ message: "Batch exceeds 12000 source code points"
84
+ });
85
+ if (new TextEncoder().encode(JSON.stringify(value)).byteLength > 256 * 1024)
86
+ ctx.addIssue({ code: "custom", message: "Batch exceeds 256 KiB" });
87
+ });
88
+ function runtimeContentKey(input) {
89
+ return contentHash({
90
+ normalizationVersion: 1,
91
+ sourceLocale: Intl.getCanonicalLocales(input.sourceLocale)[0],
92
+ surface: input.surface,
93
+ source: input.source.replace(/\r\n?/g, "\n").normalize("NFC"),
94
+ format: input.format,
95
+ meaning: input.meaning
96
+ });
97
+ }
98
+
99
+ // src/icu-validation.ts
100
+ import {
101
+ parse,
102
+ TYPE
103
+ } from "@formatjs/icu-messageformat-parser";
104
+ function validateIcuSemantics(source, target, targetLocale) {
105
+ const issues = [];
106
+ function requirements(text, validateLocale) {
107
+ const entries = /* @__PURE__ */ new Set();
108
+ function visit(elements) {
109
+ for (const e of elements) {
110
+ if (e.type === TYPE.literal || e.type === TYPE.pound) continue;
111
+ const requirement = {
112
+ name: e.value,
113
+ type: e.type
114
+ };
115
+ if (e.type === TYPE.plural || e.type === TYPE.select) {
116
+ if (!("other" in e.options)) issues.push("ICU_OTHER_REQUIRED");
117
+ if (e.type === TYPE.plural) {
118
+ requirement.pluralType = e.pluralType;
119
+ requirement.offset = e.offset;
120
+ requirement.exact = Object.keys(e.options).filter((k) => k.startsWith("=")).sort();
121
+ if (validateLocale && targetLocale) {
122
+ const allowed = new Set(
123
+ new Intl.PluralRules(targetLocale, {
124
+ type: e.pluralType
125
+ }).resolvedOptions().pluralCategories
126
+ );
127
+ for (const key of Object.keys(e.options))
128
+ if (!key.startsWith("=") && !allowed.has(key))
129
+ issues.push("INVALID_PLURAL_CATEGORY");
130
+ }
131
+ } else requirement.selectors = Object.keys(e.options).sort();
132
+ for (const option of Object.values(e.options)) visit(option.value);
133
+ }
134
+ if (e.type === TYPE.number) {
135
+ requirement.style = typeof e.style === "object" && e.style ? e.style.tokens : e.style;
136
+ }
137
+ if (e.type === TYPE.tag) visit(e.children);
138
+ entries.add(canonicalJson(requirement));
139
+ }
140
+ }
141
+ visit(
142
+ parse(text, {
143
+ captureLocation: false,
144
+ ignoreTag: false,
145
+ requiresOtherClause: true
146
+ })
147
+ );
148
+ return [...entries].sort();
149
+ }
150
+ const left = requirements(source, false);
151
+ const right = requirements(target, true);
152
+ if (canonicalJson(left) !== canonicalJson(right))
153
+ issues.push("ICU_SEMANTICS_CHANGED");
154
+ return [...new Set(issues)];
155
+ }
156
+
157
+ // src/index.ts
9
158
  var SCHEMA_VERSION = "1.0";
10
159
  var MANIFEST_SCHEMA_VERSION = "2.0";
11
- var messageLocationSchema = z.object({
12
- file: z.string().min(1),
13
- line: z.number().int().positive(),
14
- column: z.number().int().nonnegative()
160
+ var messageLocationSchema = z2.object({
161
+ file: z2.string().min(1),
162
+ line: z2.number().int().positive(),
163
+ column: z2.number().int().nonnegative()
15
164
  });
16
- var placeholderSchema = z.object({
17
- name: z.string().min(1),
18
- kind: z.enum([
165
+ var placeholderSchema = z2.object({
166
+ name: z2.string().min(1),
167
+ kind: z2.enum([
19
168
  "value",
20
169
  "component",
21
170
  "plural",
@@ -25,46 +174,46 @@ var placeholderSchema = z.object({
25
174
  "time"
26
175
  ])
27
176
  });
28
- var messageContextSchema = z.object({
29
- kind: z.enum(["text", "attribute", "explicit"]),
30
- element: z.string().optional(),
31
- attribute: z.string().optional(),
32
- developer: z.string().optional()
177
+ var messageContextSchema = z2.object({
178
+ kind: z2.enum(["text", "attribute", "explicit"]),
179
+ element: z2.string().optional(),
180
+ attribute: z2.string().optional(),
181
+ developer: z2.string().optional()
33
182
  });
34
- var manifestMessageSchema = z.object({
35
- id: z.string().min(1),
36
- source: z.string().min(1),
37
- normalized: z.string().min(1),
38
- contentHash: z.string().length(64),
183
+ var manifestMessageSchema = z2.object({
184
+ id: z2.string().min(1),
185
+ source: z2.string().min(1),
186
+ normalized: z2.string().min(1),
187
+ contentHash: z2.string().length(64),
39
188
  context: messageContextSchema,
40
- placeholders: z.array(placeholderSchema).default([]),
41
- confidence: z.enum(["high", "explicit"]),
42
- locations: z.array(messageLocationSchema).min(1)
189
+ placeholders: z2.array(placeholderSchema).default([]),
190
+ confidence: z2.enum(["high", "explicit"]),
191
+ locations: z2.array(messageLocationSchema).min(1)
43
192
  });
44
- var diagnosticSchema = z.object({
45
- code: z.string().min(1),
46
- severity: z.enum(["info", "warning", "error"]),
47
- message: z.string().min(1),
193
+ var diagnosticSchema = z2.object({
194
+ code: z2.string().min(1),
195
+ severity: z2.enum(["info", "warning", "error"]),
196
+ message: z2.string().min(1),
48
197
  location: messageLocationSchema.optional(),
49
- remediation: z.string().optional()
198
+ remediation: z2.string().optional()
50
199
  });
51
- var manifestIntegrationSchema = z.object({
52
- tool: z.literal("vite"),
53
- toolVersion: z.string().min(1),
54
- framework: z.enum(["react", "vue", "svelte", "solid"]),
55
- adapterVersion: z.string().min(1),
56
- ssr: z.boolean()
200
+ var manifestIntegrationSchema = z2.object({
201
+ tool: z2.enum(["vite", "next"]),
202
+ toolVersion: z2.string().min(1),
203
+ framework: z2.enum(["react", "vue", "svelte", "solid"]),
204
+ adapterVersion: z2.string().min(1),
205
+ ssr: z2.boolean()
57
206
  });
58
- var manifestSchema = z.object({
59
- schemaVersion: z.enum([SCHEMA_VERSION, MANIFEST_SCHEMA_VERSION]),
60
- compilerVersion: z.string().min(1),
61
- projectId: z.string().min(1),
62
- environment: z.string().min(1),
63
- sourceLocale: z.string().min(2),
64
- sourceRevision: z.string().optional(),
207
+ var manifestSchema = z2.object({
208
+ schemaVersion: z2.enum([SCHEMA_VERSION, MANIFEST_SCHEMA_VERSION]),
209
+ compilerVersion: z2.string().min(1),
210
+ projectId: z2.string().min(1),
211
+ environment: z2.string().min(1),
212
+ sourceLocale: z2.string().min(2),
213
+ sourceRevision: z2.string().optional(),
65
214
  integration: manifestIntegrationSchema.optional(),
66
- messages: z.array(manifestMessageSchema),
67
- diagnostics: z.array(diagnosticSchema).default([])
215
+ messages: z2.array(manifestMessageSchema),
216
+ diagnostics: z2.array(diagnosticSchema).default([])
68
217
  }).superRefine((manifest, context) => {
69
218
  if (manifest.schemaVersion === MANIFEST_SCHEMA_VERSION && !manifest.integration) {
70
219
  context.addIssue({
@@ -74,16 +223,16 @@ var manifestSchema = z.object({
74
223
  });
75
224
  }
76
225
  });
77
- var catalogSchema = z.object({
78
- schemaVersion: z.literal(SCHEMA_VERSION),
79
- projectId: z.string().min(1),
80
- environment: z.string().min(1),
81
- sourceLocale: z.string().min(2),
82
- locale: z.string().min(2),
83
- manifestHash: z.string().length(64),
84
- version: z.number().int().positive(),
85
- messages: z.record(z.string(), z.string()),
86
- integrity: z.string().length(64)
226
+ var catalogSchema = z2.object({
227
+ schemaVersion: z2.literal(SCHEMA_VERSION),
228
+ projectId: z2.string().min(1),
229
+ environment: z2.string().min(1),
230
+ sourceLocale: z2.string().min(2),
231
+ locale: z2.string().min(2),
232
+ manifestHash: z2.string().length(64),
233
+ version: z2.number().int().positive(),
234
+ messages: z2.record(z2.string(), z2.string()),
235
+ integrity: z2.string().length(64)
87
236
  });
88
237
  function sortValue(value) {
89
238
  if (Array.isArray(value)) return value.map(sortValue);
@@ -123,36 +272,36 @@ function parseMessagePlaceholders(source) {
123
272
  const visit = (elements) => {
124
273
  for (const element of elements) {
125
274
  switch (element.type) {
126
- case TYPE.argument:
275
+ case TYPE2.argument:
127
276
  add(element.value, "value");
128
277
  break;
129
- case TYPE.number:
278
+ case TYPE2.number:
130
279
  add(element.value, "number");
131
280
  break;
132
- case TYPE.date:
281
+ case TYPE2.date:
133
282
  add(element.value, "date");
134
283
  break;
135
- case TYPE.time:
284
+ case TYPE2.time:
136
285
  add(element.value, "time");
137
286
  break;
138
- case TYPE.select:
287
+ case TYPE2.select:
139
288
  add(element.value, "select");
140
289
  for (const option of Object.values(element.options))
141
290
  visit(option.value);
142
291
  break;
143
- case TYPE.plural:
292
+ case TYPE2.plural:
144
293
  add(element.value, "plural");
145
294
  for (const option of Object.values(element.options))
146
295
  visit(option.value);
147
296
  break;
148
- case TYPE.tag:
297
+ case TYPE2.tag:
149
298
  add(element.value, "component");
150
299
  visit(element.children);
151
300
  break;
152
301
  }
153
302
  }
154
303
  };
155
- visit(parse(source, { captureLocation: false, ignoreTag: false }));
304
+ visit(parse2(source, { captureLocation: false, ignoreTag: false }));
156
305
  return [...descriptors.values()].sort(
157
306
  (left, right) => left.name.localeCompare(right.name)
158
307
  );
@@ -184,17 +333,24 @@ export {
184
333
  canonicalJson,
185
334
  catalogSchema,
186
335
  contentHash,
336
+ contextProfileSchema,
187
337
  createCatalog,
188
338
  diagnosticSchema,
189
339
  manifestHash,
190
340
  manifestIntegrationSchema,
191
341
  manifestMessageSchema,
192
342
  manifestSchema,
343
+ meaningSchema,
193
344
  messageContextSchema,
194
345
  messageLocationSchema,
195
346
  normalizeSource,
196
347
  parseMessagePlaceholders,
197
348
  placeholderSchema,
349
+ resolveContextProfile,
350
+ runtimeBatchSchema,
351
+ runtimeContentKey,
352
+ runtimeItemSchema,
198
353
  stableMessageId,
354
+ validateIcuSemantics,
199
355
  verifyCatalog
200
356
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linguanahq/catalog",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Linguana catalog integration",
6
6
  "exports": {