@uipath/vertical-solutions-tool 1.204.0 → 1.205.0-preview.258

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +80 -0
  2. package/dist/THIRD-PARTY-NOTICES.md +1617 -369
  3. package/dist/createSdk-2f9x47em.js +12 -0
  4. package/dist/deploy-jobs-3k87fkwz.js +23 -0
  5. package/dist/deploy-service-qaykt7er.js +1779 -0
  6. package/dist/deployment-search-arbm2z06.js +2649 -0
  7. package/dist/embedded-file-reader-r66242ey.js +114 -0
  8. package/dist/first-party-service-srxaftkn.js +27 -0
  9. package/dist/index-7a3eqtxv.js +3174 -0
  10. package/dist/index-8s9dxpj4.js +18812 -0
  11. package/dist/index-fyc0k5dd.js +51405 -0
  12. package/dist/index-h43sj8at.js +23075 -0
  13. package/dist/index-kmp9fmhb.js +25 -0
  14. package/dist/index-ngs0a2he.js +15 -0
  15. package/dist/index.js +14 -3
  16. package/dist/index.umd.min-fqefd9kn.js +7814 -0
  17. package/dist/job-commands-azgbrbdb.js +23 -0
  18. package/dist/job-output-t43hzj1f.js +36 -0
  19. package/dist/prompts-dtgt183n.js +260 -0
  20. package/dist/resume-service-mqfnnzb5.js +344 -0
  21. package/dist/seed-service-2d1f7zaz.js +2014 -0
  22. package/dist/seed-templates-sqqqratr.js +14 -0
  23. package/dist/service-errors-jzbq3z7p.js +22 -0
  24. package/dist/solution-auth-zq6gyxsv.js +11 -0
  25. package/dist/solution-deployments-m44fyb88.js +33 -0
  26. package/dist/templates-85yrztpk.js +15 -0
  27. package/dist/tool-342sz3hm.js +439 -0
  28. package/dist/tool-38ad77wd.js +1472 -0
  29. package/dist/tool-4z1dy1rn.js +260 -0
  30. package/dist/tool-53w8skv5.js +262 -0
  31. package/dist/tool-67587985.js +554 -0
  32. package/dist/tool-87e2ps7f.js +13 -0
  33. package/dist/tool-cag2z2d6.js +30 -0
  34. package/dist/tool-e4qgwf9h.js +2281 -0
  35. package/dist/tool-gdtpsdn7.js +342 -0
  36. package/dist/tool-gvmshnq9.js +555 -0
  37. package/dist/tool-h4f967yd.js +73 -0
  38. package/dist/tool-hdxtgckw.js +249 -0
  39. package/dist/tool-jjxbj28g.js +17067 -0
  40. package/dist/tool-keqjsxdx.js +28 -0
  41. package/dist/tool-maw0g02p.js +24 -0
  42. package/dist/tool-mn1wkbhw.js +113 -0
  43. package/dist/tool-n35nrs32.js +4949 -0
  44. package/dist/tool-p589v2r5.js +114 -0
  45. package/dist/tool-pz9qm3dv.js +6723 -0
  46. package/dist/tool-r1ewa0tf.js +5645 -0
  47. package/dist/tool-r2epw08s.js +164 -0
  48. package/dist/tool-rr9fensp.js +12 -0
  49. package/dist/tool-s04z9t80.js +19 -0
  50. package/dist/tool-s9qbxa2g.js +638 -0
  51. package/dist/tool-tjhwq0ez.js +261 -0
  52. package/dist/tool-v15vxc4x.js +183 -0
  53. package/dist/tool-vf2mgth0.js +14286 -0
  54. package/dist/tool-vp7rjr2q.js +22083 -0
  55. package/dist/tool-xd2ejt3h.js +103 -0
  56. package/dist/tool-yx29vb63.js +672 -0
  57. package/dist/tool.js +12 -2
  58. package/dist/watch-job-wh6m7xzq.js +31 -0
  59. package/package.json +3 -3
  60. package/dist/tool-xymb3dsq.js +0 -45085
@@ -0,0 +1,2281 @@
1
+ import {
2
+ SEED_CONTEXT_FILE,
3
+ entityNameProblem,
4
+ insideSeedRoot,
5
+ isDanglingLink,
6
+ isInsideWorkspace,
7
+ metadataIsItsOwnFile,
8
+ namesSeedRoot,
9
+ packageDir,
10
+ packageNameProblem,
11
+ pathHasExpectedTarget,
12
+ workspaceRoot,
13
+ writeFileByRename
14
+ } from "./tool-s9qbxa2g.js";
15
+ import {
16
+ writeWarning
17
+ } from "./tool-v15vxc4x.js";
18
+ import {
19
+ validateServiceUrl
20
+ } from "./tool-xd2ejt3h.js";
21
+ import {
22
+ encodeSegment,
23
+ scopeQuery,
24
+ vssRequestParsed
25
+ } from "./tool-4z1dy1rn.js";
26
+ import {
27
+ anyNumber,
28
+ jsonObject,
29
+ optionalText,
30
+ plainRecord
31
+ } from "./tool-87e2ps7f.js";
32
+ import {
33
+ exports_external
34
+ } from "./tool-vf2mgth0.js";
35
+ import {
36
+ catchError,
37
+ extractErrorMessage
38
+ } from "./tool-pz9qm3dv.js";
39
+ import {
40
+ getFileSystem
41
+ } from "./tool-hdxtgckw.js";
42
+ import {
43
+ __require
44
+ } from "./tool-1yh8g550.js";
45
+
46
+ // src/api/config-keys.ts
47
+ var RESERVED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
48
+ function configKeyProblem(key) {
49
+ if (key.trim() === "") {
50
+ return "a configuration key cannot be empty.";
51
+ }
52
+ if (RESERVED_KEYS.has(key)) {
53
+ return `"${key}" is not a valid configuration key.`;
54
+ }
55
+ return;
56
+ }
57
+
58
+ // src/api/manifest-sections.ts
59
+ var ConfigurationDeclarationSchema = exports_external.looseObject({
60
+ isRequired: exports_external.unknown().optional().transform((value) => value !== false),
61
+ isSecret: exports_external.unknown().optional().transform((value) => value !== undefined && value !== false),
62
+ description: optionalText,
63
+ value: optionalText
64
+ });
65
+ var ManifestConfigurationsSchema = jsonObject.transform((declared) => {
66
+ const declarations = new Map;
67
+ for (const [key, meta] of Object.entries(declared)) {
68
+ const parsed = ConfigurationDeclarationSchema.safeParse(meta);
69
+ if (parsed.success)
70
+ declarations.set(key, parsed.data);
71
+ }
72
+ return declarations;
73
+ }).catch(() => new Map);
74
+ var PropertyMetaSchema = exports_external.looseObject({
75
+ description: optionalText,
76
+ default: exports_external.unknown().optional(),
77
+ required: exports_external.unknown().optional(),
78
+ value: exports_external.unknown().optional()
79
+ });
80
+ var RequiredSchema = exports_external.union([
81
+ exports_external.boolean().transform((value) => ({ value, fromText: false })),
82
+ exports_external.string().trim().toLowerCase().pipe(exports_external.enum(["true", "false"])).transform((text) => ({ value: text === "true", fromText: true }))
83
+ ]).optional();
84
+ var DefaultSchema = exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()]).transform(String).nullish();
85
+ var DeclaredConfigurationSchema = exports_external.unknown().transform(parseDeclared).optional();
86
+ function parseDeclared(declaration) {
87
+ const properties = [];
88
+ const warnings = [];
89
+ const resources = jsonObject.safeParse(declaration);
90
+ if (!resources.success) {
91
+ warnings.push("Ignoring solution.configuration in the manifest: it is not an object.");
92
+ return { properties, warnings };
93
+ }
94
+ for (const [resourceName, resource] of Object.entries(resources.data)) {
95
+ const declared = jsonObject.safeParse(resource);
96
+ if (!declared.success) {
97
+ warnings.push(`Ignoring solution.configuration.${resourceName}: it is not an object.`);
98
+ continue;
99
+ }
100
+ for (const [propertyName, meta] of Object.entries(declared.data)) {
101
+ const parsed = toDeclaredProperty(resourceName, propertyName, meta, warnings);
102
+ if (parsed)
103
+ properties.push(parsed);
104
+ }
105
+ }
106
+ return { properties, warnings };
107
+ }
108
+ function toDeclaredProperty(resourceName, propertyName, meta, warnings) {
109
+ const problem = configKeyProblem(propertyName);
110
+ if (problem) {
111
+ warnings.push(`Ignoring ${resourceName}.${propertyName}: ${problem}`);
112
+ return;
113
+ }
114
+ const parsed = PropertyMetaSchema.safeParse(meta);
115
+ if (!parsed.success) {
116
+ return {
117
+ resourceName,
118
+ propertyName,
119
+ required: false,
120
+ hasManifestValue: false
121
+ };
122
+ }
123
+ const label = `${resourceName}.${propertyName}`;
124
+ return {
125
+ resourceName,
126
+ propertyName,
127
+ description: parsed.data.description,
128
+ defaultValue: readDefault(parsed.data.default, label, warnings),
129
+ required: readRequired(parsed.data.required, label, warnings),
130
+ hasManifestValue: hasValue(parsed.data.value)
131
+ };
132
+ }
133
+ function hasValue(value) {
134
+ if (typeof value === "string")
135
+ return value.trim() !== "";
136
+ return typeof value === "number" || typeof value === "boolean";
137
+ }
138
+ function readRequired(value, label, warnings) {
139
+ const parsed = RequiredSchema.safeParse(value);
140
+ if (!parsed.success) {
141
+ warnings.push(`Configuration ${label}: ignoring 'required' — expected true or false, got ${describeType(value)}.`);
142
+ return false;
143
+ }
144
+ if (parsed.data?.fromText) {
145
+ warnings.push(`Configuration ${label}: 'required' should be a boolean, not a string.`);
146
+ }
147
+ return parsed.data?.value === true;
148
+ }
149
+ function readDefault(value, label, warnings) {
150
+ const parsed = DefaultSchema.safeParse(value);
151
+ if (parsed.success)
152
+ return parsed.data ?? undefined;
153
+ warnings.push(`Ignoring the default for ${label}: only text, numbers and true/false can be configuration values.`);
154
+ return;
155
+ }
156
+ function describeType(value) {
157
+ if (value === null)
158
+ return "null";
159
+ if (Array.isArray(value))
160
+ return "a list";
161
+ if (typeof value === "object")
162
+ return "an object";
163
+ return `a ${typeof value}`;
164
+ }
165
+ var SeedingEntrySchema = exports_external.looseObject({ strategy: optionalText });
166
+ var SeedingSchema = jsonObject.transform((declared) => {
167
+ const entities = Object.create(null);
168
+ for (const [name, entry] of Object.entries(declared)) {
169
+ const parsed = SeedingEntrySchema.safeParse(entry);
170
+ Object.defineProperty(entities, name, {
171
+ value: parsed.success ? parsed.data : {},
172
+ writable: true,
173
+ enumerable: true,
174
+ configurable: true
175
+ });
176
+ }
177
+ return entities;
178
+ }).optional().catch(undefined);
179
+ var DeclaredNamesSchema = jsonObject.transform((declared) => Object.keys(declared)).optional().catch(undefined);
180
+ var ConnectionEntrySchema = exports_external.looseObject({ connector: optionalText });
181
+ var DeclaredConnectionsSchema = jsonObject.transform((declared) => {
182
+ const connectors = new Map;
183
+ for (const [name, entry] of Object.entries(declared)) {
184
+ const parsed = ConnectionEntrySchema.safeParse(entry);
185
+ connectors.set(name, parsed.success ? parsed.data.connector ?? "" : "");
186
+ }
187
+ return connectors;
188
+ }).optional().catch(undefined);
189
+
190
+ // src/api/manifest-schema.ts
191
+ var TenantSectionSchema = exports_external.union([jsonObject, exports_external.array(exports_external.unknown())]).optional().catch(undefined);
192
+ var PackageManifestSchema = exports_external.looseObject({
193
+ packageVersion: optionalText,
194
+ configurations: ManifestConfigurationsSchema.optional(),
195
+ solution: exports_external.looseObject({
196
+ artifactFilename: optionalText,
197
+ configuration: DeclaredConfigurationSchema,
198
+ processes: DeclaredNamesSchema,
199
+ assets: DeclaredNamesSchema,
200
+ connections: DeclaredConnectionsSchema
201
+ }).optional().catch(undefined),
202
+ dataFabric: exports_external.looseObject({
203
+ schemaFilename: exports_external.unknown().optional(),
204
+ seeding: SeedingSchema
205
+ }).optional().catch(undefined),
206
+ storageBuckets: TenantSectionSchema,
207
+ indexes: TenantSectionSchema
208
+ });
209
+
210
+ // src/api/packages.ts
211
+ var VerticalListSchema = exports_external.looseObject({
212
+ verticals: exports_external.array(exports_external.string()).optional()
213
+ });
214
+ var VerticalSolutionPackageSchema = exports_external.looseObject({
215
+ packageName: exports_external.string(),
216
+ displayName: exports_external.string().nullish().catch(undefined),
217
+ currentVersion: optionalText
218
+ });
219
+ var PackageListSchema = exports_external.looseObject({
220
+ packages: exports_external.array(VerticalSolutionPackageSchema).optional()
221
+ });
222
+ var PackageVersionSchema = exports_external.looseObject({
223
+ version: exports_external.string(),
224
+ relation: exports_external.enum(["previous", "current", "next", "blocked"]).optional().catch(undefined),
225
+ allowed: exports_external.boolean().optional().catch(undefined),
226
+ requiredVersion: optionalText
227
+ });
228
+ var VersionNavSchema = exports_external.looseObject({
229
+ versions: exports_external.array(exports_external.union([exports_external.string(), PackageVersionSchema]))
230
+ });
231
+ async function listVerticals(config, signal) {
232
+ const result = await vssRequestParsed(config, "verticals", VerticalListSchema, { query: scopeQuery(config), signal });
233
+ return result.verticals ?? [];
234
+ }
235
+ async function listPackages(config, vertical, stage, signal) {
236
+ const result = await vssRequestParsed(config, "packages", PackageListSchema, { query: { ...scopeQuery(config), vertical, env: stage }, signal });
237
+ return result.packages ?? [];
238
+ }
239
+ async function listPackageVersions(config, packageName, options = {}) {
240
+ return vssRequestParsed(config, `packages/${encodeSegment("packageName", packageName)}/versions`, VersionNavSchema, {
241
+ query: {
242
+ ...scopeQuery(config),
243
+ env: options.stage,
244
+ anchorVersion: options.anchorVersion,
245
+ after: options.after,
246
+ before: options.before
247
+ },
248
+ signal: options.signal
249
+ });
250
+ }
251
+ function normalizeVersions(nav) {
252
+ return nav.versions.map((entry) => typeof entry === "string" ? { version: entry } : entry);
253
+ }
254
+ async function getManifest(config, packageName, version, signal) {
255
+ return vssRequestParsed(config, `packages/${encodeSegment("packageName", packageName)}` + `/versions/${encodeSegment("version", version)}/manifest`, PackageManifestSchema, { query: scopeQuery(config), signal });
256
+ }
257
+
258
+ // src/services/seed/seed-context.ts
259
+ var SeedContextSchema = exports_external.object({
260
+ packageName: exports_external.string().min(1).refine((name) => packageNameProblem(name) === undefined),
261
+ packageVersion: exports_external.string().min(1),
262
+ serviceUrl: optionalText,
263
+ organizationId: optionalText,
264
+ organizationName: optionalText,
265
+ tenantId: optionalText,
266
+ tenantName: optionalText,
267
+ folderKey: optionalText,
268
+ deploymentName: optionalText
269
+ });
270
+ async function writeSeedContext(context, workspaceDir) {
271
+ const fs = getFileSystem();
272
+ const dir = workspaceDir ?? packageDir(context.packageName);
273
+ const refuseALinkedWorkspace = async () => {
274
+ if (!await namesSeedRoot(dir))
275
+ return;
276
+ const inside = await insideSeedRoot(dir);
277
+ if (!inside.ok)
278
+ throw new Error(inside.message);
279
+ };
280
+ await refuseALinkedWorkspace();
281
+ await fs.mkdir(dir);
282
+ await refuseALinkedWorkspace();
283
+ const path = fs.path.join(dir, SEED_CONTEXT_FILE);
284
+ await writeFileByRename(path, JSON.stringify(context, null, 2));
285
+ return path;
286
+ }
287
+ async function removeSeedContext(dir) {
288
+ const fs = getFileSystem();
289
+ if (await namesSeedRoot(dir)) {
290
+ const inside = await insideSeedRoot(dir);
291
+ if (!inside.ok)
292
+ throw new Error(inside.message);
293
+ }
294
+ const path = fs.path.join(dir, SEED_CONTEXT_FILE);
295
+ if (typeof fs.stat === "function") {
296
+ const stats = await fs.stat(path);
297
+ if (stats?.isDirectory()) {
298
+ throw new Error(`Refusing to remove context directory ${path}.`);
299
+ }
300
+ }
301
+ await fs.rm(path);
302
+ }
303
+ async function readSeedContext(dir) {
304
+ const fs = getFileSystem();
305
+ const path = fs.path.join(dir, SEED_CONTEXT_FILE);
306
+ if (!await metadataIsItsOwnFile(dir, SEED_CONTEXT_FILE, fs)) {
307
+ return;
308
+ }
309
+ const [readError, contents] = await catchError(fs.readFile(path, "utf-8"));
310
+ if (readError || contents === null)
311
+ return;
312
+ const [parseError, parsed] = catchError(() => JSON.parse(contents));
313
+ if (parseError)
314
+ return;
315
+ return asSeedContext(parsed);
316
+ }
317
+ function asSeedContext(value) {
318
+ const parsed = SeedContextSchema.safeParse(value);
319
+ if (!parsed.success)
320
+ return;
321
+ const context = parsed.data;
322
+ const [urlError, normalized] = catchError(() => context.serviceUrl === undefined ? undefined : validateServiceUrl(context.serviceUrl));
323
+ if (urlError) {
324
+ writeWarning(`Ignoring the service URL recorded in ${SEED_CONTEXT_FILE}: ${urlError.message} ` + "The run goes to the platform's own service; pass --service-url to name another.");
325
+ }
326
+ return {
327
+ packageName: context.packageName,
328
+ packageVersion: context.packageVersion,
329
+ serviceUrl: urlError ? undefined : normalized,
330
+ organizationId: context.organizationId,
331
+ organizationName: context.organizationName,
332
+ tenantId: context.tenantId,
333
+ tenantName: context.tenantName,
334
+ folderKey: context.folderKey,
335
+ deploymentName: context.deploymentName
336
+ };
337
+ }
338
+
339
+ // src/api/folders.ts
340
+ var FOLDER_PAGE_SIZE = 1000;
341
+ var MAX_FOLDER_PAGES = 50;
342
+ async function findFolderPath(config, folderKey, signal) {
343
+ const [clientError, api] = await catchError(foldersApi(config));
344
+ if (clientError || !api)
345
+ return;
346
+ const [error, folder] = await catchError(api.foldersGetByKeyByIdentifier({ identifier: folderKey }, { signal }));
347
+ if (!error && folder?.fullyQualifiedName) {
348
+ return folder.fullyQualifiedName;
349
+ }
350
+ const [listError, folders] = await catchError(listFolderDtos(config, signal));
351
+ if (listError || !folders)
352
+ return;
353
+ const wanted = folderKey.toLowerCase();
354
+ return folders.find((entry) => entry.key?.toLowerCase() === wanted)?.fullyQualifiedName ?? undefined;
355
+ }
356
+ async function orchestratorApi(config, pick, folderKey) {
357
+ if (!config.tenantName) {
358
+ throw new Error("No tenant selected — run 'uip login tenant set <tenant>' first.");
359
+ }
360
+ if (!config.organizationId) {
361
+ throw new Error("The login carries no organization id, which the Orchestrator route needs — " + "run 'uip login' again.");
362
+ }
363
+ const sdk = await import("./index-7a3eqtxv.js");
364
+ return sdk.createApiClient(pick(sdk), {
365
+ tenant: config.tenantName,
366
+ ...folderKey ? { folderKey } : {}
367
+ });
368
+ }
369
+ function foldersApi(config) {
370
+ return orchestratorApi(config, (sdk) => sdk.FoldersApi);
371
+ }
372
+ async function listAllFolders(config, signal) {
373
+ const raw = await listFolderDtos(config, signal);
374
+ return raw.flatMap((folder) => {
375
+ const key = folder.key;
376
+ if (!key)
377
+ return [];
378
+ const displayName = folder.displayName ?? key;
379
+ return [
380
+ {
381
+ key,
382
+ displayName,
383
+ path: folder.fullyQualifiedName ?? displayName,
384
+ kind: folder.folderType
385
+ }
386
+ ];
387
+ }).sort((a, b) => a.path.localeCompare(b.path));
388
+ }
389
+ async function listFolderDtos(config, signal) {
390
+ const api = await foldersApi(config);
391
+ return readAllPages("Orchestrator folders", (page) => api.foldersGet(page, { signal }));
392
+ }
393
+ async function readAllPages(what, fetchPage) {
394
+ const rows = [];
395
+ let previousFirst;
396
+ for (let page = 0;page < MAX_FOLDER_PAGES; page += 1) {
397
+ const [error, response] = await catchError(fetchPage({
398
+ $top: FOLDER_PAGE_SIZE,
399
+ $skip: page * FOLDER_PAGE_SIZE
400
+ }));
401
+ if (error) {
402
+ throw new Error(`Could not list ${what}: ${await extractErrorMessage(error)}`);
403
+ }
404
+ const values = response.value ?? [];
405
+ const first = values.length > 0 ? JSON.stringify(values[0]) : "";
406
+ if (values.length === 0 || first === previousFirst)
407
+ break;
408
+ previousFirst = first;
409
+ rows.push(...values);
410
+ if (values.length < FOLDER_PAGE_SIZE)
411
+ break;
412
+ }
413
+ return rows;
414
+ }
415
+
416
+ // src/services/seed/sdk-schemas.ts
417
+ var flag = exports_external.boolean().optional().catch(undefined);
418
+ var TypeNameSchema = exports_external.union([exports_external.string(), exports_external.looseObject({ name: optionalText })]).optional().catch(undefined);
419
+ var FieldSchema = exports_external.looseObject({
420
+ name: optionalText,
421
+ sqlType: TypeNameSchema,
422
+ fieldDataType: TypeNameSchema,
423
+ fieldDisplayType: optionalText,
424
+ isRequired: flag,
425
+ isUnique: flag,
426
+ isPrimaryKey: flag,
427
+ isSystemField: flag,
428
+ isAttachment: flag,
429
+ isForeignKey: flag,
430
+ choiceSetId: optionalText,
431
+ referenceChoiceSet: exports_external.looseObject({ id: optionalText }).optional().catch(undefined),
432
+ referenceEntity: exports_external.looseObject({ name: optionalText }).optional().catch(undefined)
433
+ });
434
+ var EntitySchema = exports_external.looseObject({
435
+ id: optionalText,
436
+ name: optionalText,
437
+ fields: exports_external.array(exports_external.unknown()).optional().catch(undefined),
438
+ externalFields: exports_external.array(exports_external.unknown()).optional().catch(undefined)
439
+ });
440
+ var EntityListSchema = exports_external.array(exports_external.unknown()).nullish();
441
+ var ChoiceValueSchema = exports_external.looseObject({
442
+ name: optionalText,
443
+ displayName: optionalText,
444
+ numberId: anyNumber.optional().catch(undefined)
445
+ });
446
+ var CursorSchema = exports_external.union([
447
+ exports_external.string().transform((value) => ({ value })),
448
+ exports_external.looseObject({ value: exports_external.string() })
449
+ ]).optional().catch(undefined);
450
+ var PageSchema = exports_external.looseObject({
451
+ items: exports_external.array(exports_external.unknown()).optional().catch(undefined),
452
+ hasNextPage: exports_external.unknown().optional(),
453
+ nextCursor: CursorSchema
454
+ });
455
+ var RecordItemsSchema = exports_external.array(jsonObject);
456
+ var list = exports_external.array(exports_external.unknown()).optional().catch(undefined);
457
+ var BatchResponseSchema = exports_external.looseObject({
458
+ successRecords: list,
459
+ records: list,
460
+ items: list,
461
+ value: list,
462
+ failureRecords: list
463
+ });
464
+ var WrittenRecordSchema = exports_external.looseObject({
465
+ Id: exports_external.unknown().optional(),
466
+ id: exports_external.unknown().optional()
467
+ });
468
+ var FailureRecordSchema = exports_external.looseObject({ error: optionalText });
469
+ var ExpandedValueSchema = exports_external.looseObject({
470
+ Id: optionalText,
471
+ Name: optionalText,
472
+ Size: anyNumber.optional().catch(undefined)
473
+ });
474
+
475
+ // src/services/seed/entities.ts
476
+ function needsExpandedRecords(entity) {
477
+ for (const field of entity.fields.values()) {
478
+ if (field.isForeignKey || field.isAttachment)
479
+ return true;
480
+ }
481
+ return false;
482
+ }
483
+ var SYSTEM_FIELDS = new Set([
484
+ "Id",
485
+ "CreateTime",
486
+ "CreatedBy",
487
+ "UpdateTime",
488
+ "UpdatedBy",
489
+ "RecordOwner"
490
+ ].map((name) => name.toLowerCase()));
491
+ async function loadLiveEntities(sdk, folderKey) {
492
+ const response = await sdk.entities.getAll(folderKey === undefined ? undefined : { folderKey });
493
+ const entities = EntityListSchema.safeParse(response);
494
+ if (!entities.success) {
495
+ throw new Error("Data Fabric sent an entity list that is not a list.");
496
+ }
497
+ const byName = new Map;
498
+ for (const entity of entities.data ?? []) {
499
+ const normalized = toSeedEntity(camelKeys(entity));
500
+ if (normalized)
501
+ byName.set(normalized.name.toLowerCase(), normalized);
502
+ }
503
+ return { byName };
504
+ }
505
+ function setOwn(target, key, value) {
506
+ if (key === "__proto__") {
507
+ Object.defineProperty(target, key, {
508
+ value,
509
+ enumerable: true,
510
+ writable: true,
511
+ configurable: true
512
+ });
513
+ return;
514
+ }
515
+ target[key] = value;
516
+ }
517
+ function camelKeys(value) {
518
+ if (Array.isArray(value))
519
+ return value.map(camelKeys);
520
+ const record = plainRecord.safeParse(value);
521
+ if (!record.success)
522
+ return value;
523
+ const entries = Object.entries(record.data);
524
+ const out = {};
525
+ for (const [key, val] of entries) {
526
+ if (key[0] === key[0]?.toLowerCase())
527
+ setOwn(out, key, camelKeys(val));
528
+ }
529
+ for (const [key, val] of entries) {
530
+ const lowered = (key[0]?.toLowerCase() ?? "") + key.slice(1);
531
+ if (!Object.hasOwn(out, lowered)) {
532
+ setOwn(out, lowered, camelKeys(val));
533
+ }
534
+ }
535
+ return out;
536
+ }
537
+ function toSeedEntity(value) {
538
+ const parsed = EntitySchema.safeParse(value);
539
+ if (!parsed.success)
540
+ return;
541
+ const entity = parsed.data;
542
+ if (!entity.id || !entity.name)
543
+ return;
544
+ const fields = new Map;
545
+ const identityFields = [];
546
+ for (const item of entity.fields ?? []) {
547
+ const raw = FieldSchema.safeParse(item);
548
+ if (!raw.success || !raw.data.name)
549
+ continue;
550
+ const field = toSeedField(raw.data, raw.data.name);
551
+ fields.set(field.name.toLowerCase(), field);
552
+ if (field.isUnique && !field.isSystemField && !field.isAutoNumber) {
553
+ identityFields.push(field.name);
554
+ }
555
+ }
556
+ return {
557
+ id: entity.id,
558
+ name: entity.name,
559
+ fields,
560
+ identityFields,
561
+ isFederated: (entity.externalFields?.length ?? 0) > 0
562
+ };
563
+ }
564
+ function toSeedField(raw, name) {
565
+ const displayType = raw.fieldDisplayType ?? "";
566
+ const dataType = dataTypeNameOf(raw);
567
+ const isFile = raw.isAttachment === true || displayType === "File" || dataType === "FILE";
568
+ return {
569
+ name,
570
+ sqlType: sqlTypeOf(raw),
571
+ isRequired: raw.isRequired === true,
572
+ isUnique: raw.isUnique === true,
573
+ isPrimaryKey: raw.isPrimaryKey === true,
574
+ isSystemField: raw.isSystemField === true || SYSTEM_FIELDS.has(name.toLowerCase()),
575
+ isAttachment: isFile,
576
+ choiceSetId: raw.choiceSetId ?? raw.referenceChoiceSet?.id,
577
+ isMultiChoice: displayType === "ChoiceSetMultiple" || dataType === "CHOICE_SET_MULTIPLE",
578
+ isLazyText: dataType === "MULTILINE_MAX",
579
+ isAutoNumber: displayType === "AutoNumber" || dataType === "AUTO_NUMBER",
580
+ isForeignKey: !isFile && (raw.isForeignKey === true || displayType === "Relationship" || dataType === "RELATIONSHIP"),
581
+ referenceEntityName: isFile ? undefined : raw.referenceEntity?.name
582
+ };
583
+ }
584
+ function dataTypeNameOf(raw) {
585
+ const dataType = typeof raw.fieldDataType === "string" ? raw.fieldDataType : raw.fieldDataType?.name;
586
+ return (dataType ?? "").toUpperCase();
587
+ }
588
+ function sqlTypeOf(raw) {
589
+ if (typeof raw.sqlType === "string")
590
+ return raw.sqlType.toUpperCase();
591
+ if (raw.sqlType?.name)
592
+ return raw.sqlType.name.toUpperCase();
593
+ switch (dataTypeNameOf(raw)) {
594
+ case "BOOLEAN":
595
+ return "BIT";
596
+ case "INTEGER":
597
+ return "INT";
598
+ case "BIG_INTEGER":
599
+ return "BIGINT";
600
+ case "DECIMAL":
601
+ return "DECIMAL";
602
+ case "FLOAT":
603
+ case "DOUBLE":
604
+ return "FLOAT";
605
+ case "DATE":
606
+ return "DATE";
607
+ case "DATETIME":
608
+ return "DATETIME";
609
+ case "DATETIME_WITH_TZ":
610
+ return "DATETIMEOFFSET";
611
+ case "STRING":
612
+ case "MULTILINE_TEXT":
613
+ case "MULTILINE_MAX":
614
+ case "UUID":
615
+ return "NVARCHAR";
616
+ default:
617
+ return;
618
+ }
619
+ }
620
+ async function loadChoiceSetValues(sdk, choiceSetId, folderKey) {
621
+ const [error, items] = await catchError(readAllChoiceValues(sdk, choiceSetId, folderKey));
622
+ if (error || items === undefined)
623
+ return;
624
+ const byLabel = new Map;
625
+ const labelsById = new Map;
626
+ const labels = [];
627
+ for (const item of items) {
628
+ if (item.numberId === undefined)
629
+ continue;
630
+ if (item.name)
631
+ byLabel.set(item.name.toLowerCase(), item.numberId);
632
+ if (item.displayName) {
633
+ byLabel.set(item.displayName.toLowerCase(), item.numberId);
634
+ }
635
+ const label = item.displayName ?? item.name;
636
+ if (label) {
637
+ labels.push(label);
638
+ labelsById.set(item.numberId, label);
639
+ }
640
+ }
641
+ return { byLabel, labelsById, labels };
642
+ }
643
+ async function readAllChoiceValues(sdk, choiceSetId, folderKey) {
644
+ const scope = folderKey === undefined ? {} : { folderKey };
645
+ const values = [];
646
+ const guard = pageGuard(`choice set ${choiceSetId}`);
647
+ let cursor;
648
+ do {
649
+ const response = await sdk.entities.choicesets.getById(choiceSetId, cursor === undefined ? { ...scope, pageSize: RECORD_PAGE_SIZE } : { ...scope, pageSize: RECORD_PAGE_SIZE, cursor });
650
+ const page = PageSchema.safeParse(response);
651
+ if (!page.success || page.data.items === undefined)
652
+ return;
653
+ for (const item of page.data.items) {
654
+ const value = ChoiceValueSchema.safeParse(camelKeys(item));
655
+ if (value.success)
656
+ values.push(value.data);
657
+ }
658
+ cursor = nextCursorOf(page.data, `choice set ${choiceSetId}`);
659
+ guard.next(cursor);
660
+ } while (cursor !== undefined);
661
+ return values;
662
+ }
663
+ function withSystemKeyAliases(record) {
664
+ const out = {};
665
+ for (const [key, val] of Object.entries(record)) {
666
+ setOwn(out, key, aliasNested(val));
667
+ }
668
+ for (const key of SYSTEM_VALUE_KEYS) {
669
+ const lower = key.toLowerCase();
670
+ if (!Object.hasOwn(out, key) && Object.hasOwn(out, lower)) {
671
+ setOwn(out, key, out[lower]);
672
+ }
673
+ }
674
+ return out;
675
+ }
676
+ function aliasNested(value) {
677
+ if (Array.isArray(value))
678
+ return value.map(aliasNested);
679
+ const record = plainRecord.safeParse(value);
680
+ return record.success ? withSystemKeyAliases(record.data) : value;
681
+ }
682
+ var SYSTEM_VALUE_KEYS = ["Id", "Name", "Size"];
683
+ async function loadLiveRecords(sdk, entityId, folderKey, expand = false) {
684
+ const records = [];
685
+ const scope = {
686
+ ...folderKey === undefined ? {} : { folderKey },
687
+ ...expand ? { expansionLevel: 1 } : {}
688
+ };
689
+ const guard = pageGuard(`entity ${entityId}`);
690
+ let cursor;
691
+ do {
692
+ const response = await sdk.entities.getAllRecords(entityId, cursor === undefined ? { ...scope, pageSize: RECORD_PAGE_SIZE } : { ...scope, pageSize: RECORD_PAGE_SIZE, cursor });
693
+ const page = PageSchema.safeParse(response);
694
+ if (!page.success || page.data.items === undefined) {
695
+ throw new Error(`Data Fabric sent a page of entity ${entityId} records with no items list. ` + "Stopping rather than acting on a partial read.");
696
+ }
697
+ const items = RecordItemsSchema.safeParse(page.data.items);
698
+ if (!items.success) {
699
+ throw new Error(`Data Fabric sent a page of entity ${entityId} records with an item that is ` + "not a record. Stopping rather than acting on a partial read.");
700
+ }
701
+ for (const item of items.data) {
702
+ records.push(plainRecord.safeParse(item).success ? withSystemKeyAliases(item) : item);
703
+ }
704
+ cursor = nextCursorOf(page.data, `entity ${entityId}`);
705
+ guard.next(cursor);
706
+ } while (cursor !== undefined);
707
+ return records;
708
+ }
709
+ var MAX_RECORD_PAGES = 1000;
710
+ function nextCursorOf(page, what) {
711
+ if (!page.hasNextPage)
712
+ return;
713
+ if (page.nextCursor === undefined) {
714
+ throw new Error(`Data Fabric said there are more pages of ${what} but sent no cursor to read ` + "them. Stopping rather than acting on a partial read.");
715
+ }
716
+ return page.nextCursor;
717
+ }
718
+ function pageGuard(what) {
719
+ const seen = new Set;
720
+ let pages = 1;
721
+ return {
722
+ next(cursor) {
723
+ if (cursor === undefined)
724
+ return;
725
+ const key = JSON.stringify(cursor);
726
+ if (seen.has(key)) {
727
+ throw new Error(`Data Fabric returned the same page cursor twice while reading ${what}; ` + "stopping rather than reading forever.");
728
+ }
729
+ seen.add(key);
730
+ pages += 1;
731
+ if (pages > MAX_RECORD_PAGES) {
732
+ throw new Error(`Reading ${what} did not end after ${MAX_RECORD_PAGES} pages; stopping.`);
733
+ }
734
+ }
735
+ };
736
+ }
737
+ var RECORD_PAGE_SIZE = 1000;
738
+
739
+ // src/services/seed/csv.ts
740
+ function parseCsv(contents) {
741
+ const state = {
742
+ rows: [],
743
+ row: [],
744
+ field: "",
745
+ quoted: false,
746
+ quoteOpenedAt: 0,
747
+ fieldStarted: false,
748
+ rowStarted: false
749
+ };
750
+ const text = contents.replace(/^/, "");
751
+ let index = 0;
752
+ while (index < text.length) {
753
+ index = state.quoted ? stepQuoted(state, text, index) : stepUnquoted(state, text, index);
754
+ }
755
+ if (state.quoted) {
756
+ const line = text.slice(0, state.quoteOpenedAt).split(`
757
+ `).length;
758
+ throw new Error(`unterminated quoted field: the quote opened on line ${line} is never closed`);
759
+ }
760
+ if (state.rowStarted)
761
+ endRow(state);
762
+ if (state.rows.length === 0)
763
+ return { headers: [], rows: [] };
764
+ return {
765
+ headers: state.rows[0].map((header) => header.trim()),
766
+ rows: state.rows.slice(1)
767
+ };
768
+ }
769
+ function endField(state) {
770
+ state.row.push(state.field);
771
+ state.field = "";
772
+ state.fieldStarted = false;
773
+ }
774
+ function endRow(state) {
775
+ if (!state.rowStarted) {
776
+ state.row = [];
777
+ state.field = "";
778
+ state.fieldStarted = false;
779
+ return;
780
+ }
781
+ endField(state);
782
+ state.rows.push(state.row);
783
+ state.row = [];
784
+ state.rowStarted = false;
785
+ }
786
+ function stepQuoted(state, text, index) {
787
+ const char = text[index];
788
+ if (char !== '"') {
789
+ state.field += char;
790
+ return index + 1;
791
+ }
792
+ if (text[index + 1] === '"') {
793
+ state.field += '"';
794
+ return index + 2;
795
+ }
796
+ state.quoted = false;
797
+ return index + 1;
798
+ }
799
+ function stepUnquoted(state, text, index) {
800
+ const char = text[index];
801
+ if (char === '"' && !state.fieldStarted) {
802
+ state.quoted = true;
803
+ state.quoteOpenedAt = index;
804
+ state.fieldStarted = true;
805
+ state.rowStarted = true;
806
+ return index + 1;
807
+ }
808
+ if (char === ",") {
809
+ state.rowStarted = true;
810
+ endField(state);
811
+ return index + 1;
812
+ }
813
+ if (char === "\r") {
814
+ if (text[index + 1] === `
815
+ `)
816
+ return index + 1;
817
+ endRow(state);
818
+ return index + 1;
819
+ }
820
+ if (char === `
821
+ `) {
822
+ endRow(state);
823
+ return index + 1;
824
+ }
825
+ state.field += char;
826
+ state.fieldStarted = true;
827
+ if (char.trim() !== "")
828
+ state.rowStarted = true;
829
+ return index + 1;
830
+ }
831
+
832
+ // src/services/seed/expressions.ts
833
+ var EXPRESSION = /%\{([^}]*)\}%/g;
834
+ var INDEXED_LOOKUP = /^([A-Za-z]+)\.([A-Za-z]+)\['([^']+)'\]\.([A-Za-z]+)$/;
835
+ function resolveExpressions(raw, context) {
836
+ if (!raw.includes("%{"))
837
+ return { value: raw, errors: [] };
838
+ const errors = [];
839
+ const spans = [];
840
+ let value = "";
841
+ let at = 0;
842
+ let kept = false;
843
+ EXPRESSION.lastIndex = 0;
844
+ for (let match = EXPRESSION.exec(raw);match !== null; match = EXPRESSION.exec(raw)) {
845
+ const [text, body] = match;
846
+ value += raw.slice(at, match.index);
847
+ const shared = kept && match.index < at ? at - match.index : 0;
848
+ const resolved = resolveOne(body.trim(), context);
849
+ if (resolved === undefined)
850
+ errors.push(text);
851
+ value += resolved ?? text.slice(shared);
852
+ kept = resolved === undefined;
853
+ spans.push([match.index, match.index + text.length]);
854
+ at = match.index + text.length;
855
+ EXPRESSION.lastIndex = raw[at] === "{" && raw[at - 1] === "%" ? at - 1 : at;
856
+ }
857
+ value += raw.slice(!kept && raw[at - 1] === "%" && raw[at] === "{" ? at - 1 : at);
858
+ errors.push(...strayMarkers(raw, spans));
859
+ return { value, errors };
860
+ }
861
+ function consumed(spans, at) {
862
+ return spans.some(([from, to]) => at === from || at > from && at < to - 1);
863
+ }
864
+ function strayMarkers(raw, spans) {
865
+ const stray = [];
866
+ for (let at = raw.indexOf("%{");at !== -1; at = raw.indexOf("%{", at + 2)) {
867
+ if (consumed(spans, at))
868
+ continue;
869
+ const rest = raw.slice(at);
870
+ stray.push(rest.length > 40 ? `${rest.slice(0, 40)}…` : rest);
871
+ }
872
+ return stray;
873
+ }
874
+ function resolveOne(body, context) {
875
+ const contextValue = resolveContextVariable(body, context);
876
+ if (contextValue !== undefined)
877
+ return contextValue;
878
+ const solutionValue = resolveSolutionScoped(body, context);
879
+ if (solutionValue !== undefined)
880
+ return solutionValue;
881
+ const indexed = INDEXED_LOOKUP.exec(body);
882
+ if (indexed) {
883
+ const [, namespace, collection, name, property] = indexed;
884
+ return resolveIndexed(namespace, collection, name, property, context);
885
+ }
886
+ return;
887
+ }
888
+ function resolveContextVariable(body, context) {
889
+ switch (body) {
890
+ case "UIPATH_BASE_URL":
891
+ return context.baseUrl;
892
+ case "UIPATH_ORG_ID":
893
+ return context.organizationId;
894
+ case "UIPATH_ORG_NAME":
895
+ return context.organizationName;
896
+ case "UIPATH_TENANT_ID":
897
+ return context.tenantId;
898
+ case "UIPATH_TENANT_NAME":
899
+ return context.tenantName;
900
+ case "UIPATH_FOLDER_KEY":
901
+ return context.folderKey;
902
+ case "UIPATH_FOLDER_ID":
903
+ return context.solutionFolderId;
904
+ case "UIPATH_FOLDER_PATH":
905
+ return context.solutionFolderPath;
906
+ default:
907
+ return;
908
+ }
909
+ }
910
+ function resolveSolutionScoped(body, context) {
911
+ switch (body) {
912
+ case "Solution.FolderPath":
913
+ return context.solutionFolderPath;
914
+ case "Solution.FolderKey":
915
+ return context.solutionFolderKey;
916
+ case "Solution.FolderId":
917
+ return context.solutionFolderId;
918
+ case "Solution.ParentFolderId":
919
+ return context.solutionParentFolderId;
920
+ case "Solution.ParentFolderKey":
921
+ return context.solutionParentFolderKey;
922
+ default:
923
+ return;
924
+ }
925
+ }
926
+ function resolveIndexed(namespace, collection, name, property, context) {
927
+ if (namespace === "Solution") {
928
+ return context.solutionResources?.get(collection)?.get(name)?.get(property);
929
+ }
930
+ if (namespace !== "DataFabric" || collection !== "Entities")
931
+ return;
932
+ const entity = name.toLowerCase();
933
+ if (property === "Id")
934
+ return context.entityIds?.get(entity);
935
+ if (property === "Name")
936
+ return context.entityNames?.get(entity);
937
+ return;
938
+ }
939
+ var INSTALLED_FOLDER_EXPRESSIONS = new Set([
940
+ "Solution.FolderPath",
941
+ "Solution.FolderKey",
942
+ "Solution.FolderId",
943
+ "Solution.ParentFolderId",
944
+ "Solution.ParentFolderKey",
945
+ "UIPATH_FOLDER_KEY",
946
+ "UIPATH_FOLDER_ID",
947
+ "UIPATH_FOLDER_PATH"
948
+ ]);
949
+ function describeUnresolved(expression) {
950
+ const body = expression.replace(/^%\{/, "").replace(/\}%$/, "").trim();
951
+ if (!expression.endsWith("}%")) {
952
+ return `${expression} is not closed: a seed expression is written %{NAME}%. ` + "Close it, or remove the %{ if the cell is meant to hold that text.";
953
+ }
954
+ if (body === "") {
955
+ return `${expression} names no expression. Write the name inside it, for example ` + "%{UIPATH_TENANT_ID}%, or remove it if the cell is meant to be empty.";
956
+ }
957
+ if (INSTALLED_FOLDER_EXPRESSIONS.has(body)) {
958
+ return `${expression} needs the folder the solution installed into, which this seed run did ` + "not know or could not read. Seed after the install completes, or pass --folder-key or " + "--folder-path for the installed folder.";
959
+ }
960
+ if (body.startsWith("Solution.")) {
961
+ return `${expression} names a solution resource that was not found in the folder the ` + "solution installed into. Check that the manifest declares it and that it is " + "deployed, or replace it with a literal value in the seed file.";
962
+ }
963
+ return `${expression} is not a supported seed expression. Supported: ` + "%{UIPATH_BASE_URL}%, %{UIPATH_ORG_ID}%, %{UIPATH_ORG_NAME}%, %{UIPATH_TENANT_ID}%, " + "%{UIPATH_TENANT_NAME}%, %{UIPATH_FOLDER_KEY}%, %{UIPATH_FOLDER_ID}%, %{UIPATH_FOLDER_PATH}%, " + "%{Solution.FolderPath}%, %{Solution.Processes['Name'].Id}% and the other Solution " + "resources, %{DataFabric.Entities['Name'].Id}%.";
964
+ }
965
+
966
+ // src/services/seed/own-keys.ts
967
+ function journalEntity(journal, entityName) {
968
+ if (Object.hasOwn(journal, entityName))
969
+ return journal[entityName];
970
+ const entries = Object.create(null);
971
+ return entries;
972
+ }
973
+ function putOwn(map, key, value) {
974
+ Object.defineProperty(map, key, {
975
+ value,
976
+ writable: true,
977
+ enumerable: true,
978
+ configurable: true
979
+ });
980
+ }
981
+ function fieldMap(from) {
982
+ const map = Object.create(null);
983
+ return from === undefined ? map : Object.assign(map, from);
984
+ }
985
+ function ownValue(record, key) {
986
+ return Object.hasOwn(record, key) ? record[key] : undefined;
987
+ }
988
+
989
+ // src/services/seed/csv-cells.ts
990
+ var CSV_NULL = "NULL";
991
+ var DATE_PATTERNS = [
992
+ { pattern: /^(\d{4})-(\d{1,2})-(\d{1,2})$/, order: [1, 2, 3] },
993
+ { pattern: /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/, order: [3, 2, 1] },
994
+ { pattern: /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/, order: [3, 2, 1] }
995
+ ];
996
+ function pad(value) {
997
+ return value.length === 1 ? `0${value}` : value;
998
+ }
999
+ var TIME_SUFFIX = /[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(\.\d{1,6})?)?$/;
1000
+ var ZONE_SUFFIX = /(Z|[+-]\d{2}:\d{2})$/i;
1001
+ function readCell(raw, sqlType) {
1002
+ const trimmed = raw.trim();
1003
+ if (trimmed === "")
1004
+ return { present: false };
1005
+ if (trimmed.toUpperCase() === CSV_NULL) {
1006
+ return { present: true, value: null };
1007
+ }
1008
+ if (trimmed === `\\${CSV_NULL}`) {
1009
+ return { present: true, value: CSV_NULL };
1010
+ }
1011
+ return coerce(trimmed, sqlType, raw);
1012
+ }
1013
+ function coerce(value, sqlType, raw = value) {
1014
+ switch ((sqlType ?? "").toUpperCase()) {
1015
+ case "BIT":
1016
+ return coerceBoolean(value);
1017
+ case "INT":
1018
+ return coerceInteger(value, false);
1019
+ case "BIGINT":
1020
+ return coerceInteger(value, true);
1021
+ case "DECIMAL":
1022
+ case "FLOAT":
1023
+ case "NUMERIC":
1024
+ return coerceNumber(value);
1025
+ case "DATE":
1026
+ return coerceDate(value);
1027
+ case "DATETIME":
1028
+ case "DATETIME2":
1029
+ return coerceDateTime(value, false);
1030
+ case "DATETIMEOFFSET":
1031
+ return coerceDateTime(value, true);
1032
+ default:
1033
+ return { present: true, value: raw };
1034
+ }
1035
+ }
1036
+ function coerceBoolean(value) {
1037
+ const lower = value.toLowerCase();
1038
+ if (["true", "1", "yes", "y"].includes(lower)) {
1039
+ return { present: true, value: true };
1040
+ }
1041
+ if (["false", "0", "no", "n"].includes(lower)) {
1042
+ return { present: true, value: false };
1043
+ }
1044
+ return { present: true, error: `"${value}" is not a boolean` };
1045
+ }
1046
+ var INTEGER_RANGES = {
1047
+ INT: { min: -(2n ** 31n), max: 2n ** 31n - 1n },
1048
+ BIGINT: { min: -(2n ** 63n), max: 2n ** 63n - 1n }
1049
+ };
1050
+ function coerceInteger(value, bigint) {
1051
+ if (!/^[+-]?\d+$/.test(value)) {
1052
+ return { present: true, error: `"${value}" is not an integer` };
1053
+ }
1054
+ const type = bigint ? "BIGINT" : "INT";
1055
+ const { min, max } = INTEGER_RANGES[type];
1056
+ const exact = BigInt(value.startsWith("+") ? value.slice(1) : value);
1057
+ if (exact < min || exact > max) {
1058
+ return {
1059
+ present: true,
1060
+ error: `"${value}" is outside the ${type} range (${min} to ${max})`
1061
+ };
1062
+ }
1063
+ const parsed = Number(value);
1064
+ if (Number.isSafeInteger(parsed))
1065
+ return { present: true, value: parsed };
1066
+ return { present: true, value };
1067
+ }
1068
+ var DECIMAL_NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
1069
+ var EXACT_DIGITS = 15;
1070
+ function coerceNumber(value) {
1071
+ const parsed = DECIMAL_NUMBER.test(value) ? Number(value) : Number.NaN;
1072
+ if (!Number.isFinite(parsed)) {
1073
+ return { present: true, error: `"${value}" is not a number` };
1074
+ }
1075
+ const digits = significantDigits(value);
1076
+ if (digits > EXACT_DIGITS || parsed === 0 && digits > 0) {
1077
+ return { present: true, value };
1078
+ }
1079
+ return { present: true, value: parsed };
1080
+ }
1081
+ function significantDigits(value) {
1082
+ return value.replace(/^[+-]/, "").replace(/[eE].*$/, "").replace(".", "").replace(/^0+/, "").replace(/0+$/, "").length;
1083
+ }
1084
+ function coerceDate(value) {
1085
+ const parts = matchDateParts(value);
1086
+ if (!parts) {
1087
+ return {
1088
+ present: true,
1089
+ error: `"${value}" is not a date (use YYYY-MM-DD, DD/MM/YYYY or DD.MM.YYYY)`
1090
+ };
1091
+ }
1092
+ return { present: true, value: parts };
1093
+ }
1094
+ function coerceDateTime(value, zoned) {
1095
+ const zoneMatch = ZONE_SUFFIX.exec(value);
1096
+ if (!zoned && zoneMatch) {
1097
+ return {
1098
+ present: true,
1099
+ error: `"${value}" carries a time zone, but this column holds a wall-clock date/time with none — drop the ${zoneMatch[1]}`
1100
+ };
1101
+ }
1102
+ const zone = zoneMatch ? zoneMatch[1] : "Z";
1103
+ const withoutZone = zoneMatch ? value.slice(0, value.length - zoneMatch[1].length).trim() : value;
1104
+ const suffix = zoned ? normalizeZone(zone) : "";
1105
+ const timeMatch = TIME_SUFFIX.exec(withoutZone);
1106
+ const datePart = timeMatch ? withoutZone.slice(0, withoutZone.length - timeMatch[0].length) : withoutZone;
1107
+ const date = matchDateParts(datePart.trim());
1108
+ if (!date) {
1109
+ return {
1110
+ present: true,
1111
+ error: `"${value}" is not a date/time (use YYYY-MM-DDTHH:MM:SS)`
1112
+ };
1113
+ }
1114
+ if (!timeMatch) {
1115
+ if (zoneMatch) {
1116
+ return {
1117
+ present: true,
1118
+ error: `"${value}" carries a time zone but no time — write the time too (YYYY-MM-DDTHH:MM:SS${zoneMatch[1]}), or drop the ${zoneMatch[1]}`
1119
+ };
1120
+ }
1121
+ if (!isRealZone(zone)) {
1122
+ return {
1123
+ present: true,
1124
+ error: `"${value}" is not a date/time: the zone offset is out of range`
1125
+ };
1126
+ }
1127
+ return {
1128
+ present: true,
1129
+ value: `${date}T00:00:00${suffix}`
1130
+ };
1131
+ }
1132
+ const hours = pad(timeMatch[1]);
1133
+ const minutes = pad(timeMatch[2]);
1134
+ const seconds = pad(timeMatch[3] ?? "00");
1135
+ const fraction = timeMatch[4] ?? "";
1136
+ if (!isRealTime(hours, minutes, seconds) || !isRealZone(zone)) {
1137
+ return {
1138
+ present: true,
1139
+ error: `"${value}" is not a date/time: the time or zone offset is out of range`
1140
+ };
1141
+ }
1142
+ return {
1143
+ present: true,
1144
+ value: `${date}T${hours}:${minutes}:${seconds}${fraction}${suffix}`
1145
+ };
1146
+ }
1147
+ function normalizeZone(zone) {
1148
+ return zone.toUpperCase() === "Z" ? "Z" : zone;
1149
+ }
1150
+ function isRealTime(hours, minutes, seconds) {
1151
+ return Number(hours) <= 23 && Number(minutes) <= 59 && Number(seconds) <= 59;
1152
+ }
1153
+ function isRealZone(zone) {
1154
+ if (zone.toUpperCase() === "Z")
1155
+ return true;
1156
+ const maxHours = zone.startsWith("-") ? 12 : 14;
1157
+ const hours = Number(zone.slice(1, 3));
1158
+ const minutes = Number(zone.slice(4, 6));
1159
+ if (minutes > 59 || hours > maxHours)
1160
+ return false;
1161
+ return hours < maxHours || minutes === 0;
1162
+ }
1163
+ function matchDateParts(value) {
1164
+ for (const { pattern, order } of DATE_PATTERNS) {
1165
+ const match = pattern.exec(value);
1166
+ if (!match)
1167
+ continue;
1168
+ const [yearIndex, monthIndex, dayIndex] = order;
1169
+ const year = match[yearIndex];
1170
+ const month = match[monthIndex];
1171
+ const day = match[dayIndex];
1172
+ if (!isRealDate(Number(year), Number(month), Number(day)))
1173
+ return;
1174
+ return `${year}-${pad(month)}-${pad(day)}`;
1175
+ }
1176
+ return;
1177
+ }
1178
+ function isRealDate(year, month, day) {
1179
+ if (year < 1 || month < 1 || month > 12 || day < 1 || day > 31) {
1180
+ return false;
1181
+ }
1182
+ const date = new Date(0);
1183
+ date.setUTCFullYear(year, month - 1, day);
1184
+ return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
1185
+ }
1186
+
1187
+ // src/services/seed/plan-columns.ts
1188
+ import { createHash } from "node:crypto";
1189
+ function stripColumnAnnotation(header) {
1190
+ const trimmed = header.trim();
1191
+ if (!trimmed.endsWith(")"))
1192
+ return trimmed;
1193
+ const open = trimmed.lastIndexOf("(");
1194
+ if (open < 0 || trimmed.slice(open + 1, -1).includes(")"))
1195
+ return trimmed;
1196
+ return trimmed.slice(0, open).trim();
1197
+ }
1198
+ function mapColumns(entity, headers, warnings) {
1199
+ const columns = [];
1200
+ for (const [sourceIndex, header] of headers.entries()) {
1201
+ const lower = stripColumnAnnotation(header).toLowerCase();
1202
+ if (SYSTEM_FIELDS.has(lower))
1203
+ continue;
1204
+ const field = entity.fields.get(lower);
1205
+ if (!field) {
1206
+ warnings.push(`${entity.name}: ignoring column "${header}" — the entity has no such field.`);
1207
+ continue;
1208
+ }
1209
+ if (field.isAutoNumber)
1210
+ continue;
1211
+ columns.push({ header, sourceIndex, field });
1212
+ }
1213
+ return columns;
1214
+ }
1215
+ function noSeedableColumns(entityName, table, columns) {
1216
+ if (columns.length > 0 || table.rows.length === 0)
1217
+ return;
1218
+ const ignored = table.headers.filter((header) => stripColumnAnnotation(header).toLowerCase() !== "id");
1219
+ return `${entityName}: no column in ${entityName}.csv matches a field of the entity` + (ignored.length > 0 ? ` (ignored: ${ignored.join(", ")})` : "") + ", so its rows were not seeded. Fix the column headers and run again.";
1220
+ }
1221
+ function seedableFields(entity) {
1222
+ return [...entity.fields.values()].filter((field) => !field.isSystemField && !field.isPrimaryKey && !field.isAutoNumber);
1223
+ }
1224
+ function requiredColumnsMissing(entity, columns) {
1225
+ const present = new Set(columns.map((column) => column.field.name.toLowerCase()));
1226
+ return seedableFields(entity).filter((field) => field.isRequired && !present.has(field.name.toLowerCase())).map((field) => field.name);
1227
+ }
1228
+ function optionalColumnsMissing(entity, columns) {
1229
+ const present = new Set(columns.map((column) => column.field.name.toLowerCase()));
1230
+ return seedableFields(entity).filter((field) => !field.isRequired && !present.has(field.name.toLowerCase())).map((field) => field.name);
1231
+ }
1232
+ function rowKeysFor(headers, rows) {
1233
+ const localIds = localIdsFor(headers, rows);
1234
+ const seen = new Map;
1235
+ return rows.map((cells, index) => {
1236
+ const localId = localIds[index];
1237
+ const base = localId ? `id:${localId}` : `h:${contentHash(headers, cells)}`;
1238
+ const occurrence = (seen.get(base) ?? 0) + 1;
1239
+ seen.set(base, occurrence);
1240
+ return occurrence === 1 ? base : `${base}#${occurrence}`;
1241
+ });
1242
+ }
1243
+ function localIdsFor(headers, rows) {
1244
+ const idIndex = headers.findIndex((header) => stripColumnAnnotation(header).toLowerCase() === "id");
1245
+ return rows.map((cells) => idIndex >= 0 ? (cells[idIndex] ?? "").trim() : "");
1246
+ }
1247
+ function contentHash(headers, cells) {
1248
+ const canonical = JSON.stringify(headers.map((header, index) => [header, cells[index] ?? ""]).sort(([a], [b]) => a.localeCompare(b)));
1249
+ return createHash("sha256").update(canonical, "utf8").digest("hex").slice(0, 16);
1250
+ }
1251
+ function rowLabel(key, line) {
1252
+ return key.startsWith("id:") ? key.slice(3) : `row ${line}`;
1253
+ }
1254
+
1255
+ // src/services/seed/plan-values.ts
1256
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1257
+ var EMPTY = "<empty>";
1258
+ var LAZY_TEXT_MARKER = /^HasValue=(true|false) Length=\d+$/i;
1259
+ var LAZY_TEXT_TRUNCATED = /\.\.\.\[Truncated\]$/i;
1260
+ function lazyTextPreview(liveText) {
1261
+ const marker = LAZY_TEXT_MARKER.exec(liveText);
1262
+ if (marker)
1263
+ return { hasValue: marker[1].toLowerCase() === "true" };
1264
+ return LAZY_TEXT_TRUNCATED.test(liveText) ? { hasValue: true } : undefined;
1265
+ }
1266
+ function truncatedPreviewText(liveText) {
1267
+ return LAZY_TEXT_TRUNCATED.test(liveText) ? liveText.replace(LAZY_TEXT_TRUNCATED, "") : undefined;
1268
+ }
1269
+ function matchValue(value, field) {
1270
+ return field.isForeignKey ? referenceId(value).toLowerCase() : comparableText(value, field).toLowerCase();
1271
+ }
1272
+ function comparableText(value, field) {
1273
+ return roundLikeJson(normalizeForCompare(value, field), field);
1274
+ }
1275
+ var ROUNDED_BY_JSON = new Set(["BIGINT", "DECIMAL", "NUMERIC", "FLOAT"]);
1276
+ function roundLikeJson(text, field) {
1277
+ return ROUNDED_BY_JSON.has((field.sqlType ?? "").toUpperCase()) && /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(text) ? String(Number(text)) : text;
1278
+ }
1279
+ function choiceLabels(text, choices) {
1280
+ if (!choices || text === "")
1281
+ return text;
1282
+ return text.split(",").map((part) => part.trim()).filter((part) => part !== "").map((part) => /^\d+$/.test(part) ? choices.labelsById.get(Number(part)) ?? part : part).join(", ");
1283
+ }
1284
+ var ATTACHED_FILE = "an attached file";
1285
+ function expanded(value) {
1286
+ const parsed = ExpandedValueSchema.safeParse(value);
1287
+ return parsed.success ? parsed.data : undefined;
1288
+ }
1289
+ function attachmentName(value) {
1290
+ if (value === null || typeof value !== "object") {
1291
+ return typeof value === "string" && !UUID.test(value.trim()) ? value.trim() : "";
1292
+ }
1293
+ return expanded(value)?.Name ?? "";
1294
+ }
1295
+ function hasAttachment(value) {
1296
+ if (value === null || value === undefined)
1297
+ return false;
1298
+ if (typeof value === "string")
1299
+ return value.trim() !== "";
1300
+ const file = expanded(value);
1301
+ return file?.Id !== undefined || file?.Name !== undefined;
1302
+ }
1303
+ function attachmentSize(value) {
1304
+ return expanded(value)?.Size;
1305
+ }
1306
+ function referenceId(value) {
1307
+ if (typeof value === "string")
1308
+ return value.trim();
1309
+ return expanded(value)?.Id ?? "";
1310
+ }
1311
+ function normalizeForCompare(value, field) {
1312
+ if (value === null || value === undefined)
1313
+ return "";
1314
+ if (typeof value === "boolean")
1315
+ return value ? "true" : "false";
1316
+ if (typeof value === "number")
1317
+ return String(value);
1318
+ if (Array.isArray(value)) {
1319
+ const items = value.map((item) => normalizeForCompare(item, field));
1320
+ return (field.isMultiChoice ? [...items].sort((left, right) => left.localeCompare(right)) : items).join(",");
1321
+ }
1322
+ if (typeof value === "object")
1323
+ return normalizeObject(value);
1324
+ if (typeof value !== "string")
1325
+ return "";
1326
+ return normalizeText(value, field);
1327
+ }
1328
+ function normalizeObject(value) {
1329
+ const record = expanded(value);
1330
+ if (record?.Id !== undefined)
1331
+ return record.Id;
1332
+ if (record?.Name !== undefined)
1333
+ return record.Name;
1334
+ return JSON.stringify(value);
1335
+ }
1336
+ function normalizeText(raw, field) {
1337
+ const sqlType = (field.sqlType ?? "").toUpperCase();
1338
+ const text = raw.trim();
1339
+ if (sqlType === "BIT")
1340
+ return normalizeBit(text);
1341
+ if (field.isMultiChoice) {
1342
+ return text.split(",").map((part) => part.trim()).filter((part) => part !== "").sort((left, right) => left.localeCompare(right)).join(",");
1343
+ }
1344
+ if (sqlType === "DATE") {
1345
+ return text.length >= 10 && /^\d{4}-\d{2}-\d{2}/.test(text) ? text.slice(0, 10) : text;
1346
+ }
1347
+ if (sqlType.startsWith("DATETIME"))
1348
+ return normalizeDateTime(text);
1349
+ return raw;
1350
+ }
1351
+ function normalizeBit(text) {
1352
+ const lower = text.toLowerCase();
1353
+ if (["1", "yes", "y", "true"].includes(lower))
1354
+ return "true";
1355
+ if (["0", "no", "n", "false"].includes(lower))
1356
+ return "false";
1357
+ return lower;
1358
+ }
1359
+ function normalizeDateTime(text) {
1360
+ const withZone = /(Z|[+-]\d{2}:\d{2})$/i.test(text) ? text : `${text}Z`;
1361
+ const parsed = Date.parse(withZone);
1362
+ if (Number.isFinite(parsed)) {
1363
+ return new Date(parsed).toISOString().replace(/\.000Z$/, "Z");
1364
+ }
1365
+ return text;
1366
+ }
1367
+
1368
+ // src/services/seed/plan-cells.ts
1369
+ function buildRow(identity, cells, columns, input, errors, unresolved, ownLocalIds) {
1370
+ const parts = {
1371
+ values: fieldMap(),
1372
+ display: fieldMap(),
1373
+ attachments: fieldMap(),
1374
+ foreignKeys: fieldMap(),
1375
+ uuidReferences: fieldMap(),
1376
+ ownLocalIds
1377
+ };
1378
+ const { values, display, attachments, foreignKeys } = parts;
1379
+ const prefix = `${input.entityName} row ${identity.line}`;
1380
+ let rowFailed = false;
1381
+ for (const column of columns) {
1382
+ const error = readColumn(column, cells, input, parts, unresolved);
1383
+ if (error !== undefined) {
1384
+ errors.push(`${prefix}, column "${column.header}": ${error}`);
1385
+ rowFailed = true;
1386
+ }
1387
+ }
1388
+ if (rowFailed)
1389
+ return;
1390
+ const missing = missingRequiredValues(input.entity, columns, values, attachments, foreignKeys);
1391
+ if (missing.length > 0) {
1392
+ errors.push(`${prefix}: required ${missing.length === 1 ? "field" : "fields"} left empty: ${missing.join(", ")}.`);
1393
+ return;
1394
+ }
1395
+ const { uuidReferences } = parts;
1396
+ return {
1397
+ ...identity,
1398
+ values,
1399
+ display,
1400
+ attachments,
1401
+ foreignKeys,
1402
+ ...Object.keys(uuidReferences).length > 0 ? { uuidReferences } : {}
1403
+ };
1404
+ }
1405
+ function readColumn(column, cells, input, parts, unresolved) {
1406
+ const raw = cells[column.sourceIndex] ?? "";
1407
+ const { field } = column;
1408
+ const resolved = resolveExpressions(raw, input.expressionContext);
1409
+ let value = resolved.value;
1410
+ if (resolved.errors.length > 0) {
1411
+ for (const expression of resolved.errors)
1412
+ unresolved.add(expression);
1413
+ value = "";
1414
+ }
1415
+ const text = value.trim();
1416
+ parts.display[field.name] = displayText(text);
1417
+ if (field.isAttachment) {
1418
+ return readAttachmentCell(text, field, input, parts.attachments) || undefined;
1419
+ }
1420
+ if (field.isForeignKey) {
1421
+ return readForeignKeyCell(text, field, input, parts);
1422
+ }
1423
+ const cell = readCell(value, field.sqlType);
1424
+ if (!cell.present)
1425
+ return;
1426
+ if (cell.error)
1427
+ return `${cell.error}.`;
1428
+ if (typeof cell.value === "string" && isDateType(field.sqlType)) {
1429
+ parts.display[field.name] = cell.value;
1430
+ }
1431
+ const choice = mapChoiceValue(field, cell.value, input);
1432
+ if (choice.error)
1433
+ return choice.error;
1434
+ parts.values[field.name] = choice.value;
1435
+ return;
1436
+ }
1437
+ function isDateType(sqlType) {
1438
+ return (sqlType ?? "").toUpperCase().startsWith("DATE");
1439
+ }
1440
+ function displayText(text) {
1441
+ if (text === "")
1442
+ return EMPTY;
1443
+ if (text.toUpperCase() === "NULL")
1444
+ return "<null>";
1445
+ if (text === String.raw`\NULL`)
1446
+ return "NULL";
1447
+ return text;
1448
+ }
1449
+ function readAttachmentCell(text, field, input, attachments) {
1450
+ if (text === "" || text.toUpperCase() === "NULL")
1451
+ return;
1452
+ if (text.includes("/") || text.includes("\\")) {
1453
+ return `an attachment cell must be a filename, not a path: "${text}". Put the file under ${input.entityName}/ in the seed workspace.`;
1454
+ }
1455
+ const path = `${input.entityName}/${text}`;
1456
+ if (input.bundledFiles && !input.bundledFiles.has(path)) {
1457
+ return `file not found for "${field.name}": ${path}.`;
1458
+ }
1459
+ attachments[field.name] = path;
1460
+ return;
1461
+ }
1462
+ function readForeignKeyCell(text, field, input, parts) {
1463
+ if (text === "" || text.toUpperCase() === "NULL")
1464
+ return;
1465
+ const entity = (field.referenceEntityName ?? "").toLowerCase();
1466
+ const reference = { entity, localId: text };
1467
+ if (entity === input.entity.name.toLowerCase()) {
1468
+ if (UUID.test(text) && !parts.ownLocalIds.has(text)) {
1469
+ parts.values[field.name] = { Id: text };
1470
+ return;
1471
+ }
1472
+ const resolvedBefore = input.idMaps?.get(entity)?.get(text);
1473
+ if (!parts.ownLocalIds.has(text) && resolvedBefore === undefined && field.isRequired) {
1474
+ return `"${text}" does not match the Id of any row in ` + `${input.entityName}.csv, and ${field.name} is required. ` + "Add that row, or put the record id of an existing " + `${input.entityName} in the cell.`;
1475
+ }
1476
+ parts.foreignKeys[field.name] = reference;
1477
+ return;
1478
+ }
1479
+ const known = input.idMaps?.get(entity)?.get(text);
1480
+ if (known) {
1481
+ parts.values[field.name] = { Id: known };
1482
+ return;
1483
+ }
1484
+ if (UUID.test(text)) {
1485
+ parts.values[field.name] = { Id: text };
1486
+ parts.uuidReferences[field.name] = reference;
1487
+ return;
1488
+ }
1489
+ parts.foreignKeys[field.name] = reference;
1490
+ return;
1491
+ }
1492
+ function mapChoiceValue(field, value, input) {
1493
+ if (value === undefined || value === null)
1494
+ return { value: value ?? null };
1495
+ if (!field.choiceSetId || typeof value !== "string")
1496
+ return { value };
1497
+ const choices = input.choiceValues?.get(field.name.toLowerCase());
1498
+ const parts = field.isMultiChoice ? value.split(",").map((part) => part.trim()).filter((part) => part !== "") : [value.trim()];
1499
+ const mapped = [];
1500
+ for (const part of parts) {
1501
+ const id = choices?.byLabel.get(part.toLowerCase());
1502
+ if (id !== undefined) {
1503
+ mapped.push(String(id));
1504
+ continue;
1505
+ }
1506
+ if (/^\d+$/.test(part)) {
1507
+ mapped.push(part);
1508
+ continue;
1509
+ }
1510
+ if (choices) {
1511
+ return {
1512
+ value,
1513
+ error: `"${part}" is not a valid choice for "${field.name}". Valid: ${choices.labels.join(", ")}.`
1514
+ };
1515
+ }
1516
+ mapped.push(part);
1517
+ }
1518
+ if (field.isMultiChoice)
1519
+ return { value: mapped.join(",") };
1520
+ const only = mapped[0];
1521
+ return { value: /^\d+$/.test(only) ? Number(only) : only };
1522
+ }
1523
+ function missingRequiredValues(entity, columns, values, attachments, foreignKeys) {
1524
+ const declared = new Set(columns.map((column) => column.field.name));
1525
+ return seedableFields(entity).filter((field) => field.isRequired && declared.has(field.name)).filter((field) => {
1526
+ if (field.isAttachment) {
1527
+ return attachments[field.name] === undefined;
1528
+ }
1529
+ if (field.isForeignKey) {
1530
+ return foreignKeys[field.name] === undefined && values[field.name] === undefined;
1531
+ }
1532
+ return values[field.name] === undefined || values[field.name] === null;
1533
+ }).map((field) => field.name);
1534
+ }
1535
+ function relationshipKey(row, field) {
1536
+ const reference = row.foreignKeys[field.name];
1537
+ if (reference) {
1538
+ return `${reference.entity}\x00${reference.localId.toLowerCase()}`;
1539
+ }
1540
+ return matchValue(row.values[field.name], field);
1541
+ }
1542
+ function uniqueValuesFree(row, columns, seen, input, errors) {
1543
+ for (const { field } of columns) {
1544
+ if (!field.isUnique || field.isAttachment)
1545
+ continue;
1546
+ const value = field.isForeignKey ? relationshipKey(row, field) : normalizeForCompare(row.values[field.name], field).toLowerCase();
1547
+ if (value === "")
1548
+ continue;
1549
+ const tracker = seen.get(field.name) ?? new Map;
1550
+ seen.set(field.name, tracker);
1551
+ const first = tracker.get(value);
1552
+ if (first !== undefined) {
1553
+ errors.push(`${input.entityName} row ${row.line}: duplicate value "${row.display[field.name] ?? value}" for unique field "${field.name}" (first seen in row ${first}).`);
1554
+ return false;
1555
+ }
1556
+ tracker.set(value, row.line);
1557
+ }
1558
+ return true;
1559
+ }
1560
+
1561
+ // src/services/seed/plan-compare.ts
1562
+ function compareWithLive(row, recordId, live, columns, input, pendingFiles) {
1563
+ const changes = fieldMap();
1564
+ const optional = fieldMap();
1565
+ const attachmentChanges = fieldMap();
1566
+ const optionalClears = [];
1567
+ for (const { field } of columns) {
1568
+ const liveValue = ownValue(live, field.name);
1569
+ if (field.isAttachment) {
1570
+ if (pendingFiles?.includes(field.name) && row.attachments[field.name] !== undefined) {
1571
+ continue;
1572
+ }
1573
+ compareAttachment({
1574
+ row,
1575
+ field,
1576
+ liveValue,
1577
+ input,
1578
+ changes,
1579
+ optional,
1580
+ attachmentChanges,
1581
+ optionalClears
1582
+ });
1583
+ continue;
1584
+ }
1585
+ const scope = { row, field, liveValue, input, changes, optional };
1586
+ if (field.isForeignKey) {
1587
+ compareForeignKey(scope);
1588
+ continue;
1589
+ }
1590
+ compareScalar(scope);
1591
+ }
1592
+ return {
1593
+ ...row,
1594
+ recordId,
1595
+ changes,
1596
+ optional,
1597
+ attachmentChanges,
1598
+ optionalClears,
1599
+ allAttachments: row.attachments,
1600
+ ...pendingFiles && pendingFiles.length > 0 ? { pendingFiles } : {}
1601
+ };
1602
+ }
1603
+ function compareForeignKey({
1604
+ row,
1605
+ field,
1606
+ liveValue,
1607
+ input,
1608
+ changes,
1609
+ optional
1610
+ }) {
1611
+ const liveId = referenceId(liveValue);
1612
+ const ref = row.foreignKeys[field.name];
1613
+ if (ref !== undefined) {
1614
+ if (ref.entity !== input.entity.name.toLowerCase()) {
1615
+ changes[field.name] = {
1616
+ from: liveId || EMPTY,
1617
+ to: `${ref.localId} (local Id)`
1618
+ };
1619
+ }
1620
+ return;
1621
+ }
1622
+ const wanted = referenceId(row.values[field.name]);
1623
+ if (liveId.toLowerCase() === wanted.toLowerCase())
1624
+ return;
1625
+ const change = { from: liveId || EMPTY, to: wanted || EMPTY };
1626
+ if (wanted === "" && !field.isRequired) {
1627
+ optional[field.name] = change;
1628
+ } else {
1629
+ changes[field.name] = change;
1630
+ }
1631
+ }
1632
+ function compareScalar({
1633
+ row,
1634
+ field,
1635
+ liveValue,
1636
+ input,
1637
+ changes,
1638
+ optional
1639
+ }) {
1640
+ const liveText = normalizeForCompare(liveValue, field);
1641
+ const hasCell = Object.hasOwn(row.values, field.name);
1642
+ const wantedText = hasCell ? normalizeForCompare(row.values[field.name], field) : "";
1643
+ const preview = field.isLazyText ? lazyTextPreview(liveText) : undefined;
1644
+ if (preview) {
1645
+ compareLazyText({
1646
+ scope: { row, field, liveValue, input, changes, optional },
1647
+ liveText,
1648
+ liveHasValue: preview.hasValue,
1649
+ hasCell,
1650
+ wantedText
1651
+ });
1652
+ return;
1653
+ }
1654
+ if (roundLikeJson(liveText, field).toLowerCase() === roundLikeJson(wantedText, field).toLowerCase()) {
1655
+ return;
1656
+ }
1657
+ const choices = field.choiceSetId ? input.choiceValues?.get(field.name.toLowerCase()) : undefined;
1658
+ const change = {
1659
+ from: choiceLabels(liveText, choices) || EMPTY,
1660
+ to: choiceLabels(wantedText, choices) || EMPTY
1661
+ };
1662
+ if (!hasCell && !field.isRequired) {
1663
+ optional[field.name] = change;
1664
+ } else {
1665
+ changes[field.name] = change;
1666
+ }
1667
+ }
1668
+ function compareLazyText(input) {
1669
+ const { scope, liveText, liveHasValue, hasCell, wantedText } = input;
1670
+ const { field, changes, optional } = scope;
1671
+ if (!liveHasValue) {
1672
+ if (wantedText !== "") {
1673
+ changes[field.name] = { from: EMPTY, to: wantedText };
1674
+ }
1675
+ return;
1676
+ }
1677
+ if (wantedText !== "") {
1678
+ changes[field.name] = { from: liveText, to: wantedText };
1679
+ return;
1680
+ }
1681
+ const change = { from: liveText, to: EMPTY };
1682
+ if (!hasCell && !field.isRequired) {
1683
+ optional[field.name] = change;
1684
+ } else {
1685
+ changes[field.name] = change;
1686
+ }
1687
+ }
1688
+ function withSelfReferenceChanges(row, input, idMap) {
1689
+ const own = input.entity.name.toLowerCase();
1690
+ const live = input.liveRecords.find((record) => typeof record.Id === "string" && record.Id.toLowerCase() === row.recordId.toLowerCase());
1691
+ const changes = fieldMap(row.changes);
1692
+ let changed = false;
1693
+ for (const [field, ref] of Object.entries(row.foreignKeys)) {
1694
+ if (ref.entity !== own)
1695
+ continue;
1696
+ const liveId = referenceId(live === undefined ? undefined : ownValue(live, field));
1697
+ const wanted = idMap.get(ref.localId);
1698
+ if (wanted?.toLowerCase() === liveId.toLowerCase()) {
1699
+ continue;
1700
+ }
1701
+ changes[field] = {
1702
+ from: liveId || EMPTY,
1703
+ to: wanted ?? `${ref.localId} (local Id)`
1704
+ };
1705
+ changed = true;
1706
+ }
1707
+ return changed ? { ...row, changes } : row;
1708
+ }
1709
+ function compareAttachment({
1710
+ row,
1711
+ field,
1712
+ liveValue,
1713
+ input,
1714
+ changes,
1715
+ optional,
1716
+ attachmentChanges,
1717
+ optionalClears
1718
+ }) {
1719
+ const liveName = attachmentName(liveValue);
1720
+ const livePresent = hasAttachment(liveValue);
1721
+ const path = row.attachments[field.name];
1722
+ if (path === undefined) {
1723
+ if (livePresent) {
1724
+ optionalClears.push(field.name);
1725
+ optional[field.name] = {
1726
+ from: liveName || ATTACHED_FILE,
1727
+ to: EMPTY
1728
+ };
1729
+ }
1730
+ return;
1731
+ }
1732
+ const wantedName = path.slice(path.lastIndexOf("/") + 1);
1733
+ if (livePresent && !liveName) {
1734
+ return;
1735
+ }
1736
+ if (liveName.toLowerCase() !== wantedName.toLowerCase()) {
1737
+ changes[field.name] = { from: liveName || EMPTY, to: wantedName };
1738
+ attachmentChanges[field.name] = path;
1739
+ return;
1740
+ }
1741
+ const liveSize = attachmentSize(liveValue);
1742
+ const bundledSize = input.bundledFiles?.get(path);
1743
+ if (liveSize !== undefined && bundledSize !== undefined && liveSize !== bundledSize) {
1744
+ changes[field.name] = {
1745
+ from: `${liveName} (${liveSize} B)`,
1746
+ to: `${wantedName} (${bundledSize} B)`
1747
+ };
1748
+ attachmentChanges[field.name] = path;
1749
+ }
1750
+ }
1751
+
1752
+ // src/services/seed/plan-live.ts
1753
+ function recordsNewSincePlan(records, planned) {
1754
+ const seen = new Set(planned.map((record) => record.Id).filter((id) => typeof id === "string").map((id) => id.toLowerCase()));
1755
+ return records.filter((record) => typeof record.Id !== "string" || !seen.has(record.Id.toLowerCase()));
1756
+ }
1757
+ function indexLive(records, entity, columns, options = {}) {
1758
+ const claimed = new Set;
1759
+ const claim = (row, recordId) => {
1760
+ if (recordId === undefined || options.claimOnce === false) {
1761
+ return recordId;
1762
+ }
1763
+ const key = recordId.toLowerCase();
1764
+ if (claimed.has(key)) {
1765
+ options.onDoubleClaim?.(row, recordId);
1766
+ return;
1767
+ }
1768
+ claimed.add(key);
1769
+ return recordId;
1770
+ };
1771
+ const byId = new Map;
1772
+ for (const record of records) {
1773
+ if (typeof record.Id === "string") {
1774
+ byId.set(record.Id.toLowerCase(), record);
1775
+ }
1776
+ }
1777
+ const identity = entity.identityFields.map((name) => entity.fields.get(name.toLowerCase())).filter((field) => field !== undefined);
1778
+ const filledOf = contentFields(columns);
1779
+ const linksOf = linkFields(columns);
1780
+ if (identity.length > 0) {
1781
+ const indexes = new Map;
1782
+ const indexFor = (used) => {
1783
+ const signature = used.map((field) => field.name).join("\x00");
1784
+ const cached = indexes.get(signature);
1785
+ if (cached)
1786
+ return cached;
1787
+ const built = keyIndex(records, used);
1788
+ indexes.set(signature, built);
1789
+ return built;
1790
+ };
1791
+ const lookup2 = (row) => {
1792
+ const used = identity.filter((field) => {
1793
+ if (unresolvedRelationship(row, field))
1794
+ return false;
1795
+ if (field.isRequired)
1796
+ return true;
1797
+ if (!Object.hasOwn(row.values, field.name))
1798
+ return false;
1799
+ return row.values[field.name] === null || normalizeForCompare(row.values[field.name], field) !== "";
1800
+ });
1801
+ const key = used.length === 0 ? undefined : identityKey(used.map((field) => [field, row.values[field.name]]));
1802
+ if (key === undefined) {
1803
+ return options.contentFallback ? {
1804
+ recordId: soleContentMatch(records, row, [
1805
+ ...filledOf(row),
1806
+ ...identity
1807
+ ])
1808
+ } : {};
1809
+ }
1810
+ const { byKey, counts } = indexFor(used);
1811
+ const hits = counts.get(key) ?? 0;
1812
+ const ambiguous = ambiguityOf(row, used, hits);
1813
+ if (ambiguous)
1814
+ return { ambiguous };
1815
+ const recordId = byKey.get(key);
1816
+ return hits > 1 ? { recordId, shared: hits } : { recordId };
1817
+ };
1818
+ return liveIndex(byId, lookup2, claim);
1819
+ }
1820
+ const lookup = (row) => {
1821
+ const filled = filledOf(row);
1822
+ if (filled.length === 0)
1823
+ return {};
1824
+ const scalarHits = records.filter((record) => sameContent(record, row, filled));
1825
+ const hits = narrowByLinks(scalarHits, row, linksOf(row));
1826
+ const ambiguous = ambiguityOf(row, filled, hits.length);
1827
+ if (ambiguous)
1828
+ return { ambiguous };
1829
+ const hit = hits[0];
1830
+ if (typeof hit?.Id !== "string")
1831
+ return {};
1832
+ return hits.length > 1 ? { recordId: hit.Id, shared: hits.length } : { recordId: hit.Id };
1833
+ };
1834
+ return liveIndex(byId, lookup, claim);
1835
+ }
1836
+ function liveIndex(byId, lookup, claim) {
1837
+ return {
1838
+ byId,
1839
+ match: (row) => claim(row, lookup(row).recordId),
1840
+ ambiguous: (row) => lookup(row).ambiguous,
1841
+ shared: (row) => lookup(row).shared
1842
+ };
1843
+ }
1844
+ function keyIndex(records, used) {
1845
+ const byKey = new Map;
1846
+ const counts = new Map;
1847
+ for (const record of records) {
1848
+ if (typeof record.Id !== "string")
1849
+ continue;
1850
+ const key = identityKey(used.map((field) => [field, ownValue(record, field.name)]));
1851
+ if (key === undefined)
1852
+ continue;
1853
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1854
+ if (!byKey.has(key))
1855
+ byKey.set(key, record.Id);
1856
+ }
1857
+ return { byKey, counts };
1858
+ }
1859
+ function ambiguityOf(row, fields, records) {
1860
+ if (records < 2)
1861
+ return;
1862
+ const field = roundedBigintField(row, fields);
1863
+ return field ? { field, records } : undefined;
1864
+ }
1865
+ function roundedBigintField(row, fields) {
1866
+ return fields.find((field) => {
1867
+ if ((field.sqlType ?? "").toUpperCase() !== "BIGINT")
1868
+ return false;
1869
+ const text = normalizeForCompare(row.values[field.name], field);
1870
+ return /^[+-]?\d+$/.test(text) && !Number.isSafeInteger(Number(text));
1871
+ });
1872
+ }
1873
+ function contentFields(columns) {
1874
+ const scalars = columns.map((column) => column.field).filter((field) => !field.isForeignKey && !field.isAttachment);
1875
+ const relationships = columns.map((column) => column.field).filter((field) => field.isForeignKey);
1876
+ return (row) => {
1877
+ const filled = scalars.filter((field) => fills(row, field));
1878
+ return filled.length > 0 ? filled : relationships.filter((field) => fills(row, field));
1879
+ };
1880
+ }
1881
+ function linkFields(columns) {
1882
+ const relationships = columns.map((column) => column.field).filter((field) => field.isForeignKey);
1883
+ return (row) => relationships.filter((field) => fills(row, field));
1884
+ }
1885
+ function fills(row, field) {
1886
+ return Object.hasOwn(row.values, field.name) && !unresolvedRelationship(row, field);
1887
+ }
1888
+ function narrowByLinks(hits, row, links) {
1889
+ if (hits.length < 2 || links.length === 0)
1890
+ return hits;
1891
+ const narrowed = hits.filter((record) => sameContent(record, row, links));
1892
+ return narrowed.length > 0 ? narrowed : hits;
1893
+ }
1894
+ function sameContent(record, row, fields) {
1895
+ const usePreviews = fields.some((field) => !field.isLazyText && normalizeForCompare(row.values[field.name], field) !== "");
1896
+ return fields.every((field) => sameValue(ownValue(record, field.name), row.values[field.name], field, usePreviews));
1897
+ }
1898
+ function sameValue(live, wanted, field, usePreviews) {
1899
+ const liveText = normalizeForCompare(live, field);
1900
+ const preview = usePreviews && field.isLazyText ? lazyTextPreview(liveText) : undefined;
1901
+ if (!preview)
1902
+ return matchValue(live, field) === matchValue(wanted, field);
1903
+ const wantedText = normalizeForCompare(wanted, field);
1904
+ if (!preview.hasValue)
1905
+ return wantedText === "";
1906
+ const shown = truncatedPreviewText(liveText);
1907
+ return shown === undefined ? wantedText !== "" : wantedText.toLowerCase().startsWith(shown.toLowerCase());
1908
+ }
1909
+ function soleContentMatch(records, row, fields) {
1910
+ if (!fields.some((field) => Object.hasOwn(row.values, field.name))) {
1911
+ return;
1912
+ }
1913
+ const hits = records.flatMap((record) => typeof record.Id === "string" && sameContent(record, row, fields) ? [record.Id] : []);
1914
+ return hits.length === 1 ? hits[0] : undefined;
1915
+ }
1916
+ function unresolvedRelationship(row, field) {
1917
+ return field.isForeignKey && Object.hasOwn(row.foreignKeys, field.name);
1918
+ }
1919
+ function locateRows(rows, records, entity, columns) {
1920
+ const live = indexLive(records, entity, columns, {
1921
+ claimOnce: false,
1922
+ contentFallback: true
1923
+ });
1924
+ const ids = rows.map((row) => live.match(row));
1925
+ const claims = new Map;
1926
+ for (const id of ids) {
1927
+ if (id !== undefined)
1928
+ claims.set(id, (claims.get(id) ?? 0) + 1);
1929
+ }
1930
+ return ids.map((id) => id !== undefined && claims.get(id) === 1 ? id : undefined);
1931
+ }
1932
+ function identityKey(parts) {
1933
+ const normalized = parts.map(([field, value]) => [field.name, matchValue(value, field)]);
1934
+ if (parts.every(([field, value]) => normalizeForCompare(value, field) === "")) {
1935
+ return;
1936
+ }
1937
+ return JSON.stringify(normalized);
1938
+ }
1939
+
1940
+ // src/services/seed/plan.ts
1941
+ function readStrategy(raw) {
1942
+ if (raw === undefined)
1943
+ return { strategy: "insert" };
1944
+ const lower = raw.toLowerCase();
1945
+ if (lower === "insert" || lower === "upsert") {
1946
+ return { strategy: lower };
1947
+ }
1948
+ return {
1949
+ strategy: "upsert",
1950
+ warning: `Unknown seeding strategy "${raw}" — treating it as upsert.`
1951
+ };
1952
+ }
1953
+ function buildEntityPlan(input) {
1954
+ const errors = [];
1955
+ const warnings = [];
1956
+ const [parseError, table] = catchError(() => parseCsv(input.csv));
1957
+ if (parseError || !table) {
1958
+ errors.push(`${input.entityName}: ${input.entityName}.csv could not be read — ${parseError?.message ?? "unknown error"}.`);
1959
+ return emptyPlan(input, errors, warnings, 0);
1960
+ }
1961
+ if (table.headers.length === 0) {
1962
+ return emptyPlan(input, errors, warnings, 0);
1963
+ }
1964
+ const columns = mapColumns(input.entity, table.headers, warnings);
1965
+ const noColumns = noSeedableColumns(input.entityName, table, columns);
1966
+ if (noColumns !== undefined) {
1967
+ errors.push(noColumns);
1968
+ return emptyPlan(input, errors, warnings, table.rows.length);
1969
+ }
1970
+ const missingRequired = requiredColumnsMissing(input.entity, columns);
1971
+ if (missingRequired.length > 0) {
1972
+ errors.push(`${input.entityName}: the CSV is missing required ${missingRequired.length === 1 ? "column" : "columns"}: ${missingRequired.join(", ")}.`);
1973
+ return emptyPlan(input, errors, warnings, table.rows.length);
1974
+ }
1975
+ const missingOptional = optionalColumnsMissing(input.entity, columns);
1976
+ if (missingOptional.length > 0) {
1977
+ warnings.push(`${input.entityName}: optional columns not in the CSV (Data Fabric defaults apply): ${missingOptional.join(", ")}.`);
1978
+ }
1979
+ const entityJournal = journalEntity(input.journal, input.entityName);
1980
+ const journalKeys = rowKeysFor(table.headers, table.rows);
1981
+ const localIds = localIdsFor(table.headers, table.rows);
1982
+ const live = indexLive(input.liveRecords, input.entity, columns, {
1983
+ onDoubleClaim: (row, recordId) => {
1984
+ warnings.push(`${input.entityName} ${row.label}: another row already matches ` + `record ${recordId}, so this one is planned as a new record. ` + "Give the entity an identity field, or make the rows tell " + "themselves apart, if that is not what you want.");
1985
+ }
1986
+ });
1987
+ const state = {
1988
+ input,
1989
+ columns,
1990
+ live,
1991
+ entityJournal,
1992
+ errors,
1993
+ warnings,
1994
+ uniqueSeen: new Map,
1995
+ firstLineOfLocalId: new Map,
1996
+ idMap: new Map,
1997
+ ownLocalIds: new Set(localIds.filter((localId) => localId !== "")),
1998
+ unresolved: new Set,
1999
+ journalChanged: false,
2000
+ toInsert: [],
2001
+ matched: []
2002
+ };
2003
+ for (const [index, cells] of table.rows.entries()) {
2004
+ planRow(state, cells, index, journalKeys[index], localIds[index]);
2005
+ }
2006
+ const { idMap, unresolved, journalChanged, toInsert, matched } = state;
2007
+ for (const [index, row] of matched.entries()) {
2008
+ matched[index] = withSelfReferenceChanges(row, input, idMap);
2009
+ }
2010
+ if (journalChanged) {
2011
+ putOwn(input.journal, input.entityName, entityJournal);
2012
+ }
2013
+ if (unresolved.size > 0) {
2014
+ warnings.push(`${input.entityName}: unresolved expressions (replaced with empty): ${[...unresolved].sort((a, b) => a.localeCompare(b)).join(", ")}. ` + describeUnresolved([...unresolved][0]));
2015
+ }
2016
+ return {
2017
+ entityName: input.entityName,
2018
+ entityId: input.entity.id,
2019
+ expandRecords: needsExpandedRecords(input.entity),
2020
+ requiredRelationships: requiredRelationshipsOf(input.entity),
2021
+ strategy: input.strategy,
2022
+ toInsert,
2023
+ ...classify(matched, input.strategy, false),
2024
+ optionalDiffs: matched.flatMap((row) => Object.entries(row.optional).map(([field, change]) => ({
2025
+ rowKey: row.key,
2026
+ label: row.label,
2027
+ field,
2028
+ live: change.from
2029
+ }))),
2030
+ idMap,
2031
+ journalChanged,
2032
+ rowCount: table.rows.length,
2033
+ errors,
2034
+ warnings,
2035
+ matched,
2036
+ locate: (rows, records) => locateRows(rows, recordsNewSincePlan(records, input.liveRecords), input.entity, columns)
2037
+ };
2038
+ }
2039
+ function planRow(state, cells, index, key, localId) {
2040
+ const { input, columns, errors } = state;
2041
+ const line = index + 2;
2042
+ if (localId) {
2043
+ const first = state.firstLineOfLocalId.get(localId);
2044
+ if (first !== undefined) {
2045
+ errors.push(`${input.entityName} row ${line}: duplicate local Id "${localId}" (also on row ${first}) — local Ids must be unique per entity.`);
2046
+ return;
2047
+ }
2048
+ state.firstLineOfLocalId.set(localId, line);
2049
+ }
2050
+ const row = buildRow({
2051
+ line,
2052
+ key,
2053
+ ...localId ? { localId } : {},
2054
+ label: rowLabel(key, line)
2055
+ }, cells, columns, input, errors, state.unresolved, state.ownLocalIds);
2056
+ if (!row)
2057
+ return;
2058
+ if (!uniqueValuesFree(row, columns, state.uniqueSeen, input, errors)) {
2059
+ return;
2060
+ }
2061
+ const journaled = Object.hasOwn(state.entityJournal, row.key) ? state.entityJournal[row.key] : undefined;
2062
+ const journaledId = journaled?.recordId;
2063
+ if (journaledId === undefined && !liveMatchIsClear(state, row))
2064
+ return;
2065
+ const recordId = journaledId === undefined ? adoptLiveMatch(state, row) : journaledId;
2066
+ if (recordId === undefined) {
2067
+ state.toInsert.push(row);
2068
+ return;
2069
+ }
2070
+ if (localId)
2071
+ state.idMap.set(localId, recordId);
2072
+ const liveRecord = state.live.byId.get(recordId.toLowerCase());
2073
+ state.matched.push(compareWithLive(row, recordId, liveRecord ?? {}, columns, input, journaled?.pendingFiles));
2074
+ }
2075
+ function liveMatchIsClear(state, row) {
2076
+ const ambiguous = state.live.ambiguous(row);
2077
+ if (ambiguous === undefined)
2078
+ return true;
2079
+ const { field, records } = ambiguous;
2080
+ const name = state.input.entityName;
2081
+ const reported = comparableText(row.values[field.name], field);
2082
+ state.errors.push(`${name} row ${row.line}, field "${field.name}": ${records} live ` + `records share the value ${reported} as the service reports it ` + "(values past 2^53 lose precision), so seed cannot tell which " + `one this row is. ${name} is not seeded until the duplicate is ` + "removed or the row is journaled.");
2083
+ return false;
2084
+ }
2085
+ function adoptLiveMatch(state, row) {
2086
+ const recordId = state.live.match(row);
2087
+ if (recordId === undefined)
2088
+ return;
2089
+ const shared = state.live.shared(row);
2090
+ if (shared !== undefined) {
2091
+ state.warnings.push(`${state.input.entityName} row ${row.line}: ${shared} live records match every ` + "column this row fills in, so which one it is cannot be told — the first was " + "used and the match was not recorded. Remove the duplicates, or give the " + "entity a unique field, so re-runs stay on the same record.");
2092
+ return recordId;
2093
+ }
2094
+ putOwn(state.entityJournal, row.key, { recordId });
2095
+ state.journalChanged = true;
2096
+ return recordId;
2097
+ }
2098
+ function withOptionalClears(plan) {
2099
+ return {
2100
+ ...plan,
2101
+ ...classify(plan.matched, plan.strategy, true)
2102
+ };
2103
+ }
2104
+ function classify(matched, strategy, includeOptional) {
2105
+ const toUpdate = [];
2106
+ const toRepairAttachments = [];
2107
+ const ignoredRows = [];
2108
+ let unchanged = 0;
2109
+ for (const row of matched) {
2110
+ const { changes, clears } = requestedChanges(row, includeOptional);
2111
+ const pending = (row.pendingFiles ?? []).filter((field) => Object.hasOwn(row.allAttachments, field));
2112
+ if (Object.keys(changes).length === 0) {
2113
+ if (pending.length > 0) {
2114
+ toRepairAttachments.push(repairRow(row, pending));
2115
+ } else {
2116
+ unchanged += 1;
2117
+ }
2118
+ continue;
2119
+ }
2120
+ if (strategy === "insert") {
2121
+ ignoredRows.push({ ...withoutMatchDetail(row), changes });
2122
+ if (pending.length > 0) {
2123
+ toRepairAttachments.push(repairRow(row, pending));
2124
+ }
2125
+ continue;
2126
+ }
2127
+ toUpdate.push(updateRow(row, changes, clears, pending));
2128
+ }
2129
+ return {
2130
+ toUpdate,
2131
+ toRepairAttachments,
2132
+ ignoredRows,
2133
+ ignored: ignoredRows.length,
2134
+ unchanged
2135
+ };
2136
+ }
2137
+ function requestedChanges(row, includeOptional) {
2138
+ const changes = fieldMap(row.changes);
2139
+ if (includeOptional)
2140
+ Object.assign(changes, row.optional);
2141
+ const clears = includeOptional ? row.optionalClears : [];
2142
+ return { changes, clears };
2143
+ }
2144
+ function updateRow(row, changes, clears, pending) {
2145
+ const values = fieldMap();
2146
+ for (const field of Object.keys(changes)) {
2147
+ if (Object.hasOwn(row.attachmentChanges, field) || clears.includes(field) || Object.hasOwn(row.foreignKeys, field)) {
2148
+ continue;
2149
+ }
2150
+ values[field] = Object.hasOwn(row.values, field) ? row.values[field] : null;
2151
+ }
2152
+ const attachments = fieldMap(row.attachmentChanges);
2153
+ for (const field of pending) {
2154
+ attachments[field] = row.allAttachments[field];
2155
+ }
2156
+ return {
2157
+ ...withoutMatchDetail(row),
2158
+ values,
2159
+ attachments,
2160
+ ...clears.length > 0 ? { attachmentClears: clears } : {},
2161
+ changes
2162
+ };
2163
+ }
2164
+ function repairRow(row, pending) {
2165
+ return {
2166
+ ...withoutMatchDetail(row),
2167
+ values: fieldMap(),
2168
+ attachments: fieldMap(Object.fromEntries(pending.map((field) => [field, row.allAttachments[field]]))),
2169
+ pendingFiles: pending
2170
+ };
2171
+ }
2172
+ function withoutMatchDetail(row) {
2173
+ const {
2174
+ optional: _optional,
2175
+ attachmentChanges: _attachmentChanges,
2176
+ optionalClears: _optionalClears,
2177
+ allAttachments: _allAttachments,
2178
+ pendingFiles: _pendingFiles,
2179
+ ...planned
2180
+ } = row;
2181
+ return planned;
2182
+ }
2183
+ function emptyPlan(input, errors, warnings, rowCount) {
2184
+ return {
2185
+ entityName: input.entityName,
2186
+ entityId: input.entity.id,
2187
+ expandRecords: needsExpandedRecords(input.entity),
2188
+ requiredRelationships: requiredRelationshipsOf(input.entity),
2189
+ strategy: input.strategy,
2190
+ toInsert: [],
2191
+ toUpdate: [],
2192
+ toRepairAttachments: [],
2193
+ ignoredRows: [],
2194
+ ignored: 0,
2195
+ unchanged: 0,
2196
+ optionalDiffs: [],
2197
+ idMap: new Map,
2198
+ journalChanged: false,
2199
+ rowCount,
2200
+ errors,
2201
+ warnings,
2202
+ matched: [],
2203
+ locate: (rows) => rows.map(() => {
2204
+ return;
2205
+ })
2206
+ };
2207
+ }
2208
+ function requiredRelationshipsOf(entity) {
2209
+ const names = new Set;
2210
+ for (const field of entity.fields.values()) {
2211
+ if (field.isForeignKey && field.isRequired && !field.isAttachment) {
2212
+ names.add(field.name);
2213
+ }
2214
+ }
2215
+ return names;
2216
+ }
2217
+
2218
+ // src/services/seed/seed-file.ts
2219
+ async function readSeedFile(fs, workspaceDir, entityName) {
2220
+ const nameProblem = entityNameProblem(entityName);
2221
+ if (nameProblem) {
2222
+ return { kind: "invalid", path: workspaceDir, why: nameProblem };
2223
+ }
2224
+ const path = fs.path.join(workspaceDir, `${entityName}.csv`);
2225
+ const linkProblem = await seedFileLinkProblem(fs, workspaceDir, path);
2226
+ if (linkProblem)
2227
+ return { kind: "invalid", path, why: linkProblem };
2228
+ const [readError, csv] = await catchError(fs.readFile(path, "utf-8"));
2229
+ if (readError) {
2230
+ return { kind: "invalid", path, why: readError.message };
2231
+ }
2232
+ if (csv === null)
2233
+ return { kind: "absent", path };
2234
+ const [parseError, table] = catchError(() => parseCsv(csv));
2235
+ if (parseError || !table) {
2236
+ return {
2237
+ kind: "invalid",
2238
+ path,
2239
+ why: parseError?.message ?? "unknown error"
2240
+ };
2241
+ }
2242
+ return { kind: "table", path, csv, table };
2243
+ }
2244
+ async function seedFileLinkProblem(fs, workspaceDir, path) {
2245
+ const [rootError, rooted] = await catchError(async () => {
2246
+ if (!await namesSeedRoot(workspaceDir, fs))
2247
+ return;
2248
+ return insideSeedRoot(workspaceDir, fs);
2249
+ });
2250
+ if (rootError) {
2251
+ return `Could not check where the workspace leads: ${rootError.message}`;
2252
+ }
2253
+ if (rooted && !rooted.ok)
2254
+ return rooted.message;
2255
+ const [checkError, inside] = await catchError(async () => {
2256
+ const root = await workspaceRoot(workspaceDir, fs);
2257
+ return await isInsideWorkspace(root, path, fs) ? root : undefined;
2258
+ });
2259
+ if (checkError) {
2260
+ return `Could not check where the file leads: ${checkError.message}`;
2261
+ }
2262
+ if (!inside) {
2263
+ return "The file is a link that leads outside the seed workspace; " + "refusing to read it. Replace the link with the file itself.";
2264
+ }
2265
+ const [danglingError, dangling] = await catchError(isDanglingLink(path, fs));
2266
+ if (danglingError) {
2267
+ return `Could not check where the file leads: ${danglingError.message}`;
2268
+ }
2269
+ if (dangling) {
2270
+ return "The file is a link to a path that does not exist; refusing to " + "read it. Replace the link with the file itself.";
2271
+ }
2272
+ const [aliasError, ownFile] = await catchError(pathHasExpectedTarget(inside, path, fs));
2273
+ if (aliasError || ownFile === undefined) {
2274
+ return `Could not check where the file leads: ${aliasError?.message ?? "the folder could not be listed"}`;
2275
+ }
2276
+ return ownFile ? undefined : "The file is a link to another file in the seed workspace; " + "refusing to read it — that file's rows would be seeded as " + "this entity's. Replace the link with the file itself.";
2277
+ }
2278
+
2279
+ export { configKeyProblem, listVerticals, listPackages, listPackageVersions, normalizeVersions, getManifest, findFolderPath, orchestratorApi, listAllFolders, listFolderDtos, readAllPages, writeSeedContext, removeSeedContext, readSeedContext, BatchResponseSchema, WrittenRecordSchema, FailureRecordSchema, needsExpandedRecords, loadLiveEntities, loadChoiceSetValues, loadLiveRecords, journalEntity, putOwn, fieldMap, readStrategy, buildEntityPlan, withOptionalClears, readSeedFile };
2280
+
2281
+ //# debugId=6FF4397A8C3FBAFA64756E2164756E21