@workbench-kit/field-remap 0.0.1-prototype.0 → 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.
@@ -1,95 +1,95 @@
1
- import {
2
- createFieldRemapDocument,
3
- normalizeFieldRemapDocument,
4
- } from '../document/fieldRemapDocument.js';
5
- import type { MappingEdge, FieldRemapDocument } from '../types.js';
6
-
7
- /**
8
- * Managed conversion between one or more source shapes and a target shape.
9
- * Edges live in `document`; runtime uses `convertToShape` (not a method on a shape).
10
- */
11
- export interface ConversionDefinition {
12
- readonly id: string;
13
- readonly label?: string;
14
- /** One or more managed source shape ids (multi-input). */
15
- readonly sourceShapeIds: readonly string[];
16
- readonly targetShapeId: string;
17
- readonly document: FieldRemapDocument;
18
- }
19
-
20
- export interface ConversionRegistry {
21
- register(conversion: ConversionDefinition): void;
22
- get(id: string): ConversionDefinition | undefined;
23
- list(): readonly ConversionDefinition[];
24
- }
25
-
26
- export interface DefineConversionInput {
27
- readonly id: string;
28
- readonly label?: string;
29
- readonly sourceShapeIds: readonly string[];
30
- readonly targetShapeId: string;
31
- readonly document?: FieldRemapDocument;
32
- readonly edges?: readonly MappingEdge[];
33
- }
34
-
35
- export function defineConversion(input: DefineConversionInput): ConversionDefinition {
36
- const id = input.id.trim();
37
- if (!id) {
38
- throw new Error('ConversionDefinition.id must be a non-empty string.');
39
- }
40
- const sourceShapeIds = input.sourceShapeIds.map((item) => item.trim()).filter(Boolean);
41
- if (sourceShapeIds.length === 0) {
42
- throw new Error('ConversionDefinition.sourceShapeIds must include at least one shape id.');
43
- }
44
- const targetShapeId = input.targetShapeId.trim();
45
- if (!targetShapeId) {
46
- throw new Error('ConversionDefinition.targetShapeId must be a non-empty string.');
47
- }
48
-
49
- const document = input.document
50
- ? normalizeFieldRemapDocument(input.document)
51
- : createFieldRemapDocument(input.edges ?? []);
52
-
53
- return {
54
- id,
55
- ...(input.label?.trim() ? { label: input.label.trim() } : {}),
56
- sourceShapeIds,
57
- targetShapeId,
58
- document,
59
- };
60
- }
61
-
62
- export function createConversionRegistry(
63
- initial: readonly ConversionDefinition[] = [],
64
- ): ConversionRegistry {
65
- const byId = new Map<string, ConversionDefinition>();
66
-
67
- const api: ConversionRegistry = {
68
- register(conversion) {
69
- const defined = defineConversion(conversion);
70
- byId.set(defined.id, defined);
71
- },
72
- get(id) {
73
- return byId.get(id.trim());
74
- },
75
- list() {
76
- return [...byId.values()];
77
- },
78
- };
79
-
80
- for (const conversion of initial) {
81
- api.register(conversion);
82
- }
83
- return api;
84
- }
85
-
86
- /** Replace edges on a conversion (immutable). */
87
- export function withConversionEdges(
88
- conversion: ConversionDefinition,
89
- edges: readonly MappingEdge[],
90
- ): ConversionDefinition {
91
- return {
92
- ...conversion,
93
- document: createFieldRemapDocument(edges),
94
- };
95
- }
1
+ import {
2
+ createFieldRemapDocument,
3
+ normalizeFieldRemapDocument,
4
+ } from '../document/fieldRemapDocument.js';
5
+ import type { MappingEdge, FieldRemapDocument } from '../types.js';
6
+
7
+ /**
8
+ * Managed conversion between one or more source shapes and a target shape.
9
+ * Edges live in `document`; runtime uses `convertToShape` (not a method on a shape).
10
+ */
11
+ export interface ConversionDefinition {
12
+ readonly id: string;
13
+ readonly label?: string;
14
+ /** One or more managed source shape ids (multi-input). */
15
+ readonly sourceShapeIds: readonly string[];
16
+ readonly targetShapeId: string;
17
+ readonly document: FieldRemapDocument;
18
+ }
19
+
20
+ export interface ConversionRegistry {
21
+ register(conversion: ConversionDefinition): void;
22
+ get(id: string): ConversionDefinition | undefined;
23
+ list(): readonly ConversionDefinition[];
24
+ }
25
+
26
+ export interface DefineConversionInput {
27
+ readonly id: string;
28
+ readonly label?: string;
29
+ readonly sourceShapeIds: readonly string[];
30
+ readonly targetShapeId: string;
31
+ readonly document?: FieldRemapDocument;
32
+ readonly edges?: readonly MappingEdge[];
33
+ }
34
+
35
+ export function defineConversion(input: DefineConversionInput): ConversionDefinition {
36
+ const id = input.id.trim();
37
+ if (!id) {
38
+ throw new Error('ConversionDefinition.id must be a non-empty string.');
39
+ }
40
+ const sourceShapeIds = input.sourceShapeIds.map((item) => item.trim()).filter(Boolean);
41
+ if (sourceShapeIds.length === 0) {
42
+ throw new Error('ConversionDefinition.sourceShapeIds must include at least one shape id.');
43
+ }
44
+ const targetShapeId = input.targetShapeId.trim();
45
+ if (!targetShapeId) {
46
+ throw new Error('ConversionDefinition.targetShapeId must be a non-empty string.');
47
+ }
48
+
49
+ const document = input.document
50
+ ? normalizeFieldRemapDocument(input.document)
51
+ : createFieldRemapDocument(input.edges ?? []);
52
+
53
+ return {
54
+ id,
55
+ ...(input.label?.trim() ? { label: input.label.trim() } : {}),
56
+ sourceShapeIds,
57
+ targetShapeId,
58
+ document,
59
+ };
60
+ }
61
+
62
+ export function createConversionRegistry(
63
+ initial: readonly ConversionDefinition[] = [],
64
+ ): ConversionRegistry {
65
+ const byId = new Map<string, ConversionDefinition>();
66
+
67
+ const api: ConversionRegistry = {
68
+ register(conversion) {
69
+ const defined = defineConversion(conversion);
70
+ byId.set(defined.id, defined);
71
+ },
72
+ get(id) {
73
+ return byId.get(id.trim());
74
+ },
75
+ list() {
76
+ return [...byId.values()];
77
+ },
78
+ };
79
+
80
+ for (const conversion of initial) {
81
+ api.register(conversion);
82
+ }
83
+ return api;
84
+ }
85
+
86
+ /** Replace edges on a conversion (immutable). */
87
+ export function withConversionEdges(
88
+ conversion: ConversionDefinition,
89
+ edges: readonly MappingEdge[],
90
+ ): ConversionDefinition {
91
+ return {
92
+ ...conversion,
93
+ document: createFieldRemapDocument(edges),
94
+ };
95
+ }
@@ -0,0 +1,158 @@
1
+ import { applyMappingOperators, normalizeMappingOperators } from '../mapping/mappingOperators.js';
2
+ import { flattenSourceFields } from '../mapping/treeUtils.js';
3
+ import type {
4
+ MappingEdge,
5
+ MappingOperator,
6
+ SourceField,
7
+ TargetSlot,
8
+ ValueTransformRegistry,
9
+ } from '../types.js';
10
+ import { defineConversion, withConversionEdges } from './conversionDefinition.js';
11
+ import { attachShapeIdToSourceFields, defineDataShape } from './dataShape.js';
12
+ import { convertToShape, type ConvertToShapeResult } from './convertToShape.js';
13
+
14
+ export interface ConvertMappedInputsInput {
15
+ readonly sources: readonly SourceField[];
16
+ readonly targets: readonly TargetSlot[];
17
+ readonly edges: readonly MappingEdge[];
18
+ readonly operators?: readonly MappingOperator[];
19
+ /**
20
+ * Named input bags keyed by source shape id (same contract as `convertToShape`).
21
+ * When sources omit `shapeId`, bags should use the resolved primary source shape id
22
+ * (default `source`).
23
+ */
24
+ readonly inputs: Readonly<Record<string, unknown>>;
25
+ readonly transforms: ValueTransformRegistry;
26
+ readonly signal?: AbortSignal;
27
+ /** Override inferred source shape ids (order preserved for conversion). */
28
+ readonly sourceShapeIds?: readonly string[];
29
+ readonly targetShapeId?: string;
30
+ readonly sourceLabel?: string;
31
+ readonly targetLabel?: string;
32
+ }
33
+
34
+ function collectSourceShapeIds(sources: readonly SourceField[]): string[] {
35
+ const ids: string[] = [];
36
+ const seen = new Set<string>();
37
+ for (const field of flattenSourceFields(sources)) {
38
+ const shapeId = field.shapeId?.trim();
39
+ if (!shapeId || seen.has(shapeId)) {
40
+ continue;
41
+ }
42
+ seen.add(shapeId);
43
+ ids.push(shapeId);
44
+ }
45
+ return ids;
46
+ }
47
+
48
+ function filterSourceTreeByShapeId(
49
+ fields: readonly SourceField[],
50
+ shapeId: string,
51
+ treatMissingAsMatch: boolean,
52
+ ): SourceField[] {
53
+ const next: SourceField[] = [];
54
+ for (const field of fields) {
55
+ const fieldShapeId = field.shapeId?.trim();
56
+ const children = field.children
57
+ ? filterSourceTreeByShapeId(field.children, shapeId, treatMissingAsMatch)
58
+ : undefined;
59
+ const selfMatches =
60
+ fieldShapeId === shapeId ||
61
+ (treatMissingAsMatch && (fieldShapeId === undefined || fieldShapeId === ''));
62
+ if (selfMatches || (children && children.length > 0)) {
63
+ next.push({
64
+ ...field,
65
+ shapeId,
66
+ ...(children ? { children } : {}),
67
+ });
68
+ }
69
+ }
70
+ return next;
71
+ }
72
+
73
+ /**
74
+ * Host-friendly evaluate path for persisted `MappingEdge` graphs (including transform
75
+ * chains and optional n→m operators) without requiring a full `FieldRemapDocument`.
76
+ *
77
+ * Same semantics as the shell panel preview: `convertToShape` then
78
+ * `applyMappingOperators`. Prefer this when the host catalog stores edges (+ optional
79
+ * operators) separately from kit document JSON.
80
+ */
81
+ export async function convertMappedInputs(
82
+ input: ConvertMappedInputsInput,
83
+ ): Promise<ConvertToShapeResult> {
84
+ const inferredSourceIds = collectSourceShapeIds(input.sources);
85
+ const sourceShapeIds =
86
+ input.sourceShapeIds && input.sourceShapeIds.length > 0
87
+ ? [...input.sourceShapeIds]
88
+ : inferredSourceIds.length > 0
89
+ ? inferredSourceIds
90
+ : ['source'];
91
+ const targetShapeId = input.targetShapeId?.trim() || 'target';
92
+ const primarySourceId = sourceShapeIds[0]!;
93
+ const needsStamp = inferredSourceIds.length === 0;
94
+
95
+ const stampedSources = needsStamp
96
+ ? attachShapeIdToSourceFields(input.sources, primarySourceId)
97
+ : input.sources;
98
+
99
+ const sourceShapes = sourceShapeIds.map((shapeId) =>
100
+ defineDataShape({
101
+ id: shapeId,
102
+ label: input.sourceLabel ?? shapeId,
103
+ role: 'source',
104
+ fields:
105
+ sourceShapeIds.length === 1
106
+ ? stampedSources
107
+ : filterSourceTreeByShapeId(stampedSources, shapeId, shapeId === primarySourceId),
108
+ }),
109
+ );
110
+
111
+ const shapes = [
112
+ ...sourceShapes,
113
+ defineDataShape({
114
+ id: targetShapeId,
115
+ label: input.targetLabel ?? targetShapeId,
116
+ role: 'target',
117
+ fields: input.targets,
118
+ }),
119
+ ];
120
+
121
+ const conversion = withConversionEdges(
122
+ defineConversion({
123
+ id: `${primarySourceId}→${targetShapeId}`,
124
+ sourceShapeIds,
125
+ targetShapeId,
126
+ edges: [],
127
+ }),
128
+ input.edges,
129
+ );
130
+
131
+ const converted = await convertToShape({
132
+ conversion,
133
+ shapes,
134
+ inputs: input.inputs,
135
+ transforms: input.transforms,
136
+ signal: input.signal,
137
+ });
138
+
139
+ const operators = normalizeMappingOperators(input.operators);
140
+ if (!operators?.length) {
141
+ return converted;
142
+ }
143
+
144
+ const merged = await applyMappingOperators({
145
+ operators,
146
+ sources: stampedSources,
147
+ targets: input.targets,
148
+ inputs: input.inputs,
149
+ transforms: input.transforms,
150
+ output: converted.output,
151
+ signal: input.signal,
152
+ });
153
+
154
+ return {
155
+ output: merged.output,
156
+ slots: converted.slots,
157
+ };
158
+ }