@_linked/translation 0.7.0 → 0.8.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.
Files changed (60) hide show
  1. package/README.md +15 -16
  2. package/lib/esm/archive.d.ts +2 -2
  3. package/lib/esm/archive.js +2 -2
  4. package/lib/esm/archive.js.map +1 -1
  5. package/lib/esm/authorization.d.ts +1 -1
  6. package/lib/esm/authorization.js +1 -1
  7. package/lib/esm/authorization.js.map +1 -1
  8. package/lib/esm/cdn-loader.d.ts +1 -1
  9. package/lib/esm/cdn-loader.js.map +1 -1
  10. package/lib/esm/core/messages.d.ts +14 -1
  11. package/lib/esm/core/messages.js +33 -2
  12. package/lib/esm/core/messages.js.map +1 -1
  13. package/lib/esm/exchange.d.ts +1 -1
  14. package/lib/esm/exchange.js +1 -1
  15. package/lib/esm/exchange.js.map +1 -1
  16. package/lib/esm/export.js +4 -1
  17. package/lib/esm/export.js.map +1 -1
  18. package/lib/esm/formats/xliff.js +1 -1
  19. package/lib/esm/formats/xliff.js.map +1 -1
  20. package/lib/esm/key-sync.d.ts +12 -0
  21. package/lib/esm/key-sync.js +32 -7
  22. package/lib/esm/key-sync.js.map +1 -1
  23. package/lib/esm/mt.js +1 -1
  24. package/lib/esm/mt.js.map +1 -1
  25. package/lib/esm/ontologies/translation.d.ts +3 -1
  26. package/lib/esm/ontologies/translation.js +4 -2
  27. package/lib/esm/ontologies/translation.js.map +1 -1
  28. package/lib/esm/react.d.ts +38 -2
  29. package/lib/esm/react.js +98 -15
  30. package/lib/esm/react.js.map +1 -1
  31. package/lib/esm/records.d.ts +2 -1
  32. package/lib/esm/records.js.map +1 -1
  33. package/lib/esm/release.d.ts +3 -1
  34. package/lib/esm/release.js +21 -4
  35. package/lib/esm/release.js.map +1 -1
  36. package/lib/esm/shape-catalog.d.ts +1 -1
  37. package/lib/esm/shape-catalog.js.map +1 -1
  38. package/lib/esm/shapes/TranslationKey.d.ts +10 -5
  39. package/lib/esm/shapes/TranslationKey.js +16 -5
  40. package/lib/esm/shapes/TranslationKey.js.map +1 -1
  41. package/lib/esm/shapes/TranslationProvider.d.ts +25 -8
  42. package/lib/esm/shapes/TranslationProvider.js +50 -19
  43. package/lib/esm/shapes/TranslationProvider.js.map +1 -1
  44. package/package.json +3 -3
  45. package/src/archive.ts +2 -2
  46. package/src/authorization.ts +1 -1
  47. package/src/cdn-loader.ts +1 -1
  48. package/src/core/messages.ts +35 -2
  49. package/src/exchange.ts +1 -1
  50. package/src/export.ts +4 -1
  51. package/src/formats/xliff.ts +1 -1
  52. package/src/key-sync.ts +46 -6
  53. package/src/mt.ts +1 -1
  54. package/src/ontologies/translation.ts +4 -2
  55. package/src/react.tsx +158 -35
  56. package/src/records.ts +2 -1
  57. package/src/release.ts +31 -3
  58. package/src/shape-catalog.ts +1 -1
  59. package/src/shapes/TranslationKey.ts +14 -5
  60. package/src/shapes/TranslationProvider.ts +73 -13
package/README.md CHANGED
@@ -43,8 +43,8 @@ package works without either peer.
43
43
 
44
44
  ## Code-canonical discovery
45
45
 
46
- Create Now's Host Agent can inventory a managed app without running its
47
- packages:
46
+ A host application's tooling can inventory a consuming app without running
47
+ its packages:
48
48
 
49
49
  - literal `t()` and `<T>` calls are extracted from `src/`;
50
50
  - finite dynamic key families are declared in a root
@@ -53,11 +53,10 @@ packages:
53
53
  `"linkedTranslationCatalog": "./translation.catalog.json"` in its
54
54
  `package.json`.
55
55
 
56
- The Host Agent walks installed `dependencies` and `optionalDependencies`,
57
- parses only JSON, and never imports package JavaScript. A package catalog path
58
- must remain inside that package. Inventory is branch-local because discovery
59
- always runs against the exact managed branch clone and persists into that
60
- branch's metadata dataset.
56
+ Discovery walks installed `dependencies` and `optionalDependencies`, parses
57
+ only JSON, and never imports package JavaScript. A package catalog path must
58
+ remain inside that package. Inventory is branch-local when a host runs
59
+ discovery against the exact branch checkout and stores it per branch.
61
60
 
62
61
  ## Full-fidelity archives
63
62
 
@@ -85,13 +84,12 @@ Reusable asset packages can include a schema-versioned shape translation
85
84
  catalog. The catalog is code-canonical package content; installation copies
86
85
  only missing keys and active-language units into the consuming app's graph.
87
86
  Existing app-authored translations are never overwritten. Packages may export
88
- the TypeScript object or ship the same contract as JSON for Create Now's Host
89
- Agent to discover without executing application code.
87
+ the TypeScript object or ship the same contract as JSON for a host's tooling
88
+ to discover without executing application code.
90
89
 
91
- Create Now's editor, menu contribution, entitlement, and capability activation
92
- are deliberately not part of this package. They live in Create Now's
93
- `translation-studio` feature so applications can use this runtime without
94
- depending on Create Now.
90
+ Authoring tools — editors, menus, entitlements, capability activation — are
91
+ deliberately not part of this package. They belong to the host application, so
92
+ any app can use this runtime without depending on a particular host.
95
93
 
96
94
  The package targets `@_linked/core` 2.14.4. Its ontology terms live at
97
95
  `https://linked.cm/ont/translation/` (prefix `translation:`), following the
@@ -105,9 +103,10 @@ follow `https://linked.cm/shape/translation/{ShapeName}` (e.g.
105
103
  Language codes are BCP-47 strings. `TranslationLanguage` stores app-scoped RDF
106
104
  resources for the code, stable language identifier, native and English names,
107
105
  text direction, parent/fallback links, and enabled/supported flags. Hosts choose
108
- an explicit configuration store; the portable package does not assume a CN
109
- control plane. `defineLanguage` supplies editable defaults from the platform's
110
- Unicode locale data. Validate relationship cycles before saving.
106
+ an explicit configuration store; the portable package does not assume a
107
+ particular host's control plane. `defineLanguage` supplies editable defaults
108
+ from the platform's Unicode locale data. Validate relationship cycles before
109
+ saving.
111
110
 
112
111
  Publish the resource list as `languages.json` alongside the catalogs. Wire
113
112
  `createCdnLanguageLoader({base})` to the React provider's `loadLanguages` prop
@@ -4,8 +4,8 @@ import { type TranslationJsonArchiveLimits } from './formats/json.js';
4
4
  export declare const DEFAULT_TRANSLATION_ARCHIVE_LIMITS: TranslationJsonArchiveLimits;
5
5
  export declare const TRANSLATION_ARCHIVE_MANIFEST_PATH = "linked.translation-archive.json";
6
6
  /**
7
- * Archives written before 0.3.0 named the manifest — and the archive format
8
- * itself — after Create Now. Both spellings are read; only the current one is
7
+ * Archives written before 0.3.0 used the pre-0.3.0 spelling for the manifest
8
+ * and the archive format. Both spellings are read; only the current one is
9
9
  * written. The format id is inside `archiveContentHash`'s input, so a legacy
10
10
  * manifest keeps the value it was hashed with.
11
11
  */
@@ -14,8 +14,8 @@ export const DEFAULT_TRANSLATION_ARCHIVE_LIMITS = Object.freeze({
14
14
  });
15
15
  export const TRANSLATION_ARCHIVE_MANIFEST_PATH = 'linked.translation-archive.json';
16
16
  /**
17
- * Archives written before 0.3.0 named the manifest — and the archive format
18
- * itself — after Create Now. Both spellings are read; only the current one is
17
+ * Archives written before 0.3.0 used the pre-0.3.0 spelling for the manifest
18
+ * and the archive format. Both spellings are read; only the current one is
19
19
  * written. The format id is inside `archiveContentHash`'s input, so a legacy
20
20
  * manifest keeps the value it was hashed with.
21
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/archive.ts"],"names":[],"mappings":"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","sourcesContent":["import { zipSync, type Zippable } from 'fflate';\n\nimport { canonicalJson } from './release.js';\nimport {\n DEFAULT_TRANSLATION_JSON_ARCHIVE_LIMITS,\n safeUnzipTranslationZip,\n translationZipLimits,\n type TranslationJsonArchiveLimits,\n} from './formats/json.js';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder('utf-8', { fatal: true });\n\n/** Full backups include every language and its revision history. CAT-file\n * imports retain their smaller defaults; native backups remain bounded too. */\nexport const DEFAULT_TRANSLATION_ARCHIVE_LIMITS: TranslationJsonArchiveLimits =\n Object.freeze({\n ...DEFAULT_TRANSLATION_JSON_ARCHIVE_LIMITS,\n maxArchiveBytes: 64 * 1024 * 1024,\n maxEntryCompressedBytes: 32 * 1024 * 1024,\n maxEntryUncompressedBytes: 128 * 1024 * 1024,\n maxTotalUncompressedBytes: 256 * 1024 * 1024,\n });\n\nexport const TRANSLATION_ARCHIVE_MANIFEST_PATH =\n 'linked.translation-archive.json';\n\n/**\n * Archives written before 0.3.0 named the manifest — and the archive format\n * itself — after Create Now. Both spellings are read; only the current one is\n * written. The format id is inside `archiveContentHash`'s input, so a legacy\n * manifest keeps the value it was hashed with.\n */\nexport const LEGACY_TRANSLATION_ARCHIVE_MANIFEST_PATH =\n 'create-now.translation-archive.json';\n\nexport const TRANSLATION_ARCHIVE_FORMAT = 'linked-translation-archive';\nexport const LEGACY_TRANSLATION_ARCHIVE_FORMAT = 'create-now-translation-archive';\n\nexport type TranslationArchiveCollection =\n | 'configuration'\n | 'keys'\n | 'keyVersions'\n | 'units'\n | 'glossary'\n | 'style'\n | 'revisions'\n | 'provenance'\n | 'content'\n | 'releases'\n | 'buildPins';\n\nexport const TRANSLATION_ARCHIVE_COLLECTIONS:\n readonly TranslationArchiveCollection[] = Object.freeze([\n 'configuration',\n 'keys',\n 'keyVersions',\n 'units',\n 'glossary',\n 'style',\n 'revisions',\n 'provenance',\n 'content',\n 'releases',\n 'buildPins',\n ]);\n\nexport const TRANSLATION_ARCHIVE_EXCLUSIONS = Object.freeze([\n 'provider secrets and API keys',\n 'import/export sessions',\n 'collaborators, assignments, and invitations',\n 'workspace entitlements',\n 'identity and authentication records',\n 'unrelated application graph data',\n]);\n\nexport type TranslationArchiveJson =\n | null\n | boolean\n | number\n | string\n | TranslationArchiveJson[]\n | { [key: string]: TranslationArchiveJson };\n\nexport interface TranslationArchiveEntityInput {\n id: string;\n data: Record<string, TranslationArchiveJson>;\n}\n\nexport interface TranslationArchiveEntity extends TranslationArchiveEntityInput {\n contentHash: string;\n}\n\nexport interface TranslationArchiveObjectInput {\n hash: string;\n body?: Uint8Array;\n mediaType?: string;\n}\n\nexport interface TranslationArchiveSnapshot {\n appId: string;\n branchId: string;\n revisionWatermark: string;\n createdAt?: string;\n collections: Partial<\n Record<TranslationArchiveCollection, TranslationArchiveEntityInput[]>\n >;\n /** Immutable release catalog objects; omitted bodies remain references only. */\n objects?: TranslationArchiveObjectInput[];\n}\n\nexport interface TranslationArchiveFileInventory {\n path: string;\n collection: TranslationArchiveCollection;\n hash: string;\n bytes: number;\n records: number;\n}\n\nexport interface TranslationArchiveObjectInventory {\n path: string;\n hash: string;\n bytes?: number;\n mediaType?: string;\n included: boolean;\n}\n\nexport interface TranslationArchiveManifest {\n schemaVersion: 1;\n format:\n | typeof TRANSLATION_ARCHIVE_FORMAT\n | typeof LEGACY_TRANSLATION_ARCHIVE_FORMAT;\n appId: string;\n branchId: string;\n revisionWatermark: string;\n createdAt?: string;\n files: TranslationArchiveFileInventory[];\n objects: TranslationArchiveObjectInventory[];\n exclusions: string[];\n archiveContentHash: string;\n}\n\nexport interface SerializedTranslationArchive {\n fileName: string;\n mediaType: 'application/zip';\n body: Uint8Array;\n manifest: TranslationArchiveManifest;\n}\n\nexport interface ParsedTranslationArchive {\n manifest: TranslationArchiveManifest;\n collections: Record<\n TranslationArchiveCollection,\n TranslationArchiveEntity[]\n >;\n objects: Array<{\n hash: string;\n body?: Uint8Array;\n mediaType?: string;\n }>;\n}\n\nexport interface TranslationArchiveTargetRecord {\n id: string;\n contentHash: string;\n}\n\nexport interface TranslationArchiveContentMapping {\n sourceNodeIri: string;\n sourceFieldIri: string;\n targetNodeIri: string;\n targetFieldIri: string;\n}\n\nexport interface PlanTranslationArchiveRestoreOptions {\n mode: 'same-app' | 'cross-app';\n targetAppId: string;\n targetRecords?: TranslationArchiveTargetRecord[];\n contentMappings?: TranslationArchiveContentMapping[];\n targetRevisionWatermark?: string;\n}\n\nexport interface TranslationArchiveRestoreDecision {\n collection: TranslationArchiveCollection;\n sourceId: string;\n targetId: string;\n action: 'create' | 'skip' | 'collision' | 'unmapped';\n expectedTargetContentHash?: string;\n record?: TranslationArchiveEntity;\n reason?: string;\n retryKey: string;\n}\n\nexport interface TranslationArchiveRestorePlan {\n schemaVersion: 1;\n mode: 'same-app' | 'cross-app';\n sourceAppId: string;\n targetAppId: string;\n sourceBranchId: string;\n archiveContentHash: string;\n archiveRevisionWatermark: string;\n expectedTargetRevisionWatermark?: string;\n blocked: boolean;\n decisions: TranslationArchiveRestoreDecision[];\n iriMappings: Record<string, string>;\n creates: number;\n skips: number;\n collisions: number;\n unresolvedContent: number;\n historicalReleases: number;\n identityWrites: 0;\n assignmentWrites: 0;\n}\n\nconst COLLECTION_PATHS: Record<TranslationArchiveCollection, string> = {\n configuration: 'content/configuration.json',\n keys: 'content/keys.json',\n keyVersions: 'content/key-versions.json',\n units: 'content/units.json',\n glossary: 'content/glossary.json',\n style: 'content/style.json',\n revisions: 'content/revisions.json',\n provenance: 'content/provenance.json',\n content: 'content/content-translations.json',\n releases: 'content/releases.json',\n buildPins: 'content/build-pins.json',\n};\n\nconst FORBIDDEN_ARCHIVE_FIELDS = new Set([\n 'apikey',\n 'api_key',\n 'secret',\n 'clientsecret',\n 'accesstoken',\n 'refreshtoken',\n 'password',\n 'credential',\n 'credentials',\n 'session',\n 'sessionid',\n 'collaborator',\n 'collaborators',\n 'invite',\n 'invites',\n 'assignment',\n 'assignments',\n 'entitlement',\n 'entitlements',\n]);\n\nfunction requiredString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`${label} is required.`);\n }\n return value.trim();\n}\n\nfunction record(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} must be an object.`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction assertArchiveSafeValue(value: unknown, path = 'data'): void {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean'\n ) {\n return;\n }\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error(`${path} contains a non-finite number.`);\n }\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) =>\n assertArchiveSafeValue(item, `${path}[${index}]`),\n );\n return;\n }\n if (!value || typeof value !== 'object') {\n throw new Error(`${path} contains an unsupported value.`);\n }\n for (const [key, item] of Object.entries(value)) {\n if (FORBIDDEN_ARCHIVE_FIELDS.has(key.toLowerCase())) {\n throw new Error(\n `Archive field \"${path}.${key}\" contains excluded security or control-plane data.`,\n );\n }\n assertArchiveSafeValue(item, `${path}.${key}`);\n }\n}\n\nfunction decodeJson(input: Uint8Array, label: string): unknown {\n try {\n return JSON.parse(decoder.decode(input)) as unknown;\n } catch {\n throw new Error(`${label} must contain valid UTF-8 JSON.`);\n }\n}\n\nasync function sha256Bytes(value: Uint8Array): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest(\n 'SHA-256',\n value as unknown as BufferSource,\n );\n return [...new Uint8Array(digest)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction canonicalBytes(value: unknown): Uint8Array {\n return encoder.encode(canonicalJson(value));\n}\n\nfunction normalizeEntityInput(\n value: TranslationArchiveEntityInput,\n label: string,\n): TranslationArchiveEntityInput {\n const id = requiredString(value?.id, `${label}.id`);\n const data = record(value?.data, `${label}.data`) as Record<\n string,\n TranslationArchiveJson\n >;\n assertArchiveSafeValue(data, `${label}.data`);\n return { id, data };\n}\n\nasync function archiveEntity(\n value: TranslationArchiveEntityInput,\n label: string,\n): Promise<TranslationArchiveEntity> {\n const normalized = normalizeEntityInput(value, label);\n return {\n ...normalized,\n contentHash: await sha256Bytes(canonicalBytes(normalized)),\n };\n}\n\nfunction archiveContentHashInput(\n manifest: Omit<TranslationArchiveManifest, 'archiveContentHash'>,\n): unknown {\n return {\n schemaVersion: manifest.schemaVersion,\n format: manifest.format,\n appId: manifest.appId,\n branchId: manifest.branchId,\n revisionWatermark: manifest.revisionWatermark,\n ...(manifest.createdAt ? { createdAt: manifest.createdAt } : {}),\n files: manifest.files,\n objects: manifest.objects,\n exclusions: manifest.exclusions,\n };\n}\n\nfunction objectPath(hash: string): string {\n return `objects/catalog/${hash}.json`;\n}\n\nfunction normalizeHash(value: unknown, label: string): string {\n const hash = requiredString(value, label).toLowerCase();\n if (!/^[a-f0-9]{64}$/.test(hash)) {\n throw new Error(`${label} must be a SHA-256 hex digest.`);\n }\n return hash;\n}\n\n/** Create a deterministic, full-fidelity translation archive. */\nexport async function createTranslationArchive(\n snapshot: TranslationArchiveSnapshot,\n options: { limits?: Partial<TranslationJsonArchiveLimits> } = {},\n): Promise<SerializedTranslationArchive> {\n const appId = requiredString(snapshot?.appId, 'snapshot.appId');\n const branchId = requiredString(snapshot?.branchId, 'snapshot.branchId');\n const revisionWatermark = requiredString(\n snapshot?.revisionWatermark,\n 'snapshot.revisionWatermark',\n );\n const limits = translationZipLimits({\n ...DEFAULT_TRANSLATION_ARCHIVE_LIMITS,\n ...options.limits,\n });\n const files: Zippable = Object.create(null);\n const fileInventory: TranslationArchiveFileInventory[] = [];\n for (const collection of TRANSLATION_ARCHIVE_COLLECTIONS) {\n const values = snapshot.collections?.[collection] ?? [];\n const entities = await Promise.all(\n values.map((value, index) =>\n archiveEntity(value, `${collection}[${index}]`),\n ),\n );\n entities.sort((left, right) => left.id.localeCompare(right.id));\n const duplicate = entities.find(\n (entity, index) => index > 0 && entity.id === entities[index - 1].id,\n );\n if (duplicate) {\n throw new Error(\n `Archive collection \"${collection}\" contains duplicate id \"${duplicate.id}\".`,\n );\n }\n const path = COLLECTION_PATHS[collection];\n const body = canonicalBytes(entities);\n files[path] = body;\n fileInventory.push({\n path,\n collection,\n hash: await sha256Bytes(body),\n bytes: body.byteLength,\n records: entities.length,\n });\n }\n\n const objectInventory: TranslationArchiveObjectInventory[] = [];\n const seenObjects = new Set<string>();\n for (const [index, object] of [...(snapshot.objects ?? [])]\n .sort((left, right) => left.hash.localeCompare(right.hash))\n .entries()) {\n const hash = normalizeHash(object.hash, `objects[${index}].hash`);\n if (seenObjects.has(hash)) {\n throw new Error(`Archive contains duplicate object hash \"${hash}\".`);\n }\n seenObjects.add(hash);\n const path = objectPath(hash);\n if (object.body) {\n const actual = await sha256Bytes(object.body);\n if (actual !== hash) {\n throw new Error(\n `Archive object \"${hash}\" does not match its content hash.`,\n );\n }\n files[path] = object.body;\n }\n objectInventory.push({\n path,\n hash,\n included: Boolean(object.body),\n ...(object.body ? { bytes: object.body.byteLength } : {}),\n ...(object.mediaType ? { mediaType: object.mediaType } : {}),\n });\n }\n\n const unsignedManifest: Omit<\n TranslationArchiveManifest,\n 'archiveContentHash'\n > = {\n schemaVersion: 1,\n format: TRANSLATION_ARCHIVE_FORMAT,\n appId,\n branchId,\n revisionWatermark,\n ...(snapshot.createdAt\n ? { createdAt: requiredString(snapshot.createdAt, 'snapshot.createdAt') }\n : {}),\n files: fileInventory,\n objects: objectInventory,\n exclusions: [...TRANSLATION_ARCHIVE_EXCLUSIONS],\n };\n const manifest: TranslationArchiveManifest = {\n ...unsignedManifest,\n archiveContentHash: await sha256Bytes(\n canonicalBytes(archiveContentHashInput(unsignedManifest)),\n ),\n };\n files[TRANSLATION_ARCHIVE_MANIFEST_PATH] = canonicalBytes(manifest);\n if (Object.keys(files).length > limits.maxEntries) {\n throw new Error('ZIP archive exceeds the entry-count limit.');\n }\n const body = zipSync(files, {\n level: 6,\n mtime: new Date('1980-01-01T00:00:00.000Z'),\n });\n safeUnzipTranslationZip(body, limits);\n return {\n fileName: 'linked-translations.backup.zip',\n mediaType: 'application/zip',\n body,\n manifest,\n };\n}\n\nfunction parseManifest(value: unknown): TranslationArchiveManifest {\n const raw = record(value, 'manifest');\n if (raw.schemaVersion !== 1) {\n throw new Error(\n `Unsupported translation archive schemaVersion \"${String(raw.schemaVersion)}\".`,\n );\n }\n if (\n raw.format !== TRANSLATION_ARCHIVE_FORMAT &&\n raw.format !== LEGACY_TRANSLATION_ARCHIVE_FORMAT\n ) {\n throw new Error('Unsupported translation archive format.');\n }\n if (!Array.isArray(raw.files) || !Array.isArray(raw.objects)) {\n throw new Error('Translation archive manifest inventory is invalid.');\n }\n if (\n !Array.isArray(raw.exclusions) ||\n raw.exclusions.some((item) => typeof item !== 'string')\n ) {\n throw new Error('Translation archive exclusions are invalid.');\n }\n const collectionSet = new Set(TRANSLATION_ARCHIVE_COLLECTIONS);\n const files = raw.files.map((value, index) => {\n const item = record(value, `manifest.files[${index}]`);\n const collection = item.collection as TranslationArchiveCollection;\n if (!collectionSet.has(collection)) {\n throw new Error(`manifest.files[${index}].collection is invalid.`);\n }\n if (item.path !== COLLECTION_PATHS[collection]) {\n throw new Error(`manifest.files[${index}].path is invalid.`);\n }\n if (\n !Number.isSafeInteger(item.bytes) ||\n (item.bytes as number) < 0 ||\n !Number.isSafeInteger(item.records) ||\n (item.records as number) < 0\n ) {\n throw new Error(`manifest.files[${index}] counts are invalid.`);\n }\n return {\n path: item.path as string,\n collection,\n hash: normalizeHash(item.hash, `manifest.files[${index}].hash`),\n bytes: item.bytes as number,\n records: item.records as number,\n };\n });\n const objects = raw.objects.map((value, index) => {\n const item = record(value, `manifest.objects[${index}]`);\n const hash = normalizeHash(item.hash, `manifest.objects[${index}].hash`);\n if (item.path !== objectPath(hash) || typeof item.included !== 'boolean') {\n throw new Error(`manifest.objects[${index}] is invalid.`);\n }\n if (\n item.bytes !== undefined &&\n (!Number.isSafeInteger(item.bytes) || (item.bytes as number) < 0)\n ) {\n throw new Error(`manifest.objects[${index}].bytes is invalid.`);\n }\n return {\n path: item.path as string,\n hash,\n included: item.included,\n ...(item.bytes === undefined ? {} : { bytes: item.bytes as number }),\n ...(item.mediaType === undefined\n ? {}\n : { mediaType: requiredString(item.mediaType, `manifest.objects[${index}].mediaType`) }),\n };\n });\n return {\n schemaVersion: 1,\n // Echoed back, not normalized: it is part of the content hash input.\n format: raw.format,\n appId: requiredString(raw.appId, 'manifest.appId'),\n branchId: requiredString(raw.branchId, 'manifest.branchId'),\n revisionWatermark: requiredString(\n raw.revisionWatermark,\n 'manifest.revisionWatermark',\n ),\n ...(raw.createdAt === undefined\n ? {}\n : { createdAt: requiredString(raw.createdAt, 'manifest.createdAt') }),\n files,\n objects,\n exclusions: raw.exclusions as string[],\n archiveContentHash: normalizeHash(\n raw.archiveContentHash,\n 'manifest.archiveContentHash',\n ),\n };\n}\n\nasync function parseEntities(\n value: unknown,\n collection: TranslationArchiveCollection,\n): Promise<TranslationArchiveEntity[]> {\n if (!Array.isArray(value)) {\n throw new Error(`Archive ${collection} file must contain an array.`);\n }\n const entities: TranslationArchiveEntity[] = [];\n const ids = new Set<string>();\n for (const [index, rawValue] of value.entries()) {\n const raw = record(rawValue, `${collection}[${index}]`);\n const normalized = normalizeEntityInput(\n {\n id: raw.id as string,\n data: raw.data as Record<string, TranslationArchiveJson>,\n },\n `${collection}[${index}]`,\n );\n const contentHash = normalizeHash(\n raw.contentHash,\n `${collection}[${index}].contentHash`,\n );\n const actual = await sha256Bytes(canonicalBytes(normalized));\n if (actual !== contentHash) {\n throw new Error(\n `Archive ${collection} record \"${normalized.id}\" has an invalid content hash.`,\n );\n }\n if (ids.has(normalized.id)) {\n throw new Error(\n `Archive collection \"${collection}\" contains duplicate id \"${normalized.id}\".`,\n );\n }\n ids.add(normalized.id);\n entities.push({ ...normalized, contentHash });\n }\n return entities.sort((left, right) => left.id.localeCompare(right.id));\n}\n\n/** Parse and verify every declared content file and optional catalog object. */\nexport async function parseTranslationArchive(\n input: Uint8Array,\n options: { limits?: Partial<TranslationJsonArchiveLimits> } = {},\n): Promise<ParsedTranslationArchive> {\n const files = safeUnzipTranslationZip(input, {\n ...DEFAULT_TRANSLATION_ARCHIVE_LIMITS,\n ...options.limits,\n });\n const manifestPath =\n files[TRANSLATION_ARCHIVE_MANIFEST_PATH] !== undefined\n ? TRANSLATION_ARCHIVE_MANIFEST_PATH\n : LEGACY_TRANSLATION_ARCHIVE_MANIFEST_PATH;\n const manifestBody = files[manifestPath];\n if (!manifestBody) {\n throw new Error('Translation archive manifest is missing.');\n }\n const manifest = parseManifest(decodeJson(manifestBody, manifestPath));\n const actualArchiveHash = await sha256Bytes(\n canonicalBytes(\n archiveContentHashInput(\n manifest as Omit<TranslationArchiveManifest, 'archiveContentHash'>,\n ),\n ),\n );\n if (actualArchiveHash !== manifest.archiveContentHash) {\n throw new Error('Translation archive manifest content hash is invalid.');\n }\n\n const allowedPaths = new Set<string>([\n manifestPath,\n ...manifest.files.map(({ path }) => path),\n ...manifest.objects.filter(({ included }) => included).map(({ path }) => path),\n ]);\n const unexpected = Object.keys(files).find((path) => !allowedPaths.has(path));\n if (unexpected) {\n throw new Error(`Translation archive contains undeclared file \"${unexpected}\".`);\n }\n if (manifest.files.length !== TRANSLATION_ARCHIVE_COLLECTIONS.length) {\n throw new Error('Translation archive must inventory every content collection.');\n }\n\n const collections = Object.create(null) as Record<\n TranslationArchiveCollection,\n TranslationArchiveEntity[]\n >;\n for (const item of manifest.files) {\n const body = files[item.path];\n if (!body) throw new Error(`Translation archive is missing \"${item.path}\".`);\n if (\n body.byteLength !== item.bytes ||\n (await sha256Bytes(body)) !== item.hash\n ) {\n throw new Error(`Translation archive file \"${item.path}\" failed verification.`);\n }\n const entities = await parseEntities(\n decodeJson(body, item.path),\n item.collection,\n );\n if (entities.length !== item.records) {\n throw new Error(`Translation archive file \"${item.path}\" count is invalid.`);\n }\n collections[item.collection] = entities;\n }\n\n const objects = await Promise.all(\n manifest.objects.map(async (item) => {\n const body = files[item.path];\n if (item.included) {\n if (!body) {\n throw new Error(`Translation archive object \"${item.hash}\" is missing.`);\n }\n if (\n (item.bytes !== undefined && body.byteLength !== item.bytes) ||\n (await sha256Bytes(body)) !== item.hash\n ) {\n throw new Error(\n `Translation archive object \"${item.hash}\" failed verification.`,\n );\n }\n } else if (body) {\n throw new Error(\n `Translation archive reference-only object \"${item.hash}\" unexpectedly has a body.`,\n );\n }\n return {\n hash: item.hash,\n ...(body ? { body } : {}),\n ...(item.mediaType ? { mediaType: item.mediaType } : {}),\n };\n }),\n );\n return { manifest, collections, objects };\n}\n\nfunction rebaseValue(\n value: TranslationArchiveJson,\n sourceAppId: string,\n targetAppId: string,\n mappings: Record<string, string>,\n): TranslationArchiveJson {\n if (typeof value === 'string') {\n if (value === sourceAppId || value.startsWith(`${sourceAppId}/`)) {\n const target = `${targetAppId}${value.slice(sourceAppId.length)}`;\n mappings[value] = target;\n return target;\n }\n return value;\n }\n if (Array.isArray(value)) {\n return value.map((item) =>\n rebaseValue(item, sourceAppId, targetAppId, mappings),\n );\n }\n if (value && typeof value === 'object') {\n const output: Record<string, TranslationArchiveJson> = {};\n for (const [key, item] of Object.entries(value)) {\n output[key] = rebaseValue(\n item,\n sourceAppId,\n targetAppId,\n mappings,\n );\n }\n return output;\n }\n return value;\n}\n\nfunction contentIdentity(\n nodeIri: string,\n fieldIri: string,\n): string {\n return JSON.stringify([nodeIri, fieldIri]);\n}\n\n/**\n * Build a pure, conditional restore plan. A blocked plan is never executable:\n * collisions and unresolved cross-app content mappings must be resolved first.\n */\nexport async function planTranslationArchiveRestore(\n archive: ParsedTranslationArchive,\n options: PlanTranslationArchiveRestoreOptions,\n): Promise<TranslationArchiveRestorePlan> {\n const sourceAppId = archive.manifest.appId;\n const targetAppId = requiredString(options.targetAppId, 'targetAppId');\n if (options.mode === 'same-app' && sourceAppId !== targetAppId) {\n throw new Error(\n `Same-app restore requires target app \"${sourceAppId}\".`,\n );\n }\n const targetById = new Map(\n (options.targetRecords ?? []).map((item) => [\n requiredString(item.id, 'targetRecords.id'),\n normalizeHash(item.contentHash, 'targetRecords.contentHash'),\n ]),\n );\n const contentMappings = new Map(\n (options.contentMappings ?? []).map((mapping) => [\n contentIdentity(mapping.sourceNodeIri, mapping.sourceFieldIri),\n mapping,\n ]),\n );\n const iriMappings: Record<string, string> = {};\n const decisions: TranslationArchiveRestoreDecision[] = [];\n let historicalReleases = 0;\n\n for (const collection of TRANSLATION_ARCHIVE_COLLECTIONS) {\n for (const sourceRecord of archive.collections[collection]) {\n let targetId = sourceRecord.id;\n let data = sourceRecord.data;\n if (options.mode === 'cross-app') {\n targetId = rebaseValue(\n sourceRecord.id,\n sourceAppId,\n targetAppId,\n iriMappings,\n ) as string;\n data = rebaseValue(\n sourceRecord.data,\n sourceAppId,\n targetAppId,\n iriMappings,\n ) as Record<string, TranslationArchiveJson>;\n }\n\n if (collection === 'content' && options.mode === 'cross-app') {\n const sourceNodeIri =\n typeof sourceRecord.data.nodeIri === 'string'\n ? sourceRecord.data.nodeIri\n : '';\n const sourceFieldIri =\n typeof sourceRecord.data.fieldIri === 'string'\n ? sourceRecord.data.fieldIri\n : '';\n const mapping = contentMappings.get(\n contentIdentity(sourceNodeIri, sourceFieldIri),\n );\n if (!mapping) {\n decisions.push({\n collection,\n sourceId: sourceRecord.id,\n targetId,\n action: 'unmapped',\n reason: 'Cross-app content requires an explicit node/field mapping.',\n retryKey: canonicalJson([\n archive.manifest.archiveContentHash,\n collection,\n sourceRecord.id,\n 'unmapped',\n ]),\n });\n continue;\n }\n data = {\n ...data,\n nodeIri: mapping.targetNodeIri,\n fieldIri: mapping.targetFieldIri,\n };\n iriMappings[sourceNodeIri] = mapping.targetNodeIri;\n iriMappings[sourceFieldIri] = mapping.targetFieldIri;\n }\n\n if (collection === 'releases') {\n historicalReleases += 1;\n data = {\n ...data,\n status: 'retired',\n historical: true,\n activationEligible: false,\n };\n }\n const targetRecordInput = { id: targetId, data };\n const targetRecord: TranslationArchiveEntity = {\n ...targetRecordInput,\n contentHash: await sha256Bytes(canonicalBytes(targetRecordInput)),\n };\n const currentHash = targetById.get(targetId);\n const action =\n currentHash === undefined\n ? 'create'\n : currentHash === targetRecord.contentHash\n ? 'skip'\n : 'collision';\n decisions.push({\n collection,\n sourceId: sourceRecord.id,\n targetId,\n action,\n ...(currentHash ? { expectedTargetContentHash: currentHash } : {}),\n record: targetRecord,\n ...(action === 'collision'\n ? { reason: 'Target id exists with different content.' }\n : {}),\n retryKey: canonicalJson([\n archive.manifest.archiveContentHash,\n collection,\n targetId,\n targetRecord.contentHash,\n ]),\n });\n }\n }\n\n const collisions = decisions.filter(\n ({ action }) => action === 'collision',\n ).length;\n const unresolvedContent = decisions.filter(\n ({ action }) => action === 'unmapped',\n ).length;\n return {\n schemaVersion: 1,\n mode: options.mode,\n sourceAppId,\n targetAppId,\n sourceBranchId: archive.manifest.branchId,\n archiveContentHash: archive.manifest.archiveContentHash,\n archiveRevisionWatermark: archive.manifest.revisionWatermark,\n ...(options.targetRevisionWatermark\n ? {\n expectedTargetRevisionWatermark: requiredString(\n options.targetRevisionWatermark,\n 'targetRevisionWatermark',\n ),\n }\n : {}),\n blocked: collisions > 0 || unresolvedContent > 0,\n decisions,\n iriMappings,\n creates: decisions.filter(({ action }) => action === 'create').length,\n skips: decisions.filter(({ action }) => action === 'skip').length,\n collisions,\n unresolvedContent,\n historicalReleases,\n identityWrites: 0,\n assignmentWrites: 0,\n };\n}\n"]}
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/archive.ts"],"names":[],"mappings":"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","sourcesContent":["import { zipSync, type Zippable } from 'fflate';\n\nimport { canonicalJson } from './release.js';\nimport {\n DEFAULT_TRANSLATION_JSON_ARCHIVE_LIMITS,\n safeUnzipTranslationZip,\n translationZipLimits,\n type TranslationJsonArchiveLimits,\n} from './formats/json.js';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder('utf-8', { fatal: true });\n\n/** Full backups include every language and its revision history. CAT-file\n * imports retain their smaller defaults; native backups remain bounded too. */\nexport const DEFAULT_TRANSLATION_ARCHIVE_LIMITS: TranslationJsonArchiveLimits =\n Object.freeze({\n ...DEFAULT_TRANSLATION_JSON_ARCHIVE_LIMITS,\n maxArchiveBytes: 64 * 1024 * 1024,\n maxEntryCompressedBytes: 32 * 1024 * 1024,\n maxEntryUncompressedBytes: 128 * 1024 * 1024,\n maxTotalUncompressedBytes: 256 * 1024 * 1024,\n });\n\nexport const TRANSLATION_ARCHIVE_MANIFEST_PATH =\n 'linked.translation-archive.json';\n\n/**\n * Archives written before 0.3.0 used the pre-0.3.0 spelling for the manifest\n * and the archive format. Both spellings are read; only the current one is\n * written. The format id is inside `archiveContentHash`'s input, so a legacy\n * manifest keeps the value it was hashed with.\n */\nexport const LEGACY_TRANSLATION_ARCHIVE_MANIFEST_PATH =\n 'create-now.translation-archive.json';\n\nexport const TRANSLATION_ARCHIVE_FORMAT = 'linked-translation-archive';\nexport const LEGACY_TRANSLATION_ARCHIVE_FORMAT = 'create-now-translation-archive';\n\nexport type TranslationArchiveCollection =\n | 'configuration'\n | 'keys'\n | 'keyVersions'\n | 'units'\n | 'glossary'\n | 'style'\n | 'revisions'\n | 'provenance'\n | 'content'\n | 'releases'\n | 'buildPins';\n\nexport const TRANSLATION_ARCHIVE_COLLECTIONS:\n readonly TranslationArchiveCollection[] = Object.freeze([\n 'configuration',\n 'keys',\n 'keyVersions',\n 'units',\n 'glossary',\n 'style',\n 'revisions',\n 'provenance',\n 'content',\n 'releases',\n 'buildPins',\n ]);\n\nexport const TRANSLATION_ARCHIVE_EXCLUSIONS = Object.freeze([\n 'provider secrets and API keys',\n 'import/export sessions',\n 'collaborators, assignments, and invitations',\n 'workspace entitlements',\n 'identity and authentication records',\n 'unrelated application graph data',\n]);\n\nexport type TranslationArchiveJson =\n | null\n | boolean\n | number\n | string\n | TranslationArchiveJson[]\n | { [key: string]: TranslationArchiveJson };\n\nexport interface TranslationArchiveEntityInput {\n id: string;\n data: Record<string, TranslationArchiveJson>;\n}\n\nexport interface TranslationArchiveEntity extends TranslationArchiveEntityInput {\n contentHash: string;\n}\n\nexport interface TranslationArchiveObjectInput {\n hash: string;\n body?: Uint8Array;\n mediaType?: string;\n}\n\nexport interface TranslationArchiveSnapshot {\n appId: string;\n branchId: string;\n revisionWatermark: string;\n createdAt?: string;\n collections: Partial<\n Record<TranslationArchiveCollection, TranslationArchiveEntityInput[]>\n >;\n /** Immutable release catalog objects; omitted bodies remain references only. */\n objects?: TranslationArchiveObjectInput[];\n}\n\nexport interface TranslationArchiveFileInventory {\n path: string;\n collection: TranslationArchiveCollection;\n hash: string;\n bytes: number;\n records: number;\n}\n\nexport interface TranslationArchiveObjectInventory {\n path: string;\n hash: string;\n bytes?: number;\n mediaType?: string;\n included: boolean;\n}\n\nexport interface TranslationArchiveManifest {\n schemaVersion: 1;\n format:\n | typeof TRANSLATION_ARCHIVE_FORMAT\n | typeof LEGACY_TRANSLATION_ARCHIVE_FORMAT;\n appId: string;\n branchId: string;\n revisionWatermark: string;\n createdAt?: string;\n files: TranslationArchiveFileInventory[];\n objects: TranslationArchiveObjectInventory[];\n exclusions: string[];\n archiveContentHash: string;\n}\n\nexport interface SerializedTranslationArchive {\n fileName: string;\n mediaType: 'application/zip';\n body: Uint8Array;\n manifest: TranslationArchiveManifest;\n}\n\nexport interface ParsedTranslationArchive {\n manifest: TranslationArchiveManifest;\n collections: Record<\n TranslationArchiveCollection,\n TranslationArchiveEntity[]\n >;\n objects: Array<{\n hash: string;\n body?: Uint8Array;\n mediaType?: string;\n }>;\n}\n\nexport interface TranslationArchiveTargetRecord {\n id: string;\n contentHash: string;\n}\n\nexport interface TranslationArchiveContentMapping {\n sourceNodeIri: string;\n sourceFieldIri: string;\n targetNodeIri: string;\n targetFieldIri: string;\n}\n\nexport interface PlanTranslationArchiveRestoreOptions {\n mode: 'same-app' | 'cross-app';\n targetAppId: string;\n targetRecords?: TranslationArchiveTargetRecord[];\n contentMappings?: TranslationArchiveContentMapping[];\n targetRevisionWatermark?: string;\n}\n\nexport interface TranslationArchiveRestoreDecision {\n collection: TranslationArchiveCollection;\n sourceId: string;\n targetId: string;\n action: 'create' | 'skip' | 'collision' | 'unmapped';\n expectedTargetContentHash?: string;\n record?: TranslationArchiveEntity;\n reason?: string;\n retryKey: string;\n}\n\nexport interface TranslationArchiveRestorePlan {\n schemaVersion: 1;\n mode: 'same-app' | 'cross-app';\n sourceAppId: string;\n targetAppId: string;\n sourceBranchId: string;\n archiveContentHash: string;\n archiveRevisionWatermark: string;\n expectedTargetRevisionWatermark?: string;\n blocked: boolean;\n decisions: TranslationArchiveRestoreDecision[];\n iriMappings: Record<string, string>;\n creates: number;\n skips: number;\n collisions: number;\n unresolvedContent: number;\n historicalReleases: number;\n identityWrites: 0;\n assignmentWrites: 0;\n}\n\nconst COLLECTION_PATHS: Record<TranslationArchiveCollection, string> = {\n configuration: 'content/configuration.json',\n keys: 'content/keys.json',\n keyVersions: 'content/key-versions.json',\n units: 'content/units.json',\n glossary: 'content/glossary.json',\n style: 'content/style.json',\n revisions: 'content/revisions.json',\n provenance: 'content/provenance.json',\n content: 'content/content-translations.json',\n releases: 'content/releases.json',\n buildPins: 'content/build-pins.json',\n};\n\nconst FORBIDDEN_ARCHIVE_FIELDS = new Set([\n 'apikey',\n 'api_key',\n 'secret',\n 'clientsecret',\n 'accesstoken',\n 'refreshtoken',\n 'password',\n 'credential',\n 'credentials',\n 'session',\n 'sessionid',\n 'collaborator',\n 'collaborators',\n 'invite',\n 'invites',\n 'assignment',\n 'assignments',\n 'entitlement',\n 'entitlements',\n]);\n\nfunction requiredString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`${label} is required.`);\n }\n return value.trim();\n}\n\nfunction record(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} must be an object.`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction assertArchiveSafeValue(value: unknown, path = 'data'): void {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'boolean'\n ) {\n return;\n }\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error(`${path} contains a non-finite number.`);\n }\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) =>\n assertArchiveSafeValue(item, `${path}[${index}]`),\n );\n return;\n }\n if (!value || typeof value !== 'object') {\n throw new Error(`${path} contains an unsupported value.`);\n }\n for (const [key, item] of Object.entries(value)) {\n if (FORBIDDEN_ARCHIVE_FIELDS.has(key.toLowerCase())) {\n throw new Error(\n `Archive field \"${path}.${key}\" contains excluded security or control-plane data.`,\n );\n }\n assertArchiveSafeValue(item, `${path}.${key}`);\n }\n}\n\nfunction decodeJson(input: Uint8Array, label: string): unknown {\n try {\n return JSON.parse(decoder.decode(input)) as unknown;\n } catch {\n throw new Error(`${label} must contain valid UTF-8 JSON.`);\n }\n}\n\nasync function sha256Bytes(value: Uint8Array): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest(\n 'SHA-256',\n value as unknown as BufferSource,\n );\n return [...new Uint8Array(digest)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction canonicalBytes(value: unknown): Uint8Array {\n return encoder.encode(canonicalJson(value));\n}\n\nfunction normalizeEntityInput(\n value: TranslationArchiveEntityInput,\n label: string,\n): TranslationArchiveEntityInput {\n const id = requiredString(value?.id, `${label}.id`);\n const data = record(value?.data, `${label}.data`) as Record<\n string,\n TranslationArchiveJson\n >;\n assertArchiveSafeValue(data, `${label}.data`);\n return { id, data };\n}\n\nasync function archiveEntity(\n value: TranslationArchiveEntityInput,\n label: string,\n): Promise<TranslationArchiveEntity> {\n const normalized = normalizeEntityInput(value, label);\n return {\n ...normalized,\n contentHash: await sha256Bytes(canonicalBytes(normalized)),\n };\n}\n\nfunction archiveContentHashInput(\n manifest: Omit<TranslationArchiveManifest, 'archiveContentHash'>,\n): unknown {\n return {\n schemaVersion: manifest.schemaVersion,\n format: manifest.format,\n appId: manifest.appId,\n branchId: manifest.branchId,\n revisionWatermark: manifest.revisionWatermark,\n ...(manifest.createdAt ? { createdAt: manifest.createdAt } : {}),\n files: manifest.files,\n objects: manifest.objects,\n exclusions: manifest.exclusions,\n };\n}\n\nfunction objectPath(hash: string): string {\n return `objects/catalog/${hash}.json`;\n}\n\nfunction normalizeHash(value: unknown, label: string): string {\n const hash = requiredString(value, label).toLowerCase();\n if (!/^[a-f0-9]{64}$/.test(hash)) {\n throw new Error(`${label} must be a SHA-256 hex digest.`);\n }\n return hash;\n}\n\n/** Create a deterministic, full-fidelity translation archive. */\nexport async function createTranslationArchive(\n snapshot: TranslationArchiveSnapshot,\n options: { limits?: Partial<TranslationJsonArchiveLimits> } = {},\n): Promise<SerializedTranslationArchive> {\n const appId = requiredString(snapshot?.appId, 'snapshot.appId');\n const branchId = requiredString(snapshot?.branchId, 'snapshot.branchId');\n const revisionWatermark = requiredString(\n snapshot?.revisionWatermark,\n 'snapshot.revisionWatermark',\n );\n const limits = translationZipLimits({\n ...DEFAULT_TRANSLATION_ARCHIVE_LIMITS,\n ...options.limits,\n });\n const files: Zippable = Object.create(null);\n const fileInventory: TranslationArchiveFileInventory[] = [];\n for (const collection of TRANSLATION_ARCHIVE_COLLECTIONS) {\n const values = snapshot.collections?.[collection] ?? [];\n const entities = await Promise.all(\n values.map((value, index) =>\n archiveEntity(value, `${collection}[${index}]`),\n ),\n );\n entities.sort((left, right) => left.id.localeCompare(right.id));\n const duplicate = entities.find(\n (entity, index) => index > 0 && entity.id === entities[index - 1].id,\n );\n if (duplicate) {\n throw new Error(\n `Archive collection \"${collection}\" contains duplicate id \"${duplicate.id}\".`,\n );\n }\n const path = COLLECTION_PATHS[collection];\n const body = canonicalBytes(entities);\n files[path] = body;\n fileInventory.push({\n path,\n collection,\n hash: await sha256Bytes(body),\n bytes: body.byteLength,\n records: entities.length,\n });\n }\n\n const objectInventory: TranslationArchiveObjectInventory[] = [];\n const seenObjects = new Set<string>();\n for (const [index, object] of [...(snapshot.objects ?? [])]\n .sort((left, right) => left.hash.localeCompare(right.hash))\n .entries()) {\n const hash = normalizeHash(object.hash, `objects[${index}].hash`);\n if (seenObjects.has(hash)) {\n throw new Error(`Archive contains duplicate object hash \"${hash}\".`);\n }\n seenObjects.add(hash);\n const path = objectPath(hash);\n if (object.body) {\n const actual = await sha256Bytes(object.body);\n if (actual !== hash) {\n throw new Error(\n `Archive object \"${hash}\" does not match its content hash.`,\n );\n }\n files[path] = object.body;\n }\n objectInventory.push({\n path,\n hash,\n included: Boolean(object.body),\n ...(object.body ? { bytes: object.body.byteLength } : {}),\n ...(object.mediaType ? { mediaType: object.mediaType } : {}),\n });\n }\n\n const unsignedManifest: Omit<\n TranslationArchiveManifest,\n 'archiveContentHash'\n > = {\n schemaVersion: 1,\n format: TRANSLATION_ARCHIVE_FORMAT,\n appId,\n branchId,\n revisionWatermark,\n ...(snapshot.createdAt\n ? { createdAt: requiredString(snapshot.createdAt, 'snapshot.createdAt') }\n : {}),\n files: fileInventory,\n objects: objectInventory,\n exclusions: [...TRANSLATION_ARCHIVE_EXCLUSIONS],\n };\n const manifest: TranslationArchiveManifest = {\n ...unsignedManifest,\n archiveContentHash: await sha256Bytes(\n canonicalBytes(archiveContentHashInput(unsignedManifest)),\n ),\n };\n files[TRANSLATION_ARCHIVE_MANIFEST_PATH] = canonicalBytes(manifest);\n if (Object.keys(files).length > limits.maxEntries) {\n throw new Error('ZIP archive exceeds the entry-count limit.');\n }\n const body = zipSync(files, {\n level: 6,\n mtime: new Date('1980-01-01T00:00:00.000Z'),\n });\n safeUnzipTranslationZip(body, limits);\n return {\n fileName: 'linked-translations.backup.zip',\n mediaType: 'application/zip',\n body,\n manifest,\n };\n}\n\nfunction parseManifest(value: unknown): TranslationArchiveManifest {\n const raw = record(value, 'manifest');\n if (raw.schemaVersion !== 1) {\n throw new Error(\n `Unsupported translation archive schemaVersion \"${String(raw.schemaVersion)}\".`,\n );\n }\n if (\n raw.format !== TRANSLATION_ARCHIVE_FORMAT &&\n raw.format !== LEGACY_TRANSLATION_ARCHIVE_FORMAT\n ) {\n throw new Error('Unsupported translation archive format.');\n }\n if (!Array.isArray(raw.files) || !Array.isArray(raw.objects)) {\n throw new Error('Translation archive manifest inventory is invalid.');\n }\n if (\n !Array.isArray(raw.exclusions) ||\n raw.exclusions.some((item) => typeof item !== 'string')\n ) {\n throw new Error('Translation archive exclusions are invalid.');\n }\n const collectionSet = new Set(TRANSLATION_ARCHIVE_COLLECTIONS);\n const files = raw.files.map((value, index) => {\n const item = record(value, `manifest.files[${index}]`);\n const collection = item.collection as TranslationArchiveCollection;\n if (!collectionSet.has(collection)) {\n throw new Error(`manifest.files[${index}].collection is invalid.`);\n }\n if (item.path !== COLLECTION_PATHS[collection]) {\n throw new Error(`manifest.files[${index}].path is invalid.`);\n }\n if (\n !Number.isSafeInteger(item.bytes) ||\n (item.bytes as number) < 0 ||\n !Number.isSafeInteger(item.records) ||\n (item.records as number) < 0\n ) {\n throw new Error(`manifest.files[${index}] counts are invalid.`);\n }\n return {\n path: item.path as string,\n collection,\n hash: normalizeHash(item.hash, `manifest.files[${index}].hash`),\n bytes: item.bytes as number,\n records: item.records as number,\n };\n });\n const objects = raw.objects.map((value, index) => {\n const item = record(value, `manifest.objects[${index}]`);\n const hash = normalizeHash(item.hash, `manifest.objects[${index}].hash`);\n if (item.path !== objectPath(hash) || typeof item.included !== 'boolean') {\n throw new Error(`manifest.objects[${index}] is invalid.`);\n }\n if (\n item.bytes !== undefined &&\n (!Number.isSafeInteger(item.bytes) || (item.bytes as number) < 0)\n ) {\n throw new Error(`manifest.objects[${index}].bytes is invalid.`);\n }\n return {\n path: item.path as string,\n hash,\n included: item.included,\n ...(item.bytes === undefined ? {} : { bytes: item.bytes as number }),\n ...(item.mediaType === undefined\n ? {}\n : { mediaType: requiredString(item.mediaType, `manifest.objects[${index}].mediaType`) }),\n };\n });\n return {\n schemaVersion: 1,\n // Echoed back, not normalized: it is part of the content hash input.\n format: raw.format,\n appId: requiredString(raw.appId, 'manifest.appId'),\n branchId: requiredString(raw.branchId, 'manifest.branchId'),\n revisionWatermark: requiredString(\n raw.revisionWatermark,\n 'manifest.revisionWatermark',\n ),\n ...(raw.createdAt === undefined\n ? {}\n : { createdAt: requiredString(raw.createdAt, 'manifest.createdAt') }),\n files,\n objects,\n exclusions: raw.exclusions as string[],\n archiveContentHash: normalizeHash(\n raw.archiveContentHash,\n 'manifest.archiveContentHash',\n ),\n };\n}\n\nasync function parseEntities(\n value: unknown,\n collection: TranslationArchiveCollection,\n): Promise<TranslationArchiveEntity[]> {\n if (!Array.isArray(value)) {\n throw new Error(`Archive ${collection} file must contain an array.`);\n }\n const entities: TranslationArchiveEntity[] = [];\n const ids = new Set<string>();\n for (const [index, rawValue] of value.entries()) {\n const raw = record(rawValue, `${collection}[${index}]`);\n const normalized = normalizeEntityInput(\n {\n id: raw.id as string,\n data: raw.data as Record<string, TranslationArchiveJson>,\n },\n `${collection}[${index}]`,\n );\n const contentHash = normalizeHash(\n raw.contentHash,\n `${collection}[${index}].contentHash`,\n );\n const actual = await sha256Bytes(canonicalBytes(normalized));\n if (actual !== contentHash) {\n throw new Error(\n `Archive ${collection} record \"${normalized.id}\" has an invalid content hash.`,\n );\n }\n if (ids.has(normalized.id)) {\n throw new Error(\n `Archive collection \"${collection}\" contains duplicate id \"${normalized.id}\".`,\n );\n }\n ids.add(normalized.id);\n entities.push({ ...normalized, contentHash });\n }\n return entities.sort((left, right) => left.id.localeCompare(right.id));\n}\n\n/** Parse and verify every declared content file and optional catalog object. */\nexport async function parseTranslationArchive(\n input: Uint8Array,\n options: { limits?: Partial<TranslationJsonArchiveLimits> } = {},\n): Promise<ParsedTranslationArchive> {\n const files = safeUnzipTranslationZip(input, {\n ...DEFAULT_TRANSLATION_ARCHIVE_LIMITS,\n ...options.limits,\n });\n const manifestPath =\n files[TRANSLATION_ARCHIVE_MANIFEST_PATH] !== undefined\n ? TRANSLATION_ARCHIVE_MANIFEST_PATH\n : LEGACY_TRANSLATION_ARCHIVE_MANIFEST_PATH;\n const manifestBody = files[manifestPath];\n if (!manifestBody) {\n throw new Error('Translation archive manifest is missing.');\n }\n const manifest = parseManifest(decodeJson(manifestBody, manifestPath));\n const actualArchiveHash = await sha256Bytes(\n canonicalBytes(\n archiveContentHashInput(\n manifest as Omit<TranslationArchiveManifest, 'archiveContentHash'>,\n ),\n ),\n );\n if (actualArchiveHash !== manifest.archiveContentHash) {\n throw new Error('Translation archive manifest content hash is invalid.');\n }\n\n const allowedPaths = new Set<string>([\n manifestPath,\n ...manifest.files.map(({ path }) => path),\n ...manifest.objects.filter(({ included }) => included).map(({ path }) => path),\n ]);\n const unexpected = Object.keys(files).find((path) => !allowedPaths.has(path));\n if (unexpected) {\n throw new Error(`Translation archive contains undeclared file \"${unexpected}\".`);\n }\n if (manifest.files.length !== TRANSLATION_ARCHIVE_COLLECTIONS.length) {\n throw new Error('Translation archive must inventory every content collection.');\n }\n\n const collections = Object.create(null) as Record<\n TranslationArchiveCollection,\n TranslationArchiveEntity[]\n >;\n for (const item of manifest.files) {\n const body = files[item.path];\n if (!body) throw new Error(`Translation archive is missing \"${item.path}\".`);\n if (\n body.byteLength !== item.bytes ||\n (await sha256Bytes(body)) !== item.hash\n ) {\n throw new Error(`Translation archive file \"${item.path}\" failed verification.`);\n }\n const entities = await parseEntities(\n decodeJson(body, item.path),\n item.collection,\n );\n if (entities.length !== item.records) {\n throw new Error(`Translation archive file \"${item.path}\" count is invalid.`);\n }\n collections[item.collection] = entities;\n }\n\n const objects = await Promise.all(\n manifest.objects.map(async (item) => {\n const body = files[item.path];\n if (item.included) {\n if (!body) {\n throw new Error(`Translation archive object \"${item.hash}\" is missing.`);\n }\n if (\n (item.bytes !== undefined && body.byteLength !== item.bytes) ||\n (await sha256Bytes(body)) !== item.hash\n ) {\n throw new Error(\n `Translation archive object \"${item.hash}\" failed verification.`,\n );\n }\n } else if (body) {\n throw new Error(\n `Translation archive reference-only object \"${item.hash}\" unexpectedly has a body.`,\n );\n }\n return {\n hash: item.hash,\n ...(body ? { body } : {}),\n ...(item.mediaType ? { mediaType: item.mediaType } : {}),\n };\n }),\n );\n return { manifest, collections, objects };\n}\n\nfunction rebaseValue(\n value: TranslationArchiveJson,\n sourceAppId: string,\n targetAppId: string,\n mappings: Record<string, string>,\n): TranslationArchiveJson {\n if (typeof value === 'string') {\n if (value === sourceAppId || value.startsWith(`${sourceAppId}/`)) {\n const target = `${targetAppId}${value.slice(sourceAppId.length)}`;\n mappings[value] = target;\n return target;\n }\n return value;\n }\n if (Array.isArray(value)) {\n return value.map((item) =>\n rebaseValue(item, sourceAppId, targetAppId, mappings),\n );\n }\n if (value && typeof value === 'object') {\n const output: Record<string, TranslationArchiveJson> = {};\n for (const [key, item] of Object.entries(value)) {\n output[key] = rebaseValue(\n item,\n sourceAppId,\n targetAppId,\n mappings,\n );\n }\n return output;\n }\n return value;\n}\n\nfunction contentIdentity(\n nodeIri: string,\n fieldIri: string,\n): string {\n return JSON.stringify([nodeIri, fieldIri]);\n}\n\n/**\n * Build a pure, conditional restore plan. A blocked plan is never executable:\n * collisions and unresolved cross-app content mappings must be resolved first.\n */\nexport async function planTranslationArchiveRestore(\n archive: ParsedTranslationArchive,\n options: PlanTranslationArchiveRestoreOptions,\n): Promise<TranslationArchiveRestorePlan> {\n const sourceAppId = archive.manifest.appId;\n const targetAppId = requiredString(options.targetAppId, 'targetAppId');\n if (options.mode === 'same-app' && sourceAppId !== targetAppId) {\n throw new Error(\n `Same-app restore requires target app \"${sourceAppId}\".`,\n );\n }\n const targetById = new Map(\n (options.targetRecords ?? []).map((item) => [\n requiredString(item.id, 'targetRecords.id'),\n normalizeHash(item.contentHash, 'targetRecords.contentHash'),\n ]),\n );\n const contentMappings = new Map(\n (options.contentMappings ?? []).map((mapping) => [\n contentIdentity(mapping.sourceNodeIri, mapping.sourceFieldIri),\n mapping,\n ]),\n );\n const iriMappings: Record<string, string> = {};\n const decisions: TranslationArchiveRestoreDecision[] = [];\n let historicalReleases = 0;\n\n for (const collection of TRANSLATION_ARCHIVE_COLLECTIONS) {\n for (const sourceRecord of archive.collections[collection]) {\n let targetId = sourceRecord.id;\n let data = sourceRecord.data;\n if (options.mode === 'cross-app') {\n targetId = rebaseValue(\n sourceRecord.id,\n sourceAppId,\n targetAppId,\n iriMappings,\n ) as string;\n data = rebaseValue(\n sourceRecord.data,\n sourceAppId,\n targetAppId,\n iriMappings,\n ) as Record<string, TranslationArchiveJson>;\n }\n\n if (collection === 'content' && options.mode === 'cross-app') {\n const sourceNodeIri =\n typeof sourceRecord.data.nodeIri === 'string'\n ? sourceRecord.data.nodeIri\n : '';\n const sourceFieldIri =\n typeof sourceRecord.data.fieldIri === 'string'\n ? sourceRecord.data.fieldIri\n : '';\n const mapping = contentMappings.get(\n contentIdentity(sourceNodeIri, sourceFieldIri),\n );\n if (!mapping) {\n decisions.push({\n collection,\n sourceId: sourceRecord.id,\n targetId,\n action: 'unmapped',\n reason: 'Cross-app content requires an explicit node/field mapping.',\n retryKey: canonicalJson([\n archive.manifest.archiveContentHash,\n collection,\n sourceRecord.id,\n 'unmapped',\n ]),\n });\n continue;\n }\n data = {\n ...data,\n nodeIri: mapping.targetNodeIri,\n fieldIri: mapping.targetFieldIri,\n };\n iriMappings[sourceNodeIri] = mapping.targetNodeIri;\n iriMappings[sourceFieldIri] = mapping.targetFieldIri;\n }\n\n if (collection === 'releases') {\n historicalReleases += 1;\n data = {\n ...data,\n status: 'retired',\n historical: true,\n activationEligible: false,\n };\n }\n const targetRecordInput = { id: targetId, data };\n const targetRecord: TranslationArchiveEntity = {\n ...targetRecordInput,\n contentHash: await sha256Bytes(canonicalBytes(targetRecordInput)),\n };\n const currentHash = targetById.get(targetId);\n const action =\n currentHash === undefined\n ? 'create'\n : currentHash === targetRecord.contentHash\n ? 'skip'\n : 'collision';\n decisions.push({\n collection,\n sourceId: sourceRecord.id,\n targetId,\n action,\n ...(currentHash ? { expectedTargetContentHash: currentHash } : {}),\n record: targetRecord,\n ...(action === 'collision'\n ? { reason: 'Target id exists with different content.' }\n : {}),\n retryKey: canonicalJson([\n archive.manifest.archiveContentHash,\n collection,\n targetId,\n targetRecord.contentHash,\n ]),\n });\n }\n }\n\n const collisions = decisions.filter(\n ({ action }) => action === 'collision',\n ).length;\n const unresolvedContent = decisions.filter(\n ({ action }) => action === 'unmapped',\n ).length;\n return {\n schemaVersion: 1,\n mode: options.mode,\n sourceAppId,\n targetAppId,\n sourceBranchId: archive.manifest.branchId,\n archiveContentHash: archive.manifest.archiveContentHash,\n archiveRevisionWatermark: archive.manifest.revisionWatermark,\n ...(options.targetRevisionWatermark\n ? {\n expectedTargetRevisionWatermark: requiredString(\n options.targetRevisionWatermark,\n 'targetRevisionWatermark',\n ),\n }\n : {}),\n blocked: collisions > 0 || unresolvedContent > 0,\n decisions,\n iriMappings,\n creates: decisions.filter(({ action }) => action === 'create').length,\n skips: decisions.filter(({ action }) => action === 'skip').length,\n collisions,\n unresolvedContent,\n historicalReleases,\n identityWrites: 0,\n assignmentWrites: 0,\n };\n}\n"]}
@@ -8,7 +8,7 @@ export interface TranslationAuthorizationRequest {
8
8
  export type TranslationAuthorizationResolver = (request: TranslationAuthorizationRequest) => Promise<boolean>;
9
9
  /**
10
10
  * Host hook: the host decides who may read and author an app's translations
11
- * (Create Now evaluates its translator assignments and project roles here).
11
+ * (for example by evaluating its own translator assignments and project roles).
12
12
  *
13
13
  * The resolver is the only source of a "yes". A host that does not configure
14
14
  * one gets no authoring or read access over the translation provider; only the
@@ -1,7 +1,7 @@
1
1
  let resolver;
2
2
  /**
3
3
  * Host hook: the host decides who may read and author an app's translations
4
- * (Create Now evaluates its translator assignments and project roles here).
4
+ * (for example by evaluating its own translator assignments and project roles).
5
5
  *
6
6
  * The resolver is the only source of a "yes". A host that does not configure
7
7
  * one gets no authoring or read access over the translation provider; only the
@@ -1 +1 @@
1
- {"version":3,"file":"authorization.js","sourceRoot":"","sources":["../../src/authorization.ts"],"names":[],"mappings":"AAkBA,IAAI,QAAsD,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAC/C,IAAuC;IAEvC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAwC;IAExC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,CAAC,CAAC;QAC7E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["export type TranslationAuthoringAction =\n | 'manage'\n | 'review'\n | 'propose'\n | 'run-mt'\n | 'read';\n\nexport interface TranslationAuthorizationRequest {\n appId: string;\n actorWebId: string;\n action: TranslationAuthoringAction;\n language?: string;\n}\n\nexport type TranslationAuthorizationResolver = (\n request: TranslationAuthorizationRequest,\n) => Promise<boolean>;\n\nlet resolver: TranslationAuthorizationResolver | undefined;\n\n/**\n * Host hook: the host decides who may read and author an app's translations\n * (Create Now evaluates its translator assignments and project roles here).\n *\n * The resolver is the only source of a \"yes\". A host that does not configure\n * one gets no authoring or read access over the translation provider; only the\n * public runtime fetch (`getMessages`) keeps working.\n */\nexport function configureTranslationAuthorization(\n next?: TranslationAuthorizationResolver,\n): void {\n resolver = next;\n}\n\n/**\n * Whether the host's resolver grants `request`. Denies when no resolver is\n * configured, and when the resolver throws or answers anything but `true`.\n */\nexport async function canAuthorTranslation(\n request: TranslationAuthorizationRequest,\n): Promise<boolean> {\n if (!resolver) return false;\n try {\n return (await resolver(request)) === true;\n } catch (error) {\n console.warn('[translation] authorization resolver failed; denying.', error);\n return false;\n }\n}\n"]}
1
+ {"version":3,"file":"authorization.js","sourceRoot":"","sources":["../../src/authorization.ts"],"names":[],"mappings":"AAkBA,IAAI,QAAsD,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAC/C,IAAuC;IAEvC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAwC;IAExC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,CAAC,CAAC;QAC7E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["export type TranslationAuthoringAction =\n | 'manage'\n | 'review'\n | 'propose'\n | 'run-mt'\n | 'read';\n\nexport interface TranslationAuthorizationRequest {\n appId: string;\n actorWebId: string;\n action: TranslationAuthoringAction;\n language?: string;\n}\n\nexport type TranslationAuthorizationResolver = (\n request: TranslationAuthorizationRequest,\n) => Promise<boolean>;\n\nlet resolver: TranslationAuthorizationResolver | undefined;\n\n/**\n * Host hook: the host decides who may read and author an app's translations\n * (for example by evaluating its own translator assignments and project roles).\n *\n * The resolver is the only source of a \"yes\". A host that does not configure\n * one gets no authoring or read access over the translation provider; only the\n * public runtime fetch (`getMessages`) keeps working.\n */\nexport function configureTranslationAuthorization(\n next?: TranslationAuthorizationResolver,\n): void {\n resolver = next;\n}\n\n/**\n * Whether the host's resolver grants `request`. Denies when no resolver is\n * configured, and when the resolver throws or answers anything but `true`.\n */\nexport async function canAuthorTranslation(\n request: TranslationAuthorizationRequest,\n): Promise<boolean> {\n if (!resolver) return false;\n try {\n return (await resolver(request)) === true;\n } catch (error) {\n console.warn('[translation] authorization resolver failed; denying.', error);\n return false;\n }\n}\n"]}
@@ -9,7 +9,7 @@ import { type TranslationLanguageDefinition } from './languages.js';
9
9
  * Any source that yields a non-empty map wins; all-empty ⇒ `{}` and the client
10
10
  * keeps its inline English defaults. Never throws.
11
11
  *
12
- * Portable (no CN import): the caller wires `base`/`devApi` for its environment.
12
+ * Portable (no host-application import): the caller wires `base`/`devApi` for its environment.
13
13
  */
14
14
  import type { TranslationMessages } from './core/messages.js';
15
15
  import { type TranslationBuildDescriptor } from './release.js';
@@ -1 +1 @@
1
- {"version":3,"file":"cdn-loader.js","sourceRoot":"","sources":["../../src/cdn-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineLanguage,\n validateLanguageDefinitions,\n type TranslationLanguageDefinition,\n} from './languages.js';\n/**\n * `loadMessages` factory for the CDN delivery cascade (Plan 016 P2.4, AD-J/AD-K).\n * SIMPLE-POINTER mode (the ratified default): fetch `${base}/{lang}.json` from\n * the published CDN — changing `base` (an app's `translation:cdnTarget`) repoints at\n * different cloud files with no call-site change, exactly the Tolgee model.\n * `bundledPath` (same-origin bundled copy) and `devApi` (e.g. the `getMessages`\n * RPC for live dev editing) are optional fallbacks, tried in the order given.\n * Any source that yields a non-empty map wins; all-empty ⇒ `{}` and the client\n * keeps its inline English defaults. Never throws.\n *\n * Portable (no CN import): the caller wires `base`/`devApi` for its environment.\n */\nimport type { TranslationMessages } from './core/messages.js';\nimport { sha256Hex } from './key-version.js';\nimport {\n applyCatalogPatches,\n type TranslationBuildDescriptor,\n type TranslationCatalogPatch,\n type TranslationReleaseManifest,\n} from './release.js';\n\nexport interface CdnLoaderConfig {\n /** Published CDN base — `${base}/{lang}.json`. The repoint knob. Omit to skip. */\n base?: string;\n /** Same-origin bundled base — `${bundledPath}/{lang}.json`. Omit to skip. */\n bundledPath?: string;\n /** Last-resort loader (e.g. a live backend RPC). Omit to skip. */\n devApi?: (language: string) => Promise<TranslationMessages>;\n /** Injectable for tests / non-DOM runtimes. */\n fetchImpl?: typeof fetch;\n}\n\nfunction isNonEmptyMap(v: unknown): v is TranslationMessages {\n return !!v && typeof v === 'object' && Object.keys(v as object).length > 0;\n}\n\nexport function createCdnLoader(\n config: CdnLoaderConfig\n): (language: string) => Promise<TranslationMessages> {\n const doFetch =\n config.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);\n const base = config.base?.replace(/\\/+$/, '');\n const bundled = config.bundledPath?.replace(/\\/+$/, '');\n\n const fetchJson = async (\n url: string\n ): Promise<TranslationMessages | null> => {\n if (!doFetch) return null;\n try {\n const res = await doFetch(url);\n if (!res.ok) return null;\n return (await res.json()) as TranslationMessages;\n } catch {\n return null;\n }\n };\n\n return async (language: string): Promise<TranslationMessages> => {\n const sources: Array<() => Promise<TranslationMessages | null>> = [];\n if (base) sources.push(() => fetchJson(`${base}/${language}.json`));\n if (bundled) sources.push(() => fetchJson(`${bundled}/${language}.json`));\n if (config.devApi)\n sources.push(async () => {\n try {\n return await config.devApi!(language);\n } catch {\n return null;\n }\n });\n\n for (const source of sources) {\n const result = await source();\n if (isNonEmptyMap(result)) return result;\n }\n return {};\n };\n}\n\nexport interface ReleaseLoaderConfig {\n bundledPath: string;\n descriptor:\n | TranslationBuildDescriptor\n | (() => Promise<TranslationBuildDescriptor | null>);\n fetchImpl?: typeof fetch;\n devApi?: (language: string) => Promise<TranslationMessages>;\n}\n\nfunction resolveReleaseUrl(url: string, manifestUrl: string): string {\n try {\n return new URL(url, manifestUrl).toString();\n } catch {\n return url;\n }\n}\n\nfunction isReleaseManifest(\n value: unknown\n): value is TranslationReleaseManifest {\n const manifest = value as Partial<TranslationReleaseManifest> | undefined;\n return (\n manifest?.schemaVersion === 2 &&\n typeof manifest.releaseId === 'string' &&\n typeof manifest.contractSetHash === 'string' &&\n !!manifest.keyVersions &&\n typeof manifest.keyVersions === 'object' &&\n !!manifest.languages &&\n typeof manifest.languages === 'object'\n );\n}\n\n/**\n * Schema-v2 release loader. Bundled messages always win the first render;\n * compatible immutable CDN objects fetched in the background become visible\n * only on the next call for that language.\n */\nexport function createReleaseLoader(\n config: ReleaseLoaderConfig\n): (language: string) => Promise<TranslationMessages> {\n const doFetch =\n config.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);\n const bundled = config.bundledPath.replace(/\\/+$/, '');\n const objectCache = new Map<string, unknown>();\n const pending = new Map<string, Promise<TranslationMessages | null>>();\n const requested = new Set<string>();\n let descriptorPromise: Promise<TranslationBuildDescriptor | null> | undefined;\n const getDescriptor = () =>\n (descriptorPromise ??=\n typeof config.descriptor === 'function'\n ? config.descriptor()\n : Promise.resolve(config.descriptor));\n\n const fetchText = async (url: string): Promise<string | null> => {\n if (!doFetch) return null;\n try {\n const response = await doFetch(url);\n return response.ok ? await response.text() : null;\n } catch {\n return null;\n }\n };\n\n const fetchMessages = async (\n url: string\n ): Promise<TranslationMessages | null> => {\n const text = await fetchText(url);\n if (text === null) return null;\n try {\n const parsed = JSON.parse(text);\n return isNonEmptyMap(parsed) ? parsed : null;\n } catch {\n return null;\n }\n };\n\n const fetchHashed = async <T>(\n url: string,\n hash: string\n ): Promise<T | null> => {\n if (objectCache.has(hash)) return objectCache.get(hash) as T;\n const text = await fetchText(url);\n if (text === null || (await sha256Hex(text)) !== hash) return null;\n try {\n const parsed = JSON.parse(text) as T;\n objectCache.set(hash, parsed);\n return parsed;\n } catch {\n return null;\n }\n };\n\n const fetchReleaseLanguage = async (\n language: string\n ): Promise<TranslationMessages | null> => {\n const descriptor = await getDescriptor();\n if (!descriptor) return null;\n const manifestText = await fetchText(descriptor.manifestUrl);\n if (manifestText === null) return null;\n let manifest: unknown;\n try {\n manifest = JSON.parse(manifestText);\n } catch {\n return null;\n }\n if (\n !isReleaseManifest(manifest) ||\n manifest.releaseId !== descriptor.releaseId ||\n manifest.contractSetHash !== descriptor.contractSetHash\n ) {\n return null;\n }\n const lane = manifest.languages[language];\n if (!lane) return null;\n const base = await fetchHashed<TranslationMessages>(\n resolveReleaseUrl(lane.base.url, descriptor.manifestUrl),\n lane.base.hash\n );\n if (!base) return null;\n const patches: TranslationCatalogPatch[] = [];\n for (const patch of [...lane.patches].sort(\n (left, right) => left.sequence - right.sequence\n )) {\n const body = await fetchHashed<TranslationCatalogPatch>(\n resolveReleaseUrl(patch.url, descriptor.manifestUrl),\n patch.hash\n );\n if (!body) return null;\n patches.push(body);\n }\n return applyCatalogPatches(base, patches);\n };\n\n return async (language: string): Promise<TranslationMessages> => {\n const bundledMessages = await fetchMessages(`${bundled}/${language}.json`);\n const existing = pending.get(language);\n if (requested.has(language) && existing) {\n const released = await existing;\n return released ?? bundledMessages ?? {};\n }\n requested.add(language);\n const background = fetchReleaseLanguage(language);\n pending.set(language, background);\n if (bundledMessages) {\n void background.catch(() => null);\n return bundledMessages;\n }\n const released = await background;\n if (released) return released;\n if (config.devApi) {\n try {\n return await config.devApi(language);\n } catch {\n // Preserve the empty fallback contract.\n }\n }\n return {};\n };\n}\n\n/** Fetch public language resources independently of a compiled application. */\nexport function createCdnLanguageLoader(\n config: Pick<CdnLoaderConfig, 'base' | 'fetchImpl'>\n): () => Promise<TranslationLanguageDefinition[]> {\n return async () => {\n if (!config.base) throw new Error('A translation CDN base is required.');\n const response = await (config.fetchImpl ?? fetch)(\n `${config.base.replace(/\\/+$/, '')}/languages.json`\n );\n if (!response.ok)\n throw new Error(`Language catalog HTTP ${response.status}`);\n const catalog = await response.json();\n if (catalog.schemaVersion !== 1 || !Array.isArray(catalog.languages))\n throw new Error('Invalid language catalog.');\n const definitions = catalog.languages.map(\n (item: TranslationLanguageDefinition) => defineLanguage(item.code, item)\n );\n validateLanguageDefinitions(definitions);\n return definitions;\n };\n}\n"]}
1
+ {"version":3,"file":"cdn-loader.js","sourceRoot":"","sources":["../../src/cdn-loader.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineLanguage,\n validateLanguageDefinitions,\n type TranslationLanguageDefinition,\n} from './languages.js';\n/**\n * `loadMessages` factory for the CDN delivery cascade (Plan 016 P2.4, AD-J/AD-K).\n * SIMPLE-POINTER mode (the ratified default): fetch `${base}/{lang}.json` from\n * the published CDN — changing `base` (an app's `translation:cdnTarget`) repoints at\n * different cloud files with no call-site change, exactly the Tolgee model.\n * `bundledPath` (same-origin bundled copy) and `devApi` (e.g. the `getMessages`\n * RPC for live dev editing) are optional fallbacks, tried in the order given.\n * Any source that yields a non-empty map wins; all-empty ⇒ `{}` and the client\n * keeps its inline English defaults. Never throws.\n *\n * Portable (no host-application import): the caller wires `base`/`devApi` for its environment.\n */\nimport type { TranslationMessages } from './core/messages.js';\nimport { sha256Hex } from './key-version.js';\nimport {\n applyCatalogPatches,\n type TranslationBuildDescriptor,\n type TranslationCatalogPatch,\n type TranslationReleaseManifest,\n} from './release.js';\n\nexport interface CdnLoaderConfig {\n /** Published CDN base — `${base}/{lang}.json`. The repoint knob. Omit to skip. */\n base?: string;\n /** Same-origin bundled base — `${bundledPath}/{lang}.json`. Omit to skip. */\n bundledPath?: string;\n /** Last-resort loader (e.g. a live backend RPC). Omit to skip. */\n devApi?: (language: string) => Promise<TranslationMessages>;\n /** Injectable for tests / non-DOM runtimes. */\n fetchImpl?: typeof fetch;\n}\n\nfunction isNonEmptyMap(v: unknown): v is TranslationMessages {\n return !!v && typeof v === 'object' && Object.keys(v as object).length > 0;\n}\n\nexport function createCdnLoader(\n config: CdnLoaderConfig\n): (language: string) => Promise<TranslationMessages> {\n const doFetch =\n config.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);\n const base = config.base?.replace(/\\/+$/, '');\n const bundled = config.bundledPath?.replace(/\\/+$/, '');\n\n const fetchJson = async (\n url: string\n ): Promise<TranslationMessages | null> => {\n if (!doFetch) return null;\n try {\n const res = await doFetch(url);\n if (!res.ok) return null;\n return (await res.json()) as TranslationMessages;\n } catch {\n return null;\n }\n };\n\n return async (language: string): Promise<TranslationMessages> => {\n const sources: Array<() => Promise<TranslationMessages | null>> = [];\n if (base) sources.push(() => fetchJson(`${base}/${language}.json`));\n if (bundled) sources.push(() => fetchJson(`${bundled}/${language}.json`));\n if (config.devApi)\n sources.push(async () => {\n try {\n return await config.devApi!(language);\n } catch {\n return null;\n }\n });\n\n for (const source of sources) {\n const result = await source();\n if (isNonEmptyMap(result)) return result;\n }\n return {};\n };\n}\n\nexport interface ReleaseLoaderConfig {\n bundledPath: string;\n descriptor:\n | TranslationBuildDescriptor\n | (() => Promise<TranslationBuildDescriptor | null>);\n fetchImpl?: typeof fetch;\n devApi?: (language: string) => Promise<TranslationMessages>;\n}\n\nfunction resolveReleaseUrl(url: string, manifestUrl: string): string {\n try {\n return new URL(url, manifestUrl).toString();\n } catch {\n return url;\n }\n}\n\nfunction isReleaseManifest(\n value: unknown\n): value is TranslationReleaseManifest {\n const manifest = value as Partial<TranslationReleaseManifest> | undefined;\n return (\n manifest?.schemaVersion === 2 &&\n typeof manifest.releaseId === 'string' &&\n typeof manifest.contractSetHash === 'string' &&\n !!manifest.keyVersions &&\n typeof manifest.keyVersions === 'object' &&\n !!manifest.languages &&\n typeof manifest.languages === 'object'\n );\n}\n\n/**\n * Schema-v2 release loader. Bundled messages always win the first render;\n * compatible immutable CDN objects fetched in the background become visible\n * only on the next call for that language.\n */\nexport function createReleaseLoader(\n config: ReleaseLoaderConfig\n): (language: string) => Promise<TranslationMessages> {\n const doFetch =\n config.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);\n const bundled = config.bundledPath.replace(/\\/+$/, '');\n const objectCache = new Map<string, unknown>();\n const pending = new Map<string, Promise<TranslationMessages | null>>();\n const requested = new Set<string>();\n let descriptorPromise: Promise<TranslationBuildDescriptor | null> | undefined;\n const getDescriptor = () =>\n (descriptorPromise ??=\n typeof config.descriptor === 'function'\n ? config.descriptor()\n : Promise.resolve(config.descriptor));\n\n const fetchText = async (url: string): Promise<string | null> => {\n if (!doFetch) return null;\n try {\n const response = await doFetch(url);\n return response.ok ? await response.text() : null;\n } catch {\n return null;\n }\n };\n\n const fetchMessages = async (\n url: string\n ): Promise<TranslationMessages | null> => {\n const text = await fetchText(url);\n if (text === null) return null;\n try {\n const parsed = JSON.parse(text);\n return isNonEmptyMap(parsed) ? parsed : null;\n } catch {\n return null;\n }\n };\n\n const fetchHashed = async <T>(\n url: string,\n hash: string\n ): Promise<T | null> => {\n if (objectCache.has(hash)) return objectCache.get(hash) as T;\n const text = await fetchText(url);\n if (text === null || (await sha256Hex(text)) !== hash) return null;\n try {\n const parsed = JSON.parse(text) as T;\n objectCache.set(hash, parsed);\n return parsed;\n } catch {\n return null;\n }\n };\n\n const fetchReleaseLanguage = async (\n language: string\n ): Promise<TranslationMessages | null> => {\n const descriptor = await getDescriptor();\n if (!descriptor) return null;\n const manifestText = await fetchText(descriptor.manifestUrl);\n if (manifestText === null) return null;\n let manifest: unknown;\n try {\n manifest = JSON.parse(manifestText);\n } catch {\n return null;\n }\n if (\n !isReleaseManifest(manifest) ||\n manifest.releaseId !== descriptor.releaseId ||\n manifest.contractSetHash !== descriptor.contractSetHash\n ) {\n return null;\n }\n const lane = manifest.languages[language];\n if (!lane) return null;\n const base = await fetchHashed<TranslationMessages>(\n resolveReleaseUrl(lane.base.url, descriptor.manifestUrl),\n lane.base.hash\n );\n if (!base) return null;\n const patches: TranslationCatalogPatch[] = [];\n for (const patch of [...lane.patches].sort(\n (left, right) => left.sequence - right.sequence\n )) {\n const body = await fetchHashed<TranslationCatalogPatch>(\n resolveReleaseUrl(patch.url, descriptor.manifestUrl),\n patch.hash\n );\n if (!body) return null;\n patches.push(body);\n }\n return applyCatalogPatches(base, patches);\n };\n\n return async (language: string): Promise<TranslationMessages> => {\n const bundledMessages = await fetchMessages(`${bundled}/${language}.json`);\n const existing = pending.get(language);\n if (requested.has(language) && existing) {\n const released = await existing;\n return released ?? bundledMessages ?? {};\n }\n requested.add(language);\n const background = fetchReleaseLanguage(language);\n pending.set(language, background);\n if (bundledMessages) {\n void background.catch(() => null);\n return bundledMessages;\n }\n const released = await background;\n if (released) return released;\n if (config.devApi) {\n try {\n return await config.devApi(language);\n } catch {\n // Preserve the empty fallback contract.\n }\n }\n return {};\n };\n}\n\n/** Fetch public language resources independently of a compiled application. */\nexport function createCdnLanguageLoader(\n config: Pick<CdnLoaderConfig, 'base' | 'fetchImpl'>\n): () => Promise<TranslationLanguageDefinition[]> {\n return async () => {\n if (!config.base) throw new Error('A translation CDN base is required.');\n const response = await (config.fetchImpl ?? fetch)(\n `${config.base.replace(/\\/+$/, '')}/languages.json`\n );\n if (!response.ok)\n throw new Error(`Language catalog HTTP ${response.status}`);\n const catalog = await response.json();\n if (catalog.schemaVersion !== 1 || !Array.isArray(catalog.languages))\n throw new Error('Invalid language catalog.');\n const definitions = catalog.languages.map(\n (item: TranslationLanguageDefinition) => defineLanguage(item.code, item)\n );\n validateLanguageDefinitions(definitions);\n return definitions;\n };\n}\n"]}
@@ -7,12 +7,25 @@ export interface FormattedTranslationMessage {
7
7
  export type TranslationMessage = string | FormattedTranslationMessage;
8
8
  /** key → message template, for a single language. */
9
9
  export type TranslationMessages = Record<string, TranslationMessage>;
10
+ /**
11
+ * Development pseudo-locale used by chrome coverage checks. Keeping this in
12
+ * the framework-light core means React and non-React clients exercise the
13
+ * exact same fallback path. The wrapper makes missed layout allowance visible;
14
+ * the accented glyphs make an untranslated English fallback unmistakable.
15
+ */
16
+ export declare function pseudoLocalize(message: string): string;
17
+ /**
18
+ * Layout-stress pseudo mode. Unlike en-XA's accented fallback, this deliberately
19
+ * expands the resolved message—including a real translated catalog value—so a
20
+ * preview can expose controls that only fit their English copy.
21
+ */
22
+ export declare function pseudoExpand(message: string): string;
10
23
  /** FormatSimple: replace `{name}` placeholders with `params.name`. Unknown → left as-is. */
11
24
  export declare function interpolate(template: string, params?: Record<string, unknown>): string;
12
25
  /**
13
26
  * Full ICU MessageFormat using the active locale's CLDR plural rules. Invalid
14
27
  * authoring data never crashes the host app: the unformatted template remains
15
- * visible and Translation Studio reports the syntax error for repair.
28
+ * visible and authoring tools can report the syntax error for repair.
16
29
  */
17
30
  export declare function formatIcu(template: string, params?: Record<string, unknown>, locale?: string): string;
18
31
  /**
@@ -12,6 +12,34 @@
12
12
  */
13
13
  import { localeDirection } from '../languages.js';
14
14
  import IntlMessageFormat from 'intl-messageformat';
15
+ /**
16
+ * Development pseudo-locale used by chrome coverage checks. Keeping this in
17
+ * the framework-light core means React and non-React clients exercise the
18
+ * exact same fallback path. The wrapper makes missed layout allowance visible;
19
+ * the accented glyphs make an untranslated English fallback unmistakable.
20
+ */
21
+ export function pseudoLocalize(message) {
22
+ const accents = {
23
+ A: 'Å', B: 'Ɓ', C: 'Ç', D: 'Ð', E: 'Ë', F: 'Ƒ', G: 'Ĝ', H: 'Ĥ', I: 'Ï',
24
+ J: 'Ĵ', K: 'Ķ', L: 'Ļ', M: 'Ṁ', N: 'Ñ', O: 'Ö', P: 'Ƥ', Q: 'Ǫ', R: 'Ŕ',
25
+ S: 'Š', T: 'Ŧ', U: 'Ü', V: 'Ṽ', W: 'Ŵ', X: 'Ẍ', Y: 'Ŷ', Z: 'Ž',
26
+ a: 'å', b: 'ƀ', c: 'ç', d: 'đ', e: 'ë', f: 'ƒ', g: 'ĝ', h: 'ĥ', i: 'ï',
27
+ j: 'ĵ', k: 'ķ', l: 'ļ', m: 'ṁ', n: 'ñ', o: 'ö', p: 'ƥ', q: 'ǫ', r: 'ŕ',
28
+ s: 'š', t: 'ŧ', u: 'ü', v: 'ṽ', w: 'ŵ', x: 'ẍ', y: 'ŷ', z: 'ž',
29
+ };
30
+ const accented = message.replace(/[A-Za-z]/g, (character) => accents[character]);
31
+ return `[${accented} ···]`;
32
+ }
33
+ /**
34
+ * Layout-stress pseudo mode. Unlike en-XA's accented fallback, this deliberately
35
+ * expands the resolved message—including a real translated catalog value—so a
36
+ * preview can expose controls that only fit their English copy.
37
+ */
38
+ export function pseudoExpand(message) {
39
+ const visibleLength = [...message].length;
40
+ const padding = '·'.repeat(Math.max(3, Math.ceil(visibleLength * 0.35)));
41
+ return `[${message} ${padding}]`;
42
+ }
15
43
  /** FormatSimple: replace `{name}` placeholders with `params.name`. Unknown → left as-is. */
16
44
  export function interpolate(template, params) {
17
45
  if (!params)
@@ -37,7 +65,7 @@ function compiledIcu(template, locale) {
37
65
  /**
38
66
  * Full ICU MessageFormat using the active locale's CLDR plural rules. Invalid
39
67
  * authoring data never crashes the host app: the unformatted template remains
40
- * visible and Translation Studio reports the syntax error for repair.
68
+ * visible and authoring tools can report the syntax error for repair.
41
69
  */
42
70
  export function formatIcu(template, params, locale = 'en') {
43
71
  try {
@@ -59,9 +87,12 @@ export function translate(messages, key, defaultValue, params, format = 'simple'
59
87
  ? stored
60
88
  : (_b = (_a = stored === null || stored === void 0 ? void 0 : stored.message) !== null && _a !== void 0 ? _a : defaultValue) !== null && _b !== void 0 ? _b : key;
61
89
  const resolvedFormat = typeof stored === 'object' ? stored.format : format;
62
- return resolvedFormat === 'icu'
90
+ const formatted = resolvedFormat === 'icu'
63
91
  ? formatIcu(template, params, locale)
64
92
  : interpolate(template, params);
93
+ return stored === undefined && locale.toLowerCase() === 'en-xa'
94
+ ? pseudoLocalize(formatted)
95
+ : formatted;
65
96
  }
66
97
  /** @deprecated Use directionFor; this historical set is not a support boundary. */
67
98
  export const RTL_LANGUAGES = new Set(['ar', 'ur']);
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/core/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Framework-light translation core (Plan 014, P1.5). Pure TS, dependency-free —\n * the portable heart shared by the React subpath and any non-React consumer, so\n * a hosted app can use it without pulling React.\n *\n * Preserves Tolgee's `t(key, defaultValue?, params?)` semantics so serve's\n * ~1,302 call sites (with inline English defaults) are a drop-in swap:\n * - default formatting is FormatSimple-style `{named}` interpolation (exactly\n * what serve uses today), so imported strings render identically;\n * - keys marked `format:'icu'` use standards-based ICU MessageFormat with\n * the active BCP-47 locale, including plural/select/selectordinal.\n */\nimport { localeDirection } from '../languages.js';\nimport IntlMessageFormat from 'intl-messageformat';\n\nexport type MessageFormat = 'simple' | 'icu';\n\n/** ICU keys retain their format beside the string in mixed-format catalogs. */\nexport interface FormattedTranslationMessage {\n message: string;\n format: MessageFormat;\n}\n\nexport type TranslationMessage = string | FormattedTranslationMessage;\n\n/** key → message template, for a single language. */\nexport type TranslationMessages = Record<string, TranslationMessage>;\n\n/** FormatSimple: replace `{name}` placeholders with `params.name`. Unknown → left as-is. */\nexport function interpolate(\n template: string,\n params?: Record<string, unknown>,\n): string {\n if (!params) return template;\n return template.replace(\n /\\{\\s*([\\p{L}\\p{N}_][\\p{L}\\p{N}\\p{M}_.-]*)\\s*\\}/gu,\n (whole, name) =>\n name in params ? String(params[name] ?? '') : whole,\n );\n}\n\nconst ICU_CACHE_LIMIT = 500;\nconst icuCache = new Map<string, IntlMessageFormat>();\n\nfunction compiledIcu(template: string, locale: string): IntlMessageFormat {\n const key = `${locale}\\u0000${template}`;\n const cached = icuCache.get(key);\n if (cached) return cached;\n const compiled = new IntlMessageFormat(template, locale, undefined, {\n ignoreTag: true,\n });\n if (icuCache.size >= ICU_CACHE_LIMIT) {\n icuCache.delete(icuCache.keys().next().value as string);\n }\n icuCache.set(key, compiled);\n return compiled;\n}\n\n/**\n * Full ICU MessageFormat using the active locale's CLDR plural rules. Invalid\n * authoring data never crashes the host app: the unformatted template remains\n * visible and Translation Studio reports the syntax error for repair.\n */\nexport function formatIcu(\n template: string,\n params?: Record<string, unknown>,\n locale = 'en',\n): string {\n try {\n const result = compiledIcu(template, locale).format(params as any);\n return Array.isArray(result) ? result.join('') : String(result);\n } catch {\n return interpolate(template, params);\n }\n}\n\n/**\n * Resolve `key` against a message map, falling back to the inline default (then\n * the key itself), and format with params. This is what the client `t()` calls.\n */\nexport function translate(\n messages: TranslationMessages,\n key: string,\n defaultValue?: string,\n params?: Record<string, unknown>,\n format: MessageFormat = 'simple',\n locale = 'en',\n): string {\n const stored = messages[key];\n const template =\n typeof stored === 'string'\n ? stored\n : stored?.message ?? defaultValue ?? key;\n const resolvedFormat =\n typeof stored === 'object' ? stored.format : format;\n return resolvedFormat === 'icu'\n ? formatIcu(template, params, locale)\n : interpolate(template, params);\n}\n\n/** @deprecated Use directionFor; this historical set is not a support boundary. */\nexport const RTL_LANGUAGES = new Set(['ar', 'ur']);\n\nexport function directionFor(language: string): 'ltr' | 'rtl' {\n try { return localeDirection(language); } catch { return 'ltr'; }\n}\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/core/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Framework-light translation core (Plan 014, P1.5). Pure TS, dependency-free —\n * the portable heart shared by the React subpath and any non-React consumer, so\n * a hosted app can use it without pulling React.\n *\n * Preserves Tolgee's `t(key, defaultValue?, params?)` semantics so serve's\n * ~1,302 call sites (with inline English defaults) are a drop-in swap:\n * - default formatting is FormatSimple-style `{named}` interpolation (exactly\n * what serve uses today), so imported strings render identically;\n * - keys marked `format:'icu'` use standards-based ICU MessageFormat with\n * the active BCP-47 locale, including plural/select/selectordinal.\n */\nimport { localeDirection } from '../languages.js';\nimport IntlMessageFormat from 'intl-messageformat';\n\nexport type MessageFormat = 'simple' | 'icu';\n\n/** ICU keys retain their format beside the string in mixed-format catalogs. */\nexport interface FormattedTranslationMessage {\n message: string;\n format: MessageFormat;\n}\n\nexport type TranslationMessage = string | FormattedTranslationMessage;\n\n/** key → message template, for a single language. */\nexport type TranslationMessages = Record<string, TranslationMessage>;\n\n/**\n * Development pseudo-locale used by chrome coverage checks. Keeping this in\n * the framework-light core means React and non-React clients exercise the\n * exact same fallback path. The wrapper makes missed layout allowance visible;\n * the accented glyphs make an untranslated English fallback unmistakable.\n */\nexport function pseudoLocalize(message: string): string {\n const accents: Record<string, string> = {\n A: 'Å', B: 'Ɓ', C: 'Ç', D: 'Ð', E: 'Ë', F: 'Ƒ', G: 'Ĝ', H: 'Ĥ', I: 'Ï',\n J: 'Ĵ', K: 'Ķ', L: 'Ļ', M: 'Ṁ', N: 'Ñ', O: 'Ö', P: 'Ƥ', Q: 'Ǫ', R: 'Ŕ',\n S: 'Š', T: 'Ŧ', U: 'Ü', V: 'Ṽ', W: 'Ŵ', X: 'Ẍ', Y: 'Ŷ', Z: 'Ž',\n a: 'å', b: 'ƀ', c: 'ç', d: 'đ', e: 'ë', f: 'ƒ', g: 'ĝ', h: 'ĥ', i: 'ï',\n j: 'ĵ', k: 'ķ', l: 'ļ', m: 'ṁ', n: 'ñ', o: 'ö', p: 'ƥ', q: 'ǫ', r: 'ŕ',\n s: 'š', t: 'ŧ', u: 'ü', v: 'ṽ', w: 'ŵ', x: 'ẍ', y: 'ŷ', z: 'ž',\n };\n const accented = message.replace(/[A-Za-z]/g, (character) => accents[character]);\n return `[${accented} ···]`;\n}\n\n/**\n * Layout-stress pseudo mode. Unlike en-XA's accented fallback, this deliberately\n * expands the resolved message—including a real translated catalog value—so a\n * preview can expose controls that only fit their English copy.\n */\nexport function pseudoExpand(message: string): string {\n const visibleLength = [...message].length;\n const padding = '·'.repeat(Math.max(3, Math.ceil(visibleLength * 0.35)));\n return `[${message} ${padding}]`;\n}\n\n/** FormatSimple: replace `{name}` placeholders with `params.name`. Unknown → left as-is. */\nexport function interpolate(\n template: string,\n params?: Record<string, unknown>,\n): string {\n if (!params) return template;\n return template.replace(\n /\\{\\s*([\\p{L}\\p{N}_][\\p{L}\\p{N}\\p{M}_.-]*)\\s*\\}/gu,\n (whole, name) =>\n name in params ? String(params[name] ?? '') : whole,\n );\n}\n\nconst ICU_CACHE_LIMIT = 500;\nconst icuCache = new Map<string, IntlMessageFormat>();\n\nfunction compiledIcu(template: string, locale: string): IntlMessageFormat {\n const key = `${locale}\\u0000${template}`;\n const cached = icuCache.get(key);\n if (cached) return cached;\n const compiled = new IntlMessageFormat(template, locale, undefined, {\n ignoreTag: true,\n });\n if (icuCache.size >= ICU_CACHE_LIMIT) {\n icuCache.delete(icuCache.keys().next().value as string);\n }\n icuCache.set(key, compiled);\n return compiled;\n}\n\n/**\n * Full ICU MessageFormat using the active locale's CLDR plural rules. Invalid\n * authoring data never crashes the host app: the unformatted template remains\n * visible and authoring tools can report the syntax error for repair.\n */\nexport function formatIcu(\n template: string,\n params?: Record<string, unknown>,\n locale = 'en',\n): string {\n try {\n const result = compiledIcu(template, locale).format(params as any);\n return Array.isArray(result) ? result.join('') : String(result);\n } catch {\n return interpolate(template, params);\n }\n}\n\n/**\n * Resolve `key` against a message map, falling back to the inline default (then\n * the key itself), and format with params. This is what the client `t()` calls.\n */\nexport function translate(\n messages: TranslationMessages,\n key: string,\n defaultValue?: string,\n params?: Record<string, unknown>,\n format: MessageFormat = 'simple',\n locale = 'en',\n): string {\n const stored = messages[key];\n const template =\n typeof stored === 'string'\n ? stored\n : stored?.message ?? defaultValue ?? key;\n const resolvedFormat =\n typeof stored === 'object' ? stored.format : format;\n const formatted = resolvedFormat === 'icu'\n ? formatIcu(template, params, locale)\n : interpolate(template, params);\n return stored === undefined && locale.toLowerCase() === 'en-xa'\n ? pseudoLocalize(formatted)\n : formatted;\n}\n\n/** @deprecated Use directionFor; this historical set is not a support boundary. */\nexport const RTL_LANGUAGES = new Set(['ar', 'ur']);\n\nexport function directionFor(language: string): 'ltr' | 'rtl' {\n try { return localeDirection(language); } catch { return 'ltr'; }\n}\n"]}
@@ -90,7 +90,7 @@ export interface TranslationImportDryRun {
90
90
  * file. Import disposition handles those entries as unverified later.
91
91
  */
92
92
  export declare function normalizeParsedTranslationExchangeDocument(value: unknown): ParsedTranslationExchangeDocument;
93
- /** Validate the stronger, version-evidenced document required for CN exports. */
93
+ /** Validate the stronger, version-evidenced document this package's own exports carry. */
94
94
  export declare function validateTranslationExchangeDocument(value: unknown): TranslationExchangeDocument;
95
95
  export declare function isConfirmedBuildDeclaration(declaration: TranslationBuildDeclaration): boolean;
96
96
  export declare function registerTranslationFormatAdapter(adapter: TranslationFormatAdapter): void;
@@ -182,7 +182,7 @@ export function normalizeParsedTranslationExchangeDocument(value) {
182
182
  entries,
183
183
  };
184
184
  }
185
- /** Validate the stronger, version-evidenced document required for CN exports. */
185
+ /** Validate the stronger, version-evidenced document this package's own exports carry. */
186
186
  export function validateTranslationExchangeDocument(value) {
187
187
  const parsed = normalizeParsedTranslationExchangeDocument(value);
188
188
  const entries = parsed.entries.map((entry, index) => {