@workbench-kit/shell-react 0.0.2-prototype.0.2.6 → 0.0.2-prototype.0.2.8
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 +41 -0
- package/package.json +9 -9
- package/src/{chat-ai-mock-proposals.ts → chat/ai-mock-proposals.ts} +39 -39
- package/src/{chat-command-input.ts → chat/command-input.ts} +52 -52
- package/src/{chat-command-policy.ts → chat/command-policy.ts} +54 -54
- package/src/{chat-command-surface.tsx → chat/command-surface.tsx} +155 -155
- package/src/{use-workbench-chat-command-proposals.ts → chat/use-chat-command-proposals.ts} +194 -194
- package/src/{chat-view-data.ts → chat/view-data.ts} +20 -20
- package/src/{chat-view.tsx → chat/view.tsx} +220 -220
- package/src/{command-inspector-surface.tsx → commands/inspector-surface.tsx} +46 -46
- package/src/{use-workbench-command-descriptors.ts → commands/use-command-descriptors.ts} +41 -41
- package/src/{use-context-key-revision.ts → commands/use-context-key-revision.ts} +18 -18
- package/src/{commands-view-data.ts → commands/view-data.ts} +17 -17
- package/src/{commands-view.tsx → commands/view.tsx} +43 -43
- package/src/devtools/WorkbenchDevtoolsPanel.tsx +124 -124
- package/src/devtools/WorkbenchDevtoolsShell.tsx +17 -17
- package/src/devtools/index.ts +11 -11
- package/src/devtools/use-workbench-devtools-snapshot.ts +84 -84
- package/src/devtools/workbench-devtools-snapshot.ts +140 -140
- package/src/devtools/workbench-devtools.css +82 -82
- package/src/{editor-area-dnd.ts → editor/area-dnd.ts} +69 -69
- package/src/{editor-area-model.ts → editor/area-model.ts} +71 -71
- package/src/{editor-area.css → editor/area.css} +454 -454
- package/src/{editor-area.tsx → editor/area.tsx} +676 -676
- package/src/{editor-host-surface.tsx → editor/host-surface.tsx} +631 -634
- package/src/{editor-pane-visibility.ts → editor/pane-visibility.ts} +130 -130
- package/src/{editor-resource.ts → editor/resource.ts} +36 -36
- package/src/{editor-state-storage.ts → editor/state-storage.ts} +182 -193
- package/src/{editor-tab-context-menu.ts → editor/tab-context-menu.ts} +118 -118
- package/src/{use-editor.ts → editor/use-editor.ts} +114 -107
- package/src/{editor-view-providers.tsx → editor/view-providers.tsx} +552 -552
- package/src/{editor-workspace-file-availability.ts → editor/workspace-file-availability.ts} +15 -15
- package/src/{editor-workspace-reconcile.tsx → editor/workspace-reconcile.tsx} +31 -28
- package/src/{explorer-context-menu.ts → explorer/context-menu.ts} +114 -114
- package/src/{createCommandWorkspaceExplorerPort.ts → explorer/create-command-workspace-explorer-port.ts} +86 -86
- package/src/{explorer-reveal.ts → explorer/reveal.ts} +112 -112
- package/src/{search-view-data.ts → explorer/search-view-data.ts} +15 -15
- package/src/{search-view.tsx → explorer/search-view.tsx} +65 -62
- package/src/{use-active-workspace-path.ts → explorer/use-active-workspace-path.ts} +15 -15
- package/src/{explorer-view-data.ts → explorer/view-data.ts} +58 -58
- package/src/{explorer-view.tsx → explorer/view.tsx} +227 -224
- package/src/{extension-context-menu.ts → extensions/context-menu.ts} +52 -52
- package/src/{extension-management-model.ts → extensions/management-model.ts} +313 -313
- package/src/{use-extension-management.ts → extensions/use-extension-management.ts} +241 -193
- package/src/{extensions-view-data.ts → extensions/view-data.ts} +19 -19
- package/src/{extensions-view.tsx → extensions/view.tsx} +63 -38
- package/src/{field-remap-demo.tsx → field-remap/demo.tsx} +18 -18
- package/src/{field-remap-editor-surface.tsx → field-remap/editor-surface.tsx} +36 -36
- package/src/{field-remap-flow-adapter.ts → field-remap/flow-adapter.ts} +240 -240
- package/src/{field-remap-flow.tsx → field-remap/flow.tsx} +369 -369
- package/src/{field-remap-graph.tsx → field-remap/graph.tsx} +559 -559
- package/src/field-remap/jsonata-transform.ts +166 -0
- package/src/{field-remap-panel.tsx → field-remap/panel.tsx} +193 -175
- package/src/{field-remap-samples.ts → field-remap/samples.ts} +352 -352
- package/src/{table-mapping-adapter.ts → field-remap/table-mapping-adapter.ts} +59 -59
- package/src/{field-remap-tree.tsx → field-remap/tree.tsx} +404 -404
- package/src/{field-remap-view-data.ts → field-remap/view-data.ts} +16 -16
- package/src/{field-remap-view.css → field-remap/view.css} +676 -676
- package/src/{field-remap-view.tsx → field-remap/view.tsx} +86 -86
- package/src/index.ts +212 -199
- package/src/{json-form-source-patch.ts → jdw/json-form-source-patch.ts} +323 -323
- package/src/{jdw-lab-view-data.ts → jdw/lab-view-data.ts} +25 -25
- package/src/{jdw-lab-view.css → jdw/lab-view.css} +21 -21
- package/src/{jdw-lab-view.tsx → jdw/lab-view.tsx} +79 -79
- package/src/{jdw-widget-form-view.tsx → jdw/widget-form-view.tsx} +68 -65
- package/src/{jdw-widget-preview-view.tsx → jdw/widget-preview-view.tsx} +54 -51
- package/src/{keybinding-overrides-storage.ts → management/keybinding-overrides-storage.ts} +49 -57
- package/src/{keybinding-management-settings.tsx → management/keybinding-settings.tsx} +16 -16
- package/src/{management-palette-commands.ts → management/palette-commands.ts} +38 -38
- package/src/{preference-settings-storage.ts → management/preference-settings-storage.ts} +50 -58
- package/src/{management-settings-ids.ts → management/settings-ids.ts} +12 -12
- package/src/{management-settings.tsx → management/settings.tsx} +96 -75
- package/src/{use-command-management.ts → management/use-command-management.ts} +191 -191
- package/src/{use-keybinding-management.ts → management/use-keybinding-management.ts} +130 -130
- package/src/{shell-model.tsx → shell/model.tsx} +149 -149
- package/src/{provider.tsx → shell/provider.tsx} +603 -573
- package/src/{shell-secondary-actions.tsx → shell/secondary-actions.tsx} +59 -59
- package/src/{shell-settings-constants.ts → shell/settings-constants.ts} +9 -9
- package/src/{shell-settings.tsx → shell/settings.tsx} +599 -599
- package/src/{shell.tsx → shell/shell.tsx} +680 -649
- package/src/{shell-titlebar-layout-controls.tsx → shell/titlebar-layout-controls.tsx} +4 -4
- package/src/{shell-view-host.tsx → shell/view-host.tsx} +212 -199
- package/src/{workbench-appearance-storage.ts → workbench/appearance-storage.ts} +107 -115
- package/src/{workbench-command-host.tsx → workbench/command-host.tsx} +243 -243
- package/src/{workbench-command-palette.ts → workbench/command-palette.ts} +208 -208
- package/src/{workbench-keybinding-bridge.ts → workbench/keybinding-bridge.ts} +54 -54
- package/src/{workbench-layout-storage.ts → workbench/layout-storage.ts} +134 -145
- package/src/{workbench-profile-modal.tsx → workbench/profile-modal.tsx} +118 -118
- package/src/{workbench-shell-command-registration.ts → workbench/shell-command-registration.ts} +58 -58
- package/src/{workbench-startup-gate.tsx → workbench/startup-gate.tsx} +136 -136
- package/src/{workbench-status-sections.ts → workbench/status-sections.ts} +43 -43
- package/src/{use-persisted-workbench-appearance.ts → workbench/use-persisted-appearance.ts} +33 -33
- package/src/{workbench-user-commands.ts → workbench/user-commands.ts} +46 -46
- package/src/{workspace-view-state.ts → workbench/workspace-view-state.ts} +35 -35
- package/src/jsonata-transform.ts +0 -36
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import jsonata from 'jsonata';
|
|
2
|
+
import type { ValueTransformDefinition } from '@workbench-kit/field-remap';
|
|
3
|
+
|
|
4
|
+
/** Host-registered JSONata expression transform (Stedi-style advanced mapping). */
|
|
5
|
+
export const JSONATA_TRANSFORM_ID = 'expr:jsonata' as const;
|
|
6
|
+
|
|
7
|
+
/** Default wall-clock budget for a single expression evaluation. */
|
|
8
|
+
export const DEFAULT_JSONATA_TIMEOUT_MS = 2_000;
|
|
9
|
+
|
|
10
|
+
/** Default maximum expression source length (characters). */
|
|
11
|
+
export const DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH = 4_096;
|
|
12
|
+
|
|
13
|
+
export type JsonataTransformErrorPolicy = 'passthrough' | 'throw';
|
|
14
|
+
|
|
15
|
+
export interface CreateJsonataValueTransformOptions {
|
|
16
|
+
/** Wall-clock timeout for `evaluate` (default {@link DEFAULT_JSONATA_TIMEOUT_MS}). */
|
|
17
|
+
readonly timeoutMs?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Reject expressions longer than this many characters
|
|
20
|
+
* (default {@link DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH}).
|
|
21
|
+
*/
|
|
22
|
+
readonly maxExpressionLength?: number;
|
|
23
|
+
/**
|
|
24
|
+
* - `throw` (default): surface timeout / compile / evaluate failures to the host
|
|
25
|
+
* - `passthrough`: return the original value (legacy silent identity)
|
|
26
|
+
*/
|
|
27
|
+
readonly onError?: JsonataTransformErrorPolicy;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createJsonataValueTransform(
|
|
31
|
+
options: CreateJsonataValueTransformOptions = {},
|
|
32
|
+
): ValueTransformDefinition {
|
|
33
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_JSONATA_TIMEOUT_MS;
|
|
34
|
+
const maxExpressionLength = options.maxExpressionLength ?? DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH;
|
|
35
|
+
const onError = options.onError ?? 'throw';
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
id: JSONATA_TRANSFORM_ID,
|
|
39
|
+
label: 'JSONata expression',
|
|
40
|
+
description:
|
|
41
|
+
'Evaluate a JSONata expression against the source value (host-registered; bounded).',
|
|
42
|
+
category: 'expression',
|
|
43
|
+
inputTypes: ['string', 'number', 'boolean', 'object', 'array', 'unknown'],
|
|
44
|
+
outputType: 'unknown',
|
|
45
|
+
optionFields: [
|
|
46
|
+
{
|
|
47
|
+
key: 'expression',
|
|
48
|
+
label: 'Expression',
|
|
49
|
+
kind: 'string',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
apply: async (value, context) => {
|
|
53
|
+
const expression =
|
|
54
|
+
typeof context.options?.expression === 'string' ? context.options.expression.trim() : '';
|
|
55
|
+
if (!expression) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (expression.length > maxExpressionLength) {
|
|
60
|
+
return handleJsonataError(
|
|
61
|
+
new Error(
|
|
62
|
+
`JSONata expression exceeds max length (${expression.length} > ${maxExpressionLength}).`,
|
|
63
|
+
),
|
|
64
|
+
value,
|
|
65
|
+
onError,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const compiled = jsonata(expression);
|
|
71
|
+
// jsonata@2.x evaluate returns a Promise (1.x was synchronous).
|
|
72
|
+
return await raceJsonataEvaluation(compiled.evaluate(value), {
|
|
73
|
+
timeoutMs,
|
|
74
|
+
signal: context.signal,
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return handleJsonataError(error, value, onError);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Default bounded transform (`onError: 'throw'`, 2s timeout, 4k expression cap). */
|
|
84
|
+
export const jsonataValueTransform: ValueTransformDefinition = createJsonataValueTransform();
|
|
85
|
+
|
|
86
|
+
export class JsonataTransformTimeoutError extends Error {
|
|
87
|
+
override readonly name = 'JsonataTransformTimeoutError';
|
|
88
|
+
|
|
89
|
+
constructor(timeoutMs: number) {
|
|
90
|
+
super(`JSONata evaluation timed out after ${timeoutMs}ms.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Race a JSONata evaluation against timeout / AbortSignal (exported for unit tests). */
|
|
95
|
+
export async function raceJsonataEvaluation<T>(
|
|
96
|
+
evaluation: PromiseLike<T>,
|
|
97
|
+
bounds: { readonly timeoutMs: number; readonly signal?: AbortSignal },
|
|
98
|
+
): Promise<T> {
|
|
99
|
+
const { timeoutMs, signal } = bounds;
|
|
100
|
+
if (signal?.aborted) {
|
|
101
|
+
throw signal.reason instanceof Error
|
|
102
|
+
? signal.reason
|
|
103
|
+
: Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
107
|
+
return evaluation;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return new Promise<T>((resolve, reject) => {
|
|
111
|
+
let settled = false;
|
|
112
|
+
const timer = setTimeout(() => {
|
|
113
|
+
if (settled) return;
|
|
114
|
+
settled = true;
|
|
115
|
+
cleanup();
|
|
116
|
+
reject(new JsonataTransformTimeoutError(timeoutMs));
|
|
117
|
+
}, timeoutMs);
|
|
118
|
+
|
|
119
|
+
const onAbort = () => {
|
|
120
|
+
if (settled) return;
|
|
121
|
+
settled = true;
|
|
122
|
+
cleanup();
|
|
123
|
+
reject(
|
|
124
|
+
signal?.reason instanceof Error
|
|
125
|
+
? signal.reason
|
|
126
|
+
: Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' }),
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const cleanup = () => {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
signal?.removeEventListener('abort', onAbort);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
136
|
+
|
|
137
|
+
Promise.resolve(evaluation).then(
|
|
138
|
+
(result) => {
|
|
139
|
+
if (settled) return;
|
|
140
|
+
settled = true;
|
|
141
|
+
cleanup();
|
|
142
|
+
resolve(result);
|
|
143
|
+
},
|
|
144
|
+
(error: unknown) => {
|
|
145
|
+
if (settled) return;
|
|
146
|
+
settled = true;
|
|
147
|
+
cleanup();
|
|
148
|
+
reject(error);
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function handleJsonataError(
|
|
155
|
+
error: unknown,
|
|
156
|
+
value: unknown,
|
|
157
|
+
onError: JsonataTransformErrorPolicy,
|
|
158
|
+
): unknown {
|
|
159
|
+
if (onError === 'passthrough') {
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
if (error instanceof Error) {
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
throw new Error(String(error));
|
|
166
|
+
}
|
|
@@ -1,175 +1,193 @@
|
|
|
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 './
|
|
16
|
-
import {
|
|
17
|
-
getFieldRemapSample,
|
|
18
|
-
type FieldRemapSampleDefinition,
|
|
19
|
-
type FieldRemapSampleId,
|
|
20
|
-
} from './
|
|
21
|
-
import { jsonataValueTransform } from './jsonata-transform.js';
|
|
22
|
-
import './
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
defineDataShape({
|
|
83
|
-
id: sample.
|
|
84
|
-
label: sample.
|
|
85
|
-
role: '
|
|
86
|
-
fields:
|
|
87
|
-
}),
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1
|
+
import { useEffect, 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 './flow.js';
|
|
16
|
+
import {
|
|
17
|
+
getFieldRemapSample,
|
|
18
|
+
type FieldRemapSampleDefinition,
|
|
19
|
+
type FieldRemapSampleId,
|
|
20
|
+
} from './samples.js';
|
|
21
|
+
import { jsonataValueTransform } from './jsonata-transform.js';
|
|
22
|
+
import './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
|
+
type FieldRemapPreviewResult = {
|
|
33
|
+
readonly output: Record<string, unknown>;
|
|
34
|
+
readonly error?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function resolveSample(sample: FieldRemapPanelProps['sample']): FieldRemapSampleDefinition {
|
|
38
|
+
if (!sample) {
|
|
39
|
+
return getFieldRemapSample('nested-ab');
|
|
40
|
+
}
|
|
41
|
+
if (typeof sample === 'string') {
|
|
42
|
+
return getFieldRemapSample(sample);
|
|
43
|
+
}
|
|
44
|
+
return sample;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Self-contained field-remap workbench panel:
|
|
49
|
+
* XYFlow mapper + convertToShape preview for one A→B (or T_A→T_B) sample.
|
|
50
|
+
*
|
|
51
|
+
* Remount with `key={sampleId}` when switching catalog entries so edge state resets.
|
|
52
|
+
*/
|
|
53
|
+
export function FieldRemapPanel({
|
|
54
|
+
sample: sampleProp,
|
|
55
|
+
transforms: transformsProp,
|
|
56
|
+
className,
|
|
57
|
+
}: FieldRemapPanelProps): JSX.Element {
|
|
58
|
+
const sample = resolveSample(sampleProp);
|
|
59
|
+
const registry = useMemo(() => {
|
|
60
|
+
if (transformsProp) {
|
|
61
|
+
return transformsProp;
|
|
62
|
+
}
|
|
63
|
+
const next = createBuiltinValueTransformRegistry();
|
|
64
|
+
next.register(jsonataValueTransform);
|
|
65
|
+
return next;
|
|
66
|
+
}, [transformsProp]);
|
|
67
|
+
|
|
68
|
+
const [edges, setEdges] = useState<readonly MappingEdge[]>(() => [...sample.edges]);
|
|
69
|
+
const [result, setResult] = useState<FieldRemapPreviewResult>({ output: {} });
|
|
70
|
+
|
|
71
|
+
const sourceFields = useMemo(
|
|
72
|
+
() => sourceFieldsFromPlainObject(sample.source, { idPrefix: sample.sourceIdPrefix }),
|
|
73
|
+
[sample],
|
|
74
|
+
);
|
|
75
|
+
const targetSlots = useMemo(
|
|
76
|
+
() => targetSlotsFromPlainObject(sample.targetShape, { idPrefix: sample.targetIdPrefix }),
|
|
77
|
+
[sample],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const shapes = useMemo(
|
|
81
|
+
() => [
|
|
82
|
+
defineDataShape({
|
|
83
|
+
id: sample.sourceIdPrefix,
|
|
84
|
+
label: sample.sourceLabel,
|
|
85
|
+
role: 'source',
|
|
86
|
+
fields: sourceFields,
|
|
87
|
+
}),
|
|
88
|
+
defineDataShape({
|
|
89
|
+
id: sample.targetIdPrefix,
|
|
90
|
+
label: sample.targetLabel,
|
|
91
|
+
role: 'target',
|
|
92
|
+
fields: targetSlots,
|
|
93
|
+
}),
|
|
94
|
+
],
|
|
95
|
+
[sample, sourceFields, targetSlots],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const conflicts = useMemo(
|
|
99
|
+
() => findParentChildMappingConflicts(edges, sourceFields, targetSlots),
|
|
100
|
+
[edges, sourceFields, targetSlots],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
const conversion = withConversionEdges(
|
|
106
|
+
defineConversion({
|
|
107
|
+
id: `${sample.sourceIdPrefix}→${sample.targetIdPrefix}`,
|
|
108
|
+
sourceShapeIds: [sample.sourceIdPrefix],
|
|
109
|
+
targetShapeId: sample.targetIdPrefix,
|
|
110
|
+
edges: [...sample.edges],
|
|
111
|
+
}),
|
|
112
|
+
edges,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
void convertToShape({
|
|
116
|
+
conversion,
|
|
117
|
+
shapes,
|
|
118
|
+
inputs: { [sample.sourceIdPrefix]: sample.source },
|
|
119
|
+
transforms: registry,
|
|
120
|
+
signal: controller.signal,
|
|
121
|
+
})
|
|
122
|
+
.then((next) => {
|
|
123
|
+
if (!controller.signal.aborted) {
|
|
124
|
+
setResult({ output: next.output });
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
.catch((error: unknown) => {
|
|
128
|
+
if (controller.signal.aborted) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
setResult({
|
|
132
|
+
output: {},
|
|
133
|
+
error: error instanceof Error ? error.message : String(error),
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return () => {
|
|
138
|
+
controller.abort();
|
|
139
|
+
};
|
|
140
|
+
}, [edges, registry, sample, shapes]);
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div
|
|
144
|
+
className={['workbench-field-remap-demo', className].filter(Boolean).join(' ')}
|
|
145
|
+
data-testid="field-remap-demo"
|
|
146
|
+
data-sample-id={sample.id}
|
|
147
|
+
>
|
|
148
|
+
<header className="workbench-field-remap-demo__header">
|
|
149
|
+
<h2 className="workbench-field-remap-demo__title">{sample.title}</h2>
|
|
150
|
+
<p className="workbench-field-remap-demo__intro">{sample.description}</p>
|
|
151
|
+
</header>
|
|
152
|
+
|
|
153
|
+
<FieldRemapFlowMapper
|
|
154
|
+
sources={sourceFields}
|
|
155
|
+
targets={targetSlots}
|
|
156
|
+
edges={edges}
|
|
157
|
+
transforms={registry}
|
|
158
|
+
onEdgesChange={setEdges}
|
|
159
|
+
sourceTitle={sample.sourceLabel}
|
|
160
|
+
targetTitle={sample.targetLabel}
|
|
161
|
+
/>
|
|
162
|
+
|
|
163
|
+
{conflicts.length > 0 ? (
|
|
164
|
+
<p
|
|
165
|
+
className="workbench-field-remap-demo__warn"
|
|
166
|
+
role="status"
|
|
167
|
+
data-testid="field-remap-conflicts"
|
|
168
|
+
>
|
|
169
|
+
Warning: parent and child fields are both mapped (
|
|
170
|
+
{conflicts.map((item) => `${item.parentId} / ${item.childId}`).join('; ')}). Prefer one
|
|
171
|
+
level.
|
|
172
|
+
</p>
|
|
173
|
+
) : null}
|
|
174
|
+
|
|
175
|
+
{result.error ? (
|
|
176
|
+
<p className="workbench-field-remap-demo__error" role="alert">
|
|
177
|
+
{result.error}
|
|
178
|
+
</p>
|
|
179
|
+
) : null}
|
|
180
|
+
|
|
181
|
+
<div className="workbench-field-remap-demo__panes">
|
|
182
|
+
<section className="workbench-field-remap-demo__pane" aria-labelledby="field-remap-source">
|
|
183
|
+
<h3 id="field-remap-source">{sample.sourceLabel}</h3>
|
|
184
|
+
<pre data-testid="field-remap-input">{JSON.stringify(sample.source, null, 2)}</pre>
|
|
185
|
+
</section>
|
|
186
|
+
<section className="workbench-field-remap-demo__pane" aria-labelledby="field-remap-target">
|
|
187
|
+
<h3 id="field-remap-target">{sample.targetLabel}</h3>
|
|
188
|
+
<pre data-testid="field-remap-result">{JSON.stringify(result.output, null, 2)}</pre>
|
|
189
|
+
</section>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
}
|