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
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var zod = require('zod');
4
- var fs2 = require('fs');
5
- var path4 = require('path');
4
+ var fs3 = require('fs');
5
+ var path5 = require('path');
6
6
  var chalk = require('chalk');
7
7
  var ora = require('ora');
8
8
 
@@ -26,8 +26,8 @@ function _interopNamespace(e) {
26
26
  return Object.freeze(n);
27
27
  }
28
28
 
29
- var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
30
- var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
29
+ var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
30
+ var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
31
31
  var chalk__default = /*#__PURE__*/_interopDefault(chalk);
32
32
  var ora__default = /*#__PURE__*/_interopDefault(ora);
33
33
 
@@ -105,7 +105,7 @@ function filesField(options) {
105
105
  return schema;
106
106
  }
107
107
  var RELATION_METADATA_KEY = "__pocketbase_relation__";
108
- function relationField(config) {
108
+ function RelationField(config) {
109
109
  const metadata = {
110
110
  [RELATION_METADATA_KEY]: {
111
111
  type: "single",
@@ -117,7 +117,7 @@ function relationField(config) {
117
117
  };
118
118
  return zod.z.string().describe(JSON.stringify(metadata));
119
119
  }
120
- function relationsField(config) {
120
+ function RelationsField(config) {
121
121
  const metadata = {
122
122
  [RELATION_METADATA_KEY]: {
123
123
  type: "multiple",
@@ -176,6 +176,22 @@ function withIndexes(schema, indexes) {
176
176
  };
177
177
  return schema.describe(JSON.stringify(metadata));
178
178
  }
179
+ function defineCollection(config) {
180
+ const { collectionName, schema, permissions, indexes, ...futureOptions } = config;
181
+ const metadata = {
182
+ collectionName
183
+ };
184
+ if (permissions) {
185
+ metadata.permissions = permissions;
186
+ }
187
+ if (indexes) {
188
+ metadata.indexes = indexes;
189
+ }
190
+ if (Object.keys(futureOptions).length > 0) {
191
+ Object.assign(metadata, futureOptions);
192
+ }
193
+ return schema.describe(JSON.stringify(metadata));
194
+ }
179
195
 
180
196
  // src/utils/permission-templates.ts
181
197
  var PermissionTemplates = {
@@ -396,6 +412,28 @@ function mergePermissions(...schemas) {
396
412
  }
397
413
  return merged;
398
414
  }
415
+ var ProjectInputSchema = zod.z.object({
416
+ // Required fields
417
+ title: zod.z.string(),
418
+ content: zod.z.string(),
419
+ status: StatusEnum,
420
+ summary: zod.z.string().optional(),
421
+ OwnerUser: RelationField({ collection: "Users" }),
422
+ SubscriberUsers: RelationsField({ collection: "Users" })
423
+ }).extend(inputImageFileSchema);
424
+ var ProjectSchema = ProjectInputSchema.omit(omitImageFilesSchema).extend(baseImageFileSchema);
425
+ var ProjectCollection = defineCollection({
426
+ collectionName: "Projects",
427
+ schema: ProjectSchema,
428
+ permissions: {
429
+ template: "owner-only",
430
+ ownerField: "OwnerUser",
431
+ customRules: {
432
+ listRule: '@request.auth.id != ""',
433
+ viewRule: '@request.auth.id != "" && (OwnerUser = @request.auth.id || SubscriberUsers ?= @request.auth.id)'
434
+ }
435
+ }
436
+ });
399
437
  var UserInputSchema = zod.z.object({
400
438
  name: zod.z.string().optional(),
401
439
  email: zod.z.string().email(),
@@ -403,14 +441,17 @@ var UserInputSchema = zod.z.object({
403
441
  passwordConfirm: zod.z.string(),
404
442
  avatar: zod.z.instanceof(File).optional()
405
443
  });
406
- var UserDatabaseSchema = zod.z.object({
444
+ var UserCollectionSchema = zod.z.object({
407
445
  name: zod.z.string().optional(),
408
446
  email: zod.z.string().email(),
409
447
  password: zod.z.string().min(8, "Password must be at least 8 characters"),
410
448
  avatar: zod.z.instanceof(File).optional()
411
449
  });
412
- var UserSchema = withIndexes(
413
- withPermissions(UserDatabaseSchema.extend(baseSchema), {
450
+ var UserSchema = UserCollectionSchema.extend(baseSchema);
451
+ var UserCollection = defineCollection({
452
+ collectionName: "Users",
453
+ schema: UserSchema,
454
+ permissions: {
414
455
  // Users can list their own profile
415
456
  listRule: "id = @request.auth.id",
416
457
  // Users can view their own profile
@@ -422,13 +463,13 @@ var UserSchema = withIndexes(
422
463
  // Users can only delete their own account
423
464
  deleteRule: "id = @request.auth.id"
424
465
  // manageRule is null in PocketBase default (not set)
425
- }),
426
- [
466
+ },
467
+ indexes: [
427
468
  // PocketBase's default indexes for auth collections
428
469
  "CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
429
470
  "CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''"
430
471
  ]
431
- );
472
+ });
432
473
  var BaseMutator = class {
433
474
  pb;
434
475
  // Define a default property that subclasses will override
@@ -871,10 +912,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
871
912
  operation;
872
913
  code;
873
914
  originalError;
874
- constructor(message, path6, operation, code, originalError) {
915
+ constructor(message, path7, operation, code, originalError) {
875
916
  super(message);
876
917
  this.name = "FileSystemError";
877
- this.path = path6;
918
+ this.path = path7;
878
919
  this.operation = operation;
879
920
  this.code = code;
880
921
  this.originalError = originalError;
@@ -1950,6 +1991,16 @@ function getFieldTypeInfo(zodType, fieldName) {
1950
1991
  }
1951
1992
 
1952
1993
  // src/migration/analyzer.ts
1994
+ var tsxLoaderRegistered = false;
1995
+ async function ensureTsxLoader() {
1996
+ if (tsxLoaderRegistered) return;
1997
+ try {
1998
+ await import('tsx/esm');
1999
+ tsxLoaderRegistered = true;
2000
+ } catch {
2001
+ tsxLoaderRegistered = false;
2002
+ }
2003
+ }
1953
2004
  var DEFAULT_CONFIG = {
1954
2005
  workspaceRoot: process.cwd(),
1955
2006
  excludePatterns: [
@@ -1977,20 +2028,20 @@ function mergeConfig(config) {
1977
2028
  }
1978
2029
  function resolveSchemaDir(config) {
1979
2030
  const workspaceRoot = config.workspaceRoot || process.cwd();
1980
- if (path4__namespace.isAbsolute(config.schemaDir)) {
2031
+ if (path5__namespace.isAbsolute(config.schemaDir)) {
1981
2032
  return config.schemaDir;
1982
2033
  }
1983
- return path4__namespace.join(workspaceRoot, config.schemaDir);
2034
+ return path5__namespace.join(workspaceRoot, config.schemaDir);
1984
2035
  }
1985
2036
  function discoverSchemaFiles(config) {
1986
2037
  const normalizedConfig = typeof config === "string" ? { schemaDir: config } : config;
1987
2038
  const mergedConfig = mergeConfig(normalizedConfig);
1988
2039
  const schemaDir = resolveSchemaDir(normalizedConfig);
1989
2040
  try {
1990
- if (!fs2__namespace.existsSync(schemaDir)) {
2041
+ if (!fs3__namespace.existsSync(schemaDir)) {
1991
2042
  throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
1992
2043
  }
1993
- const files = fs2__namespace.readdirSync(schemaDir);
2044
+ const files = fs3__namespace.readdirSync(schemaDir);
1994
2045
  const schemaFiles = files.filter((file) => {
1995
2046
  const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
1996
2047
  if (!hasValidExtension) return false;
@@ -2006,7 +2057,7 @@ function discoverSchemaFiles(config) {
2006
2057
  });
2007
2058
  return schemaFiles.map((file) => {
2008
2059
  const ext = mergedConfig.includeExtensions.find((ext2) => file.endsWith(ext2)) || ".ts";
2009
- return path4__namespace.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
2060
+ return path5__namespace.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
2010
2061
  });
2011
2062
  } catch (error) {
2012
2063
  if (error instanceof FileSystemError) {
@@ -2040,40 +2091,66 @@ async function importSchemaModule(filePath, config) {
2040
2091
  let resolvedPath = null;
2041
2092
  const jsPath = `${importPath}.js`;
2042
2093
  const tsPath = `${importPath}.ts`;
2043
- if (fs2__namespace.existsSync(jsPath)) {
2094
+ if (fs3__namespace.existsSync(jsPath)) {
2044
2095
  resolvedPath = jsPath;
2045
- } else if (fs2__namespace.existsSync(tsPath)) {
2096
+ } else if (fs3__namespace.existsSync(tsPath)) {
2046
2097
  resolvedPath = tsPath;
2047
2098
  } else {
2048
2099
  resolvedPath = jsPath;
2049
2100
  }
2050
- const fileUrl = new URL(`file://${path4__namespace.resolve(resolvedPath)}`);
2101
+ if (resolvedPath.endsWith(".ts")) {
2102
+ await ensureTsxLoader();
2103
+ if (!tsxLoaderRegistered) {
2104
+ throw new SchemaParsingError(
2105
+ `Failed to import TypeScript schema file. The 'tsx' package is required to load TypeScript files.
2106
+ Please install tsx: npm install tsx (or yarn add tsx, or pnpm add tsx)
2107
+ Alternatively, compile your schema files to JavaScript first.`,
2108
+ filePath
2109
+ );
2110
+ }
2111
+ }
2112
+ const fileUrl = new URL(`file://${path5__namespace.resolve(resolvedPath)}`);
2051
2113
  const module = await import(fileUrl.href);
2052
2114
  return module;
2053
2115
  } catch (error) {
2054
2116
  const tsPath = `${filePath}.ts`;
2055
- const isTypeScriptFile = fs2__namespace.existsSync(tsPath);
2117
+ const isTypeScriptFile = fs3__namespace.existsSync(tsPath);
2118
+ if (isTypeScriptFile && error instanceof SchemaParsingError) {
2119
+ throw error;
2120
+ }
2056
2121
  if (isTypeScriptFile) {
2057
2122
  throw new SchemaParsingError(
2058
- `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
2059
- Please either:
2060
- 1. Compile your schema files to JavaScript first, or
2061
- 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")`,
2123
+ `Failed to import TypeScript schema file. The 'tsx' package is required to load TypeScript files.
2124
+ Please install tsx: npm install tsx (or yarn add tsx, or pnpm add tsx)
2125
+ Alternatively, compile your schema files to JavaScript first.`,
2062
2126
  filePath,
2063
2127
  error
2064
2128
  );
2065
2129
  }
2066
2130
  throw new SchemaParsingError(
2067
- `Failed to import schema module. Make sure the schema files are compiled to JavaScript.`,
2131
+ `Failed to import schema module. Make sure the schema files exist and are valid.`,
2068
2132
  filePath,
2069
2133
  error
2070
2134
  );
2071
2135
  }
2072
2136
  }
2073
2137
  function getCollectionNameFromFile(filePath) {
2074
- const filename = path4__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
2138
+ const filename = path5__namespace.basename(filePath).replace(/\.(ts|js)$/, "");
2075
2139
  return toCollectionName(filename);
2076
2140
  }
2141
+ function extractCollectionNameFromSchema(zodSchema) {
2142
+ if (!zodSchema.description) {
2143
+ return null;
2144
+ }
2145
+ try {
2146
+ const metadata = JSON.parse(zodSchema.description);
2147
+ if (metadata.collectionName && typeof metadata.collectionName === "string") {
2148
+ return metadata.collectionName;
2149
+ }
2150
+ } catch {
2151
+ }
2152
+ return null;
2153
+ }
2077
2154
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema"]) {
2078
2155
  const result = {};
2079
2156
  for (const [key, value] of Object.entries(module)) {
@@ -2236,7 +2313,7 @@ async function buildSchemaDefinition(config) {
2236
2313
  importPath = normalizedConfig.pathTransformer(filePath);
2237
2314
  } else if (mergedConfig.useCompiledFiles) {
2238
2315
  const distPath = filePath.replace(/\/src\//, "/dist/");
2239
- if (fs2__namespace.existsSync(`${distPath}.js`) || fs2__namespace.existsSync(`${distPath}.mjs`)) {
2316
+ if (fs3__namespace.existsSync(`${distPath}.js`) || fs3__namespace.existsSync(`${distPath}.mjs`)) {
2240
2317
  importPath = distPath;
2241
2318
  } else {
2242
2319
  importPath = filePath;
@@ -2249,7 +2326,8 @@ async function buildSchemaDefinition(config) {
2249
2326
  console.warn(`No valid schema found in ${filePath}, skipping...`);
2250
2327
  continue;
2251
2328
  }
2252
- const collectionName = getCollectionNameFromFile(filePath);
2329
+ const collectionNameFromSchema = extractCollectionNameFromSchema(zodSchema);
2330
+ const collectionName = collectionNameFromSchema ?? getCollectionNameFromFile(filePath);
2253
2331
  const collectionSchema = convertZodSchemaToCollectionSchema(collectionName, zodSchema);
2254
2332
  collections.set(collectionName, collectionSchema);
2255
2333
  } catch (error) {
@@ -2292,7 +2370,359 @@ var SchemaAnalyzer = class {
2292
2370
  return convertZodSchemaToCollectionSchema(name, schema);
2293
2371
  }
2294
2372
  };
2373
+
2374
+ // src/migration/pocketbase-converter.ts
2295
2375
  var SNAPSHOT_VERSION = "1.0.0";
2376
+ function resolveCollectionIdToName(collectionId) {
2377
+ if (collectionId === "_pb_users_auth_") {
2378
+ return "Users";
2379
+ }
2380
+ const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2381
+ if (nameMatch) {
2382
+ return nameMatch[1];
2383
+ }
2384
+ return collectionId;
2385
+ }
2386
+ function convertPocketBaseCollection(pbCollection) {
2387
+ const fields = [];
2388
+ const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
2389
+ const authSystemFieldNames = ["email", "emailVisibility", "verified", "password", "tokenKey"];
2390
+ if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
2391
+ for (const pbField of pbCollection.fields) {
2392
+ if (pbField.system || systemFieldNames.includes(pbField.name)) {
2393
+ continue;
2394
+ }
2395
+ if (pbCollection.type === "auth" && authSystemFieldNames.includes(pbField.name)) {
2396
+ continue;
2397
+ }
2398
+ const field = {
2399
+ name: pbField.name,
2400
+ type: pbField.type,
2401
+ required: pbField.required || false
2402
+ };
2403
+ field.options = pbField.options ? { ...pbField.options } : {};
2404
+ if (pbField.type === "select") {
2405
+ if (pbField.values && Array.isArray(pbField.values)) {
2406
+ field.options.values = pbField.values;
2407
+ } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2408
+ field.options.values = pbField.options.values;
2409
+ }
2410
+ }
2411
+ if (pbField.type === "relation") {
2412
+ const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2413
+ const collectionName = resolveCollectionIdToName(collectionId);
2414
+ field.relation = {
2415
+ collection: collectionName,
2416
+ cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2417
+ maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2418
+ minSelect: pbField.minSelect ?? pbField.options?.minSelect
2419
+ };
2420
+ }
2421
+ const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2422
+ if (Object.keys(field.options).length === 0) {
2423
+ delete field.options;
2424
+ } else if (pbField.type === "select" && hasOnlyValues) ;
2425
+ fields.push(field);
2426
+ }
2427
+ }
2428
+ const schema = {
2429
+ name: pbCollection.name,
2430
+ type: pbCollection.type || "base",
2431
+ fields
2432
+ };
2433
+ if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2434
+ schema.indexes = pbCollection.indexes;
2435
+ }
2436
+ const rules = {};
2437
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2438
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2439
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2440
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2441
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2442
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2443
+ if (Object.keys(rules).length > 0) {
2444
+ schema.rules = rules;
2445
+ schema.permissions = { ...rules };
2446
+ }
2447
+ return schema;
2448
+ }
2449
+ function convertPocketBaseMigration(migrationContent) {
2450
+ try {
2451
+ const snapshotMatch = migrationContent.match(/const\s+snapshot\s*=\s*(\[[\s\S]*?\]);/);
2452
+ if (!snapshotMatch) {
2453
+ throw new Error("Could not find snapshot array in migration file");
2454
+ }
2455
+ const snapshotArrayStr = snapshotMatch[1];
2456
+ let snapshotArray;
2457
+ try {
2458
+ snapshotArray = new Function(`return ${snapshotArrayStr}`)();
2459
+ } catch (parseError) {
2460
+ throw new Error(`Failed to parse snapshot array: ${parseError}`);
2461
+ }
2462
+ if (!Array.isArray(snapshotArray)) {
2463
+ throw new Error("Snapshot is not an array");
2464
+ }
2465
+ const collections = /* @__PURE__ */ new Map();
2466
+ for (const pbCollection of snapshotArray) {
2467
+ if (!pbCollection.name) {
2468
+ console.warn("Skipping collection without name");
2469
+ continue;
2470
+ }
2471
+ const schema = convertPocketBaseCollection(pbCollection);
2472
+ collections.set(pbCollection.name, schema);
2473
+ }
2474
+ return {
2475
+ version: SNAPSHOT_VERSION,
2476
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2477
+ collections
2478
+ };
2479
+ } catch (error) {
2480
+ throw new SnapshotError(
2481
+ `Failed to convert PocketBase migration: ${error instanceof Error ? error.message : String(error)}`,
2482
+ void 0,
2483
+ "parse",
2484
+ error instanceof Error ? error : void 0
2485
+ );
2486
+ }
2487
+ }
2488
+
2489
+ // src/migration/migration-parser.ts
2490
+ function extractTimestampFromFilename(filename) {
2491
+ const match = filename.match(/^(\d+)_/);
2492
+ if (match) {
2493
+ return parseInt(match[1], 10);
2494
+ }
2495
+ return null;
2496
+ }
2497
+ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
2498
+ try {
2499
+ if (!fs3__namespace.existsSync(migrationsPath)) {
2500
+ return [];
2501
+ }
2502
+ const files = fs3__namespace.readdirSync(migrationsPath);
2503
+ const migrationFiles = [];
2504
+ for (const file of files) {
2505
+ if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
2506
+ continue;
2507
+ }
2508
+ if (!file.endsWith(".js")) {
2509
+ continue;
2510
+ }
2511
+ const timestamp = extractTimestampFromFilename(file);
2512
+ if (timestamp && timestamp > snapshotTimestamp) {
2513
+ migrationFiles.push({
2514
+ path: path5__namespace.join(migrationsPath, file),
2515
+ timestamp
2516
+ });
2517
+ }
2518
+ }
2519
+ migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
2520
+ return migrationFiles.map((f) => f.path);
2521
+ } catch (error) {
2522
+ console.warn(`Error finding migrations after snapshot: ${error}`);
2523
+ return [];
2524
+ }
2525
+ }
2526
+ function parseMigrationOperationsFromContent(content) {
2527
+ const collectionsToCreate = [];
2528
+ const collectionsToDelete = [];
2529
+ try {
2530
+ let searchIndex = 0;
2531
+ while (true) {
2532
+ const collectionStart = content.indexOf("new Collection(", searchIndex);
2533
+ if (collectionStart === -1) {
2534
+ break;
2535
+ }
2536
+ const openParen = collectionStart + "new Collection(".length;
2537
+ let braceCount = 0;
2538
+ let parenCount = 1;
2539
+ let inString = false;
2540
+ let stringChar = null;
2541
+ let i = openParen;
2542
+ while (i < content.length && /\s/.test(content[i])) {
2543
+ i++;
2544
+ }
2545
+ if (content[i] !== "{") {
2546
+ searchIndex = i + 1;
2547
+ continue;
2548
+ }
2549
+ const objectStart = i;
2550
+ braceCount = 1;
2551
+ i++;
2552
+ while (i < content.length && (braceCount > 0 || parenCount > 0)) {
2553
+ const char = content[i];
2554
+ const prevChar = i > 0 ? content[i - 1] : "";
2555
+ if (!inString && (char === '"' || char === "'")) {
2556
+ inString = true;
2557
+ stringChar = char;
2558
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2559
+ inString = false;
2560
+ stringChar = null;
2561
+ }
2562
+ if (!inString) {
2563
+ if (char === "{") braceCount++;
2564
+ if (char === "}") braceCount--;
2565
+ if (char === "(") parenCount++;
2566
+ if (char === ")") parenCount--;
2567
+ }
2568
+ i++;
2569
+ }
2570
+ if (braceCount === 0 && parenCount === 0) {
2571
+ const objectContent = content.substring(objectStart, i - 1);
2572
+ try {
2573
+ const collectionObj = new Function(`return ${objectContent}`)();
2574
+ if (collectionObj && collectionObj.name) {
2575
+ const schema = convertPocketBaseCollection(collectionObj);
2576
+ collectionsToCreate.push(schema);
2577
+ }
2578
+ } catch (error) {
2579
+ console.warn(`Failed to parse collection definition: ${error}`);
2580
+ }
2581
+ }
2582
+ searchIndex = i;
2583
+ }
2584
+ const deleteMatches = content.matchAll(
2585
+ /app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
2586
+ );
2587
+ for (const match of deleteMatches) {
2588
+ if (match[1]) {
2589
+ collectionsToDelete.push(match[1]);
2590
+ } else {
2591
+ const varNameMatch = match[0].match(/collection_(\w+)/);
2592
+ if (varNameMatch) {
2593
+ const varName = `collection_${varNameMatch[1]}`;
2594
+ const deleteIndex = content.indexOf(match[0]);
2595
+ const beforeDelete = content.substring(0, deleteIndex);
2596
+ const varDefMatch = beforeDelete.match(
2597
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
2598
+ );
2599
+ if (varDefMatch && varDefMatch.length > 0) {
2600
+ const collectionDefMatch = beforeDelete.match(
2601
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
2602
+ );
2603
+ if (collectionDefMatch) {
2604
+ try {
2605
+ const collectionDefStr = collectionDefMatch[1];
2606
+ const collectionObj = new Function(`return ${collectionDefStr}`)();
2607
+ if (collectionObj && collectionObj.name) {
2608
+ collectionsToDelete.push(collectionObj.name);
2609
+ }
2610
+ } catch {
2611
+ }
2612
+ }
2613
+ }
2614
+ }
2615
+ }
2616
+ }
2617
+ const findAndDeleteMatches = content.matchAll(
2618
+ /app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
2619
+ );
2620
+ for (const match of findAndDeleteMatches) {
2621
+ collectionsToDelete.push(match[1]);
2622
+ }
2623
+ } catch (error) {
2624
+ console.warn(`Failed to parse migration operations from content: ${error}`);
2625
+ }
2626
+ return { collectionsToCreate, collectionsToDelete };
2627
+ }
2628
+ function parseMigrationOperations(migrationContent) {
2629
+ try {
2630
+ const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
2631
+ if (!migrateMatch) {
2632
+ return parseMigrationOperationsFromContent(migrationContent);
2633
+ }
2634
+ const startIndex = migrateMatch.index + migrateMatch[0].length;
2635
+ let i = startIndex;
2636
+ let parenCount = 0;
2637
+ let foundFirstParen = false;
2638
+ while (i < migrationContent.length) {
2639
+ const char = migrationContent[i];
2640
+ if (char === "(") {
2641
+ parenCount++;
2642
+ foundFirstParen = true;
2643
+ i++;
2644
+ break;
2645
+ }
2646
+ i++;
2647
+ }
2648
+ if (!foundFirstParen) {
2649
+ return parseMigrationOperationsFromContent(migrationContent);
2650
+ }
2651
+ let inString = false;
2652
+ let stringChar = null;
2653
+ let foundBrace = false;
2654
+ let braceStart = -1;
2655
+ while (i < migrationContent.length && !foundBrace) {
2656
+ const char = migrationContent[i];
2657
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2658
+ if (!inString && (char === '"' || char === "'")) {
2659
+ inString = true;
2660
+ stringChar = char;
2661
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2662
+ inString = false;
2663
+ stringChar = null;
2664
+ }
2665
+ if (!inString) {
2666
+ if (char === "(") parenCount++;
2667
+ if (char === ")") {
2668
+ parenCount--;
2669
+ if (parenCount === 0) {
2670
+ i++;
2671
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2672
+ i++;
2673
+ }
2674
+ if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
2675
+ i += 2;
2676
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2677
+ i++;
2678
+ }
2679
+ if (i < migrationContent.length && migrationContent[i] === "{") {
2680
+ foundBrace = true;
2681
+ braceStart = i + 1;
2682
+ break;
2683
+ }
2684
+ }
2685
+ }
2686
+ }
2687
+ }
2688
+ i++;
2689
+ }
2690
+ if (!foundBrace || braceStart === -1) {
2691
+ return parseMigrationOperationsFromContent(migrationContent);
2692
+ }
2693
+ let braceCount = 1;
2694
+ i = braceStart;
2695
+ inString = false;
2696
+ stringChar = null;
2697
+ while (i < migrationContent.length && braceCount > 0) {
2698
+ const char = migrationContent[i];
2699
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2700
+ if (!inString && (char === '"' || char === "'")) {
2701
+ inString = true;
2702
+ stringChar = char;
2703
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2704
+ inString = false;
2705
+ stringChar = null;
2706
+ }
2707
+ if (!inString) {
2708
+ if (char === "{") braceCount++;
2709
+ if (char === "}") braceCount--;
2710
+ }
2711
+ i++;
2712
+ }
2713
+ if (braceCount === 0) {
2714
+ const upMigrationContent = migrationContent.substring(braceStart, i - 1);
2715
+ return parseMigrationOperationsFromContent(upMigrationContent);
2716
+ }
2717
+ return parseMigrationOperationsFromContent(migrationContent);
2718
+ } catch (error) {
2719
+ console.warn(`Failed to parse migration operations: ${error}`);
2720
+ return { collectionsToCreate: [], collectionsToDelete: [] };
2721
+ }
2722
+ }
2723
+
2724
+ // src/migration/snapshot.ts
2725
+ var SNAPSHOT_VERSION2 = "1.0.0";
2296
2726
  var DEFAULT_SNAPSHOT_FILENAME = ".migration-snapshot.json";
2297
2727
  var SNAPSHOT_MIGRATIONS = [
2298
2728
  // Add migrations here as the format evolves
@@ -2307,7 +2737,7 @@ var DEFAULT_CONFIG2 = {
2307
2737
  snapshotPath: DEFAULT_SNAPSHOT_FILENAME,
2308
2738
  workspaceRoot: process.cwd(),
2309
2739
  autoMigrate: true,
2310
- version: SNAPSHOT_VERSION
2740
+ version: SNAPSHOT_VERSION2
2311
2741
  };
2312
2742
  function mergeConfig2(config = {}) {
2313
2743
  return {
@@ -2319,15 +2749,15 @@ function getSnapshotPath(config = {}) {
2319
2749
  const mergedConfig = mergeConfig2(config);
2320
2750
  const workspaceRoot = mergedConfig.workspaceRoot;
2321
2751
  const snapshotFilename = mergedConfig.snapshotPath;
2322
- if (path4__namespace.isAbsolute(snapshotFilename)) {
2752
+ if (path5__namespace.isAbsolute(snapshotFilename)) {
2323
2753
  return snapshotFilename;
2324
2754
  }
2325
- return path4__namespace.join(workspaceRoot, snapshotFilename);
2755
+ return path5__namespace.join(workspaceRoot, snapshotFilename);
2326
2756
  }
2327
2757
  function snapshotExists(config = {}) {
2328
2758
  try {
2329
2759
  const snapshotPath = getSnapshotPath(config);
2330
- return fs2__namespace.existsSync(snapshotPath);
2760
+ return fs3__namespace.existsSync(snapshotPath);
2331
2761
  } catch {
2332
2762
  return false;
2333
2763
  }
@@ -2386,13 +2816,13 @@ function addSnapshotMetadata(schema, config) {
2386
2816
  function saveSnapshot(schema, config = {}) {
2387
2817
  const snapshotPath = getSnapshotPath(config);
2388
2818
  try {
2389
- const snapshotDir = path4__namespace.dirname(snapshotPath);
2390
- if (!fs2__namespace.existsSync(snapshotDir)) {
2391
- fs2__namespace.mkdirSync(snapshotDir, { recursive: true });
2819
+ const snapshotDir = path5__namespace.dirname(snapshotPath);
2820
+ if (!fs3__namespace.existsSync(snapshotDir)) {
2821
+ fs3__namespace.mkdirSync(snapshotDir, { recursive: true });
2392
2822
  }
2393
2823
  const snapshotData = addSnapshotMetadata(schema, config);
2394
2824
  const jsonContent = JSON.stringify(snapshotData, null, 2);
2395
- fs2__namespace.writeFileSync(snapshotPath, jsonContent, "utf-8");
2825
+ fs3__namespace.writeFileSync(snapshotPath, jsonContent, "utf-8");
2396
2826
  } catch (error) {
2397
2827
  handleFileSystemError(error, "write", snapshotPath);
2398
2828
  }
@@ -2487,7 +2917,7 @@ function deserializeSnapshot(data) {
2487
2917
  function loadSnapshot(config = {}) {
2488
2918
  const snapshotPath = getSnapshotPath(config);
2489
2919
  try {
2490
- const jsonContent = fs2__namespace.readFileSync(snapshotPath, "utf-8");
2920
+ const jsonContent = fs3__namespace.readFileSync(snapshotPath, "utf-8");
2491
2921
  const data = parseAndValidateSnapshot(jsonContent, snapshotPath);
2492
2922
  const migratedData = migrateSnapshotFormat(data, config);
2493
2923
  return deserializeSnapshot(migratedData);
@@ -2522,10 +2952,10 @@ function mergeSnapshots(baseSnapshot, customSnapshot) {
2522
2952
  }
2523
2953
  function findLatestSnapshot(migrationsPath) {
2524
2954
  try {
2525
- if (!fs2__namespace.existsSync(migrationsPath)) {
2955
+ if (!fs3__namespace.existsSync(migrationsPath)) {
2526
2956
  return null;
2527
2957
  }
2528
- const files = fs2__namespace.readdirSync(migrationsPath);
2958
+ const files = fs3__namespace.readdirSync(migrationsPath);
2529
2959
  const snapshotFiles = files.filter(
2530
2960
  (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2531
2961
  );
@@ -2537,20 +2967,74 @@ function findLatestSnapshot(migrationsPath) {
2537
2967
  if (!latestSnapshot) {
2538
2968
  return null;
2539
2969
  }
2540
- return path4__namespace.join(migrationsPath, latestSnapshot);
2970
+ return path5__namespace.join(migrationsPath, latestSnapshot);
2541
2971
  } catch (error) {
2542
2972
  console.warn(`Error finding latest snapshot: ${error}`);
2543
2973
  return null;
2544
2974
  }
2545
2975
  }
2976
+ function applyMigrationOperations(snapshot, operations) {
2977
+ const updatedCollections = new Map(snapshot.collections);
2978
+ for (const collectionName of operations.collectionsToDelete) {
2979
+ updatedCollections.delete(collectionName);
2980
+ }
2981
+ for (const collection of operations.collectionsToCreate) {
2982
+ updatedCollections.set(collection.name, collection);
2983
+ }
2984
+ return {
2985
+ ...snapshot,
2986
+ collections: updatedCollections
2987
+ };
2988
+ }
2989
+ function loadSnapshotWithMigrations(config = {}) {
2990
+ const migrationsPath = config.migrationsPath;
2991
+ if (!migrationsPath) {
2992
+ return null;
2993
+ }
2994
+ if (fs3__namespace.existsSync(migrationsPath) && fs3__namespace.statSync(migrationsPath).isFile()) {
2995
+ try {
2996
+ const migrationContent = fs3__namespace.readFileSync(migrationsPath, "utf-8");
2997
+ return convertPocketBaseMigration(migrationContent);
2998
+ } catch (error) {
2999
+ console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
3000
+ return null;
3001
+ }
3002
+ }
3003
+ const latestSnapshotPath = findLatestSnapshot(migrationsPath);
3004
+ if (!latestSnapshotPath) {
3005
+ return null;
3006
+ }
3007
+ try {
3008
+ const migrationContent = fs3__namespace.readFileSync(latestSnapshotPath, "utf-8");
3009
+ let snapshot = convertPocketBaseMigration(migrationContent);
3010
+ const snapshotFilename = path5__namespace.basename(latestSnapshotPath);
3011
+ const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
3012
+ if (snapshotTimestamp) {
3013
+ const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
3014
+ for (const migrationFile of migrationFiles) {
3015
+ try {
3016
+ const migrationContent2 = fs3__namespace.readFileSync(migrationFile, "utf-8");
3017
+ const operations = parseMigrationOperations(migrationContent2);
3018
+ snapshot = applyMigrationOperations(snapshot, operations);
3019
+ } catch (error) {
3020
+ console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
3021
+ }
3022
+ }
3023
+ }
3024
+ return snapshot;
3025
+ } catch (error) {
3026
+ console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
3027
+ return null;
3028
+ }
3029
+ }
2546
3030
  function loadSnapshotIfExists(config = {}) {
2547
3031
  const migrationsPath = config.migrationsPath;
2548
3032
  if (!migrationsPath) {
2549
3033
  return null;
2550
3034
  }
2551
- if (fs2__namespace.existsSync(migrationsPath) && fs2__namespace.statSync(migrationsPath).isFile()) {
3035
+ if (fs3__namespace.existsSync(migrationsPath) && fs3__namespace.statSync(migrationsPath).isFile()) {
2552
3036
  try {
2553
- const migrationContent = fs2__namespace.readFileSync(migrationsPath, "utf-8");
3037
+ const migrationContent = fs3__namespace.readFileSync(migrationsPath, "utf-8");
2554
3038
  return convertPocketBaseMigration(migrationContent);
2555
3039
  } catch (error) {
2556
3040
  console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
@@ -2560,7 +3044,7 @@ function loadSnapshotIfExists(config = {}) {
2560
3044
  const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2561
3045
  if (latestSnapshotPath) {
2562
3046
  try {
2563
- const migrationContent = fs2__namespace.readFileSync(latestSnapshotPath, "utf-8");
3047
+ const migrationContent = fs3__namespace.readFileSync(latestSnapshotPath, "utf-8");
2564
3048
  return convertPocketBaseMigration(migrationContent);
2565
3049
  } catch (error) {
2566
3050
  console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
@@ -2569,100 +3053,9 @@ function loadSnapshotIfExists(config = {}) {
2569
3053
  }
2570
3054
  return null;
2571
3055
  }
2572
- function convertPocketBaseCollection(pbCollection) {
2573
- const fields = [];
2574
- const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
2575
- const authSystemFieldNames = ["email", "emailVisibility", "verified", "password", "tokenKey"];
2576
- if (pbCollection.fields && Array.isArray(pbCollection.fields)) {
2577
- for (const pbField of pbCollection.fields) {
2578
- if (pbField.system || systemFieldNames.includes(pbField.name)) {
2579
- continue;
2580
- }
2581
- if (pbCollection.type === "auth" && authSystemFieldNames.includes(pbField.name)) {
2582
- continue;
2583
- }
2584
- const field = {
2585
- name: pbField.name,
2586
- type: pbField.type,
2587
- required: pbField.required || false
2588
- };
2589
- if (pbField.options) {
2590
- field.options = pbField.options;
2591
- }
2592
- if (pbField.type === "relation") {
2593
- field.relation = {
2594
- collection: pbField.options?.collectionId || "",
2595
- cascadeDelete: pbField.options?.cascadeDelete || false,
2596
- maxSelect: pbField.options?.maxSelect,
2597
- minSelect: pbField.options?.minSelect
2598
- };
2599
- }
2600
- fields.push(field);
2601
- }
2602
- }
2603
- const schema = {
2604
- name: pbCollection.name,
2605
- type: pbCollection.type || "base",
2606
- fields
2607
- };
2608
- if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2609
- schema.indexes = pbCollection.indexes;
2610
- }
2611
- const rules = {};
2612
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2613
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2614
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2615
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2616
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2617
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2618
- if (Object.keys(rules).length > 0) {
2619
- schema.rules = rules;
2620
- schema.permissions = { ...rules };
2621
- }
2622
- return schema;
2623
- }
2624
- function convertPocketBaseMigration(migrationContent) {
2625
- try {
2626
- const snapshotMatch = migrationContent.match(/const\s+snapshot\s*=\s*(\[[\s\S]*?\]);/);
2627
- if (!snapshotMatch) {
2628
- throw new Error("Could not find snapshot array in migration file");
2629
- }
2630
- const snapshotArrayStr = snapshotMatch[1];
2631
- let snapshotArray;
2632
- try {
2633
- snapshotArray = new Function(`return ${snapshotArrayStr}`)();
2634
- } catch (parseError) {
2635
- throw new Error(`Failed to parse snapshot array: ${parseError}`);
2636
- }
2637
- if (!Array.isArray(snapshotArray)) {
2638
- throw new Error("Snapshot is not an array");
2639
- }
2640
- const collections = /* @__PURE__ */ new Map();
2641
- for (const pbCollection of snapshotArray) {
2642
- if (!pbCollection.name) {
2643
- console.warn("Skipping collection without name");
2644
- continue;
2645
- }
2646
- const schema = convertPocketBaseCollection(pbCollection);
2647
- collections.set(pbCollection.name, schema);
2648
- }
2649
- return {
2650
- version: SNAPSHOT_VERSION,
2651
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2652
- collections
2653
- };
2654
- } catch (error) {
2655
- throw new SnapshotError(
2656
- `Failed to convert PocketBase migration: ${error instanceof Error ? error.message : String(error)}`,
2657
- void 0,
2658
- "parse",
2659
- error instanceof Error ? error : void 0
2660
- );
2661
- }
2662
- }
2663
3056
  function loadBaseMigration(migrationPath) {
2664
3057
  try {
2665
- if (!fs2__namespace.existsSync(migrationPath)) {
3058
+ if (!fs3__namespace.existsSync(migrationPath)) {
2666
3059
  throw new SnapshotError(
2667
3060
  `Base migration file not found: ${migrationPath}
2668
3061
 
@@ -2673,7 +3066,7 @@ If the file exists in a different location, update the configuration.`,
2673
3066
  "read"
2674
3067
  );
2675
3068
  }
2676
- const migrationContent = fs2__namespace.readFileSync(migrationPath, "utf-8");
3069
+ const migrationContent = fs3__namespace.readFileSync(migrationPath, "utf-8");
2677
3070
  const snapshot = convertPocketBaseMigration(migrationContent);
2678
3071
  return snapshot;
2679
3072
  } catch (error) {
@@ -2709,14 +3102,14 @@ Please ensure PocketBase is properly set up by running 'yarn setup'.`,
2709
3102
  }
2710
3103
  }
2711
3104
  function getSnapshotVersion() {
2712
- return SNAPSHOT_VERSION;
3105
+ return SNAPSHOT_VERSION2;
2713
3106
  }
2714
3107
  function validateSnapshot(snapshot) {
2715
3108
  const issues = [];
2716
3109
  if (!snapshot.version) {
2717
3110
  issues.push("Missing version field");
2718
- } else if (compareVersions(snapshot.version, SNAPSHOT_VERSION) > 0) {
2719
- issues.push(`Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION}`);
3111
+ } else if (compareVersions(snapshot.version, SNAPSHOT_VERSION2) > 0) {
3112
+ issues.push(`Snapshot version ${snapshot.version} is newer than supported version ${SNAPSHOT_VERSION2}`);
2720
3113
  }
2721
3114
  if (!snapshot.timestamp) {
2722
3115
  issues.push("Missing timestamp field");
@@ -2935,6 +3328,9 @@ function compareFieldOptions(currentField, previousField) {
2935
3328
  for (const key of allKeys) {
2936
3329
  const currentValue = currentOptions[key];
2937
3330
  const previousValue = previousOptions[key];
3331
+ if (currentValue === void 0 && previousValue === void 0) {
3332
+ continue;
3333
+ }
2938
3334
  if (!areValuesEqual(currentValue, previousValue)) {
2939
3335
  changes.push({
2940
3336
  property: `options.${key}`,
@@ -2955,11 +3351,26 @@ function compareRelationConfigurations(currentField, previousField) {
2955
3351
  if (!currentRelation || !previousRelation) {
2956
3352
  return changes;
2957
3353
  }
2958
- if (currentRelation.collection !== previousRelation.collection) {
3354
+ const normalizeCollection = (collection) => {
3355
+ if (!collection) return collection;
3356
+ if (collection === "_pb_users_auth_") {
3357
+ return "Users";
3358
+ }
3359
+ const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
3360
+ if (nameMatch) {
3361
+ return nameMatch[1];
3362
+ }
3363
+ return collection;
3364
+ };
3365
+ const normalizedCurrent = normalizeCollection(currentRelation.collection);
3366
+ const normalizedPrevious = normalizeCollection(previousRelation.collection);
3367
+ if (normalizedCurrent !== normalizedPrevious) {
2959
3368
  changes.push({
2960
3369
  property: "relation.collection",
2961
- oldValue: previousRelation.collection,
2962
- newValue: currentRelation.collection
3370
+ oldValue: normalizedPrevious,
3371
+ // Use normalized value for clarity
3372
+ newValue: normalizedCurrent
3373
+ // Use normalized value for clarity
2963
3374
  });
2964
3375
  }
2965
3376
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -3332,10 +3743,10 @@ function mergeConfig4(config) {
3332
3743
  }
3333
3744
  function resolveMigrationDir(config) {
3334
3745
  const workspaceRoot = config.workspaceRoot || process.cwd();
3335
- if (path4__namespace.isAbsolute(config.migrationDir)) {
3746
+ if (path5__namespace.isAbsolute(config.migrationDir)) {
3336
3747
  return config.migrationDir;
3337
3748
  }
3338
- return path4__namespace.join(workspaceRoot, config.migrationDir);
3749
+ return path5__namespace.join(workspaceRoot, config.migrationDir);
3339
3750
  }
3340
3751
  function generateTimestamp(config) {
3341
3752
  if (config?.timestampGenerator) {
@@ -3393,9 +3804,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
3393
3804
  }
3394
3805
  function writeMigrationFile(migrationDir, filename, content) {
3395
3806
  try {
3396
- if (!fs2__namespace.existsSync(migrationDir)) {
3807
+ if (!fs3__namespace.existsSync(migrationDir)) {
3397
3808
  try {
3398
- fs2__namespace.mkdirSync(migrationDir, { recursive: true });
3809
+ fs3__namespace.mkdirSync(migrationDir, { recursive: true });
3399
3810
  } catch (error) {
3400
3811
  const fsError = error;
3401
3812
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
@@ -3416,15 +3827,15 @@ function writeMigrationFile(migrationDir, filename, content) {
3416
3827
  );
3417
3828
  }
3418
3829
  }
3419
- const filePath = path4__namespace.join(migrationDir, filename);
3420
- fs2__namespace.writeFileSync(filePath, content, "utf-8");
3830
+ const filePath = path5__namespace.join(migrationDir, filename);
3831
+ fs3__namespace.writeFileSync(filePath, content, "utf-8");
3421
3832
  return filePath;
3422
3833
  } catch (error) {
3423
3834
  if (error instanceof FileSystemError) {
3424
3835
  throw error;
3425
3836
  }
3426
3837
  const fsError = error;
3427
- const filePath = path4__namespace.join(migrationDir, filename);
3838
+ const filePath = path5__namespace.join(migrationDir, filename);
3428
3839
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
3429
3840
  throw new FileSystemError(
3430
3841
  `Permission denied writing migration file. Check file and directory permissions.`,
@@ -4168,8 +4579,8 @@ var DEFAULT_CONFIG5 = {
4168
4579
  };
4169
4580
  function findConfigFile(directory) {
4170
4581
  for (const fileName of CONFIG_FILE_NAMES) {
4171
- const filePath = path4__namespace.join(directory, fileName);
4172
- if (fs2__namespace.existsSync(filePath)) {
4582
+ const filePath = path5__namespace.join(directory, fileName);
4583
+ if (fs3__namespace.existsSync(filePath)) {
4173
4584
  return filePath;
4174
4585
  }
4175
4586
  }
@@ -4177,7 +4588,7 @@ function findConfigFile(directory) {
4177
4588
  }
4178
4589
  function loadJsonConfig(configPath) {
4179
4590
  try {
4180
- const content = fs2__namespace.readFileSync(configPath, "utf-8");
4591
+ const content = fs3__namespace.readFileSync(configPath, "utf-8");
4181
4592
  return JSON.parse(content);
4182
4593
  } catch (error) {
4183
4594
  if (error instanceof SyntaxError) {
@@ -4206,10 +4617,10 @@ async function loadJsConfig(configPath) {
4206
4617
  }
4207
4618
  }
4208
4619
  async function loadConfigFile(configPath) {
4209
- if (!fs2__namespace.existsSync(configPath)) {
4620
+ if (!fs3__namespace.existsSync(configPath)) {
4210
4621
  return null;
4211
4622
  }
4212
- const ext = path4__namespace.extname(configPath).toLowerCase();
4623
+ const ext = path5__namespace.extname(configPath).toLowerCase();
4213
4624
  if (ext === ".json") {
4214
4625
  return loadJsonConfig(configPath);
4215
4626
  } else if (ext === ".js" || ext === ".mjs") {
@@ -4276,10 +4687,10 @@ function validateConfig(config, configPath) {
4276
4687
  }
4277
4688
  const cwd = process.cwd();
4278
4689
  const possiblePaths = [
4279
- path4__namespace.resolve(cwd, config.schema.directory),
4280
- path4__namespace.resolve(cwd, "shared", config.schema.directory)
4690
+ path5__namespace.resolve(cwd, config.schema.directory),
4691
+ path5__namespace.resolve(cwd, "shared", config.schema.directory)
4281
4692
  ];
4282
- const schemaDir = possiblePaths.find((p) => fs2__namespace.existsSync(p));
4693
+ const schemaDir = possiblePaths.find((p) => fs3__namespace.existsSync(p));
4283
4694
  if (!schemaDir) {
4284
4695
  throw new ConfigurationError(`Schema directory not found. Tried: ${possiblePaths.join(", ")}`, configPath, [
4285
4696
  "schema.directory"
@@ -4291,15 +4702,15 @@ async function loadConfig(options = {}) {
4291
4702
  let configFilePath;
4292
4703
  const cwd = process.cwd();
4293
4704
  if (options.config) {
4294
- const explicitPath = path4__namespace.resolve(cwd, options.config);
4295
- if (!fs2__namespace.existsSync(explicitPath)) {
4705
+ const explicitPath = path5__namespace.resolve(cwd, options.config);
4706
+ if (!fs3__namespace.existsSync(explicitPath)) {
4296
4707
  throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
4297
4708
  }
4298
4709
  configFilePath = explicitPath;
4299
4710
  } else {
4300
- const searchDirs = [cwd, path4__namespace.join(cwd, "shared")];
4711
+ const searchDirs = [cwd, path5__namespace.join(cwd, "shared")];
4301
4712
  for (const dir of searchDirs) {
4302
- if (fs2__namespace.existsSync(dir)) {
4713
+ if (fs3__namespace.existsSync(dir)) {
4303
4714
  const found = findConfigFile(dir);
4304
4715
  if (found) {
4305
4716
  configFilePath = found;
@@ -4328,18 +4739,18 @@ async function loadConfig(options = {}) {
4328
4739
  function getSchemaDirectory(config) {
4329
4740
  const cwd = process.cwd();
4330
4741
  const possiblePaths = [
4331
- path4__namespace.resolve(cwd, config.schema.directory),
4332
- path4__namespace.resolve(cwd, "shared", config.schema.directory)
4742
+ path5__namespace.resolve(cwd, config.schema.directory),
4743
+ path5__namespace.resolve(cwd, "shared", config.schema.directory)
4333
4744
  ];
4334
- return possiblePaths.find((p) => fs2__namespace.existsSync(p)) || possiblePaths[0];
4745
+ return possiblePaths.find((p) => fs3__namespace.existsSync(p)) || possiblePaths[0];
4335
4746
  }
4336
4747
  function getMigrationsDirectory(config) {
4337
4748
  const cwd = process.cwd();
4338
4749
  const possiblePaths = [
4339
- path4__namespace.resolve(cwd, config.migrations.directory),
4340
- path4__namespace.resolve(cwd, "shared", config.migrations.directory)
4750
+ path5__namespace.resolve(cwd, config.migrations.directory),
4751
+ path5__namespace.resolve(cwd, "shared", config.migrations.directory)
4341
4752
  ];
4342
- return possiblePaths.find((p) => fs2__namespace.existsSync(p)) || possiblePaths[0];
4753
+ return possiblePaths.find((p) => fs3__namespace.existsSync(p)) || possiblePaths[0];
4343
4754
  }
4344
4755
  var currentVerbosity = "normal";
4345
4756
  function setVerbosity(level) {
@@ -4556,7 +4967,7 @@ async function executeGenerate(options) {
4556
4967
  const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
4557
4968
  logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
4558
4969
  logInfo("Loading previous snapshot...");
4559
- const previousSnapshot = loadSnapshotIfExists({
4970
+ const previousSnapshot = loadSnapshotWithMigrations({
4560
4971
  migrationsPath: migrationsDir,
4561
4972
  workspaceRoot: process.cwd()
4562
4973
  });
@@ -4583,7 +4994,7 @@ async function executeGenerate(options) {
4583
4994
  "Creating migration file...",
4584
4995
  () => Promise.resolve(generate(diff, migrationsDir))
4585
4996
  );
4586
- logSuccess(`Migration file created: ${path4__namespace.basename(migrationPath)}`);
4997
+ logSuccess(`Migration file created: ${path5__namespace.basename(migrationPath)}`);
4587
4998
  logSection("\u2705 Next Steps");
4588
4999
  console.log();
4589
5000
  console.log(" 1. Review the generated migration file:");
@@ -4749,7 +5160,7 @@ async function executeStatus(options) {
4749
5160
  const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
4750
5161
  logSuccess(`Found ${currentSchema.collections.size} collection(s) in schema`);
4751
5162
  logInfo("Loading previous snapshot...");
4752
- const previousSnapshot = loadSnapshotIfExists({
5163
+ const previousSnapshot = loadSnapshotWithMigrations({
4753
5164
  migrationsPath: migrationsDir,
4754
5165
  workspaceRoot: process.cwd()
4755
5166
  });
@@ -4860,11 +5271,18 @@ exports.MigrationGenerationError = MigrationGenerationError;
4860
5271
  exports.MigrationGenerator = MigrationGenerator;
4861
5272
  exports.POCKETBASE_FIELD_TYPES = POCKETBASE_FIELD_TYPES;
4862
5273
  exports.PermissionTemplates = PermissionTemplates;
5274
+ exports.ProjectCollection = ProjectCollection;
5275
+ exports.ProjectInputSchema = ProjectInputSchema;
5276
+ exports.ProjectSchema = ProjectSchema;
5277
+ exports.RelationField = RelationField;
5278
+ exports.RelationsField = RelationsField;
4863
5279
  exports.SchemaAnalyzer = SchemaAnalyzer;
4864
5280
  exports.SchemaParsingError = SchemaParsingError;
4865
5281
  exports.SnapshotError = SnapshotError;
4866
5282
  exports.SnapshotManager = SnapshotManager;
4867
5283
  exports.StatusEnum = StatusEnum;
5284
+ exports.UserCollection = UserCollection;
5285
+ exports.UserCollectionSchema = UserCollectionSchema;
4868
5286
  exports.UserInputSchema = UserInputSchema;
4869
5287
  exports.UserMutator = UserMutator;
4870
5288
  exports.UserSchema = UserSchema;
@@ -4887,6 +5305,7 @@ exports.convertZodSchemaToCollectionSchema = convertZodSchemaToCollectionSchema;
4887
5305
  exports.createMigrationFileStructure = createMigrationFileStructure;
4888
5306
  exports.createPermissions = createPermissions;
4889
5307
  exports.dateField = dateField;
5308
+ exports.defineCollection = defineCollection;
4890
5309
  exports.detectDestructiveChanges = detectDestructiveChanges;
4891
5310
  exports.detectFieldChanges = detectFieldChanges;
4892
5311
  exports.discoverSchemaFiles = discoverSchemaFiles;
@@ -4955,6 +5374,7 @@ exports.loadBaseMigration = loadBaseMigration;
4955
5374
  exports.loadConfig = loadConfig;
4956
5375
  exports.loadSnapshot = loadSnapshot;
4957
5376
  exports.loadSnapshotIfExists = loadSnapshotIfExists;
5377
+ exports.loadSnapshotWithMigrations = loadSnapshotWithMigrations;
4958
5378
  exports.logError = logError;
4959
5379
  exports.logInfo = logInfo;
4960
5380
  exports.logSection = logSection;
@@ -4976,8 +5396,6 @@ exports.numberField = numberField;
4976
5396
  exports.omitImageFilesSchema = omitImageFilesSchema;
4977
5397
  exports.parseSchemaFiles = parseSchemaFiles;
4978
5398
  exports.pluralize = pluralize;
4979
- exports.relationField = relationField;
4980
- exports.relationsField = relationsField;
4981
5399
  exports.requiresForceFlag = requiresForceFlag;
4982
5400
  exports.resolveTargetCollection = resolveTargetCollection;
4983
5401
  exports.resolveTemplate = resolveTemplate;