@objectstack/rest 12.5.0 → 13.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.cjs +82 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +78 -23
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,8 @@ __export(index_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
40
|
// src/rest-server.ts
|
|
41
|
-
var
|
|
41
|
+
var import_core2 = require("@objectstack/core");
|
|
42
|
+
var import_types = require("@objectstack/types");
|
|
42
43
|
|
|
43
44
|
// src/route-manager.ts
|
|
44
45
|
var RouteManager = class {
|
|
@@ -558,6 +559,17 @@ async function coerceRow(rawRow, metaMap, ctx) {
|
|
|
558
559
|
}
|
|
559
560
|
|
|
560
561
|
// src/import-runner.ts
|
|
562
|
+
var import_core = require("@objectstack/core");
|
|
563
|
+
function extractRecordId(rec) {
|
|
564
|
+
const id = rec?.id ?? rec?.record?.id;
|
|
565
|
+
return id != null ? String(id) : void 0;
|
|
566
|
+
}
|
|
567
|
+
function toFailedResult(rowNo, err) {
|
|
568
|
+
const code = err?.code ?? "IMPORT_ROW_FAILED";
|
|
569
|
+
const message = typeof err?.message === "string" ? err.message.slice(0, 300) : "Row failed";
|
|
570
|
+
return { row: rowNo, ok: false, action: "failed", error: message, code };
|
|
571
|
+
}
|
|
572
|
+
var MAX_CREATE_BATCH_SIZE = 200;
|
|
561
573
|
function runImport(opts) {
|
|
562
574
|
const {
|
|
563
575
|
p,
|
|
@@ -645,7 +657,7 @@ function runImport(opts) {
|
|
|
645
657
|
return recs[0];
|
|
646
658
|
};
|
|
647
659
|
const writeCtx = { ...context ?? {}, skipAutomations: !runAutomations };
|
|
648
|
-
const results =
|
|
660
|
+
const results = new Array(rows.length);
|
|
649
661
|
let okCount = 0, errCount = 0, created = 0, updated = 0, skipped = 0;
|
|
650
662
|
let cancelled = false;
|
|
651
663
|
const snapshot = (processed) => ({
|
|
@@ -656,6 +668,47 @@ function runImport(opts) {
|
|
|
656
668
|
skipped,
|
|
657
669
|
errors: errCount
|
|
658
670
|
});
|
|
671
|
+
const canBulkCreate = typeof p.createManyData === "function";
|
|
672
|
+
const pendingCreates = [];
|
|
673
|
+
const flushPendingCreates = async () => {
|
|
674
|
+
if (pendingCreates.length === 0) return;
|
|
675
|
+
const batch = pendingCreates.splice(0, pendingCreates.length);
|
|
676
|
+
const writeResults = await (0, import_core.bulkWrite)(
|
|
677
|
+
batch.map((b) => b.data),
|
|
678
|
+
{
|
|
679
|
+
// Flush cadence follows progressEvery, but the write batch itself is
|
|
680
|
+
// capped independently — a caller-supplied progressEvery far above
|
|
681
|
+
// the issue's suggested 100-500 rows/batch must not translate into
|
|
682
|
+
// one oversized multi-row INSERT statement.
|
|
683
|
+
batchSize: Math.min(progressEvery, MAX_CREATE_BATCH_SIZE),
|
|
684
|
+
writeBatch: (chunk) => p.createManyData({
|
|
685
|
+
object: objectName,
|
|
686
|
+
records: chunk,
|
|
687
|
+
context: writeCtx,
|
|
688
|
+
...environmentId ? { environmentId } : {}
|
|
689
|
+
}).then((r) => r.records),
|
|
690
|
+
writeOne: (row) => p.createData({
|
|
691
|
+
object: objectName,
|
|
692
|
+
data: row,
|
|
693
|
+
context: writeCtx,
|
|
694
|
+
...environmentId ? { environmentId } : {}
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
for (const res of writeResults) {
|
|
699
|
+
const { index, rowNo } = batch[res.index];
|
|
700
|
+
if (res.ok) {
|
|
701
|
+
const id = extractRecordId(res.record);
|
|
702
|
+
okCount++;
|
|
703
|
+
created++;
|
|
704
|
+
if (collectUndo && id != null) undoLog.created.push(id);
|
|
705
|
+
results[index] = { row: rowNo, ok: true, action: "created", id };
|
|
706
|
+
} else {
|
|
707
|
+
errCount++;
|
|
708
|
+
results[index] = toFailedResult(rowNo, res.error);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
};
|
|
659
712
|
return (async () => {
|
|
660
713
|
for (let i = 0; i < rows.length; i++) {
|
|
661
714
|
const rowNo = i + 1;
|
|
@@ -669,7 +722,7 @@ function runImport(opts) {
|
|
|
669
722
|
if (errors.length > 0) {
|
|
670
723
|
const first2 = errors[0];
|
|
671
724
|
errCount++;
|
|
672
|
-
results
|
|
725
|
+
results[i] = { row: rowNo, ok: false, action: "failed", field: first2.field, code: first2.code, error: first2.message };
|
|
673
726
|
} else {
|
|
674
727
|
let existing = "none";
|
|
675
728
|
let handled = false;
|
|
@@ -677,11 +730,11 @@ function runImport(opts) {
|
|
|
677
730
|
existing = await findExisting(data);
|
|
678
731
|
if (existing === "ambiguous") {
|
|
679
732
|
errCount++;
|
|
680
|
-
results
|
|
733
|
+
results[i] = { row: rowNo, ok: false, action: "failed", code: "AMBIGUOUS_MATCH", error: `matchFields matched more than one ${objectName} record` };
|
|
681
734
|
handled = true;
|
|
682
735
|
} else if (existing === "blank" && (skipBlankMatchKey || writeMode === "update")) {
|
|
683
736
|
skipped++;
|
|
684
|
-
results
|
|
737
|
+
results[i] = { row: rowNo, ok: true, action: "skipped", code: "BLANK_MATCH_KEY" };
|
|
685
738
|
handled = true;
|
|
686
739
|
}
|
|
687
740
|
}
|
|
@@ -690,45 +743,46 @@ function runImport(opts) {
|
|
|
690
743
|
const willCreate = !willUpdate && (writeMode === "insert" || writeMode === "upsert");
|
|
691
744
|
if (!willUpdate && !willCreate) {
|
|
692
745
|
skipped++;
|
|
693
|
-
results
|
|
746
|
+
results[i] = { row: rowNo, ok: true, action: "skipped", code: "NO_MATCH" };
|
|
694
747
|
} else if (dryRun) {
|
|
695
748
|
okCount++;
|
|
696
749
|
if (willUpdate) {
|
|
697
750
|
updated++;
|
|
698
|
-
results
|
|
751
|
+
results[i] = { row: rowNo, ok: true, action: "updated", id: String(existing.id ?? "") || void 0 };
|
|
699
752
|
} else {
|
|
700
753
|
created++;
|
|
701
|
-
results
|
|
754
|
+
results[i] = { row: rowNo, ok: true, action: "created" };
|
|
702
755
|
}
|
|
703
756
|
} else if (willUpdate) {
|
|
704
757
|
const target = existing;
|
|
705
|
-
const res2 = await p.updateData({ object: objectName, id: target.id, data, context: writeCtx, ...environmentId ? { environmentId } : {} });
|
|
706
|
-
const id = res2
|
|
758
|
+
const res2 = await (0, import_core.withTransientRetry)(() => p.updateData({ object: objectName, id: target.id, data, context: writeCtx, ...environmentId ? { environmentId } : {} }));
|
|
759
|
+
const id = extractRecordId(res2) ?? String(target.id);
|
|
707
760
|
okCount++;
|
|
708
761
|
updated++;
|
|
709
762
|
if (collectUndo && target.id != null) {
|
|
710
763
|
undoLog.updated.push({ id: String(target.id), before: captureBefore(target, data) });
|
|
711
764
|
}
|
|
712
|
-
results
|
|
765
|
+
results[i] = { row: rowNo, ok: true, action: "updated", id };
|
|
766
|
+
} else if (canBulkCreate) {
|
|
767
|
+
pendingCreates.push({ index: i, rowNo, data });
|
|
713
768
|
} else {
|
|
714
769
|
const res2 = await p.createData({ object: objectName, data, context: writeCtx, ...environmentId ? { environmentId } : {} });
|
|
715
|
-
const id = res2
|
|
770
|
+
const id = extractRecordId(res2);
|
|
716
771
|
okCount++;
|
|
717
772
|
created++;
|
|
718
|
-
if (collectUndo && id != null) undoLog.created.push(
|
|
719
|
-
results
|
|
773
|
+
if (collectUndo && id != null) undoLog.created.push(id);
|
|
774
|
+
results[i] = { row: rowNo, ok: true, action: "created", id };
|
|
720
775
|
}
|
|
721
776
|
}
|
|
722
777
|
}
|
|
723
778
|
} catch (err) {
|
|
724
779
|
errCount++;
|
|
725
|
-
|
|
726
|
-
const message = typeof err?.message === "string" ? err.message.slice(0, 300) : "Row failed";
|
|
727
|
-
results.push({ row: rowNo, ok: false, action: "failed", error: message, code });
|
|
780
|
+
results[i] = toFailedResult(rowNo, err);
|
|
728
781
|
}
|
|
729
782
|
const processed = i + 1;
|
|
730
|
-
if (
|
|
731
|
-
await
|
|
783
|
+
if (processed % progressEvery === 0 || processed === rows.length) {
|
|
784
|
+
await flushPendingCreates();
|
|
785
|
+
if (onProgress) await onProgress(snapshot(processed));
|
|
732
786
|
}
|
|
733
787
|
if (shouldCancel && processed < rows.length && processed % progressEvery === 0) {
|
|
734
788
|
if (await shouldCancel()) {
|
|
@@ -737,10 +791,12 @@ function runImport(opts) {
|
|
|
737
791
|
}
|
|
738
792
|
}
|
|
739
793
|
}
|
|
794
|
+
await flushPendingCreates();
|
|
795
|
+
const compacted = results.filter((r) => r !== void 0);
|
|
740
796
|
return {
|
|
741
|
-
...snapshot(
|
|
797
|
+
...snapshot(compacted.length),
|
|
742
798
|
ok: okCount,
|
|
743
|
-
results,
|
|
799
|
+
results: compacted,
|
|
744
800
|
cancelled,
|
|
745
801
|
...collectUndo ? { undoLog } : {}
|
|
746
802
|
};
|
|
@@ -1561,7 +1617,7 @@ var RestServer = class {
|
|
|
1561
1617
|
*/
|
|
1562
1618
|
enforceAuth(req, res, context) {
|
|
1563
1619
|
const gate = context?.authGate;
|
|
1564
|
-
if (gate && req?.method !== "OPTIONS" && !(0,
|
|
1620
|
+
if (gate && req?.method !== "OPTIONS" && !(0, import_core2.isAuthGateAllowlisted)(req?.path)) {
|
|
1565
1621
|
res.status(403).json({ error: { code: gate.code, message: gate.message } });
|
|
1566
1622
|
return true;
|
|
1567
1623
|
}
|
|
@@ -1717,10 +1773,10 @@ var RestServer = class {
|
|
|
1717
1773
|
return void 0;
|
|
1718
1774
|
}
|
|
1719
1775
|
};
|
|
1720
|
-
const authz = await (0,
|
|
1776
|
+
const authz = await (0, import_core2.resolveAuthzContext)({ ql, headers, getSession });
|
|
1721
1777
|
if (!authz.userId) return void 0;
|
|
1722
1778
|
const settings = this.settingsServiceProvider ? await this.settingsServiceProvider(environmentId).catch(() => void 0) : void 0;
|
|
1723
|
-
const localization = await (0,
|
|
1779
|
+
const localization = await (0, import_core2.resolveLocalizationContext)({
|
|
1724
1780
|
ql,
|
|
1725
1781
|
settings,
|
|
1726
1782
|
tenantId: authz.tenantId,
|
|
@@ -1738,7 +1794,7 @@ var RestServer = class {
|
|
|
1738
1794
|
userId: authz.userId,
|
|
1739
1795
|
tenantId: authz.tenantId,
|
|
1740
1796
|
email: authz.email,
|
|
1741
|
-
|
|
1797
|
+
positions: authz.positions,
|
|
1742
1798
|
permissions: authz.permissions,
|
|
1743
1799
|
systemPermissions: authz.systemPermissions,
|
|
1744
1800
|
...authz.tabPermissions ? { tabPermissions: authz.tabPermissions } : {},
|
|
@@ -2196,8 +2252,7 @@ var RestServer = class {
|
|
|
2196
2252
|
if (this.config.api.enableUi) {
|
|
2197
2253
|
discovery.routes.ui = `${realBase}/ui`;
|
|
2198
2254
|
}
|
|
2199
|
-
|
|
2200
|
-
if (mcpEnabled) {
|
|
2255
|
+
if ((0, import_types.isMcpServerEnabled)()) {
|
|
2201
2256
|
const unscopedBase = isScoped ? basePath.replace(/\/(environments|projects)\/:environmentId$/, "") : basePath;
|
|
2202
2257
|
discovery.routes.mcp = `${unscopedBase}/mcp`;
|
|
2203
2258
|
} else {
|