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