@objectstack/runtime 8.0.1 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -143,601 +143,10 @@ var seed_loader_exports = {};
143
143
  __export(seed_loader_exports, {
144
144
  SeedLoaderService: () => SeedLoaderService
145
145
  });
146
- import { SeedLoaderConfigSchema } from "@objectstack/spec/data";
147
- import { resolveSeedRecord } from "@objectstack/formula";
148
- var DEFAULT_EXTERNAL_ID_FIELD, _SeedLoaderService, SeedLoaderService;
146
+ import { SeedLoaderService } from "@objectstack/objectql";
149
147
  var init_seed_loader = __esm({
150
148
  "src/seed-loader.ts"() {
151
149
  "use strict";
152
- DEFAULT_EXTERNAL_ID_FIELD = "name";
153
- _SeedLoaderService = class _SeedLoaderService {
154
- constructor(engine, metadata, logger) {
155
- this.engine = engine;
156
- this.metadata = metadata;
157
- this.logger = logger;
158
- }
159
- // ==========================================================================
160
- // Public API
161
- // ==========================================================================
162
- async load(request) {
163
- const startTime = Date.now();
164
- const config = request.config;
165
- const allErrors = [];
166
- const allResults = [];
167
- const datasets = this.filterByEnv(request.seeds, config.env);
168
- if (datasets.length === 0) {
169
- return this.buildEmptyResult(config, Date.now() - startTime);
170
- }
171
- const objectNames = datasets.map((d) => d.object);
172
- const graph = await this.buildDependencyGraph(objectNames);
173
- this.logger.info("[SeedLoader] Dependency graph built", {
174
- objects: objectNames.length,
175
- insertOrder: graph.insertOrder,
176
- circularDeps: graph.circularDependencies.length
177
- });
178
- const orderedDatasets = this.orderDatasets(datasets, graph.insertOrder);
179
- const refMap = this.buildReferenceMap(graph);
180
- const insertedRecords = /* @__PURE__ */ new Map();
181
- const deferredUpdates = [];
182
- for (const dataset of orderedDatasets) {
183
- const result = await this.loadDataset(
184
- dataset,
185
- config,
186
- refMap,
187
- insertedRecords,
188
- deferredUpdates,
189
- allErrors
190
- );
191
- allResults.push(result);
192
- if (config.haltOnError && result.errored > 0) {
193
- this.logger.warn("[SeedLoader] Halting on first error", { object: dataset.object });
194
- break;
195
- }
196
- }
197
- if (config.multiPass && deferredUpdates.length > 0 && !config.dryRun) {
198
- this.logger.info("[SeedLoader] Pass 2: resolving deferred references", {
199
- count: deferredUpdates.length
200
- });
201
- await this.resolveDeferredUpdates(deferredUpdates, insertedRecords, allResults, allErrors, config.organizationId);
202
- }
203
- const durationMs = Date.now() - startTime;
204
- return this.buildResult(config, graph, allResults, allErrors, durationMs);
205
- }
206
- async buildDependencyGraph(objectNames) {
207
- const nodes = [];
208
- const objectSet = new Set(objectNames);
209
- for (const objectName of objectNames) {
210
- const objDef = await this.metadata.getObject(objectName);
211
- const dependsOn = [];
212
- const references = [];
213
- if (objDef && objDef.fields) {
214
- const fields = objDef.fields;
215
- for (const [fieldName, fieldDef] of Object.entries(fields)) {
216
- if ((fieldDef.type === "lookup" || fieldDef.type === "master_detail") && fieldDef.reference) {
217
- const targetObject = fieldDef.reference;
218
- if (objectSet.has(targetObject) && !dependsOn.includes(targetObject)) {
219
- dependsOn.push(targetObject);
220
- }
221
- references.push({
222
- field: fieldName,
223
- targetObject,
224
- targetField: DEFAULT_EXTERNAL_ID_FIELD,
225
- fieldType: fieldDef.type
226
- });
227
- }
228
- }
229
- }
230
- nodes.push({ object: objectName, dependsOn, references });
231
- }
232
- const { insertOrder, circularDependencies } = this.topologicalSort(nodes);
233
- return { nodes, insertOrder, circularDependencies };
234
- }
235
- async validate(datasets, config) {
236
- const parsedConfig = SeedLoaderConfigSchema.parse({ ...config, dryRun: true });
237
- return this.load({ seeds: datasets, config: parsedConfig });
238
- }
239
- // ==========================================================================
240
- // Internal: Seed Loading
241
- // ==========================================================================
242
- async loadDataset(dataset, config, refMap, insertedRecords, deferredUpdates, allErrors) {
243
- const objectName = dataset.object;
244
- const mode = dataset.mode || config.defaultMode;
245
- const externalId = dataset.externalId || "name";
246
- let inserted = 0;
247
- let updated = 0;
248
- let skipped = 0;
249
- let errored = 0;
250
- let referencesResolved = 0;
251
- let referencesDeferred = 0;
252
- const errors = [];
253
- if (!insertedRecords.has(objectName)) {
254
- insertedRecords.set(objectName, /* @__PURE__ */ new Map());
255
- }
256
- let existingRecords;
257
- if ((mode === "upsert" || mode === "update" || mode === "ignore") && !config.dryRun) {
258
- existingRecords = await this.loadExistingRecords(
259
- objectName,
260
- externalId,
261
- config.organizationId
262
- );
263
- }
264
- const objectRefs = refMap.get(objectName) || [];
265
- const seedNow = /* @__PURE__ */ new Date();
266
- const seedIdentity = config.identity;
267
- const baseEvalCtx = {
268
- now: seedNow,
269
- user: seedIdentity?.user,
270
- // Fall back to the per-tenant organizationId so `os.org.id` resolves
271
- // during per-org replay even without an explicit identity.org.
272
- org: seedIdentity?.org ?? (config.organizationId ? { id: config.organizationId } : void 0),
273
- env: config.env
274
- };
275
- for (let i = 0; i < dataset.records.length; i++) {
276
- const seedResult = resolveSeedRecord(
277
- dataset.records[i],
278
- baseEvalCtx
279
- );
280
- if (!seedResult.ok) {
281
- errored++;
282
- const error = {
283
- sourceObject: objectName,
284
- field: "(expression)",
285
- targetObject: objectName,
286
- targetField: "(expression)",
287
- attemptedValue: dataset.records[i],
288
- recordIndex: i,
289
- message: `Cannot resolve dynamic seed values for ${objectName} record #${i}: ${seedResult.error.message}. Records using cel\`os.user.id\` / cel\`os.org.id\` require a seed identity \u2014 ensure a system/admin user exists before seeding (see SeedLoaderConfig.identity).`
290
- };
291
- errors.push(error);
292
- allErrors.push(error);
293
- this.logger.warn(`[SeedLoader] ${error.message}`);
294
- continue;
295
- }
296
- const record = { ...seedResult.value };
297
- if (config.organizationId && record["organization_id"] == null) {
298
- record["organization_id"] = config.organizationId;
299
- }
300
- for (const ref of objectRefs) {
301
- const fieldValue = record[ref.field];
302
- if (fieldValue === void 0 || fieldValue === null) continue;
303
- if (typeof fieldValue === "object") {
304
- const wrapped = fieldValue.externalId;
305
- const hint = wrapped !== void 0 ? ` Pass the natural key directly: ${ref.field}: ${JSON.stringify(wrapped)}.` : ` Pass the target's ${ref.targetField} value as a plain string.`;
306
- const error = {
307
- sourceObject: objectName,
308
- field: ref.field,
309
- targetObject: ref.targetObject,
310
- targetField: ref.targetField,
311
- attemptedValue: fieldValue,
312
- recordIndex: i,
313
- message: `Invalid reference for ${objectName}.${ref.field}: expected a ${ref.targetObject}.${ref.targetField} natural-key string but got an object.${hint}`
314
- };
315
- errors.push(error);
316
- allErrors.push(error);
317
- this.logger.warn(`[SeedLoader] ${error.message}`, { recordIndex: i });
318
- record[ref.field] = null;
319
- continue;
320
- }
321
- if (typeof fieldValue !== "string" || this.looksLikeInternalId(fieldValue)) continue;
322
- const targetMap = insertedRecords.get(ref.targetObject);
323
- const resolvedId = targetMap?.get(String(fieldValue));
324
- if (resolvedId) {
325
- record[ref.field] = resolvedId;
326
- referencesResolved++;
327
- } else if (!config.dryRun) {
328
- const dbId = await this.resolveFromDatabase(ref.targetObject, ref.targetField, fieldValue, config.organizationId);
329
- if (dbId) {
330
- record[ref.field] = dbId;
331
- referencesResolved++;
332
- } else if (config.multiPass) {
333
- record[ref.field] = null;
334
- deferredUpdates.push({
335
- objectName,
336
- recordExternalId: String(record[externalId] ?? ""),
337
- field: ref.field,
338
- targetObject: ref.targetObject,
339
- targetField: ref.targetField,
340
- attemptedValue: fieldValue,
341
- recordIndex: i
342
- });
343
- referencesDeferred++;
344
- } else {
345
- const error = {
346
- sourceObject: objectName,
347
- field: ref.field,
348
- targetObject: ref.targetObject,
349
- targetField: ref.targetField,
350
- attemptedValue: fieldValue,
351
- recordIndex: i,
352
- message: `Cannot resolve reference: ${objectName}.${ref.field} = '${fieldValue}' \u2192 ${ref.targetObject}.${ref.targetField} not found`
353
- };
354
- errors.push(error);
355
- allErrors.push(error);
356
- }
357
- } else {
358
- const targetMap2 = insertedRecords.get(ref.targetObject);
359
- if (!targetMap2?.has(String(fieldValue))) {
360
- const error = {
361
- sourceObject: objectName,
362
- field: ref.field,
363
- targetObject: ref.targetObject,
364
- targetField: ref.targetField,
365
- attemptedValue: fieldValue,
366
- recordIndex: i,
367
- message: `[dry-run] Reference may not resolve: ${objectName}.${ref.field} = '${fieldValue}' \u2192 ${ref.targetObject}.${ref.targetField}`
368
- };
369
- errors.push(error);
370
- allErrors.push(error);
371
- }
372
- }
373
- }
374
- if (!config.dryRun) {
375
- try {
376
- const result = await this.writeRecord(
377
- objectName,
378
- record,
379
- mode,
380
- externalId,
381
- existingRecords
382
- );
383
- if (result.action === "inserted") inserted++;
384
- else if (result.action === "updated") updated++;
385
- else if (result.action === "skipped") skipped++;
386
- const externalIdValue = String(record[externalId] ?? "");
387
- const internalId = result.id;
388
- if (externalIdValue && internalId) {
389
- insertedRecords.get(objectName).set(externalIdValue, String(internalId));
390
- }
391
- } catch (err) {
392
- errored++;
393
- const error = {
394
- sourceObject: objectName,
395
- field: "(write)",
396
- targetObject: objectName,
397
- targetField: externalId,
398
- attemptedValue: record[externalId] ?? null,
399
- recordIndex: i,
400
- message: `Failed to write ${objectName} record #${i} (${externalId}=${String(record[externalId] ?? "")}): ${err.message}`
401
- };
402
- errors.push(error);
403
- allErrors.push(error);
404
- this.logger.warn(`[SeedLoader] ${error.message}`, { recordIndex: i });
405
- }
406
- } else {
407
- const externalIdValue = String(record[externalId] ?? "");
408
- if (externalIdValue) {
409
- insertedRecords.get(objectName).set(externalIdValue, `dry-run-id-${i}`);
410
- }
411
- inserted++;
412
- }
413
- }
414
- return {
415
- object: objectName,
416
- mode,
417
- inserted,
418
- updated,
419
- skipped,
420
- errored,
421
- total: dataset.records.length,
422
- referencesResolved,
423
- referencesDeferred,
424
- errors
425
- };
426
- }
427
- // ==========================================================================
428
- // Internal: Reference Resolution
429
- // ==========================================================================
430
- async resolveFromDatabase(targetObject, targetField, value, organizationId) {
431
- try {
432
- const where = { [targetField]: value };
433
- if (organizationId) where.organization_id = organizationId;
434
- const records = await this.engine.find(targetObject, {
435
- where,
436
- fields: ["id"],
437
- limit: 1,
438
- context: { isSystem: true }
439
- });
440
- if (records && records.length > 0) {
441
- return String(records[0].id || records[0]._id);
442
- }
443
- } catch {
444
- }
445
- return null;
446
- }
447
- async resolveDeferredUpdates(deferredUpdates, insertedRecords, allResults, allErrors, organizationId) {
448
- for (const deferred of deferredUpdates) {
449
- const targetMap = insertedRecords.get(deferred.targetObject);
450
- let resolvedId = targetMap?.get(String(deferred.attemptedValue));
451
- if (!resolvedId) {
452
- resolvedId = await this.resolveFromDatabase(
453
- deferred.targetObject,
454
- deferred.targetField,
455
- deferred.attemptedValue,
456
- organizationId
457
- ) ?? void 0;
458
- }
459
- if (resolvedId) {
460
- const objectRecordMap = insertedRecords.get(deferred.objectName);
461
- const recordId = objectRecordMap?.get(deferred.recordExternalId);
462
- if (recordId) {
463
- try {
464
- await this.engine.update(deferred.objectName, {
465
- id: recordId,
466
- [deferred.field]: resolvedId
467
- }, { context: { isSystem: true } });
468
- const resultEntry = allResults.find((r) => r.object === deferred.objectName);
469
- if (resultEntry) {
470
- resultEntry.referencesResolved++;
471
- resultEntry.referencesDeferred--;
472
- }
473
- } catch (err) {
474
- this.logger.warn("[SeedLoader] Failed to resolve deferred reference", {
475
- object: deferred.objectName,
476
- field: deferred.field,
477
- error: err.message
478
- });
479
- }
480
- }
481
- } else {
482
- const error = {
483
- sourceObject: deferred.objectName,
484
- field: deferred.field,
485
- targetObject: deferred.targetObject,
486
- targetField: deferred.targetField,
487
- attemptedValue: deferred.attemptedValue,
488
- recordIndex: deferred.recordIndex,
489
- message: `Deferred reference unresolved after pass 2: ${deferred.objectName}.${deferred.field} = '${deferred.attemptedValue}' \u2192 ${deferred.targetObject}.${deferred.targetField} not found`
490
- };
491
- const resultEntry = allResults.find((r) => r.object === deferred.objectName);
492
- if (resultEntry) {
493
- resultEntry.errors.push(error);
494
- }
495
- allErrors.push(error);
496
- }
497
- }
498
- }
499
- async writeRecord(objectName, record, mode, externalId, existingRecords) {
500
- const externalIdValue = record[externalId];
501
- const existing = existingRecords?.get(String(externalIdValue ?? ""));
502
- const opts = _SeedLoaderService.SEED_OPTIONS;
503
- switch (mode) {
504
- case "insert": {
505
- const result = await this.engine.insert(objectName, record, opts);
506
- return { action: "inserted", id: this.extractId(result) };
507
- }
508
- case "update": {
509
- if (!existing) {
510
- return { action: "skipped" };
511
- }
512
- const id = this.extractId(existing);
513
- await this.engine.update(objectName, { ...record, id }, opts);
514
- return { action: "updated", id };
515
- }
516
- case "upsert": {
517
- if (existing) {
518
- const id = this.extractId(existing);
519
- await this.engine.update(objectName, { ...record, id }, opts);
520
- return { action: "updated", id };
521
- } else {
522
- const result = await this.engine.insert(objectName, record, opts);
523
- return { action: "inserted", id: this.extractId(result) };
524
- }
525
- }
526
- case "ignore": {
527
- if (existing) {
528
- return { action: "skipped", id: this.extractId(existing) };
529
- }
530
- const result = await this.engine.insert(objectName, record, opts);
531
- return { action: "inserted", id: this.extractId(result) };
532
- }
533
- case "replace": {
534
- const result = await this.engine.insert(objectName, record, opts);
535
- return { action: "inserted", id: this.extractId(result) };
536
- }
537
- default: {
538
- const result = await this.engine.insert(objectName, record, opts);
539
- return { action: "inserted", id: this.extractId(result) };
540
- }
541
- }
542
- }
543
- // ==========================================================================
544
- // Internal: Dependency Graph
545
- // ==========================================================================
546
- /**
547
- * Kahn's algorithm for topological sort with cycle detection.
548
- */
549
- topologicalSort(nodes) {
550
- const inDegree = /* @__PURE__ */ new Map();
551
- const adjacency = /* @__PURE__ */ new Map();
552
- const objectSet = new Set(nodes.map((n) => n.object));
553
- for (const node of nodes) {
554
- inDegree.set(node.object, 0);
555
- adjacency.set(node.object, []);
556
- }
557
- for (const node of nodes) {
558
- for (const dep of node.dependsOn) {
559
- if (objectSet.has(dep) && dep !== node.object) {
560
- adjacency.get(dep).push(node.object);
561
- inDegree.set(node.object, (inDegree.get(node.object) || 0) + 1);
562
- }
563
- }
564
- }
565
- const queue = [];
566
- for (const [obj, degree] of inDegree) {
567
- if (degree === 0) queue.push(obj);
568
- }
569
- const insertOrder = [];
570
- while (queue.length > 0) {
571
- const current = queue.shift();
572
- insertOrder.push(current);
573
- for (const neighbor of adjacency.get(current) || []) {
574
- const newDegree = (inDegree.get(neighbor) || 0) - 1;
575
- inDegree.set(neighbor, newDegree);
576
- if (newDegree === 0) {
577
- queue.push(neighbor);
578
- }
579
- }
580
- }
581
- const circularDependencies = [];
582
- const remaining = nodes.filter((n) => !insertOrder.includes(n.object));
583
- if (remaining.length > 0) {
584
- const cycles = this.findCycles(remaining);
585
- circularDependencies.push(...cycles);
586
- for (const node of remaining) {
587
- if (!insertOrder.includes(node.object)) {
588
- insertOrder.push(node.object);
589
- }
590
- }
591
- }
592
- return { insertOrder, circularDependencies };
593
- }
594
- findCycles(nodes) {
595
- const cycles = [];
596
- const nodeMap = new Map(nodes.map((n) => [n.object, n]));
597
- const visited = /* @__PURE__ */ new Set();
598
- const inStack = /* @__PURE__ */ new Set();
599
- const dfs = (current, path) => {
600
- if (inStack.has(current)) {
601
- const cycleStart = path.indexOf(current);
602
- if (cycleStart !== -1) {
603
- cycles.push([...path.slice(cycleStart), current]);
604
- }
605
- return;
606
- }
607
- if (visited.has(current)) return;
608
- visited.add(current);
609
- inStack.add(current);
610
- path.push(current);
611
- const node = nodeMap.get(current);
612
- if (node) {
613
- for (const dep of node.dependsOn) {
614
- if (nodeMap.has(dep)) {
615
- dfs(dep, [...path]);
616
- }
617
- }
618
- }
619
- inStack.delete(current);
620
- };
621
- for (const node of nodes) {
622
- if (!visited.has(node.object)) {
623
- dfs(node.object, []);
624
- }
625
- }
626
- return cycles;
627
- }
628
- // ==========================================================================
629
- // Internal: Helpers
630
- // ==========================================================================
631
- filterByEnv(datasets, env) {
632
- if (!env) return datasets;
633
- return datasets.filter((d) => d.env.includes(env));
634
- }
635
- orderDatasets(datasets, insertOrder) {
636
- const orderMap = new Map(insertOrder.map((name, i) => [name, i]));
637
- return [...datasets].sort((a, b) => {
638
- const orderA = orderMap.get(a.object) ?? Number.MAX_SAFE_INTEGER;
639
- const orderB = orderMap.get(b.object) ?? Number.MAX_SAFE_INTEGER;
640
- return orderA - orderB;
641
- });
642
- }
643
- buildReferenceMap(graph) {
644
- const map = /* @__PURE__ */ new Map();
645
- for (const node of graph.nodes) {
646
- if (node.references.length > 0) {
647
- map.set(node.object, node.references);
648
- }
649
- }
650
- return map;
651
- }
652
- async loadExistingRecords(objectName, externalId, organizationId) {
653
- const map = /* @__PURE__ */ new Map();
654
- try {
655
- const findArgs = {
656
- fields: ["id", externalId],
657
- context: { isSystem: true }
658
- };
659
- if (organizationId) findArgs.where = { organization_id: organizationId };
660
- const records = await this.engine.find(objectName, findArgs);
661
- for (const record of records || []) {
662
- const key = String(record[externalId] ?? "");
663
- if (key) {
664
- map.set(key, record);
665
- }
666
- }
667
- } catch {
668
- }
669
- return map;
670
- }
671
- looksLikeInternalId(value) {
672
- if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) {
673
- return true;
674
- }
675
- if (/^[0-9a-f]{24}$/i.test(value)) {
676
- return true;
677
- }
678
- return false;
679
- }
680
- extractId(record) {
681
- if (!record) return void 0;
682
- return String(record.id || record._id || "");
683
- }
684
- buildEmptyResult(config, durationMs) {
685
- return {
686
- success: true,
687
- dryRun: config.dryRun,
688
- dependencyGraph: { nodes: [], insertOrder: [], circularDependencies: [] },
689
- results: [],
690
- errors: [],
691
- summary: {
692
- objectsProcessed: 0,
693
- totalRecords: 0,
694
- totalInserted: 0,
695
- totalUpdated: 0,
696
- totalSkipped: 0,
697
- totalErrored: 0,
698
- totalReferencesResolved: 0,
699
- totalReferencesDeferred: 0,
700
- circularDependencyCount: 0,
701
- durationMs
702
- }
703
- };
704
- }
705
- buildResult(config, graph, results, errors, durationMs) {
706
- const summary = {
707
- objectsProcessed: results.length,
708
- totalRecords: results.reduce((sum, r) => sum + r.total, 0),
709
- totalInserted: results.reduce((sum, r) => sum + r.inserted, 0),
710
- totalUpdated: results.reduce((sum, r) => sum + r.updated, 0),
711
- totalSkipped: results.reduce((sum, r) => sum + r.skipped, 0),
712
- totalErrored: results.reduce((sum, r) => sum + r.errored, 0),
713
- totalReferencesResolved: results.reduce((sum, r) => sum + r.referencesResolved, 0),
714
- totalReferencesDeferred: results.reduce((sum, r) => sum + r.referencesDeferred, 0),
715
- circularDependencyCount: graph.circularDependencies.length,
716
- durationMs
717
- };
718
- const hasErrors = errors.length > 0 || summary.totalErrored > 0;
719
- return {
720
- success: !hasErrors,
721
- dryRun: config.dryRun,
722
- dependencyGraph: graph,
723
- results,
724
- errors,
725
- summary
726
- };
727
- }
728
- };
729
- // ==========================================================================
730
- // Internal: Write Operations
731
- // ==========================================================================
732
- /**
733
- * Seed writes always run as a privileged system context. This bypasses
734
- * RBAC checks (so seeds can target system tables like `sys_*`) and
735
- * disables the SecurityPlugin's auto-injection of `organization_id` /
736
- * `owner_id` — seeds either declare those fields explicitly per
737
- * record, or are intentionally cross-tenant / global.
738
- */
739
- _SeedLoaderService.SEED_OPTIONS = { context: { isSystem: true } };
740
- SeedLoaderService = _SeedLoaderService;
741
150
  }
742
151
  });
743
152
 
@@ -3976,17 +3385,19 @@ var _HttpDispatcher = class _HttpDispatcher {
3976
3385
  ...organizationId ? { organizationId } : {},
3977
3386
  ...body?.actor ? { actor: body.actor } : {}
3978
3387
  });
3979
- try {
3980
- const seedNames = (result?.published ?? []).filter((p) => p?.type === "seed").map((p) => p.name);
3981
- if (seedNames.length > 0) {
3982
- result.seedApplied = await this.applyPublishedSeeds(
3983
- seedNames,
3984
- organizationId,
3985
- _context
3986
- );
3388
+ if (result?.seedApplied === void 0) {
3389
+ try {
3390
+ const seedNames = (result?.published ?? []).filter((p) => p?.type === "seed").map((p) => p.name);
3391
+ if (seedNames.length > 0) {
3392
+ result.seedApplied = await this.applyPublishedSeeds(
3393
+ seedNames,
3394
+ organizationId,
3395
+ _context
3396
+ );
3397
+ }
3398
+ } catch (e) {
3399
+ result.seedApplied = { success: false, error: e?.message ?? "seed apply failed" };
3987
3400
  }
3988
- } catch (e) {
3989
- result.seedApplied = { success: false, error: e?.message ?? "seed apply failed" };
3990
3401
  }
3991
3402
  return { handled: true, response: this.success(result) };
3992
3403
  } catch (e) {