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
package/dist/server.js CHANGED
@@ -454,13 +454,16 @@ function withIndexes(schema, indexes) {
454
454
  return schema.describe(JSON.stringify(metadata));
455
455
  }
456
456
  function defineCollection(config) {
457
- const { collectionName, schema, permissions, indexes, type, ...futureOptions } = config;
457
+ const { collectionName, schema, permissions, indexes, type, viewQuery, ...futureOptions } = config;
458
458
  const metadata = {
459
459
  collectionName
460
460
  };
461
461
  if (type) {
462
462
  metadata.type = type;
463
463
  }
464
+ if (viewQuery !== void 0) {
465
+ metadata.viewQuery = viewQuery;
466
+ }
464
467
  if (permissions) {
465
468
  metadata.permissions = permissions;
466
469
  }
@@ -726,6 +729,88 @@ function GeoPointField() {
726
729
  };
727
730
  return schema.describe(JSON.stringify(metadata));
728
731
  }
732
+ function dedentSql(value) {
733
+ const lines = value.replace(/\r\n/g, "\n").split("\n");
734
+ while (lines.length > 0 && lines[0].trim() === "") {
735
+ lines.shift();
736
+ }
737
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
738
+ lines.pop();
739
+ }
740
+ if (lines.length === 0) {
741
+ return "";
742
+ }
743
+ let minIndent = Infinity;
744
+ for (const line of lines) {
745
+ if (line.trim() === "") continue;
746
+ const indent = line.length - line.trimStart().length;
747
+ if (indent < minIndent) {
748
+ minIndent = indent;
749
+ }
750
+ }
751
+ if (!Number.isFinite(minIndent) || minIndent === 0) {
752
+ return lines.map((line) => line.trimEnd()).join("\n");
753
+ }
754
+ return lines.map((line) => line.slice(minIndent).trimEnd()).join("\n");
755
+ }
756
+ function sql(strings, ...values) {
757
+ let result = "";
758
+ for (let i = 0; i < strings.length; i++) {
759
+ result += strings[i];
760
+ if (i < values.length) {
761
+ result += String(values[i]);
762
+ }
763
+ }
764
+ return dedentSql(result);
765
+ }
766
+ function stripLeadingComments(query) {
767
+ let remaining = query.trim();
768
+ while (remaining.length > 0) {
769
+ if (remaining.startsWith("--")) {
770
+ const newline = remaining.indexOf("\n");
771
+ remaining = newline === -1 ? "" : remaining.slice(newline + 1).trim();
772
+ continue;
773
+ }
774
+ if (remaining.startsWith("/*")) {
775
+ const end = remaining.indexOf("*/");
776
+ remaining = end === -1 ? "" : remaining.slice(end + 2).trim();
777
+ continue;
778
+ }
779
+ break;
780
+ }
781
+ return remaining;
782
+ }
783
+ function validateViewQuery(collectionName, viewQuery) {
784
+ if (typeof viewQuery !== "string" || viewQuery.trim() === "") {
785
+ throw new Error(
786
+ `View collection "${collectionName}" requires a non-empty viewQuery. Provide the SQL SELECT statement backing the view.`
787
+ );
788
+ }
789
+ const body = stripLeadingComments(viewQuery);
790
+ if (!/^(select|with)\b/i.test(body)) {
791
+ throw new Error(
792
+ `View collection "${collectionName}" has an invalid viewQuery: it must start with SELECT or WITH. Received: ${body.slice(0, 40)}${body.length > 40 ? "..." : ""}`
793
+ );
794
+ }
795
+ }
796
+ function defineView(config) {
797
+ const { collectionName, schema, viewQuery, permissions } = config;
798
+ validateViewQuery(collectionName, viewQuery);
799
+ return defineCollection({
800
+ collectionName,
801
+ schema,
802
+ type: "view",
803
+ viewQuery,
804
+ // Write rules are always locked for views - PocketBase rejects anything else
805
+ permissions: {
806
+ listRule: permissions?.listRule ?? null,
807
+ viewRule: permissions?.viewRule ?? null,
808
+ createRule: null,
809
+ updateRule: null,
810
+ deleteRule: null
811
+ }
812
+ });
813
+ }
729
814
 
730
815
  // src/utils/permission-templates.ts
731
816
  var PermissionTemplates = {
@@ -2272,13 +2357,26 @@ function extractCollectionTypeFromSchema(zodSchema) {
2272
2357
  }
2273
2358
  try {
2274
2359
  const metadata = JSON.parse(zodSchema.description);
2275
- if (metadata.type === "base" || metadata.type === "auth") {
2360
+ if (metadata.type === "base" || metadata.type === "auth" || metadata.type === "view") {
2276
2361
  return metadata.type;
2277
2362
  }
2278
2363
  } catch {
2279
2364
  }
2280
2365
  return null;
2281
2366
  }
2367
+ function extractViewQueryFromSchema(zodSchema) {
2368
+ if (!zodSchema.description) {
2369
+ return null;
2370
+ }
2371
+ try {
2372
+ const metadata = JSON.parse(zodSchema.description);
2373
+ if (typeof metadata.viewQuery === "string") {
2374
+ return metadata.viewQuery;
2375
+ }
2376
+ } catch {
2377
+ }
2378
+ return null;
2379
+ }
2282
2380
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema", "Collection"]) {
2283
2381
  const result = {};
2284
2382
  if (module.default instanceof z.ZodObject) {
@@ -2437,6 +2535,15 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
2437
2535
  const rawFields = extractFieldDefinitions(zodSchema);
2438
2536
  const explicitType = extractCollectionTypeFromSchema(zodSchema);
2439
2537
  const collectionType = explicitType ?? (isAuthCollection(rawFields) ? "auth" : "base");
2538
+ const isView = collectionType === "view";
2539
+ const viewQuery = extractViewQueryFromSchema(zodSchema);
2540
+ if (isView) {
2541
+ validateViewQuery(collectionName, viewQuery);
2542
+ } else if (viewQuery !== null) {
2543
+ console.warn(
2544
+ `[${collectionName}] viewQuery is only used by view collections and will be ignored. Use defineView() or set type: "view" to define a view collection.`
2545
+ );
2546
+ }
2440
2547
  const fields = rawFields.filter((f) => !["created", "updated"].includes(f.name)).map(({ name, zodType }) => buildFieldDefinition(name, zodType));
2441
2548
  if (collectionType === "auth") {
2442
2549
  const authSystemFields = [
@@ -2496,26 +2603,43 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
2496
2603
  }
2497
2604
  }
2498
2605
  const indexes = extractIndexes(zodSchema) || [];
2606
+ if (isView && indexes.length > 0) {
2607
+ throw new Error(
2608
+ `View collection "${collectionName}" cannot declare indexes. PocketBase view collections are backed by a SQL query and do not support indexes.`
2609
+ );
2610
+ }
2499
2611
  const permissionAnalyzer = new PermissionAnalyzer();
2500
2612
  let permissions = void 0;
2501
2613
  const schemaDescription = zodSchema.description;
2502
2614
  const extractedPermissions = permissionAnalyzer.extractPermissions(schemaDescription);
2503
2615
  if (extractedPermissions) {
2504
2616
  const resolvedPermissions = permissionAnalyzer.resolvePermissions(extractedPermissions);
2505
- const validationResults = permissionAnalyzer.validatePermissions(
2506
- collectionName,
2507
- resolvedPermissions,
2508
- fields,
2509
- collectionType === "auth"
2510
- );
2511
- for (const [ruleType, result] of validationResults) {
2512
- if (!result.valid) {
2513
- console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
2514
- result.errors.forEach((error) => console.error(` - ${error}`));
2617
+ if (isView) {
2618
+ for (const ruleType of ["createRule", "updateRule", "deleteRule", "manageRule"]) {
2619
+ if (resolvedPermissions[ruleType]) {
2620
+ console.warn(
2621
+ `[${collectionName}] ${ruleType} is not supported on view collections and will be set to null.`
2622
+ );
2623
+ }
2624
+ resolvedPermissions[ruleType] = null;
2515
2625
  }
2516
- if (result.warnings.length > 0) {
2517
- console.warn(`[${collectionName}] Permission warnings for ${ruleType}:`);
2518
- result.warnings.forEach((warning) => console.warn(` - ${warning}`));
2626
+ }
2627
+ if (!isView) {
2628
+ const validationResults = permissionAnalyzer.validatePermissions(
2629
+ collectionName,
2630
+ resolvedPermissions,
2631
+ fields,
2632
+ collectionType === "auth"
2633
+ );
2634
+ for (const [ruleType, result] of validationResults) {
2635
+ if (!result.valid) {
2636
+ console.error(`[${collectionName}] Permission validation failed for ${ruleType}:`);
2637
+ result.errors.forEach((error) => console.error(` - ${error}`));
2638
+ }
2639
+ if (result.warnings.length > 0) {
2640
+ console.warn(`[${collectionName}] Permission warnings for ${ruleType}:`);
2641
+ result.warnings.forEach((warning) => console.warn(` - ${warning}`));
2642
+ }
2519
2643
  }
2520
2644
  }
2521
2645
  permissions = permissionAnalyzer.mergeWithDefaults(resolvedPermissions);
@@ -2536,6 +2660,9 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
2536
2660
  },
2537
2661
  permissions
2538
2662
  };
2663
+ if (isView) {
2664
+ collectionSchema.viewQuery = viewQuery;
2665
+ }
2539
2666
  return collectionSchema;
2540
2667
  }
2541
2668
  var tsxLoaderRegistered = false;
@@ -2817,8 +2944,9 @@ function convertPocketBaseField(pbField) {
2817
2944
  }
2818
2945
  function convertPocketBaseCollection(pbCollection) {
2819
2946
  const fields = [];
2947
+ const isView = pbCollection.type === "view";
2820
2948
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
2821
- if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
2949
+ if (!isView && pbCollection.fields && Array.isArray(pbCollection.fields)) {
2822
2950
  for (const pbField of pbCollection.fields) {
2823
2951
  if (pbField.system || systemFieldNames.includes(pbField.name)) {
2824
2952
  const isAuthSystemField = pbCollection.type === "auth" && ["email", "emailVisibility", "verified", "password", "tokenKey"].includes(pbField.name);
@@ -2838,6 +2966,9 @@ function convertPocketBaseCollection(pbCollection) {
2838
2966
  if (pbCollection.id) {
2839
2967
  schema.id = pbCollection.id;
2840
2968
  }
2969
+ if (typeof pbCollection.viewQuery === "string") {
2970
+ schema.viewQuery = dedentSql(pbCollection.viewQuery);
2971
+ }
2841
2972
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2842
2973
  schema.indexes = pbCollection.indexes;
2843
2974
  }
@@ -2939,6 +3070,24 @@ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
2939
3070
  return [];
2940
3071
  }
2941
3072
  }
3073
+ function skipTemplateLiteral(content, start) {
3074
+ let i = start + 1;
3075
+ while (i < content.length) {
3076
+ const char = content[i];
3077
+ if (char === "\\") {
3078
+ i += 2;
3079
+ continue;
3080
+ }
3081
+ if (char === "`") {
3082
+ return i + 1;
3083
+ }
3084
+ i++;
3085
+ }
3086
+ return i;
3087
+ }
3088
+ function unescapeTemplateLiteral(raw) {
3089
+ return raw.replace(/\\(`|\$\{|\\)/g, "$1");
3090
+ }
2942
3091
  function parseMigrationOperationsFromContent(content) {
2943
3092
  const collectionsToCreate = [];
2944
3093
  const collectionsToDelete = [];
@@ -3027,6 +3176,10 @@ function parseMigrationOperationsFromContent(content) {
3027
3176
  while (i < content.length && bCount > 0) {
3028
3177
  const char = content[i];
3029
3178
  const prev = i > 0 ? content[i - 1] : "";
3179
+ if (!inStr && char === "`") {
3180
+ i = skipTemplateLiteral(content, i);
3181
+ continue;
3182
+ }
3030
3183
  if (!inStr && (char === '"' || char === "'")) {
3031
3184
  inStr = true;
3032
3185
  strChar = char;
@@ -3216,6 +3369,95 @@ function parseMigrationOperationsFromContent(content) {
3216
3369
  }
3217
3370
  }
3218
3371
  }
3372
+ const viewQueryRegex = /(\w+)\.viewQuery\s*=\s*/g;
3373
+ let viewQueryMatch;
3374
+ while ((viewQueryMatch = viewQueryRegex.exec(content)) !== null) {
3375
+ const varInfo = variables.get(viewQueryMatch[1]);
3376
+ if (!varInfo || varInfo.type !== "collection") continue;
3377
+ const valueStart = viewQueryMatch.index + viewQueryMatch[0].length;
3378
+ if (content[valueStart] === "`") {
3379
+ const end = skipTemplateLiteral(content, valueStart);
3380
+ const raw = content.substring(valueStart + 1, end - 1);
3381
+ getUpdate(varInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
3382
+ viewQueryRegex.lastIndex = end;
3383
+ } else {
3384
+ const terminator = content.indexOf(";", valueStart);
3385
+ const valueStr = content.substring(valueStart, terminator === -1 ? content.length : terminator);
3386
+ try {
3387
+ getUpdate(varInfo.name).viewQuery = dedentSql(new Function("app", `return ${valueStr}`)(mockApp));
3388
+ } catch {
3389
+ getUpdate(varInfo.name).viewQuery = dedentSql(valueStr.trim());
3390
+ }
3391
+ }
3392
+ }
3393
+ const unmarshalRegex = /unmarshal\s*\(/g;
3394
+ let unmarshalMatch;
3395
+ while ((unmarshalMatch = unmarshalRegex.exec(content)) !== null) {
3396
+ let i = unmarshalMatch.index + unmarshalMatch[0].length;
3397
+ while (i < content.length && /\s/.test(content[i])) i++;
3398
+ if (content[i] !== "{") continue;
3399
+ const objStart = i;
3400
+ let braceCount = 1;
3401
+ let inStr = false;
3402
+ let strChar = null;
3403
+ i++;
3404
+ while (i < content.length && braceCount > 0) {
3405
+ const ch = content[i];
3406
+ const prev = i > 0 ? content[i - 1] : "";
3407
+ if (!inStr && ch === "`") {
3408
+ i = skipTemplateLiteral(content, i);
3409
+ continue;
3410
+ }
3411
+ if (!inStr && (ch === '"' || ch === "'")) {
3412
+ inStr = true;
3413
+ strChar = ch;
3414
+ } else if (inStr && ch === strChar && prev !== "\\") {
3415
+ inStr = false;
3416
+ strChar = null;
3417
+ }
3418
+ if (!inStr) {
3419
+ if (ch === "{") braceCount++;
3420
+ if (ch === "}") braceCount--;
3421
+ }
3422
+ i++;
3423
+ }
3424
+ if (braceCount !== 0) continue;
3425
+ const objStr = content.substring(objStart, i);
3426
+ while (i < content.length && /[\s,]/.test(content[i])) i++;
3427
+ const varStart = i;
3428
+ while (i < content.length && /\w/.test(content[i])) i++;
3429
+ const colVarName = content.substring(varStart, i);
3430
+ const colInfo = variables.get(colVarName);
3431
+ if (!colInfo || colInfo.type !== "collection") continue;
3432
+ const keyValRegex = /"(\w+Rule)"\s*:\s*([^,}\n]+)/g;
3433
+ let kvMatch;
3434
+ while ((kvMatch = keyValRegex.exec(objStr)) !== null) {
3435
+ const ruleKey = kvMatch[1];
3436
+ const valStr = kvMatch[2].trim();
3437
+ try {
3438
+ const value = new Function("app", `return ${valStr}`)(mockApp);
3439
+ getUpdate(colInfo.name).rulesToUpdate[ruleKey] = value;
3440
+ } catch {
3441
+ getUpdate(colInfo.name).rulesToUpdate[ruleKey] = valStr;
3442
+ }
3443
+ }
3444
+ const viewQueryKey = objStr.match(/"viewQuery"\s*:\s*/);
3445
+ if (viewQueryKey) {
3446
+ const valueStart = viewQueryKey.index + viewQueryKey[0].length;
3447
+ if (objStr[valueStart] === "`") {
3448
+ const end = skipTemplateLiteral(objStr, valueStart);
3449
+ const raw = objStr.substring(valueStart + 1, end - 1);
3450
+ getUpdate(colInfo.name).viewQuery = dedentSql(unescapeTemplateLiteral(raw));
3451
+ } else {
3452
+ const valStr = objStr.substring(valueStart).replace(/[,}\s]+$/, "");
3453
+ try {
3454
+ getUpdate(colInfo.name).viewQuery = dedentSql(new Function("app", `return ${valStr}`)(mockApp));
3455
+ } catch {
3456
+ getUpdate(colInfo.name).viewQuery = dedentSql(valStr);
3457
+ }
3458
+ }
3459
+ }
3460
+ }
3219
3461
  const idxPushRegex = /(\w+)\.indexes\.push\s*\(/g;
3220
3462
  let match;
3221
3463
  while ((match = idxPushRegex.exec(content)) !== null) {
@@ -3345,6 +3587,10 @@ function parseMigrationOperations(migrationContent) {
3345
3587
  while (i < migrationContent.length && braceCount > 0) {
3346
3588
  const char = migrationContent[i];
3347
3589
  const prevChar = i > 0 ? migrationContent[i - 1] : "";
3590
+ if (!inString && char === "`") {
3591
+ i = skipTemplateLiteral(migrationContent, i);
3592
+ continue;
3593
+ }
3348
3594
  if (!inString && (char === '"' || char === "'")) {
3349
3595
  inString = true;
3350
3596
  stringChar = char;
@@ -3680,6 +3926,9 @@ function applyMigrationOperations(snapshot, operations) {
3680
3926
  collection.indexes = collection.indexes.filter((idx) => !update.indexesToRemove.includes(idx));
3681
3927
  }
3682
3928
  }
3929
+ if (update.viewQuery !== void 0) {
3930
+ collection.viewQuery = update.viewQuery;
3931
+ }
3683
3932
  if (Object.keys(update.rulesToUpdate).length > 0) {
3684
3933
  if (!collection.rules) collection.rules = {};
3685
3934
  if (!collection.permissions) collection.permissions = {};
@@ -4299,7 +4548,45 @@ function compareCollectionFields(currentCollection, previousCollection, config,
4299
4548
  fieldsToRemove = fieldsToRemove.filter((_, index) => !processedRemoveIndices.has(index));
4300
4549
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
4301
4550
  }
4551
+ function normalizeSql(query) {
4552
+ if (!query) {
4553
+ return "";
4554
+ }
4555
+ return query.split("\n").map((line) => line.trim()).join(" ").replace(/\s+/g, " ").trim();
4556
+ }
4557
+ function compareViewQuery(currentCollection, previousCollection) {
4558
+ const newValue = currentCollection.viewQuery;
4559
+ if (typeof newValue !== "string") {
4560
+ return void 0;
4561
+ }
4562
+ if (normalizeSql(newValue) === normalizeSql(previousCollection.viewQuery)) {
4563
+ return void 0;
4564
+ }
4565
+ return {
4566
+ oldValue: previousCollection.viewQuery ?? null,
4567
+ newValue
4568
+ };
4569
+ }
4302
4570
  function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
4571
+ const isView = currentCollection.type === "view" || previousCollection.type === "view";
4572
+ if (isView) {
4573
+ return {
4574
+ collection: currentCollection.name,
4575
+ fieldsToAdd: [],
4576
+ fieldsToRemove: [],
4577
+ fieldsToModify: [],
4578
+ indexesToAdd: [],
4579
+ indexesToRemove: [],
4580
+ rulesToUpdate: compareRules(
4581
+ currentCollection.rules,
4582
+ previousCollection.rules,
4583
+ currentCollection.permissions,
4584
+ previousCollection.permissions
4585
+ ),
4586
+ permissionsToUpdate: comparePermissions(currentCollection.permissions, previousCollection.permissions),
4587
+ viewQueryUpdate: compareViewQuery(currentCollection, previousCollection)
4588
+ };
4589
+ }
4303
4590
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
4304
4591
  currentCollection,
4305
4592
  previousCollection,
@@ -4331,6 +4618,9 @@ function detectDestructiveChanges(diff, config) {
4331
4618
  const destructiveChanges = [];
4332
4619
  const mergedConfig = mergeConfig3(config);
4333
4620
  for (const collection of diff.collectionsToDelete) {
4621
+ if (collection.type === "view") {
4622
+ continue;
4623
+ }
4334
4624
  destructiveChanges.push({
4335
4625
  type: "collection_delete",
4336
4626
  severity: "high",
@@ -4418,7 +4708,11 @@ function categorizeChangesBySeverity(diff, _config) {
4418
4708
  const destructive = [];
4419
4709
  const nonDestructive = [];
4420
4710
  for (const collection of diff.collectionsToDelete) {
4421
- destructive.push(`Delete collection: ${collection.name}`);
4711
+ if (collection.type === "view") {
4712
+ nonDestructive.push(`Delete view collection: ${collection.name}`);
4713
+ } else {
4714
+ destructive.push(`Delete collection: ${collection.name}`);
4715
+ }
4422
4716
  }
4423
4717
  for (const collection of diff.collectionsToCreate) {
4424
4718
  nonDestructive.push(`Create collection: ${collection.name}`);
@@ -4453,6 +4747,9 @@ function categorizeChangesBySeverity(diff, _config) {
4453
4747
  for (const rule of modification.rulesToUpdate) {
4454
4748
  nonDestructive.push(`Update rule: ${collectionName}.${rule.ruleType}`);
4455
4749
  }
4750
+ if (modification.viewQueryUpdate) {
4751
+ nonDestructive.push(`Update view query: ${collectionName}`);
4752
+ }
4456
4753
  }
4457
4754
  return { destructive, nonDestructive };
4458
4755
  }
@@ -4465,6 +4762,7 @@ function generateChangeSummary(diff, config) {
4465
4762
  let indexChanges = 0;
4466
4763
  let ruleChanges = 0;
4467
4764
  let permissionChanges = 0;
4765
+ let viewQueryChanges = 0;
4468
4766
  for (const modification of diff.collectionsToModify) {
4469
4767
  fieldsToAdd += modification.fieldsToAdd.length;
4470
4768
  fieldsToRemove += modification.fieldsToRemove.length;
@@ -4472,6 +4770,9 @@ function generateChangeSummary(diff, config) {
4472
4770
  indexChanges += modification.indexesToAdd.length + modification.indexesToRemove.length;
4473
4771
  ruleChanges += modification.rulesToUpdate.length;
4474
4772
  permissionChanges += modification.permissionsToUpdate.length;
4773
+ if (modification.viewQueryUpdate) {
4774
+ viewQueryChanges += 1;
4775
+ }
4475
4776
  }
4476
4777
  return {
4477
4778
  totalChanges: diff.collectionsToCreate.length + diff.collectionsToDelete.length + diff.collectionsToModify.length,
@@ -4484,6 +4785,7 @@ function generateChangeSummary(diff, config) {
4484
4785
  indexChanges,
4485
4786
  ruleChanges,
4486
4787
  permissionChanges,
4788
+ viewQueryChanges,
4487
4789
  destructiveChanges,
4488
4790
  nonDestructiveChanges: nonDestructive
4489
4791
  };
@@ -4513,12 +4815,11 @@ function filterDiff(diff, options) {
4513
4815
  });
4514
4816
  let collectionsToDelete = diff.collectionsToDelete;
4515
4817
  if (skipDestructive) {
4516
- collectionsToDelete = [];
4517
- } else {
4518
- collectionsToDelete = collectionsToDelete.filter((col) => {
4519
- return matchesPattern(col.name, patterns);
4520
- });
4818
+ collectionsToDelete = collectionsToDelete.filter((col) => col.type === "view");
4521
4819
  }
4820
+ collectionsToDelete = collectionsToDelete.filter((col) => {
4821
+ return matchesPattern(col.name, patterns);
4822
+ });
4522
4823
  const collectionsToModify = diff.collectionsToModify.map((mod) => {
4523
4824
  const collectionMatches = matchesPattern(mod.collection, patterns);
4524
4825
  const fieldsToAdd = mod.fieldsToAdd.filter((field) => {
@@ -4543,6 +4844,7 @@ function filterDiff(diff, options) {
4543
4844
  const indexesToRemove = collectionMatches ? mod.indexesToRemove : [];
4544
4845
  const rulesToUpdate = collectionMatches ? mod.rulesToUpdate : [];
4545
4846
  const permissionsToUpdate = collectionMatches ? mod.permissionsToUpdate : [];
4847
+ const viewQueryUpdate = collectionMatches ? mod.viewQueryUpdate : void 0;
4546
4848
  return {
4547
4849
  ...mod,
4548
4850
  fieldsToAdd,
@@ -4551,10 +4853,11 @@ function filterDiff(diff, options) {
4551
4853
  indexesToAdd,
4552
4854
  indexesToRemove,
4553
4855
  rulesToUpdate,
4554
- permissionsToUpdate
4856
+ permissionsToUpdate,
4857
+ viewQueryUpdate
4555
4858
  };
4556
4859
  }).filter((mod) => {
4557
- 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;
4860
+ 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;
4558
4861
  });
4559
4862
  return {
4560
4863
  ...diff,
@@ -4566,7 +4869,7 @@ function filterDiff(diff, options) {
4566
4869
 
4567
4870
  // src/migration/diff/index.ts
4568
4871
  function hasChanges(modification) {
4569
- 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;
4872
+ 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;
4570
4873
  }
4571
4874
  function aggregateChanges(currentSchema, previousSnapshot, config) {
4572
4875
  const collectionIdToName = /* @__PURE__ */ new Map();
@@ -4781,6 +5084,13 @@ function formatValue(value) {
4781
5084
  }
4782
5085
  return String(value);
4783
5086
  }
5087
+ function formatSqlTemplate(query, indent = " ") {
5088
+ const escaped = query.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
5089
+ const body = escaped.split("\n").map((line) => line.trim() === "" ? "" : `${indent}${line}`).join("\n");
5090
+ return `\`
5091
+ ${body}
5092
+ ${indent.slice(0, -2)}\``;
5093
+ }
4784
5094
  function getFieldConstructorName(fieldType) {
4785
5095
  const constructorMap = {
4786
5096
  text: "TextField",
@@ -4819,6 +5129,36 @@ function getSystemFields() {
4819
5129
  }
4820
5130
  ];
4821
5131
  }
5132
+ function getSystemTimestampFields() {
5133
+ return [
5134
+ {
5135
+ name: "created",
5136
+ id: "autodate2990389176",
5137
+ type: "autodate",
5138
+ required: false,
5139
+ options: {
5140
+ hidden: false,
5141
+ onCreate: true,
5142
+ onUpdate: false,
5143
+ presentable: false,
5144
+ system: true
5145
+ }
5146
+ },
5147
+ {
5148
+ name: "updated",
5149
+ id: "autodate3332085495",
5150
+ type: "autodate",
5151
+ required: false,
5152
+ options: {
5153
+ hidden: false,
5154
+ onCreate: true,
5155
+ onUpdate: true,
5156
+ presentable: false,
5157
+ system: true
5158
+ }
5159
+ }
5160
+ ];
5161
+ }
4822
5162
  function getAuthSystemFields() {
4823
5163
  return [
4824
5164
  {
@@ -5111,6 +5451,15 @@ function generateCollectionRules(rules, collectionType = "base") {
5111
5451
  if (!rules) {
5112
5452
  return "";
5113
5453
  }
5454
+ if (collectionType === "view") {
5455
+ return [
5456
+ `"listRule": ${formatValue(rules.listRule ?? null)}`,
5457
+ `"viewRule": ${formatValue(rules.viewRule ?? null)}`,
5458
+ `"createRule": null`,
5459
+ `"updateRule": null`,
5460
+ `"deleteRule": null`
5461
+ ].join(",\n ");
5462
+ }
5114
5463
  const parts = [];
5115
5464
  if (rules.listRule !== void 0) {
5116
5465
  parts.push(`"listRule": ${formatValue(rules.listRule)}`);
@@ -5136,6 +5485,15 @@ function generateCollectionPermissions(permissions, collectionType = "base") {
5136
5485
  if (!permissions) {
5137
5486
  return "";
5138
5487
  }
5488
+ if (collectionType === "view") {
5489
+ return [
5490
+ `"listRule": ${formatValue(permissions.listRule ?? null)}`,
5491
+ `"viewRule": ${formatValue(permissions.viewRule ?? null)}`,
5492
+ `"createRule": null`,
5493
+ `"updateRule": null`,
5494
+ `"deleteRule": null`
5495
+ ].join(",\n ");
5496
+ }
5139
5497
  const parts = [];
5140
5498
  if (permissions.listRule !== void 0) {
5141
5499
  parts.push(`"listRule": ${formatValue(permissions.listRule)}`);
@@ -5173,6 +5531,28 @@ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, i
5173
5531
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
5174
5532
  return lines.join("\n");
5175
5533
  }
5534
+ function generateViewQueryUpdate(collectionName, viewQuery, varName, isLast = false, collectionIdMap) {
5535
+ const lines = [];
5536
+ const collectionVar = varName || `collection_${collectionName}_viewQuery`;
5537
+ lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
5538
+ lines.push(` unmarshal({`);
5539
+ lines.push(` "viewQuery": ${formatSqlTemplate(viewQuery, " ")},`);
5540
+ lines.push(` }, ${collectionVar})`);
5541
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
5542
+ return lines.join("\n");
5543
+ }
5544
+ function generateGroupedRuleUpdates(collectionName, entries, varSuffix, isLast = false, collectionIdMap) {
5545
+ const collectionVar = `collection_${collectionName}_${varSuffix}`;
5546
+ const lines = [];
5547
+ lines.push(` const ${collectionVar} = ${generateFindCollectionCode(collectionName, collectionIdMap)};`);
5548
+ lines.push(` unmarshal({`);
5549
+ for (const entry of entries) {
5550
+ lines.push(` "${entry.ruleType}": ${formatValue(entry.value)},`);
5551
+ }
5552
+ lines.push(` }, ${collectionVar})`);
5553
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
5554
+ return lines.join("\n");
5555
+ }
5176
5556
 
5177
5557
  // src/migration/generator/collections.ts
5178
5558
  function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
@@ -5191,16 +5571,24 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
5191
5571
  } else if (rulesCode) {
5192
5572
  lines.push(` ${rulesCode},`);
5193
5573
  }
5574
+ if (collection.type === "view") {
5575
+ lines.push(` "viewQuery": ${formatSqlTemplate(collection.viewQuery ?? "")},`);
5576
+ lines.push(` });`);
5577
+ lines.push(``);
5578
+ lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
5579
+ return lines.join("\n");
5580
+ }
5194
5581
  const systemFieldNames = ["created", "updated", "id"];
5195
5582
  if (collection.type === "auth") {
5196
5583
  systemFieldNames.push(...getAuthSystemFields().map((f) => f.name));
5197
5584
  }
5198
5585
  const userFields = collection.fields.filter((f) => !systemFieldNames.includes(f.name));
5199
- const allFields = [...getSystemFields().filter((f) => f.name === "id")];
5586
+ const allFields = [...getSystemFields()];
5200
5587
  if (collection.type === "auth") {
5201
5588
  allFields.push(...getAuthSystemFields());
5202
5589
  }
5203
5590
  allFields.push(...userFields);
5591
+ allFields.push(...getSystemTimestampFields());
5204
5592
  lines.push(` "fields": ${generateFieldsArray(allFields, collectionIdMap)},`);
5205
5593
  let allIndexes = [...collection.indexes || []];
5206
5594
  if (collection.type === "auth") {
@@ -5230,7 +5618,21 @@ function generateOperationUpMigration(operation, collectionIdMap) {
5230
5618
  const modification = operation.modifications;
5231
5619
  const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
5232
5620
  let operationCount = 0;
5233
- const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
5621
+ 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);
5622
+ if (modification.viewQueryUpdate) {
5623
+ operationCount++;
5624
+ const isLast = operationCount === totalOperations;
5625
+ lines.push(
5626
+ generateViewQueryUpdate(
5627
+ collectionName,
5628
+ modification.viewQueryUpdate.newValue,
5629
+ void 0,
5630
+ isLast,
5631
+ collectionIdMap
5632
+ )
5633
+ );
5634
+ if (!isLast) lines.push("");
5635
+ }
5234
5636
  for (let i = 0; i < modification.fieldsToAdd.length; i++) {
5235
5637
  const field = modification.fieldsToAdd[i];
5236
5638
  operationCount++;
@@ -5270,23 +5672,29 @@ function generateOperationUpMigration(operation, collectionIdMap) {
5270
5672
  if (!isLast) lines.push("");
5271
5673
  }
5272
5674
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
5273
- for (const permission of modification.permissionsToUpdate) {
5274
- operationCount++;
5675
+ operationCount++;
5676
+ const isLast = operationCount === totalOperations;
5677
+ if (modification.permissionsToUpdate.length >= 2) {
5678
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.newValue }));
5679
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", isLast, collectionIdMap));
5680
+ } else {
5681
+ const permission = modification.permissionsToUpdate[0];
5275
5682
  const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
5276
- const isLast = operationCount === totalOperations;
5277
- lines.push(
5278
- generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast, collectionIdMap)
5279
- );
5280
- if (!isLast) lines.push("");
5683
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast, collectionIdMap));
5281
5684
  }
5685
+ if (!isLast) lines.push("");
5282
5686
  } else if (modification.rulesToUpdate.length > 0) {
5283
- for (const rule of modification.rulesToUpdate) {
5284
- operationCount++;
5687
+ operationCount++;
5688
+ const isLast = operationCount === totalOperations;
5689
+ if (modification.rulesToUpdate.length >= 2) {
5690
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.newValue }));
5691
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", isLast, collectionIdMap));
5692
+ } else {
5693
+ const rule = modification.rulesToUpdate[0];
5285
5694
  const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
5286
- const isLast = operationCount === totalOperations;
5287
5695
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast, collectionIdMap));
5288
- if (!isLast) lines.push("");
5289
5696
  }
5697
+ if (!isLast) lines.push("");
5290
5698
  }
5291
5699
  } else if (operation.type === "delete") {
5292
5700
  const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
@@ -5325,25 +5733,45 @@ function generateOperationDownMigration(operation, collectionIdMap) {
5325
5733
  const modification = operation.modifications;
5326
5734
  const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
5327
5735
  let operationCount = 0;
5328
- const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
5736
+ 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);
5737
+ if (modification.viewQueryUpdate) {
5738
+ operationCount++;
5739
+ const isLast = operationCount === totalOperations;
5740
+ lines.push(
5741
+ generateViewQueryUpdate(
5742
+ collectionName,
5743
+ modification.viewQueryUpdate.oldValue ?? "",
5744
+ `collection_${collectionName}_revert_viewQuery`,
5745
+ isLast,
5746
+ collectionIdMap
5747
+ )
5748
+ );
5749
+ if (!isLast) lines.push("");
5750
+ }
5329
5751
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
5330
- for (const permission of modification.permissionsToUpdate) {
5331
- operationCount++;
5752
+ operationCount++;
5753
+ const isLast = operationCount === totalOperations;
5754
+ if (modification.permissionsToUpdate.length >= 2) {
5755
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.oldValue }));
5756
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", isLast, collectionIdMap));
5757
+ } else {
5758
+ const permission = modification.permissionsToUpdate[0];
5332
5759
  const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
5333
- const isLast = operationCount === totalOperations;
5334
- lines.push(
5335
- generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast, collectionIdMap)
5336
- );
5337
- if (!isLast) lines.push("");
5760
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast, collectionIdMap));
5338
5761
  }
5762
+ if (!isLast) lines.push("");
5339
5763
  } else if (modification.rulesToUpdate.length > 0) {
5340
- for (const rule of modification.rulesToUpdate) {
5341
- operationCount++;
5764
+ operationCount++;
5765
+ const isLast = operationCount === totalOperations;
5766
+ if (modification.rulesToUpdate.length >= 2) {
5767
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.oldValue }));
5768
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", isLast, collectionIdMap));
5769
+ } else {
5770
+ const rule = modification.rulesToUpdate[0];
5342
5771
  const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
5343
- const isLast = operationCount === totalOperations;
5344
5772
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast, collectionIdMap));
5345
- if (!isLast) lines.push("");
5346
5773
  }
5774
+ if (!isLast) lines.push("");
5347
5775
  }
5348
5776
  for (let i = 0; i < modification.indexesToRemove.length; i++) {
5349
5777
  operationCount++;
@@ -5450,6 +5878,19 @@ function generateUpMigration(diff) {
5450
5878
  lines.push(` // Modify existing collections`);
5451
5879
  for (const modification of diff.collectionsToModify) {
5452
5880
  const collectionName = modification.collection;
5881
+ if (modification.viewQueryUpdate) {
5882
+ lines.push(` // Update the view query of ${collectionName}`);
5883
+ lines.push(
5884
+ generateViewQueryUpdate(
5885
+ collectionName,
5886
+ modification.viewQueryUpdate.newValue,
5887
+ void 0,
5888
+ false,
5889
+ collectionIdMap
5890
+ )
5891
+ );
5892
+ lines.push(``);
5893
+ }
5453
5894
  if (modification.fieldsToAdd.length > 0) {
5454
5895
  lines.push(` // Add fields to ${collectionName}`);
5455
5896
  for (let i = 0; i < modification.fieldsToAdd.length; i++) {
@@ -5495,20 +5936,26 @@ function generateUpMigration(diff) {
5495
5936
  }
5496
5937
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
5497
5938
  lines.push(` // Update permissions for ${collectionName}`);
5498
- for (const permission of modification.permissionsToUpdate) {
5939
+ if (modification.permissionsToUpdate.length >= 2) {
5940
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.newValue }));
5941
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", false, collectionIdMap));
5942
+ } else {
5943
+ const permission = modification.permissionsToUpdate[0];
5499
5944
  const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
5500
- lines.push(
5501
- generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, false, collectionIdMap)
5502
- );
5503
- lines.push(``);
5945
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, false, collectionIdMap));
5504
5946
  }
5947
+ lines.push(``);
5505
5948
  } else if (modification.rulesToUpdate.length > 0) {
5506
5949
  lines.push(` // Update rules for ${collectionName}`);
5507
- for (const rule of modification.rulesToUpdate) {
5950
+ if (modification.rulesToUpdate.length >= 2) {
5951
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.newValue }));
5952
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "rules", false, collectionIdMap));
5953
+ } else {
5954
+ const rule = modification.rulesToUpdate[0];
5508
5955
  const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
5509
5956
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, false, collectionIdMap));
5510
- lines.push(``);
5511
5957
  }
5958
+ lines.push(``);
5512
5959
  }
5513
5960
  }
5514
5961
  }
@@ -5580,22 +6027,41 @@ function generateDownMigration(diff) {
5580
6027
  lines.push(` // Revert modifications`);
5581
6028
  for (const modification of diff.collectionsToModify) {
5582
6029
  const collectionName = modification.collection;
6030
+ if (modification.viewQueryUpdate) {
6031
+ lines.push(` // Restore the view query of ${collectionName}`);
6032
+ lines.push(
6033
+ generateViewQueryUpdate(
6034
+ collectionName,
6035
+ modification.viewQueryUpdate.oldValue ?? "",
6036
+ `collection_${collectionName}_revert_viewQuery`,
6037
+ false,
6038
+ collectionIdMap
6039
+ )
6040
+ );
6041
+ lines.push(``);
6042
+ }
5583
6043
  if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
5584
6044
  lines.push(` // Revert permissions for ${collectionName}`);
5585
- for (const permission of modification.permissionsToUpdate) {
6045
+ if (modification.permissionsToUpdate.length >= 2) {
6046
+ const entries = modification.permissionsToUpdate.map((p) => ({ ruleType: p.ruleType, value: p.oldValue }));
6047
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", false, collectionIdMap));
6048
+ } else {
6049
+ const permission = modification.permissionsToUpdate[0];
5586
6050
  const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
5587
- lines.push(
5588
- generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, false, collectionIdMap)
5589
- );
5590
- lines.push(``);
6051
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, false, collectionIdMap));
5591
6052
  }
6053
+ lines.push(``);
5592
6054
  } else if (modification.rulesToUpdate.length > 0) {
5593
6055
  lines.push(` // Revert rules for ${collectionName}`);
5594
- for (const rule of modification.rulesToUpdate) {
6056
+ if (modification.rulesToUpdate.length >= 2) {
6057
+ const entries = modification.rulesToUpdate.map((r) => ({ ruleType: r.ruleType, value: r.oldValue }));
6058
+ lines.push(generateGroupedRuleUpdates(collectionName, entries, "revert_rules", false, collectionIdMap));
6059
+ } else {
6060
+ const rule = modification.rulesToUpdate[0];
5595
6061
  const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
5596
6062
  lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, false, collectionIdMap));
5597
- lines.push(``);
5598
6063
  }
6064
+ lines.push(``);
5599
6065
  }
5600
6066
  if (modification.indexesToRemove.length > 0) {
5601
6067
  lines.push(` // Restore indexes to ${collectionName}`);
@@ -5925,6 +6391,9 @@ var MigrationGenerator = class {
5925
6391
  function detectCollectionDeletions(diff) {
5926
6392
  const changes = [];
5927
6393
  for (const collection of diff.collectionsToDelete) {
6394
+ if (collection.type === "view") {
6395
+ continue;
6396
+ }
5928
6397
  changes.push({
5929
6398
  type: "collection_deletion" /* COLLECTION_DELETION */,
5930
6399
  description: `Delete collection: ${collection.name}`,
@@ -6339,7 +6808,11 @@ function formatChangeSummary(diff) {
6339
6808
  lines.push(chalk.green.bold(`\u2713 ${totalCollectionsToCreate} collection(s) to create:`));
6340
6809
  for (const collection of diff.collectionsToCreate) {
6341
6810
  lines.push(chalk.green(` + ${collection.name} (${collection.type})`));
6342
- lines.push(chalk.gray(` ${collection.fields.length} field(s)`));
6811
+ if (collection.type === "view") {
6812
+ lines.push(chalk.gray(` fields derived from the view query`));
6813
+ } else {
6814
+ lines.push(chalk.gray(` ${collection.fields.length} field(s)`));
6815
+ }
6343
6816
  }
6344
6817
  lines.push("");
6345
6818
  }
@@ -6384,6 +6857,9 @@ function formatChangeSummary(diff) {
6384
6857
  if (modification.rulesToUpdate.length > 0) {
6385
6858
  lines.push(chalk.yellow(` ~ ${modification.rulesToUpdate.length} rule(s) to update`));
6386
6859
  }
6860
+ if (modification.viewQueryUpdate) {
6861
+ lines.push(chalk.yellow(` ~ view query to update`));
6862
+ }
6387
6863
  lines.push("");
6388
6864
  }
6389
6865
  }
@@ -6806,6 +7282,6 @@ async function executeStatus(options) {
6806
7282
  }
6807
7283
  }
6808
7284
 
6809
- export { AutodateField, BaseMutator, BoolField, CLIUsageError, ConfigurationError, DateField, DiffEngine, EditorField, EmailField, FIELD_METADATA_KEY, FIELD_TYPE_INFO, FileField, FileSystemError, FilesField, GeoPointField, JSONField, MigrationError, MigrationGenerationError, MigrationGenerator, MultiSelectField, NumberField, POCKETBASE_FIELD_TYPES, PermissionTemplates, RelationField, RelationsField, SchemaAnalyzer, SchemaParsingError, SelectField, SingleSelectField, SnapshotError, SnapshotManager, StatusEnum, TextField, URLField, aggregateChanges, baseImageFileSchema, baseSchema, baseSchemaWithTimestamps, buildFieldDefinition, buildSchemaDefinition, categorizeChangesBySeverity, compare, compareFieldConstraints, compareFieldOptions, compareFieldTypes, comparePermissions, compareRelationConfigurations, convertPocketBaseMigration, convertZodSchemaToCollectionSchema, createMigrationFileStructure, createPermissions, defineCollection, detectDestructiveChanges, detectDestructiveChanges2 as detectDestructiveChangesValidation, detectFieldChanges, discoverSchemaFiles, extractComprehensiveFieldOptions, extractFieldDefinitions, extractFieldMetadata, extractFieldOptions, extractIndexes, extractRelationMetadata, extractSchemaDefinitions, filterDiff, filterSystemCollections, findLatestSnapshot, findNewCollections, findNewFields, findRemovedCollections, findRemovedFields, formatChangeSummary, formatDestructiveChanges, generate, generateChangeSummary, generateCollectionCreation, generateCollectionPermissions, generateCollectionRules, generateDownMigration, generateFieldAddition, generateFieldDefinitionObject, generateFieldDeletion, generateFieldModification, generateFieldsArray, generateIndexesArray, executeGenerate as generateMigration, generateMigrationDescription, generateMigrationFilename, generatePermissionUpdate, generateTimestamp, generateUpMigration, getArrayElementType, getCollectionNameFromFile, getDefaultValue, getFieldTypeInfo, getMaxSelect, executeStatus as getMigrationStatus, getMinSelect, getSnapshotPath, getSnapshotVersion, getUsersSystemFields, hasDestructiveChanges, importSchemaModule, inputImageFileSchema, isArrayType, isAuthCollection, isFieldRequired, isGeoPointType, isMultipleRelationField, isPermissionSchema, isRelationField, isSingleRelationField, isSystemCollection, isTemplateConfig, loadBaseMigration, loadConfig, loadSnapshot, loadSnapshotIfExists, loadSnapshotWithMigrations, logError, logInfo, logSection, logSuccess, logWarning, mapZodArrayType, mapZodBooleanType, mapZodDateType, mapZodEnumType, mapZodNumberType, mapZodRecordType, mapZodStringType, mapZodTypeToPocketBase, matchCollectionsByName, matchFieldsByName, mergePermissions, mergeSnapshots, omitImageFilesSchema, parseSchemaFiles, pluralize, requiresForceFlag, requiresForceFlag2 as requiresForceFlagValidation, resolveTargetCollection, resolveTemplate, saveSnapshot, selectSchemaForCollection, singularize, snapshotExists, summarizeDestructiveChanges, toCollectionName, unwrapZodType, validatePermissionConfig, validateRuleExpression, validateSnapshot, withIndexes, withPermissions, withProgress, writeMigrationFile };
7285
+ export { AutodateField, BaseMutator, BoolField, CLIUsageError, ConfigurationError, DateField, DiffEngine, EditorField, EmailField, FIELD_METADATA_KEY, FIELD_TYPE_INFO, FileField, FileSystemError, FilesField, GeoPointField, JSONField, MigrationError, MigrationGenerationError, MigrationGenerator, MultiSelectField, NumberField, POCKETBASE_FIELD_TYPES, PermissionTemplates, RelationField, RelationsField, SchemaAnalyzer, SchemaParsingError, SelectField, SingleSelectField, SnapshotError, SnapshotManager, StatusEnum, TextField, URLField, aggregateChanges, baseImageFileSchema, baseSchema, baseSchemaWithTimestamps, buildFieldDefinition, buildSchemaDefinition, categorizeChangesBySeverity, compare, compareFieldConstraints, compareFieldOptions, compareFieldTypes, comparePermissions, compareRelationConfigurations, convertPocketBaseMigration, convertZodSchemaToCollectionSchema, createMigrationFileStructure, createPermissions, dedentSql, defineCollection, defineView, detectDestructiveChanges, detectDestructiveChanges2 as detectDestructiveChangesValidation, detectFieldChanges, discoverSchemaFiles, extractComprehensiveFieldOptions, extractFieldDefinitions, extractFieldMetadata, extractFieldOptions, extractIndexes, extractRelationMetadata, extractSchemaDefinitions, filterDiff, filterSystemCollections, findLatestSnapshot, findNewCollections, findNewFields, findRemovedCollections, findRemovedFields, formatChangeSummary, formatDestructiveChanges, generate, generateChangeSummary, generateCollectionCreation, generateCollectionPermissions, generateCollectionRules, generateDownMigration, generateFieldAddition, generateFieldDefinitionObject, generateFieldDeletion, generateFieldModification, generateFieldsArray, generateIndexesArray, executeGenerate as generateMigration, generateMigrationDescription, generateMigrationFilename, generatePermissionUpdate, generateTimestamp, generateUpMigration, getArrayElementType, getCollectionNameFromFile, getDefaultValue, getFieldTypeInfo, getMaxSelect, executeStatus as getMigrationStatus, getMinSelect, getSnapshotPath, getSnapshotVersion, getUsersSystemFields, hasDestructiveChanges, importSchemaModule, inputImageFileSchema, isArrayType, isAuthCollection, isFieldRequired, isGeoPointType, isMultipleRelationField, isPermissionSchema, isRelationField, isSingleRelationField, isSystemCollection, isTemplateConfig, loadBaseMigration, loadConfig, loadSnapshot, loadSnapshotIfExists, loadSnapshotWithMigrations, logError, logInfo, logSection, logSuccess, logWarning, mapZodArrayType, mapZodBooleanType, mapZodDateType, mapZodEnumType, mapZodNumberType, mapZodRecordType, mapZodStringType, mapZodTypeToPocketBase, matchCollectionsByName, matchFieldsByName, mergePermissions, mergeSnapshots, omitImageFilesSchema, parseSchemaFiles, pluralize, requiresForceFlag, requiresForceFlag2 as requiresForceFlagValidation, resolveTargetCollection, resolveTemplate, saveSnapshot, selectSchemaForCollection, singularize, snapshotExists, sql, summarizeDestructiveChanges, toCollectionName, unwrapZodType, validatePermissionConfig, validateRuleExpression, validateSnapshot, validateViewQuery, withIndexes, withPermissions, withProgress, writeMigrationFile };
6810
7286
  //# sourceMappingURL=server.js.map
6811
7287
  //# sourceMappingURL=server.js.map