pocketbase-zod-schema 0.7.0 → 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.
Files changed (70) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli/index.cjs +449 -50
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.d.cts +1 -1
  5. package/dist/cli/index.d.ts +1 -1
  6. package/dist/cli/index.js +449 -50
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/migrate.cjs +455 -53
  9. package/dist/cli/migrate.cjs.map +1 -1
  10. package/dist/cli/migrate.js +455 -53
  11. package/dist/cli/migrate.js.map +1 -1
  12. package/dist/cli/utils/index.cjs +8 -1
  13. package/dist/cli/utils/index.cjs.map +1 -1
  14. package/dist/cli/utils/index.d.cts +1 -1
  15. package/dist/cli/utils/index.d.ts +1 -1
  16. package/dist/cli/utils/index.js +8 -1
  17. package/dist/cli/utils/index.js.map +1 -1
  18. package/dist/index.cjs +90 -1
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +87 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/migration/analyzer.cjs +87 -14
  25. package/dist/migration/analyzer.cjs.map +1 -1
  26. package/dist/migration/analyzer.d.cts +12 -4
  27. package/dist/migration/analyzer.d.ts +12 -4
  28. package/dist/migration/analyzer.js +87 -15
  29. package/dist/migration/analyzer.js.map +1 -1
  30. package/dist/migration/diff.cjs +64 -9
  31. package/dist/migration/diff.cjs.map +1 -1
  32. package/dist/migration/diff.d.cts +12 -2
  33. package/dist/migration/diff.d.ts +12 -2
  34. package/dist/migration/diff.js +64 -10
  35. package/dist/migration/diff.js.map +1 -1
  36. package/dist/migration/generator.cjs +206 -39
  37. package/dist/migration/generator.cjs.map +1 -1
  38. package/dist/migration/generator.d.cts +54 -2
  39. package/dist/migration/generator.d.ts +54 -2
  40. package/dist/migration/generator.js +203 -40
  41. package/dist/migration/generator.js.map +1 -1
  42. package/dist/migration/index.cjs +501 -63
  43. package/dist/migration/index.cjs.map +1 -1
  44. package/dist/migration/index.d.cts +2 -2
  45. package/dist/migration/index.d.ts +2 -2
  46. package/dist/migration/index.js +501 -63
  47. package/dist/migration/index.js.map +1 -1
  48. package/dist/migration/snapshot.cjs +147 -1
  49. package/dist/migration/snapshot.cjs.map +1 -1
  50. package/dist/migration/snapshot.d.cts +3 -1
  51. package/dist/migration/snapshot.d.ts +3 -1
  52. package/dist/migration/snapshot.js +147 -1
  53. package/dist/migration/snapshot.js.map +1 -1
  54. package/dist/migration/utils/index.d.cts +1 -1
  55. package/dist/migration/utils/index.d.ts +1 -1
  56. package/dist/schema.cjs +90 -1
  57. package/dist/schema.cjs.map +1 -1
  58. package/dist/schema.d.cts +128 -2
  59. package/dist/schema.d.ts +128 -2
  60. package/dist/schema.js +87 -2
  61. package/dist/schema.js.map +1 -1
  62. package/dist/server.cjs +545 -65
  63. package/dist/server.cjs.map +1 -1
  64. package/dist/server.d.cts +2 -2
  65. package/dist/server.d.ts +2 -2
  66. package/dist/server.js +542 -66
  67. package/dist/server.js.map +1 -1
  68. package/dist/{types-CWHV6ATd.d.cts → types-CnzfX6JH.d.cts} +20 -2
  69. package/dist/{types-C2nGWHLV.d.ts → types-Do3jyFBm.d.ts} +20 -2
  70. package/package.json +1 -1
@@ -1460,6 +1460,60 @@ function getFieldTypeInfo(zodType, fieldName) {
1460
1460
  options
1461
1461
  };
1462
1462
  }
1463
+ function dedentSql(value) {
1464
+ const lines = value.replace(/\r\n/g, "\n").split("\n");
1465
+ while (lines.length > 0 && lines[0].trim() === "") {
1466
+ lines.shift();
1467
+ }
1468
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
1469
+ lines.pop();
1470
+ }
1471
+ if (lines.length === 0) {
1472
+ return "";
1473
+ }
1474
+ let minIndent = Infinity;
1475
+ for (const line of lines) {
1476
+ if (line.trim() === "") continue;
1477
+ const indent = line.length - line.trimStart().length;
1478
+ if (indent < minIndent) {
1479
+ minIndent = indent;
1480
+ }
1481
+ }
1482
+ if (!Number.isFinite(minIndent) || minIndent === 0) {
1483
+ return lines.map((line) => line.trimEnd()).join("\n");
1484
+ }
1485
+ return lines.map((line) => line.slice(minIndent).trimEnd()).join("\n");
1486
+ }
1487
+ function stripLeadingComments(query) {
1488
+ let remaining = query.trim();
1489
+ while (remaining.length > 0) {
1490
+ if (remaining.startsWith("--")) {
1491
+ const newline = remaining.indexOf("\n");
1492
+ remaining = newline === -1 ? "" : remaining.slice(newline + 1).trim();
1493
+ continue;
1494
+ }
1495
+ if (remaining.startsWith("/*")) {
1496
+ const end = remaining.indexOf("*/");
1497
+ remaining = end === -1 ? "" : remaining.slice(end + 2).trim();
1498
+ continue;
1499
+ }
1500
+ break;
1501
+ }
1502
+ return remaining;
1503
+ }
1504
+ function validateViewQuery(collectionName, viewQuery) {
1505
+ if (typeof viewQuery !== "string" || viewQuery.trim() === "") {
1506
+ throw new Error(
1507
+ `View collection "${collectionName}" requires a non-empty viewQuery. Provide the SQL SELECT statement backing the view.`
1508
+ );
1509
+ }
1510
+ const body = stripLeadingComments(viewQuery);
1511
+ if (!/^(select|with)\b/i.test(body)) {
1512
+ throw new Error(
1513
+ `View collection "${collectionName}" has an invalid viewQuery: it must start with SELECT or WITH. Received: ${body.slice(0, 40)}${body.length > 40 ? "..." : ""}`
1514
+ );
1515
+ }
1516
+ }
1463
1517
  function getCollectionNameFromFile(filePath) {
1464
1518
  const filename = path5.basename(filePath).replace(/\.(ts|js)$/, "");
1465
1519
  return toCollectionName(filename);
@@ -1483,13 +1537,26 @@ function extractCollectionTypeFromSchema(zodSchema) {
1483
1537
  }
1484
1538
  try {
1485
1539
  const metadata = JSON.parse(zodSchema.description);
1486
- if (metadata.type === "base" || metadata.type === "auth") {
1540
+ if (metadata.type === "base" || metadata.type === "auth" || metadata.type === "view") {
1487
1541
  return metadata.type;
1488
1542
  }
1489
1543
  } catch {
1490
1544
  }
1491
1545
  return null;
1492
1546
  }
1547
+ function extractViewQueryFromSchema(zodSchema) {
1548
+ if (!zodSchema.description) {
1549
+ return null;
1550
+ }
1551
+ try {
1552
+ const metadata = JSON.parse(zodSchema.description);
1553
+ if (typeof metadata.viewQuery === "string") {
1554
+ return metadata.viewQuery;
1555
+ }
1556
+ } catch {
1557
+ }
1558
+ return null;
1559
+ }
1493
1560
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema", "Collection"]) {
1494
1561
  const result = {};
1495
1562
  if (module.default instanceof z.ZodObject) {
@@ -1648,6 +1715,15 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
1648
1715
  const rawFields = extractFieldDefinitions(zodSchema);
1649
1716
  const explicitType = extractCollectionTypeFromSchema(zodSchema);
1650
1717
  const collectionType = explicitType ?? (isAuthCollection(rawFields) ? "auth" : "base");
1718
+ const isView = collectionType === "view";
1719
+ const viewQuery = extractViewQueryFromSchema(zodSchema);
1720
+ if (isView) {
1721
+ validateViewQuery(collectionName, viewQuery);
1722
+ } else if (viewQuery !== null) {
1723
+ console.warn(
1724
+ `[${collectionName}] viewQuery is only used by view collections and will be ignored. Use defineView() or set type: "view" to define a view collection.`
1725
+ );
1726
+ }
1651
1727
  const fields = rawFields.filter((f) => !["created", "updated"].includes(f.name)).map(({ name, zodType }) => buildFieldDefinition(name, zodType));
1652
1728
  if (collectionType === "auth") {
1653
1729
  const authSystemFields = [
@@ -1707,26 +1783,43 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
1707
1783
  }
1708
1784
  }
1709
1785
  const indexes = extractIndexes(zodSchema) || [];
1786
+ if (isView && indexes.length > 0) {
1787
+ throw new Error(
1788
+ `View collection "${collectionName}" cannot declare indexes. PocketBase view collections are backed by a SQL query and do not support indexes.`
1789
+ );
1790
+ }
1710
1791
  const permissionAnalyzer = new PermissionAnalyzer();
1711
1792
  let permissions = void 0;
1712
1793
  const schemaDescription = zodSchema.description;
1713
1794
  const extractedPermissions = permissionAnalyzer.extractPermissions(schemaDescription);
1714
1795
  if (extractedPermissions) {
1715
1796
  const resolvedPermissions = permissionAnalyzer.resolvePermissions(extractedPermissions);
1716
- const validationResults = permissionAnalyzer.validatePermissions(
1717
- collectionName,
1718
- resolvedPermissions,
1719
- fields,
1720
- collectionType === "auth"
1721
- );
1722
- for (const [ruleType, result] of validationResults) {
1723
- if (!result.valid) {
1724
- console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
1725
- result.errors.forEach((error) => console.error(` - ${error}`));
1797
+ if (isView) {
1798
+ for (const ruleType of ["createRule", "updateRule", "deleteRule", "manageRule"]) {
1799
+ if (resolvedPermissions[ruleType]) {
1800
+ console.warn(
1801
+ `[${collectionName}] ${ruleType} is not supported on view collections and will be set to null.`
1802
+ );
1803
+ }
1804
+ resolvedPermissions[ruleType] = null;
1726
1805
  }
1727
- if (result.warnings.length > 0) {
1728
- console.warn(`[${collectionName}] Permission warnings for ${ruleType}:`);
1729
- result.warnings.forEach((warning) => console.warn(` - ${warning}`));
1806
+ }
1807
+ if (!isView) {
1808
+ const validationResults = permissionAnalyzer.validatePermissions(
1809
+ collectionName,
1810
+ resolvedPermissions,
1811
+ fields,
1812
+ collectionType === "auth"
1813
+ );
1814
+ for (const [ruleType, result] of validationResults) {
1815
+ if (!result.valid) {
1816
+ console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
1817
+ result.errors.forEach((error) => console.error(` - ${error}`));
1818
+ }
1819
+ if (result.warnings.length > 0) {
1820
+ console.warn(`[${collectionName}] Permission warnings for ${ruleType}:`);
1821
+ result.warnings.forEach((warning) => console.warn(` - ${warning}`));
1822
+ }
1730
1823
  }
1731
1824
  }
1732
1825
  permissions = permissionAnalyzer.mergeWithDefaults(resolvedPermissions);
@@ -1747,6 +1840,9 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
1747
1840
  },
1748
1841
  permissions
1749
1842
  };
1843
+ if (isView) {
1844
+ collectionSchema.viewQuery = viewQuery;
1845
+ }
1750
1846
  return collectionSchema;
1751
1847
  }
1752
1848
  var tsxLoaderRegistered = false;
@@ -2028,8 +2124,9 @@ function convertPocketBaseField(pbField) {
2028
2124
  }
2029
2125
  function convertPocketBaseCollection(pbCollection) {
2030
2126
  const fields = [];
2127
+ const isView = pbCollection.type === "view";
2031
2128
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
2032
- if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
2129
+ if (!isView && pbCollection.fields && Array.isArray(pbCollection.fields)) {
2033
2130
  for (const pbField of pbCollection.fields) {
2034
2131
  if (pbField.system || systemFieldNames.includes(pbField.name)) {
2035
2132
  const isAuthSystemField = pbCollection.type === "auth" && ["email", "emailVisibility", "verified", "password", "tokenKey"].includes(pbField.name);
@@ -2049,6 +2146,9 @@ function convertPocketBaseCollection(pbCollection) {
2049
2146
  if (pbCollection.id) {
2050
2147
  schema.id = pbCollection.id;
2051
2148
  }
2149
+ if (typeof pbCollection.viewQuery === "string") {
2150
+ schema.viewQuery = dedentSql(pbCollection.viewQuery);
2151
+ }
2052
2152
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2053
2153
  schema.indexes = pbCollection.indexes;
2054
2154
  }
@@ -2150,6 +2250,24 @@ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
2150
2250
  return [];
2151
2251
  }
2152
2252
  }
2253
+ function skipTemplateLiteral(content, start) {
2254
+ let i = start + 1;
2255
+ while (i < content.length) {
2256
+ const char = content[i];
2257
+ if (char === "\\") {
2258
+ i += 2;
2259
+ continue;
2260
+ }
2261
+ if (char === "`") {
2262
+ return i + 1;
2263
+ }
2264
+ i++;
2265
+ }
2266
+ return i;
2267
+ }
2268
+ function unescapeTemplateLiteral(raw) {
2269
+ return raw.replace(/\\(`|\$\{|\\)/g, "$1");
2270
+ }
2153
2271
  function parseMigrationOperationsFromContent(content) {
2154
2272
  const collectionsToCreate = [];
2155
2273
  const collectionsToDelete = [];
@@ -2238,6 +2356,10 @@ function parseMigrationOperationsFromContent(content) {
2238
2356
  while (i < content.length && bCount > 0) {
2239
2357
  const char = content[i];
2240
2358
  const prev = i > 0 ? content[i - 1] : "";
2359
+ if (!inStr && char === "`") {
2360
+ i = skipTemplateLiteral(content, i);
2361
+ continue;
2362
+ }
2241
2363
  if (!inStr && (char === '"' || char === "'")) {
2242
2364
  inStr = true;
2243
2365
  strChar = char;
@@ -2427,6 +2549,95 @@ function parseMigrationOperationsFromContent(content) {
2427
2549
  }
2428
2550
  }
2429
2551
  }
2552
+ const viewQueryRegex = /(\w+)\.viewQuery\s*=\s*/g;
2553
+ let viewQueryMatch;
2554
+ while ((viewQueryMatch = viewQueryRegex.exec(content)) !== null) {
2555
+ const varInfo = variables.get(viewQueryMatch[1]);
2556
+ if (!varInfo || varInfo.type !== "collection") continue;
2557
+ const valueStart = viewQueryMatch.index + viewQueryMatch[0].length;
2558
+ if (content[valueStart] === "`") {
2559
+ const end = skipTemplateLiteral(content, valueStart);
2560
+ const raw = content.substring(valueStart + 1, end - 1);
2561
+ getUpdate(varInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
2562
+ viewQueryRegex.lastIndex = end;
2563
+ } else {
2564
+ const terminator = content.indexOf(";", valueStart);
2565
+ const valueStr = content.substring(valueStart, terminator === -1 ? content.length : terminator);
2566
+ try {
2567
+ getUpdate(varInfo.name).viewQuery = dedentSql(new Function("app", `return ${valueStr}`)(mockApp));
2568
+ } catch {
2569
+ getUpdate(varInfo.name).viewQuery = dedentSql(valueStr.trim());
2570
+ }
2571
+ }
2572
+ }
2573
+ const unmarshalRegex = /unmarshal\s*\(/g;
2574
+ let unmarshalMatch;
2575
+ while ((unmarshalMatch = unmarshalRegex.exec(content)) !== null) {
2576
+ let i = unmarshalMatch.index + unmarshalMatch[0].length;
2577
+ while (i < content.length && /\s/.test(content[i])) i++;
2578
+ if (content[i] !== "{") continue;
2579
+ const objStart = i;
2580
+ let braceCount = 1;
2581
+ let inStr = false;
2582
+ let strChar = null;
2583
+ i++;
2584
+ while (i < content.length && braceCount > 0) {
2585
+ const ch = content[i];
2586
+ const prev = i > 0 ? content[i - 1] : "";
2587
+ if (!inStr && ch === "`") {
2588
+ i = skipTemplateLiteral(content, i);
2589
+ continue;
2590
+ }
2591
+ if (!inStr && (ch === '"' || ch === "'")) {
2592
+ inStr = true;
2593
+ strChar = ch;
2594
+ } else if (inStr && ch === strChar && prev !== "\\") {
2595
+ inStr = false;
2596
+ strChar = null;
2597
+ }
2598
+ if (!inStr) {
2599
+ if (ch === "{") braceCount++;
2600
+ if (ch === "}") braceCount--;
2601
+ }
2602
+ i++;
2603
+ }
2604
+ if (braceCount !== 0) continue;
2605
+ const objStr = content.substring(objStart, i);
2606
+ while (i < content.length && /[\s,]/.test(content[i])) i++;
2607
+ const varStart = i;
2608
+ while (i < content.length && /\w/.test(content[i])) i++;
2609
+ const colVarName = content.substring(varStart, i);
2610
+ const colInfo = variables.get(colVarName);
2611
+ if (!colInfo || colInfo.type !== "collection") continue;
2612
+ const keyValRegex = /"(\w+Rule)"\s*:\s*([^,}\n]+)/g;
2613
+ let kvMatch;
2614
+ while ((kvMatch = keyValRegex.exec(objStr)) !== null) {
2615
+ const ruleKey = kvMatch[1];
2616
+ const valStr = kvMatch[2].trim();
2617
+ try {
2618
+ const value = new Function("app", `return ${valStr}`)(mockApp);
2619
+ getUpdate(colInfo.name).rulesToUpdate[ruleKey] = value;
2620
+ } catch {
2621
+ getUpdate(colInfo.name).rulesToUpdate[ruleKey] = valStr;
2622
+ }
2623
+ }
2624
+ const viewQueryKey = objStr.match(/"viewQuery"\s*:\s*/);
2625
+ if (viewQueryKey) {
2626
+ const valueStart = viewQueryKey.index + viewQueryKey[0].length;
2627
+ if (objStr[valueStart] === "`") {
2628
+ const end = skipTemplateLiteral(objStr, valueStart);
2629
+ const raw = objStr.substring(valueStart + 1, end - 1);
2630
+ getUpdate(colInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
2631
+ } else {
2632
+ const valStr = objStr.substring(valueStart).replace(/[,}\s]+$/, "");
2633
+ try {
2634
+ getUpdate(colInfo.name).viewQuery = dedentSql(new Function("app", `return ${valStr}`)(mockApp));
2635
+ } catch {
2636
+ getUpdate(colInfo.name).viewQuery = dedentSql(valStr);
2637
+ }
2638
+ }
2639
+ }
2640
+ }
2430
2641
  const idxPushRegex = /(\w+)\.indexes\.push\s*\(/g;
2431
2642
  let match;
2432
2643
  while ((match = idxPushRegex.exec(content)) !== null) {
@@ -2556,6 +2767,10 @@ function parseMigrationOperations(migrationContent) {
2556
2767
  while (i < migrationContent.length && braceCount > 0) {
2557
2768
  const char = migrationContent[i];
2558
2769
  const prevChar = i > 0 ? migrationContent[i - 1] : "";
2770
+ if (!inString && char === "`") {
2771
+ i = skipTemplateLiteral(migrationContent, i);
2772
+ continue;
2773
+ }
2559
2774
  if (!inString && (char === '"' || char === "'")) {
2560
2775
  inString = true;
2561
2776
  stringChar = char;
@@ -2891,6 +3106,9 @@ function applyMigrationOperations(snapshot, operations) {
2891
3106
  collection.indexes = collection.indexes.filter((idx) => !update.indexesToRemove.includes(idx));
2892
3107
  }
2893
3108
  }
3109
+ if (update.viewQuery !== void 0) {
3110
+ collection.viewQuery = update.viewQuery;
3111
+ }
2894
3112
  if (Object.keys(update.rulesToUpdate).length > 0) {
2895
3113
  if (!collection.rules) collection.rules = {};
2896
3114
  if (!collection.permissions) collection.permissions = {};
@@ -3510,7 +3728,45 @@ function compareCollectionFields(currentCollection, previousCollection, config,
3510
3728
  fieldsToRemove = fieldsToRemove.filter((_, index) => !processedRemoveIndices.has(index));
3511
3729
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
3512
3730
  }
3731
+ function normalizeSql(query) {
3732
+ if (!query) {
3733
+ return "";
3734
+ }
3735
+ return query.split("\n").map((line) => line.trim()).join(" ").replace(/\s+/g, " ").trim();
3736
+ }
3737
+ function compareViewQuery(currentCollection, previousCollection) {
3738
+ const newValue = currentCollection.viewQuery;
3739
+ if (typeof newValue !== "string") {
3740
+ return void 0;
3741
+ }
3742
+ if (normalizeSql(newValue) === normalizeSql(previousCollection.viewQuery)) {
3743
+ return void 0;
3744
+ }
3745
+ return {
3746
+ oldValue: previousCollection.viewQuery ?? null,
3747
+ newValue
3748
+ };
3749
+ }
3513
3750
  function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
3751
+ const isView = currentCollection.type === "view" || previousCollection.type === "view";
3752
+ if (isView) {
3753
+ return {
3754
+ collection: currentCollection.name,
3755
+ fieldsToAdd: [],
3756
+ fieldsToRemove: [],
3757
+ fieldsToModify: [],
3758
+ indexesToAdd: [],
3759
+ indexesToRemove: [],
3760
+ rulesToUpdate: compareRules(
3761
+ currentCollection.rules,
3762
+ previousCollection.rules,
3763
+ currentCollection.permissions,
3764
+ previousCollection.permissions
3765
+ ),
3766
+ permissionsToUpdate: comparePermissions(currentCollection.permissions, previousCollection.permissions),
3767
+ viewQueryUpdate: compareViewQuery(currentCollection, previousCollection)
3768
+ };
3769
+ }
3514
3770
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
3515
3771
  currentCollection,
3516
3772
  previousCollection,
@@ -3542,6 +3798,9 @@ function detectDestructiveChanges(diff, config) {
3542
3798
  const destructiveChanges = [];
3543
3799
  const mergedConfig = mergeConfig3(config);
3544
3800
  for (const collection of diff.collectionsToDelete) {
3801
+ if (collection.type === "view") {
3802
+ continue;
3803
+ }
3545
3804
  destructiveChanges.push({
3546
3805
  type: "collection_delete",
3547
3806
  severity: "high",
@@ -3629,7 +3888,11 @@ function categorizeChangesBySeverity(diff, _config) {
3629
3888
  const destructive = [];
3630
3889
  const nonDestructive = [];
3631
3890
  for (const collection of diff.collectionsToDelete) {
3632
- destructive.push(`Delete collection: ${collection.name}`);
3891
+ if (collection.type === "view") {
3892
+ nonDestructive.push(`Delete view collection: ${collection.name}`);
3893
+ } else {
3894
+ destructive.push(`Delete collection: ${collection.name}`);
3895
+ }
3633
3896
  }
3634
3897
  for (const collection of diff.collectionsToCreate) {
3635
3898
  nonDestructive.push(`Create collection: ${collection.name}`);
@@ -3664,6 +3927,9 @@ function categorizeChangesBySeverity(diff, _config) {
3664
3927
  for (const rule of modification.rulesToUpdate) {
3665
3928
  nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
3666
3929
  }
3930
+ if (modification.viewQueryUpdate) {
3931
+ nonDestructive.push(`Update view query: ${collectionName}`);
3932
+ }
3667
3933
  }
3668
3934
  return { destructive, nonDestructive };
3669
3935
  }
@@ -3676,6 +3942,7 @@ function generateChangeSummary(diff, config) {
3676
3942
  let indexChanges = 0;
3677
3943
  let ruleChanges = 0;
3678
3944
  let permissionChanges = 0;
3945
+ let viewQueryChanges = 0;
3679
3946
  for (const modification of diff.collectionsToModify) {
3680
3947
  fieldsToAdd += modification.fieldsToAdd.length;
3681
3948
  fieldsToRemove += modification.fieldsToRemove.length;
@@ -3683,6 +3950,9 @@ function generateChangeSummary(diff, config) {
3683
3950
  indexChanges += modification.indexesToAdd.length + modification.indexesToRemove.length;
3684
3951
  ruleChanges += modification.rulesToUpdate.length;
3685
3952
  permissionChanges += modification.permissionsToUpdate.length;
3953
+ if (modification.viewQueryUpdate) {
3954
+ viewQueryChanges += 1;
3955
+ }
3686
3956
  }
3687
3957
  return {
3688
3958
  totalChanges: diff.collectionsToCreate.length + diff.collectionsToDelete.length + diff.collectionsToModify.length,
@@ -3695,6 +3965,7 @@ function generateChangeSummary(diff, config) {
3695
3965
  indexChanges,
3696
3966
  ruleChanges,
3697
3967
  permissionChanges,
3968
+ viewQueryChanges,
3698
3969
  destructiveChanges,
3699
3970
  nonDestructiveChanges: nonDestructive
3700
3971
  };
@@ -3724,12 +3995,11 @@ function filterDiff(diff, options) {
3724
3995
  });
3725
3996
  let collectionsToDelete = diff.collectionsToDelete;
3726
3997
  if (skipDestructive) {
3727
- collectionsToDelete = [];
3728
- } else {
3729
- collectionsToDelete = collectionsToDelete.filter((col) => {
3730
- return matchesPattern(col.name, patterns);
3731
- });
3998
+ collectionsToDelete = collectionsToDelete.filter((col) => col.type === "view");
3732
3999
  }
4000
+ collectionsToDelete = collectionsToDelete.filter((col) => {
4001
+ return matchesPattern(col.name, patterns);
4002
+ });
3733
4003
  const collectionsToModify = diff.collectionsToModify.map((mod) => {
3734
4004
  const collectionMatches = matchesPattern(mod.collection, patterns);
3735
4005
  const fieldsToAdd = mod.fieldsToAdd.filter((field) => {
@@ -3754,6 +4024,7 @@ function filterDiff(diff, options) {
3754
4024
  const indexesToRemove = collectionMatches ? mod.indexesToRemove : [];
3755
4025
  const rulesToUpdate = collectionMatches ? mod.rulesToUpdate : [];
3756
4026
  const permissionsToUpdate = collectionMatches ? mod.permissionsToUpdate : [];
4027
+ const viewQueryUpdate = collectionMatches ? mod.viewQueryUpdate : void 0;
3757
4028
  return {
3758
4029
  ...mod,
3759
4030
  fieldsToAdd,
@@ -3762,10 +4033,11 @@ function filterDiff(diff, options) {
3762
4033
  indexesToAdd,
3763
4034
  indexesToRemove,
3764
4035
  rulesToUpdate,
3765
- permissionsToUpdate
4036
+ permissionsToUpdate,
4037
+ viewQueryUpdate
3766
4038
  };
3767
4039
  }).filter((mod) => {
3768
- 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;
4040
+ 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;
3769
4041
  });
3770
4042
  return {
3771
4043
  ...diff,
@@ -3777,7 +4049,7 @@ function filterDiff(diff, options) {
3777
4049
 
3778
4050
  // src/migration/diff/index.ts
3779
4051
  function hasChanges(modification) {
3780
- 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;
4052
+ 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;
3781
4053
  }
3782
4054
  function aggregateChanges(currentSchema, previousSnapshot, config) {
3783
4055
  const collectionIdToName = /* @__PURE__ */ new Map();
@@ -3992,6 +4264,13 @@ function formatValue(value) {
3992
4264
  }
3993
4265
  return String(value);
3994
4266
  }
4267
+ function formatSqlTemplate(query, indent = " ") {
4268
+ const escaped = query.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
4269
+ const body = escaped.split("\n").map((line) => line.trim() === "" ? "" : `${indent}${line}`).join("\n");
4270
+ return `\`
4271
+ ${body}
4272
+ ${indent.slice(0, -2)}\``;
4273
+ }
3995
4274
  function getFieldConstructorName(fieldType) {
3996
4275
  const constructorMap = {
3997
4276
  text: "TextField",
@@ -4030,6 +4309,36 @@ function getSystemFields() {
4030
4309
  }
4031
4310
  ];
4032
4311
  }
4312
+ function getSystemTimestampFields() {
4313
+ return [
4314
+ {
4315
+ name: "created",
4316
+ id: "autodate2990389176",
4317
+ type: "autodate",
4318
+ required: false,
4319
+ options: {
4320
+ hidden: false,
4321
+ onCreate: true,
4322
+ onUpdate: false,
4323
+ presentable: false,
4324
+ system: true
4325
+ }
4326
+ },
4327
+ {
4328
+ name: "updated",
4329
+ id: "autodate3332085495",
4330
+ type: "autodate",
4331
+ required: false,
4332
+ options: {
4333
+ hidden: false,
4334
+ onCreate: true,
4335
+ onUpdate: true,
4336
+ presentable: false,
4337
+ system: true
4338
+ }
4339
+ }
4340
+ ];
4341
+ }
4033
4342
  function getAuthSystemFields() {
4034
4343
  return [
4035
4344
  {
@@ -4322,6 +4631,15 @@ function generateCollectionRules(rules, collectionType = "base") {
4322
4631
  if (!rules) {
4323
4632
  return "";
4324
4633
  }
4634
+ if (collectionType === "view") {
4635
+ return [
4636
+ `"listRule": ${formatValue(rules.listRule ?? null)}`,
4637
+ `"viewRule": ${formatValue(rules.viewRule ?? null)}`,
4638
+ `"createRule": null`,
4639
+ `"updateRule": null`,
4640
+ `"deleteRule": null`
4641
+ ].join(",\n ");
4642
+ }
4325
4643
  const parts = [];
4326
4644
  if (rules.listRule !== void 0) {
4327
4645
  parts.push(`"listRule": ${formatValue(rules.listRule)}`);
@@ -4347,6 +4665,15 @@ function generateCollectionPermissions(permissions, collectionType = "base") {
4347
4665
  if (!permissions) {
4348
4666
  return "";
4349
4667
  }
4668
+ if (collectionType === "view") {
4669
+ return [
4670
+ `"listRule": ${formatValue(permissions.listRule ?? null)}`,
4671
+ `"viewRule": ${formatValue(permissions.viewRule ?? null)}`,
4672
+ `"createRule": null`,
4673
+ `"updateRule": null`,
4674
+ `"deleteRule": null`
4675
+ ].join(",\n ");
4676
+ }
4350
4677
  const parts = [];
4351
4678
  if (permissions.listRule !== void 0) {
4352
4679
  parts.push(`"listRule": ${formatValue(permissions.listRule)}`);
@@ -4384,6 +4711,28 @@ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, i
4384
4711
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
4385
4712
  return lines.join("\n");
4386
4713
  }
4714
+ function generateViewQueryUpdate(collectionName, viewQuery, varName, isLast = false, collectionIdMap) {
4715
+ const lines = [];
4716
+ const collectionVar = varName || `collection_${collectionName}_viewQuery`;
4717
+ lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
4718
+ lines.push(` unmarshal({`);
4719
+ lines.push(` "viewQuery": ${formatSqlTemplate(viewQuery, " ")},`);
4720
+ lines.push(` }, ${collectionVar})`);
4721
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
4722
+ return lines.join("\n");
4723
+ }
4724
+ function generateGroupedRuleUpdates(collectionName, entries, varSuffix, isLast = false, collectionIdMap) {
4725
+ const collectionVar = `collection_${collectionName}_${varSuffix}`;
4726
+ const lines = [];
4727
+ lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
4728
+ lines.push(` unmarshal({`);
4729
+ for (const entry of entries) {
4730
+ lines.push(` "${entry.ruleType}": ${formatValue(entry.value)},`);
4731
+ }
4732
+ lines.push(` }, ${collectionVar})`);
4733
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
4734
+ return lines.join("\n");
4735
+ }
4387
4736
 
4388
4737
  // src/migration/generator/collections.ts
4389
4738
  function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
@@ -4402,16 +4751,24 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
4402
4751
  } else if (rulesCode) {
4403
4752
  lines.push(` ${rulesCode},`);
4404
4753
  }
4754
+ if (collection.type === "view") {
4755
+ lines.push(` "viewQuery": ${formatSqlTemplate(collection.viewQuery ?? "")},`);
4756
+ lines.push(` });`);
4757
+ lines.push(``);
4758
+ lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
4759
+ return lines.join("\n");
4760
+ }
4405
4761
  const systemFieldNames = ["created", "updated", "id"];
4406
4762
  if (collection.type === "auth") {
4407
4763
  systemFieldNames.push(...getAuthSystemFields().map((f) => f.name));
4408
4764
  }
4409
4765
  const userFields = collection.fields.filter((f) => !systemFieldNames.includes(f.name));
4410
- const allFields = [...getSystemFields().filter((f) => f.name === "id")];
4766
+ const allFields = [...getSystemFields()];
4411
4767
  if (collection.type === "auth") {
4412
4768
  allFields.push(...getAuthSystemFields());
4413
4769
  }
4414
4770
  allFields.push(...userFields);
4771
+ allFields.push(...getSystemTimestampFields());
4415
4772
  lines.push(` "fields": ${generateFieldsArray(allFields, collectionIdMap)},`);
4416
4773
  let allIndexes = [...collection.indexes || []];
4417
4774
  if (collection.type === "auth") {
@@ -4441,7 +4798,21 @@ function generateOperationUpMigration(operation, collectionIdMap) {
4441
4798
  const modification = operation.modifications;
4442
4799
  const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
4443
4800
  let operationCount = 0;
4444
- const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
4801
+ 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);
4802
+ if (modification.viewQueryUpdate) {
4803
+ operationCount++;
4804
+ const isLast = operationCount === totalOperations;
4805
+ lines.push(
4806
+ generateViewQueryUpdate(
4807
+ collectionName,
4808
+ modification.viewQueryUpdate.newValue,
4809
+ void 0,
4810
+ isLast,
4811
+ collectionIdMap
4812
+ )
4813
+ );
4814
+ if (!isLast) lines.push("");
4815
+ }
4445
4816
  for (let i = 0; i < modification.fieldsToAdd.length; i++) {
4446
4817
  const field = modification.fieldsToAdd[i];
4447
4818
  operationCount++;
@@ -4481,23 +4852,29 @@ function generateOperationUpMigration(operation, collectionIdMap) {
4481
4852
  if (!isLast) lines.push("");
4482
4853
  }
4483
4854
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
4484
- for (const permission of modification.permissionsToUpdate) {
4485
- operationCount++;
4855
+ operationCount++;
4856
+ const isLast = operationCount === totalOperations;
4857
+ if (modification.permissionsToUpdate.length >= 2) {
4858
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.newValue }));
4859
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", isLast, collectionIdMap));
4860
+ } else {
4861
+ const permission = modification.permissionsToUpdate[0];
4486
4862
  const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
4487
- const isLast = operationCount === totalOperations;
4488
- lines.push(
4489
- generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast, collectionIdMap)
4490
- );
4491
- if (!isLast) lines.push("");
4863
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast, collectionIdMap));
4492
4864
  }
4865
+ if (!isLast) lines.push("");
4493
4866
  } else if (modification.rulesToUpdate.length > 0) {
4494
- for (const rule of modification.rulesToUpdate) {
4495
- operationCount++;
4867
+ operationCount++;
4868
+ const isLast = operationCount === totalOperations;
4869
+ if (modification.rulesToUpdate.length >= 2) {
4870
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.newValue }));
4871
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", isLast, collectionIdMap));
4872
+ } else {
4873
+ const rule = modification.rulesToUpdate[0];
4496
4874
  const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
4497
- const isLast = operationCount === totalOperations;
4498
4875
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast, collectionIdMap));
4499
- if (!isLast) lines.push("");
4500
4876
  }
4877
+ if (!isLast) lines.push("");
4501
4878
  }
4502
4879
  } else if (operation.type === "delete") {
4503
4880
  const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
@@ -4536,25 +4913,45 @@ function generateOperationDownMigration(operation, collectionIdMap) {
4536
4913
  const modification = operation.modifications;
4537
4914
  const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
4538
4915
  let operationCount = 0;
4539
- const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
4916
+ 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);
4917
+ if (modification.viewQueryUpdate) {
4918
+ operationCount++;
4919
+ const isLast = operationCount === totalOperations;
4920
+ lines.push(
4921
+ generateViewQueryUpdate(
4922
+ collectionName,
4923
+ modification.viewQueryUpdate.oldValue ?? "",
4924
+ `collection_${collectionName}_revert_viewQuery`,
4925
+ isLast,
4926
+ collectionIdMap
4927
+ )
4928
+ );
4929
+ if (!isLast) lines.push("");
4930
+ }
4540
4931
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
4541
- for (const permission of modification.permissionsToUpdate) {
4542
- operationCount++;
4932
+ operationCount++;
4933
+ const isLast = operationCount === totalOperations;
4934
+ if (modification.permissionsToUpdate.length >= 2) {
4935
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.oldValue }));
4936
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", isLast, collectionIdMap));
4937
+ } else {
4938
+ const permission = modification.permissionsToUpdate[0];
4543
4939
  const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
4544
- const isLast = operationCount === totalOperations;
4545
- lines.push(
4546
- generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast, collectionIdMap)
4547
- );
4548
- if (!isLast) lines.push("");
4940
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast, collectionIdMap));
4549
4941
  }
4942
+ if (!isLast) lines.push("");
4550
4943
  } else if (modification.rulesToUpdate.length > 0) {
4551
- for (const rule of modification.rulesToUpdate) {
4552
- operationCount++;
4944
+ operationCount++;
4945
+ const isLast = operationCount === totalOperations;
4946
+ if (modification.rulesToUpdate.length >= 2) {
4947
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.oldValue }));
4948
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", isLast, collectionIdMap));
4949
+ } else {
4950
+ const rule = modification.rulesToUpdate[0];
4553
4951
  const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
4554
- const isLast = operationCount === totalOperations;
4555
4952
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast, collectionIdMap));
4556
- if (!isLast) lines.push("");
4557
4953
  }
4954
+ if (!isLast) lines.push("");
4558
4955
  }
4559
4956
  for (let i = 0; i < modification.indexesToRemove.length; i++) {
4560
4957
  operationCount++;
@@ -4661,6 +5058,19 @@ function generateUpMigration(diff) {
4661
5058
  lines.push(` // Modify existing collections`);
4662
5059
  for (const modification of diff.collectionsToModify) {
4663
5060
  const collectionName = modification.collection;
5061
+ if (modification.viewQueryUpdate) {
5062
+ lines.push(` // Update the view query of ${collectionName}`);
5063
+ lines.push(
5064
+ generateViewQueryUpdate(
5065
+ collectionName,
5066
+ modification.viewQueryUpdate.newValue,
5067
+ void 0,
5068
+ false,
5069
+ collectionIdMap
5070
+ )
5071
+ );
5072
+ lines.push(``);
5073
+ }
4664
5074
  if (modification.fieldsToAdd.length > 0) {
4665
5075
  lines.push(` // Add fields to ${collectionName}`);
4666
5076
  for (let i = 0; i < modification.fieldsToAdd.length; i++) {
@@ -4706,20 +5116,26 @@ function generateUpMigration(diff) {
4706
5116
  }
4707
5117
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
4708
5118
  lines.push(` // Update permissions for ${collectionName}`);
4709
- for (const permission of modification.permissionsToUpdate) {
5119
+ if (modification.permissionsToUpdate.length >= 2) {
5120
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.newValue }));
5121
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", false, collectionIdMap));
5122
+ } else {
5123
+ const permission = modification.permissionsToUpdate[0];
4710
5124
  const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
4711
- lines.push(
4712
- generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, false, collectionIdMap)
4713
- );
4714
- lines.push(``);
5125
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, false, collectionIdMap));
4715
5126
  }
5127
+ lines.push(``);
4716
5128
  } else if (modification.rulesToUpdate.length > 0) {
4717
5129
  lines.push(` // Update rules for ${collectionName}`);
4718
- for (const rule of modification.rulesToUpdate) {
5130
+ if (modification.rulesToUpdate.length >= 2) {
5131
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.newValue }));
5132
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", false, collectionIdMap));
5133
+ } else {
5134
+ const rule = modification.rulesToUpdate[0];
4719
5135
  const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
4720
5136
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, false, collectionIdMap));
4721
- lines.push(``);
4722
5137
  }
5138
+ lines.push(``);
4723
5139
  }
4724
5140
  }
4725
5141
  }
@@ -4791,22 +5207,41 @@ function generateDownMigration(diff) {
4791
5207
  lines.push(` // Revert modifications`);
4792
5208
  for (const modification of diff.collectionsToModify) {
4793
5209
  const collectionName = modification.collection;
5210
+ if (modification.viewQueryUpdate) {
5211
+ lines.push(` // Restore the view query of ${collectionName}`);
5212
+ lines.push(
5213
+ generateViewQueryUpdate(
5214
+ collectionName,
5215
+ modification.viewQueryUpdate.oldValue ?? "",
5216
+ `collection_${collectionName}_revert_viewQuery`,
5217
+ false,
5218
+ collectionIdMap
5219
+ )
5220
+ );
5221
+ lines.push(``);
5222
+ }
4794
5223
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
4795
5224
  lines.push(` // Revert permissions for ${collectionName}`);
4796
- for (const permission of modification.permissionsToUpdate) {
5225
+ if (modification.permissionsToUpdate.length >= 2) {
5226
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.oldValue }));
5227
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", false, collectionIdMap));
5228
+ } else {
5229
+ const permission = modification.permissionsToUpdate[0];
4797
5230
  const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
4798
- lines.push(
4799
- generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, false, collectionIdMap)
4800
- );
4801
- lines.push(``);
5231
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, false, collectionIdMap));
4802
5232
  }
5233
+ lines.push(``);
4803
5234
  } else if (modification.rulesToUpdate.length > 0) {
4804
5235
  lines.push(` // Revert rules for ${collectionName}`);
4805
- for (const rule of modification.rulesToUpdate) {
5236
+ if (modification.rulesToUpdate.length >= 2) {
5237
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.oldValue }));
5238
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", false, collectionIdMap));
5239
+ } else {
5240
+ const rule = modification.rulesToUpdate[0];
4806
5241
  const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
4807
5242
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, false, collectionIdMap));
4808
- lines.push(``);
4809
5243
  }
5244
+ lines.push(``);
4810
5245
  }
4811
5246
  if (modification.indexesToRemove.length > 0) {
4812
5247
  lines.push(` // Restore indexes to ${collectionName}`);
@@ -5136,6 +5571,9 @@ var MigrationGenerator = class {
5136
5571
  function detectCollectionDeletions(diff) {
5137
5572
  const changes = [];
5138
5573
  for (const collection of diff.collectionsToDelete) {
5574
+ if (collection.type === "view") {
5575
+ continue;
5576
+ }
5139
5577
  changes.push({
5140
5578
  type: "collection_deletion" /* COLLECTION_DELETION */,
5141
5579
  description: `Delete collection: ${collection.name}`,