@objectstack/rest 12.4.0 → 12.6.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
@@ -519,6 +519,17 @@ async function coerceRow(rawRow, metaMap, ctx) {
519
519
  }
520
520
 
521
521
  // src/import-runner.ts
522
+ import { bulkWrite, withTransientRetry } from "@objectstack/core";
523
+ function extractRecordId(rec) {
524
+ const id = rec?.id ?? rec?.record?.id;
525
+ return id != null ? String(id) : void 0;
526
+ }
527
+ function toFailedResult(rowNo, err) {
528
+ const code = err?.code ?? "IMPORT_ROW_FAILED";
529
+ const message = typeof err?.message === "string" ? err.message.slice(0, 300) : "Row failed";
530
+ return { row: rowNo, ok: false, action: "failed", error: message, code };
531
+ }
532
+ var MAX_CREATE_BATCH_SIZE = 200;
522
533
  function runImport(opts) {
523
534
  const {
524
535
  p,
@@ -606,7 +617,7 @@ function runImport(opts) {
606
617
  return recs[0];
607
618
  };
608
619
  const writeCtx = { ...context ?? {}, skipAutomations: !runAutomations };
609
- const results = [];
620
+ const results = new Array(rows.length);
610
621
  let okCount = 0, errCount = 0, created = 0, updated = 0, skipped = 0;
611
622
  let cancelled = false;
612
623
  const snapshot = (processed) => ({
@@ -617,6 +628,47 @@ function runImport(opts) {
617
628
  skipped,
618
629
  errors: errCount
619
630
  });
631
+ const canBulkCreate = typeof p.createManyData === "function";
632
+ const pendingCreates = [];
633
+ const flushPendingCreates = async () => {
634
+ if (pendingCreates.length === 0) return;
635
+ const batch = pendingCreates.splice(0, pendingCreates.length);
636
+ const writeResults = await bulkWrite(
637
+ batch.map((b) => b.data),
638
+ {
639
+ // Flush cadence follows progressEvery, but the write batch itself is
640
+ // capped independently — a caller-supplied progressEvery far above
641
+ // the issue's suggested 100-500 rows/batch must not translate into
642
+ // one oversized multi-row INSERT statement.
643
+ batchSize: Math.min(progressEvery, MAX_CREATE_BATCH_SIZE),
644
+ writeBatch: (chunk) => p.createManyData({
645
+ object: objectName,
646
+ records: chunk,
647
+ context: writeCtx,
648
+ ...environmentId ? { environmentId } : {}
649
+ }).then((r) => r.records),
650
+ writeOne: (row) => p.createData({
651
+ object: objectName,
652
+ data: row,
653
+ context: writeCtx,
654
+ ...environmentId ? { environmentId } : {}
655
+ })
656
+ }
657
+ );
658
+ for (const res of writeResults) {
659
+ const { index, rowNo } = batch[res.index];
660
+ if (res.ok) {
661
+ const id = extractRecordId(res.record);
662
+ okCount++;
663
+ created++;
664
+ if (collectUndo && id != null) undoLog.created.push(id);
665
+ results[index] = { row: rowNo, ok: true, action: "created", id };
666
+ } else {
667
+ errCount++;
668
+ results[index] = toFailedResult(rowNo, res.error);
669
+ }
670
+ }
671
+ };
620
672
  return (async () => {
621
673
  for (let i = 0; i < rows.length; i++) {
622
674
  const rowNo = i + 1;
@@ -630,7 +682,7 @@ function runImport(opts) {
630
682
  if (errors.length > 0) {
631
683
  const first2 = errors[0];
632
684
  errCount++;
633
- results.push({ row: rowNo, ok: false, action: "failed", field: first2.field, code: first2.code, error: first2.message });
685
+ results[i] = { row: rowNo, ok: false, action: "failed", field: first2.field, code: first2.code, error: first2.message };
634
686
  } else {
635
687
  let existing = "none";
636
688
  let handled = false;
@@ -638,11 +690,11 @@ function runImport(opts) {
638
690
  existing = await findExisting(data);
639
691
  if (existing === "ambiguous") {
640
692
  errCount++;
641
- results.push({ row: rowNo, ok: false, action: "failed", code: "AMBIGUOUS_MATCH", error: `matchFields matched more than one ${objectName} record` });
693
+ results[i] = { row: rowNo, ok: false, action: "failed", code: "AMBIGUOUS_MATCH", error: `matchFields matched more than one ${objectName} record` };
642
694
  handled = true;
643
695
  } else if (existing === "blank" && (skipBlankMatchKey || writeMode === "update")) {
644
696
  skipped++;
645
- results.push({ row: rowNo, ok: true, action: "skipped", code: "BLANK_MATCH_KEY" });
697
+ results[i] = { row: rowNo, ok: true, action: "skipped", code: "BLANK_MATCH_KEY" };
646
698
  handled = true;
647
699
  }
648
700
  }
@@ -651,45 +703,46 @@ function runImport(opts) {
651
703
  const willCreate = !willUpdate && (writeMode === "insert" || writeMode === "upsert");
652
704
  if (!willUpdate && !willCreate) {
653
705
  skipped++;
654
- results.push({ row: rowNo, ok: true, action: "skipped", code: "NO_MATCH" });
706
+ results[i] = { row: rowNo, ok: true, action: "skipped", code: "NO_MATCH" };
655
707
  } else if (dryRun) {
656
708
  okCount++;
657
709
  if (willUpdate) {
658
710
  updated++;
659
- results.push({ row: rowNo, ok: true, action: "updated", id: String(existing.id ?? "") || void 0 });
711
+ results[i] = { row: rowNo, ok: true, action: "updated", id: String(existing.id ?? "") || void 0 };
660
712
  } else {
661
713
  created++;
662
- results.push({ row: rowNo, ok: true, action: "created" });
714
+ results[i] = { row: rowNo, ok: true, action: "created" };
663
715
  }
664
716
  } else if (willUpdate) {
665
717
  const target = existing;
666
- const res2 = await p.updateData({ object: objectName, id: target.id, data, context: writeCtx, ...environmentId ? { environmentId } : {} });
667
- const id = res2?.id ?? res2?.record?.id ?? target.id;
718
+ const res2 = await withTransientRetry(() => p.updateData({ object: objectName, id: target.id, data, context: writeCtx, ...environmentId ? { environmentId } : {} }));
719
+ const id = extractRecordId(res2) ?? String(target.id);
668
720
  okCount++;
669
721
  updated++;
670
722
  if (collectUndo && target.id != null) {
671
723
  undoLog.updated.push({ id: String(target.id), before: captureBefore(target, data) });
672
724
  }
673
- results.push({ row: rowNo, ok: true, action: "updated", id: id != null ? String(id) : void 0 });
725
+ results[i] = { row: rowNo, ok: true, action: "updated", id };
726
+ } else if (canBulkCreate) {
727
+ pendingCreates.push({ index: i, rowNo, data });
674
728
  } else {
675
729
  const res2 = await p.createData({ object: objectName, data, context: writeCtx, ...environmentId ? { environmentId } : {} });
676
- const id = res2?.id ?? res2?.record?.id;
730
+ const id = extractRecordId(res2);
677
731
  okCount++;
678
732
  created++;
679
- if (collectUndo && id != null) undoLog.created.push(String(id));
680
- results.push({ row: rowNo, ok: true, action: "created", id: id != null ? String(id) : void 0 });
733
+ if (collectUndo && id != null) undoLog.created.push(id);
734
+ results[i] = { row: rowNo, ok: true, action: "created", id };
681
735
  }
682
736
  }
683
737
  }
684
738
  } catch (err) {
685
739
  errCount++;
686
- const code = err?.code ?? "IMPORT_ROW_FAILED";
687
- const message = typeof err?.message === "string" ? err.message.slice(0, 300) : "Row failed";
688
- results.push({ row: rowNo, ok: false, action: "failed", error: message, code });
740
+ results[i] = toFailedResult(rowNo, err);
689
741
  }
690
742
  const processed = i + 1;
691
- if (onProgress && (processed % progressEvery === 0 || processed === rows.length)) {
692
- await onProgress(snapshot(processed));
743
+ if (processed % progressEvery === 0 || processed === rows.length) {
744
+ await flushPendingCreates();
745
+ if (onProgress) await onProgress(snapshot(processed));
693
746
  }
694
747
  if (shouldCancel && processed < rows.length && processed % progressEvery === 0) {
695
748
  if (await shouldCancel()) {
@@ -698,10 +751,12 @@ function runImport(opts) {
698
751
  }
699
752
  }
700
753
  }
754
+ await flushPendingCreates();
755
+ const compacted = results.filter((r) => r !== void 0);
701
756
  return {
702
- ...snapshot(results.length),
757
+ ...snapshot(compacted.length),
703
758
  ok: okCount,
704
- results,
759
+ results: compacted,
705
760
  cancelled,
706
761
  ...collectUndo ? { undoLog } : {}
707
762
  };