pocketbase-zod-schema 0.1.2 → 0.1.4

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 (69) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +329 -99
  3. package/dist/cli/index.cjs +577 -152
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.js +575 -150
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/migrate.cjs +595 -153
  8. package/dist/cli/migrate.cjs.map +1 -1
  9. package/dist/cli/migrate.js +592 -151
  10. package/dist/cli/migrate.js.map +1 -1
  11. package/dist/cli/utils/index.cjs +1 -1
  12. package/dist/cli/utils/index.cjs.map +1 -1
  13. package/dist/cli/utils/index.js +1 -1
  14. package/dist/cli/utils/index.js.map +1 -1
  15. package/dist/index.cjs +688 -231
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +3 -3
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +685 -230
  20. package/dist/index.js.map +1 -1
  21. package/dist/migration/analyzer.cjs +101 -28
  22. package/dist/migration/analyzer.cjs.map +1 -1
  23. package/dist/migration/analyzer.js +101 -28
  24. package/dist/migration/analyzer.js.map +1 -1
  25. package/dist/migration/diff.cjs +21 -3
  26. package/dist/migration/diff.cjs.map +1 -1
  27. package/dist/migration/diff.js +21 -3
  28. package/dist/migration/diff.js.map +1 -1
  29. package/dist/migration/generator.cjs +60 -25
  30. package/dist/migration/generator.cjs.map +1 -1
  31. package/dist/migration/generator.d.cts +9 -5
  32. package/dist/migration/generator.d.ts +9 -5
  33. package/dist/migration/generator.js +60 -25
  34. package/dist/migration/generator.js.map +1 -1
  35. package/dist/migration/index.cjs +614 -171
  36. package/dist/migration/index.cjs.map +1 -1
  37. package/dist/migration/index.d.cts +1 -1
  38. package/dist/migration/index.d.ts +1 -1
  39. package/dist/migration/index.js +613 -171
  40. package/dist/migration/index.js.map +1 -1
  41. package/dist/migration/snapshot.cjs +432 -117
  42. package/dist/migration/snapshot.cjs.map +1 -1
  43. package/dist/migration/snapshot.d.cts +34 -12
  44. package/dist/migration/snapshot.d.ts +34 -12
  45. package/dist/migration/snapshot.js +430 -116
  46. package/dist/migration/snapshot.js.map +1 -1
  47. package/dist/migration/utils/index.cjs +19 -17
  48. package/dist/migration/utils/index.cjs.map +1 -1
  49. package/dist/migration/utils/index.d.cts +3 -1
  50. package/dist/migration/utils/index.d.ts +3 -1
  51. package/dist/migration/utils/index.js +19 -17
  52. package/dist/migration/utils/index.js.map +1 -1
  53. package/dist/mutator.cjs +9 -11
  54. package/dist/mutator.cjs.map +1 -1
  55. package/dist/mutator.d.cts +5 -9
  56. package/dist/mutator.d.ts +5 -9
  57. package/dist/mutator.js +9 -11
  58. package/dist/mutator.js.map +1 -1
  59. package/dist/schema.cjs +54 -23
  60. package/dist/schema.cjs.map +1 -1
  61. package/dist/schema.d.cts +94 -12
  62. package/dist/schema.d.ts +94 -12
  63. package/dist/schema.js +54 -24
  64. package/dist/schema.js.map +1 -1
  65. package/dist/types.d.cts +1 -1
  66. package/dist/types.d.ts +1 -1
  67. package/dist/{user-jS1aYoeD.d.cts → user-_AM523hb.d.cts} +6 -6
  68. package/dist/{user-jS1aYoeD.d.ts → user-_AM523hb.d.ts} +6 -6
  69. package/package.json +2 -4
@@ -1,8 +1,37 @@
1
- import * as fs2 from 'fs';
1
+ import * as fs3 from 'fs';
2
2
  import * as path from 'path';
3
3
  import { z } from 'zod';
4
4
 
5
5
  // src/migration/analyzer.ts
6
+ ({
7
+ id: z.string().describe("unique id"),
8
+ collectionId: z.string().describe("collection id"),
9
+ collectionName: z.string().describe("collection name"),
10
+ expand: z.record(z.any()).describe("expandable fields")
11
+ });
12
+ ({
13
+ created: z.string().describe("creation timestamp"),
14
+ updated: z.string().describe("last update timestamp")
15
+ });
16
+ ({
17
+ thumbnailURL: z.string().optional(),
18
+ imageFiles: z.array(z.string())
19
+ });
20
+ ({
21
+ imageFiles: z.array(z.instanceof(File))
22
+ });
23
+ var RELATION_METADATA_KEY = "__pocketbase_relation__";
24
+ function extractRelationMetadata(description) {
25
+ if (!description) return null;
26
+ try {
27
+ const parsed = JSON.parse(description);
28
+ if (parsed[RELATION_METADATA_KEY]) {
29
+ return parsed[RELATION_METADATA_KEY];
30
+ }
31
+ } catch {
32
+ }
33
+ return null;
34
+ }
6
35
 
7
36
  // src/migration/errors.ts
8
37
  var MigrationError = class _MigrationError extends Error {
@@ -101,10 +130,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
101
130
  operation;
102
131
  code;
103
132
  originalError;
104
- constructor(message, path4, operation, code, originalError) {
133
+ constructor(message, path5, operation, code, originalError) {
105
134
  super(message);
106
135
  this.name = "FileSystemError";
107
- this.path = path4;
136
+ this.path = path5;
108
137
  this.operation = operation;
109
138
  this.code = code;
110
139
  this.originalError = originalError;
@@ -193,7 +222,7 @@ Suggestion: ${this.suggestion}`);
193
222
  }
194
223
  };
195
224
 
196
- // src/schema/permission-templates.ts
225
+ // src/utils/permission-templates.ts
197
226
  var PermissionTemplates = {
198
227
  /**
199
228
  * Public access - anyone can perform all operations
@@ -873,26 +902,28 @@ function getMaxSelect(fieldName, zodType) {
873
902
  return 1;
874
903
  }
875
904
  function getMinSelect(fieldName, zodType) {
876
- if (!isMultipleRelationField(fieldName, zodType)) {
877
- return void 0;
878
- }
879
- let unwrappedType = zodType;
880
- if (zodType instanceof z.ZodOptional) {
881
- unwrappedType = zodType._def.innerType;
882
- }
883
- if (unwrappedType instanceof z.ZodNullable) {
884
- unwrappedType = unwrappedType._def.innerType;
885
- }
886
- if (unwrappedType instanceof z.ZodDefault) {
887
- unwrappedType = unwrappedType._def.innerType;
905
+ if (isSingleRelationField(fieldName, zodType)) {
906
+ return 0;
888
907
  }
889
- if (unwrappedType instanceof z.ZodArray) {
890
- const arrayDef = unwrappedType._def;
891
- if (arrayDef.minLength) {
892
- return arrayDef.minLength.value;
908
+ if (isMultipleRelationField(fieldName, zodType)) {
909
+ let unwrappedType = zodType;
910
+ if (zodType instanceof z.ZodOptional) {
911
+ unwrappedType = zodType._def.innerType;
912
+ }
913
+ if (unwrappedType instanceof z.ZodNullable) {
914
+ unwrappedType = unwrappedType._def.innerType;
915
+ }
916
+ if (unwrappedType instanceof z.ZodDefault) {
917
+ unwrappedType = unwrappedType._def.innerType;
918
+ }
919
+ if (unwrappedType instanceof z.ZodArray) {
920
+ const arrayDef = unwrappedType._def;
921
+ if (arrayDef.minLength) {
922
+ return arrayDef.minLength.value;
923
+ }
893
924
  }
894
925
  }
895
- return void 0;
926
+ return 0;
896
927
  }
897
928
  var POCKETBASE_FIELD_TYPES = [
898
929
  "text",
@@ -1325,10 +1356,10 @@ function discoverSchemaFiles(config) {
1325
1356
  const mergedConfig = mergeConfig(normalizedConfig);
1326
1357
  const schemaDir = resolveSchemaDir(normalizedConfig);
1327
1358
  try {
1328
- if (!fs2.existsSync(schemaDir)) {
1359
+ if (!fs3.existsSync(schemaDir)) {
1329
1360
  throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
1330
1361
  }
1331
- const files = fs2.readdirSync(schemaDir);
1362
+ const files = fs3.readdirSync(schemaDir);
1332
1363
  const schemaFiles = files.filter((file) => {
1333
1364
  const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
1334
1365
  if (!hasValidExtension) return false;
@@ -1375,13 +1406,32 @@ async function importSchemaModule(filePath, config) {
1375
1406
  if (config?.pathTransformer) {
1376
1407
  importPath = config.pathTransformer(filePath);
1377
1408
  }
1378
- if (!importPath.endsWith(".js")) {
1379
- importPath = `${importPath}.js`;
1409
+ let resolvedPath = null;
1410
+ const jsPath = `${importPath}.js`;
1411
+ const tsPath = `${importPath}.ts`;
1412
+ if (fs3.existsSync(jsPath)) {
1413
+ resolvedPath = jsPath;
1414
+ } else if (fs3.existsSync(tsPath)) {
1415
+ resolvedPath = tsPath;
1416
+ } else {
1417
+ resolvedPath = jsPath;
1380
1418
  }
1381
- const fileUrl = new URL(`file://${path.resolve(importPath)}`);
1419
+ const fileUrl = new URL(`file://${path.resolve(resolvedPath)}`);
1382
1420
  const module = await import(fileUrl.href);
1383
1421
  return module;
1384
1422
  } catch (error) {
1423
+ const tsPath = `${filePath}.ts`;
1424
+ const isTypeScriptFile = fs3.existsSync(tsPath);
1425
+ if (isTypeScriptFile) {
1426
+ throw new SchemaParsingError(
1427
+ `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
1428
+ Please either:
1429
+ 1. Compile your schema files to JavaScript first, or
1430
+ 2. Use tsx to run the migration tool (e.g., "npx tsx package/dist/cli/migrate.js status" or "tsx package/dist/cli/migrate.js status")`,
1431
+ filePath,
1432
+ error
1433
+ );
1434
+ }
1385
1435
  throw new SchemaParsingError(
1386
1436
  `Failed to import schema module. Make sure the schema files are compiled to JavaScript.`,
1387
1437
  filePath,
@@ -1444,7 +1494,17 @@ function buildFieldDefinition(fieldName, zodType) {
1444
1494
  required,
1445
1495
  options
1446
1496
  };
1447
- if (isRelationField(fieldName, zodType)) {
1497
+ const relationMetadata = extractRelationMetadata(zodType.description);
1498
+ if (relationMetadata) {
1499
+ fieldDef.type = "relation";
1500
+ fieldDef.relation = {
1501
+ collection: relationMetadata.collection,
1502
+ maxSelect: relationMetadata.maxSelect,
1503
+ minSelect: relationMetadata.minSelect,
1504
+ cascadeDelete: relationMetadata.cascadeDelete
1505
+ };
1506
+ fieldDef.options = void 0;
1507
+ } else if (isRelationField(fieldName, zodType)) {
1448
1508
  fieldDef.type = "relation";
1449
1509
  const targetCollection = resolveTargetCollection(fieldName);
1450
1510
  const maxSelect = getMaxSelect(fieldName, zodType);
@@ -1456,6 +1516,13 @@ function buildFieldDefinition(fieldName, zodType) {
1456
1516
  cascadeDelete: false
1457
1517
  // Default to false, can be configured later
1458
1518
  };
1519
+ if (fieldDef.options) {
1520
+ const { min, max, pattern, ...relationSafeOptions } = fieldDef.options;
1521
+ console.log("min", min);
1522
+ console.log("max", max);
1523
+ console.log("pattern", pattern);
1524
+ fieldDef.options = Object.keys(relationSafeOptions).length > 0 ? relationSafeOptions : void 0;
1525
+ }
1459
1526
  }
1460
1527
  return fieldDef;
1461
1528
  }
@@ -1508,11 +1575,12 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
1508
1575
  fields,
1509
1576
  indexes,
1510
1577
  rules: {
1511
- listRule: null,
1512
- viewRule: null,
1513
- createRule: null,
1514
- updateRule: null,
1515
- deleteRule: null
1578
+ listRule: permissions?.listRule ?? null,
1579
+ viewRule: permissions?.viewRule ?? null,
1580
+ createRule: permissions?.createRule ?? null,
1581
+ updateRule: permissions?.updateRule ?? null,
1582
+ deleteRule: permissions?.deleteRule ?? null,
1583
+ manageRule: permissions?.manageRule ?? null
1516
1584
  },
1517
1585
  permissions
1518
1586
  };
@@ -1536,7 +1604,12 @@ async function buildSchemaDefinition(config) {
1536
1604
  if (normalizedConfig.pathTransformer) {
1537
1605
  importPath = normalizedConfig.pathTransformer(filePath);
1538
1606
  } else if (mergedConfig.useCompiledFiles) {
1539
- importPath = filePath.replace(/\/src\//, "/dist/");
1607
+ const distPath = filePath.replace(/\/src\//, "/dist/");
1608
+ if (fs3.existsSync(`${distPath}.js`) || fs3.existsSync(`${distPath}.mjs`)) {
1609
+ importPath = distPath;
1610
+ } else {
1611
+ importPath = filePath;
1612
+ }
1540
1613
  }
1541
1614
  const module = await importSchemaModule(importPath, normalizedConfig);
1542
1615
  const schemas = extractSchemaDefinitions(module, mergedConfig.schemaPatterns);
@@ -1588,7 +1661,359 @@ var SchemaAnalyzer = class {
1588
1661
  return convertZodSchemaToCollectionSchema(name, schema);
1589
1662
  }
1590
1663
  };
1664
+
1665
+ // src/migration/pocketbase-converter.ts
1591
1666
  var SNAPSHOT_VERSION = "1.0.0";
1667
+ function resolveCollectionIdToName(collectionId) {
1668
+ if (collectionId === "_pb_users_auth_") {
1669
+ return "Users";
1670
+ }
1671
+ const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1672
+ if (nameMatch) {
1673
+ return nameMatch[1];
1674
+ }
1675
+ return collectionId;
1676
+ }
1677
+ function convertPocketBaseCollection(pbCollection) {
1678
+ const fields = [];
1679
+ const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
1680
+ const authSystemFieldNames = ["email", "emailVisibility", "verified", "password", "tokenKey"];
1681
+ if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
1682
+ for (const pbField of pbCollection.fields) {
1683
+ if (pbField.system || systemFieldNames.includes(pbField.name)) {
1684
+ continue;
1685
+ }
1686
+ if (pbCollection.type === "auth" && authSystemFieldNames.includes(pbField.name)) {
1687
+ continue;
1688
+ }
1689
+ const field = {
1690
+ name: pbField.name,
1691
+ type: pbField.type,
1692
+ required: pbField.required || false
1693
+ };
1694
+ field.options = pbField.options ? { ...pbField.options } : {};
1695
+ if (pbField.type === "select") {
1696
+ if (pbField.values && Array.isArray(pbField.values)) {
1697
+ field.options.values = pbField.values;
1698
+ } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
1699
+ field.options.values = pbField.options.values;
1700
+ }
1701
+ }
1702
+ if (pbField.type === "relation") {
1703
+ const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
1704
+ const collectionName = resolveCollectionIdToName(collectionId);
1705
+ field.relation = {
1706
+ collection: collectionName,
1707
+ cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
1708
+ maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
1709
+ minSelect: pbField.minSelect ?? pbField.options?.minSelect
1710
+ };
1711
+ }
1712
+ const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
1713
+ if (Object.keys(field.options).length === 0) {
1714
+ delete field.options;
1715
+ } else if (pbField.type === "select" && hasOnlyValues) ;
1716
+ fields.push(field);
1717
+ }
1718
+ }
1719
+ const schema = {
1720
+ name: pbCollection.name,
1721
+ type: pbCollection.type || "base",
1722
+ fields
1723
+ };
1724
+ if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
1725
+ schema.indexes = pbCollection.indexes;
1726
+ }
1727
+ const rules = {};
1728
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1729
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1730
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1731
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1732
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1733
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1734
+ if (Object.keys(rules).length > 0) {
1735
+ schema.rules = rules;
1736
+ schema.permissions = { ...rules };
1737
+ }
1738
+ return schema;
1739
+ }
1740
+ function convertPocketBaseMigration(migrationContent) {
1741
+ try {
1742
+ const snapshotMatch = migrationContent.match(/const\s+snapshot\s*=\s*(\[[\s\S]*?\]);/);
1743
+ if (!snapshotMatch) {
1744
+ throw new Error("Could not find snapshot array in migration file");
1745
+ }
1746
+ const snapshotArrayStr = snapshotMatch[1];
1747
+ let snapshotArray;
1748
+ try {
1749
+ snapshotArray = new Function(`return ${snapshotArrayStr}`)();
1750
+ } catch (parseError) {
1751
+ throw new Error(`Failed to parse snapshot array: ${parseError}`);
1752
+ }
1753
+ if (!Array.isArray(snapshotArray)) {
1754
+ throw new Error("Snapshot is not an array");
1755
+ }
1756
+ const collections = /* @__PURE__ */ new Map();
1757
+ for (const pbCollection of snapshotArray) {
1758
+ if (!pbCollection.name) {
1759
+ console.warn("Skipping collection without name");
1760
+ continue;
1761
+ }
1762
+ const schema = convertPocketBaseCollection(pbCollection);
1763
+ collections.set(pbCollection.name, schema);
1764
+ }
1765
+ return {
1766
+ version: SNAPSHOT_VERSION,
1767
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1768
+ collections
1769
+ };
1770
+ } catch (error) {
1771
+ throw new SnapshotError(
1772
+ `Failed to convert PocketBase migration: ${error instanceof Error ? error.message : String(error)}`,
1773
+ void 0,
1774
+ "parse",
1775
+ error instanceof Error ? error : void 0
1776
+ );
1777
+ }
1778
+ }
1779
+
1780
+ // src/migration/migration-parser.ts
1781
+ function extractTimestampFromFilename(filename) {
1782
+ const match = filename.match(/^(\d+)_/);
1783
+ if (match) {
1784
+ return parseInt(match[1], 10);
1785
+ }
1786
+ return null;
1787
+ }
1788
+ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
1789
+ try {
1790
+ if (!fs3.existsSync(migrationsPath)) {
1791
+ return [];
1792
+ }
1793
+ const files = fs3.readdirSync(migrationsPath);
1794
+ const migrationFiles = [];
1795
+ for (const file of files) {
1796
+ if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
1797
+ continue;
1798
+ }
1799
+ if (!file.endsWith(".js")) {
1800
+ continue;
1801
+ }
1802
+ const timestamp = extractTimestampFromFilename(file);
1803
+ if (timestamp && timestamp > snapshotTimestamp) {
1804
+ migrationFiles.push({
1805
+ path: path.join(migrationsPath, file),
1806
+ timestamp
1807
+ });
1808
+ }
1809
+ }
1810
+ migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
1811
+ return migrationFiles.map((f) => f.path);
1812
+ } catch (error) {
1813
+ console.warn(`Error finding migrations after snapshot: ${error}`);
1814
+ return [];
1815
+ }
1816
+ }
1817
+ function parseMigrationOperationsFromContent(content) {
1818
+ const collectionsToCreate = [];
1819
+ const collectionsToDelete = [];
1820
+ try {
1821
+ let searchIndex = 0;
1822
+ while (true) {
1823
+ const collectionStart = content.indexOf("new Collection(", searchIndex);
1824
+ if (collectionStart === -1) {
1825
+ break;
1826
+ }
1827
+ const openParen = collectionStart + "new Collection(".length;
1828
+ let braceCount = 0;
1829
+ let parenCount = 1;
1830
+ let inString = false;
1831
+ let stringChar = null;
1832
+ let i = openParen;
1833
+ while (i < content.length && /\s/.test(content[i])) {
1834
+ i++;
1835
+ }
1836
+ if (content[i] !== "{") {
1837
+ searchIndex = i + 1;
1838
+ continue;
1839
+ }
1840
+ const objectStart = i;
1841
+ braceCount = 1;
1842
+ i++;
1843
+ while (i < content.length && (braceCount > 0 || parenCount > 0)) {
1844
+ const char = content[i];
1845
+ const prevChar = i > 0 ? content[i - 1] : "";
1846
+ if (!inString && (char === '"' || char === "'")) {
1847
+ inString = true;
1848
+ stringChar = char;
1849
+ } else if (inString && char === stringChar && prevChar !== "\\") {
1850
+ inString = false;
1851
+ stringChar = null;
1852
+ }
1853
+ if (!inString) {
1854
+ if (char === "{") braceCount++;
1855
+ if (char === "}") braceCount--;
1856
+ if (char === "(") parenCount++;
1857
+ if (char === ")") parenCount--;
1858
+ }
1859
+ i++;
1860
+ }
1861
+ if (braceCount === 0 && parenCount === 0) {
1862
+ const objectContent = content.substring(objectStart, i - 1);
1863
+ try {
1864
+ const collectionObj = new Function(`return ${objectContent}`)();
1865
+ if (collectionObj && collectionObj.name) {
1866
+ const schema = convertPocketBaseCollection(collectionObj);
1867
+ collectionsToCreate.push(schema);
1868
+ }
1869
+ } catch (error) {
1870
+ console.warn(`Failed to parse collection definition: ${error}`);
1871
+ }
1872
+ }
1873
+ searchIndex = i;
1874
+ }
1875
+ const deleteMatches = content.matchAll(
1876
+ /app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
1877
+ );
1878
+ for (const match of deleteMatches) {
1879
+ if (match[1]) {
1880
+ collectionsToDelete.push(match[1]);
1881
+ } else {
1882
+ const varNameMatch = match[0].match(/collection_(\w+)/);
1883
+ if (varNameMatch) {
1884
+ const varName = `collection_${varNameMatch[1]}`;
1885
+ const deleteIndex = content.indexOf(match[0]);
1886
+ const beforeDelete = content.substring(0, deleteIndex);
1887
+ const varDefMatch = beforeDelete.match(
1888
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
1889
+ );
1890
+ if (varDefMatch && varDefMatch.length > 0) {
1891
+ const collectionDefMatch = beforeDelete.match(
1892
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
1893
+ );
1894
+ if (collectionDefMatch) {
1895
+ try {
1896
+ const collectionDefStr = collectionDefMatch[1];
1897
+ const collectionObj = new Function(`return ${collectionDefStr}`)();
1898
+ if (collectionObj && collectionObj.name) {
1899
+ collectionsToDelete.push(collectionObj.name);
1900
+ }
1901
+ } catch {
1902
+ }
1903
+ }
1904
+ }
1905
+ }
1906
+ }
1907
+ }
1908
+ const findAndDeleteMatches = content.matchAll(
1909
+ /app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
1910
+ );
1911
+ for (const match of findAndDeleteMatches) {
1912
+ collectionsToDelete.push(match[1]);
1913
+ }
1914
+ } catch (error) {
1915
+ console.warn(`Failed to parse migration operations from content: ${error}`);
1916
+ }
1917
+ return { collectionsToCreate, collectionsToDelete };
1918
+ }
1919
+ function parseMigrationOperations(migrationContent) {
1920
+ try {
1921
+ const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
1922
+ if (!migrateMatch) {
1923
+ return parseMigrationOperationsFromContent(migrationContent);
1924
+ }
1925
+ const startIndex = migrateMatch.index + migrateMatch[0].length;
1926
+ let i = startIndex;
1927
+ let parenCount = 0;
1928
+ let foundFirstParen = false;
1929
+ while (i < migrationContent.length) {
1930
+ const char = migrationContent[i];
1931
+ if (char === "(") {
1932
+ parenCount++;
1933
+ foundFirstParen = true;
1934
+ i++;
1935
+ break;
1936
+ }
1937
+ i++;
1938
+ }
1939
+ if (!foundFirstParen) {
1940
+ return parseMigrationOperationsFromContent(migrationContent);
1941
+ }
1942
+ let inString = false;
1943
+ let stringChar = null;
1944
+ let foundBrace = false;
1945
+ let braceStart = -1;
1946
+ while (i < migrationContent.length && !foundBrace) {
1947
+ const char = migrationContent[i];
1948
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
1949
+ if (!inString && (char === '"' || char === "'")) {
1950
+ inString = true;
1951
+ stringChar = char;
1952
+ } else if (inString && char === stringChar && prevChar !== "\\") {
1953
+ inString = false;
1954
+ stringChar = null;
1955
+ }
1956
+ if (!inString) {
1957
+ if (char === "(") parenCount++;
1958
+ if (char === ")") {
1959
+ parenCount--;
1960
+ if (parenCount === 0) {
1961
+ i++;
1962
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
1963
+ i++;
1964
+ }
1965
+ if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
1966
+ i += 2;
1967
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
1968
+ i++;
1969
+ }
1970
+ if (i < migrationContent.length && migrationContent[i] === "{") {
1971
+ foundBrace = true;
1972
+ braceStart = i + 1;
1973
+ break;
1974
+ }
1975
+ }
1976
+ }
1977
+ }
1978
+ }
1979
+ i++;
1980
+ }
1981
+ if (!foundBrace || braceStart === -1) {
1982
+ return parseMigrationOperationsFromContent(migrationContent);
1983
+ }
1984
+ let braceCount = 1;
1985
+ i = braceStart;
1986
+ inString = false;
1987
+ stringChar = null;
1988
+ while (i < migrationContent.length && braceCount > 0) {
1989
+ const char = migrationContent[i];
1990
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
1991
+ if (!inString && (char === '"' || char === "'")) {
1992
+ inString = true;
1993
+ stringChar = char;
1994
+ } else if (inString && char === stringChar && prevChar !== "\\") {
1995
+ inString = false;
1996
+ stringChar = null;
1997
+ }
1998
+ if (!inString) {
1999
+ if (char === "{") braceCount++;
2000
+ if (char === "}") braceCount--;
2001
+ }
2002
+ i++;
2003
+ }
2004
+ if (braceCount === 0) {
2005
+ const upMigrationContent = migrationContent.substring(braceStart, i - 1);
2006
+ return parseMigrationOperationsFromContent(upMigrationContent);
2007
+ }
2008
+ return parseMigrationOperationsFromContent(migrationContent);
2009
+ } catch (error) {
2010
+ console.warn(`Failed to parse migration operations: ${error}`);
2011
+ return { collectionsToCreate: [], collectionsToDelete: [] };
2012
+ }
2013
+ }
2014
+
2015
+ // src/migration/snapshot.ts
2016
+ var SNAPSHOT_VERSION2 = "1.0.0";
1592
2017
  var DEFAULT_SNAPSHOT_FILENAME = ".migration-snapshot.json";
1593
2018
  var SNAPSHOT_MIGRATIONS = [
1594
2019
  // Add migrations here as the format evolves
@@ -1603,7 +2028,7 @@ var DEFAULT_CONFIG2 = {
1603
2028
  snapshotPath: DEFAULT_SNAPSHOT_FILENAME,
1604
2029
  workspaceRoot: process.cwd(),
1605
2030
  autoMigrate: true,
1606
- version: SNAPSHOT_VERSION
2031
+ version: SNAPSHOT_VERSION2
1607
2032
  };
1608
2033
  function mergeConfig2(config = {}) {
1609
2034
  return {
@@ -1623,7 +2048,7 @@ function getSnapshotPath(config = {}) {
1623
2048
  function snapshotExists(config = {}) {
1624
2049
  try {
1625
2050
  const snapshotPath = getSnapshotPath(config);
1626
- return fs2.existsSync(snapshotPath);
2051
+ return fs3.existsSync(snapshotPath);
1627
2052
  } catch {
1628
2053
  return false;
1629
2054
  }
@@ -1683,12 +2108,12 @@ function saveSnapshot(schema, config = {}) {
1683
2108
  const snapshotPath = getSnapshotPath(config);
1684
2109
  try {
1685
2110
  const snapshotDir = path.dirname(snapshotPath);
1686
- if (!fs2.existsSync(snapshotDir)) {
1687
- fs2.mkdirSync(snapshotDir, { recursive: true });
2111
+ if (!fs3.existsSync(snapshotDir)) {
2112
+ fs3.mkdirSync(snapshotDir, { recursive: true });
1688
2113
  }
1689
2114
  const snapshotData = addSnapshotMetadata(schema, config);
1690
2115
  const jsonContent = JSON.stringify(snapshotData, null, 2);
1691
- fs2.writeFileSync(snapshotPath, jsonContent, "utf-8");
2116
+ fs3.writeFileSync(snapshotPath, jsonContent, "utf-8");
1692
2117
  } catch (error) {
1693
2118
  handleFileSystemError(error, "write", snapshotPath);
1694
2119
  }
@@ -1783,7 +2208,7 @@ function deserializeSnapshot(data) {
1783
2208
  function loadSnapshot(config = {}) {
1784
2209
  const snapshotPath = getSnapshotPath(config);
1785
2210
  try {
1786
- const jsonContent = fs2.readFileSync(snapshotPath, "utf-8");
2211
+ const jsonContent = fs3.readFileSync(snapshotPath, "utf-8");
1787
2212
  const data = parseAndValidateSnapshot(jsonContent, snapshotPath);
1788
2213
  const migratedData = migrateSnapshotFormat(data, config);
1789
2214
  return deserializeSnapshot(migratedData);
@@ -1818,10 +2243,10 @@ function mergeSnapshots(baseSnapshot, customSnapshot) {
1818
2243
  }
1819
2244
  function findLatestSnapshot(migrationsPath) {
1820
2245
  try {
1821
- if (!fs2.existsSync(migrationsPath)) {
2246
+ if (!fs3.existsSync(migrationsPath)) {
1822
2247
  return null;
1823
2248
  }
1824
- const files = fs2.readdirSync(migrationsPath);
2249
+ const files = fs3.readdirSync(migrationsPath);
1825
2250
  const snapshotFiles = files.filter(
1826
2251
  (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
1827
2252
  );
@@ -1839,14 +2264,68 @@ function findLatestSnapshot(migrationsPath) {
1839
2264
  return null;
1840
2265
  }
1841
2266
  }
2267
+ function applyMigrationOperations(snapshot, operations) {
2268
+ const updatedCollections = new Map(snapshot.collections);
2269
+ for (const collectionName of operations.collectionsToDelete) {
2270
+ updatedCollections.delete(collectionName);
2271
+ }
2272
+ for (const collection of operations.collectionsToCreate) {
2273
+ updatedCollections.set(collection.name, collection);
2274
+ }
2275
+ return {
2276
+ ...snapshot,
2277
+ collections: updatedCollections
2278
+ };
2279
+ }
2280
+ function loadSnapshotWithMigrations(config = {}) {
2281
+ const migrationsPath = config.migrationsPath;
2282
+ if (!migrationsPath) {
2283
+ return null;
2284
+ }
2285
+ if (fs3.existsSync(migrationsPath) && fs3.statSync(migrationsPath).isFile()) {
2286
+ try {
2287
+ const migrationContent = fs3.readFileSync(migrationsPath, "utf-8");
2288
+ return convertPocketBaseMigration(migrationContent);
2289
+ } catch (error) {
2290
+ console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2291
+ return null;
2292
+ }
2293
+ }
2294
+ const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2295
+ if (!latestSnapshotPath) {
2296
+ return null;
2297
+ }
2298
+ try {
2299
+ const migrationContent = fs3.readFileSync(latestSnapshotPath, "utf-8");
2300
+ let snapshot = convertPocketBaseMigration(migrationContent);
2301
+ const snapshotFilename = path.basename(latestSnapshotPath);
2302
+ const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
2303
+ if (snapshotTimestamp) {
2304
+ const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
2305
+ for (const migrationFile of migrationFiles) {
2306
+ try {
2307
+ const migrationContent2 = fs3.readFileSync(migrationFile, "utf-8");
2308
+ const operations = parseMigrationOperations(migrationContent2);
2309
+ snapshot = applyMigrationOperations(snapshot, operations);
2310
+ } catch (error) {
2311
+ console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
2312
+ }
2313
+ }
2314
+ }
2315
+ return snapshot;
2316
+ } catch (error) {
2317
+ console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2318
+ return null;
2319
+ }
2320
+ }
1842
2321
  function loadSnapshotIfExists(config = {}) {
1843
2322
  const migrationsPath = config.migrationsPath;
1844
2323
  if (!migrationsPath) {
1845
2324
  return null;
1846
2325
  }
1847
- if (fs2.existsSync(migrationsPath) && fs2.statSync(migrationsPath).isFile()) {
2326
+ if (fs3.existsSync(migrationsPath) && fs3.statSync(migrationsPath).isFile()) {
1848
2327
  try {
1849
- const migrationContent = fs2.readFileSync(migrationsPath, "utf-8");
2328
+ const migrationContent = fs3.readFileSync(migrationsPath, "utf-8");
1850
2329
  return convertPocketBaseMigration(migrationContent);
1851
2330
  } catch (error) {
1852
2331
  console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
@@ -1856,7 +2335,7 @@ function loadSnapshotIfExists(config = {}) {
1856
2335
  const latestSnapshotPath = findLatestSnapshot(migrationsPath);
1857
2336
  if (latestSnapshotPath) {
1858
2337
  try {
1859
- const migrationContent = fs2.readFileSync(latestSnapshotPath, "utf-8");
2338
+ const migrationContent = fs3.readFileSync(latestSnapshotPath, "utf-8");
1860
2339
  return convertPocketBaseMigration(migrationContent);
1861
2340
  } catch (error) {
1862
2341
  console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
@@ -1865,99 +2344,9 @@ function loadSnapshotIfExists(config = {}) {
1865
2344
  }
1866
2345
  return null;
1867
2346
  }
1868
- function convertPocketBaseCollection(pbCollection) {
1869
- const fields = [];
1870
- const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
1871
- const authSystemFieldNames = ["email", "emailVisibility", "verified", "password", "tokenKey"];
1872
- if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
1873
- for (const pbField of pbCollection.fields) {
1874
- if (pbField.system || systemFieldNames.includes(pbField.name)) {
1875
- continue;
1876
- }
1877
- if (pbCollection.type === "auth" && authSystemFieldNames.includes(pbField.name)) {
1878
- continue;
1879
- }
1880
- const field = {
1881
- name: pbField.name,
1882
- type: pbField.type,
1883
- required: pbField.required || false
1884
- };
1885
- if (pbField.options) {
1886
- field.options = pbField.options;
1887
- }
1888
- if (pbField.type === "relation") {
1889
- field.relation = {
1890
- collection: pbField.options?.collectionId || "",
1891
- cascadeDelete: pbField.options?.cascadeDelete || false,
1892
- maxSelect: pbField.options?.maxSelect,
1893
- minSelect: pbField.options?.minSelect
1894
- };
1895
- }
1896
- fields.push(field);
1897
- }
1898
- }
1899
- const schema = {
1900
- name: pbCollection.name,
1901
- type: pbCollection.type || "base",
1902
- fields
1903
- };
1904
- if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
1905
- schema.indexes = pbCollection.indexes;
1906
- }
1907
- const rules = {};
1908
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1909
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1910
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1911
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1912
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1913
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1914
- if (Object.keys(rules).length > 0) {
1915
- schema.rules = rules;
1916
- }
1917
- return schema;
1918
- }
1919
- function convertPocketBaseMigration(migrationContent) {
1920
- try {
1921
- const snapshotMatch = migrationContent.match(/const\s+snapshot\s*=\s*(\[[\s\S]*?\]);/);
1922
- if (!snapshotMatch) {
1923
- throw new Error("Could not find snapshot array in migration file");
1924
- }
1925
- const snapshotArrayStr = snapshotMatch[1];
1926
- let snapshotArray;
1927
- try {
1928
- snapshotArray = new Function(`return ${snapshotArrayStr}`)();
1929
- } catch (parseError) {
1930
- throw new Error(`Failed to parse snapshot array: ${parseError}`);
1931
- }
1932
- if (!Array.isArray(snapshotArray)) {
1933
- throw new Error("Snapshot is not an array");
1934
- }
1935
- const collections = /* @__PURE__ */ new Map();
1936
- for (const pbCollection of snapshotArray) {
1937
- if (!pbCollection.name) {
1938
- console.warn("Skipping collection without name");
1939
- continue;
1940
- }
1941
- const schema = convertPocketBaseCollection(pbCollection);
1942
- collections.set(pbCollection.name, schema);
1943
- }
1944
- return {
1945
- version: SNAPSHOT_VERSION,
1946
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1947
- collections
1948
- };
1949
- } catch (error) {
1950
- throw new SnapshotError(
1951
- `Failed to convert PocketBase migration: ${error instanceof Error ? error.message : String(error)}`,
1952
- void 0,
1953
- "parse",
1954
- error instanceof Error ? error : void 0
1955
- );
1956
- }
1957
- }
1958
2347
  function loadBaseMigration(migrationPath) {
1959
2348
  try {
1960
- if (!fs2.existsSync(migrationPath)) {
2349
+ if (!fs3.existsSync(migrationPath)) {
1961
2350
  throw new SnapshotError(
1962
2351
  `Base migration file not found: ${migrationPath}
1963
2352
 
@@ -1968,7 +2357,7 @@ If the file exists in a different location, update the configuration.`,
1968
2357
  "read"
1969
2358
  );
1970
2359
  }
1971
- const migrationContent = fs2.readFileSync(migrationPath, "utf-8");
2360
+ const migrationContent = fs3.readFileSync(migrationPath, "utf-8");
1972
2361
  const snapshot = convertPocketBaseMigration(migrationContent);
1973
2362
  return snapshot;
1974
2363
  } catch (error) {
@@ -2004,14 +2393,14 @@ Please ensure PocketBase is properly set up by running 'yarn setup'.`,
2004
2393
  }
2005
2394
  }
2006
2395
  function getSnapshotVersion() {
2007
- return SNAPSHOT_VERSION;
2396
+ return SNAPSHOT_VERSION2;
2008
2397
  }
2009
2398
  function validateSnapshot(snapshot) {
2010
2399
  const issues = [];
2011
2400
  if (!snapshot.version) {
2012
2401
  issues.push("Missing version field");
2013
- } else if (compareVersions(snapshot.version, SNAPSHOT_VERSION) > 0) {
2014
- issues.push(`Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION}`);
2402
+ } else if (compareVersions(snapshot.version, SNAPSHOT_VERSION2) > 0) {
2403
+ issues.push(`Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION2}`);
2015
2404
  }
2016
2405
  if (!snapshot.timestamp) {
2017
2406
  issues.push("Missing timestamp field");
@@ -2230,6 +2619,9 @@ function compareFieldOptions(currentField, previousField) {
2230
2619
  for (const key of allKeys) {
2231
2620
  const currentValue = currentOptions[key];
2232
2621
  const previousValue = previousOptions[key];
2622
+ if (currentValue === void 0 && previousValue === void 0) {
2623
+ continue;
2624
+ }
2233
2625
  if (!areValuesEqual(currentValue, previousValue)) {
2234
2626
  changes.push({
2235
2627
  property: `options.${key}`,
@@ -2250,11 +2642,26 @@ function compareRelationConfigurations(currentField, previousField) {
2250
2642
  if (!currentRelation || !previousRelation) {
2251
2643
  return changes;
2252
2644
  }
2253
- if (currentRelation.collection !== previousRelation.collection) {
2645
+ const normalizeCollection = (collection) => {
2646
+ if (!collection) return collection;
2647
+ if (collection === "_pb_users_auth_") {
2648
+ return "Users";
2649
+ }
2650
+ const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2651
+ if (nameMatch) {
2652
+ return nameMatch[1];
2653
+ }
2654
+ return collection;
2655
+ };
2656
+ const normalizedCurrent = normalizeCollection(currentRelation.collection);
2657
+ const normalizedPrevious = normalizeCollection(previousRelation.collection);
2658
+ if (normalizedCurrent !== normalizedPrevious) {
2254
2659
  changes.push({
2255
2660
  property: "relation.collection",
2256
- oldValue: previousRelation.collection,
2257
- newValue: currentRelation.collection
2661
+ oldValue: normalizedPrevious,
2662
+ // Use normalized value for clarity
2663
+ newValue: normalizedCurrent
2664
+ // Use normalized value for clarity
2258
2665
  });
2259
2666
  }
2260
2667
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -2608,10 +3015,8 @@ var DiffEngine = class {
2608
3015
  var DEFAULT_TEMPLATE = `/// <reference path="{{TYPES_PATH}}" />
2609
3016
  migrate((app) => {
2610
3017
  {{UP_CODE}}
2611
- return true;
2612
3018
  }, (app) => {
2613
3019
  {{DOWN_CODE}}
2614
- return true;
2615
3020
  });
2616
3021
  `;
2617
3022
  var DEFAULT_CONFIG4 = {
@@ -2690,9 +3095,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
2690
3095
  }
2691
3096
  function writeMigrationFile(migrationDir, filename, content) {
2692
3097
  try {
2693
- if (!fs2.existsSync(migrationDir)) {
3098
+ if (!fs3.existsSync(migrationDir)) {
2694
3099
  try {
2695
- fs2.mkdirSync(migrationDir, { recursive: true });
3100
+ fs3.mkdirSync(migrationDir, { recursive: true });
2696
3101
  } catch (error) {
2697
3102
  const fsError = error;
2698
3103
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
@@ -2714,7 +3119,7 @@ function writeMigrationFile(migrationDir, filename, content) {
2714
3119
  }
2715
3120
  }
2716
3121
  const filePath = path.join(migrationDir, filename);
2717
- fs2.writeFileSync(filePath, content, "utf-8");
3122
+ fs3.writeFileSync(filePath, content, "utf-8");
2718
3123
  return filePath;
2719
3124
  } catch (error) {
2720
3125
  if (error instanceof FileSystemError) {
@@ -2776,7 +3181,8 @@ function generateFieldDefinitionObject(field) {
2776
3181
  }
2777
3182
  }
2778
3183
  if (field.relation) {
2779
- const collectionIdPlaceholder = field.relation.collection === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3184
+ const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3185
+ const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2780
3186
  parts.push(` collectionId: ${collectionIdPlaceholder}`);
2781
3187
  if (field.relation.maxSelect !== void 0) {
2782
3188
  parts.push(` maxSelect: ${field.relation.maxSelect}`);
@@ -2860,7 +3266,7 @@ function generateIndexesArray(indexes) {
2860
3266
  ${indexStrings.join(",\n ")},
2861
3267
  ]`;
2862
3268
  }
2863
- function generateCollectionCreation(collection, varName = "collection") {
3269
+ function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2864
3270
  const lines = [];
2865
3271
  lines.push(` const ${varName} = new Collection({`);
2866
3272
  lines.push(` name: "${collection.name}",`);
@@ -2876,7 +3282,7 @@ function generateCollectionCreation(collection, varName = "collection") {
2876
3282
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2877
3283
  lines.push(` });`);
2878
3284
  lines.push(``);
2879
- lines.push(` app.save(${varName});`);
3285
+ lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
2880
3286
  return lines.join("\n");
2881
3287
  }
2882
3288
  function getFieldConstructorName(fieldType) {
@@ -2907,7 +3313,8 @@ function generateFieldConstructorOptions(field) {
2907
3313
  }
2908
3314
  }
2909
3315
  if (field.relation && field.type === "relation") {
2910
- const collectionIdPlaceholder = field.relation.collection === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3316
+ const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3317
+ const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2911
3318
  parts.push(` collectionId: ${collectionIdPlaceholder}`);
2912
3319
  if (field.relation.maxSelect !== void 0) {
2913
3320
  parts.push(` maxSelect: ${field.relation.maxSelect}`);
@@ -2921,7 +3328,7 @@ function generateFieldConstructorOptions(field) {
2921
3328
  }
2922
3329
  return parts.join(",\n");
2923
3330
  }
2924
- function generateFieldAddition(collectionName, field, varName) {
3331
+ function generateFieldAddition(collectionName, field, varName, isLast = false) {
2925
3332
  const lines = [];
2926
3333
  const constructorName = getFieldConstructorName(field.type);
2927
3334
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
@@ -2931,10 +3338,10 @@ function generateFieldAddition(collectionName, field, varName) {
2931
3338
  lines.push(generateFieldConstructorOptions(field));
2932
3339
  lines.push(` }));`);
2933
3340
  lines.push(``);
2934
- lines.push(` app.save(${collectionVar});`);
3341
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2935
3342
  return lines.join("\n");
2936
3343
  }
2937
- function generateFieldModification(collectionName, modification, varName) {
3344
+ function generateFieldModification(collectionName, modification, varName, isLast = false) {
2938
3345
  const lines = [];
2939
3346
  const collectionVar = varName || `collection_${collectionName}_${modification.fieldName}`;
2940
3347
  const fieldVar = `${collectionVar}_field`;
@@ -2948,7 +3355,8 @@ function generateFieldModification(collectionName, modification, varName) {
2948
3355
  } else if (change.property.startsWith("relation.")) {
2949
3356
  const relationKey = change.property.replace("relation.", "");
2950
3357
  if (relationKey === "collection") {
2951
- const collectionIdValue = change.newValue === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${change.newValue}").id`;
3358
+ const isUsersCollection = String(change.newValue).toLowerCase() === "users";
3359
+ const collectionIdValue = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${change.newValue}").id`;
2952
3360
  lines.push(` ${fieldVar}.collectionId = ${collectionIdValue};`);
2953
3361
  } else {
2954
3362
  lines.push(` ${fieldVar}.${relationKey} = ${formatValue(change.newValue)};`);
@@ -2958,10 +3366,10 @@ function generateFieldModification(collectionName, modification, varName) {
2958
3366
  }
2959
3367
  }
2960
3368
  lines.push(``);
2961
- lines.push(` app.save(${collectionVar});`);
3369
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2962
3370
  return lines.join("\n");
2963
3371
  }
2964
- function generateFieldDeletion(collectionName, fieldName, varName) {
3372
+ function generateFieldDeletion(collectionName, fieldName, varName, isLast = false) {
2965
3373
  const lines = [];
2966
3374
  const collectionVar = varName || `collection_${collectionName}_${fieldName}`;
2967
3375
  const fieldVar = `${collectionVar}_field`;
@@ -2970,18 +3378,18 @@ function generateFieldDeletion(collectionName, fieldName, varName) {
2970
3378
  lines.push(``);
2971
3379
  lines.push(` ${collectionVar}.fields.remove(${fieldVar}.id);`);
2972
3380
  lines.push(``);
2973
- lines.push(` app.save(${collectionVar});`);
3381
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2974
3382
  return lines.join("\n");
2975
3383
  }
2976
- function generateIndexAddition(collectionName, index, varName) {
3384
+ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2977
3385
  const lines = [];
2978
3386
  const collectionVar = varName || `collection_${collectionName}_idx`;
2979
3387
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2980
3388
  lines.push(` ${collectionVar}.indexes.push("${index}");`);
2981
- lines.push(` app.save(${collectionVar});`);
3389
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2982
3390
  return lines.join("\n");
2983
3391
  }
2984
- function generateIndexRemoval(collectionName, index, varName) {
3392
+ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2985
3393
  const lines = [];
2986
3394
  const collectionVar = varName || `collection_${collectionName}_idx`;
2987
3395
  const indexVar = `${collectionVar}_indexToRemove`;
@@ -2990,29 +3398,29 @@ function generateIndexRemoval(collectionName, index, varName) {
2990
3398
  lines.push(` if (${indexVar} !== -1) {`);
2991
3399
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2992
3400
  lines.push(` }`);
2993
- lines.push(` app.save(${collectionVar});`);
3401
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2994
3402
  return lines.join("\n");
2995
3403
  }
2996
- function generateRuleUpdate(collectionName, ruleType, newValue, varName) {
3404
+ function generateRuleUpdate(collectionName, ruleType, newValue, varName, isLast = false) {
2997
3405
  const lines = [];
2998
3406
  const collectionVar = varName || `collection_${collectionName}_${ruleType}`;
2999
3407
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3000
3408
  lines.push(` ${collectionVar}.${ruleType} = ${formatValue(newValue)};`);
3001
- lines.push(` app.save(${collectionVar});`);
3409
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3002
3410
  return lines.join("\n");
3003
3411
  }
3004
- function generatePermissionUpdate(collectionName, ruleType, newValue, varName) {
3412
+ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, isLast = false) {
3005
3413
  const lines = [];
3006
3414
  const collectionVar = varName || `collection_${collectionName}_${ruleType}`;
3007
3415
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3008
3416
  lines.push(` ${collectionVar}.${ruleType} = ${formatValue(newValue)};`);
3009
- lines.push(` app.save(${collectionVar});`);
3417
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3010
3418
  return lines.join("\n");
3011
3419
  }
3012
- function generateCollectionDeletion(collectionName, varName = "collection") {
3420
+ function generateCollectionDeletion(collectionName, varName = "collection", isLast = false) {
3013
3421
  const lines = [];
3014
3422
  lines.push(` const ${varName} = app.findCollectionByNameOrId("${collectionName}");`);
3015
- lines.push(` app.delete(${varName});`);
3423
+ lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
3016
3424
  return lines.join("\n");
3017
3425
  }
3018
3426
  function generateUpMigration(diff) {
@@ -3104,7 +3512,24 @@ function generateUpMigration(diff) {
3104
3512
  lines.push(` // No changes detected`);
3105
3513
  lines.push(``);
3106
3514
  }
3107
- return lines.join("\n");
3515
+ let code = lines.join("\n");
3516
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3517
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3518
+ const saveMatches = [...code.matchAll(savePattern)];
3519
+ const deleteMatches = [...code.matchAll(deletePattern)];
3520
+ const allMatches = [
3521
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3522
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3523
+ ].sort((a, b) => b.index - a.index);
3524
+ if (allMatches.length > 0) {
3525
+ const lastMatch = allMatches[0];
3526
+ if (lastMatch.type === "save") {
3527
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.save(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
3528
+ } else {
3529
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.delete(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
3530
+ }
3531
+ }
3532
+ return code;
3108
3533
  }
3109
3534
  function generateDownMigration(diff) {
3110
3535
  const lines = [];
@@ -3206,7 +3631,24 @@ function generateDownMigration(diff) {
3206
3631
  lines.push(` // No changes to revert`);
3207
3632
  lines.push(``);
3208
3633
  }
3209
- return lines.join("\n");
3634
+ let code = lines.join("\n");
3635
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3636
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3637
+ const saveMatches = [...code.matchAll(savePattern)];
3638
+ const deleteMatches = [...code.matchAll(deletePattern)];
3639
+ const allMatches = [
3640
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3641
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3642
+ ].sort((a, b) => b.index - a.index);
3643
+ if (allMatches.length > 0) {
3644
+ const lastMatch = allMatches[0];
3645
+ if (lastMatch.type === "save") {
3646
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.save(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
3647
+ } else {
3648
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.delete(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
3649
+ }
3650
+ }
3651
+ return code;
3210
3652
  }
3211
3653
  function generate(diff, config) {
3212
3654
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
@@ -3260,6 +3702,6 @@ var MigrationGenerator = class {
3260
3702
  }
3261
3703
  };
3262
3704
 
3263
- export { CLIUsageError, ConfigurationError, DiffEngine, FIELD_TYPE_INFO, FileSystemError, MigrationError, MigrationGenerationError, MigrationGenerator, POCKETBASE_FIELD_TYPES, SchemaAnalyzer, SchemaParsingError, SnapshotError, SnapshotManager, aggregateChanges, buildFieldDefinition, buildSchemaDefinition, categorizeChangesBySeverity, compare, compareFieldConstraints, compareFieldOptions, compareFieldTypes, comparePermissions, compareRelationConfigurations, convertPocketBaseMigration, convertZodSchemaToCollectionSchema, createMigrationFileStructure, detectDestructiveChanges, detectFieldChanges, discoverSchemaFiles, extractComprehensiveFieldOptions, extractFieldDefinitions, extractFieldOptions, extractIndexes, extractSchemaDefinitions, filterSystemCollections, findLatestSnapshot, findNewCollections, findNewFields, findRemovedCollections, findRemovedFields, generate, generateChangeSummary, generateCollectionCreation, generateCollectionPermissions, generateCollectionRules, generateDownMigration, generateFieldAddition, generateFieldDefinitionObject, generateFieldDeletion, generateFieldModification, generateFieldsArray, generateIndexesArray, generateMigrationDescription, generateMigrationFilename, generatePermissionUpdate, generateTimestamp, generateUpMigration, getArrayElementType, getCollectionNameFromFile, getDefaultValue, getFieldTypeInfo, getMaxSelect, getMinSelect, getSnapshotPath, getSnapshotVersion, getUsersSystemFields, importSchemaModule, isArrayType, isAuthCollection, isEditorField, isFieldRequired, isFileFieldByName, isGeoPointType, isMultipleRelationField, isRelationField, isSingleRelationField, isSystemCollection, loadBaseMigration, loadSnapshot, loadSnapshotIfExists, mapZodArrayType, mapZodBooleanType, mapZodDateType, mapZodEnumType, mapZodNumberType, mapZodRecordType, mapZodStringType, mapZodTypeToPocketBase, matchCollectionsByName, matchFieldsByName, mergeSnapshots, parseSchemaFiles, pluralize, requiresForceFlag, resolveTargetCollection, saveSnapshot, selectSchemaForCollection, singularize, snapshotExists, toCollectionName, unwrapZodType, validateSnapshot, writeMigrationFile };
3705
+ export { CLIUsageError, ConfigurationError, DiffEngine, FIELD_TYPE_INFO, FileSystemError, MigrationError, MigrationGenerationError, MigrationGenerator, POCKETBASE_FIELD_TYPES, SchemaAnalyzer, SchemaParsingError, SnapshotError, SnapshotManager, aggregateChanges, buildFieldDefinition, buildSchemaDefinition, categorizeChangesBySeverity, compare, compareFieldConstraints, compareFieldOptions, compareFieldTypes, comparePermissions, compareRelationConfigurations, convertPocketBaseMigration, convertZodSchemaToCollectionSchema, createMigrationFileStructure, detectDestructiveChanges, detectFieldChanges, discoverSchemaFiles, extractComprehensiveFieldOptions, extractFieldDefinitions, extractFieldOptions, extractIndexes, extractSchemaDefinitions, filterSystemCollections, findLatestSnapshot, findNewCollections, findNewFields, findRemovedCollections, findRemovedFields, generate, generateChangeSummary, generateCollectionCreation, generateCollectionPermissions, generateCollectionRules, generateDownMigration, generateFieldAddition, generateFieldDefinitionObject, generateFieldDeletion, generateFieldModification, generateFieldsArray, generateIndexesArray, generateMigrationDescription, generateMigrationFilename, generatePermissionUpdate, generateTimestamp, generateUpMigration, getArrayElementType, getCollectionNameFromFile, getDefaultValue, getFieldTypeInfo, getMaxSelect, getMinSelect, getSnapshotPath, getSnapshotVersion, getUsersSystemFields, importSchemaModule, isArrayType, isAuthCollection, isEditorField, isFieldRequired, isFileFieldByName, isGeoPointType, isMultipleRelationField, isRelationField, isSingleRelationField, isSystemCollection, loadBaseMigration, loadSnapshot, loadSnapshotIfExists, loadSnapshotWithMigrations, mapZodArrayType, mapZodBooleanType, mapZodDateType, mapZodEnumType, mapZodNumberType, mapZodRecordType, mapZodStringType, mapZodTypeToPocketBase, matchCollectionsByName, matchFieldsByName, mergeSnapshots, parseSchemaFiles, pluralize, requiresForceFlag, resolveTargetCollection, saveSnapshot, selectSchemaForCollection, singularize, snapshotExists, toCollectionName, unwrapZodType, validateSnapshot, writeMigrationFile };
3264
3706
  //# sourceMappingURL=index.js.map
3265
3707
  //# sourceMappingURL=index.js.map