primitive-admin 1.0.53 → 1.0.55
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/README.md +38 -10
- package/dist/bin/primitive.js +8 -0
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/apps.js +54 -2
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth.js +16 -6
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +84 -7
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/catalog.js +3 -3
- package/dist/src/commands/catalog.js.map +1 -1
- package/dist/src/commands/collections.js +12 -9
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/databases.js +220 -82
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.js +77 -0
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/group-type-configs.js +1 -9
- package/dist/src/commands/group-type-configs.js.map +1 -1
- package/dist/src/commands/groups.js +12 -6
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.d.ts +161 -0
- package/dist/src/commands/guides.js +502 -84
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/integrations.js +2 -2
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/metadata.d.ts +2 -0
- package/dist/src/commands/metadata.js +344 -0
- package/dist/src/commands/metadata.js.map +1 -0
- package/dist/src/commands/prompts.js +2 -2
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +1 -0
- package/dist/src/commands/rule-sets.js +25 -3
- package/dist/src/commands/rule-sets.js.map +1 -1
- package/dist/src/commands/scripts.d.ts +2 -0
- package/dist/src/commands/scripts.js +667 -0
- package/dist/src/commands/scripts.js.map +1 -0
- package/dist/src/commands/settings.d.ts +15 -0
- package/dist/src/commands/settings.js +102 -0
- package/dist/src/commands/settings.js.map +1 -0
- package/dist/src/commands/sync-app-settings.d.ts +105 -0
- package/dist/src/commands/sync-app-settings.js +339 -0
- package/dist/src/commands/sync-app-settings.js.map +1 -0
- package/dist/src/commands/sync.d.ts +204 -9
- package/dist/src/commands/sync.js +1706 -371
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/vars.d.ts +8 -0
- package/dist/src/commands/vars.js +110 -0
- package/dist/src/commands/vars.js.map +1 -0
- package/dist/src/commands/webhooks.js +17 -3
- package/dist/src/commands/webhooks.js.map +1 -1
- package/dist/src/commands/workflows.d.ts +30 -0
- package/dist/src/commands/workflows.js +667 -59
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +122 -18
- package/dist/src/lib/api-client.js +131 -2
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +108 -0
- package/dist/src/lib/app-settings-descriptor.js +250 -0
- package/dist/src/lib/app-settings-descriptor.js.map +1 -0
- package/dist/src/lib/batch.d.ts +26 -0
- package/dist/src/lib/batch.js +32 -0
- package/dist/src/lib/batch.js.map +1 -0
- package/dist/src/lib/block-layout.d.ts +160 -0
- package/dist/src/lib/block-layout.js +451 -0
- package/dist/src/lib/block-layout.js.map +1 -0
- package/dist/src/lib/codegen-shared/generatedFiles.d.ts +76 -0
- package/dist/src/lib/codegen-shared/generatedFiles.js +144 -0
- package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +68 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +168 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -0
- package/dist/src/lib/config-toml.d.ts +10 -0
- package/dist/src/lib/config-toml.js +42 -0
- package/dist/src/lib/config-toml.js.map +1 -0
- package/dist/src/lib/config.d.ts +16 -0
- package/dist/src/lib/config.js +41 -0
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/constants.d.ts +9 -0
- package/dist/src/lib/constants.js +9 -0
- package/dist/src/lib/constants.js.map +1 -1
- package/dist/src/lib/csv.d.ts +1 -2
- package/dist/src/lib/csv.js +3 -8
- package/dist/src/lib/csv.js.map +1 -1
- package/dist/src/lib/db-codegen/dbGenerator.d.ts +21 -65
- package/dist/src/lib/db-codegen/dbGenerator.js +117 -321
- package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/db-codegen/dbNaming.d.ts +37 -0
- package/dist/src/lib/db-codegen/dbNaming.js +70 -0
- package/dist/src/lib/db-codegen/dbNaming.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTemplates.d.ts +165 -5
- package/dist/src/lib/db-codegen/dbTemplates.js +292 -16
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTsTypes.d.ts +7 -3
- package/dist/src/lib/db-codegen/dbTsTypes.js +23 -1
- package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTypeIR.d.ts +146 -0
- package/dist/src/lib/db-codegen/dbTypeIR.js +517 -0
- package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -0
- package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +101 -0
- package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +200 -0
- package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -0
- package/dist/src/lib/generated-allowlist.js +43 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/init-config.js +2 -2
- package/dist/src/lib/init-config.js.map +1 -1
- package/dist/src/lib/output.d.ts +25 -5
- package/dist/src/lib/output.js +32 -4
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/query-operators.d.ts +43 -0
- package/dist/src/lib/query-operators.js +80 -0
- package/dist/src/lib/query-operators.js.map +1 -0
- package/dist/src/lib/swift-codegen/dbGenerator.d.ts +68 -0
- package/dist/src/lib/swift-codegen/dbGenerator.js +380 -0
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +42 -0
- package/dist/src/lib/swift-codegen/dbSwiftTypes.js +100 -0
- package/dist/src/lib/swift-codegen/dbSwiftTypes.js.map +1 -0
- package/dist/src/lib/swift-codegen/generator.d.ts +84 -0
- package/dist/src/lib/swift-codegen/generator.js +178 -0
- package/dist/src/lib/swift-codegen/generator.js.map +1 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +72 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.js +644 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -0
- package/dist/src/lib/swift-codegen/swiftNaming.d.ts +85 -0
- package/dist/src/lib/swift-codegen/swiftNaming.js +198 -0
- package/dist/src/lib/swift-codegen/swiftNaming.js.map +1 -0
- package/dist/src/lib/sync-resource-types.d.ts +225 -0
- package/dist/src/lib/sync-resource-types.js +394 -0
- package/dist/src/lib/sync-resource-types.js.map +1 -0
- package/dist/src/lib/toml-database-config.d.ts +10 -19
- package/dist/src/lib/toml-database-config.js +28 -59
- package/dist/src/lib/toml-database-config.js.map +1 -1
- package/dist/src/lib/toml-metadata-config.d.ts +147 -0
- package/dist/src/lib/toml-metadata-config.js +463 -0
- package/dist/src/lib/toml-metadata-config.js.map +1 -0
- package/dist/src/lib/toml-native-form.d.ts +46 -0
- package/dist/src/lib/toml-native-form.js +78 -0
- package/dist/src/lib/toml-native-form.js.map +1 -0
- package/dist/src/lib/toml-params-validator.d.ts +34 -0
- package/dist/src/lib/toml-params-validator.js +118 -3
- package/dist/src/lib/toml-params-validator.js.map +1 -1
- package/dist/src/lib/workflow-apply.d.ts +86 -0
- package/dist/src/lib/workflow-apply.js +127 -0
- package/dist/src/lib/workflow-apply.js.map +1 -0
- package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +129 -0
- package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +269 -0
- package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
- package/dist/src/lib/workflow-codegen/generator.d.ts +87 -0
- package/dist/src/lib/workflow-codegen/generator.js +290 -0
- package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
- package/dist/src/lib/workflow-codegen/invokerIR.d.ts +85 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js +71 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -0
- package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
- package/dist/src/lib/workflow-codegen/naming.js +81 -0
- package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
- package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +64 -0
- package/dist/src/lib/workflow-codegen/schemaToTs.js +282 -0
- package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
- package/dist/src/lib/workflow-fragments.js +3 -3
- package/dist/src/lib/workflow-fragments.js.map +1 -1
- package/dist/src/lib/workflow-payload.d.ts +68 -0
- package/dist/src/lib/workflow-payload.js +150 -0
- package/dist/src/lib/workflow-payload.js.map +1 -0
- package/dist/src/lib/workflow-toml-validator.d.ts +1 -1
- package/dist/src/lib/workflow-toml-validator.js +37 -1
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/dist/src/types/index.d.ts +11 -0
- package/dist/src/validators.d.ts +4 -3
- package/dist/src/validators.js +6 -5
- package/dist/src/validators.js.map +1 -1
- package/package.json +7 -3
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, readdirSync } from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import
|
|
3
|
+
import { parseConfigToml } from "../lib/config-toml.js";
|
|
4
4
|
import { ApiClient } from "../lib/api-client.js";
|
|
5
5
|
import { getCurrentAppId } from "../lib/config.js";
|
|
6
|
-
import {
|
|
6
|
+
import { resolveCodegenSourceDir } from "../lib/codegen-shared/resolveCodegenSourceDir.js";
|
|
7
|
+
import { parseCsv, applyColumnMap, buildRows } from "../lib/csv.js";
|
|
8
|
+
import { chunk, parseBatchSize } from "../lib/batch.js";
|
|
7
9
|
import { generateDbTypes, } from "../lib/db-codegen/dbGenerator.js";
|
|
10
|
+
import { generateDbSwiftTypes } from "../lib/swift-codegen/dbGenerator.js";
|
|
8
11
|
import { success, error, info, warn, keyValue, result as printResult, formatTable, formatId, formatDate, json, } from "../lib/output.js";
|
|
9
12
|
import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
10
13
|
function resolveAppId(appId, options) {
|
|
@@ -128,6 +131,7 @@ the 'database-types' group:
|
|
|
128
131
|
.requiredOption("--type <database-type>", "Database type (required)")
|
|
129
132
|
.option("--cel-context <json>", "Initial CEL context as JSON (referenced from access rules as database.celContext.*)")
|
|
130
133
|
.option("--metadata <json>", "Initial CEL context as JSON (legacy alias for --cel-context)")
|
|
134
|
+
.option("--initial-metadata <json>", "Create-time resource metadata as JSON, keyed by category name (issue #1420; distinct from --cel-context)")
|
|
131
135
|
.option("--json", "Output as JSON")
|
|
132
136
|
.action(async (title, options) => {
|
|
133
137
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
@@ -147,6 +151,17 @@ the 'database-types' group:
|
|
|
147
151
|
process.exit(1);
|
|
148
152
|
}
|
|
149
153
|
}
|
|
154
|
+
// `--initial-metadata` is a separate concept: create-time per-category
|
|
155
|
+
// ResourceMetadata rows (issue #1420), not the database CEL context.
|
|
156
|
+
if (options.initialMetadata) {
|
|
157
|
+
try {
|
|
158
|
+
params.initialMetadata = JSON.parse(options.initialMetadata);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
error("Invalid --initial-metadata JSON.");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
150
165
|
const result = await client.createDatabase(resolvedAppId, params);
|
|
151
166
|
if (options.json) {
|
|
152
167
|
json(result);
|
|
@@ -474,7 +489,7 @@ the 'database-types' group:
|
|
|
474
489
|
try {
|
|
475
490
|
const content = readFileSync(options.filterFile, "utf-8");
|
|
476
491
|
if (options.filterFile.endsWith(".toml")) {
|
|
477
|
-
const parsed =
|
|
492
|
+
const parsed = parseConfigToml(content);
|
|
478
493
|
filter = parsed.filter || parsed;
|
|
479
494
|
}
|
|
480
495
|
else {
|
|
@@ -1059,6 +1074,8 @@ the 'database-types' group:
|
|
|
1059
1074
|
.option("--app <app-id>", "App ID")
|
|
1060
1075
|
.option("--overwrite", "Replace existing database if ID collides")
|
|
1061
1076
|
.option("--dry-run", "Show what would be imported without making changes")
|
|
1077
|
+
.option("--batch-size <n>", "Records per batch write request (default: 5000)", "5000")
|
|
1078
|
+
.option("--stop-on-error", "Abort the whole run on the first failing chunk (default: continue and report)")
|
|
1062
1079
|
.option("--json", "Output as JSON")
|
|
1063
1080
|
.action(async (first, second, options) => {
|
|
1064
1081
|
let resolvedAppId;
|
|
@@ -1075,6 +1092,21 @@ the 'database-types' group:
|
|
|
1075
1092
|
error("Import path is required.");
|
|
1076
1093
|
process.exit(1);
|
|
1077
1094
|
}
|
|
1095
|
+
// Validate --batch-size up front so a bad value fails before any write.
|
|
1096
|
+
// Unlike `import-csv` (registered-batch path, hard 100k server cap),
|
|
1097
|
+
// `admin-data/batch` has no fixed server item cap — bound it with a
|
|
1098
|
+
// conservative client-side ceiling to keep each request within
|
|
1099
|
+
// Cloudflare body-size and Durable Object memory limits.
|
|
1100
|
+
const parsedBatchSize = parseBatchSize(options.batchSize, {
|
|
1101
|
+
max: IMPORT_BATCH_SIZE_CEILING,
|
|
1102
|
+
overMaxMessage: `--batch-size cannot exceed ${IMPORT_BATCH_SIZE_CEILING} (a conservative client-side limit for batch write requests).`,
|
|
1103
|
+
});
|
|
1104
|
+
if ("error" in parsedBatchSize) {
|
|
1105
|
+
error(parsedBatchSize.error);
|
|
1106
|
+
process.exit(1);
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
const batchSize = parsedBatchSize.value;
|
|
1078
1110
|
const client = new ApiClient();
|
|
1079
1111
|
try {
|
|
1080
1112
|
// Determine if this is a manifest-based export or a single database
|
|
@@ -1109,12 +1141,32 @@ the 'database-types' group:
|
|
|
1109
1141
|
info("No databases to import.");
|
|
1110
1142
|
return;
|
|
1111
1143
|
}
|
|
1112
|
-
const summary = {
|
|
1144
|
+
const summary = {
|
|
1145
|
+
created: 0,
|
|
1146
|
+
updated: 0,
|
|
1147
|
+
skipped: 0,
|
|
1148
|
+
recordsImported: 0,
|
|
1149
|
+
recordsFailed: 0,
|
|
1150
|
+
batches: 0,
|
|
1151
|
+
aborted: false,
|
|
1152
|
+
indexesRegistered: 0,
|
|
1153
|
+
batchErrors: [],
|
|
1154
|
+
};
|
|
1113
1155
|
for (const dbDir of dbDirs) {
|
|
1114
|
-
await importSingleDatabase(client, resolvedAppId, dbDir,
|
|
1156
|
+
await importSingleDatabase(client, resolvedAppId, dbDir, {
|
|
1157
|
+
overwrite: options.overwrite || false,
|
|
1158
|
+
dryRun: options.dryRun || false,
|
|
1159
|
+
batchSize,
|
|
1160
|
+
stopOnError: options.stopOnError || false,
|
|
1161
|
+
json: options.json || false,
|
|
1162
|
+
summary,
|
|
1163
|
+
});
|
|
1164
|
+
// Fork 3: --stop-on-error stops the whole run across a multi-DB dir.
|
|
1165
|
+
if (summary.aborted)
|
|
1166
|
+
break;
|
|
1115
1167
|
}
|
|
1116
1168
|
if (options.json) {
|
|
1117
|
-
json(summary);
|
|
1169
|
+
json({ ...summary, dryRun: options.dryRun || false });
|
|
1118
1170
|
}
|
|
1119
1171
|
else {
|
|
1120
1172
|
success("Database import complete:");
|
|
@@ -1124,8 +1176,24 @@ the 'database-types' group:
|
|
|
1124
1176
|
if (summary.skipped > 0)
|
|
1125
1177
|
keyValue(" Databases skipped", String(summary.skipped));
|
|
1126
1178
|
keyValue(" Records imported", String(summary.recordsImported));
|
|
1179
|
+
if (summary.recordsFailed > 0)
|
|
1180
|
+
keyValue(" Records failed", String(summary.recordsFailed));
|
|
1181
|
+
keyValue(" Batches", String(summary.batches));
|
|
1127
1182
|
if (summary.indexesRegistered > 0)
|
|
1128
1183
|
keyValue(" Indexes registered", String(summary.indexesRegistered));
|
|
1184
|
+
if (summary.aborted)
|
|
1185
|
+
warn("Aborted after a failing chunk (--stop-on-error).");
|
|
1186
|
+
if (summary.batchErrors.length > 0) {
|
|
1187
|
+
error(`${summary.batchErrors.length} chunk(s) errored:`);
|
|
1188
|
+
for (const e of summary.batchErrors) {
|
|
1189
|
+
error(` ${e.database} batch ${e.batch + 1}: ${e.error}`);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
// Non-zero exit if any chunk errored (default continue-and-report or
|
|
1194
|
+
// --stop-on-error both surface failures this way).
|
|
1195
|
+
if (summary.batchErrors.length > 0) {
|
|
1196
|
+
process.exit(1);
|
|
1129
1197
|
}
|
|
1130
1198
|
}
|
|
1131
1199
|
catch (err) {
|
|
@@ -1178,18 +1246,18 @@ the 'database-types' group:
|
|
|
1178
1246
|
return;
|
|
1179
1247
|
}
|
|
1180
1248
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
error("--batch-size cannot exceed 100000 (the server's per-request cap).");
|
|
1249
|
+
// The registered-batch (`executeBatch`) path rejects requests over
|
|
1250
|
+
// 100000 items server-side; keep chunks under it.
|
|
1251
|
+
const parsedBatchSize = parseBatchSize(options.batchSize, {
|
|
1252
|
+
max: 100000,
|
|
1253
|
+
overMaxMessage: "--batch-size cannot exceed 100000 (the server's per-request cap).",
|
|
1254
|
+
});
|
|
1255
|
+
if ("error" in parsedBatchSize) {
|
|
1256
|
+
error(parsedBatchSize.error);
|
|
1190
1257
|
process.exit(1);
|
|
1191
1258
|
return;
|
|
1192
1259
|
}
|
|
1260
|
+
const batchSize = parsedBatchSize.value;
|
|
1193
1261
|
if (!existsSync(file)) {
|
|
1194
1262
|
error(`CSV file not found: ${file}`);
|
|
1195
1263
|
process.exit(1);
|
|
@@ -1356,7 +1424,7 @@ the 'database-types' group:
|
|
|
1356
1424
|
}
|
|
1357
1425
|
// 3. For each candidate, splice the scaffolded TOML at a stable
|
|
1358
1426
|
// anchor (before the first `[[operations]]` block, or at EOF). Per
|
|
1359
|
-
// the plan-of-record, textual insertion is deliberate —
|
|
1427
|
+
// the plan-of-record, textual insertion is deliberate — smol-toml
|
|
1360
1428
|
// parse/stringify drops comments, so we splice as a raw string.
|
|
1361
1429
|
for (const targetPath of candidateFiles) {
|
|
1362
1430
|
const original = readFileSync(targetPath, "utf-8");
|
|
@@ -1401,67 +1469,47 @@ the 'database-types' group:
|
|
|
1401
1469
|
// ============================================
|
|
1402
1470
|
databases
|
|
1403
1471
|
.command("codegen")
|
|
1404
|
-
.description("Generate
|
|
1472
|
+
.description("Generate record + operation types from the local database-types/*.toml schema. TypeScript (default): a <type>.generated.ts per database type (record interfaces, per-op input-params interfaces, per-op result aliases, and a typed <type>Ops factory). Swift (--lang swift): a <type>.generated.swift per database type (record structs, per-op params structs, per-op result typealiases, and a typed <Type>Ops factory).")
|
|
1405
1473
|
.argument("[database-type]", "Generate for a single database type (defaults to every database-types/*.toml found)")
|
|
1406
1474
|
.option("--app <app-id>", "App ID")
|
|
1407
1475
|
.option("--sync-dir <path>", "Override path to the sync directory (defaults to ./.primitive/sync/<env>/<appId>/)")
|
|
1408
|
-
.option("-o, --output <dir>", "Output directory for
|
|
1476
|
+
.option("-o, --output <dir>", "Output directory for the generated files (defaults to <sync-dir>/database-types/generated/)")
|
|
1477
|
+
.option("--lang <lang>", "Target language for the generated types: 'ts' (default) or 'swift'.", "ts")
|
|
1409
1478
|
.option("--check", "Exit non-zero if generated output is out of date (CI guard); does not write.")
|
|
1410
1479
|
.option("--json", "Output the result summary as JSON")
|
|
1411
1480
|
.action(async (databaseType, options) => {
|
|
1412
1481
|
try {
|
|
1413
|
-
//
|
|
1414
|
-
//
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
if (options.syncDir) {
|
|
1419
|
-
dbTypesDirs.push(path.join(options.syncDir, "database-types"));
|
|
1420
|
-
}
|
|
1421
|
-
else {
|
|
1422
|
-
const root = path.join(process.cwd(), ".primitive", "sync");
|
|
1423
|
-
if (existsSync(root)) {
|
|
1424
|
-
for (const env of readdirSync(root)) {
|
|
1425
|
-
const envDir = path.join(root, env);
|
|
1426
|
-
try {
|
|
1427
|
-
for (const app of readdirSync(envDir)) {
|
|
1428
|
-
const candidate = path.join(envDir, app, "database-types");
|
|
1429
|
-
if (existsSync(candidate)) {
|
|
1430
|
-
dbTypesDirs.push(candidate);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
catch {
|
|
1435
|
-
// skip non-dirs
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
if (dbTypesDirs.length === 0) {
|
|
1441
|
-
error("No database-types/ directory found. Pass --sync-dir or run `primitive sync pull` first.");
|
|
1482
|
+
// 0. Validate --lang up front so an unknown value fails clearly rather
|
|
1483
|
+
// than silently defaulting to the TS emitter.
|
|
1484
|
+
const lang = String(options.lang ?? "ts").toLowerCase();
|
|
1485
|
+
if (lang !== "ts" && lang !== "swift") {
|
|
1486
|
+
error(`Unknown --lang "${options.lang}". Use "ts" or "swift".`);
|
|
1442
1487
|
process.exit(1);
|
|
1443
1488
|
}
|
|
1489
|
+
// 1. Resolve the single source database-types/ directory via the
|
|
1490
|
+
// shared active-environment resolver (issue #1510). Honors
|
|
1491
|
+
// --sync-dir / --app overrides and resolves the active env
|
|
1492
|
+
// (--env → PRIMITIVE_ENV → defaultEnvironment → single-env) instead
|
|
1493
|
+
// of silently merging types across every synced env.
|
|
1494
|
+
const dbTypesSourceDir = resolveCodegenSourceDir({
|
|
1495
|
+
subdir: "database-types",
|
|
1496
|
+
options: { app: options.app, syncDir: options.syncDir },
|
|
1497
|
+
});
|
|
1444
1498
|
// 2. Collect the source .toml files (one per database type), filtered
|
|
1445
1499
|
// to a single type when an argument is given.
|
|
1446
1500
|
const inputs = [];
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
inputs.push({
|
|
1460
|
-
databaseType: typeName,
|
|
1461
|
-
tomlPath,
|
|
1462
|
-
tomlContent: readFileSync(tomlPath, "utf-8"),
|
|
1463
|
-
});
|
|
1464
|
-
}
|
|
1501
|
+
for (const fileName of readdirSync(dbTypesSourceDir)) {
|
|
1502
|
+
if (!fileName.endsWith(".toml"))
|
|
1503
|
+
continue;
|
|
1504
|
+
const typeName = fileName.slice(0, -".toml".length);
|
|
1505
|
+
if (databaseType && typeName !== databaseType)
|
|
1506
|
+
continue;
|
|
1507
|
+
const tomlPath = path.join(dbTypesSourceDir, fileName);
|
|
1508
|
+
inputs.push({
|
|
1509
|
+
databaseType: typeName,
|
|
1510
|
+
tomlPath,
|
|
1511
|
+
tomlContent: readFileSync(tomlPath, "utf-8"),
|
|
1512
|
+
});
|
|
1465
1513
|
}
|
|
1466
1514
|
if (inputs.length === 0) {
|
|
1467
1515
|
error(databaseType
|
|
@@ -1474,16 +1522,26 @@ the 'database-types' group:
|
|
|
1474
1522
|
const outputDir = options.output
|
|
1475
1523
|
? path.resolve(options.output)
|
|
1476
1524
|
: path.join(path.dirname(inputs[0].tomlPath), "generated");
|
|
1477
|
-
// 4. Run codegen (or --check)
|
|
1525
|
+
// 4. Run codegen (or --check) with the selected emitter. Both the TS
|
|
1526
|
+
// and Swift generators return the same WriteOrCheckResult shape, so
|
|
1527
|
+
// the reporting below is emitter-agnostic; only the emitter (and the
|
|
1528
|
+
// regenerate hint) differs. When a single database-type filter
|
|
1478
1529
|
// argument is given, the input set is partial — flag it so stale
|
|
1479
1530
|
// cleanup is scoped to that type and sibling types' generated files
|
|
1480
1531
|
// are left intact.
|
|
1481
|
-
const codegenResult =
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1532
|
+
const codegenResult = lang === "swift"
|
|
1533
|
+
? await generateDbSwiftTypes({
|
|
1534
|
+
inputs,
|
|
1535
|
+
outputDir,
|
|
1536
|
+
check: !!options.check,
|
|
1537
|
+
singleType: !!databaseType,
|
|
1538
|
+
})
|
|
1539
|
+
: await generateDbTypes({
|
|
1540
|
+
inputs,
|
|
1541
|
+
outputDir,
|
|
1542
|
+
check: !!options.check,
|
|
1543
|
+
singleType: !!databaseType,
|
|
1544
|
+
});
|
|
1487
1545
|
if (options.check) {
|
|
1488
1546
|
if (codegenResult.mismatches.length > 0) {
|
|
1489
1547
|
if (options.json) {
|
|
@@ -1494,7 +1552,9 @@ the 'database-types' group:
|
|
|
1494
1552
|
for (const m of codegenResult.mismatches) {
|
|
1495
1553
|
error(` ${m.reason}: ${path.relative(process.cwd(), m.filePath)}`);
|
|
1496
1554
|
}
|
|
1497
|
-
info(
|
|
1555
|
+
info(lang === "swift"
|
|
1556
|
+
? "Run `primitive databases codegen --lang swift` to regenerate."
|
|
1557
|
+
: "Run `primitive databases codegen` to regenerate.");
|
|
1498
1558
|
}
|
|
1499
1559
|
process.exit(1);
|
|
1500
1560
|
}
|
|
@@ -1570,8 +1630,12 @@ async function exportSingleDatabase(client, appId, databaseId, outputDir, jsonOu
|
|
|
1570
1630
|
metadata: dbMeta.metadata || null,
|
|
1571
1631
|
};
|
|
1572
1632
|
writeFileSync(path.join(dbDir, "metadata.json"), JSON.stringify(metadata, null, 2));
|
|
1573
|
-
// 3. Export records: list models, then query each
|
|
1574
|
-
|
|
1633
|
+
// 3. Export records: list models, then query each.
|
|
1634
|
+
// listDatabaseModels() resolves to `{ models: string[] }`, not a bare array,
|
|
1635
|
+
// so unwrap it before iterating (mirrors the `databases records models`
|
|
1636
|
+
// consumer above). Without this, `for (const modelName of models)` throws
|
|
1637
|
+
// "models is not iterable" and records.jsonl is left empty (#1396).
|
|
1638
|
+
const models = (await client.listDatabaseModels(appId, databaseId)).models || [];
|
|
1575
1639
|
let totalRecords = 0;
|
|
1576
1640
|
const recordsFile = path.join(dbDir, "records.jsonl");
|
|
1577
1641
|
// Clear file
|
|
@@ -1619,7 +1683,15 @@ async function exportSingleDatabase(client, appId, databaseId, outputDir, jsonOu
|
|
|
1619
1683
|
keyValue(" Indexes", String(indexes.length));
|
|
1620
1684
|
}
|
|
1621
1685
|
}
|
|
1622
|
-
|
|
1686
|
+
/**
|
|
1687
|
+
* Conservative client-side ceiling for `databases import --batch-size`.
|
|
1688
|
+
* `admin-data/batch` has no fixed server-side item cap (unlike the
|
|
1689
|
+
* registered-batch path used by `import-csv`), so this bounds each request to
|
|
1690
|
+
* keep it within Cloudflare body-size and Durable Object memory limits.
|
|
1691
|
+
*/
|
|
1692
|
+
const IMPORT_BATCH_SIZE_CEILING = 25000;
|
|
1693
|
+
async function importSingleDatabase(client, appId, dbDir, options) {
|
|
1694
|
+
const { overwrite, dryRun, batchSize, stopOnError, json: jsonOutput, summary } = options;
|
|
1623
1695
|
const metadataPath = path.join(dbDir, "metadata.json");
|
|
1624
1696
|
if (!existsSync(metadataPath)) {
|
|
1625
1697
|
warn(`Skipping ${dbDir}: no metadata.json found`);
|
|
@@ -1629,8 +1701,14 @@ async function importSingleDatabase(client, appId, dbDir, overwrite, dryRun, sum
|
|
|
1629
1701
|
const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"));
|
|
1630
1702
|
const databaseId = metadata.databaseId;
|
|
1631
1703
|
if (dryRun) {
|
|
1632
|
-
|
|
1704
|
+
// Report record/batch counts without writing anything (no DB creation,
|
|
1705
|
+
// no index registration, no batch write).
|
|
1706
|
+
const recordCount = countRecordLines(dbDir);
|
|
1707
|
+
const batchCount = chunk(new Array(recordCount), batchSize).length;
|
|
1633
1708
|
summary.created++;
|
|
1709
|
+
summary.recordsImported += recordCount;
|
|
1710
|
+
summary.batches += batchCount;
|
|
1711
|
+
info(`[dry-run] Would import database ${databaseId} (${metadata.title || "untitled"}) — ${recordCount} record(s), ${batchCount} batch(es)`);
|
|
1634
1712
|
return;
|
|
1635
1713
|
}
|
|
1636
1714
|
// Check if database type exists — warn if not
|
|
@@ -1707,19 +1785,79 @@ async function importSingleDatabase(client, appId, dbDir, overwrite, dryRun, sum
|
|
|
1707
1785
|
}
|
|
1708
1786
|
}
|
|
1709
1787
|
}
|
|
1710
|
-
// Import records
|
|
1788
|
+
// Import records via chunked batch writes (was one save per record).
|
|
1711
1789
|
const recordsPath = path.join(dbDir, "records.jsonl");
|
|
1712
1790
|
if (existsSync(recordsPath)) {
|
|
1713
1791
|
const lines = readFileSync(recordsPath, "utf-8").split("\n").filter(Boolean);
|
|
1714
|
-
|
|
1792
|
+
// Map each records.jsonl line to a batch save op. Preserve the explicit
|
|
1793
|
+
// `_type`/`_id`; strip the redundant id/type keys out of `_data` (the
|
|
1794
|
+
// per-record loop did the same) so they don't duplicate into stored data.
|
|
1795
|
+
const ops = lines.map((line) => {
|
|
1715
1796
|
const record = JSON.parse(line);
|
|
1716
1797
|
const { _type, _id, _data } = record;
|
|
1717
|
-
// Remove 'id' and 'type' from _data to avoid duplication
|
|
1718
1798
|
const { id: _ignoredId, type: _ignoredType, ...data } = _data;
|
|
1719
|
-
|
|
1720
|
-
|
|
1799
|
+
return { op: "save", modelName: _type, id: _id, data };
|
|
1800
|
+
});
|
|
1801
|
+
const chunks = chunk(ops, batchSize);
|
|
1802
|
+
summary.batches += chunks.length;
|
|
1803
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1804
|
+
const piece = chunks[i];
|
|
1805
|
+
let chunkFailed = false;
|
|
1806
|
+
try {
|
|
1807
|
+
const res = await client.batchDatabaseRecords(appId, targetDatabaseId, piece);
|
|
1808
|
+
const results = res?.results ?? [];
|
|
1809
|
+
let imported = 0;
|
|
1810
|
+
let failed = 0;
|
|
1811
|
+
const firstErrors = [];
|
|
1812
|
+
for (let j = 0; j < piece.length; j++) {
|
|
1813
|
+
const r = results[j];
|
|
1814
|
+
if (r && r.success) {
|
|
1815
|
+
imported++;
|
|
1816
|
+
}
|
|
1817
|
+
else {
|
|
1818
|
+
failed++;
|
|
1819
|
+
if (r?.error && firstErrors.length < 3)
|
|
1820
|
+
firstErrors.push(r.error);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
summary.recordsImported += imported;
|
|
1824
|
+
summary.recordsFailed += failed;
|
|
1825
|
+
if (failed > 0) {
|
|
1826
|
+
chunkFailed = true;
|
|
1827
|
+
summary.batchErrors.push({
|
|
1828
|
+
database: databaseId,
|
|
1829
|
+
batch: i,
|
|
1830
|
+
error: `${failed} record(s) failed${firstErrors.length ? `: ${firstErrors.join("; ")}` : ""}`,
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
if (!jsonOutput) {
|
|
1834
|
+
info(` ${databaseId} batch ${i + 1}/${chunks.length}: imported ${imported}, failed ${failed}`);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
catch (err) {
|
|
1838
|
+
// Chunk-level failure (e.g. a 400 for a malformed op): the whole chunk
|
|
1839
|
+
// is rejected, so every item in it counts as failed.
|
|
1840
|
+
const msg = err?.message || String(err);
|
|
1841
|
+
chunkFailed = true;
|
|
1842
|
+
summary.recordsFailed += piece.length;
|
|
1843
|
+
summary.batchErrors.push({ database: databaseId, batch: i, error: msg });
|
|
1844
|
+
if (!jsonOutput) {
|
|
1845
|
+
warn(` ${databaseId} batch ${i + 1}/${chunks.length} failed: ${msg}`);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
if (chunkFailed && stopOnError) {
|
|
1849
|
+
summary.aborted = true;
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1721
1852
|
}
|
|
1722
1853
|
}
|
|
1723
1854
|
info(`Imported database ${databaseId} (${metadata.title || "untitled"})`);
|
|
1724
1855
|
}
|
|
1856
|
+
/** Count non-empty lines in a database export's records.jsonl (0 if absent). */
|
|
1857
|
+
function countRecordLines(dbDir) {
|
|
1858
|
+
const recordsPath = path.join(dbDir, "records.jsonl");
|
|
1859
|
+
if (!existsSync(recordsPath))
|
|
1860
|
+
return 0;
|
|
1861
|
+
return readFileSync(recordsPath, "utf-8").split("\n").filter(Boolean).length;
|
|
1862
|
+
}
|
|
1725
1863
|
//# sourceMappingURL=databases.js.map
|