@_linked/translation 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -16
- package/lib/esm/archive.d.ts +2 -2
- package/lib/esm/archive.js +2 -2
- package/lib/esm/archive.js.map +1 -1
- package/lib/esm/authorization.d.ts +1 -1
- package/lib/esm/authorization.js +1 -1
- package/lib/esm/authorization.js.map +1 -1
- package/lib/esm/cdn-loader.d.ts +1 -1
- package/lib/esm/cdn-loader.js.map +1 -1
- package/lib/esm/core/messages.d.ts +14 -1
- package/lib/esm/core/messages.js +33 -2
- package/lib/esm/core/messages.js.map +1 -1
- package/lib/esm/exchange.d.ts +1 -1
- package/lib/esm/exchange.js +1 -1
- package/lib/esm/exchange.js.map +1 -1
- package/lib/esm/export.js +4 -1
- package/lib/esm/export.js.map +1 -1
- package/lib/esm/formats/xliff.js +1 -1
- package/lib/esm/formats/xliff.js.map +1 -1
- package/lib/esm/key-sync.d.ts +12 -0
- package/lib/esm/key-sync.js +32 -7
- package/lib/esm/key-sync.js.map +1 -1
- package/lib/esm/mt.js +1 -1
- package/lib/esm/mt.js.map +1 -1
- package/lib/esm/ontologies/translation.d.ts +193 -191
- package/lib/esm/ontologies/translation.js +4 -2
- package/lib/esm/ontologies/translation.js.map +1 -1
- package/lib/esm/package.d.ts +3 -3
- package/lib/esm/react.d.ts +38 -2
- package/lib/esm/react.js +98 -15
- package/lib/esm/react.js.map +1 -1
- package/lib/esm/records.d.ts +2 -1
- package/lib/esm/records.js.map +1 -1
- package/lib/esm/release.d.ts +3 -1
- package/lib/esm/release.js +21 -4
- package/lib/esm/release.js.map +1 -1
- package/lib/esm/shape-catalog.d.ts +1 -1
- package/lib/esm/shape-catalog.js.map +1 -1
- package/lib/esm/shapes/GlossaryTerm.d.ts +1 -1
- package/lib/esm/shapes/TranslationInventoryOrigin.d.ts +1 -1
- package/lib/esm/shapes/TranslationKey.d.ts +11 -6
- package/lib/esm/shapes/TranslationKey.js +16 -5
- package/lib/esm/shapes/TranslationKey.js.map +1 -1
- package/lib/esm/shapes/TranslationKeyVersion.d.ts +1 -1
- package/lib/esm/shapes/TranslationLanguage.d.ts +1 -1
- package/lib/esm/shapes/TranslationProvider.d.ts +25 -8
- package/lib/esm/shapes/TranslationProvider.js +69 -38
- package/lib/esm/shapes/TranslationProvider.js.map +1 -1
- package/lib/esm/shapes/TranslationRelease.d.ts +1 -1
- package/lib/esm/shapes/TranslationRevision.d.ts +1 -1
- package/lib/esm/shapes/TranslationUnit.d.ts +1 -1
- package/package.json +7 -7
- package/src/archive.ts +2 -2
- package/src/authorization.ts +1 -1
- package/src/cdn-loader.ts +1 -1
- package/src/core/messages.ts +35 -2
- package/src/exchange.ts +1 -1
- package/src/export.ts +4 -1
- package/src/formats/xliff.ts +1 -1
- package/src/key-sync.ts +46 -6
- package/src/mt.ts +1 -1
- package/src/ontologies/translation.ts +4 -2
- package/src/react.tsx +158 -35
- package/src/records.ts +2 -1
- package/src/release.ts +31 -3
- package/src/shape-catalog.ts +1 -1
- package/src/shapes/TranslationKey.ts +14 -5
- package/src/shapes/TranslationProvider.ts +92 -32
package/README.md
CHANGED
|
@@ -43,8 +43,8 @@ package works without either peer.
|
|
|
43
43
|
|
|
44
44
|
## Code-canonical discovery
|
|
45
45
|
|
|
46
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
109
|
-
control plane. `defineLanguage` supplies editable defaults
|
|
110
|
-
Unicode locale data. Validate relationship cycles before
|
|
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
|
package/lib/esm/archive.d.ts
CHANGED
|
@@ -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
|
|
8
|
-
*
|
|
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
|
*/
|
package/lib/esm/archive.js
CHANGED
|
@@ -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
|
|
18
|
-
*
|
|
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
|
*/
|
package/lib/esm/archive.js.map
CHANGED
|
@@ -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
|
-
* (
|
|
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
|
package/lib/esm/authorization.js
CHANGED
|
@@ -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
|
-
* (
|
|
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 * (
|
|
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"]}
|
package/lib/esm/cdn-loader.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
/**
|
package/lib/esm/core/messages.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,iBAAiB,MAAM,oBAAoB,CAAC;AAenD,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,MAAgC;IAEhC,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,OAAO,QAAQ,CAAC,OAAO,CACrB,kDAAkD,EAClD,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,WACd,OAAA,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAA,MAAM,CAAC,IAAI,CAAC,mCAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA,EAAA,CACtD,CAAC;AACJ,CAAC;AAED,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEtD,SAAS,WAAW,CAAC,QAAgB,EAAE,MAAc;IACnD,MAAM,GAAG,GAAG,GAAG,MAAM,SAAS,QAAQ,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE;QAClE,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,IAAI,QAAQ,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC;QACrC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC,CAAC;IAC1D,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC5B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,QAAgB,EAChB,MAAgC,EAChC,MAAM,GAAG,IAAI;IAEb,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,MAAa,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClE,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,QAA6B,EAC7B,GAAW,EACX,YAAqB,EACrB,MAAgC,EAChC,SAAwB,QAAQ,EAChC,MAAM,GAAG,IAAI;;IAEb,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,QAAQ,GACZ,OAAO,MAAM,KAAK,QAAQ;QACxB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAA,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,mCAAI,YAAY,mCAAI,GAAG,CAAC;IAC7C,MAAM,cAAc,GAClB,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACtD,
|
|
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"]}
|
package/lib/esm/exchange.d.ts
CHANGED
|
@@ -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
|
|
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;
|
package/lib/esm/exchange.js
CHANGED
|
@@ -182,7 +182,7 @@ export function normalizeParsedTranslationExchangeDocument(value) {
|
|
|
182
182
|
entries,
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
|
-
/** Validate the stronger, version-evidenced document
|
|
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) => {
|