pocketbase-zod-schema 0.7.1 → 0.7.2
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/CHANGELOG.md +7 -0
- package/dist/cli/index.cjs +323 -27
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +323 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/migrate.cjs +329 -30
- package/dist/cli/migrate.cjs.map +1 -1
- package/dist/cli/migrate.js +329 -30
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/utils/index.cjs +8 -1
- package/dist/cli/utils/index.cjs.map +1 -1
- package/dist/cli/utils/index.d.cts +1 -1
- package/dist/cli/utils/index.d.ts +1 -1
- package/dist/cli/utils/index.js +8 -1
- package/dist/cli/utils/index.js.map +1 -1
- package/dist/index.cjs +90 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +87 -2
- package/dist/index.js.map +1 -1
- package/dist/migration/analyzer.cjs +87 -14
- package/dist/migration/analyzer.cjs.map +1 -1
- package/dist/migration/analyzer.d.cts +12 -4
- package/dist/migration/analyzer.d.ts +12 -4
- package/dist/migration/analyzer.js +87 -15
- package/dist/migration/analyzer.js.map +1 -1
- package/dist/migration/diff.cjs +64 -9
- package/dist/migration/diff.cjs.map +1 -1
- package/dist/migration/diff.d.cts +12 -2
- package/dist/migration/diff.d.ts +12 -2
- package/dist/migration/diff.js +64 -10
- package/dist/migration/diff.js.map +1 -1
- package/dist/migration/generator.cjs +100 -2
- package/dist/migration/generator.cjs.map +1 -1
- package/dist/migration/generator.d.cts +32 -2
- package/dist/migration/generator.d.ts +32 -2
- package/dist/migration/generator.js +99 -3
- package/dist/migration/generator.js.map +1 -1
- package/dist/migration/index.cjs +349 -26
- package/dist/migration/index.cjs.map +1 -1
- package/dist/migration/index.d.cts +2 -2
- package/dist/migration/index.d.ts +2 -2
- package/dist/migration/index.js +349 -26
- package/dist/migration/index.js.map +1 -1
- package/dist/migration/snapshot.cjs +99 -1
- package/dist/migration/snapshot.cjs.map +1 -1
- package/dist/migration/snapshot.d.cts +3 -1
- package/dist/migration/snapshot.d.ts +3 -1
- package/dist/migration/snapshot.js +99 -1
- package/dist/migration/snapshot.js.map +1 -1
- package/dist/migration/utils/index.d.cts +1 -1
- package/dist/migration/utils/index.d.ts +1 -1
- package/dist/schema.cjs +90 -1
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +128 -2
- package/dist/schema.d.ts +128 -2
- package/dist/schema.js +87 -2
- package/dist/schema.js.map +1 -1
- package/dist/server.cjs +393 -28
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +390 -29
- package/dist/server.js.map +1 -1
- package/dist/{types-CWHV6ATd.d.cts → types-CnzfX6JH.d.cts} +20 -2
- package/dist/{types-C2nGWHLV.d.ts → types-Do3jyFBm.d.ts} +20 -2
- package/package.json +1 -1
package/dist/cli/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { MigrationConfig, StatusOutput, VerbosityLevel, createProgressBar, createSpinner, formatChangeSummary, formatDuration, formatStatusJson, getMigrationsDirectory, getSchemaDirectory, getVerbosity, loadConfig, logBox, logDebug, logError, logInfo, logKeyValue, logList, logSection, logStep, logSuccess, logTable, logTimed, logTimestamp, logWarning, setVerbosity, withProgress } from './utils/index.cjs';
|
|
2
2
|
import 'ora';
|
|
3
|
-
import '../types-
|
|
3
|
+
import '../types-CnzfX6JH.cjs';
|
|
4
4
|
import '../fields-B96iGprI.cjs';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { MigrationConfig, StatusOutput, VerbosityLevel, createProgressBar, createSpinner, formatChangeSummary, formatDuration, formatStatusJson, getMigrationsDirectory, getSchemaDirectory, getVerbosity, loadConfig, logBox, logDebug, logError, logInfo, logKeyValue, logList, logSection, logStep, logSuccess, logTable, logTimed, logTimestamp, logWarning, setVerbosity, withProgress } from './utils/index.js';
|
|
2
2
|
import 'ora';
|
|
3
|
-
import '../types-
|
|
3
|
+
import '../types-Do3jyFBm.js';
|
|
4
4
|
import '../fields-B96iGprI.js';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
package/dist/cli/index.js
CHANGED
|
@@ -1193,6 +1193,60 @@ function unwrapZodType(zodType) {
|
|
|
1193
1193
|
}
|
|
1194
1194
|
return unwrapped;
|
|
1195
1195
|
}
|
|
1196
|
+
function dedentSql(value) {
|
|
1197
|
+
const lines = value.replace(/\r\n/g, "\n").split("\n");
|
|
1198
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
1199
|
+
lines.shift();
|
|
1200
|
+
}
|
|
1201
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
1202
|
+
lines.pop();
|
|
1203
|
+
}
|
|
1204
|
+
if (lines.length === 0) {
|
|
1205
|
+
return "";
|
|
1206
|
+
}
|
|
1207
|
+
let minIndent = Infinity;
|
|
1208
|
+
for (const line of lines) {
|
|
1209
|
+
if (line.trim() === "") continue;
|
|
1210
|
+
const indent = line.length - line.trimStart().length;
|
|
1211
|
+
if (indent < minIndent) {
|
|
1212
|
+
minIndent = indent;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
if (!Number.isFinite(minIndent) || minIndent === 0) {
|
|
1216
|
+
return lines.map((line) => line.trimEnd()).join("\n");
|
|
1217
|
+
}
|
|
1218
|
+
return lines.map((line) => line.slice(minIndent).trimEnd()).join("\n");
|
|
1219
|
+
}
|
|
1220
|
+
function stripLeadingComments(query) {
|
|
1221
|
+
let remaining = query.trim();
|
|
1222
|
+
while (remaining.length > 0) {
|
|
1223
|
+
if (remaining.startsWith("--")) {
|
|
1224
|
+
const newline = remaining.indexOf("\n");
|
|
1225
|
+
remaining = newline === -1 ? "" : remaining.slice(newline + 1).trim();
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
if (remaining.startsWith("/*")) {
|
|
1229
|
+
const end = remaining.indexOf("*/");
|
|
1230
|
+
remaining = end === -1 ? "" : remaining.slice(end + 2).trim();
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
return remaining;
|
|
1236
|
+
}
|
|
1237
|
+
function validateViewQuery(collectionName, viewQuery) {
|
|
1238
|
+
if (typeof viewQuery !== "string" || viewQuery.trim() === "") {
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
`View collection "${collectionName}" requires a non-empty viewQuery. Provide the SQL SELECT statement backing the view.`
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
const body = stripLeadingComments(viewQuery);
|
|
1244
|
+
if (!/^(select|with)\b/i.test(body)) {
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
`View collection "${collectionName}" has an invalid viewQuery: it must start with SELECT or WITH. Received: ${body.slice(0, 40)}${body.length > 40 ? "..." : ""}`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1196
1250
|
function getCollectionNameFromFile(filePath) {
|
|
1197
1251
|
const filename = path8.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1198
1252
|
return toCollectionName(filename);
|
|
@@ -1216,13 +1270,26 @@ function extractCollectionTypeFromSchema(zodSchema) {
|
|
|
1216
1270
|
}
|
|
1217
1271
|
try {
|
|
1218
1272
|
const metadata = JSON.parse(zodSchema.description);
|
|
1219
|
-
if (metadata.type === "base" || metadata.type === "auth") {
|
|
1273
|
+
if (metadata.type === "base" || metadata.type === "auth" || metadata.type === "view") {
|
|
1220
1274
|
return metadata.type;
|
|
1221
1275
|
}
|
|
1222
1276
|
} catch {
|
|
1223
1277
|
}
|
|
1224
1278
|
return null;
|
|
1225
1279
|
}
|
|
1280
|
+
function extractViewQueryFromSchema(zodSchema) {
|
|
1281
|
+
if (!zodSchema.description) {
|
|
1282
|
+
return null;
|
|
1283
|
+
}
|
|
1284
|
+
try {
|
|
1285
|
+
const metadata = JSON.parse(zodSchema.description);
|
|
1286
|
+
if (typeof metadata.viewQuery === "string") {
|
|
1287
|
+
return metadata.viewQuery;
|
|
1288
|
+
}
|
|
1289
|
+
} catch {
|
|
1290
|
+
}
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1226
1293
|
function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema", "Collection"]) {
|
|
1227
1294
|
const result = {};
|
|
1228
1295
|
if (module.default instanceof z.ZodObject) {
|
|
@@ -1381,6 +1448,15 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
|
1381
1448
|
const rawFields = extractFieldDefinitions(zodSchema);
|
|
1382
1449
|
const explicitType = extractCollectionTypeFromSchema(zodSchema);
|
|
1383
1450
|
const collectionType = explicitType ?? (isAuthCollection(rawFields) ? "auth" : "base");
|
|
1451
|
+
const isView = collectionType === "view";
|
|
1452
|
+
const viewQuery = extractViewQueryFromSchema(zodSchema);
|
|
1453
|
+
if (isView) {
|
|
1454
|
+
validateViewQuery(collectionName, viewQuery);
|
|
1455
|
+
} else if (viewQuery !== null) {
|
|
1456
|
+
console.warn(
|
|
1457
|
+
`[${collectionName}] viewQuery is only used by view collections and will be ignored. Use defineView() or set type: "view" to define a view collection.`
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1384
1460
|
const fields = rawFields.filter((f) => !["created", "updated"].includes(f.name)).map(({ name, zodType }) => buildFieldDefinition(name, zodType));
|
|
1385
1461
|
if (collectionType === "auth") {
|
|
1386
1462
|
const authSystemFields = [
|
|
@@ -1440,26 +1516,43 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
|
1440
1516
|
}
|
|
1441
1517
|
}
|
|
1442
1518
|
const indexes = extractIndexes(zodSchema) || [];
|
|
1519
|
+
if (isView && indexes.length > 0) {
|
|
1520
|
+
throw new Error(
|
|
1521
|
+
`View collection "${collectionName}" cannot declare indexes. PocketBase view collections are backed by a SQL query and do not support indexes.`
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1443
1524
|
const permissionAnalyzer = new PermissionAnalyzer();
|
|
1444
1525
|
let permissions = void 0;
|
|
1445
1526
|
const schemaDescription = zodSchema.description;
|
|
1446
1527
|
const extractedPermissions = permissionAnalyzer.extractPermissions(schemaDescription);
|
|
1447
1528
|
if (extractedPermissions) {
|
|
1448
1529
|
const resolvedPermissions = permissionAnalyzer.resolvePermissions(extractedPermissions);
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
|
|
1458
|
-
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
1530
|
+
if (isView) {
|
|
1531
|
+
for (const ruleType of ["createRule", "updateRule", "deleteRule", "manageRule"]) {
|
|
1532
|
+
if (resolvedPermissions[ruleType]) {
|
|
1533
|
+
console.warn(
|
|
1534
|
+
`[${collectionName}] ${ruleType} is not supported on view collections and will be set to null.`
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
resolvedPermissions[ruleType] = null;
|
|
1459
1538
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1539
|
+
}
|
|
1540
|
+
if (!isView) {
|
|
1541
|
+
const validationResults = permissionAnalyzer.validatePermissions(
|
|
1542
|
+
collectionName,
|
|
1543
|
+
resolvedPermissions,
|
|
1544
|
+
fields,
|
|
1545
|
+
collectionType === "auth"
|
|
1546
|
+
);
|
|
1547
|
+
for (const [ruleType, result] of validationResults) {
|
|
1548
|
+
if (!result.valid) {
|
|
1549
|
+
console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
|
|
1550
|
+
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
1551
|
+
}
|
|
1552
|
+
if (result.warnings.length > 0) {
|
|
1553
|
+
console.warn(`[${collectionName}] Permission warnings for ${ruleType}:`);
|
|
1554
|
+
result.warnings.forEach((warning) => console.warn(` - ${warning}`));
|
|
1555
|
+
}
|
|
1463
1556
|
}
|
|
1464
1557
|
}
|
|
1465
1558
|
permissions = permissionAnalyzer.mergeWithDefaults(resolvedPermissions);
|
|
@@ -1480,6 +1573,9 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
|
1480
1573
|
},
|
|
1481
1574
|
permissions
|
|
1482
1575
|
};
|
|
1576
|
+
if (isView) {
|
|
1577
|
+
collectionSchema.viewQuery = viewQuery;
|
|
1578
|
+
}
|
|
1483
1579
|
return collectionSchema;
|
|
1484
1580
|
}
|
|
1485
1581
|
var tsxLoaderRegistered = false;
|
|
@@ -1737,8 +1833,9 @@ function convertPocketBaseField(pbField) {
|
|
|
1737
1833
|
}
|
|
1738
1834
|
function convertPocketBaseCollection(pbCollection) {
|
|
1739
1835
|
const fields = [];
|
|
1836
|
+
const isView = pbCollection.type === "view";
|
|
1740
1837
|
const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
|
|
1741
|
-
if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
|
|
1838
|
+
if (!isView && pbCollection.fields && Array.isArray(pbCollection.fields)) {
|
|
1742
1839
|
for (const pbField of pbCollection.fields) {
|
|
1743
1840
|
if (pbField.system || systemFieldNames.includes(pbField.name)) {
|
|
1744
1841
|
const isAuthSystemField = pbCollection.type === "auth" && ["email", "emailVisibility", "verified", "password", "tokenKey"].includes(pbField.name);
|
|
@@ -1758,6 +1855,9 @@ function convertPocketBaseCollection(pbCollection) {
|
|
|
1758
1855
|
if (pbCollection.id) {
|
|
1759
1856
|
schema.id = pbCollection.id;
|
|
1760
1857
|
}
|
|
1858
|
+
if (typeof pbCollection.viewQuery === "string") {
|
|
1859
|
+
schema.viewQuery = dedentSql(pbCollection.viewQuery);
|
|
1860
|
+
}
|
|
1761
1861
|
if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
|
|
1762
1862
|
schema.indexes = pbCollection.indexes;
|
|
1763
1863
|
}
|
|
@@ -1859,6 +1959,24 @@ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
|
|
|
1859
1959
|
return [];
|
|
1860
1960
|
}
|
|
1861
1961
|
}
|
|
1962
|
+
function skipTemplateLiteral(content, start) {
|
|
1963
|
+
let i = start + 1;
|
|
1964
|
+
while (i < content.length) {
|
|
1965
|
+
const char = content[i];
|
|
1966
|
+
if (char === "\\") {
|
|
1967
|
+
i += 2;
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
if (char === "`") {
|
|
1971
|
+
return i + 1;
|
|
1972
|
+
}
|
|
1973
|
+
i++;
|
|
1974
|
+
}
|
|
1975
|
+
return i;
|
|
1976
|
+
}
|
|
1977
|
+
function unescapeTemplateLiteral(raw) {
|
|
1978
|
+
return raw.replace(/\\(`|\$\{|\\)/g, "$1");
|
|
1979
|
+
}
|
|
1862
1980
|
function parseMigrationOperationsFromContent(content) {
|
|
1863
1981
|
const collectionsToCreate = [];
|
|
1864
1982
|
const collectionsToDelete = [];
|
|
@@ -1947,6 +2065,10 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
1947
2065
|
while (i < content.length && bCount > 0) {
|
|
1948
2066
|
const char = content[i];
|
|
1949
2067
|
const prev = i > 0 ? content[i - 1] : "";
|
|
2068
|
+
if (!inStr && char === "`") {
|
|
2069
|
+
i = skipTemplateLiteral(content, i);
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
1950
2072
|
if (!inStr && (char === '"' || char === "'")) {
|
|
1951
2073
|
inStr = true;
|
|
1952
2074
|
strChar = char;
|
|
@@ -2136,6 +2258,27 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2136
2258
|
}
|
|
2137
2259
|
}
|
|
2138
2260
|
}
|
|
2261
|
+
const viewQueryRegex = /(\w+)\.viewQuery\s*=\s*/g;
|
|
2262
|
+
let viewQueryMatch;
|
|
2263
|
+
while ((viewQueryMatch = viewQueryRegex.exec(content)) !== null) {
|
|
2264
|
+
const varInfo = variables.get(viewQueryMatch[1]);
|
|
2265
|
+
if (!varInfo || varInfo.type !== "collection") continue;
|
|
2266
|
+
const valueStart = viewQueryMatch.index + viewQueryMatch[0].length;
|
|
2267
|
+
if (content[valueStart] === "`") {
|
|
2268
|
+
const end = skipTemplateLiteral(content, valueStart);
|
|
2269
|
+
const raw = content.substring(valueStart + 1, end - 1);
|
|
2270
|
+
getUpdate(varInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
|
|
2271
|
+
viewQueryRegex.lastIndex = end;
|
|
2272
|
+
} else {
|
|
2273
|
+
const terminator = content.indexOf(";", valueStart);
|
|
2274
|
+
const valueStr = content.substring(valueStart, terminator === -1 ? content.length : terminator);
|
|
2275
|
+
try {
|
|
2276
|
+
getUpdate(varInfo.name).viewQuery = dedentSql(new Function("app", `return ${valueStr}`)(mockApp));
|
|
2277
|
+
} catch {
|
|
2278
|
+
getUpdate(varInfo.name).viewQuery = dedentSql(valueStr.trim());
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2139
2282
|
const unmarshalRegex = /unmarshal\s*\(/g;
|
|
2140
2283
|
let unmarshalMatch;
|
|
2141
2284
|
while ((unmarshalMatch = unmarshalRegex.exec(content)) !== null) {
|
|
@@ -2150,6 +2293,10 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2150
2293
|
while (i < content.length && braceCount > 0) {
|
|
2151
2294
|
const ch = content[i];
|
|
2152
2295
|
const prev = i > 0 ? content[i - 1] : "";
|
|
2296
|
+
if (!inStr && ch === "`") {
|
|
2297
|
+
i = skipTemplateLiteral(content, i);
|
|
2298
|
+
continue;
|
|
2299
|
+
}
|
|
2153
2300
|
if (!inStr && (ch === '"' || ch === "'")) {
|
|
2154
2301
|
inStr = true;
|
|
2155
2302
|
strChar = ch;
|
|
@@ -2183,6 +2330,22 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2183
2330
|
getUpdate(colInfo.name).rulesToUpdate[ruleKey] = valStr;
|
|
2184
2331
|
}
|
|
2185
2332
|
}
|
|
2333
|
+
const viewQueryKey = objStr.match(/"viewQuery"\s*:\s*/);
|
|
2334
|
+
if (viewQueryKey) {
|
|
2335
|
+
const valueStart = viewQueryKey.index + viewQueryKey[0].length;
|
|
2336
|
+
if (objStr[valueStart] === "`") {
|
|
2337
|
+
const end = skipTemplateLiteral(objStr, valueStart);
|
|
2338
|
+
const raw = objStr.substring(valueStart + 1, end - 1);
|
|
2339
|
+
getUpdate(colInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
|
|
2340
|
+
} else {
|
|
2341
|
+
const valStr = objStr.substring(valueStart).replace(/[,}\s]+$/, "");
|
|
2342
|
+
try {
|
|
2343
|
+
getUpdate(colInfo.name).viewQuery = dedentSql(new Function("app", `return ${valStr}`)(mockApp));
|
|
2344
|
+
} catch {
|
|
2345
|
+
getUpdate(colInfo.name).viewQuery = dedentSql(valStr);
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2186
2349
|
}
|
|
2187
2350
|
const idxPushRegex = /(\w+)\.indexes\.push\s*\(/g;
|
|
2188
2351
|
let match;
|
|
@@ -2313,6 +2476,10 @@ function parseMigrationOperations(migrationContent) {
|
|
|
2313
2476
|
while (i < migrationContent.length && braceCount > 0) {
|
|
2314
2477
|
const char = migrationContent[i];
|
|
2315
2478
|
const prevChar = i > 0 ? migrationContent[i - 1] : "";
|
|
2479
|
+
if (!inString && char === "`") {
|
|
2480
|
+
i = skipTemplateLiteral(migrationContent, i);
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2316
2483
|
if (!inString && (char === '"' || char === "'")) {
|
|
2317
2484
|
inString = true;
|
|
2318
2485
|
stringChar = char;
|
|
@@ -2420,6 +2587,9 @@ function applyMigrationOperations(snapshot, operations) {
|
|
|
2420
2587
|
collection.indexes = collection.indexes.filter((idx) => !update.indexesToRemove.includes(idx));
|
|
2421
2588
|
}
|
|
2422
2589
|
}
|
|
2590
|
+
if (update.viewQuery !== void 0) {
|
|
2591
|
+
collection.viewQuery = update.viewQuery;
|
|
2592
|
+
}
|
|
2423
2593
|
if (Object.keys(update.rulesToUpdate).length > 0) {
|
|
2424
2594
|
if (!collection.rules) collection.rules = {};
|
|
2425
2595
|
if (!collection.permissions) collection.permissions = {};
|
|
@@ -2883,7 +3053,45 @@ function compareCollectionFields(currentCollection, previousCollection, config,
|
|
|
2883
3053
|
fieldsToRemove = fieldsToRemove.filter((_, index) => !processedRemoveIndices.has(index));
|
|
2884
3054
|
return { fieldsToAdd, fieldsToRemove, fieldsToModify };
|
|
2885
3055
|
}
|
|
3056
|
+
function normalizeSql(query) {
|
|
3057
|
+
if (!query) {
|
|
3058
|
+
return "";
|
|
3059
|
+
}
|
|
3060
|
+
return query.split("\n").map((line) => line.trim()).join(" ").replace(/\s+/g, " ").trim();
|
|
3061
|
+
}
|
|
3062
|
+
function compareViewQuery(currentCollection, previousCollection) {
|
|
3063
|
+
const newValue = currentCollection.viewQuery;
|
|
3064
|
+
if (typeof newValue !== "string") {
|
|
3065
|
+
return void 0;
|
|
3066
|
+
}
|
|
3067
|
+
if (normalizeSql(newValue) === normalizeSql(previousCollection.viewQuery)) {
|
|
3068
|
+
return void 0;
|
|
3069
|
+
}
|
|
3070
|
+
return {
|
|
3071
|
+
oldValue: previousCollection.viewQuery ?? null,
|
|
3072
|
+
newValue
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
2886
3075
|
function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
|
|
3076
|
+
const isView = currentCollection.type === "view" || previousCollection.type === "view";
|
|
3077
|
+
if (isView) {
|
|
3078
|
+
return {
|
|
3079
|
+
collection: currentCollection.name,
|
|
3080
|
+
fieldsToAdd: [],
|
|
3081
|
+
fieldsToRemove: [],
|
|
3082
|
+
fieldsToModify: [],
|
|
3083
|
+
indexesToAdd: [],
|
|
3084
|
+
indexesToRemove: [],
|
|
3085
|
+
rulesToUpdate: compareRules(
|
|
3086
|
+
currentCollection.rules,
|
|
3087
|
+
previousCollection.rules,
|
|
3088
|
+
currentCollection.permissions,
|
|
3089
|
+
previousCollection.permissions
|
|
3090
|
+
),
|
|
3091
|
+
permissionsToUpdate: comparePermissions(currentCollection.permissions, previousCollection.permissions),
|
|
3092
|
+
viewQueryUpdate: compareViewQuery(currentCollection, previousCollection)
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
2887
3095
|
const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
|
|
2888
3096
|
currentCollection,
|
|
2889
3097
|
previousCollection,
|
|
@@ -2915,7 +3123,11 @@ function categorizeChangesBySeverity(diff, _config) {
|
|
|
2915
3123
|
const destructive = [];
|
|
2916
3124
|
const nonDestructive = [];
|
|
2917
3125
|
for (const collection of diff.collectionsToDelete) {
|
|
2918
|
-
|
|
3126
|
+
if (collection.type === "view") {
|
|
3127
|
+
nonDestructive.push(`Delete view collection: ${collection.name}`);
|
|
3128
|
+
} else {
|
|
3129
|
+
destructive.push(`Delete collection: ${collection.name}`);
|
|
3130
|
+
}
|
|
2919
3131
|
}
|
|
2920
3132
|
for (const collection of diff.collectionsToCreate) {
|
|
2921
3133
|
nonDestructive.push(`Create collection: ${collection.name}`);
|
|
@@ -2950,6 +3162,9 @@ function categorizeChangesBySeverity(diff, _config) {
|
|
|
2950
3162
|
for (const rule of modification.rulesToUpdate) {
|
|
2951
3163
|
nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
|
|
2952
3164
|
}
|
|
3165
|
+
if (modification.viewQueryUpdate) {
|
|
3166
|
+
nonDestructive.push(`Update view query: ${collectionName}`);
|
|
3167
|
+
}
|
|
2953
3168
|
}
|
|
2954
3169
|
return { destructive, nonDestructive };
|
|
2955
3170
|
}
|
|
@@ -2978,12 +3193,11 @@ function filterDiff(diff, options) {
|
|
|
2978
3193
|
});
|
|
2979
3194
|
let collectionsToDelete = diff.collectionsToDelete;
|
|
2980
3195
|
if (skipDestructive) {
|
|
2981
|
-
collectionsToDelete =
|
|
2982
|
-
} else {
|
|
2983
|
-
collectionsToDelete = collectionsToDelete.filter((col) => {
|
|
2984
|
-
return matchesPattern(col.name, patterns);
|
|
2985
|
-
});
|
|
3196
|
+
collectionsToDelete = collectionsToDelete.filter((col) => col.type === "view");
|
|
2986
3197
|
}
|
|
3198
|
+
collectionsToDelete = collectionsToDelete.filter((col) => {
|
|
3199
|
+
return matchesPattern(col.name, patterns);
|
|
3200
|
+
});
|
|
2987
3201
|
const collectionsToModify = diff.collectionsToModify.map((mod) => {
|
|
2988
3202
|
const collectionMatches = matchesPattern(mod.collection, patterns);
|
|
2989
3203
|
const fieldsToAdd = mod.fieldsToAdd.filter((field) => {
|
|
@@ -3008,6 +3222,7 @@ function filterDiff(diff, options) {
|
|
|
3008
3222
|
const indexesToRemove = collectionMatches ? mod.indexesToRemove : [];
|
|
3009
3223
|
const rulesToUpdate = collectionMatches ? mod.rulesToUpdate : [];
|
|
3010
3224
|
const permissionsToUpdate = collectionMatches ? mod.permissionsToUpdate : [];
|
|
3225
|
+
const viewQueryUpdate = collectionMatches ? mod.viewQueryUpdate : void 0;
|
|
3011
3226
|
return {
|
|
3012
3227
|
...mod,
|
|
3013
3228
|
fieldsToAdd,
|
|
@@ -3016,10 +3231,11 @@ function filterDiff(diff, options) {
|
|
|
3016
3231
|
indexesToAdd,
|
|
3017
3232
|
indexesToRemove,
|
|
3018
3233
|
rulesToUpdate,
|
|
3019
|
-
permissionsToUpdate
|
|
3234
|
+
permissionsToUpdate,
|
|
3235
|
+
viewQueryUpdate
|
|
3020
3236
|
};
|
|
3021
3237
|
}).filter((mod) => {
|
|
3022
|
-
return mod.fieldsToAdd.length > 0 || mod.fieldsToRemove.length > 0 || mod.fieldsToModify.length > 0 || mod.indexesToAdd.length > 0 || mod.indexesToRemove.length > 0 || mod.rulesToUpdate.length > 0 || mod.permissionsToUpdate.length > 0;
|
|
3238
|
+
return mod.fieldsToAdd.length > 0 || mod.fieldsToRemove.length > 0 || mod.fieldsToModify.length > 0 || mod.indexesToAdd.length > 0 || mod.indexesToRemove.length > 0 || mod.rulesToUpdate.length > 0 || mod.permissionsToUpdate.length > 0 || mod.viewQueryUpdate !== void 0;
|
|
3023
3239
|
});
|
|
3024
3240
|
return {
|
|
3025
3241
|
...diff,
|
|
@@ -3031,7 +3247,7 @@ function filterDiff(diff, options) {
|
|
|
3031
3247
|
|
|
3032
3248
|
// src/migration/diff/index.ts
|
|
3033
3249
|
function hasChanges(modification) {
|
|
3034
|
-
return modification.fieldsToAdd.length > 0 || modification.fieldsToRemove.length > 0 || modification.fieldsToModify.length > 0 || modification.indexesToAdd.length > 0 || modification.indexesToRemove.length > 0 || modification.rulesToUpdate.length > 0 || modification.permissionsToUpdate.length > 0;
|
|
3250
|
+
return modification.fieldsToAdd.length > 0 || modification.fieldsToRemove.length > 0 || modification.fieldsToModify.length > 0 || modification.indexesToAdd.length > 0 || modification.indexesToRemove.length > 0 || modification.rulesToUpdate.length > 0 || modification.permissionsToUpdate.length > 0 || modification.viewQueryUpdate !== void 0;
|
|
3035
3251
|
}
|
|
3036
3252
|
function aggregateChanges(currentSchema, previousSnapshot, config) {
|
|
3037
3253
|
const collectionIdToName = /* @__PURE__ */ new Map();
|
|
@@ -3210,6 +3426,13 @@ function formatValue(value) {
|
|
|
3210
3426
|
}
|
|
3211
3427
|
return String(value);
|
|
3212
3428
|
}
|
|
3429
|
+
function formatSqlTemplate(query, indent = " ") {
|
|
3430
|
+
const escaped = query.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
3431
|
+
const body = escaped.split("\n").map((line) => line.trim() === "" ? "" : `${indent}${line}`).join("\n");
|
|
3432
|
+
return `\`
|
|
3433
|
+
${body}
|
|
3434
|
+
${indent.slice(0, -2)}\``;
|
|
3435
|
+
}
|
|
3213
3436
|
function getFieldConstructorName(fieldType) {
|
|
3214
3437
|
const constructorMap = {
|
|
3215
3438
|
text: "TextField",
|
|
@@ -3570,6 +3793,15 @@ function generateCollectionRules(rules, collectionType = "base") {
|
|
|
3570
3793
|
if (!rules) {
|
|
3571
3794
|
return "";
|
|
3572
3795
|
}
|
|
3796
|
+
if (collectionType === "view") {
|
|
3797
|
+
return [
|
|
3798
|
+
`"listRule": ${formatValue(rules.listRule ?? null)}`,
|
|
3799
|
+
`"viewRule": ${formatValue(rules.viewRule ?? null)}`,
|
|
3800
|
+
`"createRule": null`,
|
|
3801
|
+
`"updateRule": null`,
|
|
3802
|
+
`"deleteRule": null`
|
|
3803
|
+
].join(",\n ");
|
|
3804
|
+
}
|
|
3573
3805
|
const parts = [];
|
|
3574
3806
|
if (rules.listRule !== void 0) {
|
|
3575
3807
|
parts.push(`"listRule": ${formatValue(rules.listRule)}`);
|
|
@@ -3595,6 +3827,15 @@ function generateCollectionPermissions(permissions, collectionType = "base") {
|
|
|
3595
3827
|
if (!permissions) {
|
|
3596
3828
|
return "";
|
|
3597
3829
|
}
|
|
3830
|
+
if (collectionType === "view") {
|
|
3831
|
+
return [
|
|
3832
|
+
`"listRule": ${formatValue(permissions.listRule ?? null)}`,
|
|
3833
|
+
`"viewRule": ${formatValue(permissions.viewRule ?? null)}`,
|
|
3834
|
+
`"createRule": null`,
|
|
3835
|
+
`"updateRule": null`,
|
|
3836
|
+
`"deleteRule": null`
|
|
3837
|
+
].join(",\n ");
|
|
3838
|
+
}
|
|
3598
3839
|
const parts = [];
|
|
3599
3840
|
if (permissions.listRule !== void 0) {
|
|
3600
3841
|
parts.push(`"listRule": ${formatValue(permissions.listRule)}`);
|
|
@@ -3632,6 +3873,16 @@ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, i
|
|
|
3632
3873
|
lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
|
|
3633
3874
|
return lines.join("\n");
|
|
3634
3875
|
}
|
|
3876
|
+
function generateViewQueryUpdate(collectionName, viewQuery, varName, isLast = false, collectionIdMap) {
|
|
3877
|
+
const lines = [];
|
|
3878
|
+
const collectionVar = varName || `collection_${collectionName}_viewQuery`;
|
|
3879
|
+
lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
|
|
3880
|
+
lines.push(` unmarshal({`);
|
|
3881
|
+
lines.push(` "viewQuery": ${formatSqlTemplate(viewQuery, " ")},`);
|
|
3882
|
+
lines.push(` }, ${collectionVar})`);
|
|
3883
|
+
lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
|
|
3884
|
+
return lines.join("\n");
|
|
3885
|
+
}
|
|
3635
3886
|
function generateGroupedRuleUpdates(collectionName, entries, varSuffix, isLast = false, collectionIdMap) {
|
|
3636
3887
|
const collectionVar = `collection_${collectionName}_${varSuffix}`;
|
|
3637
3888
|
const lines = [];
|
|
@@ -3662,6 +3913,13 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
|
|
|
3662
3913
|
} else if (rulesCode) {
|
|
3663
3914
|
lines.push(` ${rulesCode},`);
|
|
3664
3915
|
}
|
|
3916
|
+
if (collection.type === "view") {
|
|
3917
|
+
lines.push(` "viewQuery": ${formatSqlTemplate(collection.viewQuery ?? "")},`);
|
|
3918
|
+
lines.push(` });`);
|
|
3919
|
+
lines.push(``);
|
|
3920
|
+
lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
|
|
3921
|
+
return lines.join("\n");
|
|
3922
|
+
}
|
|
3665
3923
|
const systemFieldNames = ["created", "updated", "id"];
|
|
3666
3924
|
if (collection.type === "auth") {
|
|
3667
3925
|
systemFieldNames.push(...getAuthSystemFields().map((f) => f.name));
|
|
@@ -3702,7 +3960,21 @@ function generateOperationUpMigration(operation, collectionIdMap) {
|
|
|
3702
3960
|
const modification = operation.modifications;
|
|
3703
3961
|
const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
|
|
3704
3962
|
let operationCount = 0;
|
|
3705
|
-
const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + (modification.permissionsToUpdate.length > 0 ? 1 : modification.rulesToUpdate.length > 0 ? 1 : 0);
|
|
3963
|
+
const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + (modification.viewQueryUpdate ? 1 : 0) + (modification.permissionsToUpdate.length > 0 ? 1 : modification.rulesToUpdate.length > 0 ? 1 : 0);
|
|
3964
|
+
if (modification.viewQueryUpdate) {
|
|
3965
|
+
operationCount++;
|
|
3966
|
+
const isLast = operationCount === totalOperations;
|
|
3967
|
+
lines.push(
|
|
3968
|
+
generateViewQueryUpdate(
|
|
3969
|
+
collectionName,
|
|
3970
|
+
modification.viewQueryUpdate.newValue,
|
|
3971
|
+
void 0,
|
|
3972
|
+
isLast,
|
|
3973
|
+
collectionIdMap
|
|
3974
|
+
)
|
|
3975
|
+
);
|
|
3976
|
+
if (!isLast) lines.push("");
|
|
3977
|
+
}
|
|
3706
3978
|
for (let i = 0; i < modification.fieldsToAdd.length; i++) {
|
|
3707
3979
|
const field = modification.fieldsToAdd[i];
|
|
3708
3980
|
operationCount++;
|
|
@@ -3803,7 +4075,21 @@ function generateOperationDownMigration(operation, collectionIdMap) {
|
|
|
3803
4075
|
const modification = operation.modifications;
|
|
3804
4076
|
const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
|
|
3805
4077
|
let operationCount = 0;
|
|
3806
|
-
const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + (modification.permissionsToUpdate.length > 0 ? 1 : modification.rulesToUpdate.length > 0 ? 1 : 0);
|
|
4078
|
+
const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + (modification.viewQueryUpdate ? 1 : 0) + (modification.permissionsToUpdate.length > 0 ? 1 : modification.rulesToUpdate.length > 0 ? 1 : 0);
|
|
4079
|
+
if (modification.viewQueryUpdate) {
|
|
4080
|
+
operationCount++;
|
|
4081
|
+
const isLast = operationCount === totalOperations;
|
|
4082
|
+
lines.push(
|
|
4083
|
+
generateViewQueryUpdate(
|
|
4084
|
+
collectionName,
|
|
4085
|
+
modification.viewQueryUpdate.oldValue ?? "",
|
|
4086
|
+
`collection_${collectionName}_revert_viewQuery`,
|
|
4087
|
+
isLast,
|
|
4088
|
+
collectionIdMap
|
|
4089
|
+
)
|
|
4090
|
+
);
|
|
4091
|
+
if (!isLast) lines.push("");
|
|
4092
|
+
}
|
|
3807
4093
|
if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
|
|
3808
4094
|
operationCount++;
|
|
3809
4095
|
const isLast = operationCount === totalOperations;
|
|
@@ -4076,6 +4362,9 @@ function generate(diff, config) {
|
|
|
4076
4362
|
function detectCollectionDeletions(diff) {
|
|
4077
4363
|
const changes = [];
|
|
4078
4364
|
for (const collection of diff.collectionsToDelete) {
|
|
4365
|
+
if (collection.type === "view") {
|
|
4366
|
+
continue;
|
|
4367
|
+
}
|
|
4079
4368
|
changes.push({
|
|
4080
4369
|
type: "collection_deletion" /* COLLECTION_DELETION */,
|
|
4081
4370
|
description: `Delete collection: ${collection.name}`,
|
|
@@ -4489,7 +4778,11 @@ function formatChangeSummary(diff) {
|
|
|
4489
4778
|
lines.push(chalk.green.bold(`\u2713 ${totalCollectionsToCreate} collection(s) to create:`));
|
|
4490
4779
|
for (const collection of diff.collectionsToCreate) {
|
|
4491
4780
|
lines.push(chalk.green(` + ${collection.name} (${collection.type})`));
|
|
4492
|
-
|
|
4781
|
+
if (collection.type === "view") {
|
|
4782
|
+
lines.push(chalk.gray(` fields derived from the view query`));
|
|
4783
|
+
} else {
|
|
4784
|
+
lines.push(chalk.gray(` ${collection.fields.length} field(s)`));
|
|
4785
|
+
}
|
|
4493
4786
|
}
|
|
4494
4787
|
lines.push("");
|
|
4495
4788
|
}
|
|
@@ -4534,6 +4827,9 @@ function formatChangeSummary(diff) {
|
|
|
4534
4827
|
if (modification.rulesToUpdate.length > 0) {
|
|
4535
4828
|
lines.push(chalk.yellow(` ~ ${modification.rulesToUpdate.length} rule(s) to update`));
|
|
4536
4829
|
}
|
|
4830
|
+
if (modification.viewQueryUpdate) {
|
|
4831
|
+
lines.push(chalk.yellow(` ~ view query to update`));
|
|
4832
|
+
}
|
|
4537
4833
|
lines.push("");
|
|
4538
4834
|
}
|
|
4539
4835
|
}
|