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
@@ -3,6 +3,7 @@
3
3
  var fs3 = require('fs');
4
4
  var path = require('path');
5
5
  var zod = require('zod');
6
+ var crypto = require('crypto');
6
7
 
7
8
  function _interopNamespace(e) {
8
9
  if (e && e.__esModule) return e;
@@ -1581,12 +1582,22 @@ function isAuthCollection(fields) {
1581
1582
  function buildFieldDefinition(fieldName, zodType) {
1582
1583
  const fieldMetadata = extractFieldMetadata(zodType.description);
1583
1584
  if (fieldMetadata) {
1584
- const required2 = isFieldRequired(zodType);
1585
+ let required2;
1586
+ if (fieldMetadata.type === "number") {
1587
+ if (fieldMetadata.options?.required !== void 0) {
1588
+ required2 = fieldMetadata.options.required;
1589
+ } else {
1590
+ required2 = false;
1591
+ }
1592
+ } else {
1593
+ required2 = isFieldRequired(zodType);
1594
+ }
1595
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1585
1596
  const fieldDef2 = {
1586
1597
  name: fieldName,
1587
1598
  type: fieldMetadata.type,
1588
1599
  required: required2,
1589
- options: fieldMetadata.options
1600
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1590
1601
  };
1591
1602
  if (fieldMetadata.type === "relation") {
1592
1603
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -1785,7 +1796,7 @@ var SchemaAnalyzer = class {
1785
1796
  var SNAPSHOT_VERSION = "1.0.0";
1786
1797
  function resolveCollectionIdToName(collectionId) {
1787
1798
  if (collectionId === "_pb_users_auth_") {
1788
- return "Users";
1799
+ return "users";
1789
1800
  }
1790
1801
  const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1791
1802
  if (nameMatch) {
@@ -1793,6 +1804,39 @@ function resolveCollectionIdToName(collectionId) {
1793
1804
  }
1794
1805
  return collectionId;
1795
1806
  }
1807
+ function extractFieldOptions2(pbField) {
1808
+ const options = {};
1809
+ if (pbField.options && typeof pbField.options === "object") {
1810
+ Object.assign(options, pbField.options);
1811
+ }
1812
+ const directOptionKeys = [
1813
+ "min",
1814
+ "max",
1815
+ "pattern",
1816
+ "noDecimal",
1817
+ // text/number fields
1818
+ "values",
1819
+ "maxSelect",
1820
+ // select fields
1821
+ "mimeTypes",
1822
+ "maxSize",
1823
+ "thumbs",
1824
+ "protected",
1825
+ // file fields
1826
+ "onCreate",
1827
+ "onUpdate",
1828
+ // autodate fields
1829
+ "exceptDomains",
1830
+ "onlyDomains"
1831
+ // email/url fields
1832
+ ];
1833
+ for (const key of directOptionKeys) {
1834
+ if (pbField[key] !== void 0) {
1835
+ options[key] = pbField[key];
1836
+ }
1837
+ }
1838
+ return options;
1839
+ }
1796
1840
  function convertPocketBaseCollection(pbCollection) {
1797
1841
  const fields = [];
1798
1842
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
@@ -1810,23 +1854,19 @@ function convertPocketBaseCollection(pbCollection) {
1810
1854
  type: pbField.type,
1811
1855
  required: pbField.required || false
1812
1856
  };
1813
- field.options = pbField.options ? { ...pbField.options } : {};
1814
- if (pbField.type === "select") {
1815
- if (pbField.values && Array.isArray(pbField.values)) {
1816
- field.options.values = pbField.values;
1817
- } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
1818
- field.options.values = pbField.options.values;
1819
- }
1820
- }
1857
+ field.options = extractFieldOptions2(pbField);
1821
1858
  if (pbField.type === "relation") {
1822
1859
  const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
1823
- const collectionName = resolveCollectionIdToName(collectionId);
1860
+ const collectionName = resolveCollectionIdToName(collectionId || "");
1824
1861
  field.relation = {
1825
1862
  collection: collectionName,
1826
1863
  cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
1827
1864
  maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
1828
1865
  minSelect: pbField.minSelect ?? pbField.options?.minSelect
1829
1866
  };
1867
+ delete field.options.maxSelect;
1868
+ delete field.options.minSelect;
1869
+ delete field.options.cascadeDelete;
1830
1870
  }
1831
1871
  const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
1832
1872
  if (Object.keys(field.options).length === 0) {
@@ -1840,17 +1880,21 @@ function convertPocketBaseCollection(pbCollection) {
1840
1880
  type: pbCollection.type || "base",
1841
1881
  fields
1842
1882
  };
1883
+ if (pbCollection.id) {
1884
+ schema.id = pbCollection.id;
1885
+ }
1843
1886
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
1844
1887
  schema.indexes = pbCollection.indexes;
1845
1888
  }
1846
- const rules = {};
1847
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1848
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1849
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1850
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1851
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1852
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1853
- if (Object.keys(rules).length > 0) {
1889
+ 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;
1890
+ if (hasAnyRule) {
1891
+ const rules = {};
1892
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1893
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1894
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1895
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1896
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1897
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1854
1898
  schema.rules = rules;
1855
1899
  schema.permissions = { ...rules };
1856
1900
  }
@@ -2582,6 +2626,67 @@ var SnapshotManager = class {
2582
2626
  return validateSnapshot(snapshot);
2583
2627
  }
2584
2628
  };
2629
+ function generateCollectionId() {
2630
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
2631
+ const idLength = 15;
2632
+ const bytes = crypto.randomBytes(idLength);
2633
+ let id = "pb_";
2634
+ for (let i = 0; i < idLength; i++) {
2635
+ const index = bytes[i] % chars.length;
2636
+ id += chars[index];
2637
+ }
2638
+ return id;
2639
+ }
2640
+ var CollectionIdRegistry = class {
2641
+ ids;
2642
+ constructor() {
2643
+ this.ids = /* @__PURE__ */ new Set();
2644
+ }
2645
+ /**
2646
+ * Generates a unique collection ID for a given collection name
2647
+ * Retries up to 10 times if collision occurs (extremely rare)
2648
+ * Special case: returns "_pb_users_auth_" for users collection
2649
+ *
2650
+ * @param collectionName - The name of the collection (optional)
2651
+ * @returns A unique collection ID
2652
+ * @throws Error if unable to generate unique ID after max attempts
2653
+ */
2654
+ generate(collectionName) {
2655
+ if (collectionName && collectionName.toLowerCase() === "users") {
2656
+ const usersId = "_pb_users_auth_";
2657
+ if (!this.has(usersId)) {
2658
+ this.register(usersId);
2659
+ }
2660
+ return usersId;
2661
+ }
2662
+ const maxAttempts = 10;
2663
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
2664
+ const id = generateCollectionId();
2665
+ if (!this.has(id)) {
2666
+ this.register(id);
2667
+ return id;
2668
+ }
2669
+ }
2670
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
2671
+ }
2672
+ /**
2673
+ * Checks if an ID has already been registered
2674
+ *
2675
+ * @param id - The collection ID to check
2676
+ * @returns True if the ID exists in the registry
2677
+ */
2678
+ has(id) {
2679
+ return this.ids.has(id);
2680
+ }
2681
+ /**
2682
+ * Registers a collection ID in the registry
2683
+ *
2684
+ * @param id - The collection ID to register
2685
+ */
2686
+ register(id) {
2687
+ this.ids.add(id);
2688
+ }
2689
+ };
2585
2690
 
2586
2691
  // src/migration/diff.ts
2587
2692
  var DEFAULT_CONFIG3 = {
@@ -2735,18 +2840,49 @@ function compareFieldConstraints(currentField, previousField) {
2735
2840
  }
2736
2841
  return changes;
2737
2842
  }
2843
+ function normalizeOptionValue(key, value, fieldType) {
2844
+ if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
2845
+ return void 0;
2846
+ }
2847
+ if (key === "maxSize" && value === 0 && fieldType === "file") {
2848
+ return void 0;
2849
+ }
2850
+ if (fieldType === "file") {
2851
+ if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
2852
+ return void 0;
2853
+ }
2854
+ if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
2855
+ return void 0;
2856
+ }
2857
+ if (key === "protected" && value === false) {
2858
+ return void 0;
2859
+ }
2860
+ }
2861
+ if (fieldType === "autodate") {
2862
+ if (key === "onCreate" && value === true) {
2863
+ return void 0;
2864
+ }
2865
+ if (key === "onUpdate" && value === false) {
2866
+ return void 0;
2867
+ }
2868
+ }
2869
+ return value;
2870
+ }
2738
2871
  function compareFieldOptions(currentField, previousField) {
2739
2872
  const changes = [];
2740
2873
  const currentOptions = currentField.options || {};
2741
2874
  const previousOptions = previousField.options || {};
2742
2875
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(currentOptions), ...Object.keys(previousOptions)]);
2876
+ const fieldType = currentField.type;
2743
2877
  for (const key of allKeys) {
2744
2878
  const currentValue = currentOptions[key];
2745
2879
  const previousValue = previousOptions[key];
2746
- if (currentValue === void 0 && previousValue === void 0) {
2880
+ const normalizedCurrent = normalizeOptionValue(key, currentValue, fieldType);
2881
+ const normalizedPrevious = normalizeOptionValue(key, previousValue, fieldType);
2882
+ if (normalizedCurrent === void 0 && normalizedPrevious === void 0) {
2747
2883
  continue;
2748
2884
  }
2749
- if (!areValuesEqual(currentValue, previousValue)) {
2885
+ if (!areValuesEqual(normalizedCurrent, normalizedPrevious)) {
2750
2886
  changes.push({
2751
2887
  property: `options.${key}`,
2752
2888
  oldValue: previousValue,
@@ -2756,7 +2892,7 @@ function compareFieldOptions(currentField, previousField) {
2756
2892
  }
2757
2893
  return changes;
2758
2894
  }
2759
- function compareRelationConfigurations(currentField, previousField) {
2895
+ function compareRelationConfigurations(currentField, previousField, collectionIdToName) {
2760
2896
  const changes = [];
2761
2897
  const currentRelation = currentField.relation;
2762
2898
  const previousRelation = previousField.relation;
@@ -2768,8 +2904,8 @@ function compareRelationConfigurations(currentField, previousField) {
2768
2904
  }
2769
2905
  const normalizeCollection = (collection) => {
2770
2906
  if (!collection) return collection;
2771
- if (collection === "_pb_users_auth_") {
2772
- return "Users";
2907
+ if (collectionIdToName && collectionIdToName.has(collection)) {
2908
+ return collectionIdToName.get(collection);
2773
2909
  }
2774
2910
  const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2775
2911
  if (nameMatch) {
@@ -2779,13 +2915,11 @@ function compareRelationConfigurations(currentField, previousField) {
2779
2915
  };
2780
2916
  const normalizedCurrent = normalizeCollection(currentRelation.collection);
2781
2917
  const normalizedPrevious = normalizeCollection(previousRelation.collection);
2782
- if (normalizedCurrent !== normalizedPrevious) {
2918
+ if (normalizedCurrent.toLowerCase() !== normalizedPrevious.toLowerCase()) {
2783
2919
  changes.push({
2784
2920
  property: "relation.collection",
2785
- oldValue: normalizedPrevious,
2786
- // Use normalized value for clarity
2787
- newValue: normalizedCurrent
2788
- // Use normalized value for clarity
2921
+ oldValue: previousRelation.collection,
2922
+ newValue: currentRelation.collection
2789
2923
  });
2790
2924
  }
2791
2925
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -2795,14 +2929,20 @@ function compareRelationConfigurations(currentField, previousField) {
2795
2929
  newValue: currentRelation.cascadeDelete
2796
2930
  });
2797
2931
  }
2798
- if (currentRelation.maxSelect !== previousRelation.maxSelect) {
2932
+ const normalizeMax = (val) => val === 1 ? null : val;
2933
+ const currentMax = normalizeMax(currentRelation.maxSelect);
2934
+ const previousMax = normalizeMax(previousRelation.maxSelect);
2935
+ if (currentMax != previousMax) {
2799
2936
  changes.push({
2800
2937
  property: "relation.maxSelect",
2801
2938
  oldValue: previousRelation.maxSelect,
2802
2939
  newValue: currentRelation.maxSelect
2803
2940
  });
2804
2941
  }
2805
- if (currentRelation.minSelect !== previousRelation.minSelect) {
2942
+ const normalizeMin = (val) => val === 0 ? null : val;
2943
+ const currentMin = normalizeMin(currentRelation.minSelect);
2944
+ const previousMin = normalizeMin(previousRelation.minSelect);
2945
+ if (currentMin != previousMin) {
2806
2946
  changes.push({
2807
2947
  property: "relation.minSelect",
2808
2948
  oldValue: previousRelation.minSelect,
@@ -2811,7 +2951,7 @@ function compareRelationConfigurations(currentField, previousField) {
2811
2951
  }
2812
2952
  return changes;
2813
2953
  }
2814
- function detectFieldChanges(currentField, previousField) {
2954
+ function detectFieldChanges(currentField, previousField, collectionIdToName) {
2815
2955
  const changes = [];
2816
2956
  const typeChange = compareFieldTypes(currentField, previousField);
2817
2957
  if (typeChange) {
@@ -2820,7 +2960,7 @@ function detectFieldChanges(currentField, previousField) {
2820
2960
  changes.push(...compareFieldConstraints(currentField, previousField));
2821
2961
  changes.push(...compareFieldOptions(currentField, previousField));
2822
2962
  if (currentField.type === "relation" && previousField.type === "relation") {
2823
- changes.push(...compareRelationConfigurations(currentField, previousField));
2963
+ changes.push(...compareRelationConfigurations(currentField, previousField, collectionIdToName));
2824
2964
  }
2825
2965
  return changes;
2826
2966
  }
@@ -2831,7 +2971,7 @@ function compareIndexes(currentIndexes = [], previousIndexes = []) {
2831
2971
  const indexesToRemove = previousIndexes.filter((idx) => !currentSet.has(idx));
2832
2972
  return { indexesToAdd, indexesToRemove };
2833
2973
  }
2834
- function compareRules(currentRules, previousRules) {
2974
+ function compareRules(currentRules, previousRules, currentPermissions, previousPermissions) {
2835
2975
  const updates = [];
2836
2976
  const ruleTypes = [
2837
2977
  "listRule",
@@ -2842,8 +2982,8 @@ function compareRules(currentRules, previousRules) {
2842
2982
  "manageRule"
2843
2983
  ];
2844
2984
  for (const ruleType of ruleTypes) {
2845
- const currentValue = currentRules?.[ruleType] ?? null;
2846
- const previousValue = previousRules?.[ruleType] ?? null;
2985
+ const currentValue = currentRules?.[ruleType] ?? currentPermissions?.[ruleType] ?? null;
2986
+ const previousValue = previousRules?.[ruleType] ?? previousPermissions?.[ruleType] ?? null;
2847
2987
  if (currentValue !== previousValue) {
2848
2988
  updates.push({
2849
2989
  ruleType,
@@ -2870,7 +3010,7 @@ function comparePermissions(currentPermissions, previousPermissions) {
2870
3010
  }
2871
3011
  return changes;
2872
3012
  }
2873
- function compareCollectionFields(currentCollection, previousCollection, config) {
3013
+ function compareCollectionFields(currentCollection, previousCollection, config, collectionIdToName) {
2874
3014
  let fieldsToAdd = findNewFields(currentCollection.fields, previousCollection.fields);
2875
3015
  const fieldsToRemove = findRemovedFields(currentCollection.fields, previousCollection.fields);
2876
3016
  const fieldsToModify = [];
@@ -2880,7 +3020,7 @@ function compareCollectionFields(currentCollection, previousCollection, config)
2880
3020
  }
2881
3021
  const matchedFields = matchFieldsByName(currentCollection.fields, previousCollection.fields);
2882
3022
  for (const [currentField, previousField] of matchedFields) {
2883
- const changes = detectFieldChanges(currentField, previousField);
3023
+ const changes = detectFieldChanges(currentField, previousField, collectionIdToName);
2884
3024
  if (changes.length > 0) {
2885
3025
  fieldsToModify.push({
2886
3026
  fieldName: currentField.name,
@@ -2892,14 +3032,20 @@ function compareCollectionFields(currentCollection, previousCollection, config)
2892
3032
  }
2893
3033
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
2894
3034
  }
2895
- function buildCollectionModification(currentCollection, previousCollection, config) {
3035
+ function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
2896
3036
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
2897
3037
  currentCollection,
2898
3038
  previousCollection,
2899
- config
3039
+ config,
3040
+ collectionIdToName
2900
3041
  );
2901
3042
  const { indexesToAdd, indexesToRemove } = compareIndexes(currentCollection.indexes, previousCollection.indexes);
2902
- const rulesToUpdate = compareRules(currentCollection.rules, previousCollection.rules);
3043
+ const rulesToUpdate = compareRules(
3044
+ currentCollection.rules,
3045
+ previousCollection.rules,
3046
+ currentCollection.permissions,
3047
+ previousCollection.permissions
3048
+ );
2903
3049
  const permissionsToUpdate = comparePermissions(currentCollection.permissions, previousCollection.permissions);
2904
3050
  return {
2905
3051
  collection: currentCollection.name,
@@ -2916,6 +3062,14 @@ function hasChanges(modification) {
2916
3062
  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;
2917
3063
  }
2918
3064
  function aggregateChanges(currentSchema, previousSnapshot, config) {
3065
+ const collectionIdToName = /* @__PURE__ */ new Map();
3066
+ if (previousSnapshot) {
3067
+ for (const [name, collection] of previousSnapshot.collections) {
3068
+ if (collection.id) {
3069
+ collectionIdToName.set(collection.id, name);
3070
+ }
3071
+ }
3072
+ }
2919
3073
  const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
2920
3074
  const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
2921
3075
  const filteredCollectionsToCreate = collectionsToCreate.filter(
@@ -2924,16 +3078,28 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
2924
3078
  const filteredCollectionsToDelete = collectionsToDelete.filter(
2925
3079
  (collection) => !isSystemCollection(collection.name, config)
2926
3080
  );
3081
+ const registry = new CollectionIdRegistry();
3082
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
3083
+ if (collection.id) {
3084
+ registry.register(collection.id);
3085
+ return collection;
3086
+ }
3087
+ const id = registry.generate(collection.name);
3088
+ return {
3089
+ ...collection,
3090
+ id
3091
+ };
3092
+ });
2927
3093
  const collectionsToModify = [];
2928
3094
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
2929
3095
  for (const [currentCollection, previousCollection] of matchedCollections) {
2930
- const modification = buildCollectionModification(currentCollection, previousCollection, config);
3096
+ const modification = buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName);
2931
3097
  if (hasChanges(modification)) {
2932
3098
  collectionsToModify.push(modification);
2933
3099
  }
2934
3100
  }
2935
3101
  return {
2936
- collectionsToCreate: filteredCollectionsToCreate,
3102
+ collectionsToCreate: collectionsWithIds,
2937
3103
  collectionsToDelete: filteredCollectionsToDelete,
2938
3104
  collectionsToModify
2939
3105
  };
@@ -3169,6 +3335,49 @@ function generateTimestamp(config) {
3169
3335
  }
3170
3336
  return Math.floor(Date.now() / 1e3).toString();
3171
3337
  }
3338
+ function splitDiffByCollection(diff, baseTimestamp) {
3339
+ const operations = [];
3340
+ let currentTimestamp = parseInt(baseTimestamp, 10);
3341
+ for (const collection of diff.collectionsToCreate) {
3342
+ operations.push({
3343
+ type: "create",
3344
+ collection,
3345
+ timestamp: currentTimestamp.toString()
3346
+ });
3347
+ currentTimestamp += 1;
3348
+ }
3349
+ for (const modification of diff.collectionsToModify) {
3350
+ operations.push({
3351
+ type: "modify",
3352
+ collection: modification.collection,
3353
+ modifications: modification,
3354
+ timestamp: currentTimestamp.toString()
3355
+ });
3356
+ currentTimestamp += 1;
3357
+ }
3358
+ for (const collection of diff.collectionsToDelete) {
3359
+ operations.push({
3360
+ type: "delete",
3361
+ collection: collection.name || collection,
3362
+ // Handle both object and string
3363
+ timestamp: currentTimestamp.toString()
3364
+ });
3365
+ currentTimestamp += 1;
3366
+ }
3367
+ return operations;
3368
+ }
3369
+ function generateCollectionMigrationFilename(operation) {
3370
+ const timestamp = operation.timestamp;
3371
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
3372
+ let collectionName;
3373
+ if (typeof operation.collection === "string") {
3374
+ collectionName = operation.collection;
3375
+ } else {
3376
+ collectionName = operation.collection.name;
3377
+ }
3378
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
3379
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
3380
+ }
3172
3381
  function generateMigrationDescription(diff) {
3173
3382
  const parts = [];
3174
3383
  if (diff.collectionsToCreate.length > 0) {
@@ -3276,14 +3485,13 @@ function formatValue(value) {
3276
3485
  return "null";
3277
3486
  }
3278
3487
  if (typeof value === "string") {
3279
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
3488
+ return JSON.stringify(value);
3280
3489
  }
3281
3490
  if (typeof value === "number" || typeof value === "boolean") {
3282
3491
  return String(value);
3283
3492
  }
3284
3493
  if (Array.isArray(value)) {
3285
- const items = value.map((v) => formatValue(v)).join(", ");
3286
- return `[${items}]`;
3494
+ return JSON.stringify(value).replace(/","/g, '", "');
3287
3495
  }
3288
3496
  if (typeof value === "object") {
3289
3497
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -3291,7 +3499,7 @@ function formatValue(value) {
3291
3499
  }
3292
3500
  return String(value);
3293
3501
  }
3294
- function generateFieldDefinitionObject(field) {
3502
+ function generateFieldDefinitionObject(field, collectionIdMap) {
3295
3503
  const parts = [];
3296
3504
  parts.push(` name: "${field.name}"`);
3297
3505
  parts.push(` type: "${field.type}"`);
@@ -3299,34 +3507,47 @@ function generateFieldDefinitionObject(field) {
3299
3507
  if (field.unique !== void 0) {
3300
3508
  parts.push(` unique: ${field.unique}`);
3301
3509
  }
3510
+ if (field.type === "select") {
3511
+ const maxSelect = field.options?.maxSelect ?? 1;
3512
+ parts.push(` maxSelect: ${maxSelect}`);
3513
+ const values = field.options?.values ?? [];
3514
+ parts.push(` values: ${formatValue(values)}`);
3515
+ }
3302
3516
  if (field.options && Object.keys(field.options).length > 0) {
3303
3517
  for (const [key, value] of Object.entries(field.options)) {
3518
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
3519
+ continue;
3520
+ }
3304
3521
  parts.push(` ${key}: ${formatValue(value)}`);
3305
3522
  }
3306
3523
  }
3307
3524
  if (field.relation) {
3308
3525
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3309
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3310
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
3311
- if (field.relation.maxSelect !== void 0) {
3312
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
3313
- }
3314
- if (field.relation.minSelect !== void 0) {
3315
- parts.push(` minSelect: ${field.relation.minSelect}`);
3316
- }
3317
- if (field.relation.cascadeDelete !== void 0) {
3318
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
3526
+ let collectionIdValue;
3527
+ if (isUsersCollection) {
3528
+ collectionIdValue = '"_pb_users_auth_"';
3529
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
3530
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
3531
+ } else {
3532
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3319
3533
  }
3534
+ parts.push(` collectionId: ${collectionIdValue}`);
3535
+ const maxSelect = field.relation.maxSelect ?? 1;
3536
+ parts.push(` maxSelect: ${maxSelect}`);
3537
+ const minSelect = field.relation.minSelect ?? null;
3538
+ parts.push(` minSelect: ${minSelect}`);
3539
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
3540
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
3320
3541
  }
3321
3542
  return ` {
3322
3543
  ${parts.join(",\n")},
3323
3544
  }`;
3324
3545
  }
3325
- function generateFieldsArray(fields) {
3546
+ function generateFieldsArray(fields, collectionIdMap) {
3326
3547
  if (fields.length === 0) {
3327
3548
  return "[]";
3328
3549
  }
3329
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
3550
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
3330
3551
  return `[
3331
3552
  ${fieldObjects.join(",\n")},
3332
3553
  ]`;
@@ -3385,7 +3606,7 @@ function generateIndexesArray(indexes) {
3385
3606
  if (!indexes || indexes.length === 0) {
3386
3607
  return "[]";
3387
3608
  }
3388
- const indexStrings = indexes.map((idx) => `"${idx}"`);
3609
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
3389
3610
  return `[
3390
3611
  ${indexStrings.join(",\n ")},
3391
3612
  ]`;
@@ -3439,9 +3660,12 @@ function getSystemFields() {
3439
3660
  }
3440
3661
  ];
3441
3662
  }
3442
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
3663
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
3443
3664
  const lines = [];
3444
3665
  lines.push(` const ${varName} = new Collection({`);
3666
+ if (collection.id) {
3667
+ lines.push(` id: ${formatValue(collection.id)},`);
3668
+ }
3445
3669
  lines.push(` name: "${collection.name}",`);
3446
3670
  lines.push(` type: "${collection.type}",`);
3447
3671
  const permissionsCode = generateCollectionPermissions(collection.permissions);
@@ -3453,7 +3677,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
3453
3677
  }
3454
3678
  const systemFields = getSystemFields();
3455
3679
  const allFields = [...systemFields, ...collection.fields];
3456
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
3680
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
3457
3681
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
3458
3682
  lines.push(` });`);
3459
3683
  lines.push(``);
@@ -3475,42 +3699,59 @@ function getFieldConstructorName(fieldType) {
3475
3699
  };
3476
3700
  return constructorMap[fieldType] || "TextField";
3477
3701
  }
3478
- function generateFieldConstructorOptions(field) {
3702
+ function generateFieldConstructorOptions(field, collectionIdMap) {
3479
3703
  const parts = [];
3480
3704
  parts.push(` name: "${field.name}"`);
3481
3705
  parts.push(` required: ${field.required}`);
3482
3706
  if (field.unique !== void 0) {
3483
3707
  parts.push(` unique: ${field.unique}`);
3484
3708
  }
3709
+ if (field.type === "select") {
3710
+ const maxSelect = field.options?.maxSelect ?? 1;
3711
+ parts.push(` maxSelect: ${maxSelect}`);
3712
+ const values = field.options?.values ?? [];
3713
+ parts.push(` values: ${formatValue(values)}`);
3714
+ }
3485
3715
  if (field.options && Object.keys(field.options).length > 0) {
3486
3716
  for (const [key, value] of Object.entries(field.options)) {
3487
- parts.push(` ${key}: ${formatValue(value)}`);
3717
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
3718
+ continue;
3719
+ }
3720
+ if (field.type === "number" && key === "noDecimal") {
3721
+ parts.push(` onlyInt: ${formatValue(value)}`);
3722
+ } else {
3723
+ parts.push(` ${key}: ${formatValue(value)}`);
3724
+ }
3488
3725
  }
3489
3726
  }
3490
3727
  if (field.relation && field.type === "relation") {
3491
3728
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3492
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3493
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
3494
- if (field.relation.maxSelect !== void 0) {
3495
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
3496
- }
3497
- if (field.relation.minSelect !== void 0) {
3498
- parts.push(` minSelect: ${field.relation.minSelect}`);
3499
- }
3500
- if (field.relation.cascadeDelete !== void 0) {
3501
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
3729
+ let collectionIdValue;
3730
+ if (isUsersCollection) {
3731
+ collectionIdValue = '"_pb_users_auth_"';
3732
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
3733
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
3734
+ } else {
3735
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3502
3736
  }
3737
+ parts.push(` collectionId: ${collectionIdValue}`);
3738
+ const maxSelect = field.relation.maxSelect ?? 1;
3739
+ parts.push(` maxSelect: ${maxSelect}`);
3740
+ const minSelect = field.relation.minSelect ?? null;
3741
+ parts.push(` minSelect: ${minSelect}`);
3742
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
3743
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
3503
3744
  }
3504
3745
  return parts.join(",\n");
3505
3746
  }
3506
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
3747
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
3507
3748
  const lines = [];
3508
3749
  const constructorName = getFieldConstructorName(field.type);
3509
3750
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
3510
3751
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3511
3752
  lines.push(``);
3512
3753
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
3513
- lines.push(generateFieldConstructorOptions(field));
3754
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
3514
3755
  lines.push(` }));`);
3515
3756
  lines.push(``);
3516
3757
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -3560,7 +3801,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
3560
3801
  const lines = [];
3561
3802
  const collectionVar = varName || `collection_${collectionName}_idx`;
3562
3803
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3563
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
3804
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
3564
3805
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3565
3806
  return lines.join("\n");
3566
3807
  }
@@ -3569,7 +3810,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
3569
3810
  const collectionVar = varName || `collection_${collectionName}_idx`;
3570
3811
  const indexVar = `${collectionVar}_indexToRemove`;
3571
3812
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3572
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
3813
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
3573
3814
  lines.push(` if (${indexVar} !== -1) {`);
3574
3815
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
3575
3816
  lines.push(` }`);
@@ -3598,16 +3839,213 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
3598
3839
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
3599
3840
  return lines.join("\n");
3600
3841
  }
3842
+ function generateOperationUpMigration(operation, collectionIdMap) {
3843
+ const lines = [];
3844
+ if (operation.type === "create") {
3845
+ const collection = operation.collection;
3846
+ const varName = `collection_${collection.name}`;
3847
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
3848
+ } else if (operation.type === "modify") {
3849
+ const modification = operation.modifications;
3850
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
3851
+ let operationCount = 0;
3852
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
3853
+ for (const field of modification.fieldsToAdd) {
3854
+ operationCount++;
3855
+ const varName = `collection_${collectionName}_add_${field.name}`;
3856
+ const isLast = operationCount === totalOperations;
3857
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
3858
+ if (!isLast) lines.push("");
3859
+ }
3860
+ for (const fieldMod of modification.fieldsToModify) {
3861
+ operationCount++;
3862
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
3863
+ const isLast = operationCount === totalOperations;
3864
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
3865
+ if (!isLast) lines.push("");
3866
+ }
3867
+ for (const field of modification.fieldsToRemove) {
3868
+ operationCount++;
3869
+ const varName = `collection_${collectionName}_remove_${field.name}`;
3870
+ const isLast = operationCount === totalOperations;
3871
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
3872
+ if (!isLast) lines.push("");
3873
+ }
3874
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
3875
+ operationCount++;
3876
+ const index = modification.indexesToAdd[i];
3877
+ const varName = `collection_${collectionName}_addidx_${i}`;
3878
+ const isLast = operationCount === totalOperations;
3879
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
3880
+ if (!isLast) lines.push("");
3881
+ }
3882
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
3883
+ operationCount++;
3884
+ const index = modification.indexesToRemove[i];
3885
+ const varName = `collection_${collectionName}_rmidx_${i}`;
3886
+ const isLast = operationCount === totalOperations;
3887
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
3888
+ if (!isLast) lines.push("");
3889
+ }
3890
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
3891
+ for (const permission of modification.permissionsToUpdate) {
3892
+ operationCount++;
3893
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
3894
+ const isLast = operationCount === totalOperations;
3895
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
3896
+ if (!isLast) lines.push("");
3897
+ }
3898
+ } else if (modification.rulesToUpdate.length > 0) {
3899
+ for (const rule of modification.rulesToUpdate) {
3900
+ operationCount++;
3901
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
3902
+ const isLast = operationCount === totalOperations;
3903
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
3904
+ if (!isLast) lines.push("");
3905
+ }
3906
+ }
3907
+ } else if (operation.type === "delete") {
3908
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
3909
+ const varName = `collection_${collectionName}`;
3910
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
3911
+ }
3912
+ let code = lines.join("\n");
3913
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3914
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3915
+ const saveMatches = [...code.matchAll(savePattern)];
3916
+ const deleteMatches = [...code.matchAll(deletePattern)];
3917
+ const allMatches = [
3918
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3919
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3920
+ ].sort((a, b) => b.index - a.index);
3921
+ if (allMatches.length > 0) {
3922
+ const lastMatch = allMatches[0];
3923
+ if (lastMatch.type === "save") {
3924
+ 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);
3925
+ } else {
3926
+ 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);
3927
+ }
3928
+ }
3929
+ return code;
3930
+ }
3931
+ function generateOperationDownMigration(operation, collectionIdMap) {
3932
+ const lines = [];
3933
+ if (operation.type === "create") {
3934
+ const collection = operation.collection;
3935
+ const varName = `collection_${collection.name}`;
3936
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
3937
+ } else if (operation.type === "modify") {
3938
+ const modification = operation.modifications;
3939
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
3940
+ let operationCount = 0;
3941
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
3942
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
3943
+ for (const permission of modification.permissionsToUpdate) {
3944
+ operationCount++;
3945
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
3946
+ const isLast = operationCount === totalOperations;
3947
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
3948
+ if (!isLast) lines.push("");
3949
+ }
3950
+ } else if (modification.rulesToUpdate.length > 0) {
3951
+ for (const rule of modification.rulesToUpdate) {
3952
+ operationCount++;
3953
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
3954
+ const isLast = operationCount === totalOperations;
3955
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
3956
+ if (!isLast) lines.push("");
3957
+ }
3958
+ }
3959
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
3960
+ operationCount++;
3961
+ const index = modification.indexesToRemove[i];
3962
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
3963
+ const isLast = operationCount === totalOperations;
3964
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
3965
+ if (!isLast) lines.push("");
3966
+ }
3967
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
3968
+ operationCount++;
3969
+ const index = modification.indexesToAdd[i];
3970
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
3971
+ const isLast = operationCount === totalOperations;
3972
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
3973
+ if (!isLast) lines.push("");
3974
+ }
3975
+ for (const field of modification.fieldsToRemove) {
3976
+ operationCount++;
3977
+ const varName = `collection_${collectionName}_restore_${field.name}`;
3978
+ const isLast = operationCount === totalOperations;
3979
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
3980
+ if (!isLast) lines.push("");
3981
+ }
3982
+ for (const fieldMod of modification.fieldsToModify) {
3983
+ operationCount++;
3984
+ const reverseChanges = fieldMod.changes.map((change) => ({
3985
+ property: change.property,
3986
+ oldValue: change.newValue,
3987
+ newValue: change.oldValue
3988
+ }));
3989
+ const reverseMod = {
3990
+ fieldName: fieldMod.fieldName,
3991
+ currentDefinition: fieldMod.newDefinition,
3992
+ newDefinition: fieldMod.currentDefinition,
3993
+ changes: reverseChanges
3994
+ };
3995
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
3996
+ const isLast = operationCount === totalOperations;
3997
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
3998
+ if (!isLast) lines.push("");
3999
+ }
4000
+ for (const field of modification.fieldsToAdd) {
4001
+ operationCount++;
4002
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
4003
+ const isLast = operationCount === totalOperations;
4004
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
4005
+ if (!isLast) lines.push("");
4006
+ }
4007
+ } else if (operation.type === "delete") {
4008
+ const collection = operation.collection;
4009
+ if (typeof collection !== "string") {
4010
+ const varName = `collection_${collection.name}`;
4011
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
4012
+ }
4013
+ }
4014
+ let code = lines.join("\n");
4015
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4016
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4017
+ const saveMatches = [...code.matchAll(savePattern)];
4018
+ const deleteMatches = [...code.matchAll(deletePattern)];
4019
+ const allMatches = [
4020
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4021
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4022
+ ].sort((a, b) => b.index - a.index);
4023
+ if (allMatches.length > 0) {
4024
+ const lastMatch = allMatches[0];
4025
+ if (lastMatch.type === "save") {
4026
+ 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);
4027
+ } else {
4028
+ 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);
4029
+ }
4030
+ }
4031
+ return code;
4032
+ }
3601
4033
  function generateUpMigration(diff) {
3602
4034
  const lines = [];
3603
4035
  lines.push(` // UP MIGRATION`);
3604
4036
  lines.push(``);
4037
+ const collectionIdMap = /* @__PURE__ */ new Map();
4038
+ for (const collection of diff.collectionsToCreate) {
4039
+ if (collection.id) {
4040
+ collectionIdMap.set(collection.name, collection.id);
4041
+ }
4042
+ }
3605
4043
  if (diff.collectionsToCreate.length > 0) {
3606
4044
  lines.push(` // Create new collections`);
3607
4045
  for (let i = 0; i < diff.collectionsToCreate.length; i++) {
3608
4046
  const collection = diff.collectionsToCreate[i];
3609
4047
  const varName = `collection_${collection.name}_create`;
3610
- lines.push(generateCollectionCreation(collection, varName));
4048
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
3611
4049
  lines.push(``);
3612
4050
  }
3613
4051
  }
@@ -3619,7 +4057,7 @@ function generateUpMigration(diff) {
3619
4057
  lines.push(` // Add fields to ${collectionName}`);
3620
4058
  for (const field of modification.fieldsToAdd) {
3621
4059
  const varName = `collection_${collectionName}_add_${field.name}`;
3622
- lines.push(generateFieldAddition(collectionName, field, varName));
4060
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
3623
4061
  lines.push(``);
3624
4062
  }
3625
4063
  }
@@ -3710,12 +4148,23 @@ function generateDownMigration(diff) {
3710
4148
  const lines = [];
3711
4149
  lines.push(` // DOWN MIGRATION (ROLLBACK)`);
3712
4150
  lines.push(``);
4151
+ const collectionIdMap = /* @__PURE__ */ new Map();
4152
+ for (const collection of diff.collectionsToCreate) {
4153
+ if (collection.id) {
4154
+ collectionIdMap.set(collection.name, collection.id);
4155
+ }
4156
+ }
4157
+ for (const collection of diff.collectionsToDelete) {
4158
+ if (collection.id) {
4159
+ collectionIdMap.set(collection.name, collection.id);
4160
+ }
4161
+ }
3713
4162
  if (diff.collectionsToDelete.length > 0) {
3714
4163
  lines.push(` // Recreate deleted collections`);
3715
4164
  for (let i = 0; i < diff.collectionsToDelete.length; i++) {
3716
4165
  const collection = diff.collectionsToDelete[i];
3717
4166
  const varName = `collection_${collection.name}_recreate`;
3718
- lines.push(generateCollectionCreation(collection, varName));
4167
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
3719
4168
  lines.push(``);
3720
4169
  }
3721
4170
  }
@@ -3760,7 +4209,7 @@ function generateDownMigration(diff) {
3760
4209
  lines.push(` // Restore fields to ${collectionName}`);
3761
4210
  for (const field of modification.fieldsToRemove) {
3762
4211
  const varName = `collection_${collectionName}_restore_${field.name}`;
3763
- lines.push(generateFieldAddition(collectionName, field, varName));
4212
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
3764
4213
  lines.push(``);
3765
4214
  }
3766
4215
  }
@@ -3829,12 +4278,33 @@ function generate(diff, config) {
3829
4278
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
3830
4279
  try {
3831
4280
  const migrationDir = resolveMigrationDir(normalizedConfig);
3832
- const upCode = generateUpMigration(diff);
3833
- const downCode = generateDownMigration(diff);
3834
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
3835
- const filename = generateMigrationFilename(diff, normalizedConfig);
3836
- const filePath = writeMigrationFile(migrationDir, filename, content);
3837
- return filePath;
4281
+ const hasChanges2 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
4282
+ if (!hasChanges2) {
4283
+ return [];
4284
+ }
4285
+ const collectionIdMap = /* @__PURE__ */ new Map();
4286
+ for (const collection of diff.collectionsToCreate) {
4287
+ if (collection.id) {
4288
+ collectionIdMap.set(collection.name, collection.id);
4289
+ }
4290
+ }
4291
+ for (const collection of diff.collectionsToDelete) {
4292
+ if (collection.id) {
4293
+ collectionIdMap.set(collection.name, collection.id);
4294
+ }
4295
+ }
4296
+ const baseTimestamp = generateTimestamp(normalizedConfig);
4297
+ const operations = splitDiffByCollection(diff, baseTimestamp);
4298
+ const filePaths = [];
4299
+ for (const operation of operations) {
4300
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
4301
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
4302
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
4303
+ const filename = generateCollectionMigrationFilename(operation);
4304
+ const filePath = writeMigrationFile(migrationDir, filename, content);
4305
+ filePaths.push(filePath);
4306
+ }
4307
+ return filePaths;
3838
4308
  } catch (error) {
3839
4309
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
3840
4310
  throw error;
@@ -3852,7 +4322,8 @@ var MigrationGenerator = class {
3852
4322
  this.config = mergeConfig4(config);
3853
4323
  }
3854
4324
  /**
3855
- * Generates a migration file from a schema diff
4325
+ * Generates migration files from a schema diff
4326
+ * Returns array of file paths (one per collection operation)
3856
4327
  */
3857
4328
  generate(diff) {
3858
4329
  return generate(diff, this.config);