nucleus-core-ts 0.9.843 → 0.9.845

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 (19) hide show
  1. package/dist/fe/components/IntegrationsPage/components/ConnectionDelete.d.ts +20 -0
  2. package/dist/fe/components/IntegrationsPage/components/ConnectionDelete.js +116 -0
  3. package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.d.ts +3 -1
  4. package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.js +7 -1
  5. package/dist/fe/components/IntegrationsPage/components/ConnectionSwitcher.js +16 -9
  6. package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +3 -3
  7. package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +2 -2
  8. package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +1 -1
  9. package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +14 -0
  10. package/dist/fe/components/IntegrationsPage/components/MappingList.js +6 -1
  11. package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.js +2 -8
  12. package/dist/fe/components/IntegrationsPage/components/RunPanel.js +2 -2
  13. package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.d.ts +11 -0
  14. package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.js +14 -0
  15. package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.test.d.ts +1 -0
  16. package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.test.js +29 -0
  17. package/dist/fe/components/IntegrationsPage/text/index.d.ts +3 -13
  18. package/dist/fe/components/IntegrationsPage/text/index.js +4 -14
  19. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import type { IntegrationSource, IntegrationsActions } from '../types';
2
+ /**
3
+ * Removing a connection.
4
+ *
5
+ * `deleteSource` existed in the contract from the beginning and nothing ever
6
+ * called it: a connection could be created and edited but never removed, so a
7
+ * mistyped one stayed on the rail forever. The one operation that cannot be
8
+ * undone was the one with no button.
9
+ *
10
+ * Asked twice, and the second time by name. A connection is the root of its
11
+ * calls, its mappings and their history; "are you sure" is not a warning
12
+ * anybody reads, but typing the name is a sentence you cannot press by accident.
13
+ */
14
+ export type ConnectionDeleteProps = {
15
+ actions: IntegrationsActions;
16
+ source: IntegrationSource | null;
17
+ /** Called once it is gone, so the rail can come back. */
18
+ onDeleted?: () => void;
19
+ };
20
+ export declare function ConnectionDelete({ actions, source, onDeleted }: ConnectionDeleteProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { useIntegrationsStore } from '../store';
5
+ import { integrationsPageTheme as theme } from '../theme';
6
+ import { deleteIsConfirmed } from './deleteIsConfirmed';
7
+ import { Notice } from './Primitives';
8
+ export function ConnectionDelete({ actions, source, onDeleted }) {
9
+ const store = useIntegrationsStore();
10
+ const [asking, setAsking] = useState(false);
11
+ const [typed, setTyped] = useState('');
12
+ const [busy, setBusy] = useState(false);
13
+ if (!source) return null;
14
+ const confirmed = deleteIsConfirmed(source.name, typed);
15
+ const remove = ()=>{
16
+ setBusy(true);
17
+ store.setError(null);
18
+ actions.deleteSource(source.id).then((done)=>{
19
+ setBusy(false);
20
+ if (!done) {
21
+ store.setError('The connection was not deleted.');
22
+ return;
23
+ }
24
+ setAsking(false);
25
+ setTyped('');
26
+ store.selectSource(null);
27
+ onDeleted?.();
28
+ }).catch((error)=>{
29
+ setBusy(false);
30
+ store.setError(error instanceof Error ? error.message : 'The request failed.');
31
+ });
32
+ };
33
+ return /*#__PURE__*/ _jsxs("section", {
34
+ className: theme.card.wrapper,
35
+ children: [
36
+ /*#__PURE__*/ _jsxs("header", {
37
+ className: theme.card.header,
38
+ children: [
39
+ /*#__PURE__*/ _jsx("h3", {
40
+ className: theme.card.title,
41
+ children: "Remove this connection"
42
+ }),
43
+ asking ? null : /*#__PURE__*/ _jsx("button", {
44
+ className: theme.button.danger,
45
+ onClick: ()=>setAsking(true),
46
+ type: "button",
47
+ children: "Delete"
48
+ })
49
+ ]
50
+ }),
51
+ /*#__PURE__*/ _jsx("div", {
52
+ className: theme.card.body,
53
+ children: asking ? /*#__PURE__*/ _jsxs(_Fragment, {
54
+ children: [
55
+ /*#__PURE__*/ _jsxs(Notice, {
56
+ title: "This takes its calls and mappings with it",
57
+ tone: "danger",
58
+ children: [
59
+ "Everything configured under “",
60
+ source.name,
61
+ "” goes: the calls, the mappings and the record of what they imported. The rows already written into your tables stay where they are — but nothing will be able to undo the imports that put them there."
62
+ ]
63
+ }),
64
+ /*#__PURE__*/ _jsxs("label", {
65
+ className: theme.field.wrapper,
66
+ children: [
67
+ /*#__PURE__*/ _jsxs("span", {
68
+ className: theme.field.label,
69
+ children: [
70
+ "Type ",
71
+ /*#__PURE__*/ _jsx("strong", {
72
+ children: source.name
73
+ }),
74
+ " to confirm"
75
+ ]
76
+ }),
77
+ /*#__PURE__*/ _jsx("input", {
78
+ className: theme.field.input,
79
+ disabled: busy,
80
+ onChange: (event)=>setTyped(event.target.value),
81
+ placeholder: source.name,
82
+ value: typed
83
+ })
84
+ ]
85
+ }),
86
+ /*#__PURE__*/ _jsxs("div", {
87
+ className: "flex flex-wrap justify-end gap-2",
88
+ children: [
89
+ /*#__PURE__*/ _jsx("button", {
90
+ className: theme.button.secondary,
91
+ disabled: busy,
92
+ onClick: ()=>{
93
+ setAsking(false);
94
+ setTyped('');
95
+ },
96
+ type: "button",
97
+ children: "Cancel"
98
+ }),
99
+ /*#__PURE__*/ _jsx("button", {
100
+ className: theme.button.danger,
101
+ disabled: busy || !confirmed,
102
+ onClick: remove,
103
+ type: "button",
104
+ children: busy ? 'Deleting…' : 'Delete this connection'
105
+ })
106
+ ]
107
+ })
108
+ ]
109
+ }) : /*#__PURE__*/ _jsx("p", {
110
+ className: theme.field.hint,
111
+ children: "A connection can be turned off instead — that stops every call and import through it and keeps the settings. Deleting is for one that should not have existed."
112
+ })
113
+ })
114
+ ]
115
+ });
116
+ }
@@ -3,6 +3,8 @@ export type ConnectionSettingsTabProps = {
3
3
  actions: IntegrationsActions;
4
4
  /** Called after a NEW connection is saved, so the list can pick it up. */
5
5
  onSaved?: (source: IntegrationSource) => void;
6
+ /** Called after this connection is deleted, so the rail can come back. */
7
+ onDeleted?: () => void;
6
8
  };
7
9
  /**
8
10
  * Everything about the connection itself: its address and authentication, its
@@ -18,4 +20,4 @@ export type ConnectionSettingsTabProps = {
18
20
  * credential boxes invites pressing it before there is anything to authenticate
19
21
  * with, and reading the failure as "the address is wrong".
20
22
  */
21
- export declare function ConnectionSettingsTab({ actions, onSaved }: ConnectionSettingsTabProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function ConnectionSettingsTab({ actions, onSaved, onDeleted }: ConnectionSettingsTabProps): import("react/jsx-runtime").JSX.Element;
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useIntegrationsStore } from '../store';
4
4
  import { integrationsPageTheme as theme } from '../theme';
5
+ import { ConnectionDelete } from './ConnectionDelete';
5
6
  import { ConnectionForm } from './ConnectionForm';
6
7
  import { ConnectionTest } from './ConnectionTest';
7
8
  import { CredentialFields } from './CredentialFields';
@@ -18,7 +19,7 @@ import { CredentialFields } from './CredentialFields';
18
19
  * the address, enters the secret, then presses test. Putting the test above the
19
20
  * credential boxes invites pressing it before there is anything to authenticate
20
21
  * with, and reading the failure as "the address is wrong".
21
- */ export function ConnectionSettingsTab({ actions, onSaved }) {
22
+ */ export function ConnectionSettingsTab({ actions, onSaved, onDeleted }) {
22
23
  const store = useIntegrationsStore();
23
24
  const source = store.sources.find((candidate)=>candidate.id === store.selectedSourceId) ?? null;
24
25
  // The list holds only the current page, so a connection selected before a
@@ -43,6 +44,11 @@ import { CredentialFields } from './CredentialFields';
43
44
  actions: actions,
44
45
  loading: store.sourcesLoading || missingFromPage,
45
46
  source: source
47
+ }),
48
+ /*#__PURE__*/ _jsx(ConnectionDelete, {
49
+ actions: actions,
50
+ onDeleted: onDeleted,
51
+ source: source
46
52
  })
47
53
  ]
48
54
  });
@@ -31,28 +31,35 @@ import { integrationsPageTheme as theme } from '../theme';
31
31
  return /*#__PURE__*/ _jsxs("div", {
32
32
  className: cn('flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-slate-200/80 bg-white px-4 py-3 dark:border-slate-800 dark:bg-slate-900'),
33
33
  children: [
34
+ /*#__PURE__*/ _jsxs("button", {
35
+ "aria-label": labels.connections.change,
36
+ className: cn(theme.button.secondary, 'shrink-0 gap-1.5'),
37
+ onClick: ()=>store.selectSource(null),
38
+ type: "button",
39
+ children: [
40
+ /*#__PURE__*/ _jsx("span", {
41
+ "aria-hidden": "true",
42
+ children: "←"
43
+ }),
44
+ labels.connections.change
45
+ ]
46
+ }),
34
47
  /*#__PURE__*/ _jsxs("div", {
35
- className: "flex min-w-0 flex-col",
48
+ className: "flex min-w-0 flex-1 flex-col items-end text-right",
36
49
  children: [
37
50
  /*#__PURE__*/ _jsx("span", {
38
51
  className: cn(theme.field.label, 'text-slate-400 dark:text-slate-500'),
39
52
  children: labels.connections.heading
40
53
  }),
41
54
  /*#__PURE__*/ _jsx("span", {
42
- className: "truncate font-semibold text-base text-slate-900 dark:text-slate-100",
55
+ className: "w-full truncate font-semibold text-base text-slate-900 dark:text-slate-100",
43
56
  children: source?.name ?? labels.connections.heading
44
57
  }),
45
58
  source?.baseUrl ? /*#__PURE__*/ _jsx("span", {
46
- className: cn(theme.list.itemMeta, 'mt-0.5'),
59
+ className: cn(theme.list.itemMeta, 'w-full truncate'),
47
60
  children: source.baseUrl
48
61
  }) : null
49
62
  ]
50
- }),
51
- /*#__PURE__*/ _jsx("button", {
52
- className: theme.button.secondary,
53
- onClick: ()=>store.selectSource(null),
54
- type: "button",
55
- children: labels.connections.change
56
63
  })
57
64
  ]
58
65
  });
@@ -59,7 +59,7 @@ export function ConnectionTest({ actions, source, loading = false }) {
59
59
  }),
60
60
  result ? /*#__PURE__*/ _jsx(Badge, {
61
61
  tone: result.ok ? 'success' : 'danger',
62
- children: result.ok ? 'Reachable' : 'Not reachable'
62
+ children: result.ok ? labels.auth.testOkBadge : labels.auth.testNotReachable
63
63
  }) : null
64
64
  ]
65
65
  }),
@@ -75,7 +75,7 @@ export function ConnectionTest({ actions, source, loading = false }) {
75
75
  ]
76
76
  }) : /*#__PURE__*/ _jsx("p", {
77
77
  className: theme.field.hint,
78
- children: "Save the connection first — there is nothing to call yet."
78
+ children: labels.auth.testSaveFirst
79
79
  }),
80
80
  result?.ok ? /*#__PURE__*/ _jsxs(Notice, {
81
81
  title: labels.auth.testOkTitle,
@@ -110,7 +110,7 @@ export function ConnectionTest({ actions, source, loading = false }) {
110
110
  disabled: !source || testing,
111
111
  onClick: handleTest,
112
112
  type: "button",
113
- children: testing ? 'Testing…' : 'Test connection'
113
+ children: testing ? labels.auth.testing : labels.auth.testRun
114
114
  })
115
115
  })
116
116
  ]
@@ -47,7 +47,7 @@ export function CredentialSlotRow({ slot, source, value, maskedHint, disabled, o
47
47
  children: stored ? labels.auth.stored : labels.auth.notSet
48
48
  }),
49
49
  stored ? /*#__PURE__*/ _jsx("span", {
50
- className: cn(theme.field.hint, 'truncate font-mono'),
50
+ className: cn(theme.field.hint, 'min-w-0 truncate font-mono'),
51
51
  children: maskedHint
52
52
  }) : null,
53
53
  /*#__PURE__*/ _jsx("button", {
@@ -56,7 +56,7 @@ export function CredentialSlotRow({ slot, source, value, maskedHint, disabled, o
56
56
  disabled: disabled || !stored,
57
57
  onClick: onClear,
58
58
  type: "button",
59
- children: "Clear"
59
+ children: labels.auth.clear
60
60
  })
61
61
  ]
62
62
  })
@@ -42,7 +42,7 @@ export function FieldMappingRows({ rows, sourceFields, targetFields, disabled, o
42
42
  EMPTY_FIELD_MAPPING
43
43
  ]),
44
44
  type: "button",
45
- children: "Add field"
45
+ children: labels.mappings.addField
46
46
  }),
47
47
  purpose: "Which incoming value becomes which column. The order is the order they are applied in.",
48
48
  title: labels.mappings.fieldsHeading,
@@ -47,6 +47,18 @@ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect a
47
47
  setSourcesReloadToken((token)=>token + 1);
48
48
  };
49
49
  /**
50
+ * A connection was deleted.
51
+ *
52
+ * Nothing about it is worth keeping on screen — the tabs below would be
53
+ * showing the calls and mappings of a record that no longer exists — so the
54
+ * selection is dropped, the rail comes back, and the list is asked again.
55
+ * `forgetFocusOnLeave` matters here too: arriving from a table set a focus
56
+ * that would otherwise re-select the next connection instantly.
57
+ */ const handleSourceDeleted = ()=>{
58
+ forgetFocusOnLeave();
59
+ setSourcesReloadToken((token)=>token + 1);
60
+ };
61
+ /**
50
62
  * How far this connection has got, asked for directly.
51
63
  *
52
64
  * The step strip promises to say where you are, so it cannot depend on having
@@ -273,6 +285,7 @@ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect a
273
285
  children: [
274
286
  tab === 'connection' ? /*#__PURE__*/ _jsx(ConnectionSettingsTab, {
275
287
  actions: actions,
288
+ onDeleted: handleSourceDeleted,
276
289
  onSaved: handleSourceSaved
277
290
  }) : null,
278
291
  tab === 'endpoints' ? /*#__PURE__*/ _jsx(EndpointsTab, {
@@ -305,6 +318,7 @@ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect a
305
318
  className: theme.layout.main,
306
319
  children: /*#__PURE__*/ _jsx(ConnectionSettingsTab, {
307
320
  actions: actions,
321
+ onDeleted: handleSourceDeleted,
308
322
  onSaved: handleSourceSaved
309
323
  })
310
324
  })
@@ -147,7 +147,12 @@ export function MappingList({ actions, sourceId, selectedId, reloadToken, onSele
147
147
  children: [
148
148
  /*#__PURE__*/ _jsxs("button", {
149
149
  "aria-current": mapping.id === selectedId ? 'true' : undefined,
150
- className: cn(theme.list.item, 'flex-1', mapping.id === selectedId ? theme.list.itemSelected : undefined),
150
+ className: cn(theme.list.item, // `flex-1` alone leaves min-width at auto, and the title
151
+ // inside truncates — which means it never wraps, so its
152
+ // min-content width is the WHOLE name. One long mapping
153
+ // name then widened this row past the panel and scrolled
154
+ // the page sideways. min-w-0 is what lets truncate truncate.
155
+ 'min-w-0 flex-1', mapping.id === selectedId ? theme.list.itemSelected : undefined),
151
156
  onClick: ()=>onSelect(mapping),
152
157
  type: "button",
153
158
  children: [
@@ -56,15 +56,9 @@ import { Notice } from './Primitives';
56
56
  /*#__PURE__*/ _jsxs("div", {
57
57
  className: theme.card.body,
58
58
  children: [
59
- required.length === 0 ? /*#__PURE__*/ _jsxs("p", {
59
+ required.length === 0 ? /*#__PURE__*/ _jsx("p", {
60
60
  className: theme.field.hint,
61
- children: [
62
- "This endpoint takes no parameters. Add a ",
63
- /*#__PURE__*/ _jsx("code", {
64
- children: '{name}'
65
- }),
66
- " token to its path if it needs one."
67
- ]
61
+ children: labels.endpoints.bindingsNone
68
62
  }) : null,
69
63
  unbound.length > 0 ? /*#__PURE__*/ _jsxs(Notice, {
70
64
  title: labels.endpoints.bindingsTitle,
@@ -178,14 +178,14 @@ export function RunPanel({ mapping, actions, onSubscribeProgress, onRunSettled }
178
178
  disabled: store.planning || activeRunId !== null,
179
179
  onClick: preview,
180
180
  type: "button",
181
- children: store.planning ? 'Previewing…' : 'Preview'
181
+ children: store.planning ? labels.runs.previewing : labels.runs.preview
182
182
  }),
183
183
  /*#__PURE__*/ _jsx("button", {
184
184
  className: theme.button.primary,
185
185
  disabled: starting || confirming || activeRunId !== null || !passwordReady,
186
186
  onClick: ()=>start(false),
187
187
  type: "button",
188
- children: starting ? 'Starting…' : 'Run import'
188
+ children: starting ? labels.runs.starting : labels.runs.start
189
189
  })
190
190
  ]
191
191
  })
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Whether what the operator typed matches the connection they are deleting.
3
+ *
4
+ * Kept out of the component so the rule can be tested: it is the only thing
5
+ * standing between a stray click and a connection's calls, mappings and run
6
+ * history. Trimmed on both sides because a name pasted out of the list carries
7
+ * a trailing space often enough that refusing it reads as a bug, and compared
8
+ * exactly otherwise — two connections called "HR" and "HR 2" must not confirm
9
+ * for each other.
10
+ */
11
+ export declare function deleteIsConfirmed(name: string, typed: string): boolean;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Whether what the operator typed matches the connection they are deleting.
3
+ *
4
+ * Kept out of the component so the rule can be tested: it is the only thing
5
+ * standing between a stray click and a connection's calls, mappings and run
6
+ * history. Trimmed on both sides because a name pasted out of the list carries
7
+ * a trailing space often enough that refusing it reads as a bug, and compared
8
+ * exactly otherwise — two connections called "HR" and "HR 2" must not confirm
9
+ * for each other.
10
+ */ export function deleteIsConfirmed(name, typed) {
11
+ const target = name.trim();
12
+ if (target.length === 0) return false;
13
+ return typed.trim() === target;
14
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { deleteIsConfirmed } from './deleteIsConfirmed';
3
+ describe('deleteIsConfirmed', ()=>{
4
+ it('confirms the exact name', ()=>{
5
+ expect(deleteIsConfirmed('TatMetal HR', 'TatMetal HR')).toBe(true);
6
+ });
7
+ it('forgives surrounding whitespace on either side', ()=>{
8
+ expect(deleteIsConfirmed('TatMetal HR', ' TatMetal HR ')).toBe(true);
9
+ expect(deleteIsConfirmed(' TatMetal HR', 'TatMetal HR')).toBe(true);
10
+ });
11
+ it('refuses a different connection with a similar name', ()=>{
12
+ expect(deleteIsConfirmed('HR', 'HR 2')).toBe(false);
13
+ expect(deleteIsConfirmed('HR 2', 'HR')).toBe(false);
14
+ });
15
+ it('is case sensitive — the name is copied, not remembered', ()=>{
16
+ expect(deleteIsConfirmed('TatMetal HR', 'tatmetal hr')).toBe(false);
17
+ });
18
+ it('refuses an empty box', ()=>{
19
+ expect(deleteIsConfirmed('TatMetal HR', '')).toBe(false);
20
+ expect(deleteIsConfirmed('TatMetal HR', ' ')).toBe(false);
21
+ });
22
+ /**
23
+ * A connection saved with a blank name would otherwise confirm on an empty
24
+ * box — the one case where pressing nothing deletes something.
25
+ */ it('never confirms when the connection itself has no name', ()=>{
26
+ expect(deleteIsConfirmed('', '')).toBe(false);
27
+ expect(deleteIsConfirmed(' ', '')).toBe(false);
28
+ });
29
+ });
@@ -35,7 +35,6 @@ export type IntegrationsLabels = {
35
35
  };
36
36
  /** Marks a step whose prerequisite is not done yet. */
37
37
  lockedHint: string;
38
- doneHint: string;
39
38
  /** One short phrase per step proving it is done. */
40
39
  evidence: {
41
40
  connection: (name: string) => string;
@@ -69,7 +68,9 @@ export type IntegrationsLabels = {
69
68
  /** The one-line summary above the buttons. */
70
69
  summary: (target: string, writeMode: string, missing: string) => string;
71
70
  preview: string;
71
+ previewing: string;
72
72
  start: string;
73
+ starting: string;
73
74
  cancel: string;
74
75
  previewExplainTitle: string;
75
76
  previewExplainBody: string;
@@ -131,7 +132,6 @@ export type IntegrationsLabels = {
131
132
  bindingsTitle: string;
132
133
  bindingsUnbound: (names: string) => string;
133
134
  bindingsNone: string;
134
- bindFrom: string;
135
135
  bindStatic: string;
136
136
  bindEndpoint: string;
137
137
  bindEntity: string;
@@ -215,7 +215,6 @@ export type IntegrationsLabels = {
215
215
  sampleFirstTitle: string;
216
216
  sampleFirstBody: string;
217
217
  nothingMapped: string;
218
- parentPrefixHint: string;
219
218
  templateHint: string;
220
219
  source: string;
221
220
  sourcePlaceholder: string;
@@ -262,6 +261,7 @@ export type IntegrationsLabels = {
262
261
  clear: string;
263
262
  testHeading: string;
264
263
  testRun: string;
264
+ testing: string;
265
265
  testOkTitle: string;
266
266
  testOkBadge: string;
267
267
  testFailedTitle: string;
@@ -290,16 +290,6 @@ export type IntegrationsLabels = {
290
290
  deactivate: string;
291
291
  delete: string;
292
292
  };
293
- writeMode: {
294
- upsert: string;
295
- append: string;
296
- replace: string;
297
- };
298
- missingPolicy: {
299
- ignore: string;
300
- deactivate: string;
301
- delete: string;
302
- };
303
293
  common: {
304
294
  save: string;
305
295
  cancel: string;
@@ -31,7 +31,6 @@
31
31
  purpose: 'See what would happen, then let it happen.'
32
32
  },
33
33
  lockedHint: 'Finish the step before this one first.',
34
- doneHint: 'Done',
35
34
  evidence: {
36
35
  connection: (name)=>`Connected to ${name}`,
37
36
  endpoints: (total, sampled)=>sampled === total ? `${total} call(s), all tried` : `${total} call(s), ${sampled} tried`,
@@ -51,7 +50,7 @@
51
50
  loadFailed: 'This list could not be loaded',
52
51
  retry: 'Try again',
53
52
  off: 'Off',
54
- change: 'Change',
53
+ change: 'Back to connections',
55
54
  editTitle: 'Connection',
56
55
  newTitle: 'New connection',
57
56
  enabledBadge: 'Enabled',
@@ -62,7 +61,9 @@
62
61
  runs: {
63
62
  summary: (target, writeMode, missing)=>`Writes into ${target} · ${writeMode} · missing rows: ${missing}`,
64
63
  preview: 'Preview',
64
+ previewing: 'Previewing…',
65
65
  start: 'Run import',
66
+ starting: 'Starting…',
66
67
  cancel: 'Cancel',
67
68
  previewExplainTitle: 'Preview before you run',
68
69
  previewExplainBody: '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.',
@@ -123,7 +124,6 @@
123
124
  bindingsTitle: 'Parameter values',
124
125
  bindingsUnbound: (names)=>`${names} — an import cannot run until every one of them is bound. Add a binding for each below.`,
125
126
  bindingsNone: 'This call takes no parameters. Add a {name} token to its path if it needs one.',
126
- bindFrom: 'Value from',
127
127
  bindStatic: 'A fixed value',
128
128
  bindEndpoint: 'A field of another call',
129
129
  bindEntity: 'A column of one of our tables',
@@ -205,7 +205,6 @@
205
205
  sampleFirstTitle: 'Try the call first',
206
206
  sampleFirstBody: 'No field names are known yet. Trying the selected call lists what the response really returns; until then a source has to be typed from memory, and a typo shows up only as an empty column once the import has already run.',
207
207
  nothingMapped: 'Nothing is mapped yet, so a run would write empty records. Add a field to begin.',
208
- parentPrefixHint: 'to take the value from the parent call’s record instead of this one.',
209
208
  templateHint: 'to build one value from several — use it when no single field is unique on its own, which is what the matching column needs.',
210
209
  source: 'Source',
211
210
  sourcePlaceholder: 'employeeNo',
@@ -251,6 +250,7 @@
251
250
  clear: 'Clear',
252
251
  testHeading: 'Does it answer?',
253
252
  testRun: 'Test the connection',
253
+ testing: 'Testing…',
254
254
  testOkTitle: 'Connection succeeded',
255
255
  testOkBadge: 'Reachable',
256
256
  testFailedTitle: 'Connection failed',
@@ -278,16 +278,6 @@
278
278
  deactivate: 'Reversible. The row survives and history that refers to it stays readable.',
279
279
  delete: 'Permanent. A source that returns a short page for any reason takes rows with it.'
280
280
  },
281
- writeMode: {
282
- upsert: 'update what matches, insert the rest',
283
- append: 'always insert, never match',
284
- replace: 'empty the table, then insert'
285
- },
286
- missingPolicy: {
287
- ignore: 'leave them exactly as they are',
288
- deactivate: 'mark them inactive',
289
- delete: 'delete them'
290
- },
291
281
  common: {
292
282
  save: 'Save',
293
283
  cancel: 'Cancel',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.843",
3
+ "version": "0.9.845",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",