pocketbase-zod-schema 0.1.3 → 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 (46) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cli/index.cjs +406 -102
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.js +404 -100
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/cli/migrate.cjs +409 -105
  7. package/dist/cli/migrate.cjs.map +1 -1
  8. package/dist/cli/migrate.js +404 -100
  9. package/dist/cli/migrate.js.map +1 -1
  10. package/dist/index.cjs +515 -159
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +3 -3
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +511 -158
  15. package/dist/index.js.map +1 -1
  16. package/dist/migration/diff.cjs +21 -3
  17. package/dist/migration/diff.cjs.map +1 -1
  18. package/dist/migration/diff.js +21 -3
  19. package/dist/migration/diff.js.map +1 -1
  20. package/dist/migration/index.cjs +457 -123
  21. package/dist/migration/index.cjs.map +1 -1
  22. package/dist/migration/index.d.cts +1 -1
  23. package/dist/migration/index.d.ts +1 -1
  24. package/dist/migration/index.js +456 -123
  25. package/dist/migration/index.js.map +1 -1
  26. package/dist/migration/snapshot.cjs +432 -118
  27. package/dist/migration/snapshot.cjs.map +1 -1
  28. package/dist/migration/snapshot.d.cts +34 -12
  29. package/dist/migration/snapshot.d.ts +34 -12
  30. package/dist/migration/snapshot.js +430 -117
  31. package/dist/migration/snapshot.js.map +1 -1
  32. package/dist/mutator.d.cts +3 -3
  33. package/dist/mutator.d.ts +3 -3
  34. package/dist/schema.cjs +34 -0
  35. package/dist/schema.cjs.map +1 -1
  36. package/dist/schema.d.cts +1 -1
  37. package/dist/schema.d.ts +1 -1
  38. package/dist/schema.js +33 -1
  39. package/dist/schema.js.map +1 -1
  40. package/dist/types.d.cts +5 -2
  41. package/dist/types.d.ts +5 -2
  42. package/dist/user-_AM523hb.d.cts +123 -0
  43. package/dist/user-_AM523hb.d.ts +123 -0
  44. package/package.json +2 -3
  45. package/dist/user-C39DQ40N.d.cts +0 -53
  46. package/dist/user-C39DQ40N.d.ts +0 -53
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import chalk from 'chalk';
3
3
  import { Command } from 'commander';
4
- import * as fs4 from 'fs';
4
+ import * as fs5 from 'fs';
5
5
  import { readFileSync } from 'fs';
6
- import * as path4 from 'path';
6
+ import * as path5 from 'path';
7
7
  import { dirname, join } from 'path';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { z } from 'zod';
@@ -136,10 +136,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
136
136
  operation;
137
137
  code;
138
138
  originalError;
139
- constructor(message, path6, operation, code, originalError) {
139
+ constructor(message, path7, operation, code, originalError) {
140
140
  super(message);
141
141
  this.name = "FileSystemError";
142
- this.path = path6;
142
+ this.path = path7;
143
143
  this.operation = operation;
144
144
  this.code = code;
145
145
  this.originalError = originalError;
@@ -1080,20 +1080,20 @@ function mergeConfig(config) {
1080
1080
  }
1081
1081
  function resolveSchemaDir(config) {
1082
1082
  const workspaceRoot = config.workspaceRoot || process.cwd();
1083
- if (path4.isAbsolute(config.schemaDir)) {
1083
+ if (path5.isAbsolute(config.schemaDir)) {
1084
1084
  return config.schemaDir;
1085
1085
  }
1086
- return path4.join(workspaceRoot, config.schemaDir);
1086
+ return path5.join(workspaceRoot, config.schemaDir);
1087
1087
  }
1088
1088
  function discoverSchemaFiles(config) {
1089
1089
  const normalizedConfig = typeof config === "string" ? { schemaDir: config } : config;
1090
1090
  const mergedConfig = mergeConfig(normalizedConfig);
1091
1091
  const schemaDir = resolveSchemaDir(normalizedConfig);
1092
1092
  try {
1093
- if (!fs4.existsSync(schemaDir)) {
1093
+ if (!fs5.existsSync(schemaDir)) {
1094
1094
  throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
1095
1095
  }
1096
- const files = fs4.readdirSync(schemaDir);
1096
+ const files = fs5.readdirSync(schemaDir);
1097
1097
  const schemaFiles = files.filter((file) => {
1098
1098
  const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
1099
1099
  if (!hasValidExtension) return false;
@@ -1109,7 +1109,7 @@ function discoverSchemaFiles(config) {
1109
1109
  });
1110
1110
  return schemaFiles.map((file) => {
1111
1111
  const ext = mergedConfig.includeExtensions.find((ext2) => file.endsWith(ext2)) || ".ts";
1112
- return path4.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
1112
+ return path5.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
1113
1113
  });
1114
1114
  } catch (error) {
1115
1115
  if (error instanceof FileSystemError) {
@@ -1143,19 +1143,19 @@ async function importSchemaModule(filePath, config) {
1143
1143
  let resolvedPath = null;
1144
1144
  const jsPath = `${importPath}.js`;
1145
1145
  const tsPath = `${importPath}.ts`;
1146
- if (fs4.existsSync(jsPath)) {
1146
+ if (fs5.existsSync(jsPath)) {
1147
1147
  resolvedPath = jsPath;
1148
- } else if (fs4.existsSync(tsPath)) {
1148
+ } else if (fs5.existsSync(tsPath)) {
1149
1149
  resolvedPath = tsPath;
1150
1150
  } else {
1151
1151
  resolvedPath = jsPath;
1152
1152
  }
1153
- const fileUrl = new URL(`file://${path4.resolve(resolvedPath)}`);
1153
+ const fileUrl = new URL(`file://${path5.resolve(resolvedPath)}`);
1154
1154
  const module = await import(fileUrl.href);
1155
1155
  return module;
1156
1156
  } catch (error) {
1157
1157
  const tsPath = `${filePath}.ts`;
1158
- const isTypeScriptFile = fs4.existsSync(tsPath);
1158
+ const isTypeScriptFile = fs5.existsSync(tsPath);
1159
1159
  if (isTypeScriptFile) {
1160
1160
  throw new SchemaParsingError(
1161
1161
  `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
@@ -1174,7 +1174,7 @@ Please either:
1174
1174
  }
1175
1175
  }
1176
1176
  function getCollectionNameFromFile(filePath) {
1177
- const filename = path4.basename(filePath).replace(/\.(ts|js)$/, "");
1177
+ const filename = path5.basename(filePath).replace(/\.(ts|js)$/, "");
1178
1178
  return toCollectionName(filename);
1179
1179
  }
1180
1180
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema"]) {
@@ -1339,7 +1339,7 @@ async function buildSchemaDefinition(config) {
1339
1339
  importPath = normalizedConfig.pathTransformer(filePath);
1340
1340
  } else if (mergedConfig.useCompiledFiles) {
1341
1341
  const distPath = filePath.replace(/\/src\//, "/dist/");
1342
- if (fs4.existsSync(`${distPath}.js`) || fs4.existsSync(`${distPath}.mjs`)) {
1342
+ if (fs5.existsSync(`${distPath}.js`) || fs5.existsSync(`${distPath}.mjs`)) {
1343
1343
  importPath = distPath;
1344
1344
  } else {
1345
1345
  importPath = filePath;
@@ -1516,6 +1516,9 @@ function compareFieldOptions(currentField, previousField) {
1516
1516
  for (const key of allKeys) {
1517
1517
  const currentValue = currentOptions[key];
1518
1518
  const previousValue = previousOptions[key];
1519
+ if (currentValue === void 0 && previousValue === void 0) {
1520
+ continue;
1521
+ }
1519
1522
  if (!areValuesEqual(currentValue, previousValue)) {
1520
1523
  changes.push({
1521
1524
  property: `options.${key}`,
@@ -1536,11 +1539,26 @@ function compareRelationConfigurations(currentField, previousField) {
1536
1539
  if (!currentRelation || !previousRelation) {
1537
1540
  return changes;
1538
1541
  }
1539
- if (currentRelation.collection !== previousRelation.collection) {
1542
+ const normalizeCollection = (collection) => {
1543
+ if (!collection) return collection;
1544
+ if (collection === "_pb_users_auth_") {
1545
+ return "Users";
1546
+ }
1547
+ const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1548
+ if (nameMatch) {
1549
+ return nameMatch[1];
1550
+ }
1551
+ return collection;
1552
+ };
1553
+ const normalizedCurrent = normalizeCollection(currentRelation.collection);
1554
+ const normalizedPrevious = normalizeCollection(previousRelation.collection);
1555
+ if (normalizedCurrent !== normalizedPrevious) {
1540
1556
  changes.push({
1541
1557
  property: "relation.collection",
1542
- oldValue: previousRelation.collection,
1543
- newValue: currentRelation.collection
1558
+ oldValue: normalizedPrevious,
1559
+ // Use normalized value for clarity
1560
+ newValue: normalizedCurrent
1561
+ // Use normalized value for clarity
1544
1562
  });
1545
1563
  }
1546
1564
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -1760,10 +1778,10 @@ function mergeConfig3(config) {
1760
1778
  }
1761
1779
  function resolveMigrationDir(config) {
1762
1780
  const workspaceRoot = config.workspaceRoot || process.cwd();
1763
- if (path4.isAbsolute(config.migrationDir)) {
1781
+ if (path5.isAbsolute(config.migrationDir)) {
1764
1782
  return config.migrationDir;
1765
1783
  }
1766
- return path4.join(workspaceRoot, config.migrationDir);
1784
+ return path5.join(workspaceRoot, config.migrationDir);
1767
1785
  }
1768
1786
  function generateTimestamp(config) {
1769
1787
  if (config?.timestampGenerator) {
@@ -1821,9 +1839,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
1821
1839
  }
1822
1840
  function writeMigrationFile(migrationDir, filename, content) {
1823
1841
  try {
1824
- if (!fs4.existsSync(migrationDir)) {
1842
+ if (!fs5.existsSync(migrationDir)) {
1825
1843
  try {
1826
- fs4.mkdirSync(migrationDir, { recursive: true });
1844
+ fs5.mkdirSync(migrationDir, { recursive: true });
1827
1845
  } catch (error) {
1828
1846
  const fsError = error;
1829
1847
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
@@ -1844,15 +1862,15 @@ function writeMigrationFile(migrationDir, filename, content) {
1844
1862
  );
1845
1863
  }
1846
1864
  }
1847
- const filePath = path4.join(migrationDir, filename);
1848
- fs4.writeFileSync(filePath, content, "utf-8");
1865
+ const filePath = path5.join(migrationDir, filename);
1866
+ fs5.writeFileSync(filePath, content, "utf-8");
1849
1867
  return filePath;
1850
1868
  } catch (error) {
1851
1869
  if (error instanceof FileSystemError) {
1852
1870
  throw error;
1853
1871
  }
1854
1872
  const fsError = error;
1855
- const filePath = path4.join(migrationDir, filename);
1873
+ const filePath = path5.join(migrationDir, filename);
1856
1874
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
1857
1875
  throw new FileSystemError(
1858
1876
  `Permission denied writing migration file. Check file and directory permissions.`,
@@ -2397,57 +2415,18 @@ function generate(diff, config) {
2397
2415
  );
2398
2416
  }
2399
2417
  }
2418
+
2419
+ // src/migration/pocketbase-converter.ts
2400
2420
  var SNAPSHOT_VERSION = "1.0.0";
2401
- ({
2402
- workspaceRoot: process.cwd()});
2403
- function findLatestSnapshot(migrationsPath) {
2404
- try {
2405
- if (!fs4.existsSync(migrationsPath)) {
2406
- return null;
2407
- }
2408
- const files = fs4.readdirSync(migrationsPath);
2409
- const snapshotFiles = files.filter(
2410
- (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2411
- );
2412
- if (snapshotFiles.length === 0) {
2413
- return null;
2414
- }
2415
- snapshotFiles.sort().reverse();
2416
- const latestSnapshot = snapshotFiles[0];
2417
- if (!latestSnapshot) {
2418
- return null;
2419
- }
2420
- return path4.join(migrationsPath, latestSnapshot);
2421
- } catch (error) {
2422
- console.warn(`Error finding latest snapshot: ${error}`);
2423
- return null;
2421
+ function resolveCollectionIdToName(collectionId) {
2422
+ if (collectionId === "_pb_users_auth_") {
2423
+ return "Users";
2424
2424
  }
2425
- }
2426
- function loadSnapshotIfExists(config = {}) {
2427
- const migrationsPath = config.migrationsPath;
2428
- if (!migrationsPath) {
2429
- return null;
2425
+ const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2426
+ if (nameMatch) {
2427
+ return nameMatch[1];
2430
2428
  }
2431
- if (fs4.existsSync(migrationsPath) && fs4.statSync(migrationsPath).isFile()) {
2432
- try {
2433
- const migrationContent = fs4.readFileSync(migrationsPath, "utf-8");
2434
- return convertPocketBaseMigration(migrationContent);
2435
- } catch (error) {
2436
- console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2437
- return null;
2438
- }
2439
- }
2440
- const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2441
- if (latestSnapshotPath) {
2442
- try {
2443
- const migrationContent = fs4.readFileSync(latestSnapshotPath, "utf-8");
2444
- return convertPocketBaseMigration(migrationContent);
2445
- } catch (error) {
2446
- console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2447
- return null;
2448
- }
2449
- }
2450
- return null;
2429
+ return collectionId;
2451
2430
  }
2452
2431
  function convertPocketBaseCollection(pbCollection) {
2453
2432
  const fields = [];
@@ -2466,17 +2445,28 @@ function convertPocketBaseCollection(pbCollection) {
2466
2445
  type: pbField.type,
2467
2446
  required: pbField.required || false
2468
2447
  };
2469
- if (pbField.options) {
2470
- field.options = pbField.options;
2448
+ field.options = pbField.options ? { ...pbField.options } : {};
2449
+ if (pbField.type === "select") {
2450
+ if (pbField.values && Array.isArray(pbField.values)) {
2451
+ field.options.values = pbField.values;
2452
+ } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2453
+ field.options.values = pbField.options.values;
2454
+ }
2471
2455
  }
2472
2456
  if (pbField.type === "relation") {
2457
+ const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2458
+ const collectionName = resolveCollectionIdToName(collectionId);
2473
2459
  field.relation = {
2474
- collection: pbField.options?.collectionId || "",
2475
- cascadeDelete: pbField.options?.cascadeDelete || false,
2476
- maxSelect: pbField.options?.maxSelect,
2477
- minSelect: pbField.options?.minSelect
2460
+ collection: collectionName,
2461
+ cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2462
+ maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2463
+ minSelect: pbField.minSelect ?? pbField.options?.minSelect
2478
2464
  };
2479
2465
  }
2466
+ const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2467
+ if (Object.keys(field.options).length === 0) {
2468
+ delete field.options;
2469
+ } else if (pbField.type === "select" && hasOnlyValues) ;
2480
2470
  fields.push(field);
2481
2471
  }
2482
2472
  }
@@ -2541,6 +2531,320 @@ function convertPocketBaseMigration(migrationContent) {
2541
2531
  }
2542
2532
  }
2543
2533
 
2534
+ // src/migration/migration-parser.ts
2535
+ function extractTimestampFromFilename(filename) {
2536
+ const match = filename.match(/^(\d+)_/);
2537
+ if (match) {
2538
+ return parseInt(match[1], 10);
2539
+ }
2540
+ return null;
2541
+ }
2542
+ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
2543
+ try {
2544
+ if (!fs5.existsSync(migrationsPath)) {
2545
+ return [];
2546
+ }
2547
+ const files = fs5.readdirSync(migrationsPath);
2548
+ const migrationFiles = [];
2549
+ for (const file of files) {
2550
+ if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
2551
+ continue;
2552
+ }
2553
+ if (!file.endsWith(".js")) {
2554
+ continue;
2555
+ }
2556
+ const timestamp = extractTimestampFromFilename(file);
2557
+ if (timestamp && timestamp > snapshotTimestamp) {
2558
+ migrationFiles.push({
2559
+ path: path5.join(migrationsPath, file),
2560
+ timestamp
2561
+ });
2562
+ }
2563
+ }
2564
+ migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
2565
+ return migrationFiles.map((f) => f.path);
2566
+ } catch (error) {
2567
+ console.warn(`Error finding migrations after snapshot: ${error}`);
2568
+ return [];
2569
+ }
2570
+ }
2571
+ function parseMigrationOperationsFromContent(content) {
2572
+ const collectionsToCreate = [];
2573
+ const collectionsToDelete = [];
2574
+ try {
2575
+ let searchIndex = 0;
2576
+ while (true) {
2577
+ const collectionStart = content.indexOf("new Collection(", searchIndex);
2578
+ if (collectionStart === -1) {
2579
+ break;
2580
+ }
2581
+ const openParen = collectionStart + "new Collection(".length;
2582
+ let braceCount = 0;
2583
+ let parenCount = 1;
2584
+ let inString = false;
2585
+ let stringChar = null;
2586
+ let i = openParen;
2587
+ while (i < content.length && /\s/.test(content[i])) {
2588
+ i++;
2589
+ }
2590
+ if (content[i] !== "{") {
2591
+ searchIndex = i + 1;
2592
+ continue;
2593
+ }
2594
+ const objectStart = i;
2595
+ braceCount = 1;
2596
+ i++;
2597
+ while (i < content.length && (braceCount > 0 || parenCount > 0)) {
2598
+ const char = content[i];
2599
+ const prevChar = i > 0 ? content[i - 1] : "";
2600
+ if (!inString && (char === '"' || char === "'")) {
2601
+ inString = true;
2602
+ stringChar = char;
2603
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2604
+ inString = false;
2605
+ stringChar = null;
2606
+ }
2607
+ if (!inString) {
2608
+ if (char === "{") braceCount++;
2609
+ if (char === "}") braceCount--;
2610
+ if (char === "(") parenCount++;
2611
+ if (char === ")") parenCount--;
2612
+ }
2613
+ i++;
2614
+ }
2615
+ if (braceCount === 0 && parenCount === 0) {
2616
+ const objectContent = content.substring(objectStart, i - 1);
2617
+ try {
2618
+ const collectionObj = new Function(`return ${objectContent}`)();
2619
+ if (collectionObj && collectionObj.name) {
2620
+ const schema = convertPocketBaseCollection(collectionObj);
2621
+ collectionsToCreate.push(schema);
2622
+ }
2623
+ } catch (error) {
2624
+ console.warn(`Failed to parse collection definition: ${error}`);
2625
+ }
2626
+ }
2627
+ searchIndex = i;
2628
+ }
2629
+ const deleteMatches = content.matchAll(
2630
+ /app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
2631
+ );
2632
+ for (const match of deleteMatches) {
2633
+ if (match[1]) {
2634
+ collectionsToDelete.push(match[1]);
2635
+ } else {
2636
+ const varNameMatch = match[0].match(/collection_(\w+)/);
2637
+ if (varNameMatch) {
2638
+ const varName = `collection_${varNameMatch[1]}`;
2639
+ const deleteIndex = content.indexOf(match[0]);
2640
+ const beforeDelete = content.substring(0, deleteIndex);
2641
+ const varDefMatch = beforeDelete.match(
2642
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
2643
+ );
2644
+ if (varDefMatch && varDefMatch.length > 0) {
2645
+ const collectionDefMatch = beforeDelete.match(
2646
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
2647
+ );
2648
+ if (collectionDefMatch) {
2649
+ try {
2650
+ const collectionDefStr = collectionDefMatch[1];
2651
+ const collectionObj = new Function(`return ${collectionDefStr}`)();
2652
+ if (collectionObj && collectionObj.name) {
2653
+ collectionsToDelete.push(collectionObj.name);
2654
+ }
2655
+ } catch {
2656
+ }
2657
+ }
2658
+ }
2659
+ }
2660
+ }
2661
+ }
2662
+ const findAndDeleteMatches = content.matchAll(
2663
+ /app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
2664
+ );
2665
+ for (const match of findAndDeleteMatches) {
2666
+ collectionsToDelete.push(match[1]);
2667
+ }
2668
+ } catch (error) {
2669
+ console.warn(`Failed to parse migration operations from content: ${error}`);
2670
+ }
2671
+ return { collectionsToCreate, collectionsToDelete };
2672
+ }
2673
+ function parseMigrationOperations(migrationContent) {
2674
+ try {
2675
+ const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
2676
+ if (!migrateMatch) {
2677
+ return parseMigrationOperationsFromContent(migrationContent);
2678
+ }
2679
+ const startIndex = migrateMatch.index + migrateMatch[0].length;
2680
+ let i = startIndex;
2681
+ let parenCount = 0;
2682
+ let foundFirstParen = false;
2683
+ while (i < migrationContent.length) {
2684
+ const char = migrationContent[i];
2685
+ if (char === "(") {
2686
+ parenCount++;
2687
+ foundFirstParen = true;
2688
+ i++;
2689
+ break;
2690
+ }
2691
+ i++;
2692
+ }
2693
+ if (!foundFirstParen) {
2694
+ return parseMigrationOperationsFromContent(migrationContent);
2695
+ }
2696
+ let inString = false;
2697
+ let stringChar = null;
2698
+ let foundBrace = false;
2699
+ let braceStart = -1;
2700
+ while (i < migrationContent.length && !foundBrace) {
2701
+ const char = migrationContent[i];
2702
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2703
+ if (!inString && (char === '"' || char === "'")) {
2704
+ inString = true;
2705
+ stringChar = char;
2706
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2707
+ inString = false;
2708
+ stringChar = null;
2709
+ }
2710
+ if (!inString) {
2711
+ if (char === "(") parenCount++;
2712
+ if (char === ")") {
2713
+ parenCount--;
2714
+ if (parenCount === 0) {
2715
+ i++;
2716
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2717
+ i++;
2718
+ }
2719
+ if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
2720
+ i += 2;
2721
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2722
+ i++;
2723
+ }
2724
+ if (i < migrationContent.length && migrationContent[i] === "{") {
2725
+ foundBrace = true;
2726
+ braceStart = i + 1;
2727
+ break;
2728
+ }
2729
+ }
2730
+ }
2731
+ }
2732
+ }
2733
+ i++;
2734
+ }
2735
+ if (!foundBrace || braceStart === -1) {
2736
+ return parseMigrationOperationsFromContent(migrationContent);
2737
+ }
2738
+ let braceCount = 1;
2739
+ i = braceStart;
2740
+ inString = false;
2741
+ stringChar = null;
2742
+ while (i < migrationContent.length && braceCount > 0) {
2743
+ const char = migrationContent[i];
2744
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2745
+ if (!inString && (char === '"' || char === "'")) {
2746
+ inString = true;
2747
+ stringChar = char;
2748
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2749
+ inString = false;
2750
+ stringChar = null;
2751
+ }
2752
+ if (!inString) {
2753
+ if (char === "{") braceCount++;
2754
+ if (char === "}") braceCount--;
2755
+ }
2756
+ i++;
2757
+ }
2758
+ if (braceCount === 0) {
2759
+ const upMigrationContent = migrationContent.substring(braceStart, i - 1);
2760
+ return parseMigrationOperationsFromContent(upMigrationContent);
2761
+ }
2762
+ return parseMigrationOperationsFromContent(migrationContent);
2763
+ } catch (error) {
2764
+ console.warn(`Failed to parse migration operations: ${error}`);
2765
+ return { collectionsToCreate: [], collectionsToDelete: [] };
2766
+ }
2767
+ }
2768
+ ({
2769
+ workspaceRoot: process.cwd()});
2770
+ function findLatestSnapshot(migrationsPath) {
2771
+ try {
2772
+ if (!fs5.existsSync(migrationsPath)) {
2773
+ return null;
2774
+ }
2775
+ const files = fs5.readdirSync(migrationsPath);
2776
+ const snapshotFiles = files.filter(
2777
+ (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2778
+ );
2779
+ if (snapshotFiles.length === 0) {
2780
+ return null;
2781
+ }
2782
+ snapshotFiles.sort().reverse();
2783
+ const latestSnapshot = snapshotFiles[0];
2784
+ if (!latestSnapshot) {
2785
+ return null;
2786
+ }
2787
+ return path5.join(migrationsPath, latestSnapshot);
2788
+ } catch (error) {
2789
+ console.warn(`Error finding latest snapshot: ${error}`);
2790
+ return null;
2791
+ }
2792
+ }
2793
+ function applyMigrationOperations(snapshot, operations) {
2794
+ const updatedCollections = new Map(snapshot.collections);
2795
+ for (const collectionName of operations.collectionsToDelete) {
2796
+ updatedCollections.delete(collectionName);
2797
+ }
2798
+ for (const collection of operations.collectionsToCreate) {
2799
+ updatedCollections.set(collection.name, collection);
2800
+ }
2801
+ return {
2802
+ ...snapshot,
2803
+ collections: updatedCollections
2804
+ };
2805
+ }
2806
+ function loadSnapshotWithMigrations(config = {}) {
2807
+ const migrationsPath = config.migrationsPath;
2808
+ if (!migrationsPath) {
2809
+ return null;
2810
+ }
2811
+ if (fs5.existsSync(migrationsPath) && fs5.statSync(migrationsPath).isFile()) {
2812
+ try {
2813
+ const migrationContent = fs5.readFileSync(migrationsPath, "utf-8");
2814
+ return convertPocketBaseMigration(migrationContent);
2815
+ } catch (error) {
2816
+ console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2817
+ return null;
2818
+ }
2819
+ }
2820
+ const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2821
+ if (!latestSnapshotPath) {
2822
+ return null;
2823
+ }
2824
+ try {
2825
+ const migrationContent = fs5.readFileSync(latestSnapshotPath, "utf-8");
2826
+ let snapshot = convertPocketBaseMigration(migrationContent);
2827
+ const snapshotFilename = path5.basename(latestSnapshotPath);
2828
+ const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
2829
+ if (snapshotTimestamp) {
2830
+ const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
2831
+ for (const migrationFile of migrationFiles) {
2832
+ try {
2833
+ const migrationContent2 = fs5.readFileSync(migrationFile, "utf-8");
2834
+ const operations = parseMigrationOperations(migrationContent2);
2835
+ snapshot = applyMigrationOperations(snapshot, operations);
2836
+ } catch (error) {
2837
+ console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
2838
+ }
2839
+ }
2840
+ }
2841
+ return snapshot;
2842
+ } catch (error) {
2843
+ console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2844
+ return null;
2845
+ }
2846
+ }
2847
+
2544
2848
  // src/migration/validation.ts
2545
2849
  function detectCollectionDeletions(diff) {
2546
2850
  const changes = [];
@@ -2709,8 +3013,8 @@ var DEFAULT_CONFIG5 = {
2709
3013
  };
2710
3014
  function findConfigFile(directory) {
2711
3015
  for (const fileName of CONFIG_FILE_NAMES) {
2712
- const filePath = path4.join(directory, fileName);
2713
- if (fs4.existsSync(filePath)) {
3016
+ const filePath = path5.join(directory, fileName);
3017
+ if (fs5.existsSync(filePath)) {
2714
3018
  return filePath;
2715
3019
  }
2716
3020
  }
@@ -2718,7 +3022,7 @@ function findConfigFile(directory) {
2718
3022
  }
2719
3023
  function loadJsonConfig(configPath) {
2720
3024
  try {
2721
- const content = fs4.readFileSync(configPath, "utf-8");
3025
+ const content = fs5.readFileSync(configPath, "utf-8");
2722
3026
  return JSON.parse(content);
2723
3027
  } catch (error) {
2724
3028
  if (error instanceof SyntaxError) {
@@ -2747,10 +3051,10 @@ async function loadJsConfig(configPath) {
2747
3051
  }
2748
3052
  }
2749
3053
  async function loadConfigFile(configPath) {
2750
- if (!fs4.existsSync(configPath)) {
3054
+ if (!fs5.existsSync(configPath)) {
2751
3055
  return null;
2752
3056
  }
2753
- const ext = path4.extname(configPath).toLowerCase();
3057
+ const ext = path5.extname(configPath).toLowerCase();
2754
3058
  if (ext === ".json") {
2755
3059
  return loadJsonConfig(configPath);
2756
3060
  } else if (ext === ".js" || ext === ".mjs") {
@@ -2817,10 +3121,10 @@ function validateConfig(config, configPath) {
2817
3121
  }
2818
3122
  const cwd = process.cwd();
2819
3123
  const possiblePaths = [
2820
- path4.resolve(cwd, config.schema.directory),
2821
- path4.resolve(cwd, "shared", config.schema.directory)
3124
+ path5.resolve(cwd, config.schema.directory),
3125
+ path5.resolve(cwd, "shared", config.schema.directory)
2822
3126
  ];
2823
- const schemaDir = possiblePaths.find((p) => fs4.existsSync(p));
3127
+ const schemaDir = possiblePaths.find((p) => fs5.existsSync(p));
2824
3128
  if (!schemaDir) {
2825
3129
  throw new ConfigurationError(`Schema directory not found. Tried: ${possiblePaths.join(", ")}`, configPath, [
2826
3130
  "schema.directory"
@@ -2832,15 +3136,15 @@ async function loadConfig(options = {}) {
2832
3136
  let configFilePath;
2833
3137
  const cwd = process.cwd();
2834
3138
  if (options.config) {
2835
- const explicitPath = path4.resolve(cwd, options.config);
2836
- if (!fs4.existsSync(explicitPath)) {
3139
+ const explicitPath = path5.resolve(cwd, options.config);
3140
+ if (!fs5.existsSync(explicitPath)) {
2837
3141
  throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
2838
3142
  }
2839
3143
  configFilePath = explicitPath;
2840
3144
  } else {
2841
- const searchDirs = [cwd, path4.join(cwd, "shared")];
3145
+ const searchDirs = [cwd, path5.join(cwd, "shared")];
2842
3146
  for (const dir of searchDirs) {
2843
- if (fs4.existsSync(dir)) {
3147
+ if (fs5.existsSync(dir)) {
2844
3148
  const found = findConfigFile(dir);
2845
3149
  if (found) {
2846
3150
  configFilePath = found;
@@ -2869,18 +3173,18 @@ async function loadConfig(options = {}) {
2869
3173
  function getSchemaDirectory(config) {
2870
3174
  const cwd = process.cwd();
2871
3175
  const possiblePaths = [
2872
- path4.resolve(cwd, config.schema.directory),
2873
- path4.resolve(cwd, "shared", config.schema.directory)
3176
+ path5.resolve(cwd, config.schema.directory),
3177
+ path5.resolve(cwd, "shared", config.schema.directory)
2874
3178
  ];
2875
- return possiblePaths.find((p) => fs4.existsSync(p)) || possiblePaths[0];
3179
+ return possiblePaths.find((p) => fs5.existsSync(p)) || possiblePaths[0];
2876
3180
  }
2877
3181
  function getMigrationsDirectory(config) {
2878
3182
  const cwd = process.cwd();
2879
3183
  const possiblePaths = [
2880
- path4.resolve(cwd, config.migrations.directory),
2881
- path4.resolve(cwd, "shared", config.migrations.directory)
3184
+ path5.resolve(cwd, config.migrations.directory),
3185
+ path5.resolve(cwd, "shared", config.migrations.directory)
2882
3186
  ];
2883
- return possiblePaths.find((p) => fs4.existsSync(p)) || possiblePaths[0];
3187
+ return possiblePaths.find((p) => fs5.existsSync(p)) || possiblePaths[0];
2884
3188
  }
2885
3189
  var currentVerbosity = "normal";
2886
3190
  function setVerbosity(level) {
@@ -3097,7 +3401,7 @@ async function executeGenerate(options) {
3097
3401
  const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
3098
3402
  logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
3099
3403
  logInfo("Loading previous snapshot...");
3100
- const previousSnapshot = loadSnapshotIfExists({
3404
+ const previousSnapshot = loadSnapshotWithMigrations({
3101
3405
  migrationsPath: migrationsDir,
3102
3406
  workspaceRoot: process.cwd()
3103
3407
  });
@@ -3124,7 +3428,7 @@ async function executeGenerate(options) {
3124
3428
  "Creating migration file...",
3125
3429
  () => Promise.resolve(generate(diff, migrationsDir))
3126
3430
  );
3127
- logSuccess(`Migration file created: ${path4.basename(migrationPath)}`);
3431
+ logSuccess(`Migration file created: ${path5.basename(migrationPath)}`);
3128
3432
  logSection("\u2705 Next Steps");
3129
3433
  console.log();
3130
3434
  console.log(" 1. Review the generated migration file:");
@@ -3302,7 +3606,7 @@ async function executeStatus(options) {
3302
3606
  const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
3303
3607
  logSuccess(`Found ${currentSchema.collections.size} collection(s) in schema`);
3304
3608
  logInfo("Loading previous snapshot...");
3305
- const previousSnapshot = loadSnapshotIfExists({
3609
+ const previousSnapshot = loadSnapshotWithMigrations({
3306
3610
  migrationsPath: migrationsDir,
3307
3611
  workspaceRoot: process.cwd()
3308
3612
  });