@workbench-kit/shell-react 0.0.2-prototype.0.2.26 → 0.0.2-prototype.0.2.28

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 (35) hide show
  1. package/README.md +18 -2
  2. package/package.json +10 -10
  3. package/src/chat/command-policy.ts +3 -17
  4. package/src/commands/use-command-descriptors.ts +2 -30
  5. package/src/commands/use-context-key-revision.ts +12 -12
  6. package/src/commands/use-extension-registry-command-descriptors.ts +46 -0
  7. package/src/devtools/use-workbench-devtools-snapshot.ts +1 -1
  8. package/src/editor/pane-visibility.ts +0 -23
  9. package/src/editor/view-providers.tsx +0 -7
  10. package/src/explorer/reveal.ts +1 -12
  11. package/src/extensions/builtin/commands/src/command-inspector-editor-host.ts +1 -19
  12. package/src/extensions/builtin/commands/src/index.ts +2 -2
  13. package/src/field-remap/convert-note-editor.tsx +2 -4
  14. package/src/field-remap/convert-palette.tsx +17 -20
  15. package/src/field-remap/flow-adapter.ts +21 -57
  16. package/src/field-remap/flow-ops.ts +0 -24
  17. package/src/field-remap/index.ts +0 -1
  18. package/src/field-remap/io-class-browse.tsx +29 -6
  19. package/src/field-remap/jsonata-transform.ts +9 -39
  20. package/src/field-remap/shape-io-editor.tsx +1 -1
  21. package/src/field-remap/transform-options-editor.tsx +1 -2
  22. package/src/field-remap/view.css +4 -29
  23. package/src/index.ts +1 -10
  24. package/src/jdw/widget-form-view.tsx +3 -20
  25. package/src/jdw/widget-preview-view.tsx +2 -20
  26. package/src/management/use-command-management.ts +22 -7
  27. package/src/management/use-keybinding-management.ts +1 -10
  28. package/src/shell/provider.tsx +19 -9
  29. package/src/shell/shell.tsx +11 -4
  30. package/src/workbench/command-host.tsx +46 -7
  31. package/src/workbench/command-palette.ts +12 -45
  32. package/src/workbench/workspace-view-state.ts +18 -1
  33. package/src/field-remap/graph.tsx +0 -559
  34. package/src/field-remap/table-mapping-adapter.ts +0 -59
  35. package/src/field-remap/tree.tsx +0 -404
@@ -1,559 +0,0 @@
1
- import { useMemo, useState, type JSX } from 'react';
2
- import { Badge, Button } from '@workbench-kit/react/primitives';
3
- import {
4
- MAX_TRANSFORM_CHAIN,
5
- flattenSourceFields,
6
- flattenTargetSlots,
7
- type MappingEdge,
8
- type SourceField,
9
- type TargetSlot,
10
- type ValueTransformDefinition,
11
- type ValueTransformRegistry,
12
- } from '@workbench-kit/field-remap';
13
-
14
- interface KeyRow {
15
- readonly id: string;
16
- readonly label: string;
17
- readonly path?: string;
18
- readonly dataType?: string;
19
- readonly depth: number;
20
- }
21
-
22
- function collectKeys(
23
- nodes: readonly {
24
- id: string;
25
- label: string;
26
- path?: string;
27
- dataType?: string;
28
- children?: readonly unknown[];
29
- }[],
30
- depth = 0,
31
- ): KeyRow[] {
32
- const rows: KeyRow[] = [];
33
- for (const node of nodes) {
34
- rows.push({
35
- id: node.id,
36
- label: node.label,
37
- path: node.path,
38
- dataType: node.dataType,
39
- depth,
40
- });
41
- if (node.children && Array.isArray(node.children) && node.children.length > 0) {
42
- rows.push(
43
- ...collectKeys(
44
- node.children as readonly {
45
- id: string;
46
- label: string;
47
- path?: string;
48
- dataType?: string;
49
- children?: readonly unknown[];
50
- }[],
51
- depth + 1,
52
- ),
53
- );
54
- }
55
- }
56
- return rows;
57
- }
58
-
59
- function labelFor(
60
- id: string,
61
- sources: readonly SourceField[],
62
- targets: readonly TargetSlot[],
63
- ): string {
64
- const source = flattenSourceFields(sources).find((field) => field.id === id);
65
- if (source) {
66
- return source.path ?? source.label;
67
- }
68
- const target = flattenTargetSlots(targets).find((slot) => slot.id === id);
69
- return target?.path ?? target?.label ?? id;
70
- }
71
-
72
- function findSource(sources: readonly SourceField[], id: string): SourceField | undefined {
73
- return flattenSourceFields(sources).find((field) => field.id === id);
74
- }
75
-
76
- function findTarget(targets: readonly TargetSlot[], id: string): TargetSlot | undefined {
77
- return flattenTargetSlots(targets).find((slot) => slot.id === id);
78
- }
79
-
80
- export interface FieldRemapGraphMapperProps {
81
- readonly sources: readonly SourceField[];
82
- readonly targets: readonly TargetSlot[];
83
- readonly edges: readonly MappingEdge[];
84
- readonly transforms: ValueTransformRegistry;
85
- readonly onEdgesChange: (edges: readonly MappingEdge[]) => void;
86
- }
87
-
88
- /**
89
- * Left/right key columns + middle transform nodes per binding (format / reduce chain).
90
- * Edges stay `MappingEdge` (`transformIds` ≤ {@link MAX_TRANSFORM_CHAIN}).
91
- */
92
- export function FieldRemapGraphMapper({
93
- sources,
94
- targets,
95
- edges,
96
- transforms,
97
- onEdgesChange,
98
- }: FieldRemapGraphMapperProps): JSX.Element {
99
- const sourceKeys = useMemo(() => collectKeys(sources), [sources]);
100
- const targetKeys = useMemo(() => collectKeys(targets), [targets]);
101
- const catalog = useMemo(
102
- () =>
103
- transforms
104
- .list()
105
- .filter((item) => item.id !== 'identity')
106
- .sort((a, b) => a.label.localeCompare(b.label)),
107
- [transforms],
108
- );
109
-
110
- const [pendingSourceId, setPendingSourceId] = useState<string | null>(null);
111
- const [listContextEdgeId, setListContextEdgeId] = useState<string | null>(null);
112
- const [pendingItemSourceId, setPendingItemSourceId] = useState<string | null>(null);
113
- const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null);
114
-
115
- const mappedSourceIds = useMemo(() => new Set(edges.map((edge) => edge.sourceFieldId)), [edges]);
116
- const mappedTargetIds = useMemo(() => new Set(edges.map((edge) => edge.targetSlotId)), [edges]);
117
-
118
- const listContextEdge = edges.find((edge) => edge.id === listContextEdgeId) ?? null;
119
- const listSource = listContextEdge
120
- ? findSource(sources, listContextEdge.sourceFieldId)
121
- : undefined;
122
- const listTarget = listContextEdge
123
- ? findTarget(targets, listContextEdge.targetSlotId)
124
- : undefined;
125
-
126
- const patchEdge = (edgeId: string, patch: Partial<MappingEdge>) => {
127
- onEdgesChange(edges.map((edge) => (edge.id === edgeId ? { ...edge, ...patch } : edge)));
128
- };
129
-
130
- const connect = (sourceId: string, targetId: string) => {
131
- const source = findSource(sources, sourceId);
132
- const target = findTarget(targets, targetId);
133
- if (!source || !target) {
134
- return;
135
- }
136
- const isListContext =
137
- source.dataType === 'array' &&
138
- target.dataType === 'array' &&
139
- Boolean(source.children?.length && target.children?.length);
140
-
141
- const nextEdge: MappingEdge = {
142
- id: `e-${sourceId}-${targetId}-${Date.now()}`,
143
- sourceFieldId: sourceId,
144
- targetSlotId: targetId,
145
- ...(isListContext ? { itemEdges: [] as MappingEdge[] } : {}),
146
- };
147
- const withoutTarget = edges.filter((edge) => edge.targetSlotId !== targetId);
148
- onEdgesChange([...withoutTarget, nextEdge]);
149
- setPendingSourceId(null);
150
- setSelectedEdgeId(nextEdge.id);
151
- if (isListContext) {
152
- setListContextEdgeId(nextEdge.id);
153
- setPendingItemSourceId(null);
154
- }
155
- };
156
-
157
- const handleSourceSelect = (id: string) => {
158
- if (listContextEdgeId) {
159
- setPendingItemSourceId(id);
160
- return;
161
- }
162
- setPendingSourceId(id);
163
- };
164
-
165
- const handleTargetSelect = (id: string) => {
166
- if (listContextEdge && pendingItemSourceId) {
167
- const itemEdge: MappingEdge = {
168
- id: `ie-${pendingItemSourceId}-${id}-${Date.now()}`,
169
- sourceFieldId: pendingItemSourceId,
170
- targetSlotId: id,
171
- };
172
- onEdgesChange(
173
- edges.map((edge) =>
174
- edge.id === listContextEdge.id
175
- ? {
176
- ...edge,
177
- itemEdges: [
178
- ...(edge.itemEdges ?? []).filter((child) => child.targetSlotId !== id),
179
- itemEdge,
180
- ],
181
- }
182
- : edge,
183
- ),
184
- );
185
- setPendingItemSourceId(null);
186
- return;
187
- }
188
- if (!pendingSourceId) {
189
- return;
190
- }
191
- connect(pendingSourceId, id);
192
- };
193
-
194
- const addTransformNode = (edgeId: string, transformId: string) => {
195
- const edge = edges.find((item) => item.id === edgeId);
196
- if (!edge) {
197
- return;
198
- }
199
- const current = edge.transformIds ?? [];
200
- if (current.length >= MAX_TRANSFORM_CHAIN) {
201
- return;
202
- }
203
- patchEdge(edgeId, { transformIds: [...current, transformId] });
204
- };
205
-
206
- const setTransformAt = (edgeId: string, index: number, transformId: string) => {
207
- const edge = edges.find((item) => item.id === edgeId);
208
- if (!edge) {
209
- return;
210
- }
211
- const next = [...(edge.transformIds ?? [])];
212
- next[index] = transformId;
213
- patchEdge(edgeId, { transformIds: next });
214
- };
215
-
216
- const removeTransformAt = (edgeId: string, index: number) => {
217
- const edge = edges.find((item) => item.id === edgeId);
218
- if (!edge) {
219
- return;
220
- }
221
- const next = [...(edge.transformIds ?? [])];
222
- next.splice(index, 1);
223
- const optionSteps = edge.transformOptionSteps ? [...edge.transformOptionSteps] : undefined;
224
- if (optionSteps) {
225
- optionSteps.splice(index, 1);
226
- }
227
- patchEdge(edgeId, {
228
- transformIds: next.length > 0 ? next : undefined,
229
- transformOptionSteps: optionSteps && optionSteps.length > 0 ? optionSteps : undefined,
230
- });
231
- };
232
-
233
- const setNodeOption = (edgeId: string, index: number, key: string, value: string) => {
234
- const edge = edges.find((item) => item.id === edgeId);
235
- if (!edge) {
236
- return;
237
- }
238
- const chain = edge.transformIds ?? [];
239
- const steps = Array.from({ length: chain.length }, (_, i) => ({
240
- ...(edge.transformOptionSteps?.[i] ?? {}),
241
- }));
242
- steps[index] = { ...steps[index], [key]: value };
243
- patchEdge(edgeId, { transformOptionSteps: steps });
244
- };
245
-
246
- const removeEdge = (edgeId: string) => {
247
- onEdgesChange(edges.filter((edge) => edge.id !== edgeId));
248
- if (listContextEdgeId === edgeId) {
249
- setListContextEdgeId(null);
250
- setPendingItemSourceId(null);
251
- }
252
- if (selectedEdgeId === edgeId) {
253
- setSelectedEdgeId(null);
254
- }
255
- };
256
-
257
- const visibleSourceKeys =
258
- listContextEdge && listSource?.children ? collectKeys(listSource.children) : sourceKeys;
259
- const visibleTargetKeys =
260
- listContextEdge && listTarget?.children ? collectKeys(listTarget.children) : targetKeys;
261
-
262
- return (
263
- <div className="workbench-field-remap-mapper" data-testid="field-remap-mapper">
264
- <p className="workbench-field-remap-mapper__hint" data-testid="field-remap-hint">
265
- {listContextEdge
266
- ? pendingItemSourceId
267
- ? 'List context: select a target item field.'
268
- : 'List context: select a source item field, then a target item field.'
269
- : pendingSourceId
270
- ? 'Select a target key to create a binding. Add middle nodes for formatting.'
271
- : 'Select a source key, then a target key. Insert transform nodes in the middle when values need formatting.'}
272
- </p>
273
-
274
- <div className="workbench-field-remap-graph" data-testid="field-remap-graph">
275
- <KeyColumn
276
- title={listContextEdge ? `A item · ${listSource?.label ?? 'source'}` : 'A keys'}
277
- keys={visibleSourceKeys}
278
- selectedId={listContextEdge ? pendingItemSourceId : pendingSourceId}
279
- mappedIds={
280
- listContextEdge
281
- ? new Set((listContextEdge.itemEdges ?? []).map((edge) => edge.sourceFieldId))
282
- : mappedSourceIds
283
- }
284
- onSelect={handleSourceSelect}
285
- side="source"
286
- />
287
-
288
- <div className="workbench-field-remap-graph__center" data-testid="field-remap-lanes">
289
- <h4 className="workbench-field-remap-tree__title">Bindings / nodes</h4>
290
- {edges.length === 0 ? (
291
- <p className="workbench-field-remap-graph__empty">No bindings yet.</p>
292
- ) : (
293
- <ul className="workbench-field-remap-graph__lanes">
294
- {edges.map((edge) => (
295
- <BindingLane
296
- key={edge.id}
297
- edge={edge}
298
- selected={selectedEdgeId === edge.id}
299
- sourceLabel={labelFor(edge.sourceFieldId, sources, targets)}
300
- targetLabel={labelFor(edge.targetSlotId, sources, targets)}
301
- catalog={catalog}
302
- onSelect={() => setSelectedEdgeId(edge.id)}
303
- onAddNode={(transformId) => addTransformNode(edge.id, transformId)}
304
- onChangeNode={(index, transformId) => setTransformAt(edge.id, index, transformId)}
305
- onRemoveNode={(index) => removeTransformAt(edge.id, index)}
306
- onOptionChange={(index, key, value) => setNodeOption(edge.id, index, key, value)}
307
- onEditItems={
308
- edge.itemEdges
309
- ? () => {
310
- setListContextEdgeId(edge.id);
311
- setPendingItemSourceId(null);
312
- setPendingSourceId(null);
313
- }
314
- : undefined
315
- }
316
- onRemove={() => removeEdge(edge.id)}
317
- />
318
- ))}
319
- </ul>
320
- )}
321
- </div>
322
-
323
- <KeyColumn
324
- title={listContextEdge ? `B item · ${listTarget?.label ?? 'target'}` : 'B keys'}
325
- keys={visibleTargetKeys}
326
- selectedId={null}
327
- mappedIds={
328
- listContextEdge
329
- ? new Set((listContextEdge.itemEdges ?? []).map((edge) => edge.targetSlotId))
330
- : mappedTargetIds
331
- }
332
- onSelect={handleTargetSelect}
333
- side="target"
334
- />
335
- </div>
336
-
337
- {listContextEdge ? (
338
- <div
339
- className="workbench-field-remap-mapper__list-context"
340
- data-testid="field-remap-list-context"
341
- >
342
- <div className="workbench-field-remap-mapper__list-context-bar">
343
- <strong>List context</strong>
344
- <span>
345
- {listContextEdge.sourceFieldId} → {listContextEdge.targetSlotId}
346
- </span>
347
- <Button
348
- compact
349
- type="button"
350
- onClick={() => {
351
- setListContextEdgeId(null);
352
- setPendingItemSourceId(null);
353
- }}
354
- >
355
- Done
356
- </Button>
357
- </div>
358
- <ul className="workbench-field-remap-mapper__edges" data-testid="field-remap-edges">
359
- {(listContextEdge.itemEdges ?? []).map((edge) => (
360
- <li key={edge.id}>
361
- <code>
362
- {edge.sourceFieldId} → {edge.targetSlotId}
363
- </code>
364
- </li>
365
- ))}
366
- </ul>
367
- </div>
368
- ) : (
369
- <ul
370
- className="workbench-field-remap-mapper__edges visually-hidden"
371
- data-testid="field-remap-edges"
372
- >
373
- {edges.map((edge) => (
374
- <li key={edge.id}>
375
- {edge.sourceFieldId} → {edge.targetSlotId}
376
- {edge.transformIds?.length ? ` · ${edge.transformIds.join(' → ')}` : ''}
377
- </li>
378
- ))}
379
- </ul>
380
- )}
381
- </div>
382
- );
383
- }
384
-
385
- function KeyColumn({
386
- title,
387
- keys,
388
- selectedId,
389
- mappedIds,
390
- onSelect,
391
- side,
392
- }: {
393
- readonly title: string;
394
- readonly keys: readonly KeyRow[];
395
- readonly selectedId: string | null;
396
- readonly mappedIds: ReadonlySet<string>;
397
- readonly onSelect: (id: string) => void;
398
- readonly side: 'source' | 'target';
399
- }): JSX.Element {
400
- return (
401
- <div className="workbench-field-remap-tree" data-side={side}>
402
- <h4 className="workbench-field-remap-tree__title">{title}</h4>
403
- <ul className="workbench-field-remap-graph__keys" role="list">
404
- {keys.map((key) => {
405
- const badge = key.dataType === 'object' || key.dataType === 'array' ? key.dataType : null;
406
- return (
407
- <li key={key.id}>
408
- <button
409
- type="button"
410
- className={[
411
- 'workbench-field-remap-graph__key',
412
- selectedId === key.id ? 'is-selected' : '',
413
- mappedIds.has(key.id) ? 'is-mapped' : '',
414
- ]
415
- .filter(Boolean)
416
- .join(' ')}
417
- style={{ paddingLeft: `${0.5 + key.depth * 0.75}rem` }}
418
- data-testid={`field-remap-node-${key.id}`}
419
- onClick={() => onSelect(key.id)}
420
- >
421
- <span className="workbench-field-remap-tree__label">{key.label}</span>
422
- {badge ? <Badge variant="muted">{badge}</Badge> : null}
423
- </button>
424
- </li>
425
- );
426
- })}
427
- </ul>
428
- </div>
429
- );
430
- }
431
-
432
- function BindingLane({
433
- edge,
434
- selected,
435
- sourceLabel,
436
- targetLabel,
437
- catalog,
438
- onSelect,
439
- onAddNode,
440
- onChangeNode,
441
- onRemoveNode,
442
- onOptionChange,
443
- onEditItems,
444
- onRemove,
445
- }: {
446
- readonly edge: MappingEdge;
447
- readonly selected: boolean;
448
- readonly sourceLabel: string;
449
- readonly targetLabel: string;
450
- readonly catalog: readonly ValueTransformDefinition[];
451
- readonly onSelect: () => void;
452
- readonly onAddNode: (transformId: string) => void;
453
- readonly onChangeNode: (index: number, transformId: string) => void;
454
- readonly onRemoveNode: (index: number) => void;
455
- readonly onOptionChange: (index: number, key: string, value: string) => void;
456
- readonly onEditItems?: (() => void) | undefined;
457
- readonly onRemove: () => void;
458
- }): JSX.Element {
459
- const chain = edge.transformIds ?? [];
460
- const defaultAddId = catalog[0]?.id ?? 'string:trim';
461
-
462
- return (
463
- <li
464
- className={['workbench-field-remap-graph__lane', selected ? 'is-selected' : '']
465
- .filter(Boolean)
466
- .join(' ')}
467
- data-testid={`field-remap-lane-${edge.id}`}
468
- >
469
- <button type="button" className="workbench-field-remap-graph__endpoint" onClick={onSelect}>
470
- {sourceLabel}
471
- </button>
472
-
473
- <span className="workbench-field-remap-graph__arrow" aria-hidden="true">
474
-
475
- </span>
476
-
477
- <div className="workbench-field-remap-graph__nodes">
478
- {chain.length === 0 ? (
479
- <span className="workbench-field-remap-graph__passthrough" title="Pass-through">
480
- direct
481
- </span>
482
- ) : (
483
- chain.map((transformId, index) => {
484
- const definition = catalog.find((item) => item.id === transformId);
485
- const optionFields = definition?.optionFields ?? [];
486
- return (
487
- <div
488
- key={`${edge.id}-${index}-${transformId}`}
489
- className="workbench-field-remap-graph__node"
490
- data-testid={`field-remap-node-transform-${edge.id}-${index}`}
491
- >
492
- <select
493
- aria-label={`Transform step ${index + 1}`}
494
- value={transformId}
495
- onChange={(event) => onChangeNode(index, event.target.value)}
496
- >
497
- {!catalog.some((item) => item.id === transformId) ? (
498
- <option value={transformId}>{transformId}</option>
499
- ) : null}
500
- {catalog.map((item) => (
501
- <option key={item.id} value={item.id}>
502
- {item.label}
503
- </option>
504
- ))}
505
- </select>
506
- {optionFields.map((field) => (
507
- <input
508
- key={field.key}
509
- type="text"
510
- aria-label={field.label}
511
- placeholder={field.label}
512
- value={String(edge.transformOptionSteps?.[index]?.[field.key] ?? '')}
513
- onChange={(event) => onOptionChange(index, field.key, event.target.value)}
514
- />
515
- ))}
516
- <Button compact type="button" onClick={() => onRemoveNode(index)}>
517
- ×
518
- </Button>
519
- </div>
520
- );
521
- })
522
- )}
523
-
524
- {chain.length < MAX_TRANSFORM_CHAIN ? (
525
- <Button
526
- compact
527
- type="button"
528
- data-testid={`field-remap-add-node-${edge.id}`}
529
- onClick={() => onAddNode(defaultAddId)}
530
- >
531
- + node
532
- </Button>
533
- ) : null}
534
-
535
- {edge.itemSourcePath ? <Badge variant="muted">project {edge.itemSourcePath}</Badge> : null}
536
- {edge.itemEdges ? <Badge variant="muted">{edge.itemEdges.length} item fields</Badge> : null}
537
- </div>
538
-
539
- <span className="workbench-field-remap-graph__arrow" aria-hidden="true">
540
-
541
- </span>
542
-
543
- <button type="button" className="workbench-field-remap-graph__endpoint" onClick={onSelect}>
544
- {targetLabel}
545
- </button>
546
-
547
- <span className="workbench-field-remap-mapper__edge-actions">
548
- {onEditItems ? (
549
- <Button compact type="button" onClick={onEditItems}>
550
- Edit items
551
- </Button>
552
- ) : null}
553
- <Button compact type="button" onClick={onRemove}>
554
- Remove
555
- </Button>
556
- </span>
557
- </li>
558
- );
559
- }
@@ -1,59 +0,0 @@
1
- import type { MappingEdge } from '@workbench-kit/field-remap';
2
- import type { FieldItemInput, Mapping } from 'react-table-mapping';
3
-
4
- /** One column: field name (static string cell). */
5
- export const FIELD_REMAP_NAME_COLUMNS = [{ title: 'Field', key: 'name' }] as const;
6
-
7
- export interface FieldRemapLeaf {
8
- /** Kit field/slot id (may contain dots, e.g. `a.user_name`). */
9
- readonly id: string;
10
- readonly label: string;
11
- }
12
-
13
- /**
14
- * react-table-mapping builds connectors with `querySelector('#connector-…'+id)`.
15
- * Dots in ids break CSS selectors (`#a.user_name` → id=a + class=user_name).
16
- * Encode kit ids for the OSS UI and decode when adapting mappings.
17
- */
18
- export function toTableMappingFieldId(kitFieldId: string): string {
19
- return kitFieldId.split('.').join('__');
20
- }
21
-
22
- export function fromTableMappingFieldId(tableFieldId: string): string {
23
- return tableFieldId.split('__').join('.');
24
- }
25
-
26
- /** Build react-table-mapping source/target rows from leaf field descriptors. */
27
- export function toTableMappingFields(leaves: readonly FieldRemapLeaf[]): FieldItemInput[] {
28
- return leaves.map((leaf) => {
29
- const id = toTableMappingFieldId(leaf.id);
30
- return {
31
- id,
32
- key: id,
33
- name: {
34
- type: 'string' as const,
35
- columnKey: 'name',
36
- value: leaf.label,
37
- },
38
- };
39
- });
40
- }
41
-
42
- /** OSS Mapping[] → kit MappingEdge[] (identity / pass-through). */
43
- export function tableMappingsToEdges(mappings: readonly Mapping[]): MappingEdge[] {
44
- return mappings.map((mapping) => ({
45
- id: mapping.id,
46
- sourceFieldId: fromTableMappingFieldId(mapping.source),
47
- targetSlotId: fromTableMappingFieldId(mapping.target),
48
- transformIds: ['identity'] as const,
49
- }));
50
- }
51
-
52
- /** kit MappingEdge[] → OSS Mapping[] for controlled mappings. */
53
- export function edgesToTableMappings(edges: readonly MappingEdge[]): Mapping[] {
54
- return edges.map((edge) => ({
55
- id: edge.id,
56
- source: toTableMappingFieldId(edge.sourceFieldId),
57
- target: toTableMappingFieldId(edge.targetSlotId),
58
- }));
59
- }