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 path5 = require('path');
4
4
  var fs5 = require('fs');
5
5
  var zod = require('zod');
6
+ var crypto = require('crypto');
6
7
  var chalk = require('chalk');
7
8
  var ora = require('ora');
8
9
 
@@ -1315,12 +1316,22 @@ function isAuthCollection(fields) {
1315
1316
  function buildFieldDefinition(fieldName, zodType) {
1316
1317
  const fieldMetadata = extractFieldMetadata(zodType.description);
1317
1318
  if (fieldMetadata) {
1318
- const required2 = isFieldRequired(zodType);
1319
+ let required2;
1320
+ if (fieldMetadata.type === "number") {
1321
+ if (fieldMetadata.options?.required !== void 0) {
1322
+ required2 = fieldMetadata.options.required;
1323
+ } else {
1324
+ required2 = false;
1325
+ }
1326
+ } else {
1327
+ required2 = isFieldRequired(zodType);
1328
+ }
1329
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1319
1330
  const fieldDef2 = {
1320
1331
  name: fieldName,
1321
1332
  type: fieldMetadata.type,
1322
1333
  required: required2,
1323
- options: fieldMetadata.options
1334
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1324
1335
  };
1325
1336
  if (fieldMetadata.type === "relation") {
1326
1337
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -1490,6 +1501,67 @@ async function buildSchemaDefinition(config) {
1490
1501
  async function parseSchemaFiles(config) {
1491
1502
  return buildSchemaDefinition(config);
1492
1503
  }
1504
+ function generateCollectionId() {
1505
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
1506
+ const idLength = 15;
1507
+ const bytes = crypto.randomBytes(idLength);
1508
+ let id = "pb_";
1509
+ for (let i = 0; i < idLength; i++) {
1510
+ const index = bytes[i] % chars.length;
1511
+ id += chars[index];
1512
+ }
1513
+ return id;
1514
+ }
1515
+ var CollectionIdRegistry = class {
1516
+ ids;
1517
+ constructor() {
1518
+ this.ids = /* @__PURE__ */ new Set();
1519
+ }
1520
+ /**
1521
+ * Generates a unique collection ID for a given collection name
1522
+ * Retries up to 10 times if collision occurs (extremely rare)
1523
+ * Special case: returns "_pb_users_auth_" for users collection
1524
+ *
1525
+ * @param collectionName - The name of the collection (optional)
1526
+ * @returns A unique collection ID
1527
+ * @throws Error if unable to generate unique ID after max attempts
1528
+ */
1529
+ generate(collectionName) {
1530
+ if (collectionName && collectionName.toLowerCase() === "users") {
1531
+ const usersId = "_pb_users_auth_";
1532
+ if (!this.has(usersId)) {
1533
+ this.register(usersId);
1534
+ }
1535
+ return usersId;
1536
+ }
1537
+ const maxAttempts = 10;
1538
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1539
+ const id = generateCollectionId();
1540
+ if (!this.has(id)) {
1541
+ this.register(id);
1542
+ return id;
1543
+ }
1544
+ }
1545
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
1546
+ }
1547
+ /**
1548
+ * Checks if an ID has already been registered
1549
+ *
1550
+ * @param id - The collection ID to check
1551
+ * @returns True if the ID exists in the registry
1552
+ */
1553
+ has(id) {
1554
+ return this.ids.has(id);
1555
+ }
1556
+ /**
1557
+ * Registers a collection ID in the registry
1558
+ *
1559
+ * @param id - The collection ID to register
1560
+ */
1561
+ register(id) {
1562
+ this.ids.add(id);
1563
+ }
1564
+ };
1493
1565
 
1494
1566
  // src/migration/diff.ts
1495
1567
  var DEFAULT_CONFIG2 = {
@@ -1632,18 +1704,49 @@ function compareFieldConstraints(currentField, previousField) {
1632
1704
  }
1633
1705
  return changes;
1634
1706
  }
1707
+ function normalizeOptionValue(key, value, fieldType) {
1708
+ if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
1709
+ return void 0;
1710
+ }
1711
+ if (key === "maxSize" && value === 0 && fieldType === "file") {
1712
+ return void 0;
1713
+ }
1714
+ if (fieldType === "file") {
1715
+ if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
1716
+ return void 0;
1717
+ }
1718
+ if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
1719
+ return void 0;
1720
+ }
1721
+ if (key === "protected" && value === false) {
1722
+ return void 0;
1723
+ }
1724
+ }
1725
+ if (fieldType === "autodate") {
1726
+ if (key === "onCreate" && value === true) {
1727
+ return void 0;
1728
+ }
1729
+ if (key === "onUpdate" && value === false) {
1730
+ return void 0;
1731
+ }
1732
+ }
1733
+ return value;
1734
+ }
1635
1735
  function compareFieldOptions(currentField, previousField) {
1636
1736
  const changes = [];
1637
1737
  const currentOptions = currentField.options || {};
1638
1738
  const previousOptions = previousField.options || {};
1639
1739
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(currentOptions), ...Object.keys(previousOptions)]);
1740
+ const fieldType = currentField.type;
1640
1741
  for (const key of allKeys) {
1641
1742
  const currentValue = currentOptions[key];
1642
1743
  const previousValue = previousOptions[key];
1643
- if (currentValue === void 0 && previousValue === void 0) {
1744
+ const normalizedCurrent = normalizeOptionValue(key, currentValue, fieldType);
1745
+ const normalizedPrevious = normalizeOptionValue(key, previousValue, fieldType);
1746
+ if (normalizedCurrent === void 0 && normalizedPrevious === void 0) {
1644
1747
  continue;
1645
1748
  }
1646
- if (!areValuesEqual(currentValue, previousValue)) {
1749
+ if (!areValuesEqual(normalizedCurrent, normalizedPrevious)) {
1647
1750
  changes.push({
1648
1751
  property: `options.${key}`,
1649
1752
  oldValue: previousValue,
@@ -1653,7 +1756,7 @@ function compareFieldOptions(currentField, previousField) {
1653
1756
  }
1654
1757
  return changes;
1655
1758
  }
1656
- function compareRelationConfigurations(currentField, previousField) {
1759
+ function compareRelationConfigurations(currentField, previousField, collectionIdToName) {
1657
1760
  const changes = [];
1658
1761
  const currentRelation = currentField.relation;
1659
1762
  const previousRelation = previousField.relation;
@@ -1665,8 +1768,8 @@ function compareRelationConfigurations(currentField, previousField) {
1665
1768
  }
1666
1769
  const normalizeCollection = (collection) => {
1667
1770
  if (!collection) return collection;
1668
- if (collection === "_pb_users_auth_") {
1669
- return "Users";
1771
+ if (collectionIdToName && collectionIdToName.has(collection)) {
1772
+ return collectionIdToName.get(collection);
1670
1773
  }
1671
1774
  const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1672
1775
  if (nameMatch) {
@@ -1676,13 +1779,11 @@ function compareRelationConfigurations(currentField, previousField) {
1676
1779
  };
1677
1780
  const normalizedCurrent = normalizeCollection(currentRelation.collection);
1678
1781
  const normalizedPrevious = normalizeCollection(previousRelation.collection);
1679
- if (normalizedCurrent !== normalizedPrevious) {
1782
+ if (normalizedCurrent.toLowerCase() !== normalizedPrevious.toLowerCase()) {
1680
1783
  changes.push({
1681
1784
  property: "relation.collection",
1682
- oldValue: normalizedPrevious,
1683
- // Use normalized value for clarity
1684
- newValue: normalizedCurrent
1685
- // Use normalized value for clarity
1785
+ oldValue: previousRelation.collection,
1786
+ newValue: currentRelation.collection
1686
1787
  });
1687
1788
  }
1688
1789
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -1692,14 +1793,20 @@ function compareRelationConfigurations(currentField, previousField) {
1692
1793
  newValue: currentRelation.cascadeDelete
1693
1794
  });
1694
1795
  }
1695
- if (currentRelation.maxSelect !== previousRelation.maxSelect) {
1796
+ const normalizeMax = (val) => val === 1 ? null : val;
1797
+ const currentMax = normalizeMax(currentRelation.maxSelect);
1798
+ const previousMax = normalizeMax(previousRelation.maxSelect);
1799
+ if (currentMax != previousMax) {
1696
1800
  changes.push({
1697
1801
  property: "relation.maxSelect",
1698
1802
  oldValue: previousRelation.maxSelect,
1699
1803
  newValue: currentRelation.maxSelect
1700
1804
  });
1701
1805
  }
1702
- if (currentRelation.minSelect !== previousRelation.minSelect) {
1806
+ const normalizeMin = (val) => val === 0 ? null : val;
1807
+ const currentMin = normalizeMin(currentRelation.minSelect);
1808
+ const previousMin = normalizeMin(previousRelation.minSelect);
1809
+ if (currentMin != previousMin) {
1703
1810
  changes.push({
1704
1811
  property: "relation.minSelect",
1705
1812
  oldValue: previousRelation.minSelect,
@@ -1708,7 +1815,7 @@ function compareRelationConfigurations(currentField, previousField) {
1708
1815
  }
1709
1816
  return changes;
1710
1817
  }
1711
- function detectFieldChanges(currentField, previousField) {
1818
+ function detectFieldChanges(currentField, previousField, collectionIdToName) {
1712
1819
  const changes = [];
1713
1820
  const typeChange = compareFieldTypes(currentField, previousField);
1714
1821
  if (typeChange) {
@@ -1717,7 +1824,7 @@ function detectFieldChanges(currentField, previousField) {
1717
1824
  changes.push(...compareFieldConstraints(currentField, previousField));
1718
1825
  changes.push(...compareFieldOptions(currentField, previousField));
1719
1826
  if (currentField.type === "relation" && previousField.type === "relation") {
1720
- changes.push(...compareRelationConfigurations(currentField, previousField));
1827
+ changes.push(...compareRelationConfigurations(currentField, previousField, collectionIdToName));
1721
1828
  }
1722
1829
  return changes;
1723
1830
  }
@@ -1728,7 +1835,7 @@ function compareIndexes(currentIndexes = [], previousIndexes = []) {
1728
1835
  const indexesToRemove = previousIndexes.filter((idx) => !currentSet.has(idx));
1729
1836
  return { indexesToAdd, indexesToRemove };
1730
1837
  }
1731
- function compareRules(currentRules, previousRules) {
1838
+ function compareRules(currentRules, previousRules, currentPermissions, previousPermissions) {
1732
1839
  const updates = [];
1733
1840
  const ruleTypes = [
1734
1841
  "listRule",
@@ -1739,8 +1846,8 @@ function compareRules(currentRules, previousRules) {
1739
1846
  "manageRule"
1740
1847
  ];
1741
1848
  for (const ruleType of ruleTypes) {
1742
- const currentValue = currentRules?.[ruleType] ?? null;
1743
- const previousValue = previousRules?.[ruleType] ?? null;
1849
+ const currentValue = currentRules?.[ruleType] ?? currentPermissions?.[ruleType] ?? null;
1850
+ const previousValue = previousRules?.[ruleType] ?? previousPermissions?.[ruleType] ?? null;
1744
1851
  if (currentValue !== previousValue) {
1745
1852
  updates.push({
1746
1853
  ruleType,
@@ -1767,7 +1874,7 @@ function comparePermissions(currentPermissions, previousPermissions) {
1767
1874
  }
1768
1875
  return changes;
1769
1876
  }
1770
- function compareCollectionFields(currentCollection, previousCollection, config) {
1877
+ function compareCollectionFields(currentCollection, previousCollection, config, collectionIdToName) {
1771
1878
  let fieldsToAdd = findNewFields(currentCollection.fields, previousCollection.fields);
1772
1879
  const fieldsToRemove = findRemovedFields(currentCollection.fields, previousCollection.fields);
1773
1880
  const fieldsToModify = [];
@@ -1777,7 +1884,7 @@ function compareCollectionFields(currentCollection, previousCollection, config)
1777
1884
  }
1778
1885
  const matchedFields = matchFieldsByName(currentCollection.fields, previousCollection.fields);
1779
1886
  for (const [currentField, previousField] of matchedFields) {
1780
- const changes = detectFieldChanges(currentField, previousField);
1887
+ const changes = detectFieldChanges(currentField, previousField, collectionIdToName);
1781
1888
  if (changes.length > 0) {
1782
1889
  fieldsToModify.push({
1783
1890
  fieldName: currentField.name,
@@ -1789,14 +1896,20 @@ function compareCollectionFields(currentCollection, previousCollection, config)
1789
1896
  }
1790
1897
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
1791
1898
  }
1792
- function buildCollectionModification(currentCollection, previousCollection, config) {
1899
+ function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
1793
1900
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
1794
1901
  currentCollection,
1795
1902
  previousCollection,
1796
- config
1903
+ config,
1904
+ collectionIdToName
1797
1905
  );
1798
1906
  const { indexesToAdd, indexesToRemove } = compareIndexes(currentCollection.indexes, previousCollection.indexes);
1799
- const rulesToUpdate = compareRules(currentCollection.rules, previousCollection.rules);
1907
+ const rulesToUpdate = compareRules(
1908
+ currentCollection.rules,
1909
+ previousCollection.rules,
1910
+ currentCollection.permissions,
1911
+ previousCollection.permissions
1912
+ );
1800
1913
  const permissionsToUpdate = comparePermissions(currentCollection.permissions, previousCollection.permissions);
1801
1914
  return {
1802
1915
  collection: currentCollection.name,
@@ -1813,6 +1926,14 @@ function hasChanges(modification) {
1813
1926
  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;
1814
1927
  }
1815
1928
  function aggregateChanges(currentSchema, previousSnapshot, config) {
1929
+ const collectionIdToName = /* @__PURE__ */ new Map();
1930
+ if (previousSnapshot) {
1931
+ for (const [name, collection] of previousSnapshot.collections) {
1932
+ if (collection.id) {
1933
+ collectionIdToName.set(collection.id, name);
1934
+ }
1935
+ }
1936
+ }
1816
1937
  const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
1817
1938
  const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
1818
1939
  const filteredCollectionsToCreate = collectionsToCreate.filter(
@@ -1821,16 +1942,28 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1821
1942
  const filteredCollectionsToDelete = collectionsToDelete.filter(
1822
1943
  (collection) => !isSystemCollection(collection.name, config)
1823
1944
  );
1945
+ const registry = new CollectionIdRegistry();
1946
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
1947
+ if (collection.id) {
1948
+ registry.register(collection.id);
1949
+ return collection;
1950
+ }
1951
+ const id = registry.generate(collection.name);
1952
+ return {
1953
+ ...collection,
1954
+ id
1955
+ };
1956
+ });
1824
1957
  const collectionsToModify = [];
1825
1958
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
1826
1959
  for (const [currentCollection, previousCollection] of matchedCollections) {
1827
- const modification = buildCollectionModification(currentCollection, previousCollection, config);
1960
+ const modification = buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName);
1828
1961
  if (hasChanges(modification)) {
1829
1962
  collectionsToModify.push(modification);
1830
1963
  }
1831
1964
  }
1832
1965
  return {
1833
- collectionsToCreate: filteredCollectionsToCreate,
1966
+ collectionsToCreate: collectionsWithIds,
1834
1967
  collectionsToDelete: filteredCollectionsToDelete,
1835
1968
  collectionsToModify
1836
1969
  };
@@ -1913,42 +2046,48 @@ function generateTimestamp(config) {
1913
2046
  }
1914
2047
  return Math.floor(Date.now() / 1e3).toString();
1915
2048
  }
1916
- function generateMigrationDescription(diff) {
1917
- const parts = [];
1918
- if (diff.collectionsToCreate.length > 0) {
1919
- if (diff.collectionsToCreate.length === 1) {
1920
- parts.push(`created_${diff.collectionsToCreate[0].name}`);
1921
- } else {
1922
- parts.push(`created_${diff.collectionsToCreate.length}_collections`);
1923
- }
1924
- }
1925
- if (diff.collectionsToDelete.length > 0) {
1926
- if (diff.collectionsToDelete.length === 1) {
1927
- parts.push(`deleted_${diff.collectionsToDelete[0].name}`);
1928
- } else {
1929
- parts.push(`deleted_${diff.collectionsToDelete.length}_collections`);
1930
- }
1931
- }
1932
- if (diff.collectionsToModify.length > 0) {
1933
- if (diff.collectionsToModify.length === 1) {
1934
- parts.push(`updated_${diff.collectionsToModify[0].collection}`);
1935
- } else {
1936
- parts.push(`updated_${diff.collectionsToModify.length}_collections`);
1937
- }
2049
+ function splitDiffByCollection(diff, baseTimestamp) {
2050
+ const operations = [];
2051
+ let currentTimestamp = parseInt(baseTimestamp, 10);
2052
+ for (const collection of diff.collectionsToCreate) {
2053
+ operations.push({
2054
+ type: "create",
2055
+ collection,
2056
+ timestamp: currentTimestamp.toString()
2057
+ });
2058
+ currentTimestamp += 1;
1938
2059
  }
1939
- if (parts.length === 0) {
1940
- return "no_changes";
2060
+ for (const modification of diff.collectionsToModify) {
2061
+ operations.push({
2062
+ type: "modify",
2063
+ collection: modification.collection,
2064
+ modifications: modification,
2065
+ timestamp: currentTimestamp.toString()
2066
+ });
2067
+ currentTimestamp += 1;
1941
2068
  }
1942
- let description = parts.join("_");
1943
- if (description.length > 80) {
1944
- description = description.substring(0, 77) + "...";
2069
+ for (const collection of diff.collectionsToDelete) {
2070
+ operations.push({
2071
+ type: "delete",
2072
+ collection: collection.name || collection,
2073
+ // Handle both object and string
2074
+ timestamp: currentTimestamp.toString()
2075
+ });
2076
+ currentTimestamp += 1;
1945
2077
  }
1946
- return description;
2078
+ return operations;
1947
2079
  }
1948
- function generateMigrationFilename(diff, config) {
1949
- const timestamp = generateTimestamp(config);
1950
- const description = generateMigrationDescription(diff);
1951
- return `${timestamp}_${description}.js`;
2080
+ function generateCollectionMigrationFilename(operation) {
2081
+ const timestamp = operation.timestamp;
2082
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
2083
+ let collectionName;
2084
+ if (typeof operation.collection === "string") {
2085
+ collectionName = operation.collection;
2086
+ } else {
2087
+ collectionName = operation.collection.name;
2088
+ }
2089
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
2090
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
1952
2091
  }
1953
2092
  function createMigrationFileStructure(upCode, downCode, config) {
1954
2093
  const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
@@ -2020,14 +2159,13 @@ function formatValue(value) {
2020
2159
  return "null";
2021
2160
  }
2022
2161
  if (typeof value === "string") {
2023
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
2162
+ return JSON.stringify(value);
2024
2163
  }
2025
2164
  if (typeof value === "number" || typeof value === "boolean") {
2026
2165
  return String(value);
2027
2166
  }
2028
2167
  if (Array.isArray(value)) {
2029
- const items = value.map((v) => formatValue(v)).join(", ");
2030
- return `[${items}]`;
2168
+ return JSON.stringify(value).replace(/","/g, '", "');
2031
2169
  }
2032
2170
  if (typeof value === "object") {
2033
2171
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -2035,7 +2173,7 @@ function formatValue(value) {
2035
2173
  }
2036
2174
  return String(value);
2037
2175
  }
2038
- function generateFieldDefinitionObject(field) {
2176
+ function generateFieldDefinitionObject(field, collectionIdMap) {
2039
2177
  const parts = [];
2040
2178
  parts.push(` name: "${field.name}"`);
2041
2179
  parts.push(` type: "${field.type}"`);
@@ -2043,34 +2181,47 @@ function generateFieldDefinitionObject(field) {
2043
2181
  if (field.unique !== void 0) {
2044
2182
  parts.push(` unique: ${field.unique}`);
2045
2183
  }
2184
+ if (field.type === "select") {
2185
+ const maxSelect = field.options?.maxSelect ?? 1;
2186
+ parts.push(` maxSelect: ${maxSelect}`);
2187
+ const values = field.options?.values ?? [];
2188
+ parts.push(` values: ${formatValue(values)}`);
2189
+ }
2046
2190
  if (field.options && Object.keys(field.options).length > 0) {
2047
2191
  for (const [key, value] of Object.entries(field.options)) {
2192
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2193
+ continue;
2194
+ }
2048
2195
  parts.push(` ${key}: ${formatValue(value)}`);
2049
2196
  }
2050
2197
  }
2051
2198
  if (field.relation) {
2052
2199
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2053
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2054
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2055
- if (field.relation.maxSelect !== void 0) {
2056
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2057
- }
2058
- if (field.relation.minSelect !== void 0) {
2059
- parts.push(` minSelect: ${field.relation.minSelect}`);
2060
- }
2061
- if (field.relation.cascadeDelete !== void 0) {
2062
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2200
+ let collectionIdValue;
2201
+ if (isUsersCollection) {
2202
+ collectionIdValue = '"_pb_users_auth_"';
2203
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2204
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2205
+ } else {
2206
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2063
2207
  }
2208
+ parts.push(` collectionId: ${collectionIdValue}`);
2209
+ const maxSelect = field.relation.maxSelect ?? 1;
2210
+ parts.push(` maxSelect: ${maxSelect}`);
2211
+ const minSelect = field.relation.minSelect ?? null;
2212
+ parts.push(` minSelect: ${minSelect}`);
2213
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2214
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2064
2215
  }
2065
2216
  return ` {
2066
2217
  ${parts.join(",\n")},
2067
2218
  }`;
2068
2219
  }
2069
- function generateFieldsArray(fields) {
2220
+ function generateFieldsArray(fields, collectionIdMap) {
2070
2221
  if (fields.length === 0) {
2071
2222
  return "[]";
2072
2223
  }
2073
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
2224
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
2074
2225
  return `[
2075
2226
  ${fieldObjects.join(",\n")},
2076
2227
  ]`;
@@ -2129,7 +2280,7 @@ function generateIndexesArray(indexes) {
2129
2280
  if (!indexes || indexes.length === 0) {
2130
2281
  return "[]";
2131
2282
  }
2132
- const indexStrings = indexes.map((idx) => `"${idx}"`);
2283
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
2133
2284
  return `[
2134
2285
  ${indexStrings.join(",\n ")},
2135
2286
  ]`;
@@ -2183,9 +2334,12 @@ function getSystemFields() {
2183
2334
  }
2184
2335
  ];
2185
2336
  }
2186
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2337
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
2187
2338
  const lines = [];
2188
2339
  lines.push(` const ${varName} = new Collection({`);
2340
+ if (collection.id) {
2341
+ lines.push(` id: ${formatValue(collection.id)},`);
2342
+ }
2189
2343
  lines.push(` name: "${collection.name}",`);
2190
2344
  lines.push(` type: "${collection.type}",`);
2191
2345
  const permissionsCode = generateCollectionPermissions(collection.permissions);
@@ -2197,7 +2351,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
2197
2351
  }
2198
2352
  const systemFields = getSystemFields();
2199
2353
  const allFields = [...systemFields, ...collection.fields];
2200
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
2354
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
2201
2355
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2202
2356
  lines.push(` });`);
2203
2357
  lines.push(``);
@@ -2219,42 +2373,59 @@ function getFieldConstructorName(fieldType) {
2219
2373
  };
2220
2374
  return constructorMap[fieldType] || "TextField";
2221
2375
  }
2222
- function generateFieldConstructorOptions(field) {
2376
+ function generateFieldConstructorOptions(field, collectionIdMap) {
2223
2377
  const parts = [];
2224
2378
  parts.push(` name: "${field.name}"`);
2225
2379
  parts.push(` required: ${field.required}`);
2226
2380
  if (field.unique !== void 0) {
2227
2381
  parts.push(` unique: ${field.unique}`);
2228
2382
  }
2383
+ if (field.type === "select") {
2384
+ const maxSelect = field.options?.maxSelect ?? 1;
2385
+ parts.push(` maxSelect: ${maxSelect}`);
2386
+ const values = field.options?.values ?? [];
2387
+ parts.push(` values: ${formatValue(values)}`);
2388
+ }
2229
2389
  if (field.options && Object.keys(field.options).length > 0) {
2230
2390
  for (const [key, value] of Object.entries(field.options)) {
2231
- parts.push(` ${key}: ${formatValue(value)}`);
2391
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2392
+ continue;
2393
+ }
2394
+ if (field.type === "number" && key === "noDecimal") {
2395
+ parts.push(` onlyInt: ${formatValue(value)}`);
2396
+ } else {
2397
+ parts.push(` ${key}: ${formatValue(value)}`);
2398
+ }
2232
2399
  }
2233
2400
  }
2234
2401
  if (field.relation && field.type === "relation") {
2235
2402
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2236
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2237
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2238
- if (field.relation.maxSelect !== void 0) {
2239
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2240
- }
2241
- if (field.relation.minSelect !== void 0) {
2242
- parts.push(` minSelect: ${field.relation.minSelect}`);
2243
- }
2244
- if (field.relation.cascadeDelete !== void 0) {
2245
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2403
+ let collectionIdValue;
2404
+ if (isUsersCollection) {
2405
+ collectionIdValue = '"_pb_users_auth_"';
2406
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2407
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2408
+ } else {
2409
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2246
2410
  }
2411
+ parts.push(` collectionId: ${collectionIdValue}`);
2412
+ const maxSelect = field.relation.maxSelect ?? 1;
2413
+ parts.push(` maxSelect: ${maxSelect}`);
2414
+ const minSelect = field.relation.minSelect ?? null;
2415
+ parts.push(` minSelect: ${minSelect}`);
2416
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2417
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2247
2418
  }
2248
2419
  return parts.join(",\n");
2249
2420
  }
2250
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
2421
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
2251
2422
  const lines = [];
2252
2423
  const constructorName = getFieldConstructorName(field.type);
2253
2424
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
2254
2425
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2255
2426
  lines.push(``);
2256
2427
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
2257
- lines.push(generateFieldConstructorOptions(field));
2428
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
2258
2429
  lines.push(` }));`);
2259
2430
  lines.push(``);
2260
2431
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -2304,7 +2475,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2304
2475
  const lines = [];
2305
2476
  const collectionVar = varName || `collection_${collectionName}_idx`;
2306
2477
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2307
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
2478
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
2308
2479
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2309
2480
  return lines.join("\n");
2310
2481
  }
@@ -2313,7 +2484,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2313
2484
  const collectionVar = varName || `collection_${collectionName}_idx`;
2314
2485
  const indexVar = `${collectionVar}_indexToRemove`;
2315
2486
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2316
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
2487
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
2317
2488
  lines.push(` if (${indexVar} !== -1) {`);
2318
2489
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2319
2490
  lines.push(` }`);
@@ -2342,94 +2513,75 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
2342
2513
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2343
2514
  return lines.join("\n");
2344
2515
  }
2345
- function generateUpMigration(diff) {
2516
+ function generateOperationUpMigration(operation, collectionIdMap) {
2346
2517
  const lines = [];
2347
- lines.push(` // UP MIGRATION`);
2348
- lines.push(``);
2349
- if (diff.collectionsToCreate.length > 0) {
2350
- lines.push(` // Create new collections`);
2351
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2352
- const collection = diff.collectionsToCreate[i];
2353
- const varName = `collection_${collection.name}_create`;
2354
- lines.push(generateCollectionCreation(collection, varName));
2355
- lines.push(``);
2518
+ if (operation.type === "create") {
2519
+ const collection = operation.collection;
2520
+ const varName = `collection_${collection.name}`;
2521
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2522
+ } else if (operation.type === "modify") {
2523
+ const modification = operation.modifications;
2524
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2525
+ let operationCount = 0;
2526
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2527
+ for (const field of modification.fieldsToAdd) {
2528
+ operationCount++;
2529
+ const varName = `collection_${collectionName}_add_${field.name}`;
2530
+ const isLast = operationCount === totalOperations;
2531
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2532
+ if (!isLast) lines.push("");
2356
2533
  }
2357
- }
2358
- if (diff.collectionsToModify.length > 0) {
2359
- lines.push(` // Modify existing collections`);
2360
- for (const modification of diff.collectionsToModify) {
2361
- const collectionName = modification.collection;
2362
- if (modification.fieldsToAdd.length > 0) {
2363
- lines.push(` // Add fields to ${collectionName}`);
2364
- for (const field of modification.fieldsToAdd) {
2365
- const varName = `collection_${collectionName}_add_${field.name}`;
2366
- lines.push(generateFieldAddition(collectionName, field, varName));
2367
- lines.push(``);
2368
- }
2369
- }
2370
- if (modification.fieldsToModify.length > 0) {
2371
- lines.push(` // Modify fields in ${collectionName}`);
2372
- for (const fieldMod of modification.fieldsToModify) {
2373
- const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2374
- lines.push(generateFieldModification(collectionName, fieldMod, varName));
2375
- lines.push(``);
2376
- }
2377
- }
2378
- if (modification.fieldsToRemove.length > 0) {
2379
- lines.push(` // Remove fields from ${collectionName}`);
2380
- for (const field of modification.fieldsToRemove) {
2381
- const varName = `collection_${collectionName}_remove_${field.name}`;
2382
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2383
- lines.push(``);
2384
- }
2385
- }
2386
- if (modification.indexesToAdd.length > 0) {
2387
- lines.push(` // Add indexes to ${collectionName}`);
2388
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2389
- const index = modification.indexesToAdd[i];
2390
- const varName = `collection_${collectionName}_addidx_${i}`;
2391
- lines.push(generateIndexAddition(collectionName, index, varName));
2392
- lines.push(``);
2393
- }
2394
- }
2395
- if (modification.indexesToRemove.length > 0) {
2396
- lines.push(` // Remove indexes from ${collectionName}`);
2397
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2398
- const index = modification.indexesToRemove[i];
2399
- const varName = `collection_${collectionName}_rmidx_${i}`;
2400
- lines.push(generateIndexRemoval(collectionName, index, varName));
2401
- lines.push(``);
2402
- }
2534
+ for (const fieldMod of modification.fieldsToModify) {
2535
+ operationCount++;
2536
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2537
+ const isLast = operationCount === totalOperations;
2538
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
2539
+ if (!isLast) lines.push("");
2540
+ }
2541
+ for (const field of modification.fieldsToRemove) {
2542
+ operationCount++;
2543
+ const varName = `collection_${collectionName}_remove_${field.name}`;
2544
+ const isLast = operationCount === totalOperations;
2545
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2546
+ if (!isLast) lines.push("");
2547
+ }
2548
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2549
+ operationCount++;
2550
+ const index = modification.indexesToAdd[i];
2551
+ const varName = `collection_${collectionName}_addidx_${i}`;
2552
+ const isLast = operationCount === totalOperations;
2553
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2554
+ if (!isLast) lines.push("");
2555
+ }
2556
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2557
+ operationCount++;
2558
+ const index = modification.indexesToRemove[i];
2559
+ const varName = `collection_${collectionName}_rmidx_${i}`;
2560
+ const isLast = operationCount === totalOperations;
2561
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2562
+ if (!isLast) lines.push("");
2563
+ }
2564
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2565
+ for (const permission of modification.permissionsToUpdate) {
2566
+ operationCount++;
2567
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2568
+ const isLast = operationCount === totalOperations;
2569
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
2570
+ if (!isLast) lines.push("");
2403
2571
  }
2404
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2405
- lines.push(` // Update permissions for ${collectionName}`);
2406
- for (const permission of modification.permissionsToUpdate) {
2407
- const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2408
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName));
2409
- lines.push(``);
2410
- }
2411
- } else if (modification.rulesToUpdate.length > 0) {
2412
- lines.push(` // Update rules for ${collectionName}`);
2413
- for (const rule of modification.rulesToUpdate) {
2414
- const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2415
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName));
2416
- lines.push(``);
2417
- }
2572
+ } else if (modification.rulesToUpdate.length > 0) {
2573
+ for (const rule of modification.rulesToUpdate) {
2574
+ operationCount++;
2575
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2576
+ const isLast = operationCount === totalOperations;
2577
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
2578
+ if (!isLast) lines.push("");
2418
2579
  }
2419
2580
  }
2420
- }
2421
- if (diff.collectionsToDelete.length > 0) {
2422
- lines.push(` // Delete collections`);
2423
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2424
- const collection = diff.collectionsToDelete[i];
2425
- const varName = `collection_${collection.name}_delete`;
2426
- lines.push(generateCollectionDeletion(collection.name, varName));
2427
- lines.push(``);
2428
- }
2429
- }
2430
- if (lines.length === 2) {
2431
- lines.push(` // No changes detected`);
2432
- lines.push(``);
2581
+ } else if (operation.type === "delete") {
2582
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
2583
+ const varName = `collection_${collectionName}`;
2584
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
2433
2585
  }
2434
2586
  let code = lines.join("\n");
2435
2587
  const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
@@ -2450,105 +2602,88 @@ function generateUpMigration(diff) {
2450
2602
  }
2451
2603
  return code;
2452
2604
  }
2453
- function generateDownMigration(diff) {
2605
+ function generateOperationDownMigration(operation, collectionIdMap) {
2454
2606
  const lines = [];
2455
- lines.push(` // DOWN MIGRATION (ROLLBACK)`);
2456
- lines.push(``);
2457
- if (diff.collectionsToDelete.length > 0) {
2458
- lines.push(` // Recreate deleted collections`);
2459
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2460
- const collection = diff.collectionsToDelete[i];
2461
- const varName = `collection_${collection.name}_recreate`;
2462
- lines.push(generateCollectionCreation(collection, varName));
2463
- lines.push(``);
2464
- }
2465
- }
2466
- if (diff.collectionsToModify.length > 0) {
2467
- lines.push(` // Revert modifications`);
2468
- for (const modification of diff.collectionsToModify) {
2469
- const collectionName = modification.collection;
2470
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2471
- lines.push(` // Revert permissions for ${collectionName}`);
2472
- for (const permission of modification.permissionsToUpdate) {
2473
- const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2474
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName));
2475
- lines.push(``);
2476
- }
2477
- } else if (modification.rulesToUpdate.length > 0) {
2478
- lines.push(` // Revert rules for ${collectionName}`);
2479
- for (const rule of modification.rulesToUpdate) {
2480
- const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2481
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName));
2482
- lines.push(``);
2483
- }
2484
- }
2485
- if (modification.indexesToRemove.length > 0) {
2486
- lines.push(` // Restore indexes to ${collectionName}`);
2487
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2488
- const index = modification.indexesToRemove[i];
2489
- const varName = `collection_${collectionName}_restore_idx_${i}`;
2490
- lines.push(generateIndexAddition(collectionName, index, varName));
2491
- lines.push(``);
2492
- }
2493
- }
2494
- if (modification.indexesToAdd.length > 0) {
2495
- lines.push(` // Remove indexes from ${collectionName}`);
2496
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2497
- const index = modification.indexesToAdd[i];
2498
- const varName = `collection_${collectionName}_revert_idx_${i}`;
2499
- lines.push(generateIndexRemoval(collectionName, index, varName));
2500
- lines.push(``);
2501
- }
2502
- }
2503
- if (modification.fieldsToRemove.length > 0) {
2504
- lines.push(` // Restore fields to ${collectionName}`);
2505
- for (const field of modification.fieldsToRemove) {
2506
- const varName = `collection_${collectionName}_restore_${field.name}`;
2507
- lines.push(generateFieldAddition(collectionName, field, varName));
2508
- lines.push(``);
2509
- }
2607
+ if (operation.type === "create") {
2608
+ const collection = operation.collection;
2609
+ const varName = `collection_${collection.name}`;
2610
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
2611
+ } else if (operation.type === "modify") {
2612
+ const modification = operation.modifications;
2613
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2614
+ let operationCount = 0;
2615
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2616
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2617
+ for (const permission of modification.permissionsToUpdate) {
2618
+ operationCount++;
2619
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2620
+ const isLast = operationCount === totalOperations;
2621
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
2622
+ if (!isLast) lines.push("");
2510
2623
  }
2511
- if (modification.fieldsToModify.length > 0) {
2512
- lines.push(` // Revert field modifications in ${collectionName}`);
2513
- for (const fieldMod of modification.fieldsToModify) {
2514
- const reverseChanges = fieldMod.changes.map((change) => ({
2515
- property: change.property,
2516
- oldValue: change.newValue,
2517
- newValue: change.oldValue
2518
- }));
2519
- const reverseMod = {
2520
- fieldName: fieldMod.fieldName,
2521
- currentDefinition: fieldMod.newDefinition,
2522
- newDefinition: fieldMod.currentDefinition,
2523
- changes: reverseChanges
2524
- };
2525
- const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2526
- lines.push(generateFieldModification(collectionName, reverseMod, varName));
2527
- lines.push(``);
2528
- }
2529
- }
2530
- if (modification.fieldsToAdd.length > 0) {
2531
- lines.push(` // Remove added fields from ${collectionName}`);
2532
- for (const field of modification.fieldsToAdd) {
2533
- const varName = `collection_${collectionName}_revert_add_${field.name}`;
2534
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2535
- lines.push(``);
2536
- }
2624
+ } else if (modification.rulesToUpdate.length > 0) {
2625
+ for (const rule of modification.rulesToUpdate) {
2626
+ operationCount++;
2627
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2628
+ const isLast = operationCount === totalOperations;
2629
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
2630
+ if (!isLast) lines.push("");
2537
2631
  }
2538
2632
  }
2539
- }
2540
- if (diff.collectionsToCreate.length > 0) {
2541
- lines.push(` // Delete created collections`);
2542
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2543
- const collection = diff.collectionsToCreate[i];
2544
- const varName = `collection_${collection.name}_rollback`;
2545
- lines.push(generateCollectionDeletion(collection.name, varName));
2546
- lines.push(``);
2633
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2634
+ operationCount++;
2635
+ const index = modification.indexesToRemove[i];
2636
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
2637
+ const isLast = operationCount === totalOperations;
2638
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2639
+ if (!isLast) lines.push("");
2640
+ }
2641
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2642
+ operationCount++;
2643
+ const index = modification.indexesToAdd[i];
2644
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
2645
+ const isLast = operationCount === totalOperations;
2646
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2647
+ if (!isLast) lines.push("");
2648
+ }
2649
+ for (const field of modification.fieldsToRemove) {
2650
+ operationCount++;
2651
+ const varName = `collection_${collectionName}_restore_${field.name}`;
2652
+ const isLast = operationCount === totalOperations;
2653
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2654
+ if (!isLast) lines.push("");
2655
+ }
2656
+ for (const fieldMod of modification.fieldsToModify) {
2657
+ operationCount++;
2658
+ const reverseChanges = fieldMod.changes.map((change) => ({
2659
+ property: change.property,
2660
+ oldValue: change.newValue,
2661
+ newValue: change.oldValue
2662
+ }));
2663
+ const reverseMod = {
2664
+ fieldName: fieldMod.fieldName,
2665
+ currentDefinition: fieldMod.newDefinition,
2666
+ newDefinition: fieldMod.currentDefinition,
2667
+ changes: reverseChanges
2668
+ };
2669
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2670
+ const isLast = operationCount === totalOperations;
2671
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
2672
+ if (!isLast) lines.push("");
2673
+ }
2674
+ for (const field of modification.fieldsToAdd) {
2675
+ operationCount++;
2676
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
2677
+ const isLast = operationCount === totalOperations;
2678
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2679
+ if (!isLast) lines.push("");
2680
+ }
2681
+ } else if (operation.type === "delete") {
2682
+ const collection = operation.collection;
2683
+ if (typeof collection !== "string") {
2684
+ const varName = `collection_${collection.name}`;
2685
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2547
2686
  }
2548
- }
2549
- if (lines.length === 2) {
2550
- lines.push(` // No changes to revert`);
2551
- lines.push(``);
2552
2687
  }
2553
2688
  let code = lines.join("\n");
2554
2689
  const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
@@ -2573,12 +2708,33 @@ function generate(diff, config) {
2573
2708
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
2574
2709
  try {
2575
2710
  const migrationDir = resolveMigrationDir(normalizedConfig);
2576
- const upCode = generateUpMigration(diff);
2577
- const downCode = generateDownMigration(diff);
2578
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2579
- const filename = generateMigrationFilename(diff, normalizedConfig);
2580
- const filePath = writeMigrationFile(migrationDir, filename, content);
2581
- return filePath;
2711
+ const hasChanges4 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
2712
+ if (!hasChanges4) {
2713
+ return [];
2714
+ }
2715
+ const collectionIdMap = /* @__PURE__ */ new Map();
2716
+ for (const collection of diff.collectionsToCreate) {
2717
+ if (collection.id) {
2718
+ collectionIdMap.set(collection.name, collection.id);
2719
+ }
2720
+ }
2721
+ for (const collection of diff.collectionsToDelete) {
2722
+ if (collection.id) {
2723
+ collectionIdMap.set(collection.name, collection.id);
2724
+ }
2725
+ }
2726
+ const baseTimestamp = generateTimestamp(normalizedConfig);
2727
+ const operations = splitDiffByCollection(diff, baseTimestamp);
2728
+ const filePaths = [];
2729
+ for (const operation of operations) {
2730
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
2731
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
2732
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2733
+ const filename = generateCollectionMigrationFilename(operation);
2734
+ const filePath = writeMigrationFile(migrationDir, filename, content);
2735
+ filePaths.push(filePath);
2736
+ }
2737
+ return filePaths;
2582
2738
  } catch (error) {
2583
2739
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
2584
2740
  throw error;
@@ -2595,7 +2751,7 @@ function generate(diff, config) {
2595
2751
  var SNAPSHOT_VERSION = "1.0.0";
2596
2752
  function resolveCollectionIdToName(collectionId) {
2597
2753
  if (collectionId === "_pb_users_auth_") {
2598
- return "Users";
2754
+ return "users";
2599
2755
  }
2600
2756
  const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2601
2757
  if (nameMatch) {
@@ -2603,6 +2759,39 @@ function resolveCollectionIdToName(collectionId) {
2603
2759
  }
2604
2760
  return collectionId;
2605
2761
  }
2762
+ function extractFieldOptions2(pbField) {
2763
+ const options = {};
2764
+ if (pbField.options && typeof pbField.options === "object") {
2765
+ Object.assign(options, pbField.options);
2766
+ }
2767
+ const directOptionKeys = [
2768
+ "min",
2769
+ "max",
2770
+ "pattern",
2771
+ "noDecimal",
2772
+ // text/number fields
2773
+ "values",
2774
+ "maxSelect",
2775
+ // select fields
2776
+ "mimeTypes",
2777
+ "maxSize",
2778
+ "thumbs",
2779
+ "protected",
2780
+ // file fields
2781
+ "onCreate",
2782
+ "onUpdate",
2783
+ // autodate fields
2784
+ "exceptDomains",
2785
+ "onlyDomains"
2786
+ // email/url fields
2787
+ ];
2788
+ for (const key of directOptionKeys) {
2789
+ if (pbField[key] !== void 0) {
2790
+ options[key] = pbField[key];
2791
+ }
2792
+ }
2793
+ return options;
2794
+ }
2606
2795
  function convertPocketBaseCollection(pbCollection) {
2607
2796
  const fields = [];
2608
2797
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
@@ -2620,23 +2809,19 @@ function convertPocketBaseCollection(pbCollection) {
2620
2809
  type: pbField.type,
2621
2810
  required: pbField.required || false
2622
2811
  };
2623
- field.options = pbField.options ? { ...pbField.options } : {};
2624
- if (pbField.type === "select") {
2625
- if (pbField.values && Array.isArray(pbField.values)) {
2626
- field.options.values = pbField.values;
2627
- } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2628
- field.options.values = pbField.options.values;
2629
- }
2630
- }
2812
+ field.options = extractFieldOptions2(pbField);
2631
2813
  if (pbField.type === "relation") {
2632
2814
  const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2633
- const collectionName = resolveCollectionIdToName(collectionId);
2815
+ const collectionName = resolveCollectionIdToName(collectionId || "");
2634
2816
  field.relation = {
2635
2817
  collection: collectionName,
2636
2818
  cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2637
2819
  maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2638
2820
  minSelect: pbField.minSelect ?? pbField.options?.minSelect
2639
2821
  };
2822
+ delete field.options.maxSelect;
2823
+ delete field.options.minSelect;
2824
+ delete field.options.cascadeDelete;
2640
2825
  }
2641
2826
  const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2642
2827
  if (Object.keys(field.options).length === 0) {
@@ -2650,17 +2835,21 @@ function convertPocketBaseCollection(pbCollection) {
2650
2835
  type: pbCollection.type || "base",
2651
2836
  fields
2652
2837
  };
2838
+ if (pbCollection.id) {
2839
+ schema.id = pbCollection.id;
2840
+ }
2653
2841
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2654
2842
  schema.indexes = pbCollection.indexes;
2655
2843
  }
2656
- const rules = {};
2657
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2658
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2659
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2660
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2661
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2662
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2663
- if (Object.keys(rules).length > 0) {
2844
+ 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;
2845
+ if (hasAnyRule) {
2846
+ const rules = {};
2847
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2848
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2849
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2850
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2851
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2852
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2664
2853
  schema.rules = rules;
2665
2854
  schema.permissions = { ...rules };
2666
2855
  }
@@ -3659,15 +3848,25 @@ async function executeGenerate(options) {
3659
3848
  process.exit(1);
3660
3849
  }
3661
3850
  logSection("\u{1F4DD} Generating Migration");
3662
- const migrationPath = await withProgress(
3851
+ const migrationPaths = await withProgress(
3663
3852
  "Creating migration file...",
3664
3853
  () => Promise.resolve(generate(diff, migrationsDir))
3665
3854
  );
3666
- logSuccess(`Migration file created: ${path5__namespace.basename(migrationPath)}`);
3855
+ if (migrationPaths.length === 0) {
3856
+ logWarning("No migration files were generated (no changes detected).");
3857
+ return;
3858
+ }
3859
+ if (migrationPaths.length === 1) {
3860
+ logSuccess(`Migration file created: ${path5__namespace.basename(migrationPaths[0])}`);
3861
+ } else {
3862
+ logSuccess(`Created ${migrationPaths.length} migration files`);
3863
+ }
3667
3864
  logSection("\u2705 Next Steps");
3668
3865
  console.log();
3669
- console.log(" 1. Review the generated migration file:");
3670
- console.log(` ${migrationPath}`);
3866
+ console.log(" 1. Review the generated migration file(s):");
3867
+ migrationPaths.forEach((migrationPath) => {
3868
+ console.log(` ${migrationPath}`);
3869
+ });
3671
3870
  console.log();
3672
3871
  console.log(" 2. Apply the migration by running PocketBase:");
3673
3872
  console.log(" yarn pb");