@murumets-ee/imports 0.49.0 → 0.52.0
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/dist/analyze/index.d.mts +265 -0
- package/dist/analyze/index.d.mts.map +1 -0
- package/dist/analyze/index.mjs +2 -0
- package/dist/analyze/index.mjs.map +1 -0
- package/dist/carmaker-feed-B-QYr3PI.mjs +2 -0
- package/dist/{carmaker-feed-CxhZBrkO.mjs.map → carmaker-feed-B-QYr3PI.mjs.map} +1 -1
- package/dist/carmaker-feed-_QVQFLUl.d.mts +59 -0
- package/dist/carmaker-feed-_QVQFLUl.d.mts.map +1 -0
- package/dist/{cleanup-CHjDgvKT.mjs → cleanup-DDljBe0Z.mjs} +2 -2
- package/dist/{cleanup-CHjDgvKT.mjs.map → cleanup-DDljBe0Z.mjs.map} +1 -1
- package/dist/engine-B0ExE4jL.mjs +2 -0
- package/dist/engine-B0ExE4jL.mjs.map +1 -0
- package/dist/{parts-schema-x8_GRYWc.d.mts → engine-DiM4r2Vj.d.mts} +349 -49
- package/dist/engine-DiM4r2Vj.d.mts.map +1 -0
- package/dist/index-Bm69N5DW.d.mts +97 -0
- package/dist/index-Bm69N5DW.d.mts.map +1 -0
- package/dist/index.d.mts +30 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/mapping/index.d.mts +3 -359
- package/dist/mapping/index.d.mts.map +1 -1
- package/dist/mapping/index.mjs +1 -1
- package/dist/mapping-transform-CO4MNhMe.mjs +2 -0
- package/dist/{mapping-transform-D76b-P-p.mjs.map → mapping-transform-CO4MNhMe.mjs.map} +1 -1
- package/dist/matching/index.d.mts +2 -0
- package/dist/matching/index.mjs +1 -0
- package/dist/matching-Sr0z4HvN.mjs +2 -0
- package/dist/matching-Sr0z4HvN.mjs.map +1 -0
- package/dist/parts-schema-DOWhBJYh.d.mts +61 -0
- package/dist/parts-schema-DOWhBJYh.d.mts.map +1 -0
- package/dist/parts-schema-QqLci_Pl.mjs +2 -0
- package/dist/parts-schema-QqLci_Pl.mjs.map +1 -0
- package/dist/plugin.d.mts +1 -1
- package/dist/plugin.mjs +1 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/read-feed-DGjqejw3.mjs +2 -0
- package/dist/read-feed-DGjqejw3.mjs.map +1 -0
- package/dist/runner-C9GIe5Dr.mjs +2 -0
- package/dist/runner-C9GIe5Dr.mjs.map +1 -0
- package/dist/{schema-registry-C_lQje-K.mjs → schema-registry-DB5aIZMN.mjs} +2 -2
- package/dist/{schema-registry-C_lQje-K.mjs.map → schema-registry-DB5aIZMN.mjs.map} +1 -1
- package/dist/storage-resolver.d.mts +1 -1
- package/dist/transform-Cu5YEoOl.mjs +2 -0
- package/dist/transform-Cu5YEoOl.mjs.map +1 -0
- package/dist/{transform-CABBaYmF.d.mts → transform-WeiiLdYP.d.mts} +12 -1
- package/dist/transform-WeiiLdYP.d.mts.map +1 -0
- package/dist/transforms.d.mts +2 -46
- package/dist/transforms.mjs +1 -1
- package/dist/{worker-DRTgbtT7.d.mts → worker-_MlRH5hd.d.mts} +209 -8
- package/dist/worker-_MlRH5hd.d.mts.map +1 -0
- package/dist/worker.d.mts +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/worker.mjs.map +1 -1
- package/dist/xlsx-reader-_ioPAqm5.mjs +2 -0
- package/dist/{xlsx-reader-Ccesszbc.mjs.map → xlsx-reader-_ioPAqm5.mjs.map} +1 -1
- package/package.json +16 -8
- package/dist/carmaker-feed-CxhZBrkO.mjs +0 -2
- package/dist/mapping-transform-D76b-P-p.mjs +0 -2
- package/dist/parts-schema-D0gMhNWj.mjs +0 -2
- package/dist/parts-schema-D0gMhNWj.mjs.map +0 -1
- package/dist/parts-schema-x8_GRYWc.d.mts.map +0 -1
- package/dist/runner-C-s6SD7t.mjs +0 -2
- package/dist/runner-C-s6SD7t.mjs.map +0 -1
- package/dist/transform-CABBaYmF.d.mts.map +0 -1
- package/dist/transform-DIzWppgJ.mjs +0 -2
- package/dist/transform-DIzWppgJ.mjs.map +0 -1
- package/dist/transforms.d.mts.map +0 -1
- package/dist/worker-DRTgbtT7.d.mts.map +0 -1
- package/dist/xlsx-reader-Ccesszbc.mjs +0 -2
package/dist/mapping/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as RowTransform } from "../transform-
|
|
2
|
-
import { C as toMinorUnits,
|
|
3
|
-
import {
|
|
1
|
+
import { i as RowTransform } from "../transform-WeiiLdYP.mjs";
|
|
2
|
+
import { C as columnTransformSchema, D as toMinorUnits, E as parseNumber, S as applyColumnTransforms, T as parseEuropeanNumber, _ as COLUMN_TRANSFORM_OPS, a as mappingColumnSchema, b as ColumnTransformOp, c as resolveMapping, d as NumberOutcome, f as TargetField, g as textOrNull, h as rowError, i as buildMappingTransform, l as BuildRowContext, m as numberCell, n as MappingConfig, o as mappingConfigSchema, p as TargetSchema, r as ResolvableMapping, s as numberFormatSchema, t as MappingColumn, u as MappedRow, v as CellValue, w as NumberFormat, x as ColumnTransformOpDescriptor, y as ColumnTransform } from "../engine-DiM4r2Vj.mjs";
|
|
3
|
+
import { i as partsSchema, n as normalizeCode, r as partsParamsSchema, t as PartsParams } from "../parts-schema-DOWhBJYh.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/mapping/discount-table-schema.d.ts
|
|
6
6
|
/** One parsed discount-table row: a group token + the purchase discount % off list. */
|
|
@@ -10,362 +10,6 @@ interface DiscountTableRow {
|
|
|
10
10
|
}
|
|
11
11
|
declare const discountTableSchema: TargetSchema<DiscountTableRow>;
|
|
12
12
|
//#endregion
|
|
13
|
-
//#region src/mapping/engine.d.ts
|
|
14
|
-
/** One column-map entry. `target` is checked against the schema whitelist separately. */
|
|
15
|
-
declare const mappingColumnSchema: z.ZodObject<{
|
|
16
|
-
/** Source header name (or positional index string for headerless feeds). */source: z.ZodString; /** Target field key — must be in the chosen schema's whitelist (enforced in `buildMappingTransform`). */
|
|
17
|
-
target: z.ZodString; /** Ordered per-column cleaning ops. Defaults to none. */
|
|
18
|
-
transforms: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<"op", [z.ZodObject<{
|
|
19
|
-
op: z.ZodLiteral<"trim">;
|
|
20
|
-
}, "strip", z.ZodTypeAny, {
|
|
21
|
-
op: "trim";
|
|
22
|
-
}, {
|
|
23
|
-
op: "trim";
|
|
24
|
-
}>, z.ZodObject<{
|
|
25
|
-
op: z.ZodLiteral<"uppercase">;
|
|
26
|
-
}, "strip", z.ZodTypeAny, {
|
|
27
|
-
op: "uppercase";
|
|
28
|
-
}, {
|
|
29
|
-
op: "uppercase";
|
|
30
|
-
}>, z.ZodObject<{
|
|
31
|
-
op: z.ZodLiteral<"lowercase">;
|
|
32
|
-
}, "strip", z.ZodTypeAny, {
|
|
33
|
-
op: "lowercase";
|
|
34
|
-
}, {
|
|
35
|
-
op: "lowercase";
|
|
36
|
-
}>, z.ZodObject<{
|
|
37
|
-
op: z.ZodLiteral<"stripPrefix">;
|
|
38
|
-
value: z.ZodString;
|
|
39
|
-
}, "strip", z.ZodTypeAny, {
|
|
40
|
-
value: string;
|
|
41
|
-
op: "stripPrefix";
|
|
42
|
-
}, {
|
|
43
|
-
value: string;
|
|
44
|
-
op: "stripPrefix";
|
|
45
|
-
}>, z.ZodObject<{
|
|
46
|
-
op: z.ZodLiteral<"stripSuffix">;
|
|
47
|
-
value: z.ZodString;
|
|
48
|
-
}, "strip", z.ZodTypeAny, {
|
|
49
|
-
value: string;
|
|
50
|
-
op: "stripSuffix";
|
|
51
|
-
}, {
|
|
52
|
-
value: string;
|
|
53
|
-
op: "stripSuffix";
|
|
54
|
-
}>, z.ZodObject<{
|
|
55
|
-
op: z.ZodLiteral<"defaultIfEmpty">;
|
|
56
|
-
value: z.ZodString;
|
|
57
|
-
}, "strip", z.ZodTypeAny, {
|
|
58
|
-
value: string;
|
|
59
|
-
op: "defaultIfEmpty";
|
|
60
|
-
}, {
|
|
61
|
-
value: string;
|
|
62
|
-
op: "defaultIfEmpty";
|
|
63
|
-
}>, z.ZodObject<{
|
|
64
|
-
op: z.ZodLiteral<"number">;
|
|
65
|
-
}, "strip", z.ZodTypeAny, {
|
|
66
|
-
op: "number";
|
|
67
|
-
}, {
|
|
68
|
-
op: "number";
|
|
69
|
-
}>, z.ZodObject<{
|
|
70
|
-
op: z.ZodLiteral<"europeanNumber">;
|
|
71
|
-
}, "strip", z.ZodTypeAny, {
|
|
72
|
-
op: "europeanNumber";
|
|
73
|
-
}, {
|
|
74
|
-
op: "europeanNumber";
|
|
75
|
-
}>]>, "many">>;
|
|
76
|
-
}, "strip", z.ZodTypeAny, {
|
|
77
|
-
source: string;
|
|
78
|
-
target: string;
|
|
79
|
-
transforms: ({
|
|
80
|
-
op: "trim";
|
|
81
|
-
} | {
|
|
82
|
-
op: "uppercase";
|
|
83
|
-
} | {
|
|
84
|
-
op: "lowercase";
|
|
85
|
-
} | {
|
|
86
|
-
value: string;
|
|
87
|
-
op: "stripPrefix";
|
|
88
|
-
} | {
|
|
89
|
-
value: string;
|
|
90
|
-
op: "stripSuffix";
|
|
91
|
-
} | {
|
|
92
|
-
value: string;
|
|
93
|
-
op: "defaultIfEmpty";
|
|
94
|
-
} | {
|
|
95
|
-
op: "number";
|
|
96
|
-
} | {
|
|
97
|
-
op: "europeanNumber";
|
|
98
|
-
})[];
|
|
99
|
-
}, {
|
|
100
|
-
source: string;
|
|
101
|
-
target: string;
|
|
102
|
-
transforms?: ({
|
|
103
|
-
op: "trim";
|
|
104
|
-
} | {
|
|
105
|
-
op: "uppercase";
|
|
106
|
-
} | {
|
|
107
|
-
op: "lowercase";
|
|
108
|
-
} | {
|
|
109
|
-
value: string;
|
|
110
|
-
op: "stripPrefix";
|
|
111
|
-
} | {
|
|
112
|
-
value: string;
|
|
113
|
-
op: "stripSuffix";
|
|
114
|
-
} | {
|
|
115
|
-
value: string;
|
|
116
|
-
op: "defaultIfEmpty";
|
|
117
|
-
} | {
|
|
118
|
-
op: "number";
|
|
119
|
-
} | {
|
|
120
|
-
op: "europeanNumber";
|
|
121
|
-
})[] | undefined;
|
|
122
|
-
}>;
|
|
123
|
-
type MappingColumn = z.infer<typeof mappingColumnSchema>;
|
|
124
|
-
/**
|
|
125
|
-
* Decimal + thousands separators for the `number` transform.
|
|
126
|
-
*
|
|
127
|
-
* The two MUST differ: `parseNumber` strips every thousands separator FIRST, so a
|
|
128
|
-
* config where `decimal === thousands` (e.g. both `,`) would strip the decimal
|
|
129
|
-
* separator too — `326,88` → `32688`, a silent 100× error rather than a row error.
|
|
130
|
-
* Rejected at save (`assertValidMapping`) AND at run (`mappingConfigSchema`).
|
|
131
|
-
*/
|
|
132
|
-
declare const numberFormatSchema: z.ZodEffects<z.ZodObject<{
|
|
133
|
-
decimal: z.ZodEnum<[".", ","]>;
|
|
134
|
-
thousands: z.ZodEnum<["", ".", ",", " "]>;
|
|
135
|
-
}, "strip", z.ZodTypeAny, {
|
|
136
|
-
decimal: "." | ",";
|
|
137
|
-
thousands: "" | "." | "," | " ";
|
|
138
|
-
}, {
|
|
139
|
-
decimal: "." | ",";
|
|
140
|
-
thousands: "" | "." | "," | " ";
|
|
141
|
-
}>, {
|
|
142
|
-
decimal: "." | ",";
|
|
143
|
-
thousands: "" | "." | "," | " ";
|
|
144
|
-
}, {
|
|
145
|
-
decimal: "." | ",";
|
|
146
|
-
thousands: "" | "." | "," | " ";
|
|
147
|
-
}>;
|
|
148
|
-
/**
|
|
149
|
-
* The portable mapping config — the subset of an `import_mapping` row the engine
|
|
150
|
-
* needs. The enqueuer SNAPSHOTS this into `import_run.params.mapping` so a run
|
|
151
|
-
* records the exact mapping it used (reproducible) and the transform stays a pure
|
|
152
|
-
* function of `(row, ctx)` with no per-row DB I/O.
|
|
153
|
-
*/
|
|
154
|
-
declare const mappingConfigSchema: z.ZodObject<{
|
|
155
|
-
targetSchema: z.ZodString;
|
|
156
|
-
numberFormat: z.ZodEffects<z.ZodObject<{
|
|
157
|
-
decimal: z.ZodEnum<[".", ","]>;
|
|
158
|
-
thousands: z.ZodEnum<["", ".", ",", " "]>;
|
|
159
|
-
}, "strip", z.ZodTypeAny, {
|
|
160
|
-
decimal: "." | ",";
|
|
161
|
-
thousands: "" | "." | "," | " ";
|
|
162
|
-
}, {
|
|
163
|
-
decimal: "." | ",";
|
|
164
|
-
thousands: "" | "." | "," | " ";
|
|
165
|
-
}>, {
|
|
166
|
-
decimal: "." | ",";
|
|
167
|
-
thousands: "" | "." | "," | " ";
|
|
168
|
-
}, {
|
|
169
|
-
decimal: "." | ",";
|
|
170
|
-
thousands: "" | "." | "," | " ";
|
|
171
|
-
}>;
|
|
172
|
-
columnMap: z.ZodArray<z.ZodObject<{
|
|
173
|
-
/** Source header name (or positional index string for headerless feeds). */source: z.ZodString; /** Target field key — must be in the chosen schema's whitelist (enforced in `buildMappingTransform`). */
|
|
174
|
-
target: z.ZodString; /** Ordered per-column cleaning ops. Defaults to none. */
|
|
175
|
-
transforms: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<"op", [z.ZodObject<{
|
|
176
|
-
op: z.ZodLiteral<"trim">;
|
|
177
|
-
}, "strip", z.ZodTypeAny, {
|
|
178
|
-
op: "trim";
|
|
179
|
-
}, {
|
|
180
|
-
op: "trim";
|
|
181
|
-
}>, z.ZodObject<{
|
|
182
|
-
op: z.ZodLiteral<"uppercase">;
|
|
183
|
-
}, "strip", z.ZodTypeAny, {
|
|
184
|
-
op: "uppercase";
|
|
185
|
-
}, {
|
|
186
|
-
op: "uppercase";
|
|
187
|
-
}>, z.ZodObject<{
|
|
188
|
-
op: z.ZodLiteral<"lowercase">;
|
|
189
|
-
}, "strip", z.ZodTypeAny, {
|
|
190
|
-
op: "lowercase";
|
|
191
|
-
}, {
|
|
192
|
-
op: "lowercase";
|
|
193
|
-
}>, z.ZodObject<{
|
|
194
|
-
op: z.ZodLiteral<"stripPrefix">;
|
|
195
|
-
value: z.ZodString;
|
|
196
|
-
}, "strip", z.ZodTypeAny, {
|
|
197
|
-
value: string;
|
|
198
|
-
op: "stripPrefix";
|
|
199
|
-
}, {
|
|
200
|
-
value: string;
|
|
201
|
-
op: "stripPrefix";
|
|
202
|
-
}>, z.ZodObject<{
|
|
203
|
-
op: z.ZodLiteral<"stripSuffix">;
|
|
204
|
-
value: z.ZodString;
|
|
205
|
-
}, "strip", z.ZodTypeAny, {
|
|
206
|
-
value: string;
|
|
207
|
-
op: "stripSuffix";
|
|
208
|
-
}, {
|
|
209
|
-
value: string;
|
|
210
|
-
op: "stripSuffix";
|
|
211
|
-
}>, z.ZodObject<{
|
|
212
|
-
op: z.ZodLiteral<"defaultIfEmpty">;
|
|
213
|
-
value: z.ZodString;
|
|
214
|
-
}, "strip", z.ZodTypeAny, {
|
|
215
|
-
value: string;
|
|
216
|
-
op: "defaultIfEmpty";
|
|
217
|
-
}, {
|
|
218
|
-
value: string;
|
|
219
|
-
op: "defaultIfEmpty";
|
|
220
|
-
}>, z.ZodObject<{
|
|
221
|
-
op: z.ZodLiteral<"number">;
|
|
222
|
-
}, "strip", z.ZodTypeAny, {
|
|
223
|
-
op: "number";
|
|
224
|
-
}, {
|
|
225
|
-
op: "number";
|
|
226
|
-
}>, z.ZodObject<{
|
|
227
|
-
op: z.ZodLiteral<"europeanNumber">;
|
|
228
|
-
}, "strip", z.ZodTypeAny, {
|
|
229
|
-
op: "europeanNumber";
|
|
230
|
-
}, {
|
|
231
|
-
op: "europeanNumber";
|
|
232
|
-
}>]>, "many">>;
|
|
233
|
-
}, "strip", z.ZodTypeAny, {
|
|
234
|
-
source: string;
|
|
235
|
-
target: string;
|
|
236
|
-
transforms: ({
|
|
237
|
-
op: "trim";
|
|
238
|
-
} | {
|
|
239
|
-
op: "uppercase";
|
|
240
|
-
} | {
|
|
241
|
-
op: "lowercase";
|
|
242
|
-
} | {
|
|
243
|
-
value: string;
|
|
244
|
-
op: "stripPrefix";
|
|
245
|
-
} | {
|
|
246
|
-
value: string;
|
|
247
|
-
op: "stripSuffix";
|
|
248
|
-
} | {
|
|
249
|
-
value: string;
|
|
250
|
-
op: "defaultIfEmpty";
|
|
251
|
-
} | {
|
|
252
|
-
op: "number";
|
|
253
|
-
} | {
|
|
254
|
-
op: "europeanNumber";
|
|
255
|
-
})[];
|
|
256
|
-
}, {
|
|
257
|
-
source: string;
|
|
258
|
-
target: string;
|
|
259
|
-
transforms?: ({
|
|
260
|
-
op: "trim";
|
|
261
|
-
} | {
|
|
262
|
-
op: "uppercase";
|
|
263
|
-
} | {
|
|
264
|
-
op: "lowercase";
|
|
265
|
-
} | {
|
|
266
|
-
value: string;
|
|
267
|
-
op: "stripPrefix";
|
|
268
|
-
} | {
|
|
269
|
-
value: string;
|
|
270
|
-
op: "stripSuffix";
|
|
271
|
-
} | {
|
|
272
|
-
value: string;
|
|
273
|
-
op: "defaultIfEmpty";
|
|
274
|
-
} | {
|
|
275
|
-
op: "number";
|
|
276
|
-
} | {
|
|
277
|
-
op: "europeanNumber";
|
|
278
|
-
})[] | undefined;
|
|
279
|
-
}>, "many">;
|
|
280
|
-
}, "strip", z.ZodTypeAny, {
|
|
281
|
-
targetSchema: string;
|
|
282
|
-
numberFormat: {
|
|
283
|
-
decimal: "." | ",";
|
|
284
|
-
thousands: "" | "." | "," | " ";
|
|
285
|
-
};
|
|
286
|
-
columnMap: {
|
|
287
|
-
source: string;
|
|
288
|
-
target: string;
|
|
289
|
-
transforms: ({
|
|
290
|
-
op: "trim";
|
|
291
|
-
} | {
|
|
292
|
-
op: "uppercase";
|
|
293
|
-
} | {
|
|
294
|
-
op: "lowercase";
|
|
295
|
-
} | {
|
|
296
|
-
value: string;
|
|
297
|
-
op: "stripPrefix";
|
|
298
|
-
} | {
|
|
299
|
-
value: string;
|
|
300
|
-
op: "stripSuffix";
|
|
301
|
-
} | {
|
|
302
|
-
value: string;
|
|
303
|
-
op: "defaultIfEmpty";
|
|
304
|
-
} | {
|
|
305
|
-
op: "number";
|
|
306
|
-
} | {
|
|
307
|
-
op: "europeanNumber";
|
|
308
|
-
})[];
|
|
309
|
-
}[];
|
|
310
|
-
}, {
|
|
311
|
-
targetSchema: string;
|
|
312
|
-
numberFormat: {
|
|
313
|
-
decimal: "." | ",";
|
|
314
|
-
thousands: "" | "." | "," | " ";
|
|
315
|
-
};
|
|
316
|
-
columnMap: {
|
|
317
|
-
source: string;
|
|
318
|
-
target: string;
|
|
319
|
-
transforms?: ({
|
|
320
|
-
op: "trim";
|
|
321
|
-
} | {
|
|
322
|
-
op: "uppercase";
|
|
323
|
-
} | {
|
|
324
|
-
op: "lowercase";
|
|
325
|
-
} | {
|
|
326
|
-
value: string;
|
|
327
|
-
op: "stripPrefix";
|
|
328
|
-
} | {
|
|
329
|
-
value: string;
|
|
330
|
-
op: "stripSuffix";
|
|
331
|
-
} | {
|
|
332
|
-
value: string;
|
|
333
|
-
op: "defaultIfEmpty";
|
|
334
|
-
} | {
|
|
335
|
-
op: "number";
|
|
336
|
-
} | {
|
|
337
|
-
op: "europeanNumber";
|
|
338
|
-
})[] | undefined;
|
|
339
|
-
}[];
|
|
340
|
-
}>;
|
|
341
|
-
type MappingConfig = z.infer<typeof mappingConfigSchema>;
|
|
342
|
-
/**
|
|
343
|
-
* Build a `RowTransform` from a validated mapping config + its target schema.
|
|
344
|
-
*
|
|
345
|
-
* Throws at BUILD time if any `column_map` target is outside the schema's
|
|
346
|
-
* whitelist (deny-by-default, R02) — so a misconfigured mapping fails loudly
|
|
347
|
-
* before a single row is written, rather than silently dropping a column.
|
|
348
|
-
*
|
|
349
|
-
* Generic over the schema's output type, so passing the concrete `partsSchema`
|
|
350
|
-
* yields a `RowTransform<PartsDocument>` (the carmaker path keeps its typing); the
|
|
351
|
-
* registry-resolved `TargetSchema<unknown>` yields `RowTransform<unknown>` (the
|
|
352
|
-
* `commerce:mapping` path, where the output type isn't known until runtime).
|
|
353
|
-
*/
|
|
354
|
-
declare function buildMappingTransform<TOut>(config: MappingConfig, schema: TargetSchema<TOut>): RowTransform<TOut>;
|
|
355
|
-
/** The minimal shape `resolveMapping` needs — satisfied by an `import_mapping` row or a test fixture. */
|
|
356
|
-
interface ResolvableMapping {
|
|
357
|
-
/** `null` = supplier default (applies to all brands). */
|
|
358
|
-
brandId: string | null;
|
|
359
|
-
active: boolean;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Most-specific resolution (D002 ladder): given the candidate mappings for one
|
|
363
|
-
* `(supplier, target_schema)` and the requested brand, return the brand-specific
|
|
364
|
-
* mapping if one exists, else the supplier-default (`brandId === null`). Inactive
|
|
365
|
-
* mappings are skipped. Returns `undefined` when nothing matches.
|
|
366
|
-
*/
|
|
367
|
-
declare function resolveMapping<M extends ResolvableMapping>(mappings: readonly M[], brandId: string | null): M | undefined;
|
|
368
|
-
//#endregion
|
|
369
13
|
//#region src/mapping/mapping-transform.d.ts
|
|
370
14
|
/** Stable name under which the mapping-driven transform is registered. */
|
|
371
15
|
declare const MAPPING_TRANSFORM_NAME = "commerce:mapping";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/mapping/discount-table-schema.ts","../../src/mapping/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/mapping/discount-table-schema.ts","../../src/mapping/mapping-transform.ts","../../src/mapping/schema-registry.ts"],"mappings":";;;;;;UAqBiB,gBAAA;EACf,aAAA;EACA,WAAW;AAAA;AAAA,cAQA,mBAAA,EAAqB,YAAY,CAAC,gBAAA;;;;cCClC,sBAAA;;;;ACPb;;cDmCa,gBAAA,EAAkB,YAAY;;;;iBCnC3B,eAAA,CAAgB,IAAA,WAAe,YAAY;AFM3D;AAAA,iBEDgB,iBAAA,CAAA;;UAKC,mBAAA;EACf,IAAA;EACA,MAAA,WAAiB,WAAW;AAAA;;ADL9B;;;;AAAmC;iBCcnB,qBAAA,CAAA,GAAyB,mBAAmB"}
|
package/dist/mapping/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"../parts-schema-QqLci_Pl.mjs";import{a as l,c as u,i as d,n as f,o as p,r as m,s as h,t as g}from"../engine-B0ExE4jL.mjs";import{i as _,n as v,r as y,t as b}from"../schema-registry-DB5aIZMN.mjs";import{n as x,t as S}from"../mapping-transform-CO4MNhMe.mjs";export{p as COLUMN_TRANSFORM_OPS,S as MAPPING_TRANSFORM_NAME,h as applyColumnTransforms,g as buildMappingTransform,u as columnTransformSchema,b as describeTargetSchemas,_ as discountTableSchema,v as getTargetSchema,y as listTargetSchemas,f as mappingColumnSchema,m as mappingConfigSchema,x as mappingTransform,c as normalizeCode,n as numberCell,d as numberFormatSchema,s as parseEuropeanNumber,t as parseNumber,i as partsParamsSchema,o as partsSchema,l as resolveMapping,e as rowError,a as textOrNull,r as toMinorUnits};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as e,t}from"./engine-B0ExE4jL.mjs";import{n,r}from"./schema-registry-DB5aIZMN.mjs";import{z as i}from"zod";const a=`commerce:mapping`,o=i.object({mapping:e}),s=new WeakMap;function c(e){let i=s.get(e);if(i)return i;let{mapping:a}=o.parse(e),c=n(a.targetSchema);if(!c)throw Error(`Unknown target schema "${a.targetSchema}" (registered: ${r().join(`, `)})`);let l=t(a,c);return s.set(e,l),l}const l=async(e,t)=>c(t.params)(e,t);export{l as n,a as t};
|
|
2
|
+
//# sourceMappingURL=mapping-transform-CO4MNhMe.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapping-transform-
|
|
1
|
+
{"version":3,"file":"mapping-transform-CO4MNhMe.mjs","names":[],"sources":["../src/mapping/mapping-transform.ts"],"sourcesContent":["/**\n * `commerce:mapping` — the registered transform for runs driven by a saved\n * `import_mapping` (commerce-demo phase 02; the PR3 wizard enqueues these).\n *\n * The run's `params.mapping` carries a SNAPSHOT of the mapping config, so the run\n * is reproducible (it records the exact mapping used) and the transform stays a\n * pure function of `(row, ctx)` with no per-row DB I/O. This transform compiles\n * the snapshot once per `ctx.params` reference (memoized via a `WeakMap`, the same\n * shape as the carmaker path) and applies it.\n *\n * Output type is `unknown` because the target schema (parts vs discount_table)\n * isn't known until the snapshot is read at runtime. The parts schema additionally\n * reads brand/supplier identity from the SAME `params` object (alongside `mapping`).\n *\n * **Sink caveat.** The `runImport` runner bulk-writes whatever doc this produces\n * into the ES `parts` index. That's correct for a `parts` mapping but WRONG for a\n * `discount_table` mapping — its rows belong in Postgres (`supplier_discount_group`),\n * not ES. PR2 ships the discount_table SCHEMA (so the engine parses it) but no sink;\n * a `discount_table` run must NOT be enqueued through the parts-ES `runImport` path.\n * PR4 wires the Postgres sink + the route that enqueues discount runs.\n *\n * PR2 shipped + tested this transform; the wizard that creates the mappings +\n * sets `params.feed` for the reader landed in PR3. Registration is plugin-owned:\n * the `imports()` plugin registers this transform at its own init.\n */\n\nimport { z } from 'zod'\nimport type { RowTransform } from '../transform.js'\nimport { buildMappingTransform, mappingConfigSchema } from './engine.js'\nimport { getTargetSchema, listTargetSchemas } from './schema-registry.js'\n\n/** Stable name under which the mapping-driven transform is registered. */\nexport const MAPPING_TRANSFORM_NAME = 'commerce:mapping'\n\n/** The mapping snapshot lives under `params.mapping`; other params flow to the schema. */\nconst mappingParamsSchema = z.object({ mapping: mappingConfigSchema })\n\n/** Compiled-transform cache, keyed on the `ctx.params` object identity (per run). */\nconst compiledCache = new WeakMap<object, RowTransform<unknown>>()\n\nfunction compile(params: Record<string, unknown>): RowTransform<unknown> {\n const cached = compiledCache.get(params)\n if (cached) return cached\n const { mapping } = mappingParamsSchema.parse(params)\n const schema = getTargetSchema(mapping.targetSchema)\n if (!schema) {\n throw new Error(\n `Unknown target schema \"${mapping.targetSchema}\" (registered: ${listTargetSchemas().join(', ')})`,\n )\n }\n const transform = buildMappingTransform(mapping, schema)\n compiledCache.set(params, transform)\n return transform\n}\n\n/**\n * The registered transform. Resolves + compiles the mapping snapshot from\n * `ctx.params` (memoized), then delegates each row to it. A malformed snapshot or\n * unknown schema throws — surfaced by the runner as a row-level `transform_threw`.\n */\nexport const mappingTransform: RowTransform<unknown> = async (row, ctx) => {\n const transform = compile(ctx.params)\n return transform(row, ctx)\n}\n"],"mappings":"oHAgCA,MAAa,EAAyB,mBAGhC,EAAsB,EAAE,OAAO,CAAE,QAAS,CAAoB,CAAC,EAG/D,EAAgB,IAAI,QAE1B,SAAS,EAAQ,EAAwD,CACvE,IAAM,EAAS,EAAc,IAAI,CAAM,EACvC,GAAI,EAAQ,OAAO,EACnB,GAAM,CAAE,WAAY,EAAoB,MAAM,CAAM,EAC9C,EAAS,EAAgB,EAAQ,YAAY,EACnD,GAAI,CAAC,EACH,MAAU,MACR,0BAA0B,EAAQ,aAAa,iBAAiB,EAAkB,CAAC,CAAC,KAAK,IAAI,EAAE,EACjG,EAEF,IAAM,EAAY,EAAsB,EAAS,CAAM,EAEvD,OADA,EAAc,IAAI,EAAQ,CAAS,EAC5B,CACT,CAOA,MAAa,EAA0C,MAAO,EAAK,IAC/C,EAAQ,EAAI,MACf,CAAC,CAAC,EAAK,CAAG"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as SNAPSHOT_MAX_ROWS, c as AutoAssignField, i as SNAPSHOT_MAX_COLUMN_CHARS, l as suggestTargets, n as SNAPSHOT_MAX_CELL_CHARS, o as SNAPSHOT_MAX_SERIALIZED_CHARS, r as SNAPSHOT_MAX_COLUMNS, s as toHeaderSnapshot, t as HeaderSample, u as tokenizeHeader } from "../index-Bm69N5DW.mjs";
|
|
2
|
+
export { type AutoAssignField, type HeaderSample, SNAPSHOT_MAX_CELL_CHARS, SNAPSHOT_MAX_COLUMNS, SNAPSHOT_MAX_COLUMN_CHARS, SNAPSHOT_MAX_ROWS, SNAPSHOT_MAX_SERIALIZED_CHARS, suggestTargets, toHeaderSnapshot, tokenizeHeader };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"../matching-Sr0z4HvN.mjs";export{s as SNAPSHOT_MAX_CELL_CHARS,r as SNAPSHOT_MAX_COLUMNS,a as SNAPSHOT_MAX_COLUMN_CHARS,n as SNAPSHOT_MAX_ROWS,e as SNAPSHOT_MAX_SERIALIZED_CHARS,o as suggestTargets,i as toHeaderSnapshot,t as tokenizeHeader};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e){return e.toLowerCase().replace(/[^a-z0-9]/g,``)}function t(e){return e.replace(/([a-z0-9])([A-Z])/g,`$1 $2`).toLowerCase().split(/[^a-z0-9]+/).filter(e=>e!==``).slice(0,12)}function n(e){let t=[];for(let n=0;n<e.length;n++){let r=``;for(let i=n;i<e.length;i++)r+=e[i]??``,t.push(r)}return t}function r(r,i){let a=i.map(t=>{let n=new Set([e(t.key),e(t.label)]);for(let r of t.aliases??[])n.add(e(r));return n.delete(``),n}),o=[];r.forEach((r,i)=>{let s=e(r);if(s===``)return;let c=new Set(n(t(r)));a.forEach((e,t)=>{let n=0;for(let t of e)s===t?n=Math.max(n,1e3+t.length):c.has(t)&&(n=Math.max(n,t.length));n>0&&o.push({columnIndex:i,fieldIndex:t,score:n})})}),o.sort((e,t)=>t.score-e.score||e.fieldIndex-t.fieldIndex||e.columnIndex-t.columnIndex);let s=new Map,c=new Set,l=new Set;for(let e of o){if(c.has(e.columnIndex)||l.has(e.fieldIndex))continue;let t=r[e.columnIndex],n=i[e.fieldIndex];t===void 0||n===void 0||(c.add(e.columnIndex),l.add(e.fieldIndex),s.set(t,n.key))}return s}const i=256,a=256,o=5,s=500,c=1e5;function l(e){let t=e.columns.filter(e=>e.length>0);if(t.length===0||t.length>256||t.some(e=>e.length>256))return null;let n={columns:t,rows:e.rows.slice(0,5).map(e=>{let t={};for(let[n,r]of Object.entries(e))n.length!==0&&(t[n]=u(r));return t})};return JSON.stringify(n).length>1e5?null:n}function u(e){if(e.length<=500)return e;let t=e.slice(0,500),n=t.charCodeAt(t.length-1);return n>=55296&&n<=56319?t.slice(0,-1):t}export{c as a,t as c,o as i,i as n,l as o,a as r,r as s,s as t};
|
|
2
|
+
//# sourceMappingURL=matching-Sr0z4HvN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matching-Sr0z4HvN.mjs","names":[],"sources":["../src/matching/auto-assign.ts","../src/matching/header-snapshot.ts"],"sourcesContent":["/**\n * Auto-assignment of a previewed feed's columns to target fields — the wizard's first guess\n * when an operator uploads a sample file.\n *\n * Kept pure (no React, no server imports) so the matching rules are unit-testable against real\n * supplier headers instead of only being visible by driving the UI.\n *\n * ## Why this is not a substring match\n *\n * The previous rule accepted a match when either string contained the other, which is wrong in\n * both directions at once. It fired where it should not — `Barcode` contains `code`, so the\n * barcode column was offered as the REQUIRED part-code field while the actual code column\n * (`ArticleName`, `partno`) matched nothing — and it stayed silent where it mattered, because\n * no real feed names a column after our internal key. Measured against the three ground-truth\n * supplier samples, it assigned two columns correctly, one wrongly, and missed the rest,\n * including every supersession column. A missed price column is loud; a missed supersession\n * column is silent (prices import, the run succeeds, the replacement tables stay empty), which\n * is how the supersession feature shipped switched-off and stayed that way.\n *\n * ## The rules\n *\n * 1. A field's candidate spellings are its `key`, its `label`, and its declared `aliases`, each\n * reduced to lowercase alphanumerics.\n * 2. A column matches a candidate only on a TOKEN BOUNDARY — the header is split on\n * punctuation, whitespace and camelCase humps, and a candidate must equal a contiguous run\n * of those tokens. So `Net Price EUR` and `NetPrice/Discount` still reach `net_price`, while\n * `Barcode` can no longer reach `code`, because `code` is not one of its tokens.\n * 3. Every (column, field) match competes globally and the strongest wins: a whole-header hit\n * beats a partial one, and a longer candidate beats a shorter one. Assignment is greedy over\n * that order, so `Description1` claims `description1` even when a plain `Description` column\n * sits earlier in the file.\n * 4. One target is used at most once. Two columns claiming the same field would be rejected by\n * the mapping validator at save time, so the weaker claimant is left unassigned for the\n * operator to resolve rather than silently producing an unsaveable mapping.\n */\n\n/** The subset of a target field this matcher reads. */\nexport interface AutoAssignField {\n key: string\n label: string\n aliases?: readonly string[] | undefined\n}\n\n/** Lowercase alphanumerics — the comparison form for both headers and candidates. */\nfunction normalize(s: string): string {\n return s.toLowerCase().replace(/[^a-z0-9]/g, '')\n}\n\n/**\n * Tokens considered per header. Matching enumerates every contiguous run of tokens, which is\n * quadratic in their count AND in their total length, so this cap is load-bearing rather than\n * tidy: a mis-detected delimiter collapses an entire line — sometimes an entire file — into ONE\n * \"column\", and this runs in the operator's browser the moment a preview returns. Measured\n * uncapped: 500 tokens → 207 MB of run strings and 0.6s; 1,000 → 1.7 GB and 5.4s; 2,000 → 14 GB\n * and 40s, all retained in a Set. The wizard has a dedicated banner for exactly that mistake\n * (`delimiterSuggestion`), but auto-assign runs before it can paint. No real column header is\n * a dozen words long, so capping costs nothing and bounds the work at ~78 runs per column.\n */\nconst MAX_HEADER_TOKENS = 12\n\n/**\n * Split a header into comparison tokens: on punctuation/whitespace, and on camelCase humps so\n * `ArticleName` → `article` + `name` and `CurrencyCd` → `currency` + `cd`. Truncated to\n * {@link MAX_HEADER_TOKENS}.\n *\n * **Acronym boundaries are deliberately NOT split.** Adding `([A-Z]+)([A-Z][a-z])` looks like a\n * free improvement — it makes `EANCode` read as `ean` + `code` — and it re-opens this module's\n * founding bug: the standalone `code` token then outscores `barcode`'s `ean` alias (4 vs 3), so\n * the BARCODE column gets offered as the required part-code field, silently, exactly as the old\n * substring matcher did. It generalises to any `<Acronym>Code` header, which no alias list can\n * enumerate. Compound spellings that matter are handled as whole-header aliases instead, which\n * beat any partial run.\n */\nexport function tokenizeHeader(header: string): string[] {\n return header\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((t) => t !== '')\n .slice(0, MAX_HEADER_TOKENS)\n}\n\n/** Every contiguous run of tokens, joined — the strings a candidate may equal. */\nfunction tokenRuns(tokens: readonly string[]): string[] {\n const runs: string[] = []\n for (let start = 0; start < tokens.length; start++) {\n let joined = ''\n for (let end = start; end < tokens.length; end++) {\n joined += tokens[end] ?? ''\n runs.push(joined)\n }\n }\n return runs\n}\n\ninterface Candidate {\n columnIndex: number\n fieldIndex: number\n /** Whole-header hits outrank partial ones; longer candidates outrank shorter ones. */\n score: number\n}\n\n/**\n * Suggest a target field for each column. Returns `column → target key`, omitting any column\n * with no confident match — an absent entry means \"the operator decides\", never \"no mapping\n * needed\".\n */\nexport function suggestTargets(\n columns: readonly string[],\n fields: readonly AutoAssignField[],\n): Map<string, string> {\n const candidatesByField = fields.map((f) => {\n const spellings = new Set<string>([normalize(f.key), normalize(f.label)])\n for (const alias of f.aliases ?? []) spellings.add(normalize(alias))\n spellings.delete('')\n return spellings\n })\n\n const matches: Candidate[] = []\n columns.forEach((column, columnIndex) => {\n const full = normalize(column)\n if (full === '') return\n const runs = new Set(tokenRuns(tokenizeHeader(column)))\n candidatesByField.forEach((spellings, fieldIndex) => {\n let best = 0\n for (const spelling of spellings) {\n // A whole-header hit is worth more than a run hit of the same length, so a column\n // named exactly `code` beats one that merely contains the token.\n if (full === spelling) best = Math.max(best, 1000 + spelling.length)\n else if (runs.has(spelling)) best = Math.max(best, spelling.length)\n }\n if (best > 0) matches.push({ columnIndex, fieldIndex, score: best })\n })\n })\n\n // Strongest first; ties resolved by declaration order then file order so the result is\n // independent of Set/array iteration incidentals.\n matches.sort(\n (a, b) => b.score - a.score || a.fieldIndex - b.fieldIndex || a.columnIndex - b.columnIndex,\n )\n\n const assigned = new Map<string, string>()\n const takenColumns = new Set<number>()\n const takenFields = new Set<number>()\n for (const m of matches) {\n if (takenColumns.has(m.columnIndex) || takenFields.has(m.fieldIndex)) continue\n const column = columns[m.columnIndex]\n const field = fields[m.fieldIndex]\n if (column === undefined || field === undefined) continue\n takenColumns.add(m.columnIndex)\n takenFields.add(m.fieldIndex)\n assigned.set(column, field.key)\n }\n return assigned\n}\n","/**\n * The parsed-sample shape this builder consumes and emits — structurally identical to\n * `FeedPreview` from `preview-feed.ts` and to the wizard's `FeedPreviewResponse`, and\n * declared HERE rather than imported from either so this module keeps zero dependencies.\n * That is what lets one implementation serve the browser wizard and the `lumi` CLI: an\n * import of `preview-feed.js` would pull `csv-parse` and the xlsx reader toward a client\n * bundle, and an import of the wizard's types would point the dependency the wrong way.\n */\nexport interface HeaderSample {\n /** Source column names, in first-seen order. */\n columns: string[]\n /** Sample data rows, each `{ column -> cell }`. */\n rows: Array<Record<string, string>>\n}\n\n// Snapshot caps — mirror `../entities/import-mapping-validation.ts`'s\n// `headerSnapshotSchema` + `HEADER_SNAPSHOT_MAX_CHARS` ceiling, so a snapshot this\n// builder emits always passes the server's save validation.\n//\n// Still deliberately duplicated rather than imported, and the reason has changed with\n// the module's address: it is no longer \"don't drag server code into the browser\" (this\n// file now lives in the server package), it is that `import-mapping-validation.ts` pulls\n// zod and the schema registry, which would end this subpath's zero-dependency property —\n// the one thing that lets the wizard and the CLI share ONE matcher. The contract is\n// pinned the way it always was, by the round-trip test in `header-snapshot.test.ts`\n// feeding this builder's output through the real `assertValidMapping`.\nexport const SNAPSHOT_MAX_COLUMNS = 256\nexport const SNAPSHOT_MAX_COLUMN_CHARS = 256\nexport const SNAPSHOT_MAX_ROWS = 5\nexport const SNAPSHOT_MAX_CELL_CHARS = 500\nexport const SNAPSHOT_MAX_SERIALIZED_CHARS = 100_000\n\n/**\n * Build the file-less-edit `headerSnapshot` (#667) from the current preview.\n * Rows are capped + cells truncated (they're display samples, not data); a\n * preview that can't fit the caps at all (pathological column count / column\n * names) returns `null` — the mapping saves fine, it just won't support\n * file-less editing.\n *\n * Pure module (no React, no 'use client') so the caps mirror is unit-testable\n * without page infrastructure — same shape as `find-saved-mapping.ts`.\n */\nexport function toHeaderSnapshot(preview: HeaderSample): HeaderSample | null {\n // Drop blank-named columns rather than rejecting the snapshot: csv-parse\n // treats '' as a valid column name, so a header with a trailing delimiter or\n // blank middle cell (`code,price,`) reaches here from perfectly ordinary\n // feeds — while the server schema (rightly) refuses empty names. A blank\n // column is unmappable anyway (columnMap sources require min-1 names).\n const columns = preview.columns.filter((c) => c.length > 0)\n if (\n columns.length === 0 ||\n columns.length > SNAPSHOT_MAX_COLUMNS ||\n columns.some((c) => c.length > SNAPSHOT_MAX_COLUMN_CHARS)\n ) {\n return null\n }\n const snapshot: HeaderSample = {\n columns,\n rows: preview.rows.slice(0, SNAPSHOT_MAX_ROWS).map((row) => {\n const out: Record<string, string> = {}\n for (const [col, cell] of Object.entries(row)) {\n if (col.length === 0) continue // cell of a dropped blank column\n out[col] = truncateCell(cell)\n }\n return out\n }),\n }\n // Mirror the server's serialized ceiling too — without this, a wide feed\n // with long sample cells (~40+ columns × 500-char cells) passes the per-axis\n // caps here but hard-fails the SAVE server-side with an error the operator\n // can't act on. Over the ceiling → no file-less support, save proceeds.\n if (JSON.stringify(snapshot).length > SNAPSHOT_MAX_SERIALIZED_CHARS) return null\n return snapshot\n}\n\n/**\n * Truncate a sample cell to the cap WITHOUT splitting a surrogate pair: a\n * `.slice()` cut through an astral character (emoji in a product name) leaves\n * a trailing lone high surrogate, which zod accepts but Postgres rejects as\n * jsonb input — the save would die at the DB layer instead of degrading.\n */\nfunction truncateCell(cell: string): string {\n if (cell.length <= SNAPSHOT_MAX_CELL_CHARS) return cell\n const cut = cell.slice(0, SNAPSHOT_MAX_CELL_CHARS)\n const last = cut.charCodeAt(cut.length - 1)\n const endsWithLoneHighSurrogate = last >= 0xd800 && last <= 0xdbff\n return endsWithLoneHighSurrogate ? cut.slice(0, -1) : cut\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { p as TargetSchema } from "./engine-DiM4r2Vj.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { PartsDocument } from "@murumets-ee/search-elasticsearch";
|
|
4
|
+
|
|
5
|
+
//#region src/mapping/parts-schema.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Params the `parts` schema reads from the run — the per-(supplier, brand)
|
|
8
|
+
* identity resolved at enqueue time. Identical to the carmaker transform's params
|
|
9
|
+
* (the carmaker path re-exports this), so brand/supplier injection + the optional
|
|
10
|
+
* supplier code-prefix strip are validated the same way everywhere.
|
|
11
|
+
*/
|
|
12
|
+
declare const partsParamsSchema: z.ZodObject<{
|
|
13
|
+
brandId: z.ZodString;
|
|
14
|
+
brandSlug: z.ZodString;
|
|
15
|
+
supplierId: z.ZodString;
|
|
16
|
+
supplierDisplayName: z.ZodString;
|
|
17
|
+
importBatchId: z.ZodString; /** Supplier code prefix to strip before normalization (e.g. `'ME-'`). Optional. */
|
|
18
|
+
supplierCodePrefix: z.ZodOptional<z.ZodString>;
|
|
19
|
+
/**
|
|
20
|
+
* Purchase-discount map for this supplier (commerce-demo PR4, phase 03 §2):
|
|
21
|
+
* `{ groupCode → discountPct }`, resolved from `supplier_discount_group` at
|
|
22
|
+
* enqueue time and snapshotted onto the run so `buildRow` stays a pure function
|
|
23
|
+
* (no per-row DB I/O; `@murumets-ee/imports` stays a leaf). Absent for direct-net
|
|
24
|
+
* suppliers + the carmaker path. Percentages are 0–100 (the entity constrains them).
|
|
25
|
+
*/
|
|
26
|
+
discountGroups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
supplierId: string;
|
|
29
|
+
brandId: string;
|
|
30
|
+
brandSlug: string;
|
|
31
|
+
supplierDisplayName: string;
|
|
32
|
+
importBatchId: string;
|
|
33
|
+
supplierCodePrefix?: string | undefined;
|
|
34
|
+
discountGroups?: Record<string, number> | undefined;
|
|
35
|
+
}, {
|
|
36
|
+
supplierId: string;
|
|
37
|
+
brandId: string;
|
|
38
|
+
brandSlug: string;
|
|
39
|
+
supplierDisplayName: string;
|
|
40
|
+
importBatchId: string;
|
|
41
|
+
supplierCodePrefix?: string | undefined;
|
|
42
|
+
discountGroups?: Record<string, number> | undefined;
|
|
43
|
+
}>;
|
|
44
|
+
type PartsParams = z.infer<typeof partsParamsSchema>;
|
|
45
|
+
/**
|
|
46
|
+
* Uppercase + dash/whitespace-stripped (D2). `*`/other chars are preserved (e.g. `*S`
|
|
47
|
+
* markers). EXPORTED so downstream consumers that key off `code_normalized` — the
|
|
48
|
+
* replacement editor (commerce-demo PR6), which normalizes admin-typed codes before
|
|
49
|
+
* matching members — use the IDENTICAL rule (R05 Q7: normalization must match across
|
|
50
|
+
* every code source or merge-on-collision silently fails to collide).
|
|
51
|
+
*/
|
|
52
|
+
declare function normalizeCode(s: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* The `parts` schema. `buildRow` THROWS on invalid run params (so a misconfigured
|
|
55
|
+
* run surfaces as a transform error, matching the carmaker contract) and returns a
|
|
56
|
+
* typed `RowResult` per row otherwise.
|
|
57
|
+
*/
|
|
58
|
+
declare const partsSchema: TargetSchema<PartsDocument>;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { partsSchema as i, normalizeCode as n, partsParamsSchema as r, PartsParams as t };
|
|
61
|
+
//# sourceMappingURL=parts-schema-DOWhBJYh.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parts-schema-DOWhBJYh.d.mts","names":[],"sources":["../src/mapping/parts-schema.ts"],"mappings":";;;;;;;;;;;cAmDa,iBAAA,EAAiB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqBlB,WAAA,GAAc,CAAA,CAAE,KAAK,QAAQ,iBAAA;;;;;AAAzC;;;iBAsMgB,aAAA,CAAc,CAAS;;;;;AAtMmB;cA6R7C,WAAA,EAAa,YAAY,CAAC,aAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{z as e}from"zod";const t=/^-?\d+(\.\d+)?$/;function n(e,n){let r=e.trim();if(r===``||r.toUpperCase()===`NA`)return null;let{decimal:i,thousands:a}=n,o=r;if(a!==``&&(o=o.split(a).join(``)),i===`,`){if(o.includes(`.`))return NaN;o=o.split(`,`).join(`.`)}else if(o.includes(`,`))return NaN;return t.test(o)?Number.parseFloat(o):NaN}function r(e){return Math.round(e*100)}function i(e){let n=e.trim();if(n===``||n.toUpperCase()===`NA`)return null;let r=n.includes(`,`),i=n.includes(`.`),a;if(r&&i)a=n.replace(/\./g,``).replace(`,`,`.`);else if(r)a=n.replace(`,`,`.`);else if(i)return NaN;else a=n;return t.test(a)?Number.parseFloat(a):NaN}function a(e){if(typeof e!=`string`)return null;let t=e.trim();return t===``?null:t}function o(e){return e==null?{kind:`absent`}:typeof e==`number`?Number.isNaN(e)?{kind:`invalid`}:{kind:`value`,value:e}:e.trim()===``?{kind:`absent`}:{kind:`invalid`}}function s(e,t,n){return{kind:`error`,error:{errorType:e,message:t,field:n}}}const c=e.object({brandId:e.string().uuid(),brandSlug:e.string().min(1).max(64),supplierId:e.string().uuid(),supplierDisplayName:e.string().min(1).max(255),importBatchId:e.string().uuid(),supplierCodePrefix:e.string().max(8).regex(/^[A-Z0-9-]+$/,`must be uppercase alphanumeric or "-"`).optional(),discountGroups:e.record(e.string(),e.number().min(0).max(100)).optional()}),l=new WeakMap;function u(e){let t=l.get(e);if(t)return t;let n=c.parse(e);return l.set(e,n),n}const d=[`de`,`en`,`es`,`fr`,`it`,`nl`,`pt`,`ro`],f=e=>`name_${e}`,p=[{key:`code`,label:`Code`,required:!0,type:`string`,aliases:[`part no`,`part number`,`article name`,`article no`,`article number`,`sku`,`varuosa kood`,`kood`,`artikelnummer`,`artikel nr`]},{key:`net_price`,label:`Net price`,required:!1,type:`number`,aliases:[`price`,`net price discount`,`nettopreis`,`netto`]},{key:`gross_price`,label:`Gross price`,required:!1,type:`number`,aliases:[`bruttopreis`,`brutto`]},{key:`list_price`,label:`List price`,required:!1,type:`number`,aliases:[`retail price`,`rrp`,`jaehind`,`jaehind km ta`,`listenpreis`]},{key:`currency`,label:`Currency`,required:!1,type:`string`,aliases:[`currency cd`,`currency code`,`valuuta`]},{key:`barcode`,label:`Barcode`,required:!1,type:`string`,aliases:[`ean`,`ean code`,`gtin`,`gtin code`,`triipkood`]},{key:`description1`,label:`Description 1`,required:!1,type:`string`,aliases:[`description`,`kirjeldus`,`beschreibung`]},{key:`description2`,label:`Description 2`,required:!1,type:`string`},{key:`discount_group`,label:`Discount group`,required:!1,type:`string`,aliases:[`product group`,`tootegrupp`,`rabattgruppe`]},{key:`superseding_code`,label:`Replaced by (newer code)`,required:!1,type:`string`,aliases:[`replaced by`,`successor`,`successor code`,`new code`,`ersetzt durch`,`wird ersetzt durch`,`nachfolgenummer`,`vervangen door`]},{key:`replaces_code`,label:`Replaces (older code)`,required:!1,type:`string`,aliases:[`replaces`,`supersedes`,`old code`,`previous code`,`predecessor`,`vorgaengernummer`]},{key:`core_charge`,label:`Core charge`,required:!1,type:`number`,aliases:[`surcharge`,`deposit`,`core deposit`,`korpusetasu`,`pfand`]},...d.map(e=>({key:f(e),label:`Name (${e})`,required:!1,type:`string`,aliases:[`description ${e}`,`name ${e}`]}))],m=/[\s-]/g;function h(e){return e.toUpperCase().replace(m,``)}function g(e,t){let n=a(e);if(n===null)return null;let r=h(t&&n.startsWith(t)?n.slice(t.length):n);return r===``?null:r}function _(e,t){return Math.round(e*(1-t/100))}function v(e){return e===null?{core_charge_minor:null,core_charge_currency:null,core_charge_refundable:null,core_charge_tax_class:null,core_charge_tax_inclusive:null}:{core_charge_minor:e,core_charge_currency:`EUR`,core_charge_refundable:!0,core_charge_tax_class:null,core_charge_tax_inclusive:!1}}const y={name:`parts`,fields:p,buildRow(e,t){let n=u(t.params),i=typeof e.code==`string`?e.code.trim():``;if(!i)return s(`missing_code`,`code is empty`,`code`);let c=h(n.supplierCodePrefix&&i.startsWith(n.supplierCodePrefix)?i.slice(n.supplierCodePrefix.length):i);if(!c)return s(`empty_normalized_code`,`Code "${i}" is empty after normalization`,`code`);let l=a(e.currency)??`EUR`;if(l!==`EUR`)return s(`unsupported_currency`,`Currency "${l}" not supported (EUR-only)`,`currency`);if(c.endsWith(`*S`)){let t=c.slice(0,-2);if(!t)return s(`core_charge_no_parent`,`Core-fee row "${i}" has no part code before the "*S" marker`,`code`);let a=o(e.net_price);if(a.kind===`invalid`)return s(`invalid_core_charge`,`core-fee (*S) row net_price is not a number`,`net_price`);if(a.kind===`absent`)return s(`missing_core_charge`,`core-fee (*S) row has no net_price to use as the deposit`,`net_price`);let l=r(a.value);return l<=0?{kind:`skip`,reason:`core_fee_zero`}:{kind:`skip`,reason:`core_charge_marker`,patches:[{id:`${t}__${n.supplierId}`,doc:v(l),orphanWarning:{warningType:`orphan_core_charge`,message:`core-fee (*S) row references a part not present in this import`,field:`code`}}]}}let d=o(e.net_price);if(d.kind===`invalid`)return s(`invalid_net_price`,`net_price is not a number`,`net_price`);let f=o(e.gross_price);if(f.kind===`invalid`)return s(`invalid_gross_price`,`gross_price is not a number`,`gross_price`);let p=o(e.list_price);if(p.kind===`invalid`)return s(`invalid_list_price`,`list_price is not a number`,`list_price`);let m=d.kind===`value`?d.value:null,y=p.kind===`value`?p.value:null,b=m??y;if(b===null)return s(`missing_price`,`no net_price or list_price provided`,`price`);let x=a(e.discount_group),S=y===null?null:r(y),C=null,w=[];if(m!==null)C=r(m);else if(S!==null){let e=x===null?void 0:n.discountGroups?.[x];e===void 0?w.push({warningType:`no_purchase_price`,message:x===null?`list price present but no discount group on the row — cost pending`:`no discount group "${x}" configured for this supplier — cost pending`,field:`discount_group`}):C=_(S,e)}let T=[],E=g(e.superseding_code,n.supplierCodePrefix);E!==null&&E!==c&&T.push({from:c,to:E});let D=g(e.replaces_code,n.supplierCodePrefix);D!==null&&D!==c&&T.push({from:D,to:c});let O=o(e.core_charge);if(O.kind===`invalid`)return s(`invalid_core_charge`,`core_charge is not a number`,`core_charge`);let k=O.kind===`value`?r(O.value):null,A=k!==null&&k>0?k:null,j=`${c}__${n.supplierId}`;return{kind:`success`,id:j,doc:{doc_id:j,code:i,code_normalized:c,brand_id:n.brandId,brand_slug:n.brandSlug,supplier_id:n.supplierId,supplier_display_name:n.supplierDisplayName,net_price_eur:b,gross_price_eur:f.kind===`value`?f.value:null,currency:`EUR`,barcode:a(e.barcode),name_de:a(e.name_de),name_en:a(e.name_en),name_es:a(e.name_es),name_fr:a(e.name_fr),name_it:a(e.name_it),name_nl:a(e.name_nl),name_pt:a(e.name_pt),name_ro:a(e.name_ro),description1:a(e.description1),description2:a(e.description2),list_price_minor:S,discount_group:x,superseding_code:a(e.superseding_code),purchase_price_minor:C,...v(A),import_batch_id:n.importBatchId,imported_at:new Date().toISOString()},...w.length>0&&{warnings:w},...T.length>0&&{pairs:T},...C!==null&&{priceSample:C}}}};export{s as a,n as c,o as i,r as l,c as n,a as o,y as r,i as s,h as t};
|
|
2
|
+
//# sourceMappingURL=parts-schema-QqLci_Pl.mjs.map
|