nucleus-core-ts 0.9.910 → 0.9.912

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 (26) hide show
  1. package/dist/.build-ok +1 -1
  2. package/dist/fe/components/IntegrationsPage/components/ConnectionExport.js +4 -3
  3. package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +2 -2
  4. package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +5 -5
  5. package/dist/fe/components/IntegrationsPage/components/MappingAccountFields.js +10 -9
  6. package/dist/fe/components/IntegrationsPage/components/MappingList.js +1 -1
  7. package/dist/fe/components/IntegrationsPage/components/MappingScheduleFields.js +6 -5
  8. package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +2 -2
  9. package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +1 -1
  10. package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +2 -2
  11. package/dist/fe/components/IntegrationsPage/components/RunHistory.js +1 -1
  12. package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +7 -6
  13. package/dist/fe/components/IntegrationsPage/components/RunPanel.js +1 -1
  14. package/dist/fe/components/IntegrationsPage/components/RunPlanReferences.js +11 -13
  15. package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +6 -6
  16. package/dist/fe/components/IntegrationsPage/text/index.d.ts +79 -0
  17. package/dist/fe/components/IntegrationsPage/text/index.js +63 -0
  18. package/dist/index.js +8 -8
  19. package/dist/src/ElysiaPlugin/routes/auth/admin/passwordAdmin.d.ts +43 -0
  20. package/dist/src/ElysiaPlugin/routes/auth/admin/rotationPlan.d.ts +54 -0
  21. package/dist/src/ElysiaPlugin/routes/auth/index.d.ts +4 -0
  22. package/dist/src/ElysiaPlugin/routes/auth/login/identifier.d.ts +61 -0
  23. package/dist/src/ElysiaPlugin/routes/auth/login/identifierQuery.d.ts +11 -0
  24. package/dist/src/ElysiaPlugin/routes/auth/types.d.ts +10 -0
  25. package/dist/src/types.d.ts +32 -0
  26. package/package.json +1 -1
package/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.9.910
1
+ 0.9.912
@@ -30,7 +30,8 @@ import { Notice } from './Primitives';
30
30
  * is a confusing half hour otherwise.
31
31
  */ /** A filename somebody can still recognise a week later. */ const fileNameFor = (slug)=>`integration-${slug || 'connection'}.json`;
32
32
  export function ConnectionExport({ actions, source }) {
33
- const t = useLabels().transfer;
33
+ const labels = useLabels();
34
+ const t = labels.transfer;
34
35
  const store = useIntegrationsStore();
35
36
  const [busy, setBusy] = useState(false);
36
37
  const [note, setNote] = useState(null);
@@ -88,12 +89,12 @@ export function ConnectionExport({ actions, source }) {
88
89
  children: [
89
90
  /*#__PURE__*/ _jsx("p", {
90
91
  className: theme.field.hint,
91
- children: "One file holding this connection, its calls and its mappings — enough to rebuild the whole integration in another installation. Bring it in there with Import, on the connections screen."
92
+ children: labels.explain.exportBundleRebuildsIntegration
92
93
  }),
93
94
  /*#__PURE__*/ _jsx(Notice, {
94
95
  title: t.noSecretsTitle,
95
96
  tone: "info",
96
- children: "Tokens, passwords and keys are left out on purpose. Whatever this connection authenticates with has to be entered again wherever the file lands."
97
+ children: labels.explain.exportOmitsCredentials
97
98
  }),
98
99
  note ? /*#__PURE__*/ _jsx("p", {
99
100
  className: theme.field.hint,
@@ -153,12 +153,12 @@ export function CredentialFields({ actions, source, loading = false }) {
153
153
  title: labels.auth.writeOnlyTitle,
154
154
  tone: "info",
155
155
  children: /*#__PURE__*/ _jsx("p", {
156
- children: "A value typed here is stored and from then on only ever sent to the service. It is not returned to this screen, or to any other, no matter who asks — all that comes back is a masked hint. Leave a box empty to keep whatever is already stored."
156
+ children: labels.explain.credentialsWriteOnlyBody
157
157
  })
158
158
  }),
159
159
  slots.length === 0 ? /*#__PURE__*/ _jsx("p", {
160
160
  className: theme.list.empty,
161
- children: "This connection is set to no authentication, so there is no secret to store. Pick an auth type above to see the fields it needs."
161
+ children: labels.explain.credentialsNoAuthNothingToStore
162
162
  }) : null,
163
163
  slots.map((slot)=>/*#__PURE__*/ _jsx(CredentialSlotRow, {
164
164
  disabled: busy,
@@ -132,7 +132,7 @@ export function EndpointSample({ endpoint, actions }) {
132
132
  }),
133
133
  /*#__PURE__*/ _jsx("p", {
134
134
  className: theme.field.hint,
135
- children: "Used for this one request only. During a run each parameter takes its value from the binding configured for it."
135
+ children: labels.endpoints.samplePathValuesHint
136
136
  }),
137
137
  /*#__PURE__*/ _jsx("div", {
138
138
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
@@ -192,14 +192,14 @@ export function EndpointSample({ endpoint, actions }) {
192
192
  }) : null,
193
193
  sample.recordCount > 0 ? /*#__PURE__*/ _jsx("p", {
194
194
  className: theme.field.hint,
195
- children: `This is one request. The source answered it with ${sample.recordCount} record(s)${sample.recordCount > sample.sample.length ? `, of which the first ${sample.sample.length} are printed below` : ''} — not the whole table. If the endpoint pages, this is one page of it. Preview on the Run step walks every page and reports the real total.`
195
+ children: `${labels.explain.sampleOneRequestLead}${sample.recordCount}${labels.explain.sampleOneRequestRecords}${sample.recordCount > sample.sample.length ? `${labels.explain.sampleFirstPrintedLead}${sample.sample.length}${labels.explain.sampleFirstPrintedTail}` : ''}${labels.explain.sampleOneRequestTail}`
196
196
  }) : null,
197
197
  sample.recordCount === 0 ? /*#__PURE__*/ _jsxs(Notice, {
198
198
  title: labels.endpoints.sampleNoRecords,
199
199
  tone: "warning",
200
200
  children: [
201
201
  /*#__PURE__*/ _jsx("p", {
202
- children: "These are the top-level keys of the body the source returned. Set the response root path to whichever holds the list."
202
+ children: labels.explain.sampleBodyKeysAreTopLevel
203
203
  }),
204
204
  sample.bodyKeys.length > 0 ? /*#__PURE__*/ _jsx("ul", {
205
205
  className: "mt-2 flex flex-wrap gap-1.5",
@@ -211,7 +211,7 @@ export function EndpointSample({ endpoint, actions }) {
211
211
  }, key))
212
212
  }) : /*#__PURE__*/ _jsx("p", {
213
213
  className: "mt-2",
214
- children: "The body has no top-level keys at all: it is empty, or it is a bare value rather than an object."
214
+ children: labels.explain.sampleBodyHasNoTopLevelKeys
215
215
  }),
216
216
  sample.bodyPreview ? /*#__PURE__*/ _jsx("div", {
217
217
  className: "mt-2",
@@ -255,7 +255,7 @@ export function EndpointSample({ endpoint, actions }) {
255
255
  }) : null,
256
256
  !store.sampling && !sample ? /*#__PURE__*/ _jsx("p", {
257
257
  className: theme.field.hint,
258
- children: "Nothing has been sampled yet. One request is sent, read-only, and the shape it comes back with is what the field mapping is built from."
258
+ children: labels.endpoints.sampleNothingYet
259
259
  }) : null
260
260
  ]
261
261
  })
@@ -23,7 +23,8 @@ import { Field, Notice, Section } from './Primitives';
23
23
  /** Columns that plausibly hold a password, so the guess is a good one. */ const PASSWORD_LIKE = /password|passwd|sifre|şifre|parola/i;
24
24
  /** Columns that plausibly point at the account row. */ const LINK_LIKE = /^(user_?id|account_?id|login_?id)$/i;
25
25
  export function MappingAccountFields({ value, targetFields, targets, disabled, onChange }) {
26
- const t = useLabels().account;
26
+ const labels = useLabels();
27
+ const t = labels.account;
27
28
  const uid = useId();
28
29
  const accountEntity = targets.find((target)=>target.entity === value?.entity);
29
30
  const accountFields = accountEntity?.fields ?? [];
@@ -96,7 +97,7 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
96
97
  })
97
98
  }),
98
99
  /*#__PURE__*/ _jsx(Field, {
99
- hint: "The column on the account that identifies it.",
100
+ hint: labels.explain.accountMatchColumnIdentifies,
100
101
  htmlFor: `${uid}-match-column`,
101
102
  label: t.identifiedBy,
102
103
  children: /*#__PURE__*/ _jsxs("select", {
@@ -120,7 +121,7 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
120
121
  })
121
122
  }),
122
123
  /*#__PURE__*/ _jsx(Field, {
123
- hint: "Where the run's shared password is written.",
124
+ hint: labels.explain.accountPasswordColumnReceives,
124
125
  htmlFor: `${uid}-password`,
125
126
  label: t.passwordInto,
126
127
  children: /*#__PURE__*/ _jsxs("select", {
@@ -151,7 +152,7 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
151
152
  })
152
153
  }),
153
154
  /*#__PURE__*/ _jsx(Field, {
154
- hint: "The imported record's column carrying that same value.",
155
+ hint: labels.explain.accountMatchFromCarriesValue,
155
156
  htmlFor: `${uid}-match-from`,
156
157
  label: t.matchedFrom,
157
158
  children: /*#__PURE__*/ _jsxs("select", {
@@ -175,7 +176,7 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
175
176
  })
176
177
  }),
177
178
  /*#__PURE__*/ _jsx(Field, {
178
- hint: "Used only when the column above came back EMPTY, so nobody is left without a login. Name the record's own columns in braces — {firstName}.{sicilNo}@acme.com. Leave it blank to go on skipping those records.",
179
+ hint: labels.explain.accountFallbackWhenIdentifierEmpty,
179
180
  htmlFor: `${uid}-email-format`,
180
181
  label: t.identifierFallback,
181
182
  children: /*#__PURE__*/ _jsx("input", {
@@ -190,7 +191,7 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
190
191
  })
191
192
  }),
192
193
  /*#__PURE__*/ _jsx(Field, {
193
- hint: "Filled with the new account's id, so the record and its login know each other.",
194
+ hint: labels.explain.accountIdColumnLinksBack,
194
195
  htmlFor: `${uid}-id-column`,
195
196
  label: t.accountIdInto,
196
197
  children: /*#__PURE__*/ _jsxs("select", {
@@ -245,7 +246,7 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
245
246
  }),
246
247
  (value.joins ?? []).length === 0 ? /*#__PURE__*/ _jsx("p", {
247
248
  className: theme.field.hint,
248
- children: "Nothing. An account with no role can sign in and do nothing — every screen it reaches refuses it. A role usually lives in its own table, joined to the account, which is what this adds."
249
+ children: labels.explain.accountNoMembershipsYet
249
250
  }) : null,
250
251
  (value.joins ?? []).map((join, joinIndex)=>{
251
252
  const joinEntity = targets.find((target)=>target.entity === join.entity);
@@ -348,12 +349,12 @@ export function MappingAccountFields({ value, targetFields, targets, disabled, o
348
349
  /*#__PURE__*/ _jsx(Notice, {
349
350
  title: t.sharedPasswordTitle,
350
351
  tone: "info",
351
- children: "Every account this run creates is given the same opening password — asked for once, when the import is started, and stored nowhere. People change it themselves afterwards. A record whose value already has an account is linked to that one instead, so running this import again does not create a second."
352
+ children: labels.explain.accountSharedPasswordPerRun
352
353
  })
353
354
  ]
354
355
  }) : /*#__PURE__*/ _jsx("p", {
355
356
  className: theme.list.empty,
356
- children: "Off. Records are imported without a login — useful for a directory, not for people who have to sign in."
357
+ children: labels.explain.accountCreationOff
357
358
  })
358
359
  });
359
360
  }
@@ -188,7 +188,7 @@ export function MappingList({ actions, sourceId, selectedId, reloadToken, onSele
188
188
  children: [
189
189
  /*#__PURE__*/ _jsx(Badge, {
190
190
  tone: WRITE_MODE_TONE[mapping.writeMode],
191
- children: mapping.writeMode
191
+ children: labels.writeModeFull[mapping.writeMode]
192
192
  }),
193
193
  mapping.enabled === false ? /*#__PURE__*/ _jsx(Badge, {
194
194
  tone: "neutral",
@@ -46,7 +46,8 @@ import { Field, Notice, Section } from './Primitives';
46
46
  }
47
47
  ];
48
48
  export function MappingScheduleFields({ enabled, cron, timezone, needsPassword, disabled, onChange }) {
49
- const t = useLabels().schedule;
49
+ const labels = useLabels();
50
+ const t = labels.schedule;
50
51
  const presets = presetsOf(t.presets);
51
52
  const uid = useId();
52
53
  const parsed = cron.trim() === '' ? null : parseCron(cron);
@@ -102,7 +103,7 @@ export function MappingScheduleFields({ enabled, cron, timezone, needsPassword,
102
103
  })
103
104
  }),
104
105
  /*#__PURE__*/ _jsx(Field, {
105
- hint: "The company's own clock, so 02:00 stays 02:00 across a daylight-saving change.",
106
+ hint: labels.explain.scheduleTimezoneIsCompanyClock,
106
107
  htmlFor: `${uid}-timezone`,
107
108
  label: t.timezone,
108
109
  children: /*#__PURE__*/ _jsx("input", {
@@ -133,16 +134,16 @@ export function MappingScheduleFields({ enabled, cron, timezone, needsPassword,
133
134
  needsPassword ? /*#__PURE__*/ _jsx(Notice, {
134
135
  title: t.cannotRunTitle,
135
136
  tone: "warning",
136
- children: "It creates a login for every new record, and a login needs a password — which there is nobody to type at that hour. Either start this import by hand, or turn the login off and give people their accounts another way."
137
+ children: labels.explain.scheduleCannotRunUnattended
137
138
  }) : null,
138
139
  /*#__PURE__*/ _jsx("p", {
139
140
  className: theme.field.hint,
140
- children: "Every server running this app checks the schedule, and exactly one of them takes the import — the same lock that stops two people running it at once."
141
+ children: labels.explain.scheduleOneServerTakesTheRun
141
142
  })
142
143
  ]
143
144
  }) : /*#__PURE__*/ _jsx("p", {
144
145
  className: theme.list.empty,
145
- children: "Off. This import runs when somebody asks for it, and at no other time."
146
+ children: labels.explain.scheduleOffUntilAskedFor
146
147
  })
147
148
  });
148
149
  }
@@ -70,7 +70,7 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
70
70
  tone: "danger",
71
71
  children: [
72
72
  /*#__PURE__*/ _jsx("p", {
73
- children: "Every existing row is dropped and written again, so row ids change. Foreign keys, saved links, exports, report filters and anything else holding one of those ids breaks, and no amount of re-running repairs it."
73
+ children: labels.explain.writeModeReplaceRebuildsTable
74
74
  }),
75
75
  /*#__PURE__*/ _jsx("p", {
76
76
  className: "mt-1",
@@ -98,7 +98,7 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
98
98
  missingRecordPolicy === 'delete' ? /*#__PURE__*/ _jsx(Notice, {
99
99
  title: labels.mappings.deleteWarnTitle,
100
100
  tone: "warning",
101
- children: "A source that answers with a short page — a filter left on, a timeout, an expired token honoured with an empty list — looks exactly like records that went away."
101
+ children: labels.explain.writeModeDeleteTrustsShortPage
102
102
  }) : null,
103
103
  missingRecordPolicy === 'deactivate' ? /*#__PURE__*/ _jsxs("div", {
104
104
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
@@ -178,7 +178,7 @@ export function PaginationFields({ idPrefix, value, disabled, onChange }) {
178
178
  /*#__PURE__*/ _jsx(Notice, {
179
179
  title: labels.endpoints.paginationTitle,
180
180
  tone: "warning",
181
- children: "Nothing here is detected for you. Pick the wrong style and the import still succeeds — it reads the first page and never asks for the rest. In a destructive write mode the records it never saw count as missing, so the run deletes or deactivates them."
181
+ children: labels.explain.paginationWrongStyleSilentlySucceeds
182
182
  }),
183
183
  /*#__PURE__*/ _jsx(Field, {
184
184
  hint: labels.endpoints.paginationHint,
@@ -24,7 +24,7 @@ export function RunConfirmStep({ lines, counted, working, onConfirm, onDismiss }
24
24
  }),
25
25
  counted ? null : /*#__PURE__*/ _jsx("p", {
26
26
  className: "mt-2",
27
- children: "These counts are unknown because nothing was previewed. Run a preview first if you want to know how many rows this removes before it removes them."
27
+ children: labels.explain.runConfirmCountsUnknown
28
28
  }),
29
29
  /*#__PURE__*/ _jsxs("div", {
30
30
  className: "mt-3 flex flex-wrap items-center gap-2",
@@ -41,7 +41,7 @@ export function RunConfirmStep({ lines, counted, working, onConfirm, onDismiss }
41
41
  disabled: working,
42
42
  onClick: onDismiss,
43
43
  type: "button",
44
- children: "Keep them, do not run"
44
+ children: labels.explain.runConfirmKeepThem
45
45
  })
46
46
  ]
47
47
  })
@@ -118,7 +118,7 @@ export function RunHistory({ actions, mappingId, refreshKey, onRefresh }) {
118
118
  }) : null,
119
119
  !firstPagePending && list.items.length === 0 && !list.error ? /*#__PURE__*/ _jsx("p", {
120
120
  className: theme.list.empty,
121
- children: "Nothing has run yet. A preview writes nothing; the first entry appears here once an import is started."
121
+ children: labels.explain.runHistoryNothingRunYet
122
122
  }) : null,
123
123
  list.items.length > 0 ? /*#__PURE__*/ _jsx("ul", {
124
124
  className: theme.list.wrapper,
@@ -16,7 +16,8 @@ import { countersLine, errorText, formatDuration, formatMoment, RUN_STATUS_TONE,
16
16
  * a button that quietly disappears reads as a bug rather than as an expiry.
17
17
  */ const theme = integrationsPageTheme;
18
18
  export function RunHistoryRow({ run, actions, onReverted }) {
19
- const t = useLabels().errors;
19
+ const labels = useLabels();
20
+ const t = labels.errors;
20
21
  const store = useIntegrationsStore();
21
22
  const [reverting, setReverting] = useState(false);
22
23
  const [conflicts, setConflicts] = useState(null);
@@ -72,23 +73,23 @@ export function RunHistoryRow({ run, actions, onReverted }) {
72
73
  children: [
73
74
  /*#__PURE__*/ _jsx(Badge, {
74
75
  tone: RUN_STATUS_TONE[run.status],
75
- children: run.status
76
+ children: labels.runStatusFull[run.status]
76
77
  }),
77
78
  /*#__PURE__*/ _jsx("span", {
78
79
  className: theme.card.title,
79
- children: run.targetEntity ?? 'unknown target'
80
+ children: run.targetEntity ?? labels.badges.unknownTarget
80
81
  }),
81
82
  run.writeMode ? /*#__PURE__*/ _jsx(Badge, {
82
83
  tone: "neutral",
83
- children: run.writeMode
84
+ children: labels.writeModeFull[run.writeMode] ?? run.writeMode
84
85
  }) : null,
85
86
  run.trigger ? /*#__PURE__*/ _jsx(Badge, {
86
87
  tone: "neutral",
87
- children: run.trigger
88
+ children: labels.triggerFull[run.trigger] ?? run.trigger
88
89
  }) : null,
89
90
  mismatch ? /*#__PURE__*/ _jsx(Badge, {
90
91
  tone: "warning",
91
- children: `read ${run.totalFetched} of ${reportedTotal}`
92
+ children: labels.badges.readOf(run.totalFetched ?? 0, reportedTotal ?? 0)
92
93
  }) : null
93
94
  ]
94
95
  }),
@@ -279,7 +279,7 @@ export function RunPanel({ mapping, actions, onSubscribeProgress, onRunSettled }
279
279
  !(store.planning || plan || activeRunId) ? /*#__PURE__*/ _jsx(Notice, {
280
280
  tone: "info",
281
281
  title: labels.runs.previewExplainTitle,
282
- children: "A preview reads the source without writing anything, and reports how many rows would be created, updated, skipped, and how many rows in the target the source no longer returns."
282
+ children: labels.explain.runPreviewWritesNothing
283
283
  }) : null
284
284
  ]
285
285
  })
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useLabels } from '../text/context';
3
4
  import { integrationsPageTheme } from '../theme';
4
5
  import { Notice } from './Primitives';
5
6
  /**
@@ -36,6 +37,7 @@ import { Notice } from './Primitives';
36
37
  ].sort((a, b)=>b.records - a.records);
37
38
  }
38
39
  export function RunPlanReferences({ plan, busy, onCreateAndRun, onSkipAndRun }) {
40
+ const labels = useLabels();
39
41
  const missing = missingReferenceValues(plan);
40
42
  const planned = plan.referenceInserts ?? [];
41
43
  if (missing.length === 0 && planned.length === 0) return null;
@@ -43,15 +45,11 @@ export function RunPlanReferences({ plan, busy, onCreateAndRun, onSkipAndRun })
43
45
  className: "flex flex-col gap-3",
44
46
  children: [
45
47
  missing.length > 0 ? /*#__PURE__*/ _jsxs(Notice, {
46
- title: `${missing.length} value(s) this import refers to are not here yet`,
48
+ title: labels.explain.missingReferencesTitle(missing.length),
47
49
  tone: "warning",
48
50
  children: [
49
- /*#__PURE__*/ _jsxs("p", {
50
- children: [
51
- "As it stands the records that carry them are left out — ",
52
- plan.toSkip,
53
- " record(s) in total. Either add the values first, or import without those records."
54
- ]
51
+ /*#__PURE__*/ _jsx("p", {
52
+ children: labels.explain.missingReferencesRecordsLeftOut(plan.toSkip)
55
53
  }),
56
54
  /*#__PURE__*/ _jsx("ul", {
57
55
  className: "mt-2 flex flex-col gap-1",
@@ -61,7 +59,7 @@ export function RunPlanReferences({ plan, busy, onCreateAndRun, onSkipAndRun })
61
59
  /*#__PURE__*/ _jsx("strong", {
62
60
  children: entry.value
63
61
  }),
64
- ` — missing from ${entry.entity}.${entry.entityColumn}, needed by ${entry.records} record(s)`
62
+ labels.explain.missingReferenceRow(`${entry.entity}.${entry.entityColumn}`, entry.records)
65
63
  ]
66
64
  }, entry.key))
67
65
  }),
@@ -73,25 +71,25 @@ export function RunPlanReferences({ plan, busy, onCreateAndRun, onSkipAndRun })
73
71
  disabled: busy,
74
72
  onClick: onCreateAndRun,
75
73
  type: "button",
76
- children: `Create these ${missing.length} value(s) and import everything`
74
+ children: labels.explain.missingReferencesCreateAndRun(missing.length)
77
75
  }),
78
76
  /*#__PURE__*/ _jsx("button", {
79
77
  className: theme.button.secondary,
80
78
  disabled: busy,
81
79
  onClick: onSkipAndRun,
82
80
  type: "button",
83
- children: "Import without those records"
81
+ children: labels.explain.missingReferencesRunWithoutThem
84
82
  })
85
83
  ]
86
84
  })
87
85
  ]
88
86
  }) : null,
89
87
  planned.length > 0 ? /*#__PURE__*/ _jsxs(Notice, {
90
- title: `${planned.length} value(s) will be added first`,
88
+ title: labels.explain.plannedReferencesTitle(planned.length),
91
89
  tone: "info",
92
90
  children: [
93
91
  /*#__PURE__*/ _jsx("p", {
94
- children: "These are created in the same import, before the records that point at them, so nothing is written pointing at nothing. They carry only the value itself — rename them afterwards."
92
+ children: labels.explain.plannedReferencesCreatedBefore
95
93
  }),
96
94
  /*#__PURE__*/ _jsx("ul", {
97
95
  className: "mt-2 flex flex-col gap-1",
@@ -112,7 +110,7 @@ export function RunPlanReferences({ plan, busy, onCreateAndRun, onSkipAndRun })
112
110
  disabled: busy,
113
111
  onClick: onSkipAndRun,
114
112
  type: "button",
115
- children: "Do not create them — leave those records out"
113
+ children: labels.explain.plannedReferencesLeaveRecordsOut
116
114
  })
117
115
  })
118
116
  ]
@@ -35,22 +35,22 @@ export function RunPlanSummary({ plan }) {
35
35
  }),
36
36
  /*#__PURE__*/ _jsx(Badge, {
37
37
  tone: "info",
38
- children: plan.writeMode
38
+ children: labels.writeModeFull[plan.writeMode]
39
39
  }),
40
40
  /*#__PURE__*/ _jsx(Badge, {
41
41
  tone: plan.dedupConfigured ? 'neutral' : 'warning',
42
- children: plan.dedupConfigured ? 'dedup key set' : 'no dedup key'
42
+ children: plan.dedupConfigured ? labels.badges.dedupSet : labels.badges.dedupMissing
43
43
  }),
44
44
  /*#__PURE__*/ _jsx(Badge, {
45
45
  tone: "neutral",
46
- children: `missing rows: ${plan.missingPolicy}`
46
+ children: labels.badges.missingRows(labels.missingPolicyFull[plan.missingPolicy])
47
47
  })
48
48
  ]
49
49
  }),
50
50
  countMismatch ? /*#__PURE__*/ _jsx(Notice, {
51
51
  tone: "danger",
52
52
  title: labels.runExtras.countMismatchTitle,
53
- children: `The source reports ${plan.reportedTotal} record(s); ${plan.totalFetched} were actually read. Check the endpoint's pagination profile before running: a wrong profile reads one page, and every record it never saw counts as missing.`
53
+ children: labels.explain.runPlanCountMismatchBody(plan.reportedTotal, plan.totalFetched)
54
54
  }) : null,
55
55
  /*#__PURE__*/ _jsxs("div", {
56
56
  className: theme.stat.grid,
@@ -83,9 +83,9 @@ export function RunPlanSummary({ plan }) {
83
83
  ]
84
84
  }),
85
85
  plan.accountsToCreate > 0 ? /*#__PURE__*/ _jsx(Notice, {
86
- title: `${plan.accountsToCreate} login(s) will be created, with the password typed above`,
86
+ title: labels.explain.runPlanAccountsToCreateTitle(plan.accountsToCreate),
87
87
  tone: "info",
88
- children: "Each new record gets an account in the same import — one password for all of them, which people change themselves afterwards. Anyone whose value already has an account is linked to it instead."
88
+ children: labels.explain.runPlanAccountsSharePassword
89
89
  }) : null,
90
90
  plan.warnings.map((warning, index)=>/*#__PURE__*/ _jsx(Notice, {
91
91
  title: warning,
@@ -319,6 +319,33 @@ export type IntegrationsLabels = {
319
319
  deactivate: string;
320
320
  delete: string;
321
321
  };
322
+ /**
323
+ * The words on the badges.
324
+ *
325
+ * A run said "succeeded" and a mapping said "upsert" — the value out of the
326
+ * column, printed as-is. They are the first thing read on both screens, and
327
+ * they were the last thing left in English.
328
+ */
329
+ runStatusFull: {
330
+ queued: string;
331
+ running: string;
332
+ succeeded: string;
333
+ failed: string;
334
+ cancelled: string;
335
+ reverted: string;
336
+ };
337
+ triggerFull: {
338
+ manual: string;
339
+ schedule: string;
340
+ api: string;
341
+ };
342
+ badges: {
343
+ unknownTarget: string;
344
+ dedupSet: string;
345
+ dedupMissing: string;
346
+ missingRows: (policy: string) => string;
347
+ readOf: (fetched: string | number, reported: string | number) => string;
348
+ };
322
349
  missingPolicyHint: {
323
350
  ignore: string;
324
351
  deactivate: string;
@@ -527,6 +554,58 @@ export type IntegrationsLabels = {
527
554
  dismiss: string;
528
555
  remove: string;
529
556
  };
557
+ /**
558
+ * The sentences that explain a consequence rather than name a control.
559
+ *
560
+ * They used to be written into the components, which meant a Turkish
561
+ * installation read English paragraphs in the middle of a Turkish screen —
562
+ * and these are exactly the paragraphs somebody reads before doing something
563
+ * irreversible. The short labels were always translatable; the reasoning was
564
+ * not, which is backwards.
565
+ */
566
+ explain: {
567
+ exportBundleRebuildsIntegration: string;
568
+ exportOmitsCredentials: string;
569
+ credentialsWriteOnlyBody: string;
570
+ credentialsNoAuthNothingToStore: string;
571
+ sampleOneRequestLead: string;
572
+ sampleOneRequestRecords: string;
573
+ sampleFirstPrintedLead: string;
574
+ sampleFirstPrintedTail: string;
575
+ sampleOneRequestTail: string;
576
+ sampleBodyKeysAreTopLevel: string;
577
+ sampleBodyHasNoTopLevelKeys: string;
578
+ accountMatchColumnIdentifies: string;
579
+ accountPasswordColumnReceives: string;
580
+ accountMatchFromCarriesValue: string;
581
+ accountFallbackWhenIdentifierEmpty: string;
582
+ accountIdColumnLinksBack: string;
583
+ accountNoMembershipsYet: string;
584
+ accountSharedPasswordPerRun: string;
585
+ accountCreationOff: string;
586
+ scheduleTimezoneIsCompanyClock: string;
587
+ scheduleCannotRunUnattended: string;
588
+ scheduleOneServerTakesTheRun: string;
589
+ scheduleOffUntilAskedFor: string;
590
+ writeModeReplaceRebuildsTable: string;
591
+ writeModeDeleteTrustsShortPage: string;
592
+ paginationWrongStyleSilentlySucceeds: string;
593
+ runConfirmCountsUnknown: string;
594
+ runConfirmKeepThem: string;
595
+ runHistoryNothingRunYet: string;
596
+ runPreviewWritesNothing: string;
597
+ missingReferencesTitle: (count: string | number) => string;
598
+ missingReferencesRecordsLeftOut: (count: string | number) => string;
599
+ missingReferenceRow: (target: string | number, count: string | number) => string;
600
+ missingReferencesCreateAndRun: (count: string | number) => string;
601
+ missingReferencesRunWithoutThem: string;
602
+ plannedReferencesTitle: (count: string | number) => string;
603
+ plannedReferencesCreatedBefore: string;
604
+ plannedReferencesLeaveRecordsOut: string;
605
+ runPlanCountMismatchBody: (reportedTotal: string | number | null, totalFetched: string | number) => string;
606
+ runPlanAccountsToCreateTitle: (accountsToCreate: string | number) => string;
607
+ runPlanAccountsSharePassword: string;
608
+ };
530
609
  };
531
610
  /** What every app gets before it says otherwise. */
532
611
  export declare const defaultIntegrationsLabels: IntegrationsLabels;
@@ -298,6 +298,26 @@
298
298
  append: 'Nothing is matched, so running this import twice stores every record twice.',
299
299
  replace: 'The table is emptied and refilled on every run.'
300
300
  },
301
+ runStatusFull: {
302
+ queued: 'queued',
303
+ running: 'running',
304
+ succeeded: 'succeeded',
305
+ failed: 'failed',
306
+ cancelled: 'cancelled',
307
+ reverted: 'reverted'
308
+ },
309
+ triggerFull: {
310
+ manual: 'manual',
311
+ schedule: 'scheduled',
312
+ api: 'API'
313
+ },
314
+ badges: {
315
+ unknownTarget: 'unknown target',
316
+ dedupSet: 'dedup key set',
317
+ dedupMissing: 'no dedup key',
318
+ missingRows: (policy)=>`missing rows: ${policy}`,
319
+ readOf: (fetched, reported)=>`read ${fetched} of ${reported}`
320
+ },
301
321
  missingPolicyFull: {
302
322
  ignore: 'Ignore — leave them exactly as they are',
303
323
  deactivate: 'Deactivate — mark them with a column value',
@@ -488,6 +508,49 @@
488
508
  somethingWentWrong: 'Something went wrong',
489
509
  dismiss: 'Dismiss error',
490
510
  remove: 'Remove'
511
+ },
512
+ explain: {
513
+ exportBundleRebuildsIntegration: 'One file holding this connection, its calls and its mappings — enough to rebuild the whole integration in another installation. Bring it in there with Import, on the connections screen.',
514
+ exportOmitsCredentials: 'Tokens, passwords and keys are left out on purpose. Whatever this connection authenticates with has to be entered again wherever the file lands.',
515
+ credentialsWriteOnlyBody: 'A value typed here is stored and from then on only ever sent to the service. It is not returned to this screen, or to any other, no matter who asks — all that comes back is a masked hint. Leave a box empty to keep whatever is already stored.',
516
+ credentialsNoAuthNothingToStore: 'This connection is set to no authentication, so there is no secret to store. Pick an auth type above to see the fields it needs.',
517
+ sampleOneRequestLead: 'This is one request. The source answered it with ',
518
+ sampleOneRequestRecords: ' record(s)',
519
+ sampleFirstPrintedLead: ', of which the first ',
520
+ sampleFirstPrintedTail: ' are printed below',
521
+ sampleOneRequestTail: ' — not the whole table. If the endpoint pages, this is one page of it. Preview on the Run step walks every page and reports the real total.',
522
+ sampleBodyKeysAreTopLevel: 'These are the top-level keys of the body the source returned. Set the response root path to whichever holds the list.',
523
+ sampleBodyHasNoTopLevelKeys: 'The body has no top-level keys at all: it is empty, or it is a bare value rather than an object.',
524
+ accountMatchColumnIdentifies: 'The column on the account that identifies it.',
525
+ accountPasswordColumnReceives: "Where the run's shared password is written.",
526
+ accountMatchFromCarriesValue: "The imported record's column carrying that same value.",
527
+ accountFallbackWhenIdentifierEmpty: "Used only when the column above came back EMPTY, so nobody is left without a login. Name the record's own columns in braces — {firstName}.{sicilNo}@acme.com. Leave it blank to go on skipping those records.",
528
+ accountIdColumnLinksBack: "Filled with the new account's id, so the record and its login know each other.",
529
+ accountNoMembershipsYet: 'Nothing. An account with no role can sign in and do nothing — every screen it reaches refuses it. A role usually lives in its own table, joined to the account, which is what this adds.',
530
+ accountSharedPasswordPerRun: 'Every account this run creates is given the same opening password — asked for once, when the import is started, and stored nowhere. People change it themselves afterwards. A record whose value already has an account is linked to that one instead, so running this import again does not create a second.',
531
+ accountCreationOff: 'Off. Records are imported without a login — useful for a directory, not for people who have to sign in.',
532
+ scheduleTimezoneIsCompanyClock: "The company's own clock, so 02:00 stays 02:00 across a daylight-saving change.",
533
+ scheduleCannotRunUnattended: 'It creates a login for every new record, and a login needs a password — which there is nobody to type at that hour. Either start this import by hand, or turn the login off and give people their accounts another way.',
534
+ scheduleOneServerTakesTheRun: 'Every server running this app checks the schedule, and exactly one of them takes the import — the same lock that stops two people running it at once.',
535
+ scheduleOffUntilAskedFor: 'Off. This import runs when somebody asks for it, and at no other time.',
536
+ writeModeReplaceRebuildsTable: 'Every existing row is dropped and written again, so row ids change. Foreign keys, saved links, exports, report filters and anything else holding one of those ids breaks, and no amount of re-running repairs it.',
537
+ writeModeDeleteTrustsShortPage: 'A source that answers with a short page — a filter left on, a timeout, an expired token honoured with an empty list — looks exactly like records that went away.',
538
+ paginationWrongStyleSilentlySucceeds: 'Nothing here is detected for you. Pick the wrong style and the import still succeeds — it reads the first page and never asks for the rest. In a destructive write mode the records it never saw count as missing, so the run deletes or deactivates them.',
539
+ runConfirmCountsUnknown: 'These counts are unknown because nothing was previewed. Run a preview first if you want to know how many rows this removes before it removes them.',
540
+ runConfirmKeepThem: 'Keep them, do not run',
541
+ runHistoryNothingRunYet: 'Nothing has run yet. A preview writes nothing; the first entry appears here once an import is started.',
542
+ runPreviewWritesNothing: 'A preview reads the source without writing anything, and reports how many rows would be created, updated, skipped, and how many rows in the target the source no longer returns.',
543
+ missingReferencesTitle: (count)=>`$${count} value(s) this import refers to are not here yet`,
544
+ missingReferencesRecordsLeftOut: (count)=>`As it stands the records that carry them are left out — $${count} record(s) in total. Either add the values first, or import without those records.`,
545
+ missingReferenceRow: (target, count)=>` — missing from $${target}, needed by $${count} record(s)`,
546
+ missingReferencesCreateAndRun: (count)=>`Create these $${count} value(s) and import everything`,
547
+ missingReferencesRunWithoutThem: 'Import without those records',
548
+ plannedReferencesTitle: (count)=>`$${count} value(s) will be added first`,
549
+ plannedReferencesCreatedBefore: 'These are created in the same import, before the records that point at them, so nothing is written pointing at nothing. They carry only the value itself — rename them afterwards.',
550
+ plannedReferencesLeaveRecordsOut: 'Do not create them — leave those records out',
551
+ runPlanCountMismatchBody: (reportedTotal, totalFetched)=>`The source reports ${reportedTotal} record(s); ${totalFetched} were actually read. Check the endpoint's pagination profile before running: a wrong profile reads one page, and every record it never saw counts as missing.`,
552
+ runPlanAccountsToCreateTitle: (accountsToCreate)=>`${accountsToCreate} login(s) will be created, with the password typed above`,
553
+ runPlanAccountsSharePassword: 'Each new record gets an account in the same import — one password for all of them, which people change themselves afterwards. Anyone whose value already has an account is linked to it instead.'
491
554
  }
492
555
  };
493
556
  /**