@workbench-kit/shell-react 0.0.2-prototype.0.2.10
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.
- package/README.md +98 -0
- package/package.json +51 -0
- package/src/chat/ai-mock-proposals.ts +39 -0
- package/src/chat/command-input.ts +52 -0
- package/src/chat/command-policy.ts +54 -0
- package/src/chat/command-surface.tsx +155 -0
- package/src/chat/use-chat-command-proposals.ts +194 -0
- package/src/chat/view-data.ts +20 -0
- package/src/chat/view.tsx +220 -0
- package/src/commands/inspector-surface.tsx +46 -0
- package/src/commands/use-command-descriptors.ts +41 -0
- package/src/commands/use-context-key-revision.ts +18 -0
- package/src/commands/view-data.ts +17 -0
- package/src/commands/view.tsx +43 -0
- package/src/devtools/WorkbenchDevtoolsPanel.tsx +124 -0
- package/src/devtools/WorkbenchDevtoolsShell.tsx +17 -0
- package/src/devtools/index.ts +11 -0
- package/src/devtools/use-workbench-devtools-snapshot.ts +84 -0
- package/src/devtools/workbench-devtools-snapshot.ts +140 -0
- package/src/devtools/workbench-devtools.css +82 -0
- package/src/editor/area-dnd.ts +69 -0
- package/src/editor/area-model.ts +71 -0
- package/src/editor/area.css +454 -0
- package/src/editor/area.tsx +676 -0
- package/src/editor/host-surface.tsx +691 -0
- package/src/editor/markdown-document-view-data.ts +16 -0
- package/src/editor/pane-visibility.ts +130 -0
- package/src/editor/resource.ts +36 -0
- package/src/editor/state-storage.ts +182 -0
- package/src/editor/tab-context-menu.ts +118 -0
- package/src/editor/use-editor.ts +138 -0
- package/src/editor/view-providers.tsx +504 -0
- package/src/editor/workspace-file-availability.ts +15 -0
- package/src/editor/workspace-reconcile.tsx +31 -0
- package/src/explorer/context-menu.ts +114 -0
- package/src/explorer/create-command-workspace-explorer-port.ts +86 -0
- package/src/explorer/reveal.ts +112 -0
- package/src/explorer/search-view-data.ts +15 -0
- package/src/explorer/search-view.tsx +65 -0
- package/src/explorer/use-active-workspace-path.ts +15 -0
- package/src/explorer/view-data.ts +58 -0
- package/src/explorer/view.tsx +227 -0
- package/src/extensions/context-menu.ts +52 -0
- package/src/extensions/management-model.ts +313 -0
- package/src/extensions/use-extension-management.ts +241 -0
- package/src/extensions/view-data.ts +19 -0
- package/src/extensions/view.tsx +63 -0
- package/src/field-remap/chrome-labels.ts +74 -0
- package/src/field-remap/convert-note-editor.tsx +206 -0
- package/src/field-remap/convert-palette.tsx +126 -0
- package/src/field-remap/demo.tsx +105 -0
- package/src/field-remap/detail-panel.tsx +659 -0
- package/src/field-remap/editor-surface.tsx +36 -0
- package/src/field-remap/flow-adapter.ts +838 -0
- package/src/field-remap/flow-ops.ts +442 -0
- package/src/field-remap/flow.tsx +948 -0
- package/src/field-remap/graph.tsx +559 -0
- package/src/field-remap/index.ts +88 -0
- package/src/field-remap/io-class-browse.tsx +145 -0
- package/src/field-remap/jsonata-transform.ts +166 -0
- package/src/field-remap/panel.tsx +466 -0
- package/src/field-remap/samples.ts +507 -0
- package/src/field-remap/shape-io-editor.tsx +170 -0
- package/src/field-remap/table-mapping-adapter.ts +59 -0
- package/src/field-remap/transform-options-editor.tsx +312 -0
- package/src/field-remap/tree.tsx +404 -0
- package/src/field-remap/view-data.ts +16 -0
- package/src/field-remap/view.css +1367 -0
- package/src/field-remap/view.tsx +86 -0
- package/src/index.ts +294 -0
- package/src/jdw/document-view-data.ts +31 -0
- package/src/jdw/json-form-source-patch.ts +323 -0
- package/src/jdw/lab-view-data.ts +25 -0
- package/src/jdw/lab-view.css +21 -0
- package/src/jdw/lab-view.tsx +79 -0
- package/src/jdw/widget-form-view.tsx +68 -0
- package/src/jdw/widget-preview-view.tsx +54 -0
- package/src/management/keybinding-overrides-storage.ts +49 -0
- package/src/management/keybinding-settings.tsx +16 -0
- package/src/management/palette-commands.ts +38 -0
- package/src/management/preference-settings-storage.ts +50 -0
- package/src/management/settings-ids.ts +12 -0
- package/src/management/settings.tsx +96 -0
- package/src/management/use-command-management.ts +191 -0
- package/src/management/use-keybinding-management.ts +130 -0
- package/src/shell/model.tsx +149 -0
- package/src/shell/provider.tsx +606 -0
- package/src/shell/secondary-actions.tsx +59 -0
- package/src/shell/settings-constants.ts +9 -0
- package/src/shell/settings.tsx +599 -0
- package/src/shell/shell.tsx +680 -0
- package/src/shell/titlebar-layout-controls.tsx +4 -0
- package/src/shell/view-host.tsx +212 -0
- package/src/workbench/appearance-storage.ts +107 -0
- package/src/workbench/command-host.tsx +243 -0
- package/src/workbench/command-palette.ts +208 -0
- package/src/workbench/keybinding-bridge.ts +54 -0
- package/src/workbench/layout-storage.ts +134 -0
- package/src/workbench/profile-modal.tsx +118 -0
- package/src/workbench/shell-command-registration.ts +58 -0
- package/src/workbench/startup-gate.tsx +136 -0
- package/src/workbench/status-sections.ts +43 -0
- package/src/workbench/use-persisted-appearance.ts +33 -0
- package/src/workbench/user-commands.ts +46 -0
- package/src/workbench/workspace-view-state.ts +35 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_MAPPING_FAN_IN,
|
|
3
|
+
MAX_MAPPING_FAN_OUT,
|
|
4
|
+
MAX_TRANSFORM_CHAIN,
|
|
5
|
+
arePortsCompatible,
|
|
6
|
+
findSourceField,
|
|
7
|
+
findTargetSlot,
|
|
8
|
+
isTransformChainCompatible,
|
|
9
|
+
optionFieldsForStep,
|
|
10
|
+
patchOptionStep,
|
|
11
|
+
resizeOptionSteps,
|
|
12
|
+
sanitizeOptionRecord,
|
|
13
|
+
type CombineMappingOperator,
|
|
14
|
+
type FieldDataType,
|
|
15
|
+
type MappingEdge,
|
|
16
|
+
type MappingOperator,
|
|
17
|
+
type SourceField,
|
|
18
|
+
type SplitMappingOperator,
|
|
19
|
+
type TargetSlot,
|
|
20
|
+
type ValueTransformDefinition,
|
|
21
|
+
type ValueTransformRegistry,
|
|
22
|
+
} from '@workbench-kit/field-remap';
|
|
23
|
+
|
|
24
|
+
/** Canvas / bindings selection for the Flow detail panel. */
|
|
25
|
+
export type FieldRemapSelection =
|
|
26
|
+
| { readonly kind: 'edge'; readonly edgeId: string }
|
|
27
|
+
| { readonly kind: 'transformStep'; readonly edgeId: string; readonly stepIndex: number }
|
|
28
|
+
| { readonly kind: 'draft'; readonly localId: string }
|
|
29
|
+
| { readonly kind: 'operator'; readonly operatorId: string }
|
|
30
|
+
| null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Ephemeral place-then-wire transform. Not part of `FieldRemapDocument` until
|
|
34
|
+
* both ports are bound and {@link finalizeDraftTransform} succeeds.
|
|
35
|
+
*/
|
|
36
|
+
export type FieldRemapDraftTransform = {
|
|
37
|
+
readonly localId: string;
|
|
38
|
+
readonly transformId: string;
|
|
39
|
+
readonly sourceFieldId?: string;
|
|
40
|
+
readonly targetSlotId?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
let draftSequence = 0;
|
|
44
|
+
|
|
45
|
+
export function createDraftTransform(transformId: string): FieldRemapDraftTransform {
|
|
46
|
+
draftSequence += 1;
|
|
47
|
+
return {
|
|
48
|
+
localId: `d${draftSequence}-${Date.now()}`,
|
|
49
|
+
transformId,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function bindDraftSource(
|
|
54
|
+
draft: FieldRemapDraftTransform,
|
|
55
|
+
sourceFieldId: string,
|
|
56
|
+
): FieldRemapDraftTransform {
|
|
57
|
+
return { ...draft, sourceFieldId };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function bindDraftTarget(
|
|
61
|
+
draft: FieldRemapDraftTransform,
|
|
62
|
+
targetSlotId: string,
|
|
63
|
+
): FieldRemapDraftTransform {
|
|
64
|
+
return { ...draft, targetSlotId };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Materialize a fully wired draft into a MappingEdge (single-step chain).
|
|
69
|
+
* Returns null when ports are missing or the chain is type-incompatible.
|
|
70
|
+
*/
|
|
71
|
+
export function finalizeDraftTransform(
|
|
72
|
+
draft: FieldRemapDraftTransform,
|
|
73
|
+
context: {
|
|
74
|
+
readonly registry: ValueTransformRegistry;
|
|
75
|
+
readonly sources: readonly SourceField[];
|
|
76
|
+
readonly targets: readonly TargetSlot[];
|
|
77
|
+
readonly existing: readonly MappingEdge[];
|
|
78
|
+
},
|
|
79
|
+
): MappingEdge | null {
|
|
80
|
+
if (!draft.sourceFieldId || !draft.targetSlotId) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const sourceType = findSourceField(context.sources, draft.sourceFieldId)?.dataType;
|
|
84
|
+
const targetType = findTargetSlot(context.targets, draft.targetSlotId)?.dataType;
|
|
85
|
+
if (
|
|
86
|
+
!arePortsCompatible({
|
|
87
|
+
sourceType,
|
|
88
|
+
targetType,
|
|
89
|
+
transformIds: [draft.transformId],
|
|
90
|
+
registry: context.registry,
|
|
91
|
+
})
|
|
92
|
+
) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const existing = context.existing.find((edge) => edge.targetSlotId === draft.targetSlotId);
|
|
96
|
+
return {
|
|
97
|
+
id: existing?.id ?? `e-${draft.sourceFieldId}-${draft.targetSlotId}-${Date.now()}`,
|
|
98
|
+
sourceFieldId: draft.sourceFieldId,
|
|
99
|
+
targetSlotId: draft.targetSlotId,
|
|
100
|
+
transformIds: [draft.transformId],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function updateMappingEdge(
|
|
105
|
+
edges: readonly MappingEdge[],
|
|
106
|
+
edgeId: string,
|
|
107
|
+
updater: (edge: MappingEdge) => MappingEdge,
|
|
108
|
+
): readonly MappingEdge[] {
|
|
109
|
+
return edges.map((edge) => (edge.id === edgeId ? updater(edge) : edge));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function migrateStepOptions(
|
|
113
|
+
previous: Readonly<Record<string, unknown>> | undefined,
|
|
114
|
+
nextTransformId: string,
|
|
115
|
+
registry: ValueTransformRegistry,
|
|
116
|
+
): Readonly<Record<string, unknown>> | undefined {
|
|
117
|
+
if (!previous) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
const allowed = new Set(optionFieldsForStep(registry, nextTransformId).map((field) => field.key));
|
|
121
|
+
if (allowed.size === 0) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const next: Record<string, unknown> = {};
|
|
125
|
+
for (const [key, value] of Object.entries(previous)) {
|
|
126
|
+
if (allowed.has(key)) {
|
|
127
|
+
next[key] = value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return sanitizeOptionRecord(next);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Append a transform step to an edge. Returns `null` when the chain is full or
|
|
135
|
+
* the candidate breaks type compatibility.
|
|
136
|
+
*/
|
|
137
|
+
export function addTransformStepToEdge(
|
|
138
|
+
edge: MappingEdge,
|
|
139
|
+
transformId: string,
|
|
140
|
+
context: {
|
|
141
|
+
readonly registry: ValueTransformRegistry;
|
|
142
|
+
readonly sourceType?: FieldDataType;
|
|
143
|
+
readonly targetType?: FieldDataType;
|
|
144
|
+
},
|
|
145
|
+
): MappingEdge | null {
|
|
146
|
+
const current = edge.transformIds ?? [];
|
|
147
|
+
if (current.length >= MAX_TRANSFORM_CHAIN) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const nextIds = [...current, transformId];
|
|
151
|
+
if (
|
|
152
|
+
!isTransformChainCompatible(context.registry, nextIds, context.sourceType, context.targetType)
|
|
153
|
+
) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...edge,
|
|
158
|
+
transformIds: nextIds,
|
|
159
|
+
transformOptionSteps: resizeOptionSteps(edge.transformOptionSteps, nextIds.length),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Replace `transformIds[stepIndex]`. Clears / migrates that step's option bag.
|
|
165
|
+
* Returns `null` when the index is invalid or the pick is incompatible.
|
|
166
|
+
*/
|
|
167
|
+
export function setTransformStepIdOnEdge(
|
|
168
|
+
edge: MappingEdge,
|
|
169
|
+
stepIndex: number,
|
|
170
|
+
transformId: string,
|
|
171
|
+
context: {
|
|
172
|
+
readonly registry: ValueTransformRegistry;
|
|
173
|
+
readonly sourceType?: FieldDataType;
|
|
174
|
+
readonly targetType?: FieldDataType;
|
|
175
|
+
},
|
|
176
|
+
): MappingEdge | null {
|
|
177
|
+
const current = [...(edge.transformIds ?? [])];
|
|
178
|
+
if (stepIndex < 0 || stepIndex >= current.length) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const nextIds = [...current];
|
|
182
|
+
nextIds[stepIndex] = transformId;
|
|
183
|
+
if (
|
|
184
|
+
!isTransformChainCompatible(context.registry, nextIds, context.sourceType, context.targetType)
|
|
185
|
+
) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const resolvedSteps = resizeOptionSteps(edge.transformOptionSteps, nextIds.length) ?? [];
|
|
190
|
+
const migrated = migrateStepOptions(resolvedSteps[stepIndex], transformId, context.registry);
|
|
191
|
+
const nextSteps = resolvedSteps.map((step, index) => (index === stepIndex ? migrated : step));
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
...edge,
|
|
195
|
+
transformIds: nextIds,
|
|
196
|
+
transformOptionSteps: resizeOptionSteps(nextSteps, nextIds.length),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function removeTransformStepFromEdge(edge: MappingEdge, stepIndex: number): MappingEdge {
|
|
201
|
+
const current = [...(edge.transformIds ?? [])];
|
|
202
|
+
if (stepIndex < 0 || stepIndex >= current.length) {
|
|
203
|
+
return edge;
|
|
204
|
+
}
|
|
205
|
+
current.splice(stepIndex, 1);
|
|
206
|
+
const optionSteps = edge.transformOptionSteps ? [...edge.transformOptionSteps] : undefined;
|
|
207
|
+
if (optionSteps) {
|
|
208
|
+
optionSteps.splice(stepIndex, 1);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
...edge,
|
|
212
|
+
transformIds: current.length > 0 ? current : undefined,
|
|
213
|
+
transformOptionSteps: resizeOptionSteps(optionSteps, current.length),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function patchTransformStepOptionOnEdge(
|
|
218
|
+
edge: MappingEdge,
|
|
219
|
+
stepIndex: number,
|
|
220
|
+
key: string,
|
|
221
|
+
value: unknown,
|
|
222
|
+
): MappingEdge {
|
|
223
|
+
const length = edge.transformIds?.length ?? 0;
|
|
224
|
+
if (stepIndex < 0 || stepIndex >= length) {
|
|
225
|
+
return edge;
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
...edge,
|
|
229
|
+
transformOptionSteps: patchOptionStep(
|
|
230
|
+
edge.transformOptionSteps,
|
|
231
|
+
length,
|
|
232
|
+
stepIndex,
|
|
233
|
+
key,
|
|
234
|
+
value,
|
|
235
|
+
edge.transformOptions,
|
|
236
|
+
),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function replaceTransformStepOptionsOnEdge(
|
|
241
|
+
edge: MappingEdge,
|
|
242
|
+
stepIndex: number,
|
|
243
|
+
options: Readonly<Record<string, unknown>>,
|
|
244
|
+
): MappingEdge {
|
|
245
|
+
const length = edge.transformIds?.length ?? 0;
|
|
246
|
+
if (stepIndex < 0 || stepIndex >= length) {
|
|
247
|
+
return edge;
|
|
248
|
+
}
|
|
249
|
+
const base = resizeOptionSteps(edge.transformOptionSteps, length) ?? Array.from({ length });
|
|
250
|
+
const nextSteps = base.map((step, index) =>
|
|
251
|
+
index === stepIndex ? sanitizeOptionRecord(options) : step,
|
|
252
|
+
);
|
|
253
|
+
return {
|
|
254
|
+
...edge,
|
|
255
|
+
transformOptionSteps: resizeOptionSteps(nextSteps, length),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Whether an edge may host list-context (`itemEdges`) editing. */
|
|
260
|
+
export function canEditListContext(
|
|
261
|
+
edge: MappingEdge,
|
|
262
|
+
sources: readonly SourceField[],
|
|
263
|
+
targets: readonly TargetSlot[],
|
|
264
|
+
): boolean {
|
|
265
|
+
if (edge.itemEdges) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
const source = findSourceField(sources, edge.sourceFieldId);
|
|
269
|
+
const target = findTargetSlot(targets, edge.targetSlotId);
|
|
270
|
+
const sourceOk =
|
|
271
|
+
!source?.dataType || source.dataType === 'array' || source.dataType === 'unknown';
|
|
272
|
+
const targetOk =
|
|
273
|
+
!target?.dataType || target.dataType === 'array' || target.dataType === 'unknown';
|
|
274
|
+
return sourceOk && targetOk && Boolean(source?.children?.length && target?.children?.length);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function enableListContextOnEdge(edge: MappingEdge): MappingEdge {
|
|
278
|
+
if (edge.itemEdges) {
|
|
279
|
+
return edge;
|
|
280
|
+
}
|
|
281
|
+
return { ...edge, itemEdges: [] };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function setItemEdgesOnEdge(
|
|
285
|
+
edge: MappingEdge,
|
|
286
|
+
itemEdges: readonly MappingEdge[],
|
|
287
|
+
): MappingEdge {
|
|
288
|
+
return { ...edge, itemEdges: [...itemEdges] };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function upsertItemEdgeOnParent(edge: MappingEdge, itemEdge: MappingEdge): MappingEdge {
|
|
292
|
+
const current = edge.itemEdges ?? [];
|
|
293
|
+
const withoutTarget = current.filter((child) => child.targetSlotId !== itemEdge.targetSlotId);
|
|
294
|
+
return { ...edge, itemEdges: [...withoutTarget, itemEdge] };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function removeItemEdgeFromParent(edge: MappingEdge, itemEdgeId: string): MappingEdge {
|
|
298
|
+
return {
|
|
299
|
+
...edge,
|
|
300
|
+
itemEdges: (edge.itemEdges ?? []).filter((child) => child.id !== itemEdgeId),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Catalog entries that remain valid when placed at `stepIndex` (replace) or
|
|
306
|
+
* appended (`stepIndex === chain.length`).
|
|
307
|
+
*/
|
|
308
|
+
export function listCompatibleTransforms(input: {
|
|
309
|
+
readonly registry: ValueTransformRegistry;
|
|
310
|
+
readonly edge: MappingEdge;
|
|
311
|
+
readonly stepIndex: number;
|
|
312
|
+
readonly sourceType?: FieldDataType;
|
|
313
|
+
readonly targetType?: FieldDataType;
|
|
314
|
+
readonly mode: 'replace' | 'append';
|
|
315
|
+
}): ValueTransformDefinition[] {
|
|
316
|
+
const chain = input.edge.transformIds ?? [];
|
|
317
|
+
if (input.mode === 'append' && chain.length >= MAX_TRANSFORM_CHAIN) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
if (input.mode === 'replace' && (input.stepIndex < 0 || input.stepIndex >= chain.length)) {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return input.registry
|
|
325
|
+
.list()
|
|
326
|
+
.filter((definition) => definition.id !== 'identity')
|
|
327
|
+
.filter((definition) => {
|
|
328
|
+
const candidate =
|
|
329
|
+
input.mode === 'append'
|
|
330
|
+
? [...chain, definition.id]
|
|
331
|
+
: chain.map((id, index) => (index === input.stepIndex ? definition.id : id));
|
|
332
|
+
return isTransformChainCompatible(
|
|
333
|
+
input.registry,
|
|
334
|
+
candidate,
|
|
335
|
+
input.sourceType,
|
|
336
|
+
input.targetType,
|
|
337
|
+
);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function edgePortTypes(
|
|
342
|
+
edge: MappingEdge,
|
|
343
|
+
sources: readonly SourceField[],
|
|
344
|
+
targets: readonly TargetSlot[],
|
|
345
|
+
): { sourceType?: FieldDataType; targetType?: FieldDataType } {
|
|
346
|
+
return {
|
|
347
|
+
sourceType: findSourceField(sources, edge.sourceFieldId)?.dataType,
|
|
348
|
+
targetType: findTargetSlot(targets, edge.targetSlotId)?.dataType,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
let operatorSequence = 0;
|
|
353
|
+
|
|
354
|
+
export function createCombineOperator(seed?: {
|
|
355
|
+
readonly inputFieldIds?: readonly string[];
|
|
356
|
+
readonly outputSlotId?: string;
|
|
357
|
+
}): CombineMappingOperator {
|
|
358
|
+
operatorSequence += 1;
|
|
359
|
+
return {
|
|
360
|
+
kind: 'combine',
|
|
361
|
+
id: `op-combine-${operatorSequence}-${Date.now()}`,
|
|
362
|
+
inputFieldIds: [...(seed?.inputFieldIds ?? [])].slice(0, MAX_MAPPING_FAN_IN),
|
|
363
|
+
outputSlotId: seed?.outputSlotId ?? '',
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function createSplitOperator(seed?: {
|
|
368
|
+
readonly inputFieldId?: string;
|
|
369
|
+
readonly outputSlotIds?: readonly string[];
|
|
370
|
+
}): SplitMappingOperator {
|
|
371
|
+
operatorSequence += 1;
|
|
372
|
+
return {
|
|
373
|
+
kind: 'split',
|
|
374
|
+
id: `op-split-${operatorSequence}-${Date.now()}`,
|
|
375
|
+
inputFieldId: seed?.inputFieldId ?? '',
|
|
376
|
+
outputSlotIds: [...(seed?.outputSlotIds ?? [])].slice(0, MAX_MAPPING_FAN_OUT),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function updateMappingOperator(
|
|
381
|
+
operators: readonly MappingOperator[],
|
|
382
|
+
operatorId: string,
|
|
383
|
+
updater: (operator: MappingOperator) => MappingOperator,
|
|
384
|
+
): MappingOperator[] {
|
|
385
|
+
return operators.map((operator) => (operator.id === operatorId ? updater(operator) : operator));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function removeMappingOperator(
|
|
389
|
+
operators: readonly MappingOperator[],
|
|
390
|
+
operatorId: string,
|
|
391
|
+
): MappingOperator[] {
|
|
392
|
+
return operators.filter((operator) => operator.id !== operatorId);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export function bindOperatorInput(operator: MappingOperator, fieldId: string): MappingOperator {
|
|
396
|
+
if (operator.kind === 'combine') {
|
|
397
|
+
if (
|
|
398
|
+
operator.inputFieldIds.includes(fieldId) ||
|
|
399
|
+
operator.inputFieldIds.length >= MAX_MAPPING_FAN_IN
|
|
400
|
+
) {
|
|
401
|
+
return operator;
|
|
402
|
+
}
|
|
403
|
+
return { ...operator, inputFieldIds: [...operator.inputFieldIds, fieldId] };
|
|
404
|
+
}
|
|
405
|
+
return { ...operator, inputFieldId: fieldId };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function bindOperatorOutput(operator: MappingOperator, slotId: string): MappingOperator {
|
|
409
|
+
if (operator.kind === 'combine') {
|
|
410
|
+
return { ...operator, outputSlotId: slotId };
|
|
411
|
+
}
|
|
412
|
+
if (
|
|
413
|
+
operator.outputSlotIds.includes(slotId) ||
|
|
414
|
+
operator.outputSlotIds.length >= MAX_MAPPING_FAN_OUT
|
|
415
|
+
) {
|
|
416
|
+
return operator;
|
|
417
|
+
}
|
|
418
|
+
return { ...operator, outputSlotIds: [...operator.outputSlotIds, slotId] };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export function removeOperatorInput(operator: MappingOperator, fieldId: string): MappingOperator {
|
|
422
|
+
if (operator.kind === 'combine') {
|
|
423
|
+
return {
|
|
424
|
+
...operator,
|
|
425
|
+
inputFieldIds: operator.inputFieldIds.filter((id) => id !== fieldId),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (operator.inputFieldId === fieldId) {
|
|
429
|
+
return { ...operator, inputFieldId: '' };
|
|
430
|
+
}
|
|
431
|
+
return operator;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export function removeOperatorOutput(operator: MappingOperator, slotId: string): MappingOperator {
|
|
435
|
+
if (operator.kind === 'combine') {
|
|
436
|
+
return operator.outputSlotId === slotId ? { ...operator, outputSlotId: '' } : operator;
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
...operator,
|
|
440
|
+
outputSlotIds: operator.outputSlotIds.filter((id) => id !== slotId),
|
|
441
|
+
};
|
|
442
|
+
}
|