@_linked/translation 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +15 -16
  2. package/lib/esm/archive.d.ts +2 -2
  3. package/lib/esm/archive.js +2 -2
  4. package/lib/esm/archive.js.map +1 -1
  5. package/lib/esm/authorization.d.ts +1 -1
  6. package/lib/esm/authorization.js +1 -1
  7. package/lib/esm/authorization.js.map +1 -1
  8. package/lib/esm/cdn-loader.d.ts +1 -1
  9. package/lib/esm/cdn-loader.js.map +1 -1
  10. package/lib/esm/core/messages.d.ts +14 -1
  11. package/lib/esm/core/messages.js +33 -2
  12. package/lib/esm/core/messages.js.map +1 -1
  13. package/lib/esm/exchange.d.ts +1 -1
  14. package/lib/esm/exchange.js +1 -1
  15. package/lib/esm/exchange.js.map +1 -1
  16. package/lib/esm/export.js +4 -1
  17. package/lib/esm/export.js.map +1 -1
  18. package/lib/esm/formats/xliff.js +1 -1
  19. package/lib/esm/formats/xliff.js.map +1 -1
  20. package/lib/esm/key-sync.d.ts +12 -0
  21. package/lib/esm/key-sync.js +32 -7
  22. package/lib/esm/key-sync.js.map +1 -1
  23. package/lib/esm/mt.js +1 -1
  24. package/lib/esm/mt.js.map +1 -1
  25. package/lib/esm/ontologies/translation.d.ts +3 -1
  26. package/lib/esm/ontologies/translation.js +4 -2
  27. package/lib/esm/ontologies/translation.js.map +1 -1
  28. package/lib/esm/react.d.ts +38 -2
  29. package/lib/esm/react.js +98 -15
  30. package/lib/esm/react.js.map +1 -1
  31. package/lib/esm/records.d.ts +2 -1
  32. package/lib/esm/records.js.map +1 -1
  33. package/lib/esm/release.d.ts +3 -1
  34. package/lib/esm/release.js +21 -4
  35. package/lib/esm/release.js.map +1 -1
  36. package/lib/esm/shape-catalog.d.ts +1 -1
  37. package/lib/esm/shape-catalog.js.map +1 -1
  38. package/lib/esm/shapes/TranslationKey.d.ts +10 -5
  39. package/lib/esm/shapes/TranslationKey.js +16 -5
  40. package/lib/esm/shapes/TranslationKey.js.map +1 -1
  41. package/lib/esm/shapes/TranslationProvider.d.ts +25 -8
  42. package/lib/esm/shapes/TranslationProvider.js +50 -19
  43. package/lib/esm/shapes/TranslationProvider.js.map +1 -1
  44. package/package.json +3 -3
  45. package/src/archive.ts +2 -2
  46. package/src/authorization.ts +1 -1
  47. package/src/cdn-loader.ts +1 -1
  48. package/src/core/messages.ts +35 -2
  49. package/src/exchange.ts +1 -1
  50. package/src/export.ts +4 -1
  51. package/src/formats/xliff.ts +1 -1
  52. package/src/key-sync.ts +46 -6
  53. package/src/mt.ts +1 -1
  54. package/src/ontologies/translation.ts +4 -2
  55. package/src/react.tsx +158 -35
  56. package/src/records.ts +2 -1
  57. package/src/release.ts +31 -3
  58. package/src/shape-catalog.ts +1 -1
  59. package/src/shapes/TranslationKey.ts +14 -5
  60. package/src/shapes/TranslationProvider.ts +73 -13
@@ -208,6 +208,7 @@ export function groupTranslationEntries(keyRows = [], unitRows = [], versionRows
208
208
  format: row.format === 'icu' ? 'icu' : 'simple',
209
209
  ofShape: iri(row.ofShape),
210
210
  ofProperty: iri(row.ofProperty),
211
+ ofResource: iri(row.ofResource),
211
212
  fromPackage: row.fromPackage || undefined,
212
213
  overridden: row.overridden === true || row.overridden === 'true'
213
214
  ? true
@@ -287,6 +288,7 @@ export async function listTranslationKeys(data) {
287
288
  k.ofField,
288
289
  k.ofShape,
289
290
  k.ofProperty,
291
+ k.ofResource,
290
292
  k.fromPackage,
291
293
  k.overridden,
292
294
  k.format,
@@ -310,6 +312,7 @@ export async function listTranslationEntries(data) {
310
312
  k.format,
311
313
  k.ofShape,
312
314
  k.ofProperty,
315
+ k.ofResource,
313
316
  k.fromPackage,
314
317
  k.overridden,
315
318
  k.currentVersion,
@@ -846,7 +849,7 @@ export async function decideTranslationProposal(data, reviewer) {
846
849
  * Source drift marks every existing language unit stale before returning.
847
850
  */
848
851
  export async function upsertTranslationKey(data, authorship = {}) {
849
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
852
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
850
853
  const appId = requireText(data.appId, 'appId');
851
854
  const key = requireText(data.key, 'key');
852
855
  requireText(data.sourceText, 'sourceText');
@@ -862,29 +865,52 @@ export async function upsertTranslationKey(data, authorship = {}) {
862
865
  ofField: data.ofField,
863
866
  ofShape: data.ofShape ? { id: data.ofShape } : undefined,
864
867
  ofProperty: data.ofProperty ? { id: data.ofProperty } : undefined,
868
+ ofResource: data.ofResource ? { id: data.ofResource } : undefined,
865
869
  fromPackage: data.fromPackage,
866
- overridden: data.overridden === true ? true : undefined,
870
+ overridden: data.overridden,
867
871
  format: (_c = data.format) !== null && _c !== void 0 ? _c : 'simple',
868
872
  };
873
+ // No catch: a failed read is not permission to create or overwrite the key.
869
874
  const existing = await TranslationKey.select((k) => [
870
875
  k.sourceText,
871
876
  k.format,
872
877
  k.ofShape,
878
+ k.ofProperty,
879
+ k.ofResource,
880
+ k.fromPackage,
873
881
  k.overridden,
874
882
  k.currentVersion,
875
883
  ])
876
884
  .where((k) => k.equals({ id }))
877
- .one()
878
- .catch(() => null);
885
+ .one();
879
886
  if (existing) {
880
887
  const sourceChanged = existing.sourceText !== data.sourceText;
888
+ const formatChanged = ((_d = existing.format) !== null && _d !== void 0 ? _d : 'simple') !== ((_e = data.format) !== null && _e !== void 0 ? _e : 'simple');
881
889
  const wasOverridden = existing.overridden === true;
882
- // AD-N override rule: editing the source of a SHAPE-owned key through the
883
- // per-app path (this write) marks the app copy `overridden`, so shape-route
884
- // propagation won't clobber the app's local wording. An explicit
885
- // `data.overridden` (e.g. the installer clearing it) always wins.
886
- const isShapeKey = !!existing.ofShape || !!data.ofShape;
887
- if (data.overridden === undefined && isShapeKey && sourceChanged) {
890
+ if (authorship.source === 'declaration-sync' && wasOverridden) {
891
+ const versionId = iri(existing.currentVersion);
892
+ if (!versionId)
893
+ throw new Error(`Translation override ${id} has no current version; refresh refused without changing its source.`);
894
+ // No key write, version creation or unit invalidation. The declaration
895
+ // inventory still records the package's current source independently.
896
+ return { id, versionId, versionCreated: false, preservedOverride: true };
897
+ }
898
+ // AD-N ownership also covers property/resource/package declarations (help,
899
+ // actions, groups), not only shapes. App edits preserve the first canonical
900
+ // wording in the existing shapeSource carrier. Only an explicit app edit
901
+ // may clear the override; automatic discovery cannot clear it by payload.
902
+ const isDeclaredKey = !!(existing.ofShape ||
903
+ existing.ofProperty ||
904
+ existing.ofResource ||
905
+ existing.fromPackage ||
906
+ data.ofShape ||
907
+ data.ofProperty ||
908
+ data.ofResource ||
909
+ data.fromPackage);
910
+ if (authorship.source !== 'declaration-sync' &&
911
+ data.overridden === undefined &&
912
+ isDeclaredKey &&
913
+ (sourceChanged || formatChanged)) {
888
914
  values.overridden = true;
889
915
  // Snapshot the canonical at FIRST divergence so "clear override" has a
890
916
  // value to revert to even for a single-app project. Later edits keep the
@@ -896,28 +922,33 @@ export async function upsertTranslationKey(data, authorship = {}) {
896
922
  appId,
897
923
  keyId: id,
898
924
  currentVersion: existing.currentVersion,
899
- sourceLanguage: (_d = data.sourceLanguage) !== null && _d !== void 0 ? _d : 'en',
900
- sourceText: (_e = existing.sourceText) !== null && _e !== void 0 ? _e : '',
925
+ sourceLanguage: (_f = data.sourceLanguage) !== null && _f !== void 0 ? _f : 'en',
926
+ sourceText: (_g = existing.sourceText) !== null && _g !== void 0 ? _g : '',
901
927
  format: existing.format === 'icu' ? 'icu' : 'simple',
902
928
  createdBy: authorship.createdBy,
903
929
  });
904
930
  const decision = classifyKeyVersion(currentVersion, {
905
931
  sourceText: data.sourceText,
906
- format: (_f = data.format) !== null && _f !== void 0 ? _f : 'simple',
932
+ format: (_h = data.format) !== null && _h !== void 0 ? _h : 'simple',
907
933
  });
908
934
  const nextVersion = decision.action === 'create-version'
909
935
  ? await createTranslationKeyVersion({
910
936
  appId,
911
937
  keyId: id,
912
- sourceLanguage: (_h = (_g = data.sourceLanguage) !== null && _g !== void 0 ? _g : currentVersion.sourceLanguage) !== null && _h !== void 0 ? _h : 'en',
938
+ sourceLanguage: (_k = (_j = data.sourceLanguage) !== null && _j !== void 0 ? _j : currentVersion.sourceLanguage) !== null && _k !== void 0 ? _k : 'en',
913
939
  sourceText: data.sourceText,
914
- format: (_j = data.format) !== null && _j !== void 0 ? _j : 'simple',
940
+ format: (_l = data.format) !== null && _l !== void 0 ? _l : 'simple',
915
941
  supersedes: currentVersion.id,
916
942
  createdBy: authorship.createdBy,
917
943
  })
918
944
  : currentVersion;
919
945
  values.currentVersion = { id: nextVersion.id };
920
- await TranslationKey.update(values).for({ id });
946
+ // In the Linked mutation DSL an explicitly present `undefined` removes
947
+ // that predicate. A partial source edit has not requested removal of the
948
+ // declaration owner, help description, or existing override flag. Omit
949
+ // untouched fields; an explicit false (reset) remains a real write.
950
+ const patch = Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
951
+ await TranslationKey.update(patch).for({ id });
921
952
  if (decision.action === 'create-version') {
922
953
  await TranslationUnit.update({ state: 'stale' }).where((u) => u.ofKey.equals({ id }));
923
954
  }
@@ -931,9 +962,9 @@ export async function upsertTranslationKey(data, authorship = {}) {
931
962
  const firstVersion = await createTranslationKeyVersion({
932
963
  appId,
933
964
  keyId: id,
934
- sourceLanguage: (_k = data.sourceLanguage) !== null && _k !== void 0 ? _k : 'en',
965
+ sourceLanguage: (_m = data.sourceLanguage) !== null && _m !== void 0 ? _m : 'en',
935
966
  sourceText: data.sourceText,
936
- format: (_l = data.format) !== null && _l !== void 0 ? _l : 'simple',
967
+ format: (_o = data.format) !== null && _o !== void 0 ? _o : 'simple',
937
968
  createdBy: authorship.createdBy,
938
969
  });
939
970
  await TranslationKey.update({
@@ -1215,7 +1246,7 @@ export class TranslationProvider extends ShapeProvider {
1215
1246
  }
1216
1247
  /**
1217
1248
  * Authoring gate. Reads `linkedAuth` off the request the LINKED server already
1218
- * populates, so this stays portable (no Create Now import, AD-G).
1249
+ * populates, so this stays portable (no host-application import, AD-G).
1219
1250
  *
1220
1251
  * Every method except `getMessages` resolves the caller once, synchronously,
1221
1252
  * before its first `await`, and uses that value for both the permission check