@workbench-kit/shell-react 0.0.2-prototype.0.2.6

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 (93) hide show
  1. package/package.json +49 -0
  2. package/src/chat-ai-mock-proposals.ts +39 -0
  3. package/src/chat-command-input.ts +52 -0
  4. package/src/chat-command-policy.ts +54 -0
  5. package/src/chat-command-surface.tsx +155 -0
  6. package/src/chat-view-data.ts +20 -0
  7. package/src/chat-view.tsx +220 -0
  8. package/src/command-inspector-surface.tsx +46 -0
  9. package/src/commands-view-data.ts +17 -0
  10. package/src/commands-view.tsx +43 -0
  11. package/src/createCommandWorkspaceExplorerPort.ts +86 -0
  12. package/src/devtools/WorkbenchDevtoolsPanel.tsx +124 -0
  13. package/src/devtools/WorkbenchDevtoolsShell.tsx +17 -0
  14. package/src/devtools/index.ts +11 -0
  15. package/src/devtools/use-workbench-devtools-snapshot.ts +84 -0
  16. package/src/devtools/workbench-devtools-snapshot.ts +140 -0
  17. package/src/devtools/workbench-devtools.css +82 -0
  18. package/src/editor-area-dnd.ts +69 -0
  19. package/src/editor-area-model.ts +71 -0
  20. package/src/editor-area.css +454 -0
  21. package/src/editor-area.tsx +676 -0
  22. package/src/editor-host-surface.tsx +634 -0
  23. package/src/editor-pane-visibility.ts +130 -0
  24. package/src/editor-resource.ts +36 -0
  25. package/src/editor-state-storage.ts +193 -0
  26. package/src/editor-tab-context-menu.ts +118 -0
  27. package/src/editor-view-providers.tsx +552 -0
  28. package/src/editor-workspace-file-availability.ts +15 -0
  29. package/src/editor-workspace-reconcile.tsx +28 -0
  30. package/src/explorer-context-menu.ts +114 -0
  31. package/src/explorer-reveal.ts +112 -0
  32. package/src/explorer-view-data.ts +58 -0
  33. package/src/explorer-view.tsx +224 -0
  34. package/src/extension-context-menu.ts +52 -0
  35. package/src/extension-management-model.ts +313 -0
  36. package/src/extensions-view-data.ts +19 -0
  37. package/src/extensions-view.tsx +38 -0
  38. package/src/field-remap-demo.tsx +18 -0
  39. package/src/field-remap-editor-surface.tsx +36 -0
  40. package/src/field-remap-flow-adapter.ts +240 -0
  41. package/src/field-remap-flow.tsx +369 -0
  42. package/src/field-remap-graph.tsx +559 -0
  43. package/src/field-remap-panel.tsx +175 -0
  44. package/src/field-remap-samples.ts +352 -0
  45. package/src/field-remap-tree.tsx +404 -0
  46. package/src/field-remap-view-data.ts +16 -0
  47. package/src/field-remap-view.css +676 -0
  48. package/src/field-remap-view.tsx +86 -0
  49. package/src/index.ts +199 -0
  50. package/src/jdw-lab-view-data.ts +25 -0
  51. package/src/jdw-lab-view.css +21 -0
  52. package/src/jdw-lab-view.tsx +79 -0
  53. package/src/jdw-widget-form-view.tsx +65 -0
  54. package/src/jdw-widget-preview-view.tsx +51 -0
  55. package/src/json-form-source-patch.ts +323 -0
  56. package/src/jsonata-transform.ts +36 -0
  57. package/src/keybinding-management-settings.tsx +16 -0
  58. package/src/keybinding-overrides-storage.ts +57 -0
  59. package/src/management-palette-commands.ts +38 -0
  60. package/src/management-settings-ids.ts +12 -0
  61. package/src/management-settings.tsx +75 -0
  62. package/src/preference-settings-storage.ts +58 -0
  63. package/src/provider.tsx +573 -0
  64. package/src/search-view-data.ts +15 -0
  65. package/src/search-view.tsx +62 -0
  66. package/src/shell-model.tsx +149 -0
  67. package/src/shell-secondary-actions.tsx +59 -0
  68. package/src/shell-settings-constants.ts +9 -0
  69. package/src/shell-settings.tsx +599 -0
  70. package/src/shell-titlebar-layout-controls.tsx +4 -0
  71. package/src/shell-view-host.tsx +199 -0
  72. package/src/shell.tsx +649 -0
  73. package/src/table-mapping-adapter.ts +59 -0
  74. package/src/use-active-workspace-path.ts +15 -0
  75. package/src/use-command-management.ts +191 -0
  76. package/src/use-context-key-revision.ts +18 -0
  77. package/src/use-editor.ts +107 -0
  78. package/src/use-extension-management.ts +193 -0
  79. package/src/use-keybinding-management.ts +130 -0
  80. package/src/use-persisted-workbench-appearance.ts +33 -0
  81. package/src/use-workbench-chat-command-proposals.ts +194 -0
  82. package/src/use-workbench-command-descriptors.ts +41 -0
  83. package/src/workbench-appearance-storage.ts +115 -0
  84. package/src/workbench-command-host.tsx +243 -0
  85. package/src/workbench-command-palette.ts +208 -0
  86. package/src/workbench-keybinding-bridge.ts +54 -0
  87. package/src/workbench-layout-storage.ts +145 -0
  88. package/src/workbench-profile-modal.tsx +118 -0
  89. package/src/workbench-shell-command-registration.ts +58 -0
  90. package/src/workbench-startup-gate.tsx +136 -0
  91. package/src/workbench-status-sections.ts +43 -0
  92. package/src/workbench-user-commands.ts +46 -0
  93. package/src/workspace-view-state.ts +35 -0
@@ -0,0 +1,175 @@
1
+ import { useMemo, useState, type JSX } from 'react';
2
+ import {
3
+ convertToShape,
4
+ createBuiltinValueTransformRegistry,
5
+ defineConversion,
6
+ defineDataShape,
7
+ findParentChildMappingConflicts,
8
+ sourceFieldsFromPlainObject,
9
+ targetSlotsFromPlainObject,
10
+ withConversionEdges,
11
+ type MappingEdge,
12
+ type ValueTransformRegistry,
13
+ } from '@workbench-kit/field-remap';
14
+
15
+ import { FieldRemapFlowMapper } from './field-remap-flow.js';
16
+ import {
17
+ getFieldRemapSample,
18
+ type FieldRemapSampleDefinition,
19
+ type FieldRemapSampleId,
20
+ } from './field-remap-samples.js';
21
+ import { jsonataValueTransform } from './jsonata-transform.js';
22
+ import './field-remap-view.css';
23
+
24
+ export interface FieldRemapPanelProps {
25
+ /** Catalog sample id, or a full sample definition. */
26
+ readonly sample?: FieldRemapSampleId | FieldRemapSampleDefinition | undefined;
27
+ /** Optional host-owned transform registry (defaults to builtins + JSONata). */
28
+ readonly transforms?: ValueTransformRegistry | undefined;
29
+ readonly className?: string | undefined;
30
+ }
31
+
32
+ function resolveSample(sample: FieldRemapPanelProps['sample']): FieldRemapSampleDefinition {
33
+ if (!sample) {
34
+ return getFieldRemapSample('nested-ab');
35
+ }
36
+ if (typeof sample === 'string') {
37
+ return getFieldRemapSample(sample);
38
+ }
39
+ return sample;
40
+ }
41
+
42
+ /**
43
+ * Self-contained field-remap workbench panel:
44
+ * XYFlow mapper + convertToShape preview for one A→B (or T_A→T_B) sample.
45
+ *
46
+ * Remount with `key={sampleId}` when switching catalog entries so edge state resets.
47
+ */
48
+ export function FieldRemapPanel({
49
+ sample: sampleProp,
50
+ transforms: transformsProp,
51
+ className,
52
+ }: FieldRemapPanelProps): JSX.Element {
53
+ const sample = resolveSample(sampleProp);
54
+ const registry = useMemo(() => {
55
+ if (transformsProp) {
56
+ return transformsProp;
57
+ }
58
+ const next = createBuiltinValueTransformRegistry();
59
+ next.register(jsonataValueTransform);
60
+ return next;
61
+ }, [transformsProp]);
62
+
63
+ const [edges, setEdges] = useState<readonly MappingEdge[]>(() => [...sample.edges]);
64
+
65
+ const sourceFields = useMemo(
66
+ () => sourceFieldsFromPlainObject(sample.source, { idPrefix: sample.sourceIdPrefix }),
67
+ [sample],
68
+ );
69
+ const targetSlots = useMemo(
70
+ () => targetSlotsFromPlainObject(sample.targetShape, { idPrefix: sample.targetIdPrefix }),
71
+ [sample],
72
+ );
73
+
74
+ const shapes = useMemo(
75
+ () => [
76
+ defineDataShape({
77
+ id: sample.sourceIdPrefix,
78
+ label: sample.sourceLabel,
79
+ role: 'source',
80
+ fields: sourceFields,
81
+ }),
82
+ defineDataShape({
83
+ id: sample.targetIdPrefix,
84
+ label: sample.targetLabel,
85
+ role: 'target',
86
+ fields: targetSlots,
87
+ }),
88
+ ],
89
+ [sample, sourceFields, targetSlots],
90
+ );
91
+
92
+ const conflicts = useMemo(
93
+ () => findParentChildMappingConflicts(edges, sourceFields, targetSlots),
94
+ [edges, sourceFields, targetSlots],
95
+ );
96
+
97
+ const result = useMemo(() => {
98
+ const conversion = withConversionEdges(
99
+ defineConversion({
100
+ id: `${sample.sourceIdPrefix}→${sample.targetIdPrefix}`,
101
+ sourceShapeIds: [sample.sourceIdPrefix],
102
+ targetShapeId: sample.targetIdPrefix,
103
+ edges: [...sample.edges],
104
+ }),
105
+ edges,
106
+ );
107
+ try {
108
+ return convertToShape({
109
+ conversion,
110
+ shapes,
111
+ inputs: { [sample.sourceIdPrefix]: sample.source },
112
+ transforms: registry,
113
+ });
114
+ } catch (error) {
115
+ return {
116
+ output: {} as Record<string, unknown>,
117
+ error: error instanceof Error ? error.message : String(error),
118
+ };
119
+ }
120
+ }, [edges, registry, sample, shapes]);
121
+
122
+ return (
123
+ <div
124
+ className={['workbench-field-remap-demo', className].filter(Boolean).join(' ')}
125
+ data-testid="field-remap-demo"
126
+ data-sample-id={sample.id}
127
+ >
128
+ <header className="workbench-field-remap-demo__header">
129
+ <h2 className="workbench-field-remap-demo__title">{sample.title}</h2>
130
+ <p className="workbench-field-remap-demo__intro">{sample.description}</p>
131
+ </header>
132
+
133
+ <FieldRemapFlowMapper
134
+ sources={sourceFields}
135
+ targets={targetSlots}
136
+ edges={edges}
137
+ transforms={registry}
138
+ onEdgesChange={setEdges}
139
+ sourceTitle={sample.sourceLabel}
140
+ targetTitle={sample.targetLabel}
141
+ />
142
+
143
+ {conflicts.length > 0 ? (
144
+ <p
145
+ className="workbench-field-remap-demo__warn"
146
+ role="status"
147
+ data-testid="field-remap-conflicts"
148
+ >
149
+ Warning: parent and child fields are both mapped (
150
+ {conflicts.map((item) => `${item.parentId} / ${item.childId}`).join('; ')}). Prefer one
151
+ level.
152
+ </p>
153
+ ) : null}
154
+
155
+ {'error' in result && result.error ? (
156
+ <p className="workbench-field-remap-demo__error" role="alert">
157
+ {result.error}
158
+ </p>
159
+ ) : null}
160
+
161
+ <div className="workbench-field-remap-demo__panes">
162
+ <section className="workbench-field-remap-demo__pane" aria-labelledby="field-remap-source">
163
+ <h3 id="field-remap-source">{sample.sourceLabel}</h3>
164
+ <pre data-testid="field-remap-input">{JSON.stringify(sample.source, null, 2)}</pre>
165
+ </section>
166
+ <section className="workbench-field-remap-demo__pane" aria-labelledby="field-remap-target">
167
+ <h3 id="field-remap-target">{sample.targetLabel}</h3>
168
+ <pre data-testid="field-remap-result">
169
+ {JSON.stringify('output' in result ? result.output : {}, null, 2)}
170
+ </pre>
171
+ </section>
172
+ </div>
173
+ </div>
174
+ );
175
+ }
@@ -0,0 +1,352 @@
1
+ import type { MappingEdge } from '@workbench-kit/field-remap';
2
+
3
+ /**
4
+ * Field-remap demo scenarios (table / JSON shaped).
5
+ * Runtime stays MappingEdge + convertToShape; UI binds via FieldRemapPanel.
6
+ */
7
+ export type FieldRemapSampleId =
8
+ 'nested-ab' | 't-user-contact' | 't-event-time' | 't-emp-dept' | 't-product-catalog';
9
+
10
+ export interface FieldRemapSampleDefinition {
11
+ readonly id: FieldRemapSampleId;
12
+ readonly title: string;
13
+ readonly description: string;
14
+ readonly sourceLabel: string;
15
+ readonly targetLabel: string;
16
+ readonly source: Record<string, unknown>;
17
+ readonly targetShape: Record<string, unknown>;
18
+ readonly sourceIdPrefix: string;
19
+ readonly targetIdPrefix: string;
20
+ readonly edges: readonly MappingEdge[];
21
+ }
22
+
23
+ /** Alias kept so older URIs still open a known sample. */
24
+ export const FIELD_REMAP_LEGACY_SAMPLE_ALIASES: Readonly<Record<string, FieldRemapSampleId>> = {
25
+ 'interactive-bindings': 'nested-ab',
26
+ 't-order-invoice': 't-event-time',
27
+ };
28
+
29
+ export const FIELD_REMAP_SAMPLES: readonly FieldRemapSampleDefinition[] = [
30
+ {
31
+ id: 'nested-ab',
32
+ title: 'A → B',
33
+ description: 'Object port (profile→location), array itemEdges, and string fan-out',
34
+ sourceLabel: 'A',
35
+ targetLabel: 'B',
36
+ sourceIdPrefix: 'a',
37
+ targetIdPrefix: 'b',
38
+ source: {
39
+ user_name: ' Ada Lovelace ',
40
+ profile: {
41
+ city: 'London',
42
+ country: 'UK',
43
+ },
44
+ tags: [
45
+ { name: 'math', rank: 1 },
46
+ { name: 'computing', rank: 2 },
47
+ ],
48
+ },
49
+ targetShape: {
50
+ name: '',
51
+ title: '',
52
+ location: {
53
+ city: '',
54
+ country: '',
55
+ },
56
+ labels: [{ title: '', order: 0 }],
57
+ firstTag: '',
58
+ tagLine: '',
59
+ },
60
+ edges: [
61
+ {
62
+ id: 'e-name',
63
+ sourceFieldId: 'a.user_name',
64
+ targetSlotId: 'b.name',
65
+ transformIds: ['string:trim'],
66
+ },
67
+ {
68
+ id: 'e-title',
69
+ sourceFieldId: 'a.user_name',
70
+ targetSlotId: 'b.title',
71
+ transformIds: ['string:trim', 'string:upper'],
72
+ },
73
+ {
74
+ // Whole object → object (not leaf-by-leaf).
75
+ id: 'e-location',
76
+ sourceFieldId: 'a.profile',
77
+ targetSlotId: 'b.location',
78
+ },
79
+ {
80
+ id: 'e-tags',
81
+ sourceFieldId: 'a.tags',
82
+ targetSlotId: 'b.labels',
83
+ itemEdges: [
84
+ {
85
+ id: 'e-tag-title',
86
+ sourceFieldId: 'a.tags.item.name',
87
+ targetSlotId: 'b.labels.item.title',
88
+ },
89
+ {
90
+ id: 'e-tag-order',
91
+ sourceFieldId: 'a.tags.item.rank',
92
+ targetSlotId: 'b.labels.item.order',
93
+ },
94
+ ],
95
+ },
96
+ {
97
+ id: 'e-first-tag',
98
+ sourceFieldId: 'a.tags',
99
+ targetSlotId: 'b.firstTag',
100
+ itemSourcePath: 'name',
101
+ transformIds: ['array:first'],
102
+ },
103
+ {
104
+ id: 'e-tag-line',
105
+ sourceFieldId: 'a.tags',
106
+ targetSlotId: 'b.tagLine',
107
+ itemSourcePath: 'name',
108
+ transformIds: ['array:join'],
109
+ transformOptionSteps: [{ separator: ' · ' }],
110
+ },
111
+ ],
112
+ },
113
+ {
114
+ id: 't-user-contact',
115
+ title: 'T_USER → T_CONTACT',
116
+ description: 'Column map plus NAME object → fullName via string:template',
117
+ sourceLabel: 'T_USER',
118
+ targetLabel: 'T_CONTACT',
119
+ sourceIdPrefix: 't_user',
120
+ targetIdPrefix: 't_contact',
121
+ source: {
122
+ USER_ID: 1001,
123
+ NAME: {
124
+ FIRST_NM: 'Ada',
125
+ LAST_NM: 'Lovelace',
126
+ },
127
+ EMAIL_ADDR: 'ada@example.com',
128
+ PHONE_NO: '555-0100',
129
+ },
130
+ targetShape: {
131
+ id: 0,
132
+ fullName: '',
133
+ email: '',
134
+ phone: '',
135
+ },
136
+ edges: [
137
+ {
138
+ id: 'map-id',
139
+ sourceFieldId: 't_user.USER_ID',
140
+ targetSlotId: 't_contact.id',
141
+ },
142
+ {
143
+ id: 'map-full-name',
144
+ sourceFieldId: 't_user.NAME',
145
+ targetSlotId: 't_contact.fullName',
146
+ transformIds: ['string:template'],
147
+ transformOptionSteps: [{ template: '{FIRST_NM} {LAST_NM}' }],
148
+ },
149
+ {
150
+ id: 'map-email',
151
+ sourceFieldId: 't_user.EMAIL_ADDR',
152
+ targetSlotId: 't_contact.email',
153
+ transformIds: ['string:lower'],
154
+ },
155
+ {
156
+ id: 'map-phone',
157
+ sourceFieldId: 't_user.PHONE_NO',
158
+ targetSlotId: 't_contact.phone',
159
+ },
160
+ ],
161
+ },
162
+ {
163
+ id: 't-event-time',
164
+ title: 'T_EVENT → T_SLOT',
165
+ description: 'Date reformat, date+time combine, and datetime split (fan-out)',
166
+ sourceLabel: 'T_EVENT',
167
+ targetLabel: 'T_SLOT',
168
+ sourceIdPrefix: 't_event',
169
+ targetIdPrefix: 't_slot',
170
+ source: {
171
+ EVENT_ID: 'EV-7',
172
+ YMD_RAW: '20260720',
173
+ WHEN: {
174
+ date: '2026-07-20',
175
+ time: '14:30:00',
176
+ },
177
+ OCCURS_AT: '2026-07-21T09:15:00',
178
+ },
179
+ targetShape: {
180
+ eventId: '',
181
+ displayDate: '',
182
+ startsAt: '',
183
+ occurDate: '',
184
+ occurTime: '',
185
+ },
186
+ edges: [
187
+ {
188
+ id: 'ev-id',
189
+ sourceFieldId: 't_event.EVENT_ID',
190
+ targetSlotId: 't_slot.eventId',
191
+ },
192
+ {
193
+ id: 'ev-display-date',
194
+ sourceFieldId: 't_event.YMD_RAW',
195
+ targetSlotId: 't_slot.displayDate',
196
+ transformIds: ['date:reformat'],
197
+ transformOptionSteps: [{ inputFormat: 'YYYYMMDD', outputFormat: 'YYYY.MM.DD' }],
198
+ },
199
+ {
200
+ id: 'ev-starts',
201
+ sourceFieldId: 't_event.WHEN',
202
+ targetSlotId: 't_slot.startsAt',
203
+ transformIds: ['datetime:combine'],
204
+ },
205
+ {
206
+ id: 'ev-occur-date',
207
+ sourceFieldId: 't_event.OCCURS_AT',
208
+ targetSlotId: 't_slot.occurDate',
209
+ transformIds: ['datetime:date'],
210
+ },
211
+ {
212
+ id: 'ev-occur-time',
213
+ sourceFieldId: 't_event.OCCURS_AT',
214
+ targetSlotId: 't_slot.occurTime',
215
+ transformIds: ['datetime:time'],
216
+ },
217
+ ],
218
+ },
219
+ {
220
+ id: 't-emp-dept',
221
+ title: 'T_EMP → T_EMP_ROW',
222
+ description: 'Join-style flatten: nested DEPT object into flat row columns',
223
+ sourceLabel: 'T_EMP',
224
+ targetLabel: 'T_EMP_ROW',
225
+ sourceIdPrefix: 't_emp',
226
+ targetIdPrefix: 't_emp_row',
227
+ source: {
228
+ EMP_NO: 'E-42',
229
+ EMP_NM: 'Grace Hopper',
230
+ DEPT: {
231
+ DEPT_CD: 'RND',
232
+ DEPT_NM: 'Research',
233
+ },
234
+ },
235
+ targetShape: {
236
+ empNo: '',
237
+ empName: '',
238
+ deptCode: '',
239
+ deptName: '',
240
+ },
241
+ edges: [
242
+ {
243
+ id: 'emp-no',
244
+ sourceFieldId: 't_emp.EMP_NO',
245
+ targetSlotId: 't_emp_row.empNo',
246
+ },
247
+ {
248
+ id: 'emp-nm',
249
+ sourceFieldId: 't_emp.EMP_NM',
250
+ targetSlotId: 't_emp_row.empName',
251
+ },
252
+ {
253
+ id: 'emp-dept-cd',
254
+ sourceFieldId: 't_emp.DEPT.DEPT_CD',
255
+ targetSlotId: 't_emp_row.deptCode',
256
+ },
257
+ {
258
+ id: 'emp-dept-nm',
259
+ sourceFieldId: 't_emp.DEPT.DEPT_NM',
260
+ targetSlotId: 't_emp_row.deptName',
261
+ transformIds: ['string:upper'],
262
+ },
263
+ ],
264
+ },
265
+ {
266
+ id: 't-product-catalog',
267
+ title: 'T_PRODUCT → T_CATALOG_ITEM',
268
+ description: 'Parent row plus TAGS[] itemEdges into catalog labels',
269
+ sourceLabel: 'T_PRODUCT',
270
+ targetLabel: 'T_CATALOG_ITEM',
271
+ sourceIdPrefix: 't_product',
272
+ targetIdPrefix: 't_catalog',
273
+ source: {
274
+ PROD_ID: 'P-9',
275
+ PROD_NM: ' Analytical Engine ',
276
+ TAGS: [
277
+ { TAG_CD: 'hist', TAG_NM: 'history' },
278
+ { TAG_CD: 'comp', TAG_NM: 'computing' },
279
+ ],
280
+ },
281
+ targetShape: {
282
+ productId: '',
283
+ name: '',
284
+ labels: [{ code: '', title: '' }],
285
+ tagLine: '',
286
+ },
287
+ edges: [
288
+ {
289
+ id: 'prod-id',
290
+ sourceFieldId: 't_product.PROD_ID',
291
+ targetSlotId: 't_catalog.productId',
292
+ },
293
+ {
294
+ id: 'prod-nm',
295
+ sourceFieldId: 't_product.PROD_NM',
296
+ targetSlotId: 't_catalog.name',
297
+ transformIds: ['string:trim'],
298
+ },
299
+ {
300
+ id: 'prod-tags',
301
+ sourceFieldId: 't_product.TAGS',
302
+ targetSlotId: 't_catalog.labels',
303
+ itemEdges: [
304
+ {
305
+ id: 'tag-code',
306
+ sourceFieldId: 't_product.TAGS.item.TAG_CD',
307
+ targetSlotId: 't_catalog.labels.item.code',
308
+ transformIds: ['string:upper'],
309
+ },
310
+ {
311
+ id: 'tag-title',
312
+ sourceFieldId: 't_product.TAGS.item.TAG_NM',
313
+ targetSlotId: 't_catalog.labels.item.title',
314
+ },
315
+ ],
316
+ },
317
+ {
318
+ id: 'prod-tag-line',
319
+ sourceFieldId: 't_product.TAGS',
320
+ targetSlotId: 't_catalog.tagLine',
321
+ itemSourcePath: 'TAG_NM',
322
+ transformIds: ['array:join'],
323
+ transformOptionSteps: [{ separator: ' / ' }],
324
+ },
325
+ ],
326
+ },
327
+ ];
328
+
329
+ const SAMPLE_BY_ID = new Map(FIELD_REMAP_SAMPLES.map((sample) => [sample.id, sample]));
330
+
331
+ export function isFieldRemapSampleId(value: string): value is FieldRemapSampleId {
332
+ return SAMPLE_BY_ID.has(value as FieldRemapSampleId);
333
+ }
334
+
335
+ export function resolveFieldRemapSampleId(value: string | undefined): FieldRemapSampleId {
336
+ if (!value) {
337
+ return 'nested-ab';
338
+ }
339
+ const aliased = FIELD_REMAP_LEGACY_SAMPLE_ALIASES[value];
340
+ if (aliased) {
341
+ return aliased;
342
+ }
343
+ if (isFieldRemapSampleId(value)) {
344
+ return value;
345
+ }
346
+ return 'nested-ab';
347
+ }
348
+
349
+ export function getFieldRemapSample(sampleId: string | undefined): FieldRemapSampleDefinition {
350
+ const id = resolveFieldRemapSampleId(sampleId);
351
+ return SAMPLE_BY_ID.get(id) ?? FIELD_REMAP_SAMPLES[0]!;
352
+ }