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/migration/index.cjs
CHANGED
|
@@ -1483,6 +1483,60 @@ function getFieldTypeInfo(zodType, fieldName) {
|
|
|
1483
1483
|
options
|
|
1484
1484
|
};
|
|
1485
1485
|
}
|
|
1486
|
+
function dedentSql(value) {
|
|
1487
|
+
const lines = value.replace(/\r\n/g, "\n").split("\n");
|
|
1488
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
1489
|
+
lines.shift();
|
|
1490
|
+
}
|
|
1491
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
1492
|
+
lines.pop();
|
|
1493
|
+
}
|
|
1494
|
+
if (lines.length === 0) {
|
|
1495
|
+
return "";
|
|
1496
|
+
}
|
|
1497
|
+
let minIndent = Infinity;
|
|
1498
|
+
for (const line of lines) {
|
|
1499
|
+
if (line.trim() === "") continue;
|
|
1500
|
+
const indent = line.length - line.trimStart().length;
|
|
1501
|
+
if (indent < minIndent) {
|
|
1502
|
+
minIndent = indent;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
if (!Number.isFinite(minIndent) || minIndent === 0) {
|
|
1506
|
+
return lines.map((line) => line.trimEnd()).join("\n");
|
|
1507
|
+
}
|
|
1508
|
+
return lines.map((line) => line.slice(minIndent).trimEnd()).join("\n");
|
|
1509
|
+
}
|
|
1510
|
+
function stripLeadingComments(query) {
|
|
1511
|
+
let remaining = query.trim();
|
|
1512
|
+
while (remaining.length > 0) {
|
|
1513
|
+
if (remaining.startsWith("--")) {
|
|
1514
|
+
const newline = remaining.indexOf("\n");
|
|
1515
|
+
remaining = newline === -1 ? "" : remaining.slice(newline + 1).trim();
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
if (remaining.startsWith("/*")) {
|
|
1519
|
+
const end = remaining.indexOf("*/");
|
|
1520
|
+
remaining = end === -1 ? "" : remaining.slice(end + 2).trim();
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
break;
|
|
1524
|
+
}
|
|
1525
|
+
return remaining;
|
|
1526
|
+
}
|
|
1527
|
+
function validateViewQuery(collectionName, viewQuery) {
|
|
1528
|
+
if (typeof viewQuery !== "string" || viewQuery.trim() === "") {
|
|
1529
|
+
throw new Error(
|
|
1530
|
+
`View collection "${collectionName}" requires a non-empty viewQuery. Provide the SQL SELECT statement backing the view.`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
const body = stripLeadingComments(viewQuery);
|
|
1534
|
+
if (!/^(select|with)\b/i.test(body)) {
|
|
1535
|
+
throw new Error(
|
|
1536
|
+
`View collection "${collectionName}" has an invalid viewQuery: it must start with SELECT or WITH. Received: ${body.slice(0, 40)}${body.length > 40 ? "..." : ""}`
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1486
1540
|
function getCollectionNameFromFile(filePath) {
|
|
1487
1541
|
const filename = path5__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
|
|
1488
1542
|
return toCollectionName(filename);
|
|
@@ -1506,13 +1560,26 @@ function extractCollectionTypeFromSchema(zodSchema) {
|
|
|
1506
1560
|
}
|
|
1507
1561
|
try {
|
|
1508
1562
|
const metadata = JSON.parse(zodSchema.description);
|
|
1509
|
-
if (metadata.type === "base" || metadata.type === "auth") {
|
|
1563
|
+
if (metadata.type === "base" || metadata.type === "auth" || metadata.type === "view") {
|
|
1510
1564
|
return metadata.type;
|
|
1511
1565
|
}
|
|
1512
1566
|
} catch {
|
|
1513
1567
|
}
|
|
1514
1568
|
return null;
|
|
1515
1569
|
}
|
|
1570
|
+
function extractViewQueryFromSchema(zodSchema) {
|
|
1571
|
+
if (!zodSchema.description) {
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
try {
|
|
1575
|
+
const metadata = JSON.parse(zodSchema.description);
|
|
1576
|
+
if (typeof metadata.viewQuery === "string") {
|
|
1577
|
+
return metadata.viewQuery;
|
|
1578
|
+
}
|
|
1579
|
+
} catch {
|
|
1580
|
+
}
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1516
1583
|
function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema", "Collection"]) {
|
|
1517
1584
|
const result = {};
|
|
1518
1585
|
if (module.default instanceof zod.z.ZodObject) {
|
|
@@ -1671,6 +1738,15 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
|
1671
1738
|
const rawFields = extractFieldDefinitions(zodSchema);
|
|
1672
1739
|
const explicitType = extractCollectionTypeFromSchema(zodSchema);
|
|
1673
1740
|
const collectionType = explicitType ?? (isAuthCollection(rawFields) ? "auth" : "base");
|
|
1741
|
+
const isView = collectionType === "view";
|
|
1742
|
+
const viewQuery = extractViewQueryFromSchema(zodSchema);
|
|
1743
|
+
if (isView) {
|
|
1744
|
+
validateViewQuery(collectionName, viewQuery);
|
|
1745
|
+
} else if (viewQuery !== null) {
|
|
1746
|
+
console.warn(
|
|
1747
|
+
`[${collectionName}] viewQuery is only used by view collections and will be ignored. Use defineView() or set type: "view" to define a view collection.`
|
|
1748
|
+
);
|
|
1749
|
+
}
|
|
1674
1750
|
const fields = rawFields.filter((f) => !["created", "updated"].includes(f.name)).map(({ name, zodType }) => buildFieldDefinition(name, zodType));
|
|
1675
1751
|
if (collectionType === "auth") {
|
|
1676
1752
|
const authSystemFields = [
|
|
@@ -1730,26 +1806,43 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
|
1730
1806
|
}
|
|
1731
1807
|
}
|
|
1732
1808
|
const indexes = extractIndexes(zodSchema) || [];
|
|
1809
|
+
if (isView && indexes.length > 0) {
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
`View collection "${collectionName}" cannot declare indexes. PocketBase view collections are backed by a SQL query and do not support indexes.`
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1733
1814
|
const permissionAnalyzer = new PermissionAnalyzer();
|
|
1734
1815
|
let permissions = void 0;
|
|
1735
1816
|
const schemaDescription = zodSchema.description;
|
|
1736
1817
|
const extractedPermissions = permissionAnalyzer.extractPermissions(schemaDescription);
|
|
1737
1818
|
if (extractedPermissions) {
|
|
1738
1819
|
const resolvedPermissions = permissionAnalyzer.resolvePermissions(extractedPermissions);
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
|
|
1748
|
-
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
1820
|
+
if (isView) {
|
|
1821
|
+
for (const ruleType of ["createRule", "updateRule", "deleteRule", "manageRule"]) {
|
|
1822
|
+
if (resolvedPermissions[ruleType]) {
|
|
1823
|
+
console.warn(
|
|
1824
|
+
`[${collectionName}] ${ruleType} is not supported on view collections and will be set to null.`
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
resolvedPermissions[ruleType] = null;
|
|
1749
1828
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1829
|
+
}
|
|
1830
|
+
if (!isView) {
|
|
1831
|
+
const validationResults = permissionAnalyzer.validatePermissions(
|
|
1832
|
+
collectionName,
|
|
1833
|
+
resolvedPermissions,
|
|
1834
|
+
fields,
|
|
1835
|
+
collectionType === "auth"
|
|
1836
|
+
);
|
|
1837
|
+
for (const [ruleType, result] of validationResults) {
|
|
1838
|
+
if (!result.valid) {
|
|
1839
|
+
console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
|
|
1840
|
+
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
1841
|
+
}
|
|
1842
|
+
if (result.warnings.length > 0) {
|
|
1843
|
+
console.warn(`[${collectionName}] Permission warnings for ${ruleType}:`);
|
|
1844
|
+
result.warnings.forEach((warning) => console.warn(` - ${warning}`));
|
|
1845
|
+
}
|
|
1753
1846
|
}
|
|
1754
1847
|
}
|
|
1755
1848
|
permissions = permissionAnalyzer.mergeWithDefaults(resolvedPermissions);
|
|
@@ -1770,6 +1863,9 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
|
|
|
1770
1863
|
},
|
|
1771
1864
|
permissions
|
|
1772
1865
|
};
|
|
1866
|
+
if (isView) {
|
|
1867
|
+
collectionSchema.viewQuery = viewQuery;
|
|
1868
|
+
}
|
|
1773
1869
|
return collectionSchema;
|
|
1774
1870
|
}
|
|
1775
1871
|
var tsxLoaderRegistered = false;
|
|
@@ -2051,8 +2147,9 @@ function convertPocketBaseField(pbField) {
|
|
|
2051
2147
|
}
|
|
2052
2148
|
function convertPocketBaseCollection(pbCollection) {
|
|
2053
2149
|
const fields = [];
|
|
2150
|
+
const isView = pbCollection.type === "view";
|
|
2054
2151
|
const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
|
|
2055
|
-
if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
|
|
2152
|
+
if (!isView && pbCollection.fields && Array.isArray(pbCollection.fields)) {
|
|
2056
2153
|
for (const pbField of pbCollection.fields) {
|
|
2057
2154
|
if (pbField.system || systemFieldNames.includes(pbField.name)) {
|
|
2058
2155
|
const isAuthSystemField = pbCollection.type === "auth" && ["email", "emailVisibility", "verified", "password", "tokenKey"].includes(pbField.name);
|
|
@@ -2072,6 +2169,9 @@ function convertPocketBaseCollection(pbCollection) {
|
|
|
2072
2169
|
if (pbCollection.id) {
|
|
2073
2170
|
schema.id = pbCollection.id;
|
|
2074
2171
|
}
|
|
2172
|
+
if (typeof pbCollection.viewQuery === "string") {
|
|
2173
|
+
schema.viewQuery = dedentSql(pbCollection.viewQuery);
|
|
2174
|
+
}
|
|
2075
2175
|
if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
|
|
2076
2176
|
schema.indexes = pbCollection.indexes;
|
|
2077
2177
|
}
|
|
@@ -2173,6 +2273,24 @@ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
|
|
|
2173
2273
|
return [];
|
|
2174
2274
|
}
|
|
2175
2275
|
}
|
|
2276
|
+
function skipTemplateLiteral(content, start) {
|
|
2277
|
+
let i = start + 1;
|
|
2278
|
+
while (i < content.length) {
|
|
2279
|
+
const char = content[i];
|
|
2280
|
+
if (char === "\\") {
|
|
2281
|
+
i += 2;
|
|
2282
|
+
continue;
|
|
2283
|
+
}
|
|
2284
|
+
if (char === "`") {
|
|
2285
|
+
return i + 1;
|
|
2286
|
+
}
|
|
2287
|
+
i++;
|
|
2288
|
+
}
|
|
2289
|
+
return i;
|
|
2290
|
+
}
|
|
2291
|
+
function unescapeTemplateLiteral(raw) {
|
|
2292
|
+
return raw.replace(/\\(`|\$\{|\\)/g, "$1");
|
|
2293
|
+
}
|
|
2176
2294
|
function parseMigrationOperationsFromContent(content) {
|
|
2177
2295
|
const collectionsToCreate = [];
|
|
2178
2296
|
const collectionsToDelete = [];
|
|
@@ -2261,6 +2379,10 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2261
2379
|
while (i < content.length && bCount > 0) {
|
|
2262
2380
|
const char = content[i];
|
|
2263
2381
|
const prev = i > 0 ? content[i - 1] : "";
|
|
2382
|
+
if (!inStr && char === "`") {
|
|
2383
|
+
i = skipTemplateLiteral(content, i);
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2264
2386
|
if (!inStr && (char === '"' || char === "'")) {
|
|
2265
2387
|
inStr = true;
|
|
2266
2388
|
strChar = char;
|
|
@@ -2450,6 +2572,27 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2450
2572
|
}
|
|
2451
2573
|
}
|
|
2452
2574
|
}
|
|
2575
|
+
const viewQueryRegex = /(\w+)\.viewQuery\s*=\s*/g;
|
|
2576
|
+
let viewQueryMatch;
|
|
2577
|
+
while ((viewQueryMatch = viewQueryRegex.exec(content)) !== null) {
|
|
2578
|
+
const varInfo = variables.get(viewQueryMatch[1]);
|
|
2579
|
+
if (!varInfo || varInfo.type !== "collection") continue;
|
|
2580
|
+
const valueStart = viewQueryMatch.index + viewQueryMatch[0].length;
|
|
2581
|
+
if (content[valueStart] === "`") {
|
|
2582
|
+
const end = skipTemplateLiteral(content, valueStart);
|
|
2583
|
+
const raw = content.substring(valueStart + 1, end - 1);
|
|
2584
|
+
getUpdate(varInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
|
|
2585
|
+
viewQueryRegex.lastIndex = end;
|
|
2586
|
+
} else {
|
|
2587
|
+
const terminator = content.indexOf(";", valueStart);
|
|
2588
|
+
const valueStr = content.substring(valueStart, terminator === -1 ? content.length : terminator);
|
|
2589
|
+
try {
|
|
2590
|
+
getUpdate(varInfo.name).viewQuery = dedentSql(new Function("app", `return ${valueStr}`)(mockApp));
|
|
2591
|
+
} catch {
|
|
2592
|
+
getUpdate(varInfo.name).viewQuery = dedentSql(valueStr.trim());
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2453
2596
|
const unmarshalRegex = /unmarshal\s*\(/g;
|
|
2454
2597
|
let unmarshalMatch;
|
|
2455
2598
|
while ((unmarshalMatch = unmarshalRegex.exec(content)) !== null) {
|
|
@@ -2464,6 +2607,10 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2464
2607
|
while (i < content.length && braceCount > 0) {
|
|
2465
2608
|
const ch = content[i];
|
|
2466
2609
|
const prev = i > 0 ? content[i - 1] : "";
|
|
2610
|
+
if (!inStr && ch === "`") {
|
|
2611
|
+
i = skipTemplateLiteral(content, i);
|
|
2612
|
+
continue;
|
|
2613
|
+
}
|
|
2467
2614
|
if (!inStr && (ch === '"' || ch === "'")) {
|
|
2468
2615
|
inStr = true;
|
|
2469
2616
|
strChar = ch;
|
|
@@ -2497,6 +2644,22 @@ function parseMigrationOperationsFromContent(content) {
|
|
|
2497
2644
|
getUpdate(colInfo.name).rulesToUpdate[ruleKey] = valStr;
|
|
2498
2645
|
}
|
|
2499
2646
|
}
|
|
2647
|
+
const viewQueryKey = objStr.match(/"viewQuery"\s*:\s*/);
|
|
2648
|
+
if (viewQueryKey) {
|
|
2649
|
+
const valueStart = viewQueryKey.index + viewQueryKey[0].length;
|
|
2650
|
+
if (objStr[valueStart] === "`") {
|
|
2651
|
+
const end = skipTemplateLiteral(objStr, valueStart);
|
|
2652
|
+
const raw = objStr.substring(valueStart + 1, end - 1);
|
|
2653
|
+
getUpdate(colInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
|
|
2654
|
+
} else {
|
|
2655
|
+
const valStr = objStr.substring(valueStart).replace(/[,}\s]+$/, "");
|
|
2656
|
+
try {
|
|
2657
|
+
getUpdate(colInfo.name).viewQuery = dedentSql(new Function("app", `return ${valStr}`)(mockApp));
|
|
2658
|
+
} catch {
|
|
2659
|
+
getUpdate(colInfo.name).viewQuery = dedentSql(valStr);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2500
2663
|
}
|
|
2501
2664
|
const idxPushRegex = /(\w+)\.indexes\.push\s*\(/g;
|
|
2502
2665
|
let match;
|
|
@@ -2627,6 +2790,10 @@ function parseMigrationOperations(migrationContent) {
|
|
|
2627
2790
|
while (i < migrationContent.length && braceCount > 0) {
|
|
2628
2791
|
const char = migrationContent[i];
|
|
2629
2792
|
const prevChar = i > 0 ? migrationContent[i - 1] : "";
|
|
2793
|
+
if (!inString && char === "`") {
|
|
2794
|
+
i = skipTemplateLiteral(migrationContent, i);
|
|
2795
|
+
continue;
|
|
2796
|
+
}
|
|
2630
2797
|
if (!inString && (char === '"' || char === "'")) {
|
|
2631
2798
|
inString = true;
|
|
2632
2799
|
stringChar = char;
|
|
@@ -2962,6 +3129,9 @@ function applyMigrationOperations(snapshot, operations) {
|
|
|
2962
3129
|
collection.indexes = collection.indexes.filter((idx) => !update.indexesToRemove.includes(idx));
|
|
2963
3130
|
}
|
|
2964
3131
|
}
|
|
3132
|
+
if (update.viewQuery !== void 0) {
|
|
3133
|
+
collection.viewQuery = update.viewQuery;
|
|
3134
|
+
}
|
|
2965
3135
|
if (Object.keys(update.rulesToUpdate).length > 0) {
|
|
2966
3136
|
if (!collection.rules) collection.rules = {};
|
|
2967
3137
|
if (!collection.permissions) collection.permissions = {};
|
|
@@ -3581,7 +3751,45 @@ function compareCollectionFields(currentCollection, previousCollection, config,
|
|
|
3581
3751
|
fieldsToRemove = fieldsToRemove.filter((_, index) => !processedRemoveIndices.has(index));
|
|
3582
3752
|
return { fieldsToAdd, fieldsToRemove, fieldsToModify };
|
|
3583
3753
|
}
|
|
3754
|
+
function normalizeSql(query) {
|
|
3755
|
+
if (!query) {
|
|
3756
|
+
return "";
|
|
3757
|
+
}
|
|
3758
|
+
return query.split("\n").map((line) => line.trim()).join(" ").replace(/\s+/g, " ").trim();
|
|
3759
|
+
}
|
|
3760
|
+
function compareViewQuery(currentCollection, previousCollection) {
|
|
3761
|
+
const newValue = currentCollection.viewQuery;
|
|
3762
|
+
if (typeof newValue !== "string") {
|
|
3763
|
+
return void 0;
|
|
3764
|
+
}
|
|
3765
|
+
if (normalizeSql(newValue) === normalizeSql(previousCollection.viewQuery)) {
|
|
3766
|
+
return void 0;
|
|
3767
|
+
}
|
|
3768
|
+
return {
|
|
3769
|
+
oldValue: previousCollection.viewQuery ?? null,
|
|
3770
|
+
newValue
|
|
3771
|
+
};
|
|
3772
|
+
}
|
|
3584
3773
|
function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
|
|
3774
|
+
const isView = currentCollection.type === "view" || previousCollection.type === "view";
|
|
3775
|
+
if (isView) {
|
|
3776
|
+
return {
|
|
3777
|
+
collection: currentCollection.name,
|
|
3778
|
+
fieldsToAdd: [],
|
|
3779
|
+
fieldsToRemove: [],
|
|
3780
|
+
fieldsToModify: [],
|
|
3781
|
+
indexesToAdd: [],
|
|
3782
|
+
indexesToRemove: [],
|
|
3783
|
+
rulesToUpdate: compareRules(
|
|
3784
|
+
currentCollection.rules,
|
|
3785
|
+
previousCollection.rules,
|
|
3786
|
+
currentCollection.permissions,
|
|
3787
|
+
previousCollection.permissions
|
|
3788
|
+
),
|
|
3789
|
+
permissionsToUpdate: comparePermissions(currentCollection.permissions, previousCollection.permissions),
|
|
3790
|
+
viewQueryUpdate: compareViewQuery(currentCollection, previousCollection)
|
|
3791
|
+
};
|
|
3792
|
+
}
|
|
3585
3793
|
const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
|
|
3586
3794
|
currentCollection,
|
|
3587
3795
|
previousCollection,
|
|
@@ -3613,6 +3821,9 @@ function detectDestructiveChanges(diff, config) {
|
|
|
3613
3821
|
const destructiveChanges = [];
|
|
3614
3822
|
const mergedConfig = mergeConfig3(config);
|
|
3615
3823
|
for (const collection of diff.collectionsToDelete) {
|
|
3824
|
+
if (collection.type === "view") {
|
|
3825
|
+
continue;
|
|
3826
|
+
}
|
|
3616
3827
|
destructiveChanges.push({
|
|
3617
3828
|
type: "collection_delete",
|
|
3618
3829
|
severity: "high",
|
|
@@ -3700,7 +3911,11 @@ function categorizeChangesBySeverity(diff, _config) {
|
|
|
3700
3911
|
const destructive = [];
|
|
3701
3912
|
const nonDestructive = [];
|
|
3702
3913
|
for (const collection of diff.collectionsToDelete) {
|
|
3703
|
-
|
|
3914
|
+
if (collection.type === "view") {
|
|
3915
|
+
nonDestructive.push(`Delete view collection: ${collection.name}`);
|
|
3916
|
+
} else {
|
|
3917
|
+
destructive.push(`Delete collection: ${collection.name}`);
|
|
3918
|
+
}
|
|
3704
3919
|
}
|
|
3705
3920
|
for (const collection of diff.collectionsToCreate) {
|
|
3706
3921
|
nonDestructive.push(`Create collection: ${collection.name}`);
|
|
@@ -3735,6 +3950,9 @@ function categorizeChangesBySeverity(diff, _config) {
|
|
|
3735
3950
|
for (const rule of modification.rulesToUpdate) {
|
|
3736
3951
|
nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
|
|
3737
3952
|
}
|
|
3953
|
+
if (modification.viewQueryUpdate) {
|
|
3954
|
+
nonDestructive.push(`Update view query: ${collectionName}`);
|
|
3955
|
+
}
|
|
3738
3956
|
}
|
|
3739
3957
|
return { destructive, nonDestructive };
|
|
3740
3958
|
}
|
|
@@ -3747,6 +3965,7 @@ function generateChangeSummary(diff, config) {
|
|
|
3747
3965
|
let indexChanges = 0;
|
|
3748
3966
|
let ruleChanges = 0;
|
|
3749
3967
|
let permissionChanges = 0;
|
|
3968
|
+
let viewQueryChanges = 0;
|
|
3750
3969
|
for (const modification of diff.collectionsToModify) {
|
|
3751
3970
|
fieldsToAdd += modification.fieldsToAdd.length;
|
|
3752
3971
|
fieldsToRemove += modification.fieldsToRemove.length;
|
|
@@ -3754,6 +3973,9 @@ function generateChangeSummary(diff, config) {
|
|
|
3754
3973
|
indexChanges += modification.indexesToAdd.length + modification.indexesToRemove.length;
|
|
3755
3974
|
ruleChanges += modification.rulesToUpdate.length;
|
|
3756
3975
|
permissionChanges += modification.permissionsToUpdate.length;
|
|
3976
|
+
if (modification.viewQueryUpdate) {
|
|
3977
|
+
viewQueryChanges += 1;
|
|
3978
|
+
}
|
|
3757
3979
|
}
|
|
3758
3980
|
return {
|
|
3759
3981
|
totalChanges: diff.collectionsToCreate.length + diff.collectionsToDelete.length + diff.collectionsToModify.length,
|
|
@@ -3766,6 +3988,7 @@ function generateChangeSummary(diff, config) {
|
|
|
3766
3988
|
indexChanges,
|
|
3767
3989
|
ruleChanges,
|
|
3768
3990
|
permissionChanges,
|
|
3991
|
+
viewQueryChanges,
|
|
3769
3992
|
destructiveChanges,
|
|
3770
3993
|
nonDestructiveChanges: nonDestructive
|
|
3771
3994
|
};
|
|
@@ -3795,12 +4018,11 @@ function filterDiff(diff, options) {
|
|
|
3795
4018
|
});
|
|
3796
4019
|
let collectionsToDelete = diff.collectionsToDelete;
|
|
3797
4020
|
if (skipDestructive) {
|
|
3798
|
-
collectionsToDelete =
|
|
3799
|
-
} else {
|
|
3800
|
-
collectionsToDelete = collectionsToDelete.filter((col) => {
|
|
3801
|
-
return matchesPattern(col.name, patterns);
|
|
3802
|
-
});
|
|
4021
|
+
collectionsToDelete = collectionsToDelete.filter((col) => col.type === "view");
|
|
3803
4022
|
}
|
|
4023
|
+
collectionsToDelete = collectionsToDelete.filter((col) => {
|
|
4024
|
+
return matchesPattern(col.name, patterns);
|
|
4025
|
+
});
|
|
3804
4026
|
const collectionsToModify = diff.collectionsToModify.map((mod) => {
|
|
3805
4027
|
const collectionMatches = matchesPattern(mod.collection, patterns);
|
|
3806
4028
|
const fieldsToAdd = mod.fieldsToAdd.filter((field) => {
|
|
@@ -3825,6 +4047,7 @@ function filterDiff(diff, options) {
|
|
|
3825
4047
|
const indexesToRemove = collectionMatches ? mod.indexesToRemove : [];
|
|
3826
4048
|
const rulesToUpdate = collectionMatches ? mod.rulesToUpdate : [];
|
|
3827
4049
|
const permissionsToUpdate = collectionMatches ? mod.permissionsToUpdate : [];
|
|
4050
|
+
const viewQueryUpdate = collectionMatches ? mod.viewQueryUpdate : void 0;
|
|
3828
4051
|
return {
|
|
3829
4052
|
...mod,
|
|
3830
4053
|
fieldsToAdd,
|
|
@@ -3833,10 +4056,11 @@ function filterDiff(diff, options) {
|
|
|
3833
4056
|
indexesToAdd,
|
|
3834
4057
|
indexesToRemove,
|
|
3835
4058
|
rulesToUpdate,
|
|
3836
|
-
permissionsToUpdate
|
|
4059
|
+
permissionsToUpdate,
|
|
4060
|
+
viewQueryUpdate
|
|
3837
4061
|
};
|
|
3838
4062
|
}).filter((mod) => {
|
|
3839
|
-
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;
|
|
4063
|
+
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;
|
|
3840
4064
|
});
|
|
3841
4065
|
return {
|
|
3842
4066
|
...diff,
|
|
@@ -3848,7 +4072,7 @@ function filterDiff(diff, options) {
|
|
|
3848
4072
|
|
|
3849
4073
|
// src/migration/diff/index.ts
|
|
3850
4074
|
function hasChanges(modification) {
|
|
3851
|
-
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;
|
|
4075
|
+
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;
|
|
3852
4076
|
}
|
|
3853
4077
|
function aggregateChanges(currentSchema, previousSnapshot, config) {
|
|
3854
4078
|
const collectionIdToName = /* @__PURE__ */ new Map();
|
|
@@ -4063,6 +4287,13 @@ function formatValue(value) {
|
|
|
4063
4287
|
}
|
|
4064
4288
|
return String(value);
|
|
4065
4289
|
}
|
|
4290
|
+
function formatSqlTemplate(query, indent = " ") {
|
|
4291
|
+
const escaped = query.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
4292
|
+
const body = escaped.split("\n").map((line) => line.trim() === "" ? "" : `${indent}${line}`).join("\n");
|
|
4293
|
+
return `\`
|
|
4294
|
+
${body}
|
|
4295
|
+
${indent.slice(0, -2)}\``;
|
|
4296
|
+
}
|
|
4066
4297
|
function getFieldConstructorName(fieldType) {
|
|
4067
4298
|
const constructorMap = {
|
|
4068
4299
|
text: "TextField",
|
|
@@ -4423,6 +4654,15 @@ function generateCollectionRules(rules, collectionType = "base") {
|
|
|
4423
4654
|
if (!rules) {
|
|
4424
4655
|
return "";
|
|
4425
4656
|
}
|
|
4657
|
+
if (collectionType === "view") {
|
|
4658
|
+
return [
|
|
4659
|
+
`"listRule": ${formatValue(rules.listRule ?? null)}`,
|
|
4660
|
+
`"viewRule": ${formatValue(rules.viewRule ?? null)}`,
|
|
4661
|
+
`"createRule": null`,
|
|
4662
|
+
`"updateRule": null`,
|
|
4663
|
+
`"deleteRule": null`
|
|
4664
|
+
].join(",\n ");
|
|
4665
|
+
}
|
|
4426
4666
|
const parts = [];
|
|
4427
4667
|
if (rules.listRule !== void 0) {
|
|
4428
4668
|
parts.push(`"listRule": ${formatValue(rules.listRule)}`);
|
|
@@ -4448,6 +4688,15 @@ function generateCollectionPermissions(permissions, collectionType = "base") {
|
|
|
4448
4688
|
if (!permissions) {
|
|
4449
4689
|
return "";
|
|
4450
4690
|
}
|
|
4691
|
+
if (collectionType === "view") {
|
|
4692
|
+
return [
|
|
4693
|
+
`"listRule": ${formatValue(permissions.listRule ?? null)}`,
|
|
4694
|
+
`"viewRule": ${formatValue(permissions.viewRule ?? null)}`,
|
|
4695
|
+
`"createRule": null`,
|
|
4696
|
+
`"updateRule": null`,
|
|
4697
|
+
`"deleteRule": null`
|
|
4698
|
+
].join(",\n ");
|
|
4699
|
+
}
|
|
4451
4700
|
const parts = [];
|
|
4452
4701
|
if (permissions.listRule !== void 0) {
|
|
4453
4702
|
parts.push(`"listRule": ${formatValue(permissions.listRule)}`);
|
|
@@ -4485,6 +4734,16 @@ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, i
|
|
|
4485
4734
|
lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
|
|
4486
4735
|
return lines.join("\n");
|
|
4487
4736
|
}
|
|
4737
|
+
function generateViewQueryUpdate(collectionName, viewQuery, varName, isLast = false, collectionIdMap) {
|
|
4738
|
+
const lines = [];
|
|
4739
|
+
const collectionVar = varName || `collection_${collectionName}_viewQuery`;
|
|
4740
|
+
lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
|
|
4741
|
+
lines.push(` unmarshal({`);
|
|
4742
|
+
lines.push(` "viewQuery": ${formatSqlTemplate(viewQuery, " ")},`);
|
|
4743
|
+
lines.push(` }, ${collectionVar})`);
|
|
4744
|
+
lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
|
|
4745
|
+
return lines.join("\n");
|
|
4746
|
+
}
|
|
4488
4747
|
function generateGroupedRuleUpdates(collectionName, entries, varSuffix, isLast = false, collectionIdMap) {
|
|
4489
4748
|
const collectionVar = `collection_${collectionName}_${varSuffix}`;
|
|
4490
4749
|
const lines = [];
|
|
@@ -4515,6 +4774,13 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
|
|
|
4515
4774
|
} else if (rulesCode) {
|
|
4516
4775
|
lines.push(` ${rulesCode},`);
|
|
4517
4776
|
}
|
|
4777
|
+
if (collection.type === "view") {
|
|
4778
|
+
lines.push(` "viewQuery": ${formatSqlTemplate(collection.viewQuery ?? "")},`);
|
|
4779
|
+
lines.push(` });`);
|
|
4780
|
+
lines.push(``);
|
|
4781
|
+
lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
|
|
4782
|
+
return lines.join("\n");
|
|
4783
|
+
}
|
|
4518
4784
|
const systemFieldNames = ["created", "updated", "id"];
|
|
4519
4785
|
if (collection.type === "auth") {
|
|
4520
4786
|
systemFieldNames.push(...getAuthSystemFields().map((f) => f.name));
|
|
@@ -4555,7 +4821,21 @@ function generateOperationUpMigration(operation, collectionIdMap) {
|
|
|
4555
4821
|
const modification = operation.modifications;
|
|
4556
4822
|
const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
|
|
4557
4823
|
let operationCount = 0;
|
|
4558
|
-
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);
|
|
4824
|
+
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);
|
|
4825
|
+
if (modification.viewQueryUpdate) {
|
|
4826
|
+
operationCount++;
|
|
4827
|
+
const isLast = operationCount === totalOperations;
|
|
4828
|
+
lines.push(
|
|
4829
|
+
generateViewQueryUpdate(
|
|
4830
|
+
collectionName,
|
|
4831
|
+
modification.viewQueryUpdate.newValue,
|
|
4832
|
+
void 0,
|
|
4833
|
+
isLast,
|
|
4834
|
+
collectionIdMap
|
|
4835
|
+
)
|
|
4836
|
+
);
|
|
4837
|
+
if (!isLast) lines.push("");
|
|
4838
|
+
}
|
|
4559
4839
|
for (let i = 0; i < modification.fieldsToAdd.length; i++) {
|
|
4560
4840
|
const field = modification.fieldsToAdd[i];
|
|
4561
4841
|
operationCount++;
|
|
@@ -4656,7 +4936,21 @@ function generateOperationDownMigration(operation, collectionIdMap) {
|
|
|
4656
4936
|
const modification = operation.modifications;
|
|
4657
4937
|
const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
|
|
4658
4938
|
let operationCount = 0;
|
|
4659
|
-
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);
|
|
4939
|
+
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);
|
|
4940
|
+
if (modification.viewQueryUpdate) {
|
|
4941
|
+
operationCount++;
|
|
4942
|
+
const isLast = operationCount === totalOperations;
|
|
4943
|
+
lines.push(
|
|
4944
|
+
generateViewQueryUpdate(
|
|
4945
|
+
collectionName,
|
|
4946
|
+
modification.viewQueryUpdate.oldValue ?? "",
|
|
4947
|
+
`collection_${collectionName}_revert_viewQuery`,
|
|
4948
|
+
isLast,
|
|
4949
|
+
collectionIdMap
|
|
4950
|
+
)
|
|
4951
|
+
);
|
|
4952
|
+
if (!isLast) lines.push("");
|
|
4953
|
+
}
|
|
4660
4954
|
if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
|
|
4661
4955
|
operationCount++;
|
|
4662
4956
|
const isLast = operationCount === totalOperations;
|
|
@@ -4787,6 +5081,19 @@ function generateUpMigration(diff) {
|
|
|
4787
5081
|
lines.push(` // Modify existing collections`);
|
|
4788
5082
|
for (const modification of diff.collectionsToModify) {
|
|
4789
5083
|
const collectionName = modification.collection;
|
|
5084
|
+
if (modification.viewQueryUpdate) {
|
|
5085
|
+
lines.push(` // Update the view query of ${collectionName}`);
|
|
5086
|
+
lines.push(
|
|
5087
|
+
generateViewQueryUpdate(
|
|
5088
|
+
collectionName,
|
|
5089
|
+
modification.viewQueryUpdate.newValue,
|
|
5090
|
+
void 0,
|
|
5091
|
+
false,
|
|
5092
|
+
collectionIdMap
|
|
5093
|
+
)
|
|
5094
|
+
);
|
|
5095
|
+
lines.push(``);
|
|
5096
|
+
}
|
|
4790
5097
|
if (modification.fieldsToAdd.length > 0) {
|
|
4791
5098
|
lines.push(` // Add fields to ${collectionName}`);
|
|
4792
5099
|
for (let i = 0; i < modification.fieldsToAdd.length; i++) {
|
|
@@ -4923,6 +5230,19 @@ function generateDownMigration(diff) {
|
|
|
4923
5230
|
lines.push(` // Revert modifications`);
|
|
4924
5231
|
for (const modification of diff.collectionsToModify) {
|
|
4925
5232
|
const collectionName = modification.collection;
|
|
5233
|
+
if (modification.viewQueryUpdate) {
|
|
5234
|
+
lines.push(` // Restore the view query of ${collectionName}`);
|
|
5235
|
+
lines.push(
|
|
5236
|
+
generateViewQueryUpdate(
|
|
5237
|
+
collectionName,
|
|
5238
|
+
modification.viewQueryUpdate.oldValue ?? "",
|
|
5239
|
+
`collection_${collectionName}_revert_viewQuery`,
|
|
5240
|
+
false,
|
|
5241
|
+
collectionIdMap
|
|
5242
|
+
)
|
|
5243
|
+
);
|
|
5244
|
+
lines.push(``);
|
|
5245
|
+
}
|
|
4926
5246
|
if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
|
|
4927
5247
|
lines.push(` // Revert permissions for ${collectionName}`);
|
|
4928
5248
|
if (modification.permissionsToUpdate.length >= 2) {
|
|
@@ -5274,6 +5594,9 @@ var MigrationGenerator = class {
|
|
|
5274
5594
|
function detectCollectionDeletions(diff) {
|
|
5275
5595
|
const changes = [];
|
|
5276
5596
|
for (const collection of diff.collectionsToDelete) {
|
|
5597
|
+
if (collection.type === "view") {
|
|
5598
|
+
continue;
|
|
5599
|
+
}
|
|
5277
5600
|
changes.push({
|
|
5278
5601
|
type: "collection_deletion" /* COLLECTION_DELETION */,
|
|
5279
5602
|
description: `Delete collection: ${collection.name}`,
|