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
@@ -7,6 +7,7 @@ import * as path5 from 'path';
7
7
  import { dirname, join } from 'path';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { z } from 'zod';
10
+ import { randomBytes } from 'crypto';
10
11
  import ora from 'ora';
11
12
 
12
13
  ({
@@ -1292,12 +1293,22 @@ function isAuthCollection(fields) {
1292
1293
  function buildFieldDefinition(fieldName, zodType) {
1293
1294
  const fieldMetadata = extractFieldMetadata(zodType.description);
1294
1295
  if (fieldMetadata) {
1295
- const required2 = isFieldRequired(zodType);
1296
+ let required2;
1297
+ if (fieldMetadata.type === "number") {
1298
+ if (fieldMetadata.options?.required !== void 0) {
1299
+ required2 = fieldMetadata.options.required;
1300
+ } else {
1301
+ required2 = false;
1302
+ }
1303
+ } else {
1304
+ required2 = isFieldRequired(zodType);
1305
+ }
1306
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1296
1307
  const fieldDef2 = {
1297
1308
  name: fieldName,
1298
1309
  type: fieldMetadata.type,
1299
1310
  required: required2,
1300
- options: fieldMetadata.options
1311
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1301
1312
  };
1302
1313
  if (fieldMetadata.type === "relation") {
1303
1314
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -1467,6 +1478,67 @@ async function buildSchemaDefinition(config) {
1467
1478
  async function parseSchemaFiles(config) {
1468
1479
  return buildSchemaDefinition(config);
1469
1480
  }
1481
+ function generateCollectionId() {
1482
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
1483
+ const idLength = 15;
1484
+ const bytes = randomBytes(idLength);
1485
+ let id = "pb_";
1486
+ for (let i = 0; i < idLength; i++) {
1487
+ const index = bytes[i] % chars.length;
1488
+ id += chars[index];
1489
+ }
1490
+ return id;
1491
+ }
1492
+ var CollectionIdRegistry = class {
1493
+ ids;
1494
+ constructor() {
1495
+ this.ids = /* @__PURE__ */ new Set();
1496
+ }
1497
+ /**
1498
+ * Generates a unique collection ID for a given collection name
1499
+ * Retries up to 10 times if collision occurs (extremely rare)
1500
+ * Special case: returns "_pb_users_auth_" for users collection
1501
+ *
1502
+ * @param collectionName - The name of the collection (optional)
1503
+ * @returns A unique collection ID
1504
+ * @throws Error if unable to generate unique ID after max attempts
1505
+ */
1506
+ generate(collectionName) {
1507
+ if (collectionName && collectionName.toLowerCase() === "users") {
1508
+ const usersId = "_pb_users_auth_";
1509
+ if (!this.has(usersId)) {
1510
+ this.register(usersId);
1511
+ }
1512
+ return usersId;
1513
+ }
1514
+ const maxAttempts = 10;
1515
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1516
+ const id = generateCollectionId();
1517
+ if (!this.has(id)) {
1518
+ this.register(id);
1519
+ return id;
1520
+ }
1521
+ }
1522
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
1523
+ }
1524
+ /**
1525
+ * Checks if an ID has already been registered
1526
+ *
1527
+ * @param id - The collection ID to check
1528
+ * @returns True if the ID exists in the registry
1529
+ */
1530
+ has(id) {
1531
+ return this.ids.has(id);
1532
+ }
1533
+ /**
1534
+ * Registers a collection ID in the registry
1535
+ *
1536
+ * @param id - The collection ID to register
1537
+ */
1538
+ register(id) {
1539
+ this.ids.add(id);
1540
+ }
1541
+ };
1470
1542
 
1471
1543
  // src/migration/diff.ts
1472
1544
  var DEFAULT_CONFIG2 = {
@@ -1609,18 +1681,49 @@ function compareFieldConstraints(currentField, previousField) {
1609
1681
  }
1610
1682
  return changes;
1611
1683
  }
1684
+ function normalizeOptionValue(key, value, fieldType) {
1685
+ if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
1686
+ return void 0;
1687
+ }
1688
+ if (key === "maxSize" && value === 0 && fieldType === "file") {
1689
+ return void 0;
1690
+ }
1691
+ if (fieldType === "file") {
1692
+ if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
1693
+ return void 0;
1694
+ }
1695
+ if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
1696
+ return void 0;
1697
+ }
1698
+ if (key === "protected" && value === false) {
1699
+ return void 0;
1700
+ }
1701
+ }
1702
+ if (fieldType === "autodate") {
1703
+ if (key === "onCreate" && value === true) {
1704
+ return void 0;
1705
+ }
1706
+ if (key === "onUpdate" && value === false) {
1707
+ return void 0;
1708
+ }
1709
+ }
1710
+ return value;
1711
+ }
1612
1712
  function compareFieldOptions(currentField, previousField) {
1613
1713
  const changes = [];
1614
1714
  const currentOptions = currentField.options || {};
1615
1715
  const previousOptions = previousField.options || {};
1616
1716
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(currentOptions), ...Object.keys(previousOptions)]);
1717
+ const fieldType = currentField.type;
1617
1718
  for (const key of allKeys) {
1618
1719
  const currentValue = currentOptions[key];
1619
1720
  const previousValue = previousOptions[key];
1620
- if (currentValue === void 0 && previousValue === void 0) {
1721
+ const normalizedCurrent = normalizeOptionValue(key, currentValue, fieldType);
1722
+ const normalizedPrevious = normalizeOptionValue(key, previousValue, fieldType);
1723
+ if (normalizedCurrent === void 0 && normalizedPrevious === void 0) {
1621
1724
  continue;
1622
1725
  }
1623
- if (!areValuesEqual(currentValue, previousValue)) {
1726
+ if (!areValuesEqual(normalizedCurrent, normalizedPrevious)) {
1624
1727
  changes.push({
1625
1728
  property: `options.${key}`,
1626
1729
  oldValue: previousValue,
@@ -1630,7 +1733,7 @@ function compareFieldOptions(currentField, previousField) {
1630
1733
  }
1631
1734
  return changes;
1632
1735
  }
1633
- function compareRelationConfigurations(currentField, previousField) {
1736
+ function compareRelationConfigurations(currentField, previousField, collectionIdToName) {
1634
1737
  const changes = [];
1635
1738
  const currentRelation = currentField.relation;
1636
1739
  const previousRelation = previousField.relation;
@@ -1642,8 +1745,8 @@ function compareRelationConfigurations(currentField, previousField) {
1642
1745
  }
1643
1746
  const normalizeCollection = (collection) => {
1644
1747
  if (!collection) return collection;
1645
- if (collection === "_pb_users_auth_") {
1646
- return "Users";
1748
+ if (collectionIdToName && collectionIdToName.has(collection)) {
1749
+ return collectionIdToName.get(collection);
1647
1750
  }
1648
1751
  const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1649
1752
  if (nameMatch) {
@@ -1653,13 +1756,11 @@ function compareRelationConfigurations(currentField, previousField) {
1653
1756
  };
1654
1757
  const normalizedCurrent = normalizeCollection(currentRelation.collection);
1655
1758
  const normalizedPrevious = normalizeCollection(previousRelation.collection);
1656
- if (normalizedCurrent !== normalizedPrevious) {
1759
+ if (normalizedCurrent.toLowerCase() !== normalizedPrevious.toLowerCase()) {
1657
1760
  changes.push({
1658
1761
  property: "relation.collection",
1659
- oldValue: normalizedPrevious,
1660
- // Use normalized value for clarity
1661
- newValue: normalizedCurrent
1662
- // Use normalized value for clarity
1762
+ oldValue: previousRelation.collection,
1763
+ newValue: currentRelation.collection
1663
1764
  });
1664
1765
  }
1665
1766
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -1669,14 +1770,20 @@ function compareRelationConfigurations(currentField, previousField) {
1669
1770
  newValue: currentRelation.cascadeDelete
1670
1771
  });
1671
1772
  }
1672
- if (currentRelation.maxSelect !== previousRelation.maxSelect) {
1773
+ const normalizeMax = (val) => val === 1 ? null : val;
1774
+ const currentMax = normalizeMax(currentRelation.maxSelect);
1775
+ const previousMax = normalizeMax(previousRelation.maxSelect);
1776
+ if (currentMax != previousMax) {
1673
1777
  changes.push({
1674
1778
  property: "relation.maxSelect",
1675
1779
  oldValue: previousRelation.maxSelect,
1676
1780
  newValue: currentRelation.maxSelect
1677
1781
  });
1678
1782
  }
1679
- if (currentRelation.minSelect !== previousRelation.minSelect) {
1783
+ const normalizeMin = (val) => val === 0 ? null : val;
1784
+ const currentMin = normalizeMin(currentRelation.minSelect);
1785
+ const previousMin = normalizeMin(previousRelation.minSelect);
1786
+ if (currentMin != previousMin) {
1680
1787
  changes.push({
1681
1788
  property: "relation.minSelect",
1682
1789
  oldValue: previousRelation.minSelect,
@@ -1685,7 +1792,7 @@ function compareRelationConfigurations(currentField, previousField) {
1685
1792
  }
1686
1793
  return changes;
1687
1794
  }
1688
- function detectFieldChanges(currentField, previousField) {
1795
+ function detectFieldChanges(currentField, previousField, collectionIdToName) {
1689
1796
  const changes = [];
1690
1797
  const typeChange = compareFieldTypes(currentField, previousField);
1691
1798
  if (typeChange) {
@@ -1694,7 +1801,7 @@ function detectFieldChanges(currentField, previousField) {
1694
1801
  changes.push(...compareFieldConstraints(currentField, previousField));
1695
1802
  changes.push(...compareFieldOptions(currentField, previousField));
1696
1803
  if (currentField.type === "relation" && previousField.type === "relation") {
1697
- changes.push(...compareRelationConfigurations(currentField, previousField));
1804
+ changes.push(...compareRelationConfigurations(currentField, previousField, collectionIdToName));
1698
1805
  }
1699
1806
  return changes;
1700
1807
  }
@@ -1705,7 +1812,7 @@ function compareIndexes(currentIndexes = [], previousIndexes = []) {
1705
1812
  const indexesToRemove = previousIndexes.filter((idx) => !currentSet.has(idx));
1706
1813
  return { indexesToAdd, indexesToRemove };
1707
1814
  }
1708
- function compareRules(currentRules, previousRules) {
1815
+ function compareRules(currentRules, previousRules, currentPermissions, previousPermissions) {
1709
1816
  const updates = [];
1710
1817
  const ruleTypes = [
1711
1818
  "listRule",
@@ -1716,8 +1823,8 @@ function compareRules(currentRules, previousRules) {
1716
1823
  "manageRule"
1717
1824
  ];
1718
1825
  for (const ruleType of ruleTypes) {
1719
- const currentValue = currentRules?.[ruleType] ?? null;
1720
- const previousValue = previousRules?.[ruleType] ?? null;
1826
+ const currentValue = currentRules?.[ruleType] ?? currentPermissions?.[ruleType] ?? null;
1827
+ const previousValue = previousRules?.[ruleType] ?? previousPermissions?.[ruleType] ?? null;
1721
1828
  if (currentValue !== previousValue) {
1722
1829
  updates.push({
1723
1830
  ruleType,
@@ -1744,7 +1851,7 @@ function comparePermissions(currentPermissions, previousPermissions) {
1744
1851
  }
1745
1852
  return changes;
1746
1853
  }
1747
- function compareCollectionFields(currentCollection, previousCollection, config) {
1854
+ function compareCollectionFields(currentCollection, previousCollection, config, collectionIdToName) {
1748
1855
  let fieldsToAdd = findNewFields(currentCollection.fields, previousCollection.fields);
1749
1856
  const fieldsToRemove = findRemovedFields(currentCollection.fields, previousCollection.fields);
1750
1857
  const fieldsToModify = [];
@@ -1754,7 +1861,7 @@ function compareCollectionFields(currentCollection, previousCollection, config)
1754
1861
  }
1755
1862
  const matchedFields = matchFieldsByName(currentCollection.fields, previousCollection.fields);
1756
1863
  for (const [currentField, previousField] of matchedFields) {
1757
- const changes = detectFieldChanges(currentField, previousField);
1864
+ const changes = detectFieldChanges(currentField, previousField, collectionIdToName);
1758
1865
  if (changes.length > 0) {
1759
1866
  fieldsToModify.push({
1760
1867
  fieldName: currentField.name,
@@ -1766,14 +1873,20 @@ function compareCollectionFields(currentCollection, previousCollection, config)
1766
1873
  }
1767
1874
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
1768
1875
  }
1769
- function buildCollectionModification(currentCollection, previousCollection, config) {
1876
+ function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
1770
1877
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
1771
1878
  currentCollection,
1772
1879
  previousCollection,
1773
- config
1880
+ config,
1881
+ collectionIdToName
1774
1882
  );
1775
1883
  const { indexesToAdd, indexesToRemove } = compareIndexes(currentCollection.indexes, previousCollection.indexes);
1776
- const rulesToUpdate = compareRules(currentCollection.rules, previousCollection.rules);
1884
+ const rulesToUpdate = compareRules(
1885
+ currentCollection.rules,
1886
+ previousCollection.rules,
1887
+ currentCollection.permissions,
1888
+ previousCollection.permissions
1889
+ );
1777
1890
  const permissionsToUpdate = comparePermissions(currentCollection.permissions, previousCollection.permissions);
1778
1891
  return {
1779
1892
  collection: currentCollection.name,
@@ -1790,6 +1903,14 @@ function hasChanges(modification) {
1790
1903
  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;
1791
1904
  }
1792
1905
  function aggregateChanges(currentSchema, previousSnapshot, config) {
1906
+ const collectionIdToName = /* @__PURE__ */ new Map();
1907
+ if (previousSnapshot) {
1908
+ for (const [name, collection] of previousSnapshot.collections) {
1909
+ if (collection.id) {
1910
+ collectionIdToName.set(collection.id, name);
1911
+ }
1912
+ }
1913
+ }
1793
1914
  const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
1794
1915
  const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
1795
1916
  const filteredCollectionsToCreate = collectionsToCreate.filter(
@@ -1798,16 +1919,28 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1798
1919
  const filteredCollectionsToDelete = collectionsToDelete.filter(
1799
1920
  (collection) => !isSystemCollection(collection.name, config)
1800
1921
  );
1922
+ const registry = new CollectionIdRegistry();
1923
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
1924
+ if (collection.id) {
1925
+ registry.register(collection.id);
1926
+ return collection;
1927
+ }
1928
+ const id = registry.generate(collection.name);
1929
+ return {
1930
+ ...collection,
1931
+ id
1932
+ };
1933
+ });
1801
1934
  const collectionsToModify = [];
1802
1935
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
1803
1936
  for (const [currentCollection, previousCollection] of matchedCollections) {
1804
- const modification = buildCollectionModification(currentCollection, previousCollection, config);
1937
+ const modification = buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName);
1805
1938
  if (hasChanges(modification)) {
1806
1939
  collectionsToModify.push(modification);
1807
1940
  }
1808
1941
  }
1809
1942
  return {
1810
- collectionsToCreate: filteredCollectionsToCreate,
1943
+ collectionsToCreate: collectionsWithIds,
1811
1944
  collectionsToDelete: filteredCollectionsToDelete,
1812
1945
  collectionsToModify
1813
1946
  };
@@ -1890,42 +2023,48 @@ function generateTimestamp(config) {
1890
2023
  }
1891
2024
  return Math.floor(Date.now() / 1e3).toString();
1892
2025
  }
1893
- function generateMigrationDescription(diff) {
1894
- const parts = [];
1895
- if (diff.collectionsToCreate.length > 0) {
1896
- if (diff.collectionsToCreate.length === 1) {
1897
- parts.push(`created_${diff.collectionsToCreate[0].name}`);
1898
- } else {
1899
- parts.push(`created_${diff.collectionsToCreate.length}_collections`);
1900
- }
1901
- }
1902
- if (diff.collectionsToDelete.length > 0) {
1903
- if (diff.collectionsToDelete.length === 1) {
1904
- parts.push(`deleted_${diff.collectionsToDelete[0].name}`);
1905
- } else {
1906
- parts.push(`deleted_${diff.collectionsToDelete.length}_collections`);
1907
- }
1908
- }
1909
- if (diff.collectionsToModify.length > 0) {
1910
- if (diff.collectionsToModify.length === 1) {
1911
- parts.push(`updated_${diff.collectionsToModify[0].collection}`);
1912
- } else {
1913
- parts.push(`updated_${diff.collectionsToModify.length}_collections`);
1914
- }
2026
+ function splitDiffByCollection(diff, baseTimestamp) {
2027
+ const operations = [];
2028
+ let currentTimestamp = parseInt(baseTimestamp, 10);
2029
+ for (const collection of diff.collectionsToCreate) {
2030
+ operations.push({
2031
+ type: "create",
2032
+ collection,
2033
+ timestamp: currentTimestamp.toString()
2034
+ });
2035
+ currentTimestamp += 1;
1915
2036
  }
1916
- if (parts.length === 0) {
1917
- return "no_changes";
2037
+ for (const modification of diff.collectionsToModify) {
2038
+ operations.push({
2039
+ type: "modify",
2040
+ collection: modification.collection,
2041
+ modifications: modification,
2042
+ timestamp: currentTimestamp.toString()
2043
+ });
2044
+ currentTimestamp += 1;
1918
2045
  }
1919
- let description = parts.join("_");
1920
- if (description.length > 80) {
1921
- description = description.substring(0, 77) + "...";
2046
+ for (const collection of diff.collectionsToDelete) {
2047
+ operations.push({
2048
+ type: "delete",
2049
+ collection: collection.name || collection,
2050
+ // Handle both object and string
2051
+ timestamp: currentTimestamp.toString()
2052
+ });
2053
+ currentTimestamp += 1;
1922
2054
  }
1923
- return description;
2055
+ return operations;
1924
2056
  }
1925
- function generateMigrationFilename(diff, config) {
1926
- const timestamp = generateTimestamp(config);
1927
- const description = generateMigrationDescription(diff);
1928
- return `${timestamp}_${description}.js`;
2057
+ function generateCollectionMigrationFilename(operation) {
2058
+ const timestamp = operation.timestamp;
2059
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
2060
+ let collectionName;
2061
+ if (typeof operation.collection === "string") {
2062
+ collectionName = operation.collection;
2063
+ } else {
2064
+ collectionName = operation.collection.name;
2065
+ }
2066
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
2067
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
1929
2068
  }
1930
2069
  function createMigrationFileStructure(upCode, downCode, config) {
1931
2070
  const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
@@ -1997,14 +2136,13 @@ function formatValue(value) {
1997
2136
  return "null";
1998
2137
  }
1999
2138
  if (typeof value === "string") {
2000
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
2139
+ return JSON.stringify(value);
2001
2140
  }
2002
2141
  if (typeof value === "number" || typeof value === "boolean") {
2003
2142
  return String(value);
2004
2143
  }
2005
2144
  if (Array.isArray(value)) {
2006
- const items = value.map((v) => formatValue(v)).join(", ");
2007
- return `[${items}]`;
2145
+ return JSON.stringify(value).replace(/","/g, '", "');
2008
2146
  }
2009
2147
  if (typeof value === "object") {
2010
2148
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -2012,7 +2150,7 @@ function formatValue(value) {
2012
2150
  }
2013
2151
  return String(value);
2014
2152
  }
2015
- function generateFieldDefinitionObject(field) {
2153
+ function generateFieldDefinitionObject(field, collectionIdMap) {
2016
2154
  const parts = [];
2017
2155
  parts.push(` name: "${field.name}"`);
2018
2156
  parts.push(` type: "${field.type}"`);
@@ -2020,34 +2158,47 @@ function generateFieldDefinitionObject(field) {
2020
2158
  if (field.unique !== void 0) {
2021
2159
  parts.push(` unique: ${field.unique}`);
2022
2160
  }
2161
+ if (field.type === "select") {
2162
+ const maxSelect = field.options?.maxSelect ?? 1;
2163
+ parts.push(` maxSelect: ${maxSelect}`);
2164
+ const values = field.options?.values ?? [];
2165
+ parts.push(` values: ${formatValue(values)}`);
2166
+ }
2023
2167
  if (field.options && Object.keys(field.options).length > 0) {
2024
2168
  for (const [key, value] of Object.entries(field.options)) {
2169
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2170
+ continue;
2171
+ }
2025
2172
  parts.push(` ${key}: ${formatValue(value)}`);
2026
2173
  }
2027
2174
  }
2028
2175
  if (field.relation) {
2029
2176
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2030
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2031
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2032
- if (field.relation.maxSelect !== void 0) {
2033
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2034
- }
2035
- if (field.relation.minSelect !== void 0) {
2036
- parts.push(` minSelect: ${field.relation.minSelect}`);
2037
- }
2038
- if (field.relation.cascadeDelete !== void 0) {
2039
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2177
+ let collectionIdValue;
2178
+ if (isUsersCollection) {
2179
+ collectionIdValue = '"_pb_users_auth_"';
2180
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2181
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2182
+ } else {
2183
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2040
2184
  }
2185
+ parts.push(` collectionId: ${collectionIdValue}`);
2186
+ const maxSelect = field.relation.maxSelect ?? 1;
2187
+ parts.push(` maxSelect: ${maxSelect}`);
2188
+ const minSelect = field.relation.minSelect ?? null;
2189
+ parts.push(` minSelect: ${minSelect}`);
2190
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2191
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2041
2192
  }
2042
2193
  return ` {
2043
2194
  ${parts.join(",\n")},
2044
2195
  }`;
2045
2196
  }
2046
- function generateFieldsArray(fields) {
2197
+ function generateFieldsArray(fields, collectionIdMap) {
2047
2198
  if (fields.length === 0) {
2048
2199
  return "[]";
2049
2200
  }
2050
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
2201
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
2051
2202
  return `[
2052
2203
  ${fieldObjects.join(",\n")},
2053
2204
  ]`;
@@ -2106,7 +2257,7 @@ function generateIndexesArray(indexes) {
2106
2257
  if (!indexes || indexes.length === 0) {
2107
2258
  return "[]";
2108
2259
  }
2109
- const indexStrings = indexes.map((idx) => `"${idx}"`);
2260
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
2110
2261
  return `[
2111
2262
  ${indexStrings.join(",\n ")},
2112
2263
  ]`;
@@ -2160,9 +2311,12 @@ function getSystemFields() {
2160
2311
  }
2161
2312
  ];
2162
2313
  }
2163
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2314
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
2164
2315
  const lines = [];
2165
2316
  lines.push(` const ${varName} = new Collection({`);
2317
+ if (collection.id) {
2318
+ lines.push(` id: ${formatValue(collection.id)},`);
2319
+ }
2166
2320
  lines.push(` name: "${collection.name}",`);
2167
2321
  lines.push(` type: "${collection.type}",`);
2168
2322
  const permissionsCode = generateCollectionPermissions(collection.permissions);
@@ -2174,7 +2328,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
2174
2328
  }
2175
2329
  const systemFields = getSystemFields();
2176
2330
  const allFields = [...systemFields, ...collection.fields];
2177
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
2331
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
2178
2332
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2179
2333
  lines.push(` });`);
2180
2334
  lines.push(``);
@@ -2196,42 +2350,59 @@ function getFieldConstructorName(fieldType) {
2196
2350
  };
2197
2351
  return constructorMap[fieldType] || "TextField";
2198
2352
  }
2199
- function generateFieldConstructorOptions(field) {
2353
+ function generateFieldConstructorOptions(field, collectionIdMap) {
2200
2354
  const parts = [];
2201
2355
  parts.push(` name: "${field.name}"`);
2202
2356
  parts.push(` required: ${field.required}`);
2203
2357
  if (field.unique !== void 0) {
2204
2358
  parts.push(` unique: ${field.unique}`);
2205
2359
  }
2360
+ if (field.type === "select") {
2361
+ const maxSelect = field.options?.maxSelect ?? 1;
2362
+ parts.push(` maxSelect: ${maxSelect}`);
2363
+ const values = field.options?.values ?? [];
2364
+ parts.push(` values: ${formatValue(values)}`);
2365
+ }
2206
2366
  if (field.options && Object.keys(field.options).length > 0) {
2207
2367
  for (const [key, value] of Object.entries(field.options)) {
2208
- parts.push(` ${key}: ${formatValue(value)}`);
2368
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2369
+ continue;
2370
+ }
2371
+ if (field.type === "number" && key === "noDecimal") {
2372
+ parts.push(` onlyInt: ${formatValue(value)}`);
2373
+ } else {
2374
+ parts.push(` ${key}: ${formatValue(value)}`);
2375
+ }
2209
2376
  }
2210
2377
  }
2211
2378
  if (field.relation && field.type === "relation") {
2212
2379
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2213
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2214
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2215
- if (field.relation.maxSelect !== void 0) {
2216
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2217
- }
2218
- if (field.relation.minSelect !== void 0) {
2219
- parts.push(` minSelect: ${field.relation.minSelect}`);
2220
- }
2221
- if (field.relation.cascadeDelete !== void 0) {
2222
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2380
+ let collectionIdValue;
2381
+ if (isUsersCollection) {
2382
+ collectionIdValue = '"_pb_users_auth_"';
2383
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2384
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2385
+ } else {
2386
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2223
2387
  }
2388
+ parts.push(` collectionId: ${collectionIdValue}`);
2389
+ const maxSelect = field.relation.maxSelect ?? 1;
2390
+ parts.push(` maxSelect: ${maxSelect}`);
2391
+ const minSelect = field.relation.minSelect ?? null;
2392
+ parts.push(` minSelect: ${minSelect}`);
2393
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2394
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2224
2395
  }
2225
2396
  return parts.join(",\n");
2226
2397
  }
2227
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
2398
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
2228
2399
  const lines = [];
2229
2400
  const constructorName = getFieldConstructorName(field.type);
2230
2401
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
2231
2402
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2232
2403
  lines.push(``);
2233
2404
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
2234
- lines.push(generateFieldConstructorOptions(field));
2405
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
2235
2406
  lines.push(` }));`);
2236
2407
  lines.push(``);
2237
2408
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -2281,7 +2452,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2281
2452
  const lines = [];
2282
2453
  const collectionVar = varName || `collection_${collectionName}_idx`;
2283
2454
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2284
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
2455
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
2285
2456
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2286
2457
  return lines.join("\n");
2287
2458
  }
@@ -2290,7 +2461,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2290
2461
  const collectionVar = varName || `collection_${collectionName}_idx`;
2291
2462
  const indexVar = `${collectionVar}_indexToRemove`;
2292
2463
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2293
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
2464
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
2294
2465
  lines.push(` if (${indexVar} !== -1) {`);
2295
2466
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2296
2467
  lines.push(` }`);
@@ -2319,94 +2490,75 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
2319
2490
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2320
2491
  return lines.join("\n");
2321
2492
  }
2322
- function generateUpMigration(diff) {
2493
+ function generateOperationUpMigration(operation, collectionIdMap) {
2323
2494
  const lines = [];
2324
- lines.push(` // UP MIGRATION`);
2325
- lines.push(``);
2326
- if (diff.collectionsToCreate.length > 0) {
2327
- lines.push(` // Create new collections`);
2328
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2329
- const collection = diff.collectionsToCreate[i];
2330
- const varName = `collection_${collection.name}_create`;
2331
- lines.push(generateCollectionCreation(collection, varName));
2332
- lines.push(``);
2495
+ if (operation.type === "create") {
2496
+ const collection = operation.collection;
2497
+ const varName = `collection_${collection.name}`;
2498
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2499
+ } else if (operation.type === "modify") {
2500
+ const modification = operation.modifications;
2501
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2502
+ let operationCount = 0;
2503
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2504
+ for (const field of modification.fieldsToAdd) {
2505
+ operationCount++;
2506
+ const varName = `collection_${collectionName}_add_${field.name}`;
2507
+ const isLast = operationCount === totalOperations;
2508
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2509
+ if (!isLast) lines.push("");
2333
2510
  }
2334
- }
2335
- if (diff.collectionsToModify.length > 0) {
2336
- lines.push(` // Modify existing collections`);
2337
- for (const modification of diff.collectionsToModify) {
2338
- const collectionName = modification.collection;
2339
- if (modification.fieldsToAdd.length > 0) {
2340
- lines.push(` // Add fields to ${collectionName}`);
2341
- for (const field of modification.fieldsToAdd) {
2342
- const varName = `collection_${collectionName}_add_${field.name}`;
2343
- lines.push(generateFieldAddition(collectionName, field, varName));
2344
- lines.push(``);
2345
- }
2346
- }
2347
- if (modification.fieldsToModify.length > 0) {
2348
- lines.push(` // Modify fields in ${collectionName}`);
2349
- for (const fieldMod of modification.fieldsToModify) {
2350
- const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2351
- lines.push(generateFieldModification(collectionName, fieldMod, varName));
2352
- lines.push(``);
2353
- }
2354
- }
2355
- if (modification.fieldsToRemove.length > 0) {
2356
- lines.push(` // Remove fields from ${collectionName}`);
2357
- for (const field of modification.fieldsToRemove) {
2358
- const varName = `collection_${collectionName}_remove_${field.name}`;
2359
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2360
- lines.push(``);
2361
- }
2362
- }
2363
- if (modification.indexesToAdd.length > 0) {
2364
- lines.push(` // Add indexes to ${collectionName}`);
2365
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2366
- const index = modification.indexesToAdd[i];
2367
- const varName = `collection_${collectionName}_addidx_${i}`;
2368
- lines.push(generateIndexAddition(collectionName, index, varName));
2369
- lines.push(``);
2370
- }
2371
- }
2372
- if (modification.indexesToRemove.length > 0) {
2373
- lines.push(` // Remove indexes from ${collectionName}`);
2374
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2375
- const index = modification.indexesToRemove[i];
2376
- const varName = `collection_${collectionName}_rmidx_${i}`;
2377
- lines.push(generateIndexRemoval(collectionName, index, varName));
2378
- lines.push(``);
2379
- }
2511
+ for (const fieldMod of modification.fieldsToModify) {
2512
+ operationCount++;
2513
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2514
+ const isLast = operationCount === totalOperations;
2515
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
2516
+ if (!isLast) lines.push("");
2517
+ }
2518
+ for (const field of modification.fieldsToRemove) {
2519
+ operationCount++;
2520
+ const varName = `collection_${collectionName}_remove_${field.name}`;
2521
+ const isLast = operationCount === totalOperations;
2522
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2523
+ if (!isLast) lines.push("");
2524
+ }
2525
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2526
+ operationCount++;
2527
+ const index = modification.indexesToAdd[i];
2528
+ const varName = `collection_${collectionName}_addidx_${i}`;
2529
+ const isLast = operationCount === totalOperations;
2530
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2531
+ if (!isLast) lines.push("");
2532
+ }
2533
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2534
+ operationCount++;
2535
+ const index = modification.indexesToRemove[i];
2536
+ const varName = `collection_${collectionName}_rmidx_${i}`;
2537
+ const isLast = operationCount === totalOperations;
2538
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2539
+ if (!isLast) lines.push("");
2540
+ }
2541
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2542
+ for (const permission of modification.permissionsToUpdate) {
2543
+ operationCount++;
2544
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2545
+ const isLast = operationCount === totalOperations;
2546
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
2547
+ if (!isLast) lines.push("");
2380
2548
  }
2381
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2382
- lines.push(` // Update permissions for ${collectionName}`);
2383
- for (const permission of modification.permissionsToUpdate) {
2384
- const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2385
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName));
2386
- lines.push(``);
2387
- }
2388
- } else if (modification.rulesToUpdate.length > 0) {
2389
- lines.push(` // Update rules for ${collectionName}`);
2390
- for (const rule of modification.rulesToUpdate) {
2391
- const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2392
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName));
2393
- lines.push(``);
2394
- }
2549
+ } else if (modification.rulesToUpdate.length > 0) {
2550
+ for (const rule of modification.rulesToUpdate) {
2551
+ operationCount++;
2552
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2553
+ const isLast = operationCount === totalOperations;
2554
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
2555
+ if (!isLast) lines.push("");
2395
2556
  }
2396
2557
  }
2397
- }
2398
- if (diff.collectionsToDelete.length > 0) {
2399
- lines.push(` // Delete collections`);
2400
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2401
- const collection = diff.collectionsToDelete[i];
2402
- const varName = `collection_${collection.name}_delete`;
2403
- lines.push(generateCollectionDeletion(collection.name, varName));
2404
- lines.push(``);
2405
- }
2406
- }
2407
- if (lines.length === 2) {
2408
- lines.push(` // No changes detected`);
2409
- lines.push(``);
2558
+ } else if (operation.type === "delete") {
2559
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
2560
+ const varName = `collection_${collectionName}`;
2561
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
2410
2562
  }
2411
2563
  let code = lines.join("\n");
2412
2564
  const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
@@ -2427,105 +2579,88 @@ function generateUpMigration(diff) {
2427
2579
  }
2428
2580
  return code;
2429
2581
  }
2430
- function generateDownMigration(diff) {
2582
+ function generateOperationDownMigration(operation, collectionIdMap) {
2431
2583
  const lines = [];
2432
- lines.push(` // DOWN MIGRATION (ROLLBACK)`);
2433
- lines.push(``);
2434
- if (diff.collectionsToDelete.length > 0) {
2435
- lines.push(` // Recreate deleted collections`);
2436
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2437
- const collection = diff.collectionsToDelete[i];
2438
- const varName = `collection_${collection.name}_recreate`;
2439
- lines.push(generateCollectionCreation(collection, varName));
2440
- lines.push(``);
2441
- }
2442
- }
2443
- if (diff.collectionsToModify.length > 0) {
2444
- lines.push(` // Revert modifications`);
2445
- for (const modification of diff.collectionsToModify) {
2446
- const collectionName = modification.collection;
2447
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2448
- lines.push(` // Revert permissions for ${collectionName}`);
2449
- for (const permission of modification.permissionsToUpdate) {
2450
- const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2451
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName));
2452
- lines.push(``);
2453
- }
2454
- } else if (modification.rulesToUpdate.length > 0) {
2455
- lines.push(` // Revert rules for ${collectionName}`);
2456
- for (const rule of modification.rulesToUpdate) {
2457
- const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2458
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName));
2459
- lines.push(``);
2460
- }
2461
- }
2462
- if (modification.indexesToRemove.length > 0) {
2463
- lines.push(` // Restore indexes to ${collectionName}`);
2464
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2465
- const index = modification.indexesToRemove[i];
2466
- const varName = `collection_${collectionName}_restore_idx_${i}`;
2467
- lines.push(generateIndexAddition(collectionName, index, varName));
2468
- lines.push(``);
2469
- }
2470
- }
2471
- if (modification.indexesToAdd.length > 0) {
2472
- lines.push(` // Remove indexes from ${collectionName}`);
2473
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2474
- const index = modification.indexesToAdd[i];
2475
- const varName = `collection_${collectionName}_revert_idx_${i}`;
2476
- lines.push(generateIndexRemoval(collectionName, index, varName));
2477
- lines.push(``);
2478
- }
2479
- }
2480
- if (modification.fieldsToRemove.length > 0) {
2481
- lines.push(` // Restore fields to ${collectionName}`);
2482
- for (const field of modification.fieldsToRemove) {
2483
- const varName = `collection_${collectionName}_restore_${field.name}`;
2484
- lines.push(generateFieldAddition(collectionName, field, varName));
2485
- lines.push(``);
2486
- }
2584
+ if (operation.type === "create") {
2585
+ const collection = operation.collection;
2586
+ const varName = `collection_${collection.name}`;
2587
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
2588
+ } else if (operation.type === "modify") {
2589
+ const modification = operation.modifications;
2590
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2591
+ let operationCount = 0;
2592
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2593
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2594
+ for (const permission of modification.permissionsToUpdate) {
2595
+ operationCount++;
2596
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2597
+ const isLast = operationCount === totalOperations;
2598
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
2599
+ if (!isLast) lines.push("");
2487
2600
  }
2488
- if (modification.fieldsToModify.length > 0) {
2489
- lines.push(` // Revert field modifications in ${collectionName}`);
2490
- for (const fieldMod of modification.fieldsToModify) {
2491
- const reverseChanges = fieldMod.changes.map((change) => ({
2492
- property: change.property,
2493
- oldValue: change.newValue,
2494
- newValue: change.oldValue
2495
- }));
2496
- const reverseMod = {
2497
- fieldName: fieldMod.fieldName,
2498
- currentDefinition: fieldMod.newDefinition,
2499
- newDefinition: fieldMod.currentDefinition,
2500
- changes: reverseChanges
2501
- };
2502
- const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2503
- lines.push(generateFieldModification(collectionName, reverseMod, varName));
2504
- lines.push(``);
2505
- }
2506
- }
2507
- if (modification.fieldsToAdd.length > 0) {
2508
- lines.push(` // Remove added fields from ${collectionName}`);
2509
- for (const field of modification.fieldsToAdd) {
2510
- const varName = `collection_${collectionName}_revert_add_${field.name}`;
2511
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2512
- lines.push(``);
2513
- }
2601
+ } else if (modification.rulesToUpdate.length > 0) {
2602
+ for (const rule of modification.rulesToUpdate) {
2603
+ operationCount++;
2604
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2605
+ const isLast = operationCount === totalOperations;
2606
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
2607
+ if (!isLast) lines.push("");
2514
2608
  }
2515
2609
  }
2516
- }
2517
- if (diff.collectionsToCreate.length > 0) {
2518
- lines.push(` // Delete created collections`);
2519
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2520
- const collection = diff.collectionsToCreate[i];
2521
- const varName = `collection_${collection.name}_rollback`;
2522
- lines.push(generateCollectionDeletion(collection.name, varName));
2523
- lines.push(``);
2610
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2611
+ operationCount++;
2612
+ const index = modification.indexesToRemove[i];
2613
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
2614
+ const isLast = operationCount === totalOperations;
2615
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2616
+ if (!isLast) lines.push("");
2617
+ }
2618
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2619
+ operationCount++;
2620
+ const index = modification.indexesToAdd[i];
2621
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
2622
+ const isLast = operationCount === totalOperations;
2623
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2624
+ if (!isLast) lines.push("");
2625
+ }
2626
+ for (const field of modification.fieldsToRemove) {
2627
+ operationCount++;
2628
+ const varName = `collection_${collectionName}_restore_${field.name}`;
2629
+ const isLast = operationCount === totalOperations;
2630
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2631
+ if (!isLast) lines.push("");
2632
+ }
2633
+ for (const fieldMod of modification.fieldsToModify) {
2634
+ operationCount++;
2635
+ const reverseChanges = fieldMod.changes.map((change) => ({
2636
+ property: change.property,
2637
+ oldValue: change.newValue,
2638
+ newValue: change.oldValue
2639
+ }));
2640
+ const reverseMod = {
2641
+ fieldName: fieldMod.fieldName,
2642
+ currentDefinition: fieldMod.newDefinition,
2643
+ newDefinition: fieldMod.currentDefinition,
2644
+ changes: reverseChanges
2645
+ };
2646
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2647
+ const isLast = operationCount === totalOperations;
2648
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
2649
+ if (!isLast) lines.push("");
2650
+ }
2651
+ for (const field of modification.fieldsToAdd) {
2652
+ operationCount++;
2653
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
2654
+ const isLast = operationCount === totalOperations;
2655
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2656
+ if (!isLast) lines.push("");
2657
+ }
2658
+ } else if (operation.type === "delete") {
2659
+ const collection = operation.collection;
2660
+ if (typeof collection !== "string") {
2661
+ const varName = `collection_${collection.name}`;
2662
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2524
2663
  }
2525
- }
2526
- if (lines.length === 2) {
2527
- lines.push(` // No changes to revert`);
2528
- lines.push(``);
2529
2664
  }
2530
2665
  let code = lines.join("\n");
2531
2666
  const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
@@ -2550,12 +2685,33 @@ function generate(diff, config) {
2550
2685
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
2551
2686
  try {
2552
2687
  const migrationDir = resolveMigrationDir(normalizedConfig);
2553
- const upCode = generateUpMigration(diff);
2554
- const downCode = generateDownMigration(diff);
2555
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2556
- const filename = generateMigrationFilename(diff, normalizedConfig);
2557
- const filePath = writeMigrationFile(migrationDir, filename, content);
2558
- return filePath;
2688
+ const hasChanges4 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
2689
+ if (!hasChanges4) {
2690
+ return [];
2691
+ }
2692
+ const collectionIdMap = /* @__PURE__ */ new Map();
2693
+ for (const collection of diff.collectionsToCreate) {
2694
+ if (collection.id) {
2695
+ collectionIdMap.set(collection.name, collection.id);
2696
+ }
2697
+ }
2698
+ for (const collection of diff.collectionsToDelete) {
2699
+ if (collection.id) {
2700
+ collectionIdMap.set(collection.name, collection.id);
2701
+ }
2702
+ }
2703
+ const baseTimestamp = generateTimestamp(normalizedConfig);
2704
+ const operations = splitDiffByCollection(diff, baseTimestamp);
2705
+ const filePaths = [];
2706
+ for (const operation of operations) {
2707
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
2708
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
2709
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2710
+ const filename = generateCollectionMigrationFilename(operation);
2711
+ const filePath = writeMigrationFile(migrationDir, filename, content);
2712
+ filePaths.push(filePath);
2713
+ }
2714
+ return filePaths;
2559
2715
  } catch (error) {
2560
2716
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
2561
2717
  throw error;
@@ -2572,7 +2728,7 @@ function generate(diff, config) {
2572
2728
  var SNAPSHOT_VERSION = "1.0.0";
2573
2729
  function resolveCollectionIdToName(collectionId) {
2574
2730
  if (collectionId === "_pb_users_auth_") {
2575
- return "Users";
2731
+ return "users";
2576
2732
  }
2577
2733
  const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2578
2734
  if (nameMatch) {
@@ -2580,6 +2736,39 @@ function resolveCollectionIdToName(collectionId) {
2580
2736
  }
2581
2737
  return collectionId;
2582
2738
  }
2739
+ function extractFieldOptions2(pbField) {
2740
+ const options = {};
2741
+ if (pbField.options && typeof pbField.options === "object") {
2742
+ Object.assign(options, pbField.options);
2743
+ }
2744
+ const directOptionKeys = [
2745
+ "min",
2746
+ "max",
2747
+ "pattern",
2748
+ "noDecimal",
2749
+ // text/number fields
2750
+ "values",
2751
+ "maxSelect",
2752
+ // select fields
2753
+ "mimeTypes",
2754
+ "maxSize",
2755
+ "thumbs",
2756
+ "protected",
2757
+ // file fields
2758
+ "onCreate",
2759
+ "onUpdate",
2760
+ // autodate fields
2761
+ "exceptDomains",
2762
+ "onlyDomains"
2763
+ // email/url fields
2764
+ ];
2765
+ for (const key of directOptionKeys) {
2766
+ if (pbField[key] !== void 0) {
2767
+ options[key] = pbField[key];
2768
+ }
2769
+ }
2770
+ return options;
2771
+ }
2583
2772
  function convertPocketBaseCollection(pbCollection) {
2584
2773
  const fields = [];
2585
2774
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
@@ -2597,23 +2786,19 @@ function convertPocketBaseCollection(pbCollection) {
2597
2786
  type: pbField.type,
2598
2787
  required: pbField.required || false
2599
2788
  };
2600
- field.options = pbField.options ? { ...pbField.options } : {};
2601
- if (pbField.type === "select") {
2602
- if (pbField.values && Array.isArray(pbField.values)) {
2603
- field.options.values = pbField.values;
2604
- } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2605
- field.options.values = pbField.options.values;
2606
- }
2607
- }
2789
+ field.options = extractFieldOptions2(pbField);
2608
2790
  if (pbField.type === "relation") {
2609
2791
  const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2610
- const collectionName = resolveCollectionIdToName(collectionId);
2792
+ const collectionName = resolveCollectionIdToName(collectionId || "");
2611
2793
  field.relation = {
2612
2794
  collection: collectionName,
2613
2795
  cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2614
2796
  maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2615
2797
  minSelect: pbField.minSelect ?? pbField.options?.minSelect
2616
2798
  };
2799
+ delete field.options.maxSelect;
2800
+ delete field.options.minSelect;
2801
+ delete field.options.cascadeDelete;
2617
2802
  }
2618
2803
  const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2619
2804
  if (Object.keys(field.options).length === 0) {
@@ -2627,17 +2812,21 @@ function convertPocketBaseCollection(pbCollection) {
2627
2812
  type: pbCollection.type || "base",
2628
2813
  fields
2629
2814
  };
2815
+ if (pbCollection.id) {
2816
+ schema.id = pbCollection.id;
2817
+ }
2630
2818
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2631
2819
  schema.indexes = pbCollection.indexes;
2632
2820
  }
2633
- const rules = {};
2634
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2635
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2636
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2637
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2638
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2639
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2640
- if (Object.keys(rules).length > 0) {
2821
+ 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;
2822
+ if (hasAnyRule) {
2823
+ const rules = {};
2824
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2825
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2826
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2827
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2828
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2829
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2641
2830
  schema.rules = rules;
2642
2831
  schema.permissions = { ...rules };
2643
2832
  }
@@ -3576,15 +3765,25 @@ async function executeGenerate(options) {
3576
3765
  process.exit(1);
3577
3766
  }
3578
3767
  logSection("\u{1F4DD} Generating Migration");
3579
- const migrationPath = await withProgress(
3768
+ const migrationPaths = await withProgress(
3580
3769
  "Creating migration file...",
3581
3770
  () => Promise.resolve(generate(diff, migrationsDir))
3582
3771
  );
3583
- logSuccess(`Migration file created: ${path5.basename(migrationPath)}`);
3772
+ if (migrationPaths.length === 0) {
3773
+ logWarning("No migration files were generated (no changes detected).");
3774
+ return;
3775
+ }
3776
+ if (migrationPaths.length === 1) {
3777
+ logSuccess(`Migration file created: ${path5.basename(migrationPaths[0])}`);
3778
+ } else {
3779
+ logSuccess(`Created ${migrationPaths.length} migration files`);
3780
+ }
3584
3781
  logSection("\u2705 Next Steps");
3585
3782
  console.log();
3586
- console.log(" 1. Review the generated migration file:");
3587
- console.log(` ${migrationPath}`);
3783
+ console.log(" 1. Review the generated migration file(s):");
3784
+ migrationPaths.forEach((migrationPath) => {
3785
+ console.log(` ${migrationPath}`);
3786
+ });
3588
3787
  console.log();
3589
3788
  console.log(" 2. Apply the migration by running PocketBase:");
3590
3789
  console.log(" yarn pb");