pocketbase-zod-schema 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +329 -99
  3. package/dist/cli/index.cjs +577 -152
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.js +575 -150
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/migrate.cjs +595 -153
  8. package/dist/cli/migrate.cjs.map +1 -1
  9. package/dist/cli/migrate.js +592 -151
  10. package/dist/cli/migrate.js.map +1 -1
  11. package/dist/cli/utils/index.cjs +1 -1
  12. package/dist/cli/utils/index.cjs.map +1 -1
  13. package/dist/cli/utils/index.js +1 -1
  14. package/dist/cli/utils/index.js.map +1 -1
  15. package/dist/index.cjs +688 -231
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +3 -3
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +685 -230
  20. package/dist/index.js.map +1 -1
  21. package/dist/migration/analyzer.cjs +101 -28
  22. package/dist/migration/analyzer.cjs.map +1 -1
  23. package/dist/migration/analyzer.js +101 -28
  24. package/dist/migration/analyzer.js.map +1 -1
  25. package/dist/migration/diff.cjs +21 -3
  26. package/dist/migration/diff.cjs.map +1 -1
  27. package/dist/migration/diff.js +21 -3
  28. package/dist/migration/diff.js.map +1 -1
  29. package/dist/migration/generator.cjs +60 -25
  30. package/dist/migration/generator.cjs.map +1 -1
  31. package/dist/migration/generator.d.cts +9 -5
  32. package/dist/migration/generator.d.ts +9 -5
  33. package/dist/migration/generator.js +60 -25
  34. package/dist/migration/generator.js.map +1 -1
  35. package/dist/migration/index.cjs +614 -171
  36. package/dist/migration/index.cjs.map +1 -1
  37. package/dist/migration/index.d.cts +1 -1
  38. package/dist/migration/index.d.ts +1 -1
  39. package/dist/migration/index.js +613 -171
  40. package/dist/migration/index.js.map +1 -1
  41. package/dist/migration/snapshot.cjs +432 -117
  42. package/dist/migration/snapshot.cjs.map +1 -1
  43. package/dist/migration/snapshot.d.cts +34 -12
  44. package/dist/migration/snapshot.d.ts +34 -12
  45. package/dist/migration/snapshot.js +430 -116
  46. package/dist/migration/snapshot.js.map +1 -1
  47. package/dist/migration/utils/index.cjs +19 -17
  48. package/dist/migration/utils/index.cjs.map +1 -1
  49. package/dist/migration/utils/index.d.cts +3 -1
  50. package/dist/migration/utils/index.d.ts +3 -1
  51. package/dist/migration/utils/index.js +19 -17
  52. package/dist/migration/utils/index.js.map +1 -1
  53. package/dist/mutator.cjs +9 -11
  54. package/dist/mutator.cjs.map +1 -1
  55. package/dist/mutator.d.cts +5 -9
  56. package/dist/mutator.d.ts +5 -9
  57. package/dist/mutator.js +9 -11
  58. package/dist/mutator.js.map +1 -1
  59. package/dist/schema.cjs +54 -23
  60. package/dist/schema.cjs.map +1 -1
  61. package/dist/schema.d.cts +94 -12
  62. package/dist/schema.d.ts +94 -12
  63. package/dist/schema.js +54 -24
  64. package/dist/schema.js.map +1 -1
  65. package/dist/types.d.cts +1 -1
  66. package/dist/types.d.ts +1 -1
  67. package/dist/{user-jS1aYoeD.d.cts → user-_AM523hb.d.cts} +6 -6
  68. package/dist/{user-jS1aYoeD.d.ts → user-_AM523hb.d.ts} +6 -6
  69. package/package.json +2 -4
package/dist/cli/index.js CHANGED
@@ -1,10 +1,39 @@
1
- import * as path4 from 'path';
2
- import * as fs4 from 'fs';
1
+ import * as path5 from 'path';
2
+ import * as fs5 from 'fs';
3
3
  import { z } from 'zod';
4
4
  import chalk from 'chalk';
5
5
  import ora from 'ora';
6
6
 
7
7
  // src/cli/commands/generate.ts
8
+ ({
9
+ id: z.string().describe("unique id"),
10
+ collectionId: z.string().describe("collection id"),
11
+ collectionName: z.string().describe("collection name"),
12
+ expand: z.record(z.any()).describe("expandable fields")
13
+ });
14
+ ({
15
+ created: z.string().describe("creation timestamp"),
16
+ updated: z.string().describe("last update timestamp")
17
+ });
18
+ ({
19
+ thumbnailURL: z.string().optional(),
20
+ imageFiles: z.array(z.string())
21
+ });
22
+ ({
23
+ imageFiles: z.array(z.instanceof(File))
24
+ });
25
+ var RELATION_METADATA_KEY = "__pocketbase_relation__";
26
+ function extractRelationMetadata(description) {
27
+ if (!description) return null;
28
+ try {
29
+ const parsed = JSON.parse(description);
30
+ if (parsed[RELATION_METADATA_KEY]) {
31
+ return parsed[RELATION_METADATA_KEY];
32
+ }
33
+ } catch {
34
+ }
35
+ return null;
36
+ }
8
37
 
9
38
  // src/migration/errors.ts
10
39
  var MigrationError = class _MigrationError extends Error {
@@ -103,10 +132,10 @@ var FileSystemError = class _FileSystemError extends MigrationError {
103
132
  operation;
104
133
  code;
105
134
  originalError;
106
- constructor(message, path6, operation, code, originalError) {
135
+ constructor(message, path7, operation, code, originalError) {
107
136
  super(message);
108
137
  this.name = "FileSystemError";
109
- this.path = path6;
138
+ this.path = path7;
110
139
  this.operation = operation;
111
140
  this.code = code;
112
141
  this.originalError = originalError;
@@ -169,7 +198,7 @@ Cause: ${this.originalError.message}`);
169
198
  }
170
199
  };
171
200
 
172
- // src/schema/permission-templates.ts
201
+ // src/utils/permission-templates.ts
173
202
  var PermissionTemplates = {
174
203
  /**
175
204
  * Public access - anyone can perform all operations
@@ -828,26 +857,28 @@ function getMaxSelect(fieldName, zodType) {
828
857
  return 1;
829
858
  }
830
859
  function getMinSelect(fieldName, zodType) {
831
- if (!isMultipleRelationField(fieldName, zodType)) {
832
- return void 0;
833
- }
834
- let unwrappedType = zodType;
835
- if (zodType instanceof z.ZodOptional) {
836
- unwrappedType = zodType._def.innerType;
837
- }
838
- if (unwrappedType instanceof z.ZodNullable) {
839
- unwrappedType = unwrappedType._def.innerType;
840
- }
841
- if (unwrappedType instanceof z.ZodDefault) {
842
- unwrappedType = unwrappedType._def.innerType;
860
+ if (isSingleRelationField(fieldName, zodType)) {
861
+ return 0;
843
862
  }
844
- if (unwrappedType instanceof z.ZodArray) {
845
- const arrayDef = unwrappedType._def;
846
- if (arrayDef.minLength) {
847
- return arrayDef.minLength.value;
863
+ if (isMultipleRelationField(fieldName, zodType)) {
864
+ let unwrappedType = zodType;
865
+ if (zodType instanceof z.ZodOptional) {
866
+ unwrappedType = zodType._def.innerType;
867
+ }
868
+ if (unwrappedType instanceof z.ZodNullable) {
869
+ unwrappedType = unwrappedType._def.innerType;
870
+ }
871
+ if (unwrappedType instanceof z.ZodDefault) {
872
+ unwrappedType = unwrappedType._def.innerType;
873
+ }
874
+ if (unwrappedType instanceof z.ZodArray) {
875
+ const arrayDef = unwrappedType._def;
876
+ if (arrayDef.minLength) {
877
+ return arrayDef.minLength.value;
878
+ }
848
879
  }
849
880
  }
850
- return void 0;
881
+ return 0;
851
882
  }
852
883
  function mapZodStringType(zodType) {
853
884
  const checks = zodType._def.checks || [];
@@ -1045,20 +1076,20 @@ function mergeConfig(config) {
1045
1076
  }
1046
1077
  function resolveSchemaDir(config) {
1047
1078
  const workspaceRoot = config.workspaceRoot || process.cwd();
1048
- if (path4.isAbsolute(config.schemaDir)) {
1079
+ if (path5.isAbsolute(config.schemaDir)) {
1049
1080
  return config.schemaDir;
1050
1081
  }
1051
- return path4.join(workspaceRoot, config.schemaDir);
1082
+ return path5.join(workspaceRoot, config.schemaDir);
1052
1083
  }
1053
1084
  function discoverSchemaFiles(config) {
1054
1085
  const normalizedConfig = typeof config === "string" ? { schemaDir: config } : config;
1055
1086
  const mergedConfig = mergeConfig(normalizedConfig);
1056
1087
  const schemaDir = resolveSchemaDir(normalizedConfig);
1057
1088
  try {
1058
- if (!fs4.existsSync(schemaDir)) {
1089
+ if (!fs5.existsSync(schemaDir)) {
1059
1090
  throw new FileSystemError(`Schema directory not found: ${schemaDir}`, schemaDir, "access", "ENOENT");
1060
1091
  }
1061
- const files = fs4.readdirSync(schemaDir);
1092
+ const files = fs5.readdirSync(schemaDir);
1062
1093
  const schemaFiles = files.filter((file) => {
1063
1094
  const hasValidExtension = mergedConfig.includeExtensions.some((ext) => file.endsWith(ext));
1064
1095
  if (!hasValidExtension) return false;
@@ -1074,7 +1105,7 @@ function discoverSchemaFiles(config) {
1074
1105
  });
1075
1106
  return schemaFiles.map((file) => {
1076
1107
  const ext = mergedConfig.includeExtensions.find((ext2) => file.endsWith(ext2)) || ".ts";
1077
- return path4.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
1108
+ return path5.join(schemaDir, file.replace(new RegExp(`\\${ext}$`), ""));
1078
1109
  });
1079
1110
  } catch (error) {
1080
1111
  if (error instanceof FileSystemError) {
@@ -1105,13 +1136,32 @@ async function importSchemaModule(filePath, config) {
1105
1136
  if (config?.pathTransformer) {
1106
1137
  importPath = config.pathTransformer(filePath);
1107
1138
  }
1108
- if (!importPath.endsWith(".js")) {
1109
- importPath = `${importPath}.js`;
1139
+ let resolvedPath = null;
1140
+ const jsPath = `${importPath}.js`;
1141
+ const tsPath = `${importPath}.ts`;
1142
+ if (fs5.existsSync(jsPath)) {
1143
+ resolvedPath = jsPath;
1144
+ } else if (fs5.existsSync(tsPath)) {
1145
+ resolvedPath = tsPath;
1146
+ } else {
1147
+ resolvedPath = jsPath;
1110
1148
  }
1111
- const fileUrl = new URL(`file://${path4.resolve(importPath)}`);
1149
+ const fileUrl = new URL(`file://${path5.resolve(resolvedPath)}`);
1112
1150
  const module = await import(fileUrl.href);
1113
1151
  return module;
1114
1152
  } catch (error) {
1153
+ const tsPath = `${filePath}.ts`;
1154
+ const isTypeScriptFile = fs5.existsSync(tsPath);
1155
+ if (isTypeScriptFile) {
1156
+ throw new SchemaParsingError(
1157
+ `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
1158
+ Please either:
1159
+ 1. Compile your schema files to JavaScript first, or
1160
+ 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")`,
1161
+ filePath,
1162
+ error
1163
+ );
1164
+ }
1115
1165
  throw new SchemaParsingError(
1116
1166
  `Failed to import schema module. Make sure the schema files are compiled to JavaScript.`,
1117
1167
  filePath,
@@ -1120,7 +1170,7 @@ async function importSchemaModule(filePath, config) {
1120
1170
  }
1121
1171
  }
1122
1172
  function getCollectionNameFromFile(filePath) {
1123
- const filename = path4.basename(filePath).replace(/\.(ts|js)$/, "");
1173
+ const filename = path5.basename(filePath).replace(/\.(ts|js)$/, "");
1124
1174
  return toCollectionName(filename);
1125
1175
  }
1126
1176
  function extractSchemaDefinitions(module, patterns = ["Schema", "InputSchema"]) {
@@ -1174,7 +1224,17 @@ function buildFieldDefinition(fieldName, zodType) {
1174
1224
  required,
1175
1225
  options
1176
1226
  };
1177
- if (isRelationField(fieldName, zodType)) {
1227
+ const relationMetadata = extractRelationMetadata(zodType.description);
1228
+ if (relationMetadata) {
1229
+ fieldDef.type = "relation";
1230
+ fieldDef.relation = {
1231
+ collection: relationMetadata.collection,
1232
+ maxSelect: relationMetadata.maxSelect,
1233
+ minSelect: relationMetadata.minSelect,
1234
+ cascadeDelete: relationMetadata.cascadeDelete
1235
+ };
1236
+ fieldDef.options = void 0;
1237
+ } else if (isRelationField(fieldName, zodType)) {
1178
1238
  fieldDef.type = "relation";
1179
1239
  const targetCollection = resolveTargetCollection(fieldName);
1180
1240
  const maxSelect = getMaxSelect(fieldName, zodType);
@@ -1186,6 +1246,13 @@ function buildFieldDefinition(fieldName, zodType) {
1186
1246
  cascadeDelete: false
1187
1247
  // Default to false, can be configured later
1188
1248
  };
1249
+ if (fieldDef.options) {
1250
+ const { min, max, pattern, ...relationSafeOptions } = fieldDef.options;
1251
+ console.log("min", min);
1252
+ console.log("max", max);
1253
+ console.log("pattern", pattern);
1254
+ fieldDef.options = Object.keys(relationSafeOptions).length > 0 ? relationSafeOptions : void 0;
1255
+ }
1189
1256
  }
1190
1257
  return fieldDef;
1191
1258
  }
@@ -1238,11 +1305,12 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
1238
1305
  fields,
1239
1306
  indexes,
1240
1307
  rules: {
1241
- listRule: null,
1242
- viewRule: null,
1243
- createRule: null,
1244
- updateRule: null,
1245
- deleteRule: null
1308
+ listRule: permissions?.listRule ?? null,
1309
+ viewRule: permissions?.viewRule ?? null,
1310
+ createRule: permissions?.createRule ?? null,
1311
+ updateRule: permissions?.updateRule ?? null,
1312
+ deleteRule: permissions?.deleteRule ?? null,
1313
+ manageRule: permissions?.manageRule ?? null
1246
1314
  },
1247
1315
  permissions
1248
1316
  };
@@ -1266,7 +1334,12 @@ async function buildSchemaDefinition(config) {
1266
1334
  if (normalizedConfig.pathTransformer) {
1267
1335
  importPath = normalizedConfig.pathTransformer(filePath);
1268
1336
  } else if (mergedConfig.useCompiledFiles) {
1269
- importPath = filePath.replace(/\/src\//, "/dist/");
1337
+ const distPath = filePath.replace(/\/src\//, "/dist/");
1338
+ if (fs5.existsSync(`${distPath}.js`) || fs5.existsSync(`${distPath}.mjs`)) {
1339
+ importPath = distPath;
1340
+ } else {
1341
+ importPath = filePath;
1342
+ }
1270
1343
  }
1271
1344
  const module = await importSchemaModule(importPath, normalizedConfig);
1272
1345
  const schemas = extractSchemaDefinitions(module, mergedConfig.schemaPatterns);
@@ -1439,6 +1512,9 @@ function compareFieldOptions(currentField, previousField) {
1439
1512
  for (const key of allKeys) {
1440
1513
  const currentValue = currentOptions[key];
1441
1514
  const previousValue = previousOptions[key];
1515
+ if (currentValue === void 0 && previousValue === void 0) {
1516
+ continue;
1517
+ }
1442
1518
  if (!areValuesEqual(currentValue, previousValue)) {
1443
1519
  changes.push({
1444
1520
  property: `options.${key}`,
@@ -1459,11 +1535,26 @@ function compareRelationConfigurations(currentField, previousField) {
1459
1535
  if (!currentRelation || !previousRelation) {
1460
1536
  return changes;
1461
1537
  }
1462
- if (currentRelation.collection !== previousRelation.collection) {
1538
+ const normalizeCollection = (collection) => {
1539
+ if (!collection) return collection;
1540
+ if (collection === "_pb_users_auth_") {
1541
+ return "Users";
1542
+ }
1543
+ const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
1544
+ if (nameMatch) {
1545
+ return nameMatch[1];
1546
+ }
1547
+ return collection;
1548
+ };
1549
+ const normalizedCurrent = normalizeCollection(currentRelation.collection);
1550
+ const normalizedPrevious = normalizeCollection(previousRelation.collection);
1551
+ if (normalizedCurrent !== normalizedPrevious) {
1463
1552
  changes.push({
1464
1553
  property: "relation.collection",
1465
- oldValue: previousRelation.collection,
1466
- newValue: currentRelation.collection
1554
+ oldValue: normalizedPrevious,
1555
+ // Use normalized value for clarity
1556
+ newValue: normalizedCurrent
1557
+ // Use normalized value for clarity
1467
1558
  });
1468
1559
  }
1469
1560
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -1664,10 +1755,8 @@ function compare(currentSchema, previousSnapshot, config) {
1664
1755
  var DEFAULT_TEMPLATE = `/// <reference path="{{TYPES_PATH}}" />
1665
1756
  migrate((app) => {
1666
1757
  {{UP_CODE}}
1667
- return true;
1668
1758
  }, (app) => {
1669
1759
  {{DOWN_CODE}}
1670
- return true;
1671
1760
  });
1672
1761
  `;
1673
1762
  var DEFAULT_CONFIG3 = {
@@ -1685,10 +1774,10 @@ function mergeConfig3(config) {
1685
1774
  }
1686
1775
  function resolveMigrationDir(config) {
1687
1776
  const workspaceRoot = config.workspaceRoot || process.cwd();
1688
- if (path4.isAbsolute(config.migrationDir)) {
1777
+ if (path5.isAbsolute(config.migrationDir)) {
1689
1778
  return config.migrationDir;
1690
1779
  }
1691
- return path4.join(workspaceRoot, config.migrationDir);
1780
+ return path5.join(workspaceRoot, config.migrationDir);
1692
1781
  }
1693
1782
  function generateTimestamp(config) {
1694
1783
  if (config?.timestampGenerator) {
@@ -1746,9 +1835,9 @@ function createMigrationFileStructure(upCode, downCode, config) {
1746
1835
  }
1747
1836
  function writeMigrationFile(migrationDir, filename, content) {
1748
1837
  try {
1749
- if (!fs4.existsSync(migrationDir)) {
1838
+ if (!fs5.existsSync(migrationDir)) {
1750
1839
  try {
1751
- fs4.mkdirSync(migrationDir, { recursive: true });
1840
+ fs5.mkdirSync(migrationDir, { recursive: true });
1752
1841
  } catch (error) {
1753
1842
  const fsError = error;
1754
1843
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
@@ -1769,15 +1858,15 @@ function writeMigrationFile(migrationDir, filename, content) {
1769
1858
  );
1770
1859
  }
1771
1860
  }
1772
- const filePath = path4.join(migrationDir, filename);
1773
- fs4.writeFileSync(filePath, content, "utf-8");
1861
+ const filePath = path5.join(migrationDir, filename);
1862
+ fs5.writeFileSync(filePath, content, "utf-8");
1774
1863
  return filePath;
1775
1864
  } catch (error) {
1776
1865
  if (error instanceof FileSystemError) {
1777
1866
  throw error;
1778
1867
  }
1779
1868
  const fsError = error;
1780
- const filePath = path4.join(migrationDir, filename);
1869
+ const filePath = path5.join(migrationDir, filename);
1781
1870
  if (fsError.code === "EACCES" || fsError.code === "EPERM") {
1782
1871
  throw new FileSystemError(
1783
1872
  `Permission denied writing migration file. Check file and directory permissions.`,
@@ -1832,7 +1921,8 @@ function generateFieldDefinitionObject(field) {
1832
1921
  }
1833
1922
  }
1834
1923
  if (field.relation) {
1835
- const collectionIdPlaceholder = field.relation.collection === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
1924
+ const isUsersCollection = field.relation.collection.toLowerCase() === "users";
1925
+ const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
1836
1926
  parts.push(` collectionId: ${collectionIdPlaceholder}`);
1837
1927
  if (field.relation.maxSelect !== void 0) {
1838
1928
  parts.push(` maxSelect: ${field.relation.maxSelect}`);
@@ -1916,7 +2006,7 @@ function generateIndexesArray(indexes) {
1916
2006
  ${indexStrings.join(",\n ")},
1917
2007
  ]`;
1918
2008
  }
1919
- function generateCollectionCreation(collection, varName = "collection") {
2009
+ function generateCollectionCreation(collection, varName = "collection", isLast = false) {
1920
2010
  const lines = [];
1921
2011
  lines.push(` const ${varName} = new Collection({`);
1922
2012
  lines.push(` name: "${collection.name}",`);
@@ -1932,7 +2022,7 @@ function generateCollectionCreation(collection, varName = "collection") {
1932
2022
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
1933
2023
  lines.push(` });`);
1934
2024
  lines.push(``);
1935
- lines.push(` app.save(${varName});`);
2025
+ lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
1936
2026
  return lines.join("\n");
1937
2027
  }
1938
2028
  function getFieldConstructorName(fieldType) {
@@ -1963,7 +2053,8 @@ function generateFieldConstructorOptions(field) {
1963
2053
  }
1964
2054
  }
1965
2055
  if (field.relation && field.type === "relation") {
1966
- const collectionIdPlaceholder = field.relation.collection === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2056
+ const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2057
+ const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
1967
2058
  parts.push(` collectionId: ${collectionIdPlaceholder}`);
1968
2059
  if (field.relation.maxSelect !== void 0) {
1969
2060
  parts.push(` maxSelect: ${field.relation.maxSelect}`);
@@ -1977,7 +2068,7 @@ function generateFieldConstructorOptions(field) {
1977
2068
  }
1978
2069
  return parts.join(",\n");
1979
2070
  }
1980
- function generateFieldAddition(collectionName, field, varName) {
2071
+ function generateFieldAddition(collectionName, field, varName, isLast = false) {
1981
2072
  const lines = [];
1982
2073
  const constructorName = getFieldConstructorName(field.type);
1983
2074
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
@@ -1987,10 +2078,10 @@ function generateFieldAddition(collectionName, field, varName) {
1987
2078
  lines.push(generateFieldConstructorOptions(field));
1988
2079
  lines.push(` }));`);
1989
2080
  lines.push(``);
1990
- lines.push(` app.save(${collectionVar});`);
2081
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
1991
2082
  return lines.join("\n");
1992
2083
  }
1993
- function generateFieldModification(collectionName, modification, varName) {
2084
+ function generateFieldModification(collectionName, modification, varName, isLast = false) {
1994
2085
  const lines = [];
1995
2086
  const collectionVar = varName || `collection_${collectionName}_${modification.fieldName}`;
1996
2087
  const fieldVar = `${collectionVar}_field`;
@@ -2004,7 +2095,8 @@ function generateFieldModification(collectionName, modification, varName) {
2004
2095
  } else if (change.property.startsWith("relation.")) {
2005
2096
  const relationKey = change.property.replace("relation.", "");
2006
2097
  if (relationKey === "collection") {
2007
- const collectionIdValue = change.newValue === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${change.newValue}").id`;
2098
+ const isUsersCollection = String(change.newValue).toLowerCase() === "users";
2099
+ const collectionIdValue = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${change.newValue}").id`;
2008
2100
  lines.push(` ${fieldVar}.collectionId = ${collectionIdValue};`);
2009
2101
  } else {
2010
2102
  lines.push(` ${fieldVar}.${relationKey} = ${formatValue(change.newValue)};`);
@@ -2014,10 +2106,10 @@ function generateFieldModification(collectionName, modification, varName) {
2014
2106
  }
2015
2107
  }
2016
2108
  lines.push(``);
2017
- lines.push(` app.save(${collectionVar});`);
2109
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2018
2110
  return lines.join("\n");
2019
2111
  }
2020
- function generateFieldDeletion(collectionName, fieldName, varName) {
2112
+ function generateFieldDeletion(collectionName, fieldName, varName, isLast = false) {
2021
2113
  const lines = [];
2022
2114
  const collectionVar = varName || `collection_${collectionName}_${fieldName}`;
2023
2115
  const fieldVar = `${collectionVar}_field`;
@@ -2026,18 +2118,18 @@ function generateFieldDeletion(collectionName, fieldName, varName) {
2026
2118
  lines.push(``);
2027
2119
  lines.push(` ${collectionVar}.fields.remove(${fieldVar}.id);`);
2028
2120
  lines.push(``);
2029
- lines.push(` app.save(${collectionVar});`);
2121
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2030
2122
  return lines.join("\n");
2031
2123
  }
2032
- function generateIndexAddition(collectionName, index, varName) {
2124
+ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2033
2125
  const lines = [];
2034
2126
  const collectionVar = varName || `collection_${collectionName}_idx`;
2035
2127
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2036
2128
  lines.push(` ${collectionVar}.indexes.push("${index}");`);
2037
- lines.push(` app.save(${collectionVar});`);
2129
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2038
2130
  return lines.join("\n");
2039
2131
  }
2040
- function generateIndexRemoval(collectionName, index, varName) {
2132
+ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2041
2133
  const lines = [];
2042
2134
  const collectionVar = varName || `collection_${collectionName}_idx`;
2043
2135
  const indexVar = `${collectionVar}_indexToRemove`;
@@ -2046,29 +2138,29 @@ function generateIndexRemoval(collectionName, index, varName) {
2046
2138
  lines.push(` if (${indexVar} !== -1) {`);
2047
2139
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2048
2140
  lines.push(` }`);
2049
- lines.push(` app.save(${collectionVar});`);
2141
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2050
2142
  return lines.join("\n");
2051
2143
  }
2052
- function generateRuleUpdate(collectionName, ruleType, newValue, varName) {
2144
+ function generateRuleUpdate(collectionName, ruleType, newValue, varName, isLast = false) {
2053
2145
  const lines = [];
2054
2146
  const collectionVar = varName || `collection_${collectionName}_${ruleType}`;
2055
2147
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2056
2148
  lines.push(` ${collectionVar}.${ruleType} = ${formatValue(newValue)};`);
2057
- lines.push(` app.save(${collectionVar});`);
2149
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2058
2150
  return lines.join("\n");
2059
2151
  }
2060
- function generatePermissionUpdate(collectionName, ruleType, newValue, varName) {
2152
+ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, isLast = false) {
2061
2153
  const lines = [];
2062
2154
  const collectionVar = varName || `collection_${collectionName}_${ruleType}`;
2063
2155
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2064
2156
  lines.push(` ${collectionVar}.${ruleType} = ${formatValue(newValue)};`);
2065
- lines.push(` app.save(${collectionVar});`);
2157
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2066
2158
  return lines.join("\n");
2067
2159
  }
2068
- function generateCollectionDeletion(collectionName, varName = "collection") {
2160
+ function generateCollectionDeletion(collectionName, varName = "collection", isLast = false) {
2069
2161
  const lines = [];
2070
2162
  lines.push(` const ${varName} = app.findCollectionByNameOrId("${collectionName}");`);
2071
- lines.push(` app.delete(${varName});`);
2163
+ lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2072
2164
  return lines.join("\n");
2073
2165
  }
2074
2166
  function generateUpMigration(diff) {
@@ -2160,7 +2252,24 @@ function generateUpMigration(diff) {
2160
2252
  lines.push(` // No changes detected`);
2161
2253
  lines.push(``);
2162
2254
  }
2163
- return lines.join("\n");
2255
+ let code = lines.join("\n");
2256
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2257
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2258
+ const saveMatches = [...code.matchAll(savePattern)];
2259
+ const deleteMatches = [...code.matchAll(deletePattern)];
2260
+ const allMatches = [
2261
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2262
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2263
+ ].sort((a, b) => b.index - a.index);
2264
+ if (allMatches.length > 0) {
2265
+ const lastMatch = allMatches[0];
2266
+ if (lastMatch.type === "save") {
2267
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.save(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
2268
+ } else {
2269
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.delete(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
2270
+ }
2271
+ }
2272
+ return code;
2164
2273
  }
2165
2274
  function generateDownMigration(diff) {
2166
2275
  const lines = [];
@@ -2262,7 +2371,24 @@ function generateDownMigration(diff) {
2262
2371
  lines.push(` // No changes to revert`);
2263
2372
  lines.push(``);
2264
2373
  }
2265
- return lines.join("\n");
2374
+ let code = lines.join("\n");
2375
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2376
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2377
+ const saveMatches = [...code.matchAll(savePattern)];
2378
+ const deleteMatches = [...code.matchAll(deletePattern)];
2379
+ const allMatches = [
2380
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2381
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2382
+ ].sort((a, b) => b.index - a.index);
2383
+ if (allMatches.length > 0) {
2384
+ const lastMatch = allMatches[0];
2385
+ if (lastMatch.type === "save") {
2386
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.save(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
2387
+ } else {
2388
+ code = code.substring(0, lastMatch.match.index) + lastMatch.match[1] + "return app.delete(" + lastMatch.match[2] + ");" + code.substring(lastMatch.match.index + lastMatch.match[0].length);
2389
+ }
2390
+ }
2391
+ return code;
2266
2392
  }
2267
2393
  function generate(diff, config) {
2268
2394
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
@@ -2285,57 +2411,18 @@ function generate(diff, config) {
2285
2411
  );
2286
2412
  }
2287
2413
  }
2414
+
2415
+ // src/migration/pocketbase-converter.ts
2288
2416
  var SNAPSHOT_VERSION = "1.0.0";
2289
- ({
2290
- workspaceRoot: process.cwd()});
2291
- function findLatestSnapshot(migrationsPath) {
2292
- try {
2293
- if (!fs4.existsSync(migrationsPath)) {
2294
- return null;
2295
- }
2296
- const files = fs4.readdirSync(migrationsPath);
2297
- const snapshotFiles = files.filter(
2298
- (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2299
- );
2300
- if (snapshotFiles.length === 0) {
2301
- return null;
2302
- }
2303
- snapshotFiles.sort().reverse();
2304
- const latestSnapshot = snapshotFiles[0];
2305
- if (!latestSnapshot) {
2306
- return null;
2307
- }
2308
- return path4.join(migrationsPath, latestSnapshot);
2309
- } catch (error) {
2310
- console.warn(`Error finding latest snapshot: ${error}`);
2311
- return null;
2417
+ function resolveCollectionIdToName(collectionId) {
2418
+ if (collectionId === "_pb_users_auth_") {
2419
+ return "Users";
2312
2420
  }
2313
- }
2314
- function loadSnapshotIfExists(config = {}) {
2315
- const migrationsPath = config.migrationsPath;
2316
- if (!migrationsPath) {
2317
- return null;
2421
+ const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2422
+ if (nameMatch) {
2423
+ return nameMatch[1];
2318
2424
  }
2319
- if (fs4.existsSync(migrationsPath) && fs4.statSync(migrationsPath).isFile()) {
2320
- try {
2321
- const migrationContent = fs4.readFileSync(migrationsPath, "utf-8");
2322
- return convertPocketBaseMigration(migrationContent);
2323
- } catch (error) {
2324
- console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2325
- return null;
2326
- }
2327
- }
2328
- const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2329
- if (latestSnapshotPath) {
2330
- try {
2331
- const migrationContent = fs4.readFileSync(latestSnapshotPath, "utf-8");
2332
- return convertPocketBaseMigration(migrationContent);
2333
- } catch (error) {
2334
- console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2335
- return null;
2336
- }
2337
- }
2338
- return null;
2425
+ return collectionId;
2339
2426
  }
2340
2427
  function convertPocketBaseCollection(pbCollection) {
2341
2428
  const fields = [];
@@ -2354,17 +2441,28 @@ function convertPocketBaseCollection(pbCollection) {
2354
2441
  type: pbField.type,
2355
2442
  required: pbField.required || false
2356
2443
  };
2357
- if (pbField.options) {
2358
- field.options = pbField.options;
2444
+ field.options = pbField.options ? { ...pbField.options } : {};
2445
+ if (pbField.type === "select") {
2446
+ if (pbField.values && Array.isArray(pbField.values)) {
2447
+ field.options.values = pbField.values;
2448
+ } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2449
+ field.options.values = pbField.options.values;
2450
+ }
2359
2451
  }
2360
2452
  if (pbField.type === "relation") {
2453
+ const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2454
+ const collectionName = resolveCollectionIdToName(collectionId);
2361
2455
  field.relation = {
2362
- collection: pbField.options?.collectionId || "",
2363
- cascadeDelete: pbField.options?.cascadeDelete || false,
2364
- maxSelect: pbField.options?.maxSelect,
2365
- minSelect: pbField.options?.minSelect
2456
+ collection: collectionName,
2457
+ cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2458
+ maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2459
+ minSelect: pbField.minSelect ?? pbField.options?.minSelect
2366
2460
  };
2367
2461
  }
2462
+ const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2463
+ if (Object.keys(field.options).length === 0) {
2464
+ delete field.options;
2465
+ } else if (pbField.type === "select" && hasOnlyValues) ;
2368
2466
  fields.push(field);
2369
2467
  }
2370
2468
  }
@@ -2385,6 +2483,7 @@ function convertPocketBaseCollection(pbCollection) {
2385
2483
  if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2386
2484
  if (Object.keys(rules).length > 0) {
2387
2485
  schema.rules = rules;
2486
+ schema.permissions = { ...rules };
2388
2487
  }
2389
2488
  return schema;
2390
2489
  }
@@ -2428,6 +2527,320 @@ function convertPocketBaseMigration(migrationContent) {
2428
2527
  }
2429
2528
  }
2430
2529
 
2530
+ // src/migration/migration-parser.ts
2531
+ function extractTimestampFromFilename(filename) {
2532
+ const match = filename.match(/^(\d+)_/);
2533
+ if (match) {
2534
+ return parseInt(match[1], 10);
2535
+ }
2536
+ return null;
2537
+ }
2538
+ function findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp) {
2539
+ try {
2540
+ if (!fs5.existsSync(migrationsPath)) {
2541
+ return [];
2542
+ }
2543
+ const files = fs5.readdirSync(migrationsPath);
2544
+ const migrationFiles = [];
2545
+ for (const file of files) {
2546
+ if (file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")) {
2547
+ continue;
2548
+ }
2549
+ if (!file.endsWith(".js")) {
2550
+ continue;
2551
+ }
2552
+ const timestamp = extractTimestampFromFilename(file);
2553
+ if (timestamp && timestamp > snapshotTimestamp) {
2554
+ migrationFiles.push({
2555
+ path: path5.join(migrationsPath, file),
2556
+ timestamp
2557
+ });
2558
+ }
2559
+ }
2560
+ migrationFiles.sort((a, b) => a.timestamp - b.timestamp);
2561
+ return migrationFiles.map((f) => f.path);
2562
+ } catch (error) {
2563
+ console.warn(`Error finding migrations after snapshot: ${error}`);
2564
+ return [];
2565
+ }
2566
+ }
2567
+ function parseMigrationOperationsFromContent(content) {
2568
+ const collectionsToCreate = [];
2569
+ const collectionsToDelete = [];
2570
+ try {
2571
+ let searchIndex = 0;
2572
+ while (true) {
2573
+ const collectionStart = content.indexOf("new Collection(", searchIndex);
2574
+ if (collectionStart === -1) {
2575
+ break;
2576
+ }
2577
+ const openParen = collectionStart + "new Collection(".length;
2578
+ let braceCount = 0;
2579
+ let parenCount = 1;
2580
+ let inString = false;
2581
+ let stringChar = null;
2582
+ let i = openParen;
2583
+ while (i < content.length && /\s/.test(content[i])) {
2584
+ i++;
2585
+ }
2586
+ if (content[i] !== "{") {
2587
+ searchIndex = i + 1;
2588
+ continue;
2589
+ }
2590
+ const objectStart = i;
2591
+ braceCount = 1;
2592
+ i++;
2593
+ while (i < content.length && (braceCount > 0 || parenCount > 0)) {
2594
+ const char = content[i];
2595
+ const prevChar = i > 0 ? content[i - 1] : "";
2596
+ if (!inString && (char === '"' || char === "'")) {
2597
+ inString = true;
2598
+ stringChar = char;
2599
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2600
+ inString = false;
2601
+ stringChar = null;
2602
+ }
2603
+ if (!inString) {
2604
+ if (char === "{") braceCount++;
2605
+ if (char === "}") braceCount--;
2606
+ if (char === "(") parenCount++;
2607
+ if (char === ")") parenCount--;
2608
+ }
2609
+ i++;
2610
+ }
2611
+ if (braceCount === 0 && parenCount === 0) {
2612
+ const objectContent = content.substring(objectStart, i - 1);
2613
+ try {
2614
+ const collectionObj = new Function(`return ${objectContent}`)();
2615
+ if (collectionObj && collectionObj.name) {
2616
+ const schema = convertPocketBaseCollection(collectionObj);
2617
+ collectionsToCreate.push(schema);
2618
+ }
2619
+ } catch (error) {
2620
+ console.warn(`Failed to parse collection definition: ${error}`);
2621
+ }
2622
+ }
2623
+ searchIndex = i;
2624
+ }
2625
+ const deleteMatches = content.matchAll(
2626
+ /app\.delete\s*\(\s*(?:collection_\w+|app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\))\s*\)/g
2627
+ );
2628
+ for (const match of deleteMatches) {
2629
+ if (match[1]) {
2630
+ collectionsToDelete.push(match[1]);
2631
+ } else {
2632
+ const varNameMatch = match[0].match(/collection_(\w+)/);
2633
+ if (varNameMatch) {
2634
+ const varName = `collection_${varNameMatch[1]}`;
2635
+ const deleteIndex = content.indexOf(match[0]);
2636
+ const beforeDelete = content.substring(0, deleteIndex);
2637
+ const varDefMatch = beforeDelete.match(
2638
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`, "g")
2639
+ );
2640
+ if (varDefMatch && varDefMatch.length > 0) {
2641
+ const collectionDefMatch = beforeDelete.match(
2642
+ new RegExp(`const\\s+${varName}\\s*=\\s*new\\s+Collection\\(\\s*(\\{[\\s\\S]*?\\})\\s*\\)`)
2643
+ );
2644
+ if (collectionDefMatch) {
2645
+ try {
2646
+ const collectionDefStr = collectionDefMatch[1];
2647
+ const collectionObj = new Function(`return ${collectionDefStr}`)();
2648
+ if (collectionObj && collectionObj.name) {
2649
+ collectionsToDelete.push(collectionObj.name);
2650
+ }
2651
+ } catch {
2652
+ }
2653
+ }
2654
+ }
2655
+ }
2656
+ }
2657
+ }
2658
+ const findAndDeleteMatches = content.matchAll(
2659
+ /app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)[\s\S]*?app\.delete/g
2660
+ );
2661
+ for (const match of findAndDeleteMatches) {
2662
+ collectionsToDelete.push(match[1]);
2663
+ }
2664
+ } catch (error) {
2665
+ console.warn(`Failed to parse migration operations from content: ${error}`);
2666
+ }
2667
+ return { collectionsToCreate, collectionsToDelete };
2668
+ }
2669
+ function parseMigrationOperations(migrationContent) {
2670
+ try {
2671
+ const migrateMatch = migrationContent.match(/migrate\s*\(\s*/);
2672
+ if (!migrateMatch) {
2673
+ return parseMigrationOperationsFromContent(migrationContent);
2674
+ }
2675
+ const startIndex = migrateMatch.index + migrateMatch[0].length;
2676
+ let i = startIndex;
2677
+ let parenCount = 0;
2678
+ let foundFirstParen = false;
2679
+ while (i < migrationContent.length) {
2680
+ const char = migrationContent[i];
2681
+ if (char === "(") {
2682
+ parenCount++;
2683
+ foundFirstParen = true;
2684
+ i++;
2685
+ break;
2686
+ }
2687
+ i++;
2688
+ }
2689
+ if (!foundFirstParen) {
2690
+ return parseMigrationOperationsFromContent(migrationContent);
2691
+ }
2692
+ let inString = false;
2693
+ let stringChar = null;
2694
+ let foundBrace = false;
2695
+ let braceStart = -1;
2696
+ while (i < migrationContent.length && !foundBrace) {
2697
+ const char = migrationContent[i];
2698
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2699
+ if (!inString && (char === '"' || char === "'")) {
2700
+ inString = true;
2701
+ stringChar = char;
2702
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2703
+ inString = false;
2704
+ stringChar = null;
2705
+ }
2706
+ if (!inString) {
2707
+ if (char === "(") parenCount++;
2708
+ if (char === ")") {
2709
+ parenCount--;
2710
+ if (parenCount === 0) {
2711
+ i++;
2712
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2713
+ i++;
2714
+ }
2715
+ if (i < migrationContent.length - 1 && migrationContent[i] === "=" && migrationContent[i + 1] === ">") {
2716
+ i += 2;
2717
+ while (i < migrationContent.length && /\s/.test(migrationContent[i])) {
2718
+ i++;
2719
+ }
2720
+ if (i < migrationContent.length && migrationContent[i] === "{") {
2721
+ foundBrace = true;
2722
+ braceStart = i + 1;
2723
+ break;
2724
+ }
2725
+ }
2726
+ }
2727
+ }
2728
+ }
2729
+ i++;
2730
+ }
2731
+ if (!foundBrace || braceStart === -1) {
2732
+ return parseMigrationOperationsFromContent(migrationContent);
2733
+ }
2734
+ let braceCount = 1;
2735
+ i = braceStart;
2736
+ inString = false;
2737
+ stringChar = null;
2738
+ while (i < migrationContent.length && braceCount > 0) {
2739
+ const char = migrationContent[i];
2740
+ const prevChar = i > 0 ? migrationContent[i - 1] : "";
2741
+ if (!inString && (char === '"' || char === "'")) {
2742
+ inString = true;
2743
+ stringChar = char;
2744
+ } else if (inString && char === stringChar && prevChar !== "\\") {
2745
+ inString = false;
2746
+ stringChar = null;
2747
+ }
2748
+ if (!inString) {
2749
+ if (char === "{") braceCount++;
2750
+ if (char === "}") braceCount--;
2751
+ }
2752
+ i++;
2753
+ }
2754
+ if (braceCount === 0) {
2755
+ const upMigrationContent = migrationContent.substring(braceStart, i - 1);
2756
+ return parseMigrationOperationsFromContent(upMigrationContent);
2757
+ }
2758
+ return parseMigrationOperationsFromContent(migrationContent);
2759
+ } catch (error) {
2760
+ console.warn(`Failed to parse migration operations: ${error}`);
2761
+ return { collectionsToCreate: [], collectionsToDelete: [] };
2762
+ }
2763
+ }
2764
+ ({
2765
+ workspaceRoot: process.cwd()});
2766
+ function findLatestSnapshot(migrationsPath) {
2767
+ try {
2768
+ if (!fs5.existsSync(migrationsPath)) {
2769
+ return null;
2770
+ }
2771
+ const files = fs5.readdirSync(migrationsPath);
2772
+ const snapshotFiles = files.filter(
2773
+ (file) => file.endsWith("_collections_snapshot.js") || file.endsWith("_snapshot.js")
2774
+ );
2775
+ if (snapshotFiles.length === 0) {
2776
+ return null;
2777
+ }
2778
+ snapshotFiles.sort().reverse();
2779
+ const latestSnapshot = snapshotFiles[0];
2780
+ if (!latestSnapshot) {
2781
+ return null;
2782
+ }
2783
+ return path5.join(migrationsPath, latestSnapshot);
2784
+ } catch (error) {
2785
+ console.warn(`Error finding latest snapshot: ${error}`);
2786
+ return null;
2787
+ }
2788
+ }
2789
+ function applyMigrationOperations(snapshot, operations) {
2790
+ const updatedCollections = new Map(snapshot.collections);
2791
+ for (const collectionName of operations.collectionsToDelete) {
2792
+ updatedCollections.delete(collectionName);
2793
+ }
2794
+ for (const collection of operations.collectionsToCreate) {
2795
+ updatedCollections.set(collection.name, collection);
2796
+ }
2797
+ return {
2798
+ ...snapshot,
2799
+ collections: updatedCollections
2800
+ };
2801
+ }
2802
+ function loadSnapshotWithMigrations(config = {}) {
2803
+ const migrationsPath = config.migrationsPath;
2804
+ if (!migrationsPath) {
2805
+ return null;
2806
+ }
2807
+ if (fs5.existsSync(migrationsPath) && fs5.statSync(migrationsPath).isFile()) {
2808
+ try {
2809
+ const migrationContent = fs5.readFileSync(migrationsPath, "utf-8");
2810
+ return convertPocketBaseMigration(migrationContent);
2811
+ } catch (error) {
2812
+ console.warn(`Failed to load snapshot from ${migrationsPath}: ${error}`);
2813
+ return null;
2814
+ }
2815
+ }
2816
+ const latestSnapshotPath = findLatestSnapshot(migrationsPath);
2817
+ if (!latestSnapshotPath) {
2818
+ return null;
2819
+ }
2820
+ try {
2821
+ const migrationContent = fs5.readFileSync(latestSnapshotPath, "utf-8");
2822
+ let snapshot = convertPocketBaseMigration(migrationContent);
2823
+ const snapshotFilename = path5.basename(latestSnapshotPath);
2824
+ const snapshotTimestamp = extractTimestampFromFilename(snapshotFilename);
2825
+ if (snapshotTimestamp) {
2826
+ const migrationFiles = findMigrationsAfterSnapshot(migrationsPath, snapshotTimestamp);
2827
+ for (const migrationFile of migrationFiles) {
2828
+ try {
2829
+ const migrationContent2 = fs5.readFileSync(migrationFile, "utf-8");
2830
+ const operations = parseMigrationOperations(migrationContent2);
2831
+ snapshot = applyMigrationOperations(snapshot, operations);
2832
+ } catch (error) {
2833
+ console.warn(`Failed to apply migration ${migrationFile}: ${error}`);
2834
+ }
2835
+ }
2836
+ }
2837
+ return snapshot;
2838
+ } catch (error) {
2839
+ console.warn(`Failed to load snapshot from ${latestSnapshotPath}: ${error}`);
2840
+ return null;
2841
+ }
2842
+ }
2843
+
2431
2844
  // src/migration/validation.ts
2432
2845
  function detectCollectionDeletions(diff) {
2433
2846
  const changes = [];
@@ -2596,8 +3009,8 @@ var DEFAULT_CONFIG5 = {
2596
3009
  };
2597
3010
  function findConfigFile(directory) {
2598
3011
  for (const fileName of CONFIG_FILE_NAMES) {
2599
- const filePath = path4.join(directory, fileName);
2600
- if (fs4.existsSync(filePath)) {
3012
+ const filePath = path5.join(directory, fileName);
3013
+ if (fs5.existsSync(filePath)) {
2601
3014
  return filePath;
2602
3015
  }
2603
3016
  }
@@ -2605,7 +3018,7 @@ function findConfigFile(directory) {
2605
3018
  }
2606
3019
  function loadJsonConfig(configPath) {
2607
3020
  try {
2608
- const content = fs4.readFileSync(configPath, "utf-8");
3021
+ const content = fs5.readFileSync(configPath, "utf-8");
2609
3022
  return JSON.parse(content);
2610
3023
  } catch (error) {
2611
3024
  if (error instanceof SyntaxError) {
@@ -2634,10 +3047,10 @@ async function loadJsConfig(configPath) {
2634
3047
  }
2635
3048
  }
2636
3049
  async function loadConfigFile(configPath) {
2637
- if (!fs4.existsSync(configPath)) {
3050
+ if (!fs5.existsSync(configPath)) {
2638
3051
  return null;
2639
3052
  }
2640
- const ext = path4.extname(configPath).toLowerCase();
3053
+ const ext = path5.extname(configPath).toLowerCase();
2641
3054
  if (ext === ".json") {
2642
3055
  return loadJsonConfig(configPath);
2643
3056
  } else if (ext === ".js" || ext === ".mjs") {
@@ -2704,10 +3117,10 @@ function validateConfig(config, configPath) {
2704
3117
  }
2705
3118
  const cwd = process.cwd();
2706
3119
  const possiblePaths = [
2707
- path4.resolve(cwd, config.schema.directory),
2708
- path4.resolve(cwd, "shared", config.schema.directory)
3120
+ path5.resolve(cwd, config.schema.directory),
3121
+ path5.resolve(cwd, "shared", config.schema.directory)
2709
3122
  ];
2710
- const schemaDir = possiblePaths.find((p) => fs4.existsSync(p));
3123
+ const schemaDir = possiblePaths.find((p) => fs5.existsSync(p));
2711
3124
  if (!schemaDir) {
2712
3125
  throw new ConfigurationError(`Schema directory not found. Tried: ${possiblePaths.join(", ")}`, configPath, [
2713
3126
  "schema.directory"
@@ -2719,15 +3132,15 @@ async function loadConfig(options = {}) {
2719
3132
  let configFilePath;
2720
3133
  const cwd = process.cwd();
2721
3134
  if (options.config) {
2722
- const explicitPath = path4.resolve(cwd, options.config);
2723
- if (!fs4.existsSync(explicitPath)) {
3135
+ const explicitPath = path5.resolve(cwd, options.config);
3136
+ if (!fs5.existsSync(explicitPath)) {
2724
3137
  throw new ConfigurationError(`Configuration file not found: ${explicitPath}`, explicitPath);
2725
3138
  }
2726
3139
  configFilePath = explicitPath;
2727
3140
  } else {
2728
- const searchDirs = [cwd, path4.join(cwd, "shared")];
3141
+ const searchDirs = [cwd, path5.join(cwd, "shared")];
2729
3142
  for (const dir of searchDirs) {
2730
- if (fs4.existsSync(dir)) {
3143
+ if (fs5.existsSync(dir)) {
2731
3144
  const found = findConfigFile(dir);
2732
3145
  if (found) {
2733
3146
  configFilePath = found;
@@ -2756,18 +3169,18 @@ async function loadConfig(options = {}) {
2756
3169
  function getSchemaDirectory(config) {
2757
3170
  const cwd = process.cwd();
2758
3171
  const possiblePaths = [
2759
- path4.resolve(cwd, config.schema.directory),
2760
- path4.resolve(cwd, "shared", config.schema.directory)
3172
+ path5.resolve(cwd, config.schema.directory),
3173
+ path5.resolve(cwd, "shared", config.schema.directory)
2761
3174
  ];
2762
- return possiblePaths.find((p) => fs4.existsSync(p)) || possiblePaths[0];
3175
+ return possiblePaths.find((p) => fs5.existsSync(p)) || possiblePaths[0];
2763
3176
  }
2764
3177
  function getMigrationsDirectory(config) {
2765
3178
  const cwd = process.cwd();
2766
3179
  const possiblePaths = [
2767
- path4.resolve(cwd, config.migrations.directory),
2768
- path4.resolve(cwd, "shared", config.migrations.directory)
3180
+ path5.resolve(cwd, config.migrations.directory),
3181
+ path5.resolve(cwd, "shared", config.migrations.directory)
2769
3182
  ];
2770
- return possiblePaths.find((p) => fs4.existsSync(p)) || possiblePaths[0];
3183
+ return possiblePaths.find((p) => fs5.existsSync(p)) || possiblePaths[0];
2771
3184
  }
2772
3185
  var currentVerbosity = "normal";
2773
3186
  function setVerbosity(level) {
@@ -3035,10 +3448,16 @@ async function executeGenerate(options) {
3035
3448
  const schemaDir = getSchemaDirectory(config);
3036
3449
  const migrationsDir = getMigrationsDirectory(config);
3037
3450
  logSection("\u{1F50D} Analyzing Schema");
3038
- const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(schemaDir));
3451
+ const analyzerConfig = {
3452
+ schemaDir,
3453
+ excludePatterns: config.schema.exclude,
3454
+ useCompiledFiles: false
3455
+ // Use source files since we're in development/testing
3456
+ };
3457
+ const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
3039
3458
  logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
3040
3459
  logInfo("Loading previous snapshot...");
3041
- const previousSnapshot = loadSnapshotIfExists({
3460
+ const previousSnapshot = loadSnapshotWithMigrations({
3042
3461
  migrationsPath: migrationsDir,
3043
3462
  workspaceRoot: process.cwd()
3044
3463
  });
@@ -3065,7 +3484,7 @@ async function executeGenerate(options) {
3065
3484
  "Creating migration file...",
3066
3485
  () => Promise.resolve(generate(diff, migrationsDir))
3067
3486
  );
3068
- logSuccess(`Migration file created: ${path4.basename(migrationPath)}`);
3487
+ logSuccess(`Migration file created: ${path5.basename(migrationPath)}`);
3069
3488
  logSection("\u2705 Next Steps");
3070
3489
  console.log();
3071
3490
  console.log(" 1. Review the generated migration file:");
@@ -3222,10 +3641,16 @@ async function executeStatus(options) {
3222
3641
  const schemaDir = getSchemaDirectory(config);
3223
3642
  const migrationsDir = getMigrationsDirectory(config);
3224
3643
  logSection("\u{1F50D} Checking Migration Status");
3225
- const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(schemaDir));
3644
+ const analyzerConfig = {
3645
+ schemaDir,
3646
+ excludePatterns: config.schema.exclude,
3647
+ useCompiledFiles: false
3648
+ // Use source files since we're in development/testing
3649
+ };
3650
+ const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
3226
3651
  logSuccess(`Found ${currentSchema.collections.size} collection(s) in schema`);
3227
3652
  logInfo("Loading previous snapshot...");
3228
- const previousSnapshot = loadSnapshotIfExists({
3653
+ const previousSnapshot = loadSnapshotWithMigrations({
3229
3654
  migrationsPath: migrationsDir,
3230
3655
  workspaceRoot: process.cwd()
3231
3656
  });