pocketbase-zod-schema 0.2.5 → 0.3.1

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 (68) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli/index.cjs +497 -298
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.d.cts +2 -2
  5. package/dist/cli/index.d.ts +2 -2
  6. package/dist/cli/index.js +497 -298
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/migrate.cjs +497 -298
  9. package/dist/cli/migrate.cjs.map +1 -1
  10. package/dist/cli/migrate.js +497 -298
  11. package/dist/cli/migrate.js.map +1 -1
  12. package/dist/cli/utils/index.d.cts +2 -2
  13. package/dist/cli/utils/index.d.ts +2 -2
  14. package/dist/{fields-YjcpBXVp.d.cts → fields-RVj26U-O.d.cts} +17 -0
  15. package/dist/{fields-YjcpBXVp.d.ts → fields-RVj26U-O.d.ts} +17 -0
  16. package/dist/index.cjs +575 -155
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -4
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +576 -144
  21. package/dist/index.js.map +1 -1
  22. package/dist/migration/analyzer.cjs +12 -2
  23. package/dist/migration/analyzer.cjs.map +1 -1
  24. package/dist/migration/analyzer.d.cts +2 -2
  25. package/dist/migration/analyzer.d.ts +2 -2
  26. package/dist/migration/analyzer.js +12 -2
  27. package/dist/migration/analyzer.js.map +1 -1
  28. package/dist/migration/diff.cjs +150 -24
  29. package/dist/migration/diff.cjs.map +1 -1
  30. package/dist/migration/diff.d.cts +4 -4
  31. package/dist/migration/diff.d.ts +4 -4
  32. package/dist/migration/diff.js +150 -24
  33. package/dist/migration/diff.js.map +1 -1
  34. package/dist/migration/generator.cjs +360 -46
  35. package/dist/migration/generator.cjs.map +1 -1
  36. package/dist/migration/generator.d.cts +59 -12
  37. package/dist/migration/generator.d.ts +59 -12
  38. package/dist/migration/generator.js +356 -47
  39. package/dist/migration/generator.js.map +1 -1
  40. package/dist/migration/index.cjs +561 -90
  41. package/dist/migration/index.cjs.map +1 -1
  42. package/dist/migration/index.d.cts +3 -3
  43. package/dist/migration/index.d.ts +3 -3
  44. package/dist/migration/index.js +561 -90
  45. package/dist/migration/index.js.map +1 -1
  46. package/dist/migration/snapshot.cjs +51 -18
  47. package/dist/migration/snapshot.cjs.map +1 -1
  48. package/dist/migration/snapshot.d.cts +2 -2
  49. package/dist/migration/snapshot.d.ts +2 -2
  50. package/dist/migration/snapshot.js +51 -18
  51. package/dist/migration/snapshot.js.map +1 -1
  52. package/dist/migration/utils/index.cjs +66 -0
  53. package/dist/migration/utils/index.cjs.map +1 -1
  54. package/dist/migration/utils/index.d.cts +39 -202
  55. package/dist/migration/utils/index.d.ts +39 -202
  56. package/dist/migration/utils/index.js +65 -1
  57. package/dist/migration/utils/index.js.map +1 -1
  58. package/dist/schema.cjs +0 -61
  59. package/dist/schema.cjs.map +1 -1
  60. package/dist/schema.d.cts +2 -86
  61. package/dist/schema.d.ts +2 -86
  62. package/dist/schema.js +1 -50
  63. package/dist/schema.js.map +1 -1
  64. package/dist/type-mapper-CZzVeDj7.d.ts +208 -0
  65. package/dist/type-mapper-DaBe-1ph.d.cts +208 -0
  66. package/dist/{types-LFBGHl9Y.d.ts → types-CUVzgZ9k.d.ts} +33 -2
  67. package/dist/{types-mhQXWNi3.d.cts → types-D-Fsdn_O.d.cts} +33 -2
  68. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  import * as fs3 from 'fs';
2
2
  import * as path from 'path';
3
3
  import { z } from 'zod';
4
+ import { randomBytes } from 'crypto';
4
5
 
5
6
  // src/migration/analyzer.ts
6
7
  ({
@@ -1558,12 +1559,22 @@ function isAuthCollection(fields) {
1558
1559
  function buildFieldDefinition(fieldName, zodType) {
1559
1560
  const fieldMetadata = extractFieldMetadata(zodType.description);
1560
1561
  if (fieldMetadata) {
1561
- const required2 = isFieldRequired(zodType);
1562
+ let required2;
1563
+ if (fieldMetadata.type === "number") {
1564
+ if (fieldMetadata.options?.required !== void 0) {
1565
+ required2 = fieldMetadata.options.required;
1566
+ } else {
1567
+ required2 = false;
1568
+ }
1569
+ } else {
1570
+ required2 = isFieldRequired(zodType);
1571
+ }
1572
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1562
1573
  const fieldDef2 = {
1563
1574
  name: fieldName,
1564
1575
  type: fieldMetadata.type,
1565
1576
  required: required2,
1566
- options: fieldMetadata.options
1577
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1567
1578
  };
1568
1579
  if (fieldMetadata.type === "relation") {
1569
1580
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -1762,7 +1773,7 @@ var SchemaAnalyzer = class {
1762
1773
  var SNAPSHOT_VERSION = "1.0.0";
1763
1774
  function resolveCollectionIdToName(collectionId) {
1764
1775
  if (collectionId === "_pb_users_auth_") {
1765
- return "Users";
1776
+ return "users";
1766
1777
  }
1767
1778
  const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1768
1779
  if (nameMatch) {
@@ -1770,6 +1781,39 @@ function resolveCollectionIdToName(collectionId) {
1770
1781
  }
1771
1782
  return collectionId;
1772
1783
  }
1784
+ function extractFieldOptions2(pbField) {
1785
+ const options = {};
1786
+ if (pbField.options && typeof pbField.options === "object") {
1787
+ Object.assign(options, pbField.options);
1788
+ }
1789
+ const directOptionKeys = [
1790
+ "min",
1791
+ "max",
1792
+ "pattern",
1793
+ "noDecimal",
1794
+ // text/number fields
1795
+ "values",
1796
+ "maxSelect",
1797
+ // select fields
1798
+ "mimeTypes",
1799
+ "maxSize",
1800
+ "thumbs",
1801
+ "protected",
1802
+ // file fields
1803
+ "onCreate",
1804
+ "onUpdate",
1805
+ // autodate fields
1806
+ "exceptDomains",
1807
+ "onlyDomains"
1808
+ // email/url fields
1809
+ ];
1810
+ for (const key of directOptionKeys) {
1811
+ if (pbField[key] !== void 0) {
1812
+ options[key] = pbField[key];
1813
+ }
1814
+ }
1815
+ return options;
1816
+ }
1773
1817
  function convertPocketBaseCollection(pbCollection) {
1774
1818
  const fields = [];
1775
1819
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
@@ -1787,23 +1831,19 @@ function convertPocketBaseCollection(pbCollection) {
1787
1831
  type: pbField.type,
1788
1832
  required: pbField.required || false
1789
1833
  };
1790
- field.options = pbField.options ? { ...pbField.options } : {};
1791
- if (pbField.type === "select") {
1792
- if (pbField.values && Array.isArray(pbField.values)) {
1793
- field.options.values = pbField.values;
1794
- } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
1795
- field.options.values = pbField.options.values;
1796
- }
1797
- }
1834
+ field.options = extractFieldOptions2(pbField);
1798
1835
  if (pbField.type === "relation") {
1799
1836
  const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
1800
- const collectionName = resolveCollectionIdToName(collectionId);
1837
+ const collectionName = resolveCollectionIdToName(collectionId || "");
1801
1838
  field.relation = {
1802
1839
  collection: collectionName,
1803
1840
  cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
1804
1841
  maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
1805
1842
  minSelect: pbField.minSelect ?? pbField.options?.minSelect
1806
1843
  };
1844
+ delete field.options.maxSelect;
1845
+ delete field.options.minSelect;
1846
+ delete field.options.cascadeDelete;
1807
1847
  }
1808
1848
  const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
1809
1849
  if (Object.keys(field.options).length === 0) {
@@ -1817,17 +1857,21 @@ function convertPocketBaseCollection(pbCollection) {
1817
1857
  type: pbCollection.type || "base",
1818
1858
  fields
1819
1859
  };
1860
+ if (pbCollection.id) {
1861
+ schema.id = pbCollection.id;
1862
+ }
1820
1863
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
1821
1864
  schema.indexes = pbCollection.indexes;
1822
1865
  }
1823
- const rules = {};
1824
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1825
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1826
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1827
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1828
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1829
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1830
- if (Object.keys(rules).length > 0) {
1866
+ const hasAnyRule = pbCollection.listRule !== void 0 || pbCollection.viewRule !== void 0 || pbCollection.createRule !== void 0 || pbCollection.updateRule !== void 0 || pbCollection.deleteRule !== void 0 || pbCollection.manageRule !== void 0;
1867
+ if (hasAnyRule) {
1868
+ const rules = {};
1869
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1870
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1871
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1872
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1873
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1874
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1831
1875
  schema.rules = rules;
1832
1876
  schema.permissions = { ...rules };
1833
1877
  }
@@ -2559,6 +2603,67 @@ var SnapshotManager = class {
2559
2603
  return validateSnapshot(snapshot);
2560
2604
  }
2561
2605
  };
2606
+ function generateCollectionId() {
2607
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
2608
+ const idLength = 15;
2609
+ const bytes = randomBytes(idLength);
2610
+ let id = "pb_";
2611
+ for (let i = 0; i < idLength; i++) {
2612
+ const index = bytes[i] % chars.length;
2613
+ id += chars[index];
2614
+ }
2615
+ return id;
2616
+ }
2617
+ var CollectionIdRegistry = class {
2618
+ ids;
2619
+ constructor() {
2620
+ this.ids = /* @__PURE__ */ new Set();
2621
+ }
2622
+ /**
2623
+ * Generates a unique collection ID for a given collection name
2624
+ * Retries up to 10 times if collision occurs (extremely rare)
2625
+ * Special case: returns "_pb_users_auth_" for users collection
2626
+ *
2627
+ * @param collectionName - The name of the collection (optional)
2628
+ * @returns A unique collection ID
2629
+ * @throws Error if unable to generate unique ID after max attempts
2630
+ */
2631
+ generate(collectionName) {
2632
+ if (collectionName && collectionName.toLowerCase() === "users") {
2633
+ const usersId = "_pb_users_auth_";
2634
+ if (!this.has(usersId)) {
2635
+ this.register(usersId);
2636
+ }
2637
+ return usersId;
2638
+ }
2639
+ const maxAttempts = 10;
2640
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
2641
+ const id = generateCollectionId();
2642
+ if (!this.has(id)) {
2643
+ this.register(id);
2644
+ return id;
2645
+ }
2646
+ }
2647
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
2648
+ }
2649
+ /**
2650
+ * Checks if an ID has already been registered
2651
+ *
2652
+ * @param id - The collection ID to check
2653
+ * @returns True if the ID exists in the registry
2654
+ */
2655
+ has(id) {
2656
+ return this.ids.has(id);
2657
+ }
2658
+ /**
2659
+ * Registers a collection ID in the registry
2660
+ *
2661
+ * @param id - The collection ID to register
2662
+ */
2663
+ register(id) {
2664
+ this.ids.add(id);
2665
+ }
2666
+ };
2562
2667
 
2563
2668
  // src/migration/diff.ts
2564
2669
  var DEFAULT_CONFIG3 = {
@@ -2712,18 +2817,49 @@ function compareFieldConstraints(currentField, previousField) {
2712
2817
  }
2713
2818
  return changes;
2714
2819
  }
2820
+ function normalizeOptionValue(key, value, fieldType) {
2821
+ if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
2822
+ return void 0;
2823
+ }
2824
+ if (key === "maxSize" && value === 0 && fieldType === "file") {
2825
+ return void 0;
2826
+ }
2827
+ if (fieldType === "file") {
2828
+ if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
2829
+ return void 0;
2830
+ }
2831
+ if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
2832
+ return void 0;
2833
+ }
2834
+ if (key === "protected" && value === false) {
2835
+ return void 0;
2836
+ }
2837
+ }
2838
+ if (fieldType === "autodate") {
2839
+ if (key === "onCreate" && value === true) {
2840
+ return void 0;
2841
+ }
2842
+ if (key === "onUpdate" && value === false) {
2843
+ return void 0;
2844
+ }
2845
+ }
2846
+ return value;
2847
+ }
2715
2848
  function compareFieldOptions(currentField, previousField) {
2716
2849
  const changes = [];
2717
2850
  const currentOptions = currentField.options || {};
2718
2851
  const previousOptions = previousField.options || {};
2719
2852
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(currentOptions), ...Object.keys(previousOptions)]);
2853
+ const fieldType = currentField.type;
2720
2854
  for (const key of allKeys) {
2721
2855
  const currentValue = currentOptions[key];
2722
2856
  const previousValue = previousOptions[key];
2723
- if (currentValue === void 0 && previousValue === void 0) {
2857
+ const normalizedCurrent = normalizeOptionValue(key, currentValue, fieldType);
2858
+ const normalizedPrevious = normalizeOptionValue(key, previousValue, fieldType);
2859
+ if (normalizedCurrent === void 0 && normalizedPrevious === void 0) {
2724
2860
  continue;
2725
2861
  }
2726
- if (!areValuesEqual(currentValue, previousValue)) {
2862
+ if (!areValuesEqual(normalizedCurrent, normalizedPrevious)) {
2727
2863
  changes.push({
2728
2864
  property: `options.${key}`,
2729
2865
  oldValue: previousValue,
@@ -2733,7 +2869,7 @@ function compareFieldOptions(currentField, previousField) {
2733
2869
  }
2734
2870
  return changes;
2735
2871
  }
2736
- function compareRelationConfigurations(currentField, previousField) {
2872
+ function compareRelationConfigurations(currentField, previousField, collectionIdToName) {
2737
2873
  const changes = [];
2738
2874
  const currentRelation = currentField.relation;
2739
2875
  const previousRelation = previousField.relation;
@@ -2745,8 +2881,8 @@ function compareRelationConfigurations(currentField, previousField) {
2745
2881
  }
2746
2882
  const normalizeCollection = (collection) => {
2747
2883
  if (!collection) return collection;
2748
- if (collection === "_pb_users_auth_") {
2749
- return "Users";
2884
+ if (collectionIdToName && collectionIdToName.has(collection)) {
2885
+ return collectionIdToName.get(collection);
2750
2886
  }
2751
2887
  const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2752
2888
  if (nameMatch) {
@@ -2756,13 +2892,11 @@ function compareRelationConfigurations(currentField, previousField) {
2756
2892
  };
2757
2893
  const normalizedCurrent = normalizeCollection(currentRelation.collection);
2758
2894
  const normalizedPrevious = normalizeCollection(previousRelation.collection);
2759
- if (normalizedCurrent !== normalizedPrevious) {
2895
+ if (normalizedCurrent.toLowerCase() !== normalizedPrevious.toLowerCase()) {
2760
2896
  changes.push({
2761
2897
  property: "relation.collection",
2762
- oldValue: normalizedPrevious,
2763
- // Use normalized value for clarity
2764
- newValue: normalizedCurrent
2765
- // Use normalized value for clarity
2898
+ oldValue: previousRelation.collection,
2899
+ newValue: currentRelation.collection
2766
2900
  });
2767
2901
  }
2768
2902
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -2772,14 +2906,20 @@ function compareRelationConfigurations(currentField, previousField) {
2772
2906
  newValue: currentRelation.cascadeDelete
2773
2907
  });
2774
2908
  }
2775
- if (currentRelation.maxSelect !== previousRelation.maxSelect) {
2909
+ const normalizeMax = (val) => val === 1 ? null : val;
2910
+ const currentMax = normalizeMax(currentRelation.maxSelect);
2911
+ const previousMax = normalizeMax(previousRelation.maxSelect);
2912
+ if (currentMax != previousMax) {
2776
2913
  changes.push({
2777
2914
  property: "relation.maxSelect",
2778
2915
  oldValue: previousRelation.maxSelect,
2779
2916
  newValue: currentRelation.maxSelect
2780
2917
  });
2781
2918
  }
2782
- if (currentRelation.minSelect !== previousRelation.minSelect) {
2919
+ const normalizeMin = (val) => val === 0 ? null : val;
2920
+ const currentMin = normalizeMin(currentRelation.minSelect);
2921
+ const previousMin = normalizeMin(previousRelation.minSelect);
2922
+ if (currentMin != previousMin) {
2783
2923
  changes.push({
2784
2924
  property: "relation.minSelect",
2785
2925
  oldValue: previousRelation.minSelect,
@@ -2788,7 +2928,7 @@ function compareRelationConfigurations(currentField, previousField) {
2788
2928
  }
2789
2929
  return changes;
2790
2930
  }
2791
- function detectFieldChanges(currentField, previousField) {
2931
+ function detectFieldChanges(currentField, previousField, collectionIdToName) {
2792
2932
  const changes = [];
2793
2933
  const typeChange = compareFieldTypes(currentField, previousField);
2794
2934
  if (typeChange) {
@@ -2797,7 +2937,7 @@ function detectFieldChanges(currentField, previousField) {
2797
2937
  changes.push(...compareFieldConstraints(currentField, previousField));
2798
2938
  changes.push(...compareFieldOptions(currentField, previousField));
2799
2939
  if (currentField.type === "relation" && previousField.type === "relation") {
2800
- changes.push(...compareRelationConfigurations(currentField, previousField));
2940
+ changes.push(...compareRelationConfigurations(currentField, previousField, collectionIdToName));
2801
2941
  }
2802
2942
  return changes;
2803
2943
  }
@@ -2808,7 +2948,7 @@ function compareIndexes(currentIndexes = [], previousIndexes = []) {
2808
2948
  const indexesToRemove = previousIndexes.filter((idx) => !currentSet.has(idx));
2809
2949
  return { indexesToAdd, indexesToRemove };
2810
2950
  }
2811
- function compareRules(currentRules, previousRules) {
2951
+ function compareRules(currentRules, previousRules, currentPermissions, previousPermissions) {
2812
2952
  const updates = [];
2813
2953
  const ruleTypes = [
2814
2954
  "listRule",
@@ -2819,8 +2959,8 @@ function compareRules(currentRules, previousRules) {
2819
2959
  "manageRule"
2820
2960
  ];
2821
2961
  for (const ruleType of ruleTypes) {
2822
- const currentValue = currentRules?.[ruleType] ?? null;
2823
- const previousValue = previousRules?.[ruleType] ?? null;
2962
+ const currentValue = currentRules?.[ruleType] ?? currentPermissions?.[ruleType] ?? null;
2963
+ const previousValue = previousRules?.[ruleType] ?? previousPermissions?.[ruleType] ?? null;
2824
2964
  if (currentValue !== previousValue) {
2825
2965
  updates.push({
2826
2966
  ruleType,
@@ -2847,7 +2987,7 @@ function comparePermissions(currentPermissions, previousPermissions) {
2847
2987
  }
2848
2988
  return changes;
2849
2989
  }
2850
- function compareCollectionFields(currentCollection, previousCollection, config) {
2990
+ function compareCollectionFields(currentCollection, previousCollection, config, collectionIdToName) {
2851
2991
  let fieldsToAdd = findNewFields(currentCollection.fields, previousCollection.fields);
2852
2992
  const fieldsToRemove = findRemovedFields(currentCollection.fields, previousCollection.fields);
2853
2993
  const fieldsToModify = [];
@@ -2857,7 +2997,7 @@ function compareCollectionFields(currentCollection, previousCollection, config)
2857
2997
  }
2858
2998
  const matchedFields = matchFieldsByName(currentCollection.fields, previousCollection.fields);
2859
2999
  for (const [currentField, previousField] of matchedFields) {
2860
- const changes = detectFieldChanges(currentField, previousField);
3000
+ const changes = detectFieldChanges(currentField, previousField, collectionIdToName);
2861
3001
  if (changes.length > 0) {
2862
3002
  fieldsToModify.push({
2863
3003
  fieldName: currentField.name,
@@ -2869,14 +3009,20 @@ function compareCollectionFields(currentCollection, previousCollection, config)
2869
3009
  }
2870
3010
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
2871
3011
  }
2872
- function buildCollectionModification(currentCollection, previousCollection, config) {
3012
+ function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
2873
3013
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
2874
3014
  currentCollection,
2875
3015
  previousCollection,
2876
- config
3016
+ config,
3017
+ collectionIdToName
2877
3018
  );
2878
3019
  const { indexesToAdd, indexesToRemove } = compareIndexes(currentCollection.indexes, previousCollection.indexes);
2879
- const rulesToUpdate = compareRules(currentCollection.rules, previousCollection.rules);
3020
+ const rulesToUpdate = compareRules(
3021
+ currentCollection.rules,
3022
+ previousCollection.rules,
3023
+ currentCollection.permissions,
3024
+ previousCollection.permissions
3025
+ );
2880
3026
  const permissionsToUpdate = comparePermissions(currentCollection.permissions, previousCollection.permissions);
2881
3027
  return {
2882
3028
  collection: currentCollection.name,
@@ -2893,6 +3039,14 @@ function hasChanges(modification) {
2893
3039
  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;
2894
3040
  }
2895
3041
  function aggregateChanges(currentSchema, previousSnapshot, config) {
3042
+ const collectionIdToName = /* @__PURE__ */ new Map();
3043
+ if (previousSnapshot) {
3044
+ for (const [name, collection] of previousSnapshot.collections) {
3045
+ if (collection.id) {
3046
+ collectionIdToName.set(collection.id, name);
3047
+ }
3048
+ }
3049
+ }
2896
3050
  const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
2897
3051
  const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
2898
3052
  const filteredCollectionsToCreate = collectionsToCreate.filter(
@@ -2901,16 +3055,28 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
2901
3055
  const filteredCollectionsToDelete = collectionsToDelete.filter(
2902
3056
  (collection) => !isSystemCollection(collection.name, config)
2903
3057
  );
3058
+ const registry = new CollectionIdRegistry();
3059
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
3060
+ if (collection.id) {
3061
+ registry.register(collection.id);
3062
+ return collection;
3063
+ }
3064
+ const id = registry.generate(collection.name);
3065
+ return {
3066
+ ...collection,
3067
+ id
3068
+ };
3069
+ });
2904
3070
  const collectionsToModify = [];
2905
3071
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
2906
3072
  for (const [currentCollection, previousCollection] of matchedCollections) {
2907
- const modification = buildCollectionModification(currentCollection, previousCollection, config);
3073
+ const modification = buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName);
2908
3074
  if (hasChanges(modification)) {
2909
3075
  collectionsToModify.push(modification);
2910
3076
  }
2911
3077
  }
2912
3078
  return {
2913
- collectionsToCreate: filteredCollectionsToCreate,
3079
+ collectionsToCreate: collectionsWithIds,
2914
3080
  collectionsToDelete: filteredCollectionsToDelete,
2915
3081
  collectionsToModify
2916
3082
  };
@@ -3146,6 +3312,49 @@ function generateTimestamp(config) {
3146
3312
  }
3147
3313
  return Math.floor(Date.now() / 1e3).toString();
3148
3314
  }
3315
+ function splitDiffByCollection(diff, baseTimestamp) {
3316
+ const operations = [];
3317
+ let currentTimestamp = parseInt(baseTimestamp, 10);
3318
+ for (const collection of diff.collectionsToCreate) {
3319
+ operations.push({
3320
+ type: "create",
3321
+ collection,
3322
+ timestamp: currentTimestamp.toString()
3323
+ });
3324
+ currentTimestamp += 1;
3325
+ }
3326
+ for (const modification of diff.collectionsToModify) {
3327
+ operations.push({
3328
+ type: "modify",
3329
+ collection: modification.collection,
3330
+ modifications: modification,
3331
+ timestamp: currentTimestamp.toString()
3332
+ });
3333
+ currentTimestamp += 1;
3334
+ }
3335
+ for (const collection of diff.collectionsToDelete) {
3336
+ operations.push({
3337
+ type: "delete",
3338
+ collection: collection.name || collection,
3339
+ // Handle both object and string
3340
+ timestamp: currentTimestamp.toString()
3341
+ });
3342
+ currentTimestamp += 1;
3343
+ }
3344
+ return operations;
3345
+ }
3346
+ function generateCollectionMigrationFilename(operation) {
3347
+ const timestamp = operation.timestamp;
3348
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
3349
+ let collectionName;
3350
+ if (typeof operation.collection === "string") {
3351
+ collectionName = operation.collection;
3352
+ } else {
3353
+ collectionName = operation.collection.name;
3354
+ }
3355
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
3356
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
3357
+ }
3149
3358
  function generateMigrationDescription(diff) {
3150
3359
  const parts = [];
3151
3360
  if (diff.collectionsToCreate.length > 0) {
@@ -3253,14 +3462,13 @@ function formatValue(value) {
3253
3462
  return "null";
3254
3463
  }
3255
3464
  if (typeof value === "string") {
3256
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
3465
+ return JSON.stringify(value);
3257
3466
  }
3258
3467
  if (typeof value === "number" || typeof value === "boolean") {
3259
3468
  return String(value);
3260
3469
  }
3261
3470
  if (Array.isArray(value)) {
3262
- const items = value.map((v) => formatValue(v)).join(", ");
3263
- return `[${items}]`;
3471
+ return JSON.stringify(value).replace(/","/g, '", "');
3264
3472
  }
3265
3473
  if (typeof value === "object") {
3266
3474
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -3268,7 +3476,7 @@ function formatValue(value) {
3268
3476
  }
3269
3477
  return String(value);
3270
3478
  }
3271
- function generateFieldDefinitionObject(field) {
3479
+ function generateFieldDefinitionObject(field, collectionIdMap) {
3272
3480
  const parts = [];
3273
3481
  parts.push(` name: "${field.name}"`);
3274
3482
  parts.push(` type: "${field.type}"`);
@@ -3276,34 +3484,47 @@ function generateFieldDefinitionObject(field) {
3276
3484
  if (field.unique !== void 0) {
3277
3485
  parts.push(` unique: ${field.unique}`);
3278
3486
  }
3487
+ if (field.type === "select") {
3488
+ const maxSelect = field.options?.maxSelect ?? 1;
3489
+ parts.push(` maxSelect: ${maxSelect}`);
3490
+ const values = field.options?.values ?? [];
3491
+ parts.push(` values: ${formatValue(values)}`);
3492
+ }
3279
3493
  if (field.options && Object.keys(field.options).length > 0) {
3280
3494
  for (const [key, value] of Object.entries(field.options)) {
3495
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
3496
+ continue;
3497
+ }
3281
3498
  parts.push(` ${key}: ${formatValue(value)}`);
3282
3499
  }
3283
3500
  }
3284
3501
  if (field.relation) {
3285
3502
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3286
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3287
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
3288
- if (field.relation.maxSelect !== void 0) {
3289
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
3290
- }
3291
- if (field.relation.minSelect !== void 0) {
3292
- parts.push(` minSelect: ${field.relation.minSelect}`);
3293
- }
3294
- if (field.relation.cascadeDelete !== void 0) {
3295
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
3503
+ let collectionIdValue;
3504
+ if (isUsersCollection) {
3505
+ collectionIdValue = '"_pb_users_auth_"';
3506
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
3507
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
3508
+ } else {
3509
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3296
3510
  }
3511
+ parts.push(` collectionId: ${collectionIdValue}`);
3512
+ const maxSelect = field.relation.maxSelect ?? 1;
3513
+ parts.push(` maxSelect: ${maxSelect}`);
3514
+ const minSelect = field.relation.minSelect ?? null;
3515
+ parts.push(` minSelect: ${minSelect}`);
3516
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
3517
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
3297
3518
  }
3298
3519
  return ` {
3299
3520
  ${parts.join(",\n")},
3300
3521
  }`;
3301
3522
  }
3302
- function generateFieldsArray(fields) {
3523
+ function generateFieldsArray(fields, collectionIdMap) {
3303
3524
  if (fields.length === 0) {
3304
3525
  return "[]";
3305
3526
  }
3306
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
3527
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
3307
3528
  return `[
3308
3529
  ${fieldObjects.join(",\n")},
3309
3530
  ]`;
@@ -3362,7 +3583,7 @@ function generateIndexesArray(indexes) {
3362
3583
  if (!indexes || indexes.length === 0) {
3363
3584
  return "[]";
3364
3585
  }
3365
- const indexStrings = indexes.map((idx) => `"${idx}"`);
3586
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
3366
3587
  return `[
3367
3588
  ${indexStrings.join(",\n ")},
3368
3589
  ]`;
@@ -3416,9 +3637,12 @@ function getSystemFields() {
3416
3637
  }
3417
3638
  ];
3418
3639
  }
3419
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
3640
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
3420
3641
  const lines = [];
3421
3642
  lines.push(` const ${varName} = new Collection({`);
3643
+ if (collection.id) {
3644
+ lines.push(` id: ${formatValue(collection.id)},`);
3645
+ }
3422
3646
  lines.push(` name: "${collection.name}",`);
3423
3647
  lines.push(` type: "${collection.type}",`);
3424
3648
  const permissionsCode = generateCollectionPermissions(collection.permissions);
@@ -3430,7 +3654,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
3430
3654
  }
3431
3655
  const systemFields = getSystemFields();
3432
3656
  const allFields = [...systemFields, ...collection.fields];
3433
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
3657
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
3434
3658
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
3435
3659
  lines.push(` });`);
3436
3660
  lines.push(``);
@@ -3452,42 +3676,59 @@ function getFieldConstructorName(fieldType) {
3452
3676
  };
3453
3677
  return constructorMap[fieldType] || "TextField";
3454
3678
  }
3455
- function generateFieldConstructorOptions(field) {
3679
+ function generateFieldConstructorOptions(field, collectionIdMap) {
3456
3680
  const parts = [];
3457
3681
  parts.push(` name: "${field.name}"`);
3458
3682
  parts.push(` required: ${field.required}`);
3459
3683
  if (field.unique !== void 0) {
3460
3684
  parts.push(` unique: ${field.unique}`);
3461
3685
  }
3686
+ if (field.type === "select") {
3687
+ const maxSelect = field.options?.maxSelect ?? 1;
3688
+ parts.push(` maxSelect: ${maxSelect}`);
3689
+ const values = field.options?.values ?? [];
3690
+ parts.push(` values: ${formatValue(values)}`);
3691
+ }
3462
3692
  if (field.options && Object.keys(field.options).length > 0) {
3463
3693
  for (const [key, value] of Object.entries(field.options)) {
3464
- parts.push(` ${key}: ${formatValue(value)}`);
3694
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
3695
+ continue;
3696
+ }
3697
+ if (field.type === "number" && key === "noDecimal") {
3698
+ parts.push(` onlyInt: ${formatValue(value)}`);
3699
+ } else {
3700
+ parts.push(` ${key}: ${formatValue(value)}`);
3701
+ }
3465
3702
  }
3466
3703
  }
3467
3704
  if (field.relation && field.type === "relation") {
3468
3705
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3469
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3470
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
3471
- if (field.relation.maxSelect !== void 0) {
3472
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
3473
- }
3474
- if (field.relation.minSelect !== void 0) {
3475
- parts.push(` minSelect: ${field.relation.minSelect}`);
3476
- }
3477
- if (field.relation.cascadeDelete !== void 0) {
3478
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
3706
+ let collectionIdValue;
3707
+ if (isUsersCollection) {
3708
+ collectionIdValue = '"_pb_users_auth_"';
3709
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
3710
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
3711
+ } else {
3712
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3479
3713
  }
3714
+ parts.push(` collectionId: ${collectionIdValue}`);
3715
+ const maxSelect = field.relation.maxSelect ?? 1;
3716
+ parts.push(` maxSelect: ${maxSelect}`);
3717
+ const minSelect = field.relation.minSelect ?? null;
3718
+ parts.push(` minSelect: ${minSelect}`);
3719
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
3720
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
3480
3721
  }
3481
3722
  return parts.join(",\n");
3482
3723
  }
3483
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
3724
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
3484
3725
  const lines = [];
3485
3726
  const constructorName = getFieldConstructorName(field.type);
3486
3727
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
3487
3728
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3488
3729
  lines.push(``);
3489
3730
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
3490
- lines.push(generateFieldConstructorOptions(field));
3731
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
3491
3732
  lines.push(` }));`);
3492
3733
  lines.push(``);
3493
3734
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -3537,7 +3778,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
3537
3778
  const lines = [];
3538
3779
  const collectionVar = varName || `collection_${collectionName}_idx`;
3539
3780
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3540
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
3781
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
3541
3782
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3542
3783
  return lines.join("\n");
3543
3784
  }
@@ -3546,7 +3787,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
3546
3787
  const collectionVar = varName || `collection_${collectionName}_idx`;
3547
3788
  const indexVar = `${collectionVar}_indexToRemove`;
3548
3789
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3549
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
3790
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
3550
3791
  lines.push(` if (${indexVar} !== -1) {`);
3551
3792
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
3552
3793
  lines.push(` }`);
@@ -3575,16 +3816,213 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
3575
3816
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
3576
3817
  return lines.join("\n");
3577
3818
  }
3819
+ function generateOperationUpMigration(operation, collectionIdMap) {
3820
+ const lines = [];
3821
+ if (operation.type === "create") {
3822
+ const collection = operation.collection;
3823
+ const varName = `collection_${collection.name}`;
3824
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
3825
+ } else if (operation.type === "modify") {
3826
+ const modification = operation.modifications;
3827
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
3828
+ let operationCount = 0;
3829
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
3830
+ for (const field of modification.fieldsToAdd) {
3831
+ operationCount++;
3832
+ const varName = `collection_${collectionName}_add_${field.name}`;
3833
+ const isLast = operationCount === totalOperations;
3834
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
3835
+ if (!isLast) lines.push("");
3836
+ }
3837
+ for (const fieldMod of modification.fieldsToModify) {
3838
+ operationCount++;
3839
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
3840
+ const isLast = operationCount === totalOperations;
3841
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
3842
+ if (!isLast) lines.push("");
3843
+ }
3844
+ for (const field of modification.fieldsToRemove) {
3845
+ operationCount++;
3846
+ const varName = `collection_${collectionName}_remove_${field.name}`;
3847
+ const isLast = operationCount === totalOperations;
3848
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
3849
+ if (!isLast) lines.push("");
3850
+ }
3851
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
3852
+ operationCount++;
3853
+ const index = modification.indexesToAdd[i];
3854
+ const varName = `collection_${collectionName}_addidx_${i}`;
3855
+ const isLast = operationCount === totalOperations;
3856
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
3857
+ if (!isLast) lines.push("");
3858
+ }
3859
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
3860
+ operationCount++;
3861
+ const index = modification.indexesToRemove[i];
3862
+ const varName = `collection_${collectionName}_rmidx_${i}`;
3863
+ const isLast = operationCount === totalOperations;
3864
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
3865
+ if (!isLast) lines.push("");
3866
+ }
3867
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
3868
+ for (const permission of modification.permissionsToUpdate) {
3869
+ operationCount++;
3870
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
3871
+ const isLast = operationCount === totalOperations;
3872
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
3873
+ if (!isLast) lines.push("");
3874
+ }
3875
+ } else if (modification.rulesToUpdate.length > 0) {
3876
+ for (const rule of modification.rulesToUpdate) {
3877
+ operationCount++;
3878
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
3879
+ const isLast = operationCount === totalOperations;
3880
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
3881
+ if (!isLast) lines.push("");
3882
+ }
3883
+ }
3884
+ } else if (operation.type === "delete") {
3885
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
3886
+ const varName = `collection_${collectionName}`;
3887
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
3888
+ }
3889
+ let code = lines.join("\n");
3890
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3891
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3892
+ const saveMatches = [...code.matchAll(savePattern)];
3893
+ const deleteMatches = [...code.matchAll(deletePattern)];
3894
+ const allMatches = [
3895
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3896
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3897
+ ].sort((a, b) => b.index - a.index);
3898
+ if (allMatches.length > 0) {
3899
+ const lastMatch = allMatches[0];
3900
+ if (lastMatch.type === "save") {
3901
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.save(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
3902
+ } else {
3903
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.delete(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
3904
+ }
3905
+ }
3906
+ return code;
3907
+ }
3908
+ function generateOperationDownMigration(operation, collectionIdMap) {
3909
+ const lines = [];
3910
+ if (operation.type === "create") {
3911
+ const collection = operation.collection;
3912
+ const varName = `collection_${collection.name}`;
3913
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
3914
+ } else if (operation.type === "modify") {
3915
+ const modification = operation.modifications;
3916
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
3917
+ let operationCount = 0;
3918
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
3919
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
3920
+ for (const permission of modification.permissionsToUpdate) {
3921
+ operationCount++;
3922
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
3923
+ const isLast = operationCount === totalOperations;
3924
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
3925
+ if (!isLast) lines.push("");
3926
+ }
3927
+ } else if (modification.rulesToUpdate.length > 0) {
3928
+ for (const rule of modification.rulesToUpdate) {
3929
+ operationCount++;
3930
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
3931
+ const isLast = operationCount === totalOperations;
3932
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
3933
+ if (!isLast) lines.push("");
3934
+ }
3935
+ }
3936
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
3937
+ operationCount++;
3938
+ const index = modification.indexesToRemove[i];
3939
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
3940
+ const isLast = operationCount === totalOperations;
3941
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
3942
+ if (!isLast) lines.push("");
3943
+ }
3944
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
3945
+ operationCount++;
3946
+ const index = modification.indexesToAdd[i];
3947
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
3948
+ const isLast = operationCount === totalOperations;
3949
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
3950
+ if (!isLast) lines.push("");
3951
+ }
3952
+ for (const field of modification.fieldsToRemove) {
3953
+ operationCount++;
3954
+ const varName = `collection_${collectionName}_restore_${field.name}`;
3955
+ const isLast = operationCount === totalOperations;
3956
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
3957
+ if (!isLast) lines.push("");
3958
+ }
3959
+ for (const fieldMod of modification.fieldsToModify) {
3960
+ operationCount++;
3961
+ const reverseChanges = fieldMod.changes.map((change) => ({
3962
+ property: change.property,
3963
+ oldValue: change.newValue,
3964
+ newValue: change.oldValue
3965
+ }));
3966
+ const reverseMod = {
3967
+ fieldName: fieldMod.fieldName,
3968
+ currentDefinition: fieldMod.newDefinition,
3969
+ newDefinition: fieldMod.currentDefinition,
3970
+ changes: reverseChanges
3971
+ };
3972
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
3973
+ const isLast = operationCount === totalOperations;
3974
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
3975
+ if (!isLast) lines.push("");
3976
+ }
3977
+ for (const field of modification.fieldsToAdd) {
3978
+ operationCount++;
3979
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
3980
+ const isLast = operationCount === totalOperations;
3981
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
3982
+ if (!isLast) lines.push("");
3983
+ }
3984
+ } else if (operation.type === "delete") {
3985
+ const collection = operation.collection;
3986
+ if (typeof collection !== "string") {
3987
+ const varName = `collection_${collection.name}`;
3988
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
3989
+ }
3990
+ }
3991
+ let code = lines.join("\n");
3992
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3993
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3994
+ const saveMatches = [...code.matchAll(savePattern)];
3995
+ const deleteMatches = [...code.matchAll(deletePattern)];
3996
+ const allMatches = [
3997
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3998
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3999
+ ].sort((a, b) => b.index - a.index);
4000
+ if (allMatches.length > 0) {
4001
+ const lastMatch = allMatches[0];
4002
+ if (lastMatch.type === "save") {
4003
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.save(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
4004
+ } else {
4005
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.delete(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
4006
+ }
4007
+ }
4008
+ return code;
4009
+ }
3578
4010
  function generateUpMigration(diff) {
3579
4011
  const lines = [];
3580
4012
  lines.push(` // UP MIGRATION`);
3581
4013
  lines.push(``);
4014
+ const collectionIdMap = /* @__PURE__ */ new Map();
4015
+ for (const collection of diff.collectionsToCreate) {
4016
+ if (collection.id) {
4017
+ collectionIdMap.set(collection.name, collection.id);
4018
+ }
4019
+ }
3582
4020
  if (diff.collectionsToCreate.length > 0) {
3583
4021
  lines.push(` // Create new collections`);
3584
4022
  for (let i = 0; i < diff.collectionsToCreate.length; i++) {
3585
4023
  const collection = diff.collectionsToCreate[i];
3586
4024
  const varName = `collection_${collection.name}_create`;
3587
- lines.push(generateCollectionCreation(collection, varName));
4025
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
3588
4026
  lines.push(``);
3589
4027
  }
3590
4028
  }
@@ -3596,7 +4034,7 @@ function generateUpMigration(diff) {
3596
4034
  lines.push(` // Add fields to ${collectionName}`);
3597
4035
  for (const field of modification.fieldsToAdd) {
3598
4036
  const varName = `collection_${collectionName}_add_${field.name}`;
3599
- lines.push(generateFieldAddition(collectionName, field, varName));
4037
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
3600
4038
  lines.push(``);
3601
4039
  }
3602
4040
  }
@@ -3687,12 +4125,23 @@ function generateDownMigration(diff) {
3687
4125
  const lines = [];
3688
4126
  lines.push(` // DOWN MIGRATION (ROLLBACK)`);
3689
4127
  lines.push(``);
4128
+ const collectionIdMap = /* @__PURE__ */ new Map();
4129
+ for (const collection of diff.collectionsToCreate) {
4130
+ if (collection.id) {
4131
+ collectionIdMap.set(collection.name, collection.id);
4132
+ }
4133
+ }
4134
+ for (const collection of diff.collectionsToDelete) {
4135
+ if (collection.id) {
4136
+ collectionIdMap.set(collection.name, collection.id);
4137
+ }
4138
+ }
3690
4139
  if (diff.collectionsToDelete.length > 0) {
3691
4140
  lines.push(` // Recreate deleted collections`);
3692
4141
  for (let i = 0; i < diff.collectionsToDelete.length; i++) {
3693
4142
  const collection = diff.collectionsToDelete[i];
3694
4143
  const varName = `collection_${collection.name}_recreate`;
3695
- lines.push(generateCollectionCreation(collection, varName));
4144
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
3696
4145
  lines.push(``);
3697
4146
  }
3698
4147
  }
@@ -3737,7 +4186,7 @@ function generateDownMigration(diff) {
3737
4186
  lines.push(` // Restore fields to ${collectionName}`);
3738
4187
  for (const field of modification.fieldsToRemove) {
3739
4188
  const varName = `collection_${collectionName}_restore_${field.name}`;
3740
- lines.push(generateFieldAddition(collectionName, field, varName));
4189
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
3741
4190
  lines.push(``);
3742
4191
  }
3743
4192
  }
@@ -3806,12 +4255,33 @@ function generate(diff, config) {
3806
4255
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
3807
4256
  try {
3808
4257
  const migrationDir = resolveMigrationDir(normalizedConfig);
3809
- const upCode = generateUpMigration(diff);
3810
- const downCode = generateDownMigration(diff);
3811
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
3812
- const filename = generateMigrationFilename(diff, normalizedConfig);
3813
- const filePath = writeMigrationFile(migrationDir, filename, content);
3814
- return filePath;
4258
+ const hasChanges2 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
4259
+ if (!hasChanges2) {
4260
+ return [];
4261
+ }
4262
+ const collectionIdMap = /* @__PURE__ */ new Map();
4263
+ for (const collection of diff.collectionsToCreate) {
4264
+ if (collection.id) {
4265
+ collectionIdMap.set(collection.name, collection.id);
4266
+ }
4267
+ }
4268
+ for (const collection of diff.collectionsToDelete) {
4269
+ if (collection.id) {
4270
+ collectionIdMap.set(collection.name, collection.id);
4271
+ }
4272
+ }
4273
+ const baseTimestamp = generateTimestamp(normalizedConfig);
4274
+ const operations = splitDiffByCollection(diff, baseTimestamp);
4275
+ const filePaths = [];
4276
+ for (const operation of operations) {
4277
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
4278
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
4279
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
4280
+ const filename = generateCollectionMigrationFilename(operation);
4281
+ const filePath = writeMigrationFile(migrationDir, filename, content);
4282
+ filePaths.push(filePath);
4283
+ }
4284
+ return filePaths;
3815
4285
  } catch (error) {
3816
4286
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
3817
4287
  throw error;
@@ -3829,7 +4299,8 @@ var MigrationGenerator = class {
3829
4299
  this.config = mergeConfig4(config);
3830
4300
  }
3831
4301
  /**
3832
- * Generates a migration file from a schema diff
4302
+ * Generates migration files from a schema diff
4303
+ * Returns array of file paths (one per collection operation)
3833
4304
  */
3834
4305
  generate(diff) {
3835
4306
  return generate(diff, this.config);