@_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
@@ -44,6 +44,7 @@ export interface TranslationKeyInput {
44
44
  ofField?: string;
45
45
  ofShape?: string;
46
46
  ofProperty?: string;
47
+ ofResource?: string;
47
48
  fromPackage?: string;
48
49
  /** Force the override flag; when omitted it is derived (see upsertTranslationKey). */
49
50
  overridden?: boolean;
@@ -51,6 +52,25 @@ export interface TranslationKeyInput {
51
52
  sourceLanguage?: string;
52
53
  }
53
54
 
55
+ /** Trusted caller context, not a client payload flag. Package discovery and
56
+ * explicit app authoring have different ownership semantics. */
57
+ export interface TranslationKeyWriteContext {
58
+ createdBy?: string;
59
+ /**
60
+ * `'declaration-sync'` is a refresh from code/package declarations: it never
61
+ * marks a key overridden, and it leaves an existing app override untouched
62
+ * (answering `preservedOverride: true`). Anything else is an app edit.
63
+ */
64
+ source?: 'app-edit' | 'declaration-sync';
65
+ }
66
+ export interface TranslationKeyWriteResult {
67
+ id: string;
68
+ versionId: string;
69
+ versionCreated: boolean;
70
+ /** Present when a declaration refresh kept the app's override and wrote nothing. */
71
+ preservedOverride?: true;
72
+ }
73
+
54
74
  export interface TranslationUnitInput {
55
75
  appId: string;
56
76
  key: string;
@@ -313,6 +333,7 @@ export function groupTranslationEntries(
313
333
  format: row.format === 'icu' ? 'icu' : 'simple',
314
334
  ofShape: iri(row.ofShape),
315
335
  ofProperty: iri(row.ofProperty),
336
+ ofResource: iri(row.ofResource),
316
337
  fromPackage: row.fromPackage || undefined,
317
338
  overridden:
318
339
  (row as any).overridden === true || (row as any).overridden === 'true'
@@ -397,6 +418,7 @@ export async function listTranslationKeys(data: { appId: string }) {
397
418
  k.ofField,
398
419
  k.ofShape,
399
420
  k.ofProperty,
421
+ k.ofResource,
400
422
  k.fromPackage,
401
423
  k.overridden,
402
424
  k.format,
@@ -423,6 +445,7 @@ export async function listTranslationEntries(data: {
423
445
  k.format,
424
446
  k.ofShape,
425
447
  k.ofProperty,
448
+ k.ofResource,
426
449
  k.fromPackage,
427
450
  k.overridden,
428
451
  k.currentVersion,
@@ -1061,8 +1084,8 @@ export async function decideTranslationProposal(
1061
1084
  */
1062
1085
  export async function upsertTranslationKey(
1063
1086
  data: TranslationKeyInput,
1064
- authorship: { createdBy?: string } = {},
1065
- ): Promise<{ id: string; versionId: string; versionCreated: boolean }> {
1087
+ authorship: TranslationKeyWriteContext = {},
1088
+ ): Promise<TranslationKeyWriteResult> {
1066
1089
  const appId = requireText(data.appId, 'appId');
1067
1090
  const key = requireText(data.key, 'key');
1068
1091
  requireText(data.sourceText, 'sourceText');
@@ -1078,29 +1101,59 @@ export async function upsertTranslationKey(
1078
1101
  ofField: data.ofField,
1079
1102
  ofShape: data.ofShape ? { id: data.ofShape } : undefined,
1080
1103
  ofProperty: data.ofProperty ? { id: data.ofProperty } : undefined,
1104
+ ofResource: data.ofResource ? { id: data.ofResource } : undefined,
1081
1105
  fromPackage: data.fromPackage,
1082
- overridden: data.overridden === true ? true : undefined,
1106
+ overridden: data.overridden,
1083
1107
  format: data.format ?? 'simple',
1084
1108
  } as any;
1109
+ // No catch: a failed read is not permission to create or overwrite the key.
1085
1110
  const existing = await TranslationKey.select((k) => [
1086
1111
  k.sourceText,
1087
1112
  k.format,
1088
1113
  k.ofShape,
1114
+ k.ofProperty,
1115
+ k.ofResource,
1116
+ k.fromPackage,
1089
1117
  k.overridden,
1090
1118
  k.currentVersion,
1091
1119
  ])
1092
1120
  .where((k) => k.equals({ id } as any))
1093
- .one()
1094
- .catch(() => null);
1121
+ .one();
1095
1122
  if (existing) {
1096
1123
  const sourceChanged = (existing as any).sourceText !== data.sourceText;
1124
+ const formatChanged =
1125
+ ((existing as any).format ?? 'simple') !== (data.format ?? 'simple');
1097
1126
  const wasOverridden = (existing as any).overridden === true;
1098
- // AD-N override rule: editing the source of a SHAPE-owned key through the
1099
- // per-app path (this write) marks the app copy `overridden`, so shape-route
1100
- // propagation won't clobber the app's local wording. An explicit
1101
- // `data.overridden` (e.g. the installer clearing it) always wins.
1102
- const isShapeKey = !!(existing as any).ofShape || !!data.ofShape;
1103
- if (data.overridden === undefined && isShapeKey && sourceChanged) {
1127
+ if (authorship.source === 'declaration-sync' && wasOverridden) {
1128
+ const versionId = iri((existing as any).currentVersion);
1129
+ if (!versionId)
1130
+ throw new Error(
1131
+ `Translation override ${id} has no current version; refresh refused without changing its source.`
1132
+ );
1133
+ // No key write, version creation or unit invalidation. The declaration
1134
+ // inventory still records the package's current source independently.
1135
+ return { id, versionId, versionCreated: false, preservedOverride: true };
1136
+ }
1137
+ // AD-N ownership also covers property/resource/package declarations (help,
1138
+ // actions, groups), not only shapes. App edits preserve the first canonical
1139
+ // wording in the existing shapeSource carrier. Only an explicit app edit
1140
+ // may clear the override; automatic discovery cannot clear it by payload.
1141
+ const isDeclaredKey = !!(
1142
+ (existing as any).ofShape ||
1143
+ (existing as any).ofProperty ||
1144
+ (existing as any).ofResource ||
1145
+ (existing as any).fromPackage ||
1146
+ data.ofShape ||
1147
+ data.ofProperty ||
1148
+ data.ofResource ||
1149
+ data.fromPackage
1150
+ );
1151
+ if (
1152
+ authorship.source !== 'declaration-sync' &&
1153
+ data.overridden === undefined &&
1154
+ isDeclaredKey &&
1155
+ (sourceChanged || formatChanged)
1156
+ ) {
1104
1157
  values.overridden = true;
1105
1158
  // Snapshot the canonical at FIRST divergence so "clear override" has a
1106
1159
  // value to revert to even for a single-app project. Later edits keep the
@@ -1134,7 +1187,14 @@ export async function upsertTranslationKey(
1134
1187
  })
1135
1188
  : currentVersion;
1136
1189
  values.currentVersion = { id: nextVersion.id };
1137
- await TranslationKey.update(values).for({ id } as any);
1190
+ // In the Linked mutation DSL an explicitly present `undefined` removes
1191
+ // that predicate. A partial source edit has not requested removal of the
1192
+ // declaration owner, help description, or existing override flag. Omit
1193
+ // untouched fields; an explicit false (reset) remains a real write.
1194
+ const patch = Object.fromEntries(
1195
+ Object.entries(values).filter(([, value]) => value !== undefined)
1196
+ );
1197
+ await TranslationKey.update(patch as any).for({ id } as any);
1138
1198
  if (decision.action === 'create-version') {
1139
1199
  await TranslationUnit.update({ state: 'stale' } as any).where((u) =>
1140
1200
  u.ofKey.equals({ id } as any),
@@ -1491,7 +1551,7 @@ export class TranslationProvider extends ShapeProvider {
1491
1551
 
1492
1552
  /**
1493
1553
  * Authoring gate. Reads `linkedAuth` off the request the LINKED server already
1494
- * populates, so this stays portable (no Create Now import, AD-G).
1554
+ * populates, so this stays portable (no host-application import, AD-G).
1495
1555
  *
1496
1556
  * Every method except `getMessages` resolves the caller once, synchronously,
1497
1557
  * before its first `await`, and uses that value for both the permission check