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,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var path4 = require('path');
4
- var fs4 = require('fs');
3
+ var path5 = require('path');
4
+ var fs5 = require('fs');
5
5
  var zod = require('zod');
6
6
  var chalk = require('chalk');
7
7
  var ora = require('ora');
@@ -26,8 +26,8 @@ function _interopNamespace(e) {
26
26
  return Object.freeze(n);
27
27
  }
28
28
 
29
- var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
30
- var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
29
+ var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
30
+ var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
31
31
  var chalk__default = /*#__PURE__*/_interopDefault(chalk);
32
32
  var ora__default = /*#__PURE__*/_interopDefault(ora);
33
33
 
@@ -159,10 +159,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
159
159
  operation;
160
160
  code;
161
161
  originalError;
162
- constructor(message, path6, operation, code, originalError) {
162
+ constructor(message, path7, operation, code, originalError) {
163
163
  super(message);
164
164
  this.name = "FileSystemError";
165
- this.path = path6;
165
+ this.path = path7;
166
166
  this.operation = operation;
167
167
  this.code = code;
168
168
  this.originalError = originalError;
@@ -1076,6 +1076,16 @@ function isFieldRequired(zodType) {
1076
1076
  }
1077
1077
 
1078
1078
  // src/migration/analyzer.ts
1079
+ var tsxLoaderRegistered = false;
1080
+ async function ensureTsxLoader() {
1081
+ if (tsxLoaderRegistered) return;
1082
+ try {
1083
+ await import('tsx/esm');
1084
+ tsxLoaderRegistered = true;
1085
+ } catch {
1086
+ tsxLoaderRegistered = false;
1087
+ }
1088
+ }
1079
1089
  var DEFAULT_CONFIG = {
1080
1090
  workspaceRoot: process.cwd(),
1081
1091
  excludePatterns: [
@@ -1103,20 +1113,20 @@ function mergeConfig(config) {
1103
1113
  }
1104
1114
  function resolveSchemaDir(config) {
1105
1115
  const workspaceRoot = config.workspaceRoot || process.cwd();
1106
- if (path4__namespace.isAbsolute(config.schemaDir)) {
1116
+ if (path5__namespace.isAbsolute(config.schemaDir)) {
1107
1117
  return config.schemaDir;
1108
1118
  }
1109
- return path4__namespace.join(workspaceRoot, config.schemaDir);
1119
+ return path5__namespace.join(workspaceRoot, config.schemaDir);
1110
1120
  }
1111
1121
  function discoverSchemaFiles(config) {
1112
1122
  const normalizedConfig = typeof config === "string" ? { schemaDir: config } : config;
1113
1123
  const mergedConfig = mergeConfig(normalizedConfig);
1114
1124
  const schemaDir = resolveSchemaDir(normalizedConfig);
1115
1125
  try {
1116
- if (!fs4__namespace.existsSync(schemaDir)) {
1126
+ if (!fs5__namespace.existsSync(schemaDir)) {
1117
1127
  throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
1118
1128
  }
1119
- const files = fs4__namespace.readdirSync(schemaDir);
1129
+ const files = fs5__namespace.readdirSync(schemaDir);
1120
1130
  const schemaFiles = files.filter((file) => {
1121
1131
  const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
1122
1132
  if (!hasValidExtension) return false;
@@ -1132,7 +1142,7 @@ function discoverSchemaFiles(config) {
1132
1142
  });
1133
1143
  return schemaFiles.map((file) => {
1134
1144
  const ext = mergedConfig.includeExtensions.find((ext2) => file.endsWith(ext2)) || ".ts";
1135
- return path4__namespace.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
1145
+ return path5__namespace.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
1136
1146
  });
1137
1147
  } catch (error) {
1138
1148
  if (error instanceof FileSystemError) {
@@ -1166,40 +1176,66 @@ async function importSchemaModule(filePath, config) {
1166
1176
  let resolvedPath = null;
1167
1177
  const jsPath = `${importPath}.js`;
1168
1178
  const tsPath = `${importPath}.ts`;
1169
- if (fs4__namespace.existsSync(jsPath)) {
1179
+ if (fs5__namespace.existsSync(jsPath)) {
1170
1180
  resolvedPath = jsPath;
1171
- } else if (fs4__namespace.existsSync(tsPath)) {
1181
+ } else if (fs5__namespace.existsSync(tsPath)) {
1172
1182
  resolvedPath = tsPath;
1173
1183
  } else {
1174
1184
  resolvedPath = jsPath;
1175
1185
  }
1176
- const fileUrl = new URL(`file://${path4__namespace.resolve(resolvedPath)}`);
1186
+ if (resolvedPath.endsWith(".ts")) {
1187
+ await ensureTsxLoader();
1188
+ if (!tsxLoaderRegistered) {
1189
+ throw new SchemaParsingError(
1190
+ `Failed to import TypeScript schema file. The 'tsx' package is required to load TypeScript files.
1191
+ Please install tsx: npm install tsx (or yarn add tsx, or pnpm add tsx)
1192
+ Alternatively, compile your schema files to JavaScript first.`,
1193
+ filePath
1194
+ );
1195
+ }
1196
+ }
1197
+ const fileUrl = new URL(`file://${path5__namespace.resolve(resolvedPath)}`);
1177
1198
  const module = await import(fileUrl.href);
1178
1199
  return module;
1179
1200
  } catch (error) {
1180
1201
  const tsPath = `${filePath}.ts`;
1181
- const isTypeScriptFile = fs4__namespace.existsSync(tsPath);
1202
+ const isTypeScriptFile = fs5__namespace.existsSync(tsPath);
1203
+ if (isTypeScriptFile && error instanceof SchemaParsingError) {
1204
+ throw error;
1205
+ }
1182
1206
  if (isTypeScriptFile) {
1183
1207
  throw new SchemaParsingError(
1184
- `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
1185
- Please either:
1186
- 1. Compile your schema files to JavaScript first, or
1187
- 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")`,
1208
+ `Failed to import TypeScript schema file. The 'tsx' package is required to load TypeScript files.
1209
+ Please install tsx: npm install tsx (or yarn add tsx, or pnpm add tsx)
1210
+ Alternatively, compile your schema files to JavaScript first.`,
1188
1211
  filePath,
1189
1212
  error
1190
1213
  );
1191
1214
  }
1192
1215
  throw new SchemaParsingError(
1193
- `Failed to import schema module. Make sure the schema files are compiled to JavaScript.`,
1216
+ `Failed to import schema module. Make sure the schema files exist and are valid.`,
1194
1217
  filePath,
1195
1218
  error
1196
1219
  );
1197
1220
  }
1198
1221
  }
1199
1222
  function getCollectionNameFromFile(filePath) {
1200
- const filename = path4__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
1223
+ const filename = path5__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
1201
1224
  return toCollectionName(filename);
1202
1225
  }
1226
+ function extractCollectionNameFromSchema(zodSchema) {
1227
+ if (!zodSchema.description) {
1228
+ return null;
1229
+ }
1230
+ try {
1231
+ const metadata = JSON.parse(zodSchema.description);
1232
+ if (metadata.collectionName && typeof metadata.collectionName === "string") {
1233
+ return metadata.collectionName;
1234
+ }
1235
+ } catch {
1236
+ }
1237
+ return null;
1238
+ }
1203
1239
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema"]) {
1204
1240
  const result = {};
1205
1241
  for (const [key, value] of Object.entries(module)) {
@@ -1362,7 +1398,7 @@ async function buildSchemaDefinition(config) {
1362
1398
  importPath = normalizedConfig.pathTransformer(filePath);
1363
1399
  } else if (mergedConfig.useCompiledFiles) {
1364
1400
  const distPath = filePath.replace(/\/src\//, "/dist/");
1365
- if (fs4__namespace.existsSync(`${distPath}.js`) || fs4__namespace.existsSync(`${distPath}.mjs`)) {
1401
+ if (fs5__namespace.existsSync(`${distPath}.js`) || fs5__namespace.existsSync(`${distPath}.mjs`)) {
1366
1402
  importPath = distPath;
1367
1403
  } else {
1368
1404
  importPath = filePath;
@@ -1375,7 +1411,8 @@ async function buildSchemaDefinition(config) {
1375
1411
  console.warn(`No valid schema found in ${filePath}, skipping...`);
1376
1412
  continue;
1377
1413
  }
1378
- const collectionName = getCollectionNameFromFile(filePath);
1414
+ const collectionNameFromSchema = extractCollectionNameFromSchema(zodSchema);
1415
+ const collectionName = collectionNameFromSchema ?? getCollectionNameFromFile(filePath);
1379
1416
  const collectionSchema = convertZodSchemaToCollectionSchema(collectionName, zodSchema);
1380
1417
  collections.set(collectionName, collectionSchema);
1381
1418
  } catch (error) {
@@ -1539,6 +1576,9 @@ function compareFieldOptions(currentField, previousField) {
1539
1576
  for (const key of allKeys) {
1540
1577
  const currentValue = currentOptions[key];
1541
1578
  const previousValue = previousOptions[key];
1579
+ if (currentValue === void 0 && previousValue === void 0) {
1580
+ continue;
1581
+ }
1542
1582
  if (!areValuesEqual(currentValue, previousValue)) {
1543
1583
  changes.push({
1544
1584
  property: `options.${key}`,
@@ -1559,11 +1599,26 @@ function compareRelationConfigurations(currentField, previousField) {
1559
1599
  if (!currentRelation || !previousRelation) {
1560
1600
  return changes;
1561
1601
  }
1562
- if (currentRelation.collection !== previousRelation.collection) {
1602
+ const normalizeCollection = (collection) => {
1603
+ if (!collection) return collection;
1604
+ if (collection === "_pb_users_auth_") {
1605
+ return "Users";
1606
+ }
1607
+ const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1608
+ if (nameMatch) {
1609
+ return nameMatch[1];
1610
+ }
1611
+ return collection;
1612
+ };
1613
+ const normalizedCurrent = normalizeCollection(currentRelation.collection);
1614
+ const normalizedPrevious = normalizeCollection(previousRelation.collection);
1615
+ if (normalizedCurrent !== normalizedPrevious) {
1563
1616
  changes.push({
1564
1617
  property: "relation.collection",
1565
- oldValue: previousRelation.collection,
1566
- newValue: currentRelation.collection
1618
+ oldValue: normalizedPrevious,
1619
+ // Use normalized value for clarity
1620
+ newValue: normalizedCurrent
1621
+ // Use normalized value for clarity
1567
1622
  });
1568
1623
  }
1569
1624
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -1783,10 +1838,10 @@ function mergeConfig3(config) {
1783
1838
  }
1784
1839
  function resolveMigrationDir(config) {
1785
1840
  const workspaceRoot = config.workspaceRoot || process.cwd();
1786
- if (path4__namespace.isAbsolute(config.migrationDir)) {
1841
+ if (path5__namespace.isAbsolute(config.migrationDir)) {
1787
1842
  return config.migrationDir;
1788
1843
  }
1789
- return path4__namespace.join(workspaceRoot, config.migrationDir);
1844
+ return path5__namespace.join(workspaceRoot, config.migrationDir);
1790
1845
  }
1791
1846
  function generateTimestamp(config) {
1792
1847
  if (config?.timestampGenerator) {
@@ -1844,9 +1899,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
1844
1899
  }
1845
1900
  function writeMigrationFile(migrationDir, filename, content) {
1846
1901
  try {
1847
- if (!fs4__namespace.existsSync(migrationDir)) {
1902
+ if (!fs5__namespace.existsSync(migrationDir)) {
1848
1903
  try {
1849
- fs4__namespace.mkdirSync(migrationDir, { recursive: true });
1904
+ fs5__namespace.mkdirSync(migrationDir, { recursive: true });
1850
1905
  } catch (error) {
1851
1906
  const fsError = error;
1852
1907
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
@@ -1867,15 +1922,15 @@ function writeMigrationFile(migrationDir, filename, content) {
1867
1922
  );
1868
1923
  }
1869
1924
  }
1870
- const filePath = path4__namespace.join(migrationDir, filename);
1871
- fs4__namespace.writeFileSync(filePath, content, "utf-8");
1925
+ const filePath = path5__namespace.join(migrationDir, filename);
1926
+ fs5__namespace.writeFileSync(filePath, content, "utf-8");
1872
1927
  return filePath;
1873
1928
  } catch (error) {
1874
1929
  if (error instanceof FileSystemError) {
1875
1930
  throw error;
1876
1931
  }
1877
1932
  const fsError = error;
1878
- const filePath = path4__namespace.join(migrationDir, filename);
1933
+ const filePath = path5__namespace.join(migrationDir, filename);
1879
1934
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
1880
1935
  throw new FileSystemError(
1881
1936
  `Permission denied writing migration file. Check file and directory permissions.`,
@@ -2420,57 +2475,18 @@ function generate(diff, config) {
2420
2475
  );
2421
2476
  }
2422
2477
  }
2478
+
2479
+ // src/migration/pocketbase-converter.ts
2423
2480
  var SNAPSHOT_VERSION = "1.0.0";
2424
- ({
2425
- workspaceRoot: process.cwd()});
2426
- function findLatestSnapshot(migrationsPath) {
2427
- try {
2428
- if (!fs4__namespace.existsSync(migrationsPath)) {
2429
- return null;
2430
- }
2431
- const files = fs4__namespace.readdirSync(migrationsPath);
2432
- const snapshotFiles = files.filter(
2433
- (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2434
- );
2435
- if (snapshotFiles.length === 0) {
2436
- return null;
2437
- }
2438
- snapshotFiles.sort().reverse();
2439
- const latestSnapshot = snapshotFiles[0];
2440
- if (!latestSnapshot) {
2441
- return null;
2442
- }
2443
- return path4__namespace.join(migrationsPath, latestSnapshot);
2444
- } catch (error) {
2445
- console.warn(`Error finding latest snapshot: ${error}`);
2446
- return null;
2481
+ function resolveCollectionIdToName(collectionId) {
2482
+ if (collectionId === "_pb_users_auth_") {
2483
+ return "Users";
2447
2484
  }
2448
- }
2449
- function loadSnapshotIfExists(config = {}) {
2450
- const migrationsPath = config.migrationsPath;
2451
- if (!migrationsPath) {
2452
- return null;
2453
- }
2454
- if (fs4__namespace.existsSync(migrationsPath) && fs4__namespace.statSync(migrationsPath).isFile()) {
2455
- try {
2456
- const migrationContent = fs4__namespace.readFileSync(migrationsPath, "utf-8");
2457
- return convertPocketBaseMigration(migrationContent);
2458
- } catch (error) {
2459
- console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2460
- return null;
2461
- }
2485
+ const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2486
+ if (nameMatch) {
2487
+ return nameMatch[1];
2462
2488
  }
2463
- const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2464
- if (latestSnapshotPath) {
2465
- try {
2466
- const migrationContent = fs4__namespace.readFileSync(latestSnapshotPath, "utf-8");
2467
- return convertPocketBaseMigration(migrationContent);
2468
- } catch (error) {
2469
- console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2470
- return null;
2471
- }
2472
- }
2473
- return null;
2489
+ return collectionId;
2474
2490
  }
2475
2491
  function convertPocketBaseCollection(pbCollection) {
2476
2492
  const fields = [];
@@ -2489,17 +2505,28 @@ function convertPocketBaseCollection(pbCollection) {
2489
2505
  type: pbField.type,
2490
2506
  required: pbField.required || false
2491
2507
  };
2492
- if (pbField.options) {
2493
- field.options = pbField.options;
2508
+ field.options = pbField.options ? { ...pbField.options } : {};
2509
+ if (pbField.type === "select") {
2510
+ if (pbField.values && Array.isArray(pbField.values)) {
2511
+ field.options.values = pbField.values;
2512
+ } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2513
+ field.options.values = pbField.options.values;
2514
+ }
2494
2515
  }
2495
2516
  if (pbField.type === "relation") {
2517
+ const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2518
+ const collectionName = resolveCollectionIdToName(collectionId);
2496
2519
  field.relation = {
2497
- collection: pbField.options?.collectionId || "",
2498
- cascadeDelete: pbField.options?.cascadeDelete || false,
2499
- maxSelect: pbField.options?.maxSelect,
2500
- minSelect: pbField.options?.minSelect
2520
+ collection: collectionName,
2521
+ cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2522
+ maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2523
+ minSelect: pbField.minSelect ?? pbField.options?.minSelect
2501
2524
  };
2502
2525
  }
2526
+ const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2527
+ if (Object.keys(field.options).length === 0) {
2528
+ delete field.options;
2529
+ } else if (pbField.type === "select" && hasOnlyValues) ;
2503
2530
  fields.push(field);
2504
2531
  }
2505
2532
  }
@@ -2564,6 +2591,320 @@ function convertPocketBaseMigration(migrationContent) {
2564
2591
  }
2565
2592
  }
2566
2593
 
2594
+ // src/migration/migration-parser.ts
2595
+ function extractTimestampFromFilename(filename) {
2596
+ const match = filename.match(/^(\d+)_/);
2597
+ if (match) {
2598
+ return parseInt(match[1], 10);
2599
+ }
2600
+ return null;
2601
+ }
2602
+ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
2603
+ try {
2604
+ if (!fs5__namespace.existsSync(migrationsPath)) {
2605
+ return [];
2606
+ }
2607
+ const files = fs5__namespace.readdirSync(migrationsPath);
2608
+ const migrationFiles = [];
2609
+ for (const file of files) {
2610
+ if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
2611
+ continue;
2612
+ }
2613
+ if (!file.endsWith(".js")) {
2614
+ continue;
2615
+ }
2616
+ const timestamp = extractTimestampFromFilename(file);
2617
+ if (timestamp && timestamp > snapshotTimestamp) {
2618
+ migrationFiles.push({
2619
+ path: path5__namespace.join(migrationsPath, file),
2620
+ timestamp
2621
+ });
2622
+ }
2623
+ }
2624
+ migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
2625
+ return migrationFiles.map((f) => f.path);
2626
+ } catch (error) {
2627
+ console.warn(`Error finding migrations after snapshot: ${error}`);
2628
+ return [];
2629
+ }
2630
+ }
2631
+ function parseMigrationOperationsFromContent(content) {
2632
+ const collectionsToCreate = [];
2633
+ const collectionsToDelete = [];
2634
+ try {
2635
+ let searchIndex = 0;
2636
+ while (true) {
2637
+ const collectionStart = content.indexOf("new Collection(", searchIndex);
2638
+ if (collectionStart === -1) {
2639
+ break;
2640
+ }
2641
+ const openParen = collectionStart + "new Collection(".length;
2642
+ let braceCount = 0;
2643
+ let parenCount = 1;
2644
+ let inString = false;
2645
+ let stringChar = null;
2646
+ let i = openParen;
2647
+ while (i < content.length && /\s/.test(content[i])) {
2648
+ i++;
2649
+ }
2650
+ if (content[i] !== "{") {
2651
+ searchIndex = i + 1;
2652
+ continue;
2653
+ }
2654
+ const objectStart = i;
2655
+ braceCount = 1;
2656
+ i++;
2657
+ while (i < content.length && (braceCount > 0 || parenCount > 0)) {
2658
+ const char = content[i];
2659
+ const prevChar = i > 0 ? content[i - 1] : "";
2660
+ if (!inString && (char === '"' || char === "'")) {
2661
+ inString = true;
2662
+ stringChar = char;
2663
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2664
+ inString = false;
2665
+ stringChar = null;
2666
+ }
2667
+ if (!inString) {
2668
+ if (char === "{") braceCount++;
2669
+ if (char === "}") braceCount--;
2670
+ if (char === "(") parenCount++;
2671
+ if (char === ")") parenCount--;
2672
+ }
2673
+ i++;
2674
+ }
2675
+ if (braceCount === 0 && parenCount === 0) {
2676
+ const objectContent = content.substring(objectStart, i - 1);
2677
+ try {
2678
+ const collectionObj = new Function(`return ${objectContent}`)();
2679
+ if (collectionObj && collectionObj.name) {
2680
+ const schema = convertPocketBaseCollection(collectionObj);
2681
+ collectionsToCreate.push(schema);
2682
+ }
2683
+ } catch (error) {
2684
+ console.warn(`Failed to parse collection definition: ${error}`);
2685
+ }
2686
+ }
2687
+ searchIndex = i;
2688
+ }
2689
+ const deleteMatches = content.matchAll(
2690
+ /app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
2691
+ );
2692
+ for (const match of deleteMatches) {
2693
+ if (match[1]) {
2694
+ collectionsToDelete.push(match[1]);
2695
+ } else {
2696
+ const varNameMatch = match[0].match(/collection_(\w+)/);
2697
+ if (varNameMatch) {
2698
+ const varName = `collection_${varNameMatch[1]}`;
2699
+ const deleteIndex = content.indexOf(match[0]);
2700
+ const beforeDelete = content.substring(0, deleteIndex);
2701
+ const varDefMatch = beforeDelete.match(
2702
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
2703
+ );
2704
+ if (varDefMatch && varDefMatch.length > 0) {
2705
+ const collectionDefMatch = beforeDelete.match(
2706
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
2707
+ );
2708
+ if (collectionDefMatch) {
2709
+ try {
2710
+ const collectionDefStr = collectionDefMatch[1];
2711
+ const collectionObj = new Function(`return ${collectionDefStr}`)();
2712
+ if (collectionObj && collectionObj.name) {
2713
+ collectionsToDelete.push(collectionObj.name);
2714
+ }
2715
+ } catch {
2716
+ }
2717
+ }
2718
+ }
2719
+ }
2720
+ }
2721
+ }
2722
+ const findAndDeleteMatches = content.matchAll(
2723
+ /app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
2724
+ );
2725
+ for (const match of findAndDeleteMatches) {
2726
+ collectionsToDelete.push(match[1]);
2727
+ }
2728
+ } catch (error) {
2729
+ console.warn(`Failed to parse migration operations from content: ${error}`);
2730
+ }
2731
+ return { collectionsToCreate, collectionsToDelete };
2732
+ }
2733
+ function parseMigrationOperations(migrationContent) {
2734
+ try {
2735
+ const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
2736
+ if (!migrateMatch) {
2737
+ return parseMigrationOperationsFromContent(migrationContent);
2738
+ }
2739
+ const startIndex = migrateMatch.index + migrateMatch[0].length;
2740
+ let i = startIndex;
2741
+ let parenCount = 0;
2742
+ let foundFirstParen = false;
2743
+ while (i < migrationContent.length) {
2744
+ const char = migrationContent[i];
2745
+ if (char === "(") {
2746
+ parenCount++;
2747
+ foundFirstParen = true;
2748
+ i++;
2749
+ break;
2750
+ }
2751
+ i++;
2752
+ }
2753
+ if (!foundFirstParen) {
2754
+ return parseMigrationOperationsFromContent(migrationContent);
2755
+ }
2756
+ let inString = false;
2757
+ let stringChar = null;
2758
+ let foundBrace = false;
2759
+ let braceStart = -1;
2760
+ while (i < migrationContent.length && !foundBrace) {
2761
+ const char = migrationContent[i];
2762
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2763
+ if (!inString && (char === '"' || char === "'")) {
2764
+ inString = true;
2765
+ stringChar = char;
2766
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2767
+ inString = false;
2768
+ stringChar = null;
2769
+ }
2770
+ if (!inString) {
2771
+ if (char === "(") parenCount++;
2772
+ if (char === ")") {
2773
+ parenCount--;
2774
+ if (parenCount === 0) {
2775
+ i++;
2776
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2777
+ i++;
2778
+ }
2779
+ if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
2780
+ i += 2;
2781
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2782
+ i++;
2783
+ }
2784
+ if (i < migrationContent.length && migrationContent[i] === "{") {
2785
+ foundBrace = true;
2786
+ braceStart = i + 1;
2787
+ break;
2788
+ }
2789
+ }
2790
+ }
2791
+ }
2792
+ }
2793
+ i++;
2794
+ }
2795
+ if (!foundBrace || braceStart === -1) {
2796
+ return parseMigrationOperationsFromContent(migrationContent);
2797
+ }
2798
+ let braceCount = 1;
2799
+ i = braceStart;
2800
+ inString = false;
2801
+ stringChar = null;
2802
+ while (i < migrationContent.length && braceCount > 0) {
2803
+ const char = migrationContent[i];
2804
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2805
+ if (!inString && (char === '"' || char === "'")) {
2806
+ inString = true;
2807
+ stringChar = char;
2808
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2809
+ inString = false;
2810
+ stringChar = null;
2811
+ }
2812
+ if (!inString) {
2813
+ if (char === "{") braceCount++;
2814
+ if (char === "}") braceCount--;
2815
+ }
2816
+ i++;
2817
+ }
2818
+ if (braceCount === 0) {
2819
+ const upMigrationContent = migrationContent.substring(braceStart, i - 1);
2820
+ return parseMigrationOperationsFromContent(upMigrationContent);
2821
+ }
2822
+ return parseMigrationOperationsFromContent(migrationContent);
2823
+ } catch (error) {
2824
+ console.warn(`Failed to parse migration operations: ${error}`);
2825
+ return { collectionsToCreate: [], collectionsToDelete: [] };
2826
+ }
2827
+ }
2828
+ ({
2829
+ workspaceRoot: process.cwd()});
2830
+ function findLatestSnapshot(migrationsPath) {
2831
+ try {
2832
+ if (!fs5__namespace.existsSync(migrationsPath)) {
2833
+ return null;
2834
+ }
2835
+ const files = fs5__namespace.readdirSync(migrationsPath);
2836
+ const snapshotFiles = files.filter(
2837
+ (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2838
+ );
2839
+ if (snapshotFiles.length === 0) {
2840
+ return null;
2841
+ }
2842
+ snapshotFiles.sort().reverse();
2843
+ const latestSnapshot = snapshotFiles[0];
2844
+ if (!latestSnapshot) {
2845
+ return null;
2846
+ }
2847
+ return path5__namespace.join(migrationsPath, latestSnapshot);
2848
+ } catch (error) {
2849
+ console.warn(`Error finding latest snapshot: ${error}`);
2850
+ return null;
2851
+ }
2852
+ }
2853
+ function applyMigrationOperations(snapshot, operations) {
2854
+ const updatedCollections = new Map(snapshot.collections);
2855
+ for (const collectionName of operations.collectionsToDelete) {
2856
+ updatedCollections.delete(collectionName);
2857
+ }
2858
+ for (const collection of operations.collectionsToCreate) {
2859
+ updatedCollections.set(collection.name, collection);
2860
+ }
2861
+ return {
2862
+ ...snapshot,
2863
+ collections: updatedCollections
2864
+ };
2865
+ }
2866
+ function loadSnapshotWithMigrations(config = {}) {
2867
+ const migrationsPath = config.migrationsPath;
2868
+ if (!migrationsPath) {
2869
+ return null;
2870
+ }
2871
+ if (fs5__namespace.existsSync(migrationsPath) && fs5__namespace.statSync(migrationsPath).isFile()) {
2872
+ try {
2873
+ const migrationContent = fs5__namespace.readFileSync(migrationsPath, "utf-8");
2874
+ return convertPocketBaseMigration(migrationContent);
2875
+ } catch (error) {
2876
+ console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2877
+ return null;
2878
+ }
2879
+ }
2880
+ const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2881
+ if (!latestSnapshotPath) {
2882
+ return null;
2883
+ }
2884
+ try {
2885
+ const migrationContent = fs5__namespace.readFileSync(latestSnapshotPath, "utf-8");
2886
+ let snapshot = convertPocketBaseMigration(migrationContent);
2887
+ const snapshotFilename = path5__namespace.basename(latestSnapshotPath);
2888
+ const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
2889
+ if (snapshotTimestamp) {
2890
+ const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
2891
+ for (const migrationFile of migrationFiles) {
2892
+ try {
2893
+ const migrationContent2 = fs5__namespace.readFileSync(migrationFile, "utf-8");
2894
+ const operations = parseMigrationOperations(migrationContent2);
2895
+ snapshot = applyMigrationOperations(snapshot, operations);
2896
+ } catch (error) {
2897
+ console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
2898
+ }
2899
+ }
2900
+ }
2901
+ return snapshot;
2902
+ } catch (error) {
2903
+ console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2904
+ return null;
2905
+ }
2906
+ }
2907
+
2567
2908
  // src/migration/validation.ts
2568
2909
  function detectCollectionDeletions(diff) {
2569
2910
  const changes = [];
@@ -2732,8 +3073,8 @@ var DEFAULT_CONFIG5 = {
2732
3073
  };
2733
3074
  function findConfigFile(directory) {
2734
3075
  for (const fileName of CONFIG_FILE_NAMES) {
2735
- const filePath = path4__namespace.join(directory, fileName);
2736
- if (fs4__namespace.existsSync(filePath)) {
3076
+ const filePath = path5__namespace.join(directory, fileName);
3077
+ if (fs5__namespace.existsSync(filePath)) {
2737
3078
  return filePath;
2738
3079
  }
2739
3080
  }
@@ -2741,7 +3082,7 @@ function findConfigFile(directory) {
2741
3082
  }
2742
3083
  function loadJsonConfig(configPath) {
2743
3084
  try {
2744
- const content = fs4__namespace.readFileSync(configPath, "utf-8");
3085
+ const content = fs5__namespace.readFileSync(configPath, "utf-8");
2745
3086
  return JSON.parse(content);
2746
3087
  } catch (error) {
2747
3088
  if (error instanceof SyntaxError) {
@@ -2770,10 +3111,10 @@ async function loadJsConfig(configPath) {
2770
3111
  }
2771
3112
  }
2772
3113
  async function loadConfigFile(configPath) {
2773
- if (!fs4__namespace.existsSync(configPath)) {
3114
+ if (!fs5__namespace.existsSync(configPath)) {
2774
3115
  return null;
2775
3116
  }
2776
- const ext = path4__namespace.extname(configPath).toLowerCase();
3117
+ const ext = path5__namespace.extname(configPath).toLowerCase();
2777
3118
  if (ext === ".json") {
2778
3119
  return loadJsonConfig(configPath);
2779
3120
  } else if (ext === ".js" || ext === ".mjs") {
@@ -2840,10 +3181,10 @@ function validateConfig(config, configPath) {
2840
3181
  }
2841
3182
  const cwd = process.cwd();
2842
3183
  const possiblePaths = [
2843
- path4__namespace.resolve(cwd, config.schema.directory),
2844
- path4__namespace.resolve(cwd, "shared", config.schema.directory)
3184
+ path5__namespace.resolve(cwd, config.schema.directory),
3185
+ path5__namespace.resolve(cwd, "shared", config.schema.directory)
2845
3186
  ];
2846
- const schemaDir = possiblePaths.find((p) => fs4__namespace.existsSync(p));
3187
+ const schemaDir = possiblePaths.find((p) => fs5__namespace.existsSync(p));
2847
3188
  if (!schemaDir) {
2848
3189
  throw new ConfigurationError(`Schema directory not found. Tried: ${possiblePaths.join(", ")}`, configPath, [
2849
3190
  "schema.directory"
@@ -2855,15 +3196,15 @@ async function loadConfig(options = {}) {
2855
3196
  let configFilePath;
2856
3197
  const cwd = process.cwd();
2857
3198
  if (options.config) {
2858
- const explicitPath = path4__namespace.resolve(cwd, options.config);
2859
- if (!fs4__namespace.existsSync(explicitPath)) {
3199
+ const explicitPath = path5__namespace.resolve(cwd, options.config);
3200
+ if (!fs5__namespace.existsSync(explicitPath)) {
2860
3201
  throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
2861
3202
  }
2862
3203
  configFilePath = explicitPath;
2863
3204
  } else {
2864
- const searchDirs = [cwd, path4__namespace.join(cwd, "shared")];
3205
+ const searchDirs = [cwd, path5__namespace.join(cwd, "shared")];
2865
3206
  for (const dir of searchDirs) {
2866
- if (fs4__namespace.existsSync(dir)) {
3207
+ if (fs5__namespace.existsSync(dir)) {
2867
3208
  const found = findConfigFile(dir);
2868
3209
  if (found) {
2869
3210
  configFilePath = found;
@@ -2892,18 +3233,18 @@ async function loadConfig(options = {}) {
2892
3233
  function getSchemaDirectory(config) {
2893
3234
  const cwd = process.cwd();
2894
3235
  const possiblePaths = [
2895
- path4__namespace.resolve(cwd, config.schema.directory),
2896
- path4__namespace.resolve(cwd, "shared", config.schema.directory)
3236
+ path5__namespace.resolve(cwd, config.schema.directory),
3237
+ path5__namespace.resolve(cwd, "shared", config.schema.directory)
2897
3238
  ];
2898
- return possiblePaths.find((p) => fs4__namespace.existsSync(p)) || possiblePaths[0];
3239
+ return possiblePaths.find((p) => fs5__namespace.existsSync(p)) || possiblePaths[0];
2899
3240
  }
2900
3241
  function getMigrationsDirectory(config) {
2901
3242
  const cwd = process.cwd();
2902
3243
  const possiblePaths = [
2903
- path4__namespace.resolve(cwd, config.migrations.directory),
2904
- path4__namespace.resolve(cwd, "shared", config.migrations.directory)
3244
+ path5__namespace.resolve(cwd, config.migrations.directory),
3245
+ path5__namespace.resolve(cwd, "shared", config.migrations.directory)
2905
3246
  ];
2906
- return possiblePaths.find((p) => fs4__namespace.existsSync(p)) || possiblePaths[0];
3247
+ return possiblePaths.find((p) => fs5__namespace.existsSync(p)) || possiblePaths[0];
2907
3248
  }
2908
3249
  var currentVerbosity = "normal";
2909
3250
  function setVerbosity(level) {
@@ -3180,7 +3521,7 @@ async function executeGenerate(options) {
3180
3521
  const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
3181
3522
  logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
3182
3523
  logInfo("Loading previous snapshot...");
3183
- const previousSnapshot = loadSnapshotIfExists({
3524
+ const previousSnapshot = loadSnapshotWithMigrations({
3184
3525
  migrationsPath: migrationsDir,
3185
3526
  workspaceRoot: process.cwd()
3186
3527
  });
@@ -3207,7 +3548,7 @@ async function executeGenerate(options) {
3207
3548
  "Creating migration file...",
3208
3549
  () => Promise.resolve(generate(diff, migrationsDir))
3209
3550
  );
3210
- logSuccess(`Migration file created: ${path4__namespace.basename(migrationPath)}`);
3551
+ logSuccess(`Migration file created: ${path5__namespace.basename(migrationPath)}`);
3211
3552
  logSection("\u2705 Next Steps");
3212
3553
  console.log();
3213
3554
  console.log(" 1. Review the generated migration file:");
@@ -3373,7 +3714,7 @@ async function executeStatus(options) {
3373
3714
  const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
3374
3715
  logSuccess(`Found ${currentSchema.collections.size} collection(s) in schema`);
3375
3716
  logInfo("Loading previous snapshot...");
3376
- const previousSnapshot = loadSnapshotIfExists({
3717
+ const previousSnapshot = loadSnapshotWithMigrations({
3377
3718
  migrationsPath: migrationsDir,
3378
3719
  workspaceRoot: process.cwd()
3379
3720
  });