@cliff-format/json 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CLIFF contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,68 @@
1
- # Temporary Holding Version
1
+ # @cliff-format/json
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
+ JSON converters for CLIFF 1.0 / 1.1.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @cliff-format/core @cliff-format/json
9
+ ```
10
+
11
+ `@cliff-format/core` is a peer dependency.
12
+
13
+ ## CLIFF-shaped JSON
14
+
15
+ `toCliJson` / `fromCliJson` preserve the full CLIFF data model: header, group metadata, entry fields, extensions and the declared specification version. This is the lossless JSON form.
16
+
17
+ ```ts
18
+ import { parse } from "@cliff-format/core";
19
+ import { toCliJson, fromCliJson } from "@cliff-format/json";
20
+
21
+ const document = parse(text);
22
+ const object = toCliJson(document);
23
+ const restored = fromCliJson(object);
24
+ ```
25
+
26
+ ## Flat and nested JSON
27
+
28
+ Flat JSON maps dotted keys to strings. Nested JSON maps nested objects to strings and produces a structure i18next can consume for simple keys.
29
+
30
+ ```ts
31
+ import { fromFlatJson, toFlatJson, fromNestedJson, toNestedJson } from "@cliff-format/json";
32
+
33
+ const { document, warnings } = fromFlatJson(
34
+ { "video.resolution": "分辨率" },
35
+ {
36
+ namespace: "demo",
37
+ clan: "settings",
38
+ sourceLanguage: "en-US",
39
+ targetLanguage: "zh-CN",
40
+ }
41
+ );
42
+
43
+ const flat = toFlatJson(document, { keyStyle: "canonical" });
44
+ const nested = toNestedJson(document);
45
+ ```
46
+
47
+ Import options:
48
+
49
+ - `valueRole: "target"` (default): key becomes `source`, value becomes `target`; status defaults to `translated`.
50
+ - `valueRole: "source"`: value becomes `source` and no target is created; status defaults to `initial`.
51
+ - `entryType` defaults to `sentence`.
52
+ - `keySeparator` defaults to `.`.
53
+
54
+ Imports normalize non-conforming keys using the CLIFF tolerant identifier algorithm and return a `warnings` list. They never silently overwrite a duplicate.
55
+
56
+ Export options:
57
+
58
+ - `keyStyle`: `canonical` (default), `group-entry`, `entry`.
59
+ - `value`: `target` (default) or `source`.
60
+ - `fallbackToSource`: when the selected value is missing, fall back to the other side; default `false`.
61
+
62
+ Flat and nested JSON are lossy: they do not carry header prose, context, types, emotion, status, width, reviewer, extensions or references. Use CLIFF-shaped JSON when those matter.
63
+
64
+ This package only converts resources. For runtime rendering, use `@cliff-format/i18n`; it resolves CLIFF entry IDs and ICU messages instead of i18next key conventions.
65
+
66
+ ## License
67
+
68
+ MIT.
package/dist/index.cjs ADDED
@@ -0,0 +1,470 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ buildDocumentFromEntries: () => buildDocumentFromEntries,
24
+ fromCliJson: () => fromCliJson,
25
+ fromFlatJson: () => fromFlatJson,
26
+ fromJsonString: () => fromJsonString,
27
+ fromNestedJson: () => fromNestedJson,
28
+ isCliffObject: () => isCliffObject,
29
+ toCliJson: () => toCliJson,
30
+ toFlatJson: () => toFlatJson,
31
+ toJsonString: () => toJsonString,
32
+ toNestedJson: () => toNestedJson
33
+ });
34
+ module.exports = __toCommonJS(src_exports);
35
+
36
+ // src/cli-json.ts
37
+ var import_core = require("@cliff-format/core");
38
+ function isRecord(value) {
39
+ return typeof value === "object" && value !== null && !Array.isArray(value);
40
+ }
41
+ function ownString(value, key, label) {
42
+ const result = value[key];
43
+ if (typeof result !== "string") {
44
+ throw new import_core.CliffModelError(`${label} must be a string`);
45
+ }
46
+ return result;
47
+ }
48
+ function optionalString(value, key) {
49
+ const result = value[key];
50
+ if (result === void 0 || result === null) return void 0;
51
+ if (typeof result !== "string") throw new import_core.CliffModelError(`${key} must be a string`);
52
+ return result;
53
+ }
54
+ function stringArray(value, label) {
55
+ if (value === void 0 || value === null) return [];
56
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
57
+ throw new import_core.CliffModelError(`${label} must be an array of strings`);
58
+ }
59
+ return value.slice();
60
+ }
61
+ function extensions(value, label) {
62
+ if (value === void 0 || value === null) return (0, import_core.createExtensions)();
63
+ if (!isRecord(value)) throw new import_core.CliffModelError(`${label} must be an object`);
64
+ const result = (0, import_core.createExtensions)();
65
+ for (const key of Object.keys(value)) {
66
+ const item = value[key];
67
+ if (typeof item !== "string") {
68
+ throw new import_core.CliffModelError(`${label}.${key} must be a string`);
69
+ }
70
+ result[key] = item;
71
+ }
72
+ return result;
73
+ }
74
+ function pickHeaderString(header, keys, label) {
75
+ for (const key of keys) {
76
+ const value = header[key];
77
+ if (typeof value === "string" && value.length > 0) return value;
78
+ }
79
+ throw new import_core.CliffModelError(`CLIFF-shaped input is missing required header field '${label}'`);
80
+ }
81
+ function isCliffObject(value) {
82
+ return isRecord(value) && isRecord(value.header) && Array.isArray(value.groups);
83
+ }
84
+ function toCliJson(document) {
85
+ const header = document.header;
86
+ const headerData = {
87
+ namespace: header.namespace,
88
+ clan: header.clan,
89
+ "source-language": header.sourceLanguage,
90
+ "target-language": header.targetLanguage
91
+ };
92
+ if (header.version !== void 0) headerData.version = header.version;
93
+ if (header.variant !== "standard") headerData.variant = header.variant;
94
+ if (header.title !== void 0) headerData.title = header.title;
95
+ if (header.info !== void 0) headerData.info = header.info;
96
+ if (header.standard !== void 0) headerData.standard = header.standard;
97
+ if (header.dependency.length > 0) headerData.dependency = [...header.dependency];
98
+ if (Object.keys(header.extensions).length > 0) headerData.extensions = { ...header.extensions };
99
+ const groups = document.groups.map((group) => {
100
+ const groupData = { path: group.path };
101
+ if (group.context !== void 0) groupData.context = group.context;
102
+ if (group.type !== void 0) groupData.type = group.type;
103
+ if (group.emotion.length > 0) groupData.emotion = [...group.emotion];
104
+ if (group.maxWidth !== void 0) groupData["max-width"] = group.maxWidth;
105
+ if (Object.keys(group.extensions).length > 0) {
106
+ groupData.extensions = { ...group.extensions };
107
+ }
108
+ groupData.entries = group.entries.map((entry) => {
109
+ const entryData = { id: entry.id };
110
+ if (entry.source !== void 0) entryData.source = entry.source;
111
+ if (entry.target !== void 0) entryData.target = entry.target;
112
+ if (entry.type !== void 0) entryData.type = entry.type;
113
+ if (entry.emotion.length > 0) entryData.emotion = [...entry.emotion];
114
+ if (entry.status !== void 0) entryData.status = entry.status;
115
+ if (entry.context !== void 0) entryData.context = entry.context;
116
+ if (entry.maxWidth !== void 0) entryData["max-width"] = entry.maxWidth;
117
+ if (entry.reference.length > 0) entryData.reference = [...entry.reference];
118
+ if (entry.reviewer !== void 0) entryData.reviewer = entry.reviewer;
119
+ if (Object.keys(entry.extensions).length > 0) {
120
+ entryData.extensions = { ...entry.extensions };
121
+ }
122
+ return entryData;
123
+ });
124
+ return groupData;
125
+ });
126
+ return { "spec-version": document.specVersion, header: headerData, groups };
127
+ }
128
+ function toJsonString(document, space = 2) {
129
+ return JSON.stringify(toCliJson(document), null, space);
130
+ }
131
+ function fromCliJson(value) {
132
+ if (!isCliffObject(value)) {
133
+ throw new import_core.CliffModelError("input is not a CLIFF-shaped object");
134
+ }
135
+ const headerData = value.header;
136
+ const document = (0, import_core.createDocument)(
137
+ value["spec-version"] === "1.0" ? "1.0" : value["spec-version"] === "1.1" ? "1.1" : "1.1"
138
+ );
139
+ if (value["spec-version"] !== void 0 && value["spec-version"] !== "1.0" && value["spec-version"] !== "1.1") {
140
+ throw new import_core.CliffModelError(`unsupported spec-version '${String(value["spec-version"])}'`);
141
+ }
142
+ document.header.namespace = pickHeaderString(headerData, ["namespace"], "namespace");
143
+ document.header.clan = pickHeaderString(headerData, ["clan"], "clan");
144
+ document.header.sourceLanguage = pickHeaderString(
145
+ headerData,
146
+ ["source-language", "source_language", "sourceLanguage"],
147
+ "source-language"
148
+ );
149
+ document.header.targetLanguage = pickHeaderString(
150
+ headerData,
151
+ ["target-language", "target_language", "targetLanguage"],
152
+ "target-language"
153
+ );
154
+ const version = optionalString(headerData, "version");
155
+ if (version !== void 0) document.header.version = version;
156
+ const variant = optionalString(headerData, "variant");
157
+ if (variant !== void 0) document.header.variant = variant;
158
+ const title = optionalString(headerData, "title");
159
+ if (title !== void 0) document.header.title = title;
160
+ const info = optionalString(headerData, "info");
161
+ if (info !== void 0) document.header.info = info;
162
+ const standard = optionalString(headerData, "standard");
163
+ if (standard !== void 0) document.header.standard = standard;
164
+ document.header.dependency = stringArray(headerData.dependency, "header.dependency");
165
+ document.header.extensions = extensions(headerData.extensions, "header.extensions");
166
+ for (const rawGroup of value.groups) {
167
+ if (!isRecord(rawGroup)) throw new import_core.CliffModelError("each group must be an object");
168
+ const path = ownString(rawGroup, "path", "group.path");
169
+ const group = {
170
+ path,
171
+ line: 0,
172
+ emotion: stringArray(rawGroup.emotion, "group.emotion"),
173
+ entries: [],
174
+ extensions: extensions(rawGroup.extensions, "group.extensions")
175
+ };
176
+ const groupContext = optionalString(rawGroup, "context");
177
+ if (groupContext !== void 0) group.context = groupContext;
178
+ const groupType = optionalString(rawGroup, "type");
179
+ if (groupType !== void 0) group.type = groupType;
180
+ const groupWidth = rawGroup["max-width"];
181
+ if (groupWidth !== void 0 && groupWidth !== null) {
182
+ if (typeof groupWidth !== "number" || !Number.isInteger(groupWidth)) {
183
+ throw new import_core.CliffModelError("group.max-width must be an integer");
184
+ }
185
+ group.maxWidth = groupWidth;
186
+ }
187
+ const rawEntries = rawGroup.entries;
188
+ if (!Array.isArray(rawEntries)) throw new import_core.CliffModelError("group.entries must be an array");
189
+ for (const rawEntry of rawEntries) {
190
+ if (!isRecord(rawEntry)) throw new import_core.CliffModelError("each entry must be an object");
191
+ const entry = {
192
+ id: ownString(rawEntry, "id", "entry.id"),
193
+ line: 0,
194
+ emotion: stringArray(rawEntry.emotion, "entry.emotion"),
195
+ reference: stringArray(rawEntry.reference, "entry.reference"),
196
+ extensions: extensions(rawEntry.extensions, "entry.extensions")
197
+ };
198
+ for (const key of ["source", "target", "type", "status", "context", "reviewer"]) {
199
+ const item = optionalString(rawEntry, key);
200
+ if (item !== void 0) entry[key] = item;
201
+ }
202
+ const entryWidth = rawEntry["max-width"];
203
+ if (entryWidth !== void 0 && entryWidth !== null) {
204
+ if (typeof entryWidth !== "number" || !Number.isInteger(entryWidth)) {
205
+ throw new import_core.CliffModelError("entry.max-width must be an integer");
206
+ }
207
+ entry.maxWidth = entryWidth;
208
+ }
209
+ group.entries.push(entry);
210
+ }
211
+ document.groups.push(group);
212
+ }
213
+ return document;
214
+ }
215
+ function fromJsonString(text) {
216
+ let value;
217
+ try {
218
+ value = JSON.parse(text);
219
+ } catch (cause) {
220
+ throw new import_core.CliffModelError("invalid JSON", { cause });
221
+ }
222
+ return fromCliJson(value);
223
+ }
224
+
225
+ // src/flat.ts
226
+ var import_core2 = require("@cliff-format/core");
227
+ function requireName(value, label) {
228
+ if (!(0, import_core2.isName)(value)) throw new import_core2.CliffModelError(`${label} must be a valid CLIFF name`);
229
+ }
230
+ function requireLanguage(value, label) {
231
+ if (!import_core2.LANG_RE.test(value)) {
232
+ throw new import_core2.CliffModelError(`${label} must be a plausible BCP 47 language tag`);
233
+ }
234
+ }
235
+ function normalizedOptions(options) {
236
+ requireName(options.namespace, "namespace");
237
+ requireName(options.clan, "clan");
238
+ requireLanguage(options.sourceLanguage, "sourceLanguage");
239
+ requireLanguage(options.targetLanguage, "targetLanguage");
240
+ const maxEntries = options.maxEntries ?? 1e6;
241
+ const maxDepth = options.maxDepth ?? 64;
242
+ if (!Number.isSafeInteger(maxEntries) || maxEntries <= 0) {
243
+ throw new RangeError("maxEntries must be a positive safe integer");
244
+ }
245
+ if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) {
246
+ throw new RangeError("maxDepth must be a positive safe integer");
247
+ }
248
+ return {
249
+ namespace: options.namespace,
250
+ clan: options.clan,
251
+ sourceLanguage: options.sourceLanguage,
252
+ targetLanguage: options.targetLanguage,
253
+ valueRole: options.valueRole ?? "target",
254
+ entryType: options.entryType ?? "sentence",
255
+ entryStatus: options.entryStatus,
256
+ keySeparator: options.keySeparator ?? ".",
257
+ maxEntries,
258
+ maxDepth
259
+ };
260
+ }
261
+ function buildDocumentFromEntries(entries, options) {
262
+ const normalized = normalizedOptions(options);
263
+ if (normalized.keySeparator.length === 0) {
264
+ throw new import_core2.CliffModelError("keySeparator must not be empty");
265
+ }
266
+ if (entries.length > normalized.maxEntries) {
267
+ throw new import_core2.CliffModelError(
268
+ `JSON contains ${entries.length} entries, which exceeds the limit of ${normalized.maxEntries}`
269
+ );
270
+ }
271
+ const document = (0, import_core2.createDocument)();
272
+ document.header.namespace = normalized.namespace;
273
+ document.header.clan = normalized.clan;
274
+ document.header.sourceLanguage = normalized.sourceLanguage;
275
+ document.header.targetLanguage = normalized.targetLanguage;
276
+ const warnings = [];
277
+ const groups = /* @__PURE__ */ new Map();
278
+ const usedEntryIds = /* @__PURE__ */ new Set();
279
+ for (const item of entries) {
280
+ if (item.value.length === 0) {
281
+ warnings.push({ path: item.key, message: "empty value" });
282
+ }
283
+ const parts = item.key.split(normalized.keySeparator);
284
+ if (parts.length === 0 || parts.some((part) => part.length === 0)) {
285
+ throw new import_core2.CliffModelError(`JSON key '${item.key}' has an empty path segment`);
286
+ }
287
+ const rawEntry = parts[parts.length - 1];
288
+ const normalizedGroupParts = parts.slice(0, -1).map((part) => {
289
+ const value = (0, import_core2.normalizeIdentifier)(part, "group");
290
+ if (value !== part) {
291
+ warnings.push({
292
+ path: item.key,
293
+ message: "normalized a group path segment",
294
+ before: part,
295
+ after: value
296
+ });
297
+ }
298
+ return value;
299
+ });
300
+ const groupPath = normalizedGroupParts.length > 0 ? normalizedGroupParts.join(".") : "imported";
301
+ let entryId = (0, import_core2.normalizeIdentifier)(rawEntry, "entry");
302
+ if (entryId !== rawEntry) {
303
+ warnings.push({
304
+ path: item.key,
305
+ message: "normalized an entry id",
306
+ before: rawEntry,
307
+ after: entryId
308
+ });
309
+ }
310
+ if (usedEntryIds.has(entryId)) {
311
+ const before = entryId;
312
+ entryId = (0, import_core2.uniqueIdentifier)(entryId, usedEntryIds);
313
+ warnings.push({
314
+ path: item.key,
315
+ message: "disambiguated a duplicate entry id",
316
+ before,
317
+ after: entryId
318
+ });
319
+ } else {
320
+ usedEntryIds.add(entryId);
321
+ }
322
+ let group = groups.get(groupPath);
323
+ if (group === void 0) {
324
+ group = {
325
+ path: groupPath,
326
+ line: 0,
327
+ emotion: [],
328
+ entries: [],
329
+ extensions: (0, import_core2.createExtensions)()
330
+ };
331
+ groups.set(groupPath, group);
332
+ document.groups.push(group);
333
+ }
334
+ const target = normalized.valueRole === "target" ? item.value : void 0;
335
+ const source = normalized.valueRole === "target" ? item.key : item.value;
336
+ const status = normalized.entryStatus ?? (target !== void 0 && target.length > 0 ? "translated" : "initial");
337
+ group.entries.push({
338
+ id: entryId,
339
+ line: 0,
340
+ source,
341
+ ...target !== void 0 ? { target } : {},
342
+ type: normalized.entryType,
343
+ emotion: [],
344
+ status,
345
+ reference: [],
346
+ extensions: (0, import_core2.createExtensions)()
347
+ });
348
+ }
349
+ const issues = (0, import_core2.validateDocument)(document);
350
+ if ((0, import_core2.hasErrors)(issues)) throw new import_core2.CliffValidationError(issues);
351
+ return { document, warnings };
352
+ }
353
+ function fromFlatJson(value, options) {
354
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
355
+ throw new import_core2.CliffModelError("flat JSON root must be an object");
356
+ }
357
+ const record = value;
358
+ const entries = [];
359
+ for (const key of Object.keys(record)) {
360
+ const item = record[key];
361
+ if (typeof item !== "string") {
362
+ throw new import_core2.CliffModelError(`flat JSON value at '${key}' must be a string`);
363
+ }
364
+ entries.push({ key, value: item });
365
+ }
366
+ return buildDocumentFromEntries(entries, options);
367
+ }
368
+ function entryKey(document, group, id, style) {
369
+ if (style === "canonical") {
370
+ return (0, import_core2.canonicalId)(document.header.namespace, document.header.clan, group.path, id);
371
+ }
372
+ if (style === "group-entry") return group.path.length > 0 ? `${group.path}.${id}` : id;
373
+ return id;
374
+ }
375
+ function toFlatJson(document, options = {}) {
376
+ const style = options.keyStyle ?? "canonical";
377
+ const chosen = options.value ?? "target";
378
+ const fallback = options.fallbackToSource ?? false;
379
+ const result = /* @__PURE__ */ Object.create(null);
380
+ for (const group of document.groups) {
381
+ for (const entry of group.entries) {
382
+ const key = entryKey(document, group, entry.id, style);
383
+ let value = chosen === "target" ? entry.target : entry.source;
384
+ if (value === void 0 && fallback) {
385
+ value = chosen === "target" ? entry.source : entry.target;
386
+ }
387
+ if (value === void 0) continue;
388
+ if (Object.hasOwn(result, key)) {
389
+ throw new import_core2.CliffModelError(`flat JSON key collision at '${key}'`);
390
+ }
391
+ result[key] = value;
392
+ }
393
+ }
394
+ return result;
395
+ }
396
+
397
+ // src/nested.ts
398
+ var import_core3 = require("@cliff-format/core");
399
+ function isRecord2(value) {
400
+ return typeof value === "object" && value !== null && !Array.isArray(value);
401
+ }
402
+ function fromNestedJson(value, options) {
403
+ if (!isRecord2(value)) throw new import_core3.CliffModelError("nested JSON root must be an object");
404
+ const maxDepth = options.maxDepth ?? 64;
405
+ const maxEntries = options.maxEntries ?? 1e6;
406
+ const separator = options.keySeparator ?? ".";
407
+ const entries = [];
408
+ const walk = (object, prefix, depth) => {
409
+ if (depth > maxDepth) throw new import_core3.CliffModelError("nested JSON exceeds the depth limit");
410
+ for (const key of Object.keys(object)) {
411
+ if (key.length === 0) throw new import_core3.CliffModelError("nested JSON has an empty key");
412
+ if (separator.length > 0 && key.includes(separator)) {
413
+ throw new import_core3.CliffModelError(
414
+ `nested JSON key '${key}' contains the key separator '${separator}'`
415
+ );
416
+ }
417
+ const item = object[key];
418
+ const path = prefix.concat(key);
419
+ if (isRecord2(item)) {
420
+ walk(item, path, depth + 1);
421
+ } else if (typeof item === "string") {
422
+ entries.push({ key: path.join(separator), value: item });
423
+ if (entries.length > maxEntries) {
424
+ throw new import_core3.CliffModelError("nested JSON exceeds the entry limit");
425
+ }
426
+ } else {
427
+ throw new import_core3.CliffModelError(`nested JSON leaf '${path.join(separator)}' must be a string`);
428
+ }
429
+ }
430
+ };
431
+ walk(value, [], 1);
432
+ return buildDocumentFromEntries(entries, { ...options, maxDepth, maxEntries });
433
+ }
434
+ function createObject() {
435
+ return /* @__PURE__ */ Object.create(null);
436
+ }
437
+ function toNestedJson(document, options = {}) {
438
+ const chosen = options.value ?? "target";
439
+ const fallback = options.fallbackToSource ?? false;
440
+ const root = createObject();
441
+ for (const group of document.groups) {
442
+ const segments = group.path.split(".").filter((segment) => segment.length > 0);
443
+ let current = root;
444
+ for (const segment of segments) {
445
+ const existing = current[segment];
446
+ if (existing === void 0) {
447
+ const next = createObject();
448
+ current[segment] = next;
449
+ current = next;
450
+ } else if (isRecord2(existing)) {
451
+ current = existing;
452
+ } else {
453
+ throw new import_core3.CliffModelError(`nested JSON collision at '${segment}'`);
454
+ }
455
+ }
456
+ for (const entry of group.entries) {
457
+ let value = chosen === "target" ? entry.target : entry.source;
458
+ if (value === void 0 && fallback) {
459
+ value = chosen === "target" ? entry.source : entry.target;
460
+ }
461
+ if (value === void 0) continue;
462
+ if (Object.hasOwn(current, entry.id)) {
463
+ throw new import_core3.CliffModelError(`nested JSON key collision at '${entry.id}'`);
464
+ }
465
+ current[entry.id] = value;
466
+ }
467
+ }
468
+ return root;
469
+ }
470
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cli-json.ts","../src/flat.ts","../src/nested.ts"],"sourcesContent":["export * from \"./cli-json.js\";\nexport * from \"./flat.js\";\nexport * from \"./nested.js\";\nexport * from \"./result.js\";\n","import {\n type CliffDocument,\n type CliffEntry,\n type CliffGroup,\n CliffModelError,\n createDocument,\n createExtensions,\n type SpecVersion,\n} from \"@cliff-format/core\";\n\nexport interface CliJsonObject {\n \"spec-version\": SpecVersion;\n header: Record<string, unknown>;\n groups: unknown[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction ownString(value: Record<string, unknown>, key: string, label: string): string {\n const result = value[key];\n if (typeof result !== \"string\") {\n throw new CliffModelError(`${label} must be a string`);\n }\n return result;\n}\n\nfunction optionalString(value: Record<string, unknown>, key: string): string | undefined {\n const result = value[key];\n if (result === undefined || result === null) return undefined;\n if (typeof result !== \"string\") throw new CliffModelError(`${key} must be a string`);\n return result;\n}\n\nfunction stringArray(value: unknown, label: string): string[] {\n if (value === undefined || value === null) return [];\n if (!Array.isArray(value) || value.some((item) => typeof item !== \"string\")) {\n throw new CliffModelError(`${label} must be an array of strings`);\n }\n return value.slice() as string[];\n}\n\nfunction extensions(value: unknown, label: string): Record<string, string> {\n if (value === undefined || value === null) return createExtensions();\n if (!isRecord(value)) throw new CliffModelError(`${label} must be an object`);\n const result = createExtensions();\n for (const key of Object.keys(value)) {\n const item = value[key];\n if (typeof item !== \"string\") {\n throw new CliffModelError(`${label}.${key} must be a string`);\n }\n result[key] = item;\n }\n return result;\n}\n\nfunction pickHeaderString(header: Record<string, unknown>, keys: string[], label: string): string {\n for (const key of keys) {\n const value = header[key];\n if (typeof value === \"string\" && value.length > 0) return value;\n }\n throw new CliffModelError(`CLIFF-shaped input is missing required header field '${label}'`);\n}\n\nexport function isCliffObject(value: unknown): value is CliJsonObject {\n return isRecord(value) && isRecord(value.header) && Array.isArray(value.groups);\n}\n\nexport function toCliJson(document: CliffDocument): CliJsonObject {\n const header = document.header;\n const headerData: Record<string, unknown> = {\n namespace: header.namespace,\n clan: header.clan,\n \"source-language\": header.sourceLanguage,\n \"target-language\": header.targetLanguage,\n };\n if (header.version !== undefined) headerData.version = header.version;\n if (header.variant !== \"standard\") headerData.variant = header.variant;\n if (header.title !== undefined) headerData.title = header.title;\n if (header.info !== undefined) headerData.info = header.info;\n if (header.standard !== undefined) headerData.standard = header.standard;\n if (header.dependency.length > 0) headerData.dependency = [...header.dependency];\n if (Object.keys(header.extensions).length > 0) headerData.extensions = { ...header.extensions };\n\n const groups = document.groups.map((group) => {\n const groupData: Record<string, unknown> = { path: group.path };\n if (group.context !== undefined) groupData.context = group.context;\n if (group.type !== undefined) groupData.type = group.type;\n if (group.emotion.length > 0) groupData.emotion = [...group.emotion];\n if (group.maxWidth !== undefined) groupData[\"max-width\"] = group.maxWidth;\n if (Object.keys(group.extensions).length > 0) {\n groupData.extensions = { ...group.extensions };\n }\n groupData.entries = group.entries.map((entry) => {\n const entryData: Record<string, unknown> = { id: entry.id };\n if (entry.source !== undefined) entryData.source = entry.source;\n if (entry.target !== undefined) entryData.target = entry.target;\n if (entry.type !== undefined) entryData.type = entry.type;\n if (entry.emotion.length > 0) entryData.emotion = [...entry.emotion];\n if (entry.status !== undefined) entryData.status = entry.status;\n if (entry.context !== undefined) entryData.context = entry.context;\n if (entry.maxWidth !== undefined) entryData[\"max-width\"] = entry.maxWidth;\n if (entry.reference.length > 0) entryData.reference = [...entry.reference];\n if (entry.reviewer !== undefined) entryData.reviewer = entry.reviewer;\n if (Object.keys(entry.extensions).length > 0) {\n entryData.extensions = { ...entry.extensions };\n }\n return entryData;\n });\n return groupData;\n });\n\n return { \"spec-version\": document.specVersion, header: headerData, groups };\n}\n\nexport function toJsonString(document: CliffDocument, space = 2): string {\n return JSON.stringify(toCliJson(document), null, space);\n}\n\nexport function fromCliJson(value: unknown): CliffDocument {\n if (!isCliffObject(value)) {\n throw new CliffModelError(\"input is not a CLIFF-shaped object\");\n }\n const headerData = value.header;\n const document = createDocument(\n value[\"spec-version\"] === \"1.0\" ? \"1.0\" : value[\"spec-version\"] === \"1.1\" ? \"1.1\" : \"1.1\",\n );\n if (\n value[\"spec-version\"] !== undefined &&\n value[\"spec-version\"] !== \"1.0\" &&\n value[\"spec-version\"] !== \"1.1\"\n ) {\n throw new CliffModelError(`unsupported spec-version '${String(value[\"spec-version\"])}'`);\n }\n document.header.namespace = pickHeaderString(headerData, [\"namespace\"], \"namespace\");\n document.header.clan = pickHeaderString(headerData, [\"clan\"], \"clan\");\n document.header.sourceLanguage = pickHeaderString(\n headerData,\n [\"source-language\", \"source_language\", \"sourceLanguage\"],\n \"source-language\",\n );\n document.header.targetLanguage = pickHeaderString(\n headerData,\n [\"target-language\", \"target_language\", \"targetLanguage\"],\n \"target-language\",\n );\n const version = optionalString(headerData, \"version\");\n if (version !== undefined) document.header.version = version;\n const variant = optionalString(headerData, \"variant\");\n if (variant !== undefined) document.header.variant = variant;\n const title = optionalString(headerData, \"title\");\n if (title !== undefined) document.header.title = title;\n const info = optionalString(headerData, \"info\");\n if (info !== undefined) document.header.info = info;\n const standard = optionalString(headerData, \"standard\");\n if (standard !== undefined) document.header.standard = standard;\n document.header.dependency = stringArray(headerData.dependency, \"header.dependency\");\n document.header.extensions = extensions(headerData.extensions, \"header.extensions\");\n\n for (const rawGroup of value.groups) {\n if (!isRecord(rawGroup)) throw new CliffModelError(\"each group must be an object\");\n const path = ownString(rawGroup, \"path\", \"group.path\");\n const group: CliffGroup = {\n path,\n line: 0,\n emotion: stringArray(rawGroup.emotion, \"group.emotion\"),\n entries: [],\n extensions: extensions(rawGroup.extensions, \"group.extensions\"),\n };\n const groupContext = optionalString(rawGroup, \"context\");\n if (groupContext !== undefined) group.context = groupContext;\n const groupType = optionalString(rawGroup, \"type\");\n if (groupType !== undefined) group.type = groupType;\n const groupWidth = rawGroup[\"max-width\"];\n if (groupWidth !== undefined && groupWidth !== null) {\n if (typeof groupWidth !== \"number\" || !Number.isInteger(groupWidth)) {\n throw new CliffModelError(\"group.max-width must be an integer\");\n }\n group.maxWidth = groupWidth;\n }\n const rawEntries = rawGroup.entries;\n if (!Array.isArray(rawEntries)) throw new CliffModelError(\"group.entries must be an array\");\n for (const rawEntry of rawEntries) {\n if (!isRecord(rawEntry)) throw new CliffModelError(\"each entry must be an object\");\n const entry: CliffEntry = {\n id: ownString(rawEntry, \"id\", \"entry.id\"),\n line: 0,\n emotion: stringArray(rawEntry.emotion, \"entry.emotion\"),\n reference: stringArray(rawEntry.reference, \"entry.reference\"),\n extensions: extensions(rawEntry.extensions, \"entry.extensions\"),\n };\n for (const key of [\"source\", \"target\", \"type\", \"status\", \"context\", \"reviewer\"] as const) {\n const item = optionalString(rawEntry, key);\n if (item !== undefined) entry[key] = item;\n }\n const entryWidth = rawEntry[\"max-width\"];\n if (entryWidth !== undefined && entryWidth !== null) {\n if (typeof entryWidth !== \"number\" || !Number.isInteger(entryWidth)) {\n throw new CliffModelError(\"entry.max-width must be an integer\");\n }\n entry.maxWidth = entryWidth;\n }\n group.entries.push(entry);\n }\n document.groups.push(group);\n }\n return document;\n}\n\nexport function fromJsonString(text: string): CliffDocument {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch (cause) {\n throw new CliffModelError(\"invalid JSON\", { cause });\n }\n return fromCliJson(value);\n}\n","import {\n type CliffDocument,\n type CliffGroup,\n CliffModelError,\n CliffValidationError,\n canonicalId,\n createDocument,\n createExtensions,\n hasErrors,\n isName,\n LANG_RE,\n normalizeIdentifier,\n uniqueIdentifier,\n validateDocument,\n} from \"@cliff-format/core\";\nimport type { ConversionWarning, ConvertResult } from \"./result.js\";\n\nexport interface JsonImportOptions {\n namespace: string;\n clan: string;\n sourceLanguage: string;\n targetLanguage: string;\n valueRole?: \"target\" | \"source\";\n entryType?: string;\n entryStatus?: string;\n keySeparator?: string;\n maxEntries?: number;\n maxDepth?: number;\n}\n\nexport interface JsonExportOptions {\n keyStyle?: \"canonical\" | \"group-entry\" | \"entry\";\n value?: \"target\" | \"source\";\n fallbackToSource?: boolean;\n}\n\nexport interface FlatEntry {\n key: string;\n value: string;\n}\n\nfunction requireName(value: string, label: string): void {\n if (!isName(value)) throw new CliffModelError(`${label} must be a valid CLIFF name`);\n}\n\nfunction requireLanguage(value: string, label: string): void {\n if (!LANG_RE.test(value)) {\n throw new CliffModelError(`${label} must be a plausible BCP 47 language tag`);\n }\n}\n\nfunction normalizedOptions(options: JsonImportOptions) {\n requireName(options.namespace, \"namespace\");\n requireName(options.clan, \"clan\");\n requireLanguage(options.sourceLanguage, \"sourceLanguage\");\n requireLanguage(options.targetLanguage, \"targetLanguage\");\n const maxEntries = options.maxEntries ?? 1_000_000;\n const maxDepth = options.maxDepth ?? 64;\n if (!Number.isSafeInteger(maxEntries) || maxEntries <= 0) {\n throw new RangeError(\"maxEntries must be a positive safe integer\");\n }\n if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) {\n throw new RangeError(\"maxDepth must be a positive safe integer\");\n }\n return {\n namespace: options.namespace,\n clan: options.clan,\n sourceLanguage: options.sourceLanguage,\n targetLanguage: options.targetLanguage,\n valueRole: options.valueRole ?? \"target\",\n entryType: options.entryType ?? \"sentence\",\n entryStatus: options.entryStatus,\n keySeparator: options.keySeparator ?? \".\",\n maxEntries,\n maxDepth,\n };\n}\n\nexport function buildDocumentFromEntries(\n entries: readonly FlatEntry[],\n options: JsonImportOptions,\n): ConvertResult {\n const normalized = normalizedOptions(options);\n if (normalized.keySeparator.length === 0) {\n throw new CliffModelError(\"keySeparator must not be empty\");\n }\n if (entries.length > normalized.maxEntries) {\n throw new CliffModelError(\n `JSON contains ${entries.length} entries, which exceeds the limit of ${normalized.maxEntries}`,\n );\n }\n\n const document = createDocument();\n document.header.namespace = normalized.namespace;\n document.header.clan = normalized.clan;\n document.header.sourceLanguage = normalized.sourceLanguage;\n document.header.targetLanguage = normalized.targetLanguage;\n const warnings: ConversionWarning[] = [];\n const groups = new Map<string, CliffGroup>();\n const usedEntryIds = new Set<string>();\n\n for (const item of entries) {\n if (item.value.length === 0) {\n warnings.push({ path: item.key, message: \"empty value\" });\n }\n const parts = item.key.split(normalized.keySeparator);\n if (parts.length === 0 || parts.some((part) => part.length === 0)) {\n throw new CliffModelError(`JSON key '${item.key}' has an empty path segment`);\n }\n const rawEntry = parts[parts.length - 1] as string;\n const normalizedGroupParts = parts.slice(0, -1).map((part) => {\n const value = normalizeIdentifier(part, \"group\");\n if (value !== part) {\n warnings.push({\n path: item.key,\n message: \"normalized a group path segment\",\n before: part,\n after: value,\n });\n }\n return value;\n });\n const groupPath = normalizedGroupParts.length > 0 ? normalizedGroupParts.join(\".\") : \"imported\";\n let entryId = normalizeIdentifier(rawEntry, \"entry\");\n if (entryId !== rawEntry) {\n warnings.push({\n path: item.key,\n message: \"normalized an entry id\",\n before: rawEntry,\n after: entryId,\n });\n }\n if (usedEntryIds.has(entryId)) {\n const before = entryId;\n entryId = uniqueIdentifier(entryId, usedEntryIds);\n warnings.push({\n path: item.key,\n message: \"disambiguated a duplicate entry id\",\n before,\n after: entryId,\n });\n } else {\n usedEntryIds.add(entryId);\n }\n\n let group = groups.get(groupPath);\n if (group === undefined) {\n group = {\n path: groupPath,\n line: 0,\n emotion: [],\n entries: [],\n extensions: createExtensions(),\n };\n groups.set(groupPath, group);\n document.groups.push(group);\n }\n\n const target = normalized.valueRole === \"target\" ? item.value : undefined;\n const source = normalized.valueRole === \"target\" ? item.key : item.value;\n const status =\n normalized.entryStatus ??\n (target !== undefined && target.length > 0 ? \"translated\" : \"initial\");\n group.entries.push({\n id: entryId,\n line: 0,\n source,\n ...(target !== undefined ? { target } : {}),\n type: normalized.entryType,\n emotion: [],\n status,\n reference: [],\n extensions: createExtensions(),\n });\n }\n\n const issues = validateDocument(document);\n if (hasErrors(issues)) throw new CliffValidationError(issues);\n return { document, warnings };\n}\n\nexport function fromFlatJson(value: unknown, options: JsonImportOptions): ConvertResult {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new CliffModelError(\"flat JSON root must be an object\");\n }\n const record = value as Record<string, unknown>;\n const entries: FlatEntry[] = [];\n for (const key of Object.keys(record)) {\n const item = record[key];\n if (typeof item !== \"string\") {\n throw new CliffModelError(`flat JSON value at '${key}' must be a string`);\n }\n entries.push({ key, value: item });\n }\n return buildDocumentFromEntries(entries, options);\n}\n\nfunction entryKey(document: CliffDocument, group: CliffGroup, id: string, style: string): string {\n if (style === \"canonical\") {\n return canonicalId(document.header.namespace, document.header.clan, group.path, id);\n }\n if (style === \"group-entry\") return group.path.length > 0 ? `${group.path}.${id}` : id;\n return id;\n}\n\nexport function toFlatJson(\n document: CliffDocument,\n options: JsonExportOptions = {},\n): Record<string, string> {\n const style = options.keyStyle ?? \"canonical\";\n const chosen = options.value ?? \"target\";\n const fallback = options.fallbackToSource ?? false;\n const result = Object.create(null) as Record<string, string>;\n for (const group of document.groups) {\n for (const entry of group.entries) {\n const key = entryKey(document, group, entry.id, style);\n let value = chosen === \"target\" ? entry.target : entry.source;\n if (value === undefined && fallback) {\n value = chosen === \"target\" ? entry.source : entry.target;\n }\n if (value === undefined) continue;\n if (Object.hasOwn(result, key)) {\n throw new CliffModelError(`flat JSON key collision at '${key}'`);\n }\n result[key] = value;\n }\n }\n return result;\n}\n","import { type CliffDocument, CliffModelError } from \"@cliff-format/core\";\nimport {\n buildDocumentFromEntries,\n type FlatEntry,\n type JsonExportOptions,\n type JsonImportOptions,\n} from \"./flat.js\";\nimport type { ConvertResult } from \"./result.js\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function fromNestedJson(value: unknown, options: JsonImportOptions): ConvertResult {\n if (!isRecord(value)) throw new CliffModelError(\"nested JSON root must be an object\");\n const maxDepth = options.maxDepth ?? 64;\n const maxEntries = options.maxEntries ?? 1_000_000;\n const separator = options.keySeparator ?? \".\";\n const entries: FlatEntry[] = [];\n const walk = (object: Record<string, unknown>, prefix: string[], depth: number): void => {\n if (depth > maxDepth) throw new CliffModelError(\"nested JSON exceeds the depth limit\");\n for (const key of Object.keys(object)) {\n if (key.length === 0) throw new CliffModelError(\"nested JSON has an empty key\");\n if (separator.length > 0 && key.includes(separator)) {\n throw new CliffModelError(\n `nested JSON key '${key}' contains the key separator '${separator}'`,\n );\n }\n const item = object[key];\n const path = prefix.concat(key);\n if (isRecord(item)) {\n walk(item, path, depth + 1);\n } else if (typeof item === \"string\") {\n entries.push({ key: path.join(separator), value: item });\n if (entries.length > maxEntries) {\n throw new CliffModelError(\"nested JSON exceeds the entry limit\");\n }\n } else {\n throw new CliffModelError(`nested JSON leaf '${path.join(separator)}' must be a string`);\n }\n }\n };\n walk(value, [], 1);\n return buildDocumentFromEntries(entries, { ...options, maxDepth, maxEntries });\n}\n\nfunction createObject(): Record<string, unknown> {\n return Object.create(null) as Record<string, unknown>;\n}\n\nexport function toNestedJson(\n document: CliffDocument,\n options: JsonExportOptions = {},\n): Record<string, unknown> {\n const chosen = options.value ?? \"target\";\n const fallback = options.fallbackToSource ?? false;\n const root = createObject();\n for (const group of document.groups) {\n const segments = group.path.split(\".\").filter((segment) => segment.length > 0);\n let current = root;\n for (const segment of segments) {\n const existing = current[segment];\n if (existing === undefined) {\n const next = createObject();\n current[segment] = next;\n current = next;\n } else if (isRecord(existing)) {\n current = existing;\n } else {\n throw new CliffModelError(`nested JSON collision at '${segment}'`);\n }\n }\n for (const entry of group.entries) {\n let value = chosen === \"target\" ? entry.target : entry.source;\n if (value === undefined && fallback) {\n value = chosen === \"target\" ? entry.source : entry.target;\n }\n if (value === undefined) continue;\n if (Object.hasOwn(current, entry.id)) {\n throw new CliffModelError(`nested JSON key collision at '${entry.id}'`);\n }\n current[entry.id] = value;\n }\n }\n return root;\n}\n"],"mappings":"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","names":["import_core","import_core","isRecord"]}