@cliff-format/json 0.0.0-stage → 0.1.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.
@@ -0,0 +1,54 @@
1
+ import * as _cliff_format_core from '@cliff-format/core';
2
+ import { SpecVersion, CliffDocument } from '@cliff-format/core';
3
+
4
+ interface CliJsonObject {
5
+ "spec-version": SpecVersion;
6
+ header: Record<string, unknown>;
7
+ groups: unknown[];
8
+ }
9
+ declare function isCliffObject(value: unknown): value is CliJsonObject;
10
+ declare function toCliJson(document: CliffDocument): CliJsonObject;
11
+ declare function toJsonString(document: CliffDocument, space?: number): string;
12
+ declare function fromCliJson(value: unknown): CliffDocument;
13
+ declare function fromJsonString(text: string): CliffDocument;
14
+
15
+ interface ConversionWarning {
16
+ path: string;
17
+ message: string;
18
+ before?: string;
19
+ after?: string;
20
+ }
21
+ interface ConvertResult {
22
+ document: _cliff_format_core.CliffDocument;
23
+ warnings: ConversionWarning[];
24
+ }
25
+
26
+ interface JsonImportOptions {
27
+ namespace: string;
28
+ clan: string;
29
+ sourceLanguage: string;
30
+ targetLanguage: string;
31
+ valueRole?: "target" | "source";
32
+ entryType?: string;
33
+ entryStatus?: string;
34
+ keySeparator?: string;
35
+ maxEntries?: number;
36
+ maxDepth?: number;
37
+ }
38
+ interface JsonExportOptions {
39
+ keyStyle?: "canonical" | "group-entry" | "entry";
40
+ value?: "target" | "source";
41
+ fallbackToSource?: boolean;
42
+ }
43
+ interface FlatEntry {
44
+ key: string;
45
+ value: string;
46
+ }
47
+ declare function buildDocumentFromEntries(entries: readonly FlatEntry[], options: JsonImportOptions): ConvertResult;
48
+ declare function fromFlatJson(value: unknown, options: JsonImportOptions): ConvertResult;
49
+ declare function toFlatJson(document: CliffDocument, options?: JsonExportOptions): Record<string, string>;
50
+
51
+ declare function fromNestedJson(value: unknown, options: JsonImportOptions): ConvertResult;
52
+ declare function toNestedJson(document: CliffDocument, options?: JsonExportOptions): Record<string, unknown>;
53
+
54
+ export { type CliJsonObject, type ConversionWarning, type ConvertResult, type FlatEntry, type JsonExportOptions, type JsonImportOptions, buildDocumentFromEntries, fromCliJson, fromFlatJson, fromJsonString, fromNestedJson, isCliffObject, toCliJson, toFlatJson, toJsonString, toNestedJson };
@@ -0,0 +1,54 @@
1
+ import * as _cliff_format_core from '@cliff-format/core';
2
+ import { SpecVersion, CliffDocument } from '@cliff-format/core';
3
+
4
+ interface CliJsonObject {
5
+ "spec-version": SpecVersion;
6
+ header: Record<string, unknown>;
7
+ groups: unknown[];
8
+ }
9
+ declare function isCliffObject(value: unknown): value is CliJsonObject;
10
+ declare function toCliJson(document: CliffDocument): CliJsonObject;
11
+ declare function toJsonString(document: CliffDocument, space?: number): string;
12
+ declare function fromCliJson(value: unknown): CliffDocument;
13
+ declare function fromJsonString(text: string): CliffDocument;
14
+
15
+ interface ConversionWarning {
16
+ path: string;
17
+ message: string;
18
+ before?: string;
19
+ after?: string;
20
+ }
21
+ interface ConvertResult {
22
+ document: _cliff_format_core.CliffDocument;
23
+ warnings: ConversionWarning[];
24
+ }
25
+
26
+ interface JsonImportOptions {
27
+ namespace: string;
28
+ clan: string;
29
+ sourceLanguage: string;
30
+ targetLanguage: string;
31
+ valueRole?: "target" | "source";
32
+ entryType?: string;
33
+ entryStatus?: string;
34
+ keySeparator?: string;
35
+ maxEntries?: number;
36
+ maxDepth?: number;
37
+ }
38
+ interface JsonExportOptions {
39
+ keyStyle?: "canonical" | "group-entry" | "entry";
40
+ value?: "target" | "source";
41
+ fallbackToSource?: boolean;
42
+ }
43
+ interface FlatEntry {
44
+ key: string;
45
+ value: string;
46
+ }
47
+ declare function buildDocumentFromEntries(entries: readonly FlatEntry[], options: JsonImportOptions): ConvertResult;
48
+ declare function fromFlatJson(value: unknown, options: JsonImportOptions): ConvertResult;
49
+ declare function toFlatJson(document: CliffDocument, options?: JsonExportOptions): Record<string, string>;
50
+
51
+ declare function fromNestedJson(value: unknown, options: JsonImportOptions): ConvertResult;
52
+ declare function toNestedJson(document: CliffDocument, options?: JsonExportOptions): Record<string, unknown>;
53
+
54
+ export { type CliJsonObject, type ConversionWarning, type ConvertResult, type FlatEntry, type JsonExportOptions, type JsonImportOptions, buildDocumentFromEntries, fromCliJson, fromFlatJson, fromJsonString, fromNestedJson, isCliffObject, toCliJson, toFlatJson, toJsonString, toNestedJson };
package/dist/index.js ADDED
@@ -0,0 +1,463 @@
1
+ // src/cli-json.ts
2
+ import {
3
+ CliffModelError,
4
+ createDocument,
5
+ createExtensions
6
+ } from "@cliff-format/core";
7
+ function isRecord(value) {
8
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9
+ }
10
+ function ownString(value, key, label) {
11
+ const result = value[key];
12
+ if (typeof result !== "string") {
13
+ throw new CliffModelError(`${label} must be a string`);
14
+ }
15
+ return result;
16
+ }
17
+ function optionalString(value, key) {
18
+ const result = value[key];
19
+ if (result === void 0 || result === null) return void 0;
20
+ if (typeof result !== "string") throw new CliffModelError(`${key} must be a string`);
21
+ return result;
22
+ }
23
+ function stringArray(value, label) {
24
+ if (value === void 0 || value === null) return [];
25
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
26
+ throw new CliffModelError(`${label} must be an array of strings`);
27
+ }
28
+ return value.slice();
29
+ }
30
+ function extensions(value, label) {
31
+ if (value === void 0 || value === null) return createExtensions();
32
+ if (!isRecord(value)) throw new CliffModelError(`${label} must be an object`);
33
+ const result = createExtensions();
34
+ for (const key of Object.keys(value)) {
35
+ const item = value[key];
36
+ if (typeof item !== "string") {
37
+ throw new CliffModelError(`${label}.${key} must be a string`);
38
+ }
39
+ result[key] = item;
40
+ }
41
+ return result;
42
+ }
43
+ function pickHeaderString(header, keys, label) {
44
+ for (const key of keys) {
45
+ const value = header[key];
46
+ if (typeof value === "string" && value.length > 0) return value;
47
+ }
48
+ throw new CliffModelError(`CLIFF-shaped input is missing required header field '${label}'`);
49
+ }
50
+ function isCliffObject(value) {
51
+ return isRecord(value) && isRecord(value.header) && Array.isArray(value.groups);
52
+ }
53
+ function toCliJson(document) {
54
+ const header = document.header;
55
+ const headerData = {
56
+ namespace: header.namespace,
57
+ clan: header.clan,
58
+ "source-language": header.sourceLanguage,
59
+ "target-language": header.targetLanguage
60
+ };
61
+ if (header.version !== void 0) headerData.version = header.version;
62
+ if (header.variant !== "standard") headerData.variant = header.variant;
63
+ if (header.title !== void 0) headerData.title = header.title;
64
+ if (header.info !== void 0) headerData.info = header.info;
65
+ if (header.standard !== void 0) headerData.standard = header.standard;
66
+ if (header.dependency.length > 0) headerData.dependency = [...header.dependency];
67
+ if (Object.keys(header.extensions).length > 0) headerData.extensions = { ...header.extensions };
68
+ const groups = document.groups.map((group) => {
69
+ const groupData = { path: group.path };
70
+ if (group.context !== void 0) groupData.context = group.context;
71
+ if (group.type !== void 0) groupData.type = group.type;
72
+ if (group.emotion.length > 0) groupData.emotion = [...group.emotion];
73
+ if (group.maxWidth !== void 0) groupData["max-width"] = group.maxWidth;
74
+ if (Object.keys(group.extensions).length > 0) {
75
+ groupData.extensions = { ...group.extensions };
76
+ }
77
+ groupData.entries = group.entries.map((entry) => {
78
+ const entryData = { id: entry.id };
79
+ if (entry.source !== void 0) entryData.source = entry.source;
80
+ if (entry.target !== void 0) entryData.target = entry.target;
81
+ if (entry.type !== void 0) entryData.type = entry.type;
82
+ if (entry.emotion.length > 0) entryData.emotion = [...entry.emotion];
83
+ if (entry.status !== void 0) entryData.status = entry.status;
84
+ if (entry.context !== void 0) entryData.context = entry.context;
85
+ if (entry.maxWidth !== void 0) entryData["max-width"] = entry.maxWidth;
86
+ if (entry.reference.length > 0) entryData.reference = [...entry.reference];
87
+ if (entry.reviewer !== void 0) entryData.reviewer = entry.reviewer;
88
+ if (Object.keys(entry.extensions).length > 0) {
89
+ entryData.extensions = { ...entry.extensions };
90
+ }
91
+ return entryData;
92
+ });
93
+ return groupData;
94
+ });
95
+ return { "spec-version": document.specVersion, header: headerData, groups };
96
+ }
97
+ function toJsonString(document, space = 2) {
98
+ return JSON.stringify(toCliJson(document), null, space);
99
+ }
100
+ function fromCliJson(value) {
101
+ if (!isCliffObject(value)) {
102
+ throw new CliffModelError("input is not a CLIFF-shaped object");
103
+ }
104
+ const headerData = value.header;
105
+ const document = createDocument(
106
+ value["spec-version"] === "1.0" ? "1.0" : value["spec-version"] === "1.1" ? "1.1" : "1.1"
107
+ );
108
+ if (value["spec-version"] !== void 0 && value["spec-version"] !== "1.0" && value["spec-version"] !== "1.1") {
109
+ throw new CliffModelError(`unsupported spec-version '${String(value["spec-version"])}'`);
110
+ }
111
+ document.header.namespace = pickHeaderString(headerData, ["namespace"], "namespace");
112
+ document.header.clan = pickHeaderString(headerData, ["clan"], "clan");
113
+ document.header.sourceLanguage = pickHeaderString(
114
+ headerData,
115
+ ["source-language", "source_language", "sourceLanguage"],
116
+ "source-language"
117
+ );
118
+ document.header.targetLanguage = pickHeaderString(
119
+ headerData,
120
+ ["target-language", "target_language", "targetLanguage"],
121
+ "target-language"
122
+ );
123
+ const version = optionalString(headerData, "version");
124
+ if (version !== void 0) document.header.version = version;
125
+ const variant = optionalString(headerData, "variant");
126
+ if (variant !== void 0) document.header.variant = variant;
127
+ const title = optionalString(headerData, "title");
128
+ if (title !== void 0) document.header.title = title;
129
+ const info = optionalString(headerData, "info");
130
+ if (info !== void 0) document.header.info = info;
131
+ const standard = optionalString(headerData, "standard");
132
+ if (standard !== void 0) document.header.standard = standard;
133
+ document.header.dependency = stringArray(headerData.dependency, "header.dependency");
134
+ document.header.extensions = extensions(headerData.extensions, "header.extensions");
135
+ for (const rawGroup of value.groups) {
136
+ if (!isRecord(rawGroup)) throw new CliffModelError("each group must be an object");
137
+ const path = ownString(rawGroup, "path", "group.path");
138
+ const group = {
139
+ path,
140
+ line: 0,
141
+ emotion: stringArray(rawGroup.emotion, "group.emotion"),
142
+ entries: [],
143
+ extensions: extensions(rawGroup.extensions, "group.extensions")
144
+ };
145
+ const groupContext = optionalString(rawGroup, "context");
146
+ if (groupContext !== void 0) group.context = groupContext;
147
+ const groupType = optionalString(rawGroup, "type");
148
+ if (groupType !== void 0) group.type = groupType;
149
+ const groupWidth = rawGroup["max-width"];
150
+ if (groupWidth !== void 0 && groupWidth !== null) {
151
+ if (typeof groupWidth !== "number" || !Number.isInteger(groupWidth)) {
152
+ throw new CliffModelError("group.max-width must be an integer");
153
+ }
154
+ group.maxWidth = groupWidth;
155
+ }
156
+ const rawEntries = rawGroup.entries;
157
+ if (!Array.isArray(rawEntries)) throw new CliffModelError("group.entries must be an array");
158
+ for (const rawEntry of rawEntries) {
159
+ if (!isRecord(rawEntry)) throw new CliffModelError("each entry must be an object");
160
+ const entry = {
161
+ id: ownString(rawEntry, "id", "entry.id"),
162
+ line: 0,
163
+ emotion: stringArray(rawEntry.emotion, "entry.emotion"),
164
+ reference: stringArray(rawEntry.reference, "entry.reference"),
165
+ extensions: extensions(rawEntry.extensions, "entry.extensions")
166
+ };
167
+ for (const key of ["source", "target", "type", "status", "context", "reviewer"]) {
168
+ const item = optionalString(rawEntry, key);
169
+ if (item !== void 0) entry[key] = item;
170
+ }
171
+ const entryWidth = rawEntry["max-width"];
172
+ if (entryWidth !== void 0 && entryWidth !== null) {
173
+ if (typeof entryWidth !== "number" || !Number.isInteger(entryWidth)) {
174
+ throw new CliffModelError("entry.max-width must be an integer");
175
+ }
176
+ entry.maxWidth = entryWidth;
177
+ }
178
+ group.entries.push(entry);
179
+ }
180
+ document.groups.push(group);
181
+ }
182
+ return document;
183
+ }
184
+ function fromJsonString(text) {
185
+ let value;
186
+ try {
187
+ value = JSON.parse(text);
188
+ } catch (cause) {
189
+ throw new CliffModelError("invalid JSON", { cause });
190
+ }
191
+ return fromCliJson(value);
192
+ }
193
+
194
+ // src/flat.ts
195
+ import {
196
+ CliffModelError as CliffModelError2,
197
+ CliffValidationError,
198
+ canonicalId,
199
+ createDocument as createDocument2,
200
+ createExtensions as createExtensions2,
201
+ hasErrors,
202
+ isName,
203
+ LANG_RE,
204
+ normalizeIdentifier,
205
+ uniqueIdentifier,
206
+ validateDocument
207
+ } from "@cliff-format/core";
208
+ function requireName(value, label) {
209
+ if (!isName(value)) throw new CliffModelError2(`${label} must be a valid CLIFF name`);
210
+ }
211
+ function requireLanguage(value, label) {
212
+ if (!LANG_RE.test(value)) {
213
+ throw new CliffModelError2(`${label} must be a plausible BCP 47 language tag`);
214
+ }
215
+ }
216
+ function normalizedOptions(options) {
217
+ requireName(options.namespace, "namespace");
218
+ requireName(options.clan, "clan");
219
+ requireLanguage(options.sourceLanguage, "sourceLanguage");
220
+ requireLanguage(options.targetLanguage, "targetLanguage");
221
+ const maxEntries = options.maxEntries ?? 1e6;
222
+ const maxDepth = options.maxDepth ?? 64;
223
+ if (!Number.isSafeInteger(maxEntries) || maxEntries <= 0) {
224
+ throw new RangeError("maxEntries must be a positive safe integer");
225
+ }
226
+ if (!Number.isSafeInteger(maxDepth) || maxDepth <= 0) {
227
+ throw new RangeError("maxDepth must be a positive safe integer");
228
+ }
229
+ return {
230
+ namespace: options.namespace,
231
+ clan: options.clan,
232
+ sourceLanguage: options.sourceLanguage,
233
+ targetLanguage: options.targetLanguage,
234
+ valueRole: options.valueRole ?? "target",
235
+ entryType: options.entryType ?? "sentence",
236
+ entryStatus: options.entryStatus,
237
+ keySeparator: options.keySeparator ?? ".",
238
+ maxEntries,
239
+ maxDepth
240
+ };
241
+ }
242
+ function buildDocumentFromEntries(entries, options) {
243
+ const normalized = normalizedOptions(options);
244
+ if (normalized.keySeparator.length === 0) {
245
+ throw new CliffModelError2("keySeparator must not be empty");
246
+ }
247
+ if (entries.length > normalized.maxEntries) {
248
+ throw new CliffModelError2(
249
+ `JSON contains ${entries.length} entries, which exceeds the limit of ${normalized.maxEntries}`
250
+ );
251
+ }
252
+ const document = createDocument2();
253
+ document.header.namespace = normalized.namespace;
254
+ document.header.clan = normalized.clan;
255
+ document.header.sourceLanguage = normalized.sourceLanguage;
256
+ document.header.targetLanguage = normalized.targetLanguage;
257
+ const warnings = [];
258
+ const groups = /* @__PURE__ */ new Map();
259
+ const usedEntryIds = /* @__PURE__ */ new Set();
260
+ for (const item of entries) {
261
+ if (item.value.length === 0) {
262
+ warnings.push({ path: item.key, message: "empty value" });
263
+ }
264
+ const parts = item.key.split(normalized.keySeparator);
265
+ if (parts.length === 0 || parts.some((part) => part.length === 0)) {
266
+ throw new CliffModelError2(`JSON key '${item.key}' has an empty path segment`);
267
+ }
268
+ const rawEntry = parts[parts.length - 1];
269
+ const normalizedGroupParts = parts.slice(0, -1).map((part) => {
270
+ const value = normalizeIdentifier(part, "group");
271
+ if (value !== part) {
272
+ warnings.push({
273
+ path: item.key,
274
+ message: "normalized a group path segment",
275
+ before: part,
276
+ after: value
277
+ });
278
+ }
279
+ return value;
280
+ });
281
+ const groupPath = normalizedGroupParts.length > 0 ? normalizedGroupParts.join(".") : "imported";
282
+ let entryId = normalizeIdentifier(rawEntry, "entry");
283
+ if (entryId !== rawEntry) {
284
+ warnings.push({
285
+ path: item.key,
286
+ message: "normalized an entry id",
287
+ before: rawEntry,
288
+ after: entryId
289
+ });
290
+ }
291
+ if (usedEntryIds.has(entryId)) {
292
+ const before = entryId;
293
+ entryId = uniqueIdentifier(entryId, usedEntryIds);
294
+ warnings.push({
295
+ path: item.key,
296
+ message: "disambiguated a duplicate entry id",
297
+ before,
298
+ after: entryId
299
+ });
300
+ } else {
301
+ usedEntryIds.add(entryId);
302
+ }
303
+ let group = groups.get(groupPath);
304
+ if (group === void 0) {
305
+ group = {
306
+ path: groupPath,
307
+ line: 0,
308
+ emotion: [],
309
+ entries: [],
310
+ extensions: createExtensions2()
311
+ };
312
+ groups.set(groupPath, group);
313
+ document.groups.push(group);
314
+ }
315
+ const target = normalized.valueRole === "target" ? item.value : void 0;
316
+ const source = normalized.valueRole === "target" ? item.key : item.value;
317
+ const status = normalized.entryStatus ?? (target !== void 0 && target.length > 0 ? "translated" : "initial");
318
+ group.entries.push({
319
+ id: entryId,
320
+ line: 0,
321
+ source,
322
+ ...target !== void 0 ? { target } : {},
323
+ type: normalized.entryType,
324
+ emotion: [],
325
+ status,
326
+ reference: [],
327
+ extensions: createExtensions2()
328
+ });
329
+ }
330
+ const issues = validateDocument(document);
331
+ if (hasErrors(issues)) throw new CliffValidationError(issues);
332
+ return { document, warnings };
333
+ }
334
+ function fromFlatJson(value, options) {
335
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
336
+ throw new CliffModelError2("flat JSON root must be an object");
337
+ }
338
+ const record = value;
339
+ const entries = [];
340
+ for (const key of Object.keys(record)) {
341
+ const item = record[key];
342
+ if (typeof item !== "string") {
343
+ throw new CliffModelError2(`flat JSON value at '${key}' must be a string`);
344
+ }
345
+ entries.push({ key, value: item });
346
+ }
347
+ return buildDocumentFromEntries(entries, options);
348
+ }
349
+ function entryKey(document, group, id, style) {
350
+ if (style === "canonical") {
351
+ return canonicalId(document.header.namespace, document.header.clan, group.path, id);
352
+ }
353
+ if (style === "group-entry") return group.path.length > 0 ? `${group.path}.${id}` : id;
354
+ return id;
355
+ }
356
+ function toFlatJson(document, options = {}) {
357
+ const style = options.keyStyle ?? "canonical";
358
+ const chosen = options.value ?? "target";
359
+ const fallback = options.fallbackToSource ?? false;
360
+ const result = /* @__PURE__ */ Object.create(null);
361
+ for (const group of document.groups) {
362
+ for (const entry of group.entries) {
363
+ const key = entryKey(document, group, entry.id, style);
364
+ let value = chosen === "target" ? entry.target : entry.source;
365
+ if (value === void 0 && fallback) {
366
+ value = chosen === "target" ? entry.source : entry.target;
367
+ }
368
+ if (value === void 0) continue;
369
+ if (Object.hasOwn(result, key)) {
370
+ throw new CliffModelError2(`flat JSON key collision at '${key}'`);
371
+ }
372
+ result[key] = value;
373
+ }
374
+ }
375
+ return result;
376
+ }
377
+
378
+ // src/nested.ts
379
+ import { CliffModelError as CliffModelError3 } from "@cliff-format/core";
380
+ function isRecord2(value) {
381
+ return typeof value === "object" && value !== null && !Array.isArray(value);
382
+ }
383
+ function fromNestedJson(value, options) {
384
+ if (!isRecord2(value)) throw new CliffModelError3("nested JSON root must be an object");
385
+ const maxDepth = options.maxDepth ?? 64;
386
+ const maxEntries = options.maxEntries ?? 1e6;
387
+ const separator = options.keySeparator ?? ".";
388
+ const entries = [];
389
+ const walk = (object, prefix, depth) => {
390
+ if (depth > maxDepth) throw new CliffModelError3("nested JSON exceeds the depth limit");
391
+ for (const key of Object.keys(object)) {
392
+ if (key.length === 0) throw new CliffModelError3("nested JSON has an empty key");
393
+ if (separator.length > 0 && key.includes(separator)) {
394
+ throw new CliffModelError3(
395
+ `nested JSON key '${key}' contains the key separator '${separator}'`
396
+ );
397
+ }
398
+ const item = object[key];
399
+ const path = prefix.concat(key);
400
+ if (isRecord2(item)) {
401
+ walk(item, path, depth + 1);
402
+ } else if (typeof item === "string") {
403
+ entries.push({ key: path.join(separator), value: item });
404
+ if (entries.length > maxEntries) {
405
+ throw new CliffModelError3("nested JSON exceeds the entry limit");
406
+ }
407
+ } else {
408
+ throw new CliffModelError3(`nested JSON leaf '${path.join(separator)}' must be a string`);
409
+ }
410
+ }
411
+ };
412
+ walk(value, [], 1);
413
+ return buildDocumentFromEntries(entries, { ...options, maxDepth, maxEntries });
414
+ }
415
+ function createObject() {
416
+ return /* @__PURE__ */ Object.create(null);
417
+ }
418
+ function toNestedJson(document, options = {}) {
419
+ const chosen = options.value ?? "target";
420
+ const fallback = options.fallbackToSource ?? false;
421
+ const root = createObject();
422
+ for (const group of document.groups) {
423
+ const segments = group.path.split(".").filter((segment) => segment.length > 0);
424
+ let current = root;
425
+ for (const segment of segments) {
426
+ const existing = current[segment];
427
+ if (existing === void 0) {
428
+ const next = createObject();
429
+ current[segment] = next;
430
+ current = next;
431
+ } else if (isRecord2(existing)) {
432
+ current = existing;
433
+ } else {
434
+ throw new CliffModelError3(`nested JSON collision at '${segment}'`);
435
+ }
436
+ }
437
+ for (const entry of group.entries) {
438
+ let value = chosen === "target" ? entry.target : entry.source;
439
+ if (value === void 0 && fallback) {
440
+ value = chosen === "target" ? entry.source : entry.target;
441
+ }
442
+ if (value === void 0) continue;
443
+ if (Object.hasOwn(current, entry.id)) {
444
+ throw new CliffModelError3(`nested JSON key collision at '${entry.id}'`);
445
+ }
446
+ current[entry.id] = value;
447
+ }
448
+ }
449
+ return root;
450
+ }
451
+ export {
452
+ buildDocumentFromEntries,
453
+ fromCliJson,
454
+ fromFlatJson,
455
+ fromJsonString,
456
+ fromNestedJson,
457
+ isCliffObject,
458
+ toCliJson,
459
+ toFlatJson,
460
+ toJsonString,
461
+ toNestedJson
462
+ };
463
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-json.ts","../src/flat.ts","../src/nested.ts"],"sourcesContent":["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":["CliffModelError","createDocument","createExtensions","CliffModelError","isRecord","CliffModelError"]}