pocketbase-zod-schema 0.1.3 → 0.2.0

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 (57) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +233 -98
  3. package/dist/cli/index.cjs +449 -108
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.js +447 -106
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/migrate.cjs +452 -111
  8. package/dist/cli/migrate.cjs.map +1 -1
  9. package/dist/cli/migrate.js +447 -106
  10. package/dist/cli/migrate.js.map +1 -1
  11. package/dist/index.cjs +593 -175
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +4 -4
  14. package/dist/index.d.ts +4 -4
  15. package/dist/index.js +583 -172
  16. package/dist/index.js.map +1 -1
  17. package/dist/migration/analyzer.cjs +44 -6
  18. package/dist/migration/analyzer.cjs.map +1 -1
  19. package/dist/migration/analyzer.d.cts +11 -1
  20. package/dist/migration/analyzer.d.ts +11 -1
  21. package/dist/migration/analyzer.js +44 -7
  22. package/dist/migration/analyzer.js.map +1 -1
  23. package/dist/migration/diff.cjs +21 -3
  24. package/dist/migration/diff.cjs.map +1 -1
  25. package/dist/migration/diff.js +21 -3
  26. package/dist/migration/diff.js.map +1 -1
  27. package/dist/migration/index.cjs +500 -129
  28. package/dist/migration/index.cjs.map +1 -1
  29. package/dist/migration/index.d.cts +1 -1
  30. package/dist/migration/index.d.ts +1 -1
  31. package/dist/migration/index.js +499 -129
  32. package/dist/migration/index.js.map +1 -1
  33. package/dist/migration/snapshot.cjs +432 -118
  34. package/dist/migration/snapshot.cjs.map +1 -1
  35. package/dist/migration/snapshot.d.cts +34 -12
  36. package/dist/migration/snapshot.d.ts +34 -12
  37. package/dist/migration/snapshot.js +430 -117
  38. package/dist/migration/snapshot.js.map +1 -1
  39. package/dist/mutator.cjs +20 -21
  40. package/dist/mutator.cjs.map +1 -1
  41. package/dist/mutator.d.cts +4 -4
  42. package/dist/mutator.d.ts +4 -4
  43. package/dist/mutator.js +20 -21
  44. package/dist/mutator.js.map +1 -1
  45. package/dist/schema.cjs +69 -10
  46. package/dist/schema.cjs.map +1 -1
  47. package/dist/schema.d.cts +98 -8
  48. package/dist/schema.d.ts +98 -8
  49. package/dist/schema.js +62 -9
  50. package/dist/schema.js.map +1 -1
  51. package/dist/types.d.cts +5 -2
  52. package/dist/types.d.ts +5 -2
  53. package/dist/user-DTJQIj4K.d.cts +149 -0
  54. package/dist/user-DTJQIj4K.d.ts +149 -0
  55. package/package.json +3 -3
  56. package/dist/user-C39DQ40N.d.cts +0 -53
  57. package/dist/user-C39DQ40N.d.ts +0 -53
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var fs2 = require('fs');
3
+ var fs3 = require('fs');
4
4
  var path = require('path');
5
5
  var zod = require('zod');
6
6
 
@@ -22,7 +22,7 @@ function _interopNamespace(e) {
22
22
  return Object.freeze(n);
23
23
  }
24
24
 
25
- var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
25
+ var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
26
26
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
27
27
 
28
28
  // src/migration/analyzer.ts
@@ -153,10 +153,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
153
153
  operation;
154
154
  code;
155
155
  originalError;
156
- constructor(message, path4, operation, code, originalError) {
156
+ constructor(message, path5, operation, code, originalError) {
157
157
  super(message);
158
158
  this.name = "FileSystemError";
159
- this.path = path4;
159
+ this.path = path5;
160
160
  this.operation = operation;
161
161
  this.code = code;
162
162
  this.originalError = originalError;
@@ -1342,6 +1342,16 @@ function getFieldTypeInfo(zodType, fieldName) {
1342
1342
  }
1343
1343
 
1344
1344
  // src/migration/analyzer.ts
1345
+ var tsxLoaderRegistered = false;
1346
+ async function ensureTsxLoader() {
1347
+ if (tsxLoaderRegistered) return;
1348
+ try {
1349
+ await import('tsx/esm');
1350
+ tsxLoaderRegistered = true;
1351
+ } catch {
1352
+ tsxLoaderRegistered = false;
1353
+ }
1354
+ }
1345
1355
  var DEFAULT_CONFIG = {
1346
1356
  workspaceRoot: process.cwd(),
1347
1357
  excludePatterns: [
@@ -1379,10 +1389,10 @@ function discoverSchemaFiles(config) {
1379
1389
  const mergedConfig = mergeConfig(normalizedConfig);
1380
1390
  const schemaDir = resolveSchemaDir(normalizedConfig);
1381
1391
  try {
1382
- if (!fs2__namespace.existsSync(schemaDir)) {
1392
+ if (!fs3__namespace.existsSync(schemaDir)) {
1383
1393
  throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
1384
1394
  }
1385
- const files = fs2__namespace.readdirSync(schemaDir);
1395
+ const files = fs3__namespace.readdirSync(schemaDir);
1386
1396
  const schemaFiles = files.filter((file) => {
1387
1397
  const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
1388
1398
  if (!hasValidExtension) return false;
@@ -1432,31 +1442,44 @@ async function importSchemaModule(filePath, config) {
1432
1442
  let resolvedPath = null;
1433
1443
  const jsPath = `${importPath}.js`;
1434
1444
  const tsPath = `${importPath}.ts`;
1435
- if (fs2__namespace.existsSync(jsPath)) {
1445
+ if (fs3__namespace.existsSync(jsPath)) {
1436
1446
  resolvedPath = jsPath;
1437
- } else if (fs2__namespace.existsSync(tsPath)) {
1447
+ } else if (fs3__namespace.existsSync(tsPath)) {
1438
1448
  resolvedPath = tsPath;
1439
1449
  } else {
1440
1450
  resolvedPath = jsPath;
1441
1451
  }
1452
+ if (resolvedPath.endsWith(".ts")) {
1453
+ await ensureTsxLoader();
1454
+ if (!tsxLoaderRegistered) {
1455
+ throw new SchemaParsingError(
1456
+ `Failed to import TypeScript schema file. The 'tsx' package is required to load TypeScript files.
1457
+ Please install tsx: npm install tsx (or yarn add tsx, or pnpm add tsx)
1458
+ Alternatively, compile your schema files to JavaScript first.`,
1459
+ filePath
1460
+ );
1461
+ }
1462
+ }
1442
1463
  const fileUrl = new URL(`file://${path__namespace.resolve(resolvedPath)}`);
1443
1464
  const module = await import(fileUrl.href);
1444
1465
  return module;
1445
1466
  } catch (error) {
1446
1467
  const tsPath = `${filePath}.ts`;
1447
- const isTypeScriptFile = fs2__namespace.existsSync(tsPath);
1468
+ const isTypeScriptFile = fs3__namespace.existsSync(tsPath);
1469
+ if (isTypeScriptFile && error instanceof SchemaParsingError) {
1470
+ throw error;
1471
+ }
1448
1472
  if (isTypeScriptFile) {
1449
1473
  throw new SchemaParsingError(
1450
- `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
1451
- Please either:
1452
- 1. Compile your schema files to JavaScript first, or
1453
- 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")`,
1474
+ `Failed to import TypeScript schema file. The 'tsx' package is required to load TypeScript files.
1475
+ Please install tsx: npm install tsx (or yarn add tsx, or pnpm add tsx)
1476
+ Alternatively, compile your schema files to JavaScript first.`,
1454
1477
  filePath,
1455
1478
  error
1456
1479
  );
1457
1480
  }
1458
1481
  throw new SchemaParsingError(
1459
- `Failed to import schema module. Make sure the schema files are compiled to JavaScript.`,
1482
+ `Failed to import schema module. Make sure the schema files exist and are valid.`,
1460
1483
  filePath,
1461
1484
  error
1462
1485
  );
@@ -1466,6 +1489,19 @@ function getCollectionNameFromFile(filePath) {
1466
1489
  const filename = path__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
1467
1490
  return toCollectionName(filename);
1468
1491
  }
1492
+ function extractCollectionNameFromSchema(zodSchema) {
1493
+ if (!zodSchema.description) {
1494
+ return null;
1495
+ }
1496
+ try {
1497
+ const metadata = JSON.parse(zodSchema.description);
1498
+ if (metadata.collectionName && typeof metadata.collectionName === "string") {
1499
+ return metadata.collectionName;
1500
+ }
1501
+ } catch {
1502
+ }
1503
+ return null;
1504
+ }
1469
1505
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema"]) {
1470
1506
  const result = {};
1471
1507
  for (const [key, value] of Object.entries(module)) {
@@ -1628,7 +1664,7 @@ async function buildSchemaDefinition(config) {
1628
1664
  importPath = normalizedConfig.pathTransformer(filePath);
1629
1665
  } else if (mergedConfig.useCompiledFiles) {
1630
1666
  const distPath = filePath.replace(/\/src\//, "/dist/");
1631
- if (fs2__namespace.existsSync(`${distPath}.js`) || fs2__namespace.existsSync(`${distPath}.mjs`)) {
1667
+ if (fs3__namespace.existsSync(`${distPath}.js`) || fs3__namespace.existsSync(`${distPath}.mjs`)) {
1632
1668
  importPath = distPath;
1633
1669
  } else {
1634
1670
  importPath = filePath;
@@ -1641,7 +1677,8 @@ async function buildSchemaDefinition(config) {
1641
1677
  console.warn(`No valid schema found in ${filePath}, skipping...`);
1642
1678
  continue;
1643
1679
  }
1644
- const collectionName = getCollectionNameFromFile(filePath);
1680
+ const collectionNameFromSchema = extractCollectionNameFromSchema(zodSchema);
1681
+ const collectionName = collectionNameFromSchema ?? getCollectionNameFromFile(filePath);
1645
1682
  const collectionSchema = convertZodSchemaToCollectionSchema(collectionName, zodSchema);
1646
1683
  collections.set(collectionName, collectionSchema);
1647
1684
  } catch (error) {
@@ -1684,7 +1721,359 @@ var SchemaAnalyzer = class {
1684
1721
  return convertZodSchemaToCollectionSchema(name, schema);
1685
1722
  }
1686
1723
  };
1724
+
1725
+ // src/migration/pocketbase-converter.ts
1687
1726
  var SNAPSHOT_VERSION = "1.0.0";
1727
+ function resolveCollectionIdToName(collectionId) {
1728
+ if (collectionId === "_pb_users_auth_") {
1729
+ return "Users";
1730
+ }
1731
+ const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1732
+ if (nameMatch) {
1733
+ return nameMatch[1];
1734
+ }
1735
+ return collectionId;
1736
+ }
1737
+ function convertPocketBaseCollection(pbCollection) {
1738
+ const fields = [];
1739
+ const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
1740
+ const authSystemFieldNames = ["email", "emailVisibility", "verified", "password", "tokenKey"];
1741
+ if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
1742
+ for (const pbField of pbCollection.fields) {
1743
+ if (pbField.system || systemFieldNames.includes(pbField.name)) {
1744
+ continue;
1745
+ }
1746
+ if (pbCollection.type === "auth" && authSystemFieldNames.includes(pbField.name)) {
1747
+ continue;
1748
+ }
1749
+ const field = {
1750
+ name: pbField.name,
1751
+ type: pbField.type,
1752
+ required: pbField.required || false
1753
+ };
1754
+ field.options = pbField.options ? { ...pbField.options } : {};
1755
+ if (pbField.type === "select") {
1756
+ if (pbField.values && Array.isArray(pbField.values)) {
1757
+ field.options.values = pbField.values;
1758
+ } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
1759
+ field.options.values = pbField.options.values;
1760
+ }
1761
+ }
1762
+ if (pbField.type === "relation") {
1763
+ const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
1764
+ const collectionName = resolveCollectionIdToName(collectionId);
1765
+ field.relation = {
1766
+ collection: collectionName,
1767
+ cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
1768
+ maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
1769
+ minSelect: pbField.minSelect ?? pbField.options?.minSelect
1770
+ };
1771
+ }
1772
+ const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
1773
+ if (Object.keys(field.options).length === 0) {
1774
+ delete field.options;
1775
+ } else if (pbField.type === "select" && hasOnlyValues) ;
1776
+ fields.push(field);
1777
+ }
1778
+ }
1779
+ const schema = {
1780
+ name: pbCollection.name,
1781
+ type: pbCollection.type || "base",
1782
+ fields
1783
+ };
1784
+ if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
1785
+ schema.indexes = pbCollection.indexes;
1786
+ }
1787
+ const rules = {};
1788
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
1789
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
1790
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
1791
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
1792
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
1793
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
1794
+ if (Object.keys(rules).length > 0) {
1795
+ schema.rules = rules;
1796
+ schema.permissions = { ...rules };
1797
+ }
1798
+ return schema;
1799
+ }
1800
+ function convertPocketBaseMigration(migrationContent) {
1801
+ try {
1802
+ const snapshotMatch = migrationContent.match(/const\s+snapshot\s*=\s*(\[[\s\S]*?\]);/);
1803
+ if (!snapshotMatch) {
1804
+ throw new Error("Could not find snapshot array in migration file");
1805
+ }
1806
+ const snapshotArrayStr = snapshotMatch[1];
1807
+ let snapshotArray;
1808
+ try {
1809
+ snapshotArray = new Function(`return ${snapshotArrayStr}`)();
1810
+ } catch (parseError) {
1811
+ throw new Error(`Failed to parse snapshot array: ${parseError}`);
1812
+ }
1813
+ if (!Array.isArray(snapshotArray)) {
1814
+ throw new Error("Snapshot is not an array");
1815
+ }
1816
+ const collections = /* @__PURE__ */ new Map();
1817
+ for (const pbCollection of snapshotArray) {
1818
+ if (!pbCollection.name) {
1819
+ console.warn("Skipping collection without name");
1820
+ continue;
1821
+ }
1822
+ const schema = convertPocketBaseCollection(pbCollection);
1823
+ collections.set(pbCollection.name, schema);
1824
+ }
1825
+ return {
1826
+ version: SNAPSHOT_VERSION,
1827
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1828
+ collections
1829
+ };
1830
+ } catch (error) {
1831
+ throw new SnapshotError(
1832
+ `Failed to convert PocketBase migration: ${error instanceof Error ? error.message : String(error)}`,
1833
+ void 0,
1834
+ "parse",
1835
+ error instanceof Error ? error : void 0
1836
+ );
1837
+ }
1838
+ }
1839
+
1840
+ // src/migration/migration-parser.ts
1841
+ function extractTimestampFromFilename(filename) {
1842
+ const match = filename.match(/^(\d+)_/);
1843
+ if (match) {
1844
+ return parseInt(match[1], 10);
1845
+ }
1846
+ return null;
1847
+ }
1848
+ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
1849
+ try {
1850
+ if (!fs3__namespace.existsSync(migrationsPath)) {
1851
+ return [];
1852
+ }
1853
+ const files = fs3__namespace.readdirSync(migrationsPath);
1854
+ const migrationFiles = [];
1855
+ for (const file of files) {
1856
+ if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
1857
+ continue;
1858
+ }
1859
+ if (!file.endsWith(".js")) {
1860
+ continue;
1861
+ }
1862
+ const timestamp = extractTimestampFromFilename(file);
1863
+ if (timestamp && timestamp > snapshotTimestamp) {
1864
+ migrationFiles.push({
1865
+ path: path__namespace.join(migrationsPath, file),
1866
+ timestamp
1867
+ });
1868
+ }
1869
+ }
1870
+ migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
1871
+ return migrationFiles.map((f) => f.path);
1872
+ } catch (error) {
1873
+ console.warn(`Error finding migrations after snapshot: ${error}`);
1874
+ return [];
1875
+ }
1876
+ }
1877
+ function parseMigrationOperationsFromContent(content) {
1878
+ const collectionsToCreate = [];
1879
+ const collectionsToDelete = [];
1880
+ try {
1881
+ let searchIndex = 0;
1882
+ while (true) {
1883
+ const collectionStart = content.indexOf("new Collection(", searchIndex);
1884
+ if (collectionStart === -1) {
1885
+ break;
1886
+ }
1887
+ const openParen = collectionStart + "new Collection(".length;
1888
+ let braceCount = 0;
1889
+ let parenCount = 1;
1890
+ let inString = false;
1891
+ let stringChar = null;
1892
+ let i = openParen;
1893
+ while (i < content.length && /\s/.test(content[i])) {
1894
+ i++;
1895
+ }
1896
+ if (content[i] !== "{") {
1897
+ searchIndex = i + 1;
1898
+ continue;
1899
+ }
1900
+ const objectStart = i;
1901
+ braceCount = 1;
1902
+ i++;
1903
+ while (i < content.length && (braceCount > 0 || parenCount > 0)) {
1904
+ const char = content[i];
1905
+ const prevChar = i > 0 ? content[i - 1] : "";
1906
+ if (!inString && (char === '"' || char === "'")) {
1907
+ inString = true;
1908
+ stringChar = char;
1909
+ } else if (inString && char === stringChar && prevChar !== "\\") {
1910
+ inString = false;
1911
+ stringChar = null;
1912
+ }
1913
+ if (!inString) {
1914
+ if (char === "{") braceCount++;
1915
+ if (char === "}") braceCount--;
1916
+ if (char === "(") parenCount++;
1917
+ if (char === ")") parenCount--;
1918
+ }
1919
+ i++;
1920
+ }
1921
+ if (braceCount === 0 && parenCount === 0) {
1922
+ const objectContent = content.substring(objectStart, i - 1);
1923
+ try {
1924
+ const collectionObj = new Function(`return ${objectContent}`)();
1925
+ if (collectionObj && collectionObj.name) {
1926
+ const schema = convertPocketBaseCollection(collectionObj);
1927
+ collectionsToCreate.push(schema);
1928
+ }
1929
+ } catch (error) {
1930
+ console.warn(`Failed to parse collection definition: ${error}`);
1931
+ }
1932
+ }
1933
+ searchIndex = i;
1934
+ }
1935
+ const deleteMatches = content.matchAll(
1936
+ /app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
1937
+ );
1938
+ for (const match of deleteMatches) {
1939
+ if (match[1]) {
1940
+ collectionsToDelete.push(match[1]);
1941
+ } else {
1942
+ const varNameMatch = match[0].match(/collection_(\w+)/);
1943
+ if (varNameMatch) {
1944
+ const varName = `collection_${varNameMatch[1]}`;
1945
+ const deleteIndex = content.indexOf(match[0]);
1946
+ const beforeDelete = content.substring(0, deleteIndex);
1947
+ const varDefMatch = beforeDelete.match(
1948
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
1949
+ );
1950
+ if (varDefMatch && varDefMatch.length > 0) {
1951
+ const collectionDefMatch = beforeDelete.match(
1952
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
1953
+ );
1954
+ if (collectionDefMatch) {
1955
+ try {
1956
+ const collectionDefStr = collectionDefMatch[1];
1957
+ const collectionObj = new Function(`return ${collectionDefStr}`)();
1958
+ if (collectionObj && collectionObj.name) {
1959
+ collectionsToDelete.push(collectionObj.name);
1960
+ }
1961
+ } catch {
1962
+ }
1963
+ }
1964
+ }
1965
+ }
1966
+ }
1967
+ }
1968
+ const findAndDeleteMatches = content.matchAll(
1969
+ /app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
1970
+ );
1971
+ for (const match of findAndDeleteMatches) {
1972
+ collectionsToDelete.push(match[1]);
1973
+ }
1974
+ } catch (error) {
1975
+ console.warn(`Failed to parse migration operations from content: ${error}`);
1976
+ }
1977
+ return { collectionsToCreate, collectionsToDelete };
1978
+ }
1979
+ function parseMigrationOperations(migrationContent) {
1980
+ try {
1981
+ const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
1982
+ if (!migrateMatch) {
1983
+ return parseMigrationOperationsFromContent(migrationContent);
1984
+ }
1985
+ const startIndex = migrateMatch.index + migrateMatch[0].length;
1986
+ let i = startIndex;
1987
+ let parenCount = 0;
1988
+ let foundFirstParen = false;
1989
+ while (i < migrationContent.length) {
1990
+ const char = migrationContent[i];
1991
+ if (char === "(") {
1992
+ parenCount++;
1993
+ foundFirstParen = true;
1994
+ i++;
1995
+ break;
1996
+ }
1997
+ i++;
1998
+ }
1999
+ if (!foundFirstParen) {
2000
+ return parseMigrationOperationsFromContent(migrationContent);
2001
+ }
2002
+ let inString = false;
2003
+ let stringChar = null;
2004
+ let foundBrace = false;
2005
+ let braceStart = -1;
2006
+ while (i < migrationContent.length && !foundBrace) {
2007
+ const char = migrationContent[i];
2008
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2009
+ if (!inString && (char === '"' || char === "'")) {
2010
+ inString = true;
2011
+ stringChar = char;
2012
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2013
+ inString = false;
2014
+ stringChar = null;
2015
+ }
2016
+ if (!inString) {
2017
+ if (char === "(") parenCount++;
2018
+ if (char === ")") {
2019
+ parenCount--;
2020
+ if (parenCount === 0) {
2021
+ i++;
2022
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2023
+ i++;
2024
+ }
2025
+ if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
2026
+ i += 2;
2027
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2028
+ i++;
2029
+ }
2030
+ if (i < migrationContent.length && migrationContent[i] === "{") {
2031
+ foundBrace = true;
2032
+ braceStart = i + 1;
2033
+ break;
2034
+ }
2035
+ }
2036
+ }
2037
+ }
2038
+ }
2039
+ i++;
2040
+ }
2041
+ if (!foundBrace || braceStart === -1) {
2042
+ return parseMigrationOperationsFromContent(migrationContent);
2043
+ }
2044
+ let braceCount = 1;
2045
+ i = braceStart;
2046
+ inString = false;
2047
+ stringChar = null;
2048
+ while (i < migrationContent.length && braceCount > 0) {
2049
+ const char = migrationContent[i];
2050
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2051
+ if (!inString && (char === '"' || char === "'")) {
2052
+ inString = true;
2053
+ stringChar = char;
2054
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2055
+ inString = false;
2056
+ stringChar = null;
2057
+ }
2058
+ if (!inString) {
2059
+ if (char === "{") braceCount++;
2060
+ if (char === "}") braceCount--;
2061
+ }
2062
+ i++;
2063
+ }
2064
+ if (braceCount === 0) {
2065
+ const upMigrationContent = migrationContent.substring(braceStart, i - 1);
2066
+ return parseMigrationOperationsFromContent(upMigrationContent);
2067
+ }
2068
+ return parseMigrationOperationsFromContent(migrationContent);
2069
+ } catch (error) {
2070
+ console.warn(`Failed to parse migration operations: ${error}`);
2071
+ return { collectionsToCreate: [], collectionsToDelete: [] };
2072
+ }
2073
+ }
2074
+
2075
+ // src/migration/snapshot.ts
2076
+ var SNAPSHOT_VERSION2 = "1.0.0";
1688
2077
  var DEFAULT_SNAPSHOT_FILENAME = ".migration-snapshot.json";
1689
2078
  var SNAPSHOT_MIGRATIONS = [
1690
2079
  // Add migrations here as the format evolves
@@ -1699,7 +2088,7 @@ var DEFAULT_CONFIG2 = {
1699
2088
  snapshotPath: DEFAULT_SNAPSHOT_FILENAME,
1700
2089
  workspaceRoot: process.cwd(),
1701
2090
  autoMigrate: true,
1702
- version: SNAPSHOT_VERSION
2091
+ version: SNAPSHOT_VERSION2
1703
2092
  };
1704
2093
  function mergeConfig2(config = {}) {
1705
2094
  return {
@@ -1719,7 +2108,7 @@ function getSnapshotPath(config = {}) {
1719
2108
  function snapshotExists(config = {}) {
1720
2109
  try {
1721
2110
  const snapshotPath = getSnapshotPath(config);
1722
- return fs2__namespace.existsSync(snapshotPath);
2111
+ return fs3__namespace.existsSync(snapshotPath);
1723
2112
  } catch {
1724
2113
  return false;
1725
2114
  }
@@ -1779,12 +2168,12 @@ function saveSnapshot(schema, config = {}) {
1779
2168
  const snapshotPath = getSnapshotPath(config);
1780
2169
  try {
1781
2170
  const snapshotDir = path__namespace.dirname(snapshotPath);
1782
- if (!fs2__namespace.existsSync(snapshotDir)) {
1783
- fs2__namespace.mkdirSync(snapshotDir, { recursive: true });
2171
+ if (!fs3__namespace.existsSync(snapshotDir)) {
2172
+ fs3__namespace.mkdirSync(snapshotDir, { recursive: true });
1784
2173
  }
1785
2174
  const snapshotData = addSnapshotMetadata(schema, config);
1786
2175
  const jsonContent = JSON.stringify(snapshotData, null, 2);
1787
- fs2__namespace.writeFileSync(snapshotPath, jsonContent, "utf-8");
2176
+ fs3__namespace.writeFileSync(snapshotPath, jsonContent, "utf-8");
1788
2177
  } catch (error) {
1789
2178
  handleFileSystemError(error, "write", snapshotPath);
1790
2179
  }
@@ -1879,7 +2268,7 @@ function deserializeSnapshot(data) {
1879
2268
  function loadSnapshot(config = {}) {
1880
2269
  const snapshotPath = getSnapshotPath(config);
1881
2270
  try {
1882
- const jsonContent = fs2__namespace.readFileSync(snapshotPath, "utf-8");
2271
+ const jsonContent = fs3__namespace.readFileSync(snapshotPath, "utf-8");
1883
2272
  const data = parseAndValidateSnapshot(jsonContent, snapshotPath);
1884
2273
  const migratedData = migrateSnapshotFormat(data, config);
1885
2274
  return deserializeSnapshot(migratedData);
@@ -1914,10 +2303,10 @@ function mergeSnapshots(baseSnapshot, customSnapshot) {
1914
2303
  }
1915
2304
  function findLatestSnapshot(migrationsPath) {
1916
2305
  try {
1917
- if (!fs2__namespace.existsSync(migrationsPath)) {
2306
+ if (!fs3__namespace.existsSync(migrationsPath)) {
1918
2307
  return null;
1919
2308
  }
1920
- const files = fs2__namespace.readdirSync(migrationsPath);
2309
+ const files = fs3__namespace.readdirSync(migrationsPath);
1921
2310
  const snapshotFiles = files.filter(
1922
2311
  (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
1923
2312
  );
@@ -1935,14 +2324,68 @@ function findLatestSnapshot(migrationsPath) {
1935
2324
  return null;
1936
2325
  }
1937
2326
  }
2327
+ function applyMigrationOperations(snapshot, operations) {
2328
+ const updatedCollections = new Map(snapshot.collections);
2329
+ for (const collectionName of operations.collectionsToDelete) {
2330
+ updatedCollections.delete(collectionName);
2331
+ }
2332
+ for (const collection of operations.collectionsToCreate) {
2333
+ updatedCollections.set(collection.name, collection);
2334
+ }
2335
+ return {
2336
+ ...snapshot,
2337
+ collections: updatedCollections
2338
+ };
2339
+ }
2340
+ function loadSnapshotWithMigrations(config = {}) {
2341
+ const migrationsPath = config.migrationsPath;
2342
+ if (!migrationsPath) {
2343
+ return null;
2344
+ }
2345
+ if (fs3__namespace.existsSync(migrationsPath) && fs3__namespace.statSync(migrationsPath).isFile()) {
2346
+ try {
2347
+ const migrationContent = fs3__namespace.readFileSync(migrationsPath, "utf-8");
2348
+ return convertPocketBaseMigration(migrationContent);
2349
+ } catch (error) {
2350
+ console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2351
+ return null;
2352
+ }
2353
+ }
2354
+ const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2355
+ if (!latestSnapshotPath) {
2356
+ return null;
2357
+ }
2358
+ try {
2359
+ const migrationContent = fs3__namespace.readFileSync(latestSnapshotPath, "utf-8");
2360
+ let snapshot = convertPocketBaseMigration(migrationContent);
2361
+ const snapshotFilename = path__namespace.basename(latestSnapshotPath);
2362
+ const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
2363
+ if (snapshotTimestamp) {
2364
+ const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
2365
+ for (const migrationFile of migrationFiles) {
2366
+ try {
2367
+ const migrationContent2 = fs3__namespace.readFileSync(migrationFile, "utf-8");
2368
+ const operations = parseMigrationOperations(migrationContent2);
2369
+ snapshot = applyMigrationOperations(snapshot, operations);
2370
+ } catch (error) {
2371
+ console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
2372
+ }
2373
+ }
2374
+ }
2375
+ return snapshot;
2376
+ } catch (error) {
2377
+ console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2378
+ return null;
2379
+ }
2380
+ }
1938
2381
  function loadSnapshotIfExists(config = {}) {
1939
2382
  const migrationsPath = config.migrationsPath;
1940
2383
  if (!migrationsPath) {
1941
2384
  return null;
1942
2385
  }
1943
- if (fs2__namespace.existsSync(migrationsPath) && fs2__namespace.statSync(migrationsPath).isFile()) {
2386
+ if (fs3__namespace.existsSync(migrationsPath) && fs3__namespace.statSync(migrationsPath).isFile()) {
1944
2387
  try {
1945
- const migrationContent = fs2__namespace.readFileSync(migrationsPath, "utf-8");
2388
+ const migrationContent = fs3__namespace.readFileSync(migrationsPath, "utf-8");
1946
2389
  return convertPocketBaseMigration(migrationContent);
1947
2390
  } catch (error) {
1948
2391
  console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
@@ -1952,7 +2395,7 @@ function loadSnapshotIfExists(config = {}) {
1952
2395
  const latestSnapshotPath = findLatestSnapshot(migrationsPath);
1953
2396
  if (latestSnapshotPath) {
1954
2397
  try {
1955
- const migrationContent = fs2__namespace.readFileSync(latestSnapshotPath, "utf-8");
2398
+ const migrationContent = fs3__namespace.readFileSync(latestSnapshotPath, "utf-8");
1956
2399
  return convertPocketBaseMigration(migrationContent);
1957
2400
  } catch (error) {
1958
2401
  console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
@@ -1961,100 +2404,9 @@ function loadSnapshotIfExists(config = {}) {
1961
2404
  }
1962
2405
  return null;
1963
2406
  }
1964
- function convertPocketBaseCollection(pbCollection) {
1965
- const fields = [];
1966
- const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
1967
- const authSystemFieldNames = ["email", "emailVisibility", "verified", "password", "tokenKey"];
1968
- if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
1969
- for (const pbField of pbCollection.fields) {
1970
- if (pbField.system || systemFieldNames.includes(pbField.name)) {
1971
- continue;
1972
- }
1973
- if (pbCollection.type === "auth" && authSystemFieldNames.includes(pbField.name)) {
1974
- continue;
1975
- }
1976
- const field = {
1977
- name: pbField.name,
1978
- type: pbField.type,
1979
- required: pbField.required || false
1980
- };
1981
- if (pbField.options) {
1982
- field.options = pbField.options;
1983
- }
1984
- if (pbField.type === "relation") {
1985
- field.relation = {
1986
- collection: pbField.options?.collectionId || "",
1987
- cascadeDelete: pbField.options?.cascadeDelete || false,
1988
- maxSelect: pbField.options?.maxSelect,
1989
- minSelect: pbField.options?.minSelect
1990
- };
1991
- }
1992
- fields.push(field);
1993
- }
1994
- }
1995
- const schema = {
1996
- name: pbCollection.name,
1997
- type: pbCollection.type || "base",
1998
- fields
1999
- };
2000
- if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2001
- schema.indexes = pbCollection.indexes;
2002
- }
2003
- const rules = {};
2004
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2005
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2006
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2007
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2008
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2009
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2010
- if (Object.keys(rules).length > 0) {
2011
- schema.rules = rules;
2012
- schema.permissions = { ...rules };
2013
- }
2014
- return schema;
2015
- }
2016
- function convertPocketBaseMigration(migrationContent) {
2017
- try {
2018
- const snapshotMatch = migrationContent.match(/const\s+snapshot\s*=\s*(\[[\s\S]*?\]);/);
2019
- if (!snapshotMatch) {
2020
- throw new Error("Could not find snapshot array in migration file");
2021
- }
2022
- const snapshotArrayStr = snapshotMatch[1];
2023
- let snapshotArray;
2024
- try {
2025
- snapshotArray = new Function(`return ${snapshotArrayStr}`)();
2026
- } catch (parseError) {
2027
- throw new Error(`Failed to parse snapshot array: ${parseError}`);
2028
- }
2029
- if (!Array.isArray(snapshotArray)) {
2030
- throw new Error("Snapshot is not an array");
2031
- }
2032
- const collections = /* @__PURE__ */ new Map();
2033
- for (const pbCollection of snapshotArray) {
2034
- if (!pbCollection.name) {
2035
- console.warn("Skipping collection without name");
2036
- continue;
2037
- }
2038
- const schema = convertPocketBaseCollection(pbCollection);
2039
- collections.set(pbCollection.name, schema);
2040
- }
2041
- return {
2042
- version: SNAPSHOT_VERSION,
2043
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2044
- collections
2045
- };
2046
- } catch (error) {
2047
- throw new SnapshotError(
2048
- `Failed to convert PocketBase migration: ${error instanceof Error ? error.message : String(error)}`,
2049
- void 0,
2050
- "parse",
2051
- error instanceof Error ? error : void 0
2052
- );
2053
- }
2054
- }
2055
2407
  function loadBaseMigration(migrationPath) {
2056
2408
  try {
2057
- if (!fs2__namespace.existsSync(migrationPath)) {
2409
+ if (!fs3__namespace.existsSync(migrationPath)) {
2058
2410
  throw new SnapshotError(
2059
2411
  `Base migration file not found: ${migrationPath}
2060
2412
 
@@ -2065,7 +2417,7 @@ If the file exists in a different location, update the configuration.`,
2065
2417
  "read"
2066
2418
  );
2067
2419
  }
2068
- const migrationContent = fs2__namespace.readFileSync(migrationPath, "utf-8");
2420
+ const migrationContent = fs3__namespace.readFileSync(migrationPath, "utf-8");
2069
2421
  const snapshot = convertPocketBaseMigration(migrationContent);
2070
2422
  return snapshot;
2071
2423
  } catch (error) {
@@ -2101,14 +2453,14 @@ Please ensure PocketBase is properly set up by running 'yarn setup'.`,
2101
2453
  }
2102
2454
  }
2103
2455
  function getSnapshotVersion() {
2104
- return SNAPSHOT_VERSION;
2456
+ return SNAPSHOT_VERSION2;
2105
2457
  }
2106
2458
  function validateSnapshot(snapshot) {
2107
2459
  const issues = [];
2108
2460
  if (!snapshot.version) {
2109
2461
  issues.push("Missing version field");
2110
- } else if (compareVersions(snapshot.version, SNAPSHOT_VERSION) > 0) {
2111
- issues.push(`Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION}`);
2462
+ } else if (compareVersions(snapshot.version, SNAPSHOT_VERSION2) > 0) {
2463
+ issues.push(`Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION2}`);
2112
2464
  }
2113
2465
  if (!snapshot.timestamp) {
2114
2466
  issues.push("Missing timestamp field");
@@ -2327,6 +2679,9 @@ function compareFieldOptions(currentField, previousField) {
2327
2679
  for (const key of allKeys) {
2328
2680
  const currentValue = currentOptions[key];
2329
2681
  const previousValue = previousOptions[key];
2682
+ if (currentValue === void 0 && previousValue === void 0) {
2683
+ continue;
2684
+ }
2330
2685
  if (!areValuesEqual(currentValue, previousValue)) {
2331
2686
  changes.push({
2332
2687
  property: `options.${key}`,
@@ -2347,11 +2702,26 @@ function compareRelationConfigurations(currentField, previousField) {
2347
2702
  if (!currentRelation || !previousRelation) {
2348
2703
  return changes;
2349
2704
  }
2350
- if (currentRelation.collection !== previousRelation.collection) {
2705
+ const normalizeCollection = (collection) => {
2706
+ if (!collection) return collection;
2707
+ if (collection === "_pb_users_auth_") {
2708
+ return "Users";
2709
+ }
2710
+ const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2711
+ if (nameMatch) {
2712
+ return nameMatch[1];
2713
+ }
2714
+ return collection;
2715
+ };
2716
+ const normalizedCurrent = normalizeCollection(currentRelation.collection);
2717
+ const normalizedPrevious = normalizeCollection(previousRelation.collection);
2718
+ if (normalizedCurrent !== normalizedPrevious) {
2351
2719
  changes.push({
2352
2720
  property: "relation.collection",
2353
- oldValue: previousRelation.collection,
2354
- newValue: currentRelation.collection
2721
+ oldValue: normalizedPrevious,
2722
+ // Use normalized value for clarity
2723
+ newValue: normalizedCurrent
2724
+ // Use normalized value for clarity
2355
2725
  });
2356
2726
  }
2357
2727
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -2785,9 +3155,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
2785
3155
  }
2786
3156
  function writeMigrationFile(migrationDir, filename, content) {
2787
3157
  try {
2788
- if (!fs2__namespace.existsSync(migrationDir)) {
3158
+ if (!fs3__namespace.existsSync(migrationDir)) {
2789
3159
  try {
2790
- fs2__namespace.mkdirSync(migrationDir, { recursive: true });
3160
+ fs3__namespace.mkdirSync(migrationDir, { recursive: true });
2791
3161
  } catch (error) {
2792
3162
  const fsError = error;
2793
3163
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
@@ -2809,7 +3179,7 @@ function writeMigrationFile(migrationDir, filename, content) {
2809
3179
  }
2810
3180
  }
2811
3181
  const filePath = path__namespace.join(migrationDir, filename);
2812
- fs2__namespace.writeFileSync(filePath, content, "utf-8");
3182
+ fs3__namespace.writeFileSync(filePath, content, "utf-8");
2813
3183
  return filePath;
2814
3184
  } catch (error) {
2815
3185
  if (error instanceof FileSystemError) {
@@ -3472,6 +3842,7 @@ exports.isSystemCollection = isSystemCollection;
3472
3842
  exports.loadBaseMigration = loadBaseMigration;
3473
3843
  exports.loadSnapshot = loadSnapshot;
3474
3844
  exports.loadSnapshotIfExists = loadSnapshotIfExists;
3845
+ exports.loadSnapshotWithMigrations = loadSnapshotWithMigrations;
3475
3846
  exports.mapZodArrayType = mapZodArrayType;
3476
3847
  exports.mapZodBooleanType = mapZodBooleanType;
3477
3848
  exports.mapZodDateType = mapZodDateType;