pocketbase-zod-schema 0.2.4 → 0.3.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 (67) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +209 -24
  3. package/dist/cli/index.cjs +406 -294
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.d.cts +3 -1
  6. package/dist/cli/index.d.ts +3 -1
  7. package/dist/cli/index.js +406 -294
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/migrate.cjs +406 -294
  10. package/dist/cli/migrate.cjs.map +1 -1
  11. package/dist/cli/migrate.js +406 -294
  12. package/dist/cli/migrate.js.map +1 -1
  13. package/dist/cli/utils/index.d.cts +3 -1
  14. package/dist/cli/utils/index.d.ts +3 -1
  15. package/dist/fields-UcOPu1OQ.d.cts +364 -0
  16. package/dist/fields-UcOPu1OQ.d.ts +364 -0
  17. package/dist/index.cjs +633 -112
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +4 -3
  20. package/dist/index.d.ts +4 -3
  21. package/dist/index.js +619 -101
  22. package/dist/index.js.map +1 -1
  23. package/dist/migration/analyzer.cjs +44 -0
  24. package/dist/migration/analyzer.cjs.map +1 -1
  25. package/dist/migration/analyzer.d.cts +2 -1
  26. package/dist/migration/analyzer.d.ts +2 -1
  27. package/dist/migration/analyzer.js +44 -0
  28. package/dist/migration/analyzer.js.map +1 -1
  29. package/dist/migration/diff.cjs +76 -1
  30. package/dist/migration/diff.cjs.map +1 -1
  31. package/dist/migration/diff.d.cts +3 -1
  32. package/dist/migration/diff.d.ts +3 -1
  33. package/dist/migration/diff.js +76 -1
  34. package/dist/migration/diff.js.map +1 -1
  35. package/dist/migration/generator.cjs +323 -46
  36. package/dist/migration/generator.cjs.map +1 -1
  37. package/dist/migration/generator.d.cts +60 -11
  38. package/dist/migration/generator.d.ts +60 -11
  39. package/dist/migration/generator.js +319 -47
  40. package/dist/migration/generator.js.map +1 -1
  41. package/dist/migration/index.cjs +433 -47
  42. package/dist/migration/index.cjs.map +1 -1
  43. package/dist/migration/index.d.cts +3 -2
  44. package/dist/migration/index.d.ts +3 -2
  45. package/dist/migration/index.js +432 -48
  46. package/dist/migration/index.js.map +1 -1
  47. package/dist/migration/snapshot.cjs.map +1 -1
  48. package/dist/migration/snapshot.d.cts +3 -1
  49. package/dist/migration/snapshot.d.ts +3 -1
  50. package/dist/migration/snapshot.js.map +1 -1
  51. package/dist/migration/utils/index.cjs +80 -0
  52. package/dist/migration/utils/index.cjs.map +1 -1
  53. package/dist/migration/utils/index.d.cts +39 -202
  54. package/dist/migration/utils/index.d.ts +39 -202
  55. package/dist/migration/utils/index.js +77 -1
  56. package/dist/migration/utils/index.js.map +1 -1
  57. package/dist/schema.cjs +200 -61
  58. package/dist/schema.cjs.map +1 -1
  59. package/dist/schema.d.cts +2 -85
  60. package/dist/schema.d.ts +2 -85
  61. package/dist/schema.js +186 -50
  62. package/dist/schema.js.map +1 -1
  63. package/dist/type-mapper-DrQmtznD.d.cts +208 -0
  64. package/dist/type-mapper-n231Fspm.d.ts +208 -0
  65. package/dist/{types-z1Dkjg8m.d.ts → types-Ds3NQvny.d.ts} +33 -2
  66. package/dist/{types-BbTgmg6H.d.cts → types-YoBjsa-A.d.cts} +33 -2
  67. package/package.json +1 -1
@@ -7,6 +7,7 @@ var fs5 = require('fs');
7
7
  var path5 = require('path');
8
8
  var url = require('url');
9
9
  var zod = require('zod');
10
+ var crypto = require('crypto');
10
11
  var ora = require('ora');
11
12
 
12
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -68,6 +69,18 @@ function extractRelationMetadata(description) {
68
69
  }
69
70
  return null;
70
71
  }
72
+ var FIELD_METADATA_KEY = "__pocketbase_field__";
73
+ function extractFieldMetadata(description) {
74
+ if (!description) return null;
75
+ try {
76
+ const parsed = JSON.parse(description);
77
+ if (parsed[FIELD_METADATA_KEY]) {
78
+ return parsed[FIELD_METADATA_KEY];
79
+ }
80
+ } catch {
81
+ }
82
+ return null;
83
+ }
71
84
 
72
85
  // src/migration/errors.ts
73
86
  var MigrationError = class _MigrationError extends Error {
@@ -1306,6 +1319,38 @@ function isAuthCollection(fields) {
1306
1319
  return hasEmail && hasPassword;
1307
1320
  }
1308
1321
  function buildFieldDefinition(fieldName, zodType) {
1322
+ const fieldMetadata = extractFieldMetadata(zodType.description);
1323
+ if (fieldMetadata) {
1324
+ let required2;
1325
+ if (fieldMetadata.type === "number") {
1326
+ if (fieldMetadata.options?.required !== void 0) {
1327
+ required2 = fieldMetadata.options.required;
1328
+ } else {
1329
+ required2 = false;
1330
+ }
1331
+ } else {
1332
+ required2 = isFieldRequired(zodType);
1333
+ }
1334
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1335
+ const fieldDef2 = {
1336
+ name: fieldName,
1337
+ type: fieldMetadata.type,
1338
+ required: required2,
1339
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1340
+ };
1341
+ if (fieldMetadata.type === "relation") {
1342
+ const relationMetadata2 = extractRelationMetadata(zodType.description);
1343
+ if (relationMetadata2) {
1344
+ fieldDef2.relation = {
1345
+ collection: relationMetadata2.collection,
1346
+ maxSelect: relationMetadata2.maxSelect,
1347
+ minSelect: relationMetadata2.minSelect,
1348
+ cascadeDelete: relationMetadata2.cascadeDelete
1349
+ };
1350
+ }
1351
+ }
1352
+ return fieldDef2;
1353
+ }
1309
1354
  const fieldType = mapZodTypeToPocketBase(zodType, fieldName);
1310
1355
  const required = isFieldRequired(zodType);
1311
1356
  const options = extractFieldOptions(zodType);
@@ -1461,6 +1506,65 @@ async function buildSchemaDefinition(config) {
1461
1506
  async function parseSchemaFiles(config) {
1462
1507
  return buildSchemaDefinition(config);
1463
1508
  }
1509
+ function generateCollectionId() {
1510
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
1511
+ const idLength = 15;
1512
+ const bytes = crypto.randomBytes(idLength);
1513
+ let id = "pb_";
1514
+ for (let i = 0; i < idLength; i++) {
1515
+ const index = bytes[i] % chars.length;
1516
+ id += chars[index];
1517
+ }
1518
+ return id;
1519
+ }
1520
+ var CollectionIdRegistry = class {
1521
+ ids;
1522
+ constructor() {
1523
+ this.ids = /* @__PURE__ */ new Set();
1524
+ }
1525
+ /**
1526
+ * Generates a unique collection ID for a given collection name
1527
+ * Special case: Returns constant "_pb_users_auth_" for users collection
1528
+ * Retries up to 10 times if collision occurs (extremely rare)
1529
+ *
1530
+ * @param collectionName - The name of the collection
1531
+ * @returns A unique collection ID
1532
+ * @throws Error if unable to generate unique ID after max attempts
1533
+ */
1534
+ generate(collectionName) {
1535
+ if (collectionName && collectionName.toLowerCase() === "users") {
1536
+ const usersId = "_pb_users_auth_";
1537
+ this.register(usersId);
1538
+ return usersId;
1539
+ }
1540
+ const maxAttempts = 10;
1541
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1542
+ const id = generateCollectionId();
1543
+ if (!this.has(id)) {
1544
+ this.register(id);
1545
+ return id;
1546
+ }
1547
+ }
1548
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
1549
+ }
1550
+ /**
1551
+ * Checks if an ID has already been registered
1552
+ *
1553
+ * @param id - The collection ID to check
1554
+ * @returns True if the ID exists in the registry
1555
+ */
1556
+ has(id) {
1557
+ return this.ids.has(id);
1558
+ }
1559
+ /**
1560
+ * Registers a collection ID in the registry
1561
+ *
1562
+ * @param id - The collection ID to register
1563
+ */
1564
+ register(id) {
1565
+ this.ids.add(id);
1566
+ }
1567
+ };
1464
1568
 
1465
1569
  // src/migration/diff.ts
1466
1570
  var DEFAULT_CONFIG2 = {
@@ -1792,6 +1896,18 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1792
1896
  const filteredCollectionsToDelete = collectionsToDelete.filter(
1793
1897
  (collection) => !isSystemCollection(collection.name, config)
1794
1898
  );
1899
+ const registry = new CollectionIdRegistry();
1900
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
1901
+ if (collection.id) {
1902
+ registry.register(collection.id);
1903
+ return collection;
1904
+ }
1905
+ const id = registry.generate(collection.name);
1906
+ return {
1907
+ ...collection,
1908
+ id
1909
+ };
1910
+ });
1795
1911
  const collectionsToModify = [];
1796
1912
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
1797
1913
  for (const [currentCollection, previousCollection] of matchedCollections) {
@@ -1801,7 +1917,7 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1801
1917
  }
1802
1918
  }
1803
1919
  return {
1804
- collectionsToCreate: filteredCollectionsToCreate,
1920
+ collectionsToCreate: collectionsWithIds,
1805
1921
  collectionsToDelete: filteredCollectionsToDelete,
1806
1922
  collectionsToModify
1807
1923
  };
@@ -1884,42 +2000,48 @@ function generateTimestamp(config) {
1884
2000
  }
1885
2001
  return Math.floor(Date.now() / 1e3).toString();
1886
2002
  }
1887
- function generateMigrationDescription(diff) {
1888
- const parts = [];
1889
- if (diff.collectionsToCreate.length > 0) {
1890
- if (diff.collectionsToCreate.length === 1) {
1891
- parts.push(`created_${diff.collectionsToCreate[0].name}`);
1892
- } else {
1893
- parts.push(`created_${diff.collectionsToCreate.length}_collections`);
1894
- }
1895
- }
1896
- if (diff.collectionsToDelete.length > 0) {
1897
- if (diff.collectionsToDelete.length === 1) {
1898
- parts.push(`deleted_${diff.collectionsToDelete[0].name}`);
1899
- } else {
1900
- parts.push(`deleted_${diff.collectionsToDelete.length}_collections`);
1901
- }
1902
- }
1903
- if (diff.collectionsToModify.length > 0) {
1904
- if (diff.collectionsToModify.length === 1) {
1905
- parts.push(`updated_${diff.collectionsToModify[0].collection}`);
1906
- } else {
1907
- parts.push(`updated_${diff.collectionsToModify.length}_collections`);
1908
- }
2003
+ function splitDiffByCollection(diff, baseTimestamp) {
2004
+ const operations = [];
2005
+ let currentTimestamp = parseInt(baseTimestamp, 10);
2006
+ for (const collection of diff.collectionsToCreate) {
2007
+ operations.push({
2008
+ type: "create",
2009
+ collection,
2010
+ timestamp: currentTimestamp.toString()
2011
+ });
2012
+ currentTimestamp += 1;
1909
2013
  }
1910
- if (parts.length === 0) {
1911
- return "no_changes";
2014
+ for (const modification of diff.collectionsToModify) {
2015
+ operations.push({
2016
+ type: "modify",
2017
+ collection: modification.collection,
2018
+ modifications: modification,
2019
+ timestamp: currentTimestamp.toString()
2020
+ });
2021
+ currentTimestamp += 1;
1912
2022
  }
1913
- let description = parts.join("_");
1914
- if (description.length > 80) {
1915
- description = description.substring(0, 77) + "...";
2023
+ for (const collection of diff.collectionsToDelete) {
2024
+ operations.push({
2025
+ type: "delete",
2026
+ collection: collection.name || collection,
2027
+ // Handle both object and string
2028
+ timestamp: currentTimestamp.toString()
2029
+ });
2030
+ currentTimestamp += 1;
1916
2031
  }
1917
- return description;
2032
+ return operations;
1918
2033
  }
1919
- function generateMigrationFilename(diff, config) {
1920
- const timestamp = generateTimestamp(config);
1921
- const description = generateMigrationDescription(diff);
1922
- return `${timestamp}_${description}.js`;
2034
+ function generateCollectionMigrationFilename(operation) {
2035
+ const timestamp = operation.timestamp;
2036
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
2037
+ let collectionName;
2038
+ if (typeof operation.collection === "string") {
2039
+ collectionName = operation.collection;
2040
+ } else {
2041
+ collectionName = operation.collection.name;
2042
+ }
2043
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
2044
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
1923
2045
  }
1924
2046
  function createMigrationFileStructure(upCode, downCode, config) {
1925
2047
  const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
@@ -1991,14 +2113,13 @@ function formatValue(value) {
1991
2113
  return "null";
1992
2114
  }
1993
2115
  if (typeof value === "string") {
1994
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
2116
+ return JSON.stringify(value);
1995
2117
  }
1996
2118
  if (typeof value === "number" || typeof value === "boolean") {
1997
2119
  return String(value);
1998
2120
  }
1999
2121
  if (Array.isArray(value)) {
2000
- const items = value.map((v) => formatValue(v)).join(", ");
2001
- return `[${items}]`;
2122
+ return JSON.stringify(value).replace(/","/g, '", "');
2002
2123
  }
2003
2124
  if (typeof value === "object") {
2004
2125
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -2006,7 +2127,7 @@ function formatValue(value) {
2006
2127
  }
2007
2128
  return String(value);
2008
2129
  }
2009
- function generateFieldDefinitionObject(field) {
2130
+ function generateFieldDefinitionObject(field, collectionIdMap) {
2010
2131
  const parts = [];
2011
2132
  parts.push(` name: "${field.name}"`);
2012
2133
  parts.push(` type: "${field.type}"`);
@@ -2014,34 +2135,47 @@ function generateFieldDefinitionObject(field) {
2014
2135
  if (field.unique !== void 0) {
2015
2136
  parts.push(` unique: ${field.unique}`);
2016
2137
  }
2138
+ if (field.type === "select") {
2139
+ const maxSelect = field.options?.maxSelect ?? 1;
2140
+ parts.push(` maxSelect: ${maxSelect}`);
2141
+ const values = field.options?.values ?? [];
2142
+ parts.push(` values: ${formatValue(values)}`);
2143
+ }
2017
2144
  if (field.options && Object.keys(field.options).length > 0) {
2018
2145
  for (const [key, value] of Object.entries(field.options)) {
2146
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2147
+ continue;
2148
+ }
2019
2149
  parts.push(` ${key}: ${formatValue(value)}`);
2020
2150
  }
2021
2151
  }
2022
2152
  if (field.relation) {
2023
2153
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2024
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2025
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2026
- if (field.relation.maxSelect !== void 0) {
2027
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2028
- }
2029
- if (field.relation.minSelect !== void 0) {
2030
- parts.push(` minSelect: ${field.relation.minSelect}`);
2031
- }
2032
- if (field.relation.cascadeDelete !== void 0) {
2033
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2154
+ let collectionIdValue;
2155
+ if (isUsersCollection) {
2156
+ collectionIdValue = '"_pb_users_auth_"';
2157
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2158
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2159
+ } else {
2160
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2034
2161
  }
2162
+ parts.push(` collectionId: ${collectionIdValue}`);
2163
+ const maxSelect = field.relation.maxSelect ?? 1;
2164
+ parts.push(` maxSelect: ${maxSelect}`);
2165
+ const minSelect = field.relation.minSelect ?? null;
2166
+ parts.push(` minSelect: ${minSelect}`);
2167
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2168
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2035
2169
  }
2036
2170
  return ` {
2037
2171
  ${parts.join(",\n")},
2038
2172
  }`;
2039
2173
  }
2040
- function generateFieldsArray(fields) {
2174
+ function generateFieldsArray(fields, collectionIdMap) {
2041
2175
  if (fields.length === 0) {
2042
2176
  return "[]";
2043
2177
  }
2044
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
2178
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
2045
2179
  return `[
2046
2180
  ${fieldObjects.join(",\n")},
2047
2181
  ]`;
@@ -2100,7 +2234,7 @@ function generateIndexesArray(indexes) {
2100
2234
  if (!indexes || indexes.length === 0) {
2101
2235
  return "[]";
2102
2236
  }
2103
- const indexStrings = indexes.map((idx) => `"${idx}"`);
2237
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
2104
2238
  return `[
2105
2239
  ${indexStrings.join(",\n ")},
2106
2240
  ]`;
@@ -2154,7 +2288,7 @@ function getSystemFields() {
2154
2288
  }
2155
2289
  ];
2156
2290
  }
2157
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2291
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
2158
2292
  const lines = [];
2159
2293
  lines.push(` const ${varName} = new Collection({`);
2160
2294
  lines.push(` name: "${collection.name}",`);
@@ -2168,7 +2302,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
2168
2302
  }
2169
2303
  const systemFields = getSystemFields();
2170
2304
  const allFields = [...systemFields, ...collection.fields];
2171
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
2305
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
2172
2306
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2173
2307
  lines.push(` });`);
2174
2308
  lines.push(``);
@@ -2190,42 +2324,59 @@ function getFieldConstructorName(fieldType) {
2190
2324
  };
2191
2325
  return constructorMap[fieldType] || "TextField";
2192
2326
  }
2193
- function generateFieldConstructorOptions(field) {
2327
+ function generateFieldConstructorOptions(field, collectionIdMap) {
2194
2328
  const parts = [];
2195
2329
  parts.push(` name: "${field.name}"`);
2196
2330
  parts.push(` required: ${field.required}`);
2197
2331
  if (field.unique !== void 0) {
2198
2332
  parts.push(` unique: ${field.unique}`);
2199
2333
  }
2334
+ if (field.type === "select") {
2335
+ const maxSelect = field.options?.maxSelect ?? 1;
2336
+ parts.push(` maxSelect: ${maxSelect}`);
2337
+ const values = field.options?.values ?? [];
2338
+ parts.push(` values: ${formatValue(values)}`);
2339
+ }
2200
2340
  if (field.options && Object.keys(field.options).length > 0) {
2201
2341
  for (const [key, value] of Object.entries(field.options)) {
2202
- parts.push(` ${key}: ${formatValue(value)}`);
2342
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2343
+ continue;
2344
+ }
2345
+ if (field.type === "number" && key === "noDecimal") {
2346
+ parts.push(` onlyInt: ${formatValue(value)}`);
2347
+ } else {
2348
+ parts.push(` ${key}: ${formatValue(value)}`);
2349
+ }
2203
2350
  }
2204
2351
  }
2205
2352
  if (field.relation && field.type === "relation") {
2206
2353
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2207
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2208
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2209
- if (field.relation.maxSelect !== void 0) {
2210
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2211
- }
2212
- if (field.relation.minSelect !== void 0) {
2213
- parts.push(` minSelect: ${field.relation.minSelect}`);
2214
- }
2215
- if (field.relation.cascadeDelete !== void 0) {
2216
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2354
+ let collectionIdValue;
2355
+ if (isUsersCollection) {
2356
+ collectionIdValue = '"_pb_users_auth_"';
2357
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2358
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2359
+ } else {
2360
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2217
2361
  }
2362
+ parts.push(` collectionId: ${collectionIdValue}`);
2363
+ const maxSelect = field.relation.maxSelect ?? 1;
2364
+ parts.push(` maxSelect: ${maxSelect}`);
2365
+ const minSelect = field.relation.minSelect ?? null;
2366
+ parts.push(` minSelect: ${minSelect}`);
2367
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2368
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2218
2369
  }
2219
2370
  return parts.join(",\n");
2220
2371
  }
2221
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
2372
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
2222
2373
  const lines = [];
2223
2374
  const constructorName = getFieldConstructorName(field.type);
2224
2375
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
2225
2376
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2226
2377
  lines.push(``);
2227
2378
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
2228
- lines.push(generateFieldConstructorOptions(field));
2379
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
2229
2380
  lines.push(` }));`);
2230
2381
  lines.push(``);
2231
2382
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -2275,7 +2426,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2275
2426
  const lines = [];
2276
2427
  const collectionVar = varName || `collection_${collectionName}_idx`;
2277
2428
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2278
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
2429
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
2279
2430
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2280
2431
  return lines.join("\n");
2281
2432
  }
@@ -2284,7 +2435,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2284
2435
  const collectionVar = varName || `collection_${collectionName}_idx`;
2285
2436
  const indexVar = `${collectionVar}_indexToRemove`;
2286
2437
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2287
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
2438
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
2288
2439
  lines.push(` if (${indexVar} !== -1) {`);
2289
2440
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2290
2441
  lines.push(` }`);
@@ -2313,243 +2464,194 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
2313
2464
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2314
2465
  return lines.join("\n");
2315
2466
  }
2316
- function generateUpMigration(diff) {
2467
+ function generateOperationUpMigration(operation, collectionIdMap) {
2317
2468
  const lines = [];
2318
- lines.push(` // UP MIGRATION`);
2319
- lines.push(``);
2320
- if (diff.collectionsToCreate.length > 0) {
2321
- lines.push(` // Create new collections`);
2322
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2323
- const collection = diff.collectionsToCreate[i];
2324
- const varName = `collection_${collection.name}_create`;
2325
- lines.push(generateCollectionCreation(collection, varName));
2326
- lines.push(``);
2327
- }
2328
- }
2329
- if (diff.collectionsToModify.length > 0) {
2330
- lines.push(` // Modify existing collections`);
2331
- for (const modification of diff.collectionsToModify) {
2332
- const collectionName = modification.collection;
2333
- if (modification.fieldsToAdd.length > 0) {
2334
- lines.push(` // Add fields to ${collectionName}`);
2335
- for (const field of modification.fieldsToAdd) {
2336
- const varName = `collection_${collectionName}_add_${field.name}`;
2337
- lines.push(generateFieldAddition(collectionName, field, varName));
2338
- lines.push(``);
2339
- }
2340
- }
2341
- if (modification.fieldsToModify.length > 0) {
2342
- lines.push(` // Modify fields in ${collectionName}`);
2343
- for (const fieldMod of modification.fieldsToModify) {
2344
- const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2345
- lines.push(generateFieldModification(collectionName, fieldMod, varName));
2346
- lines.push(``);
2347
- }
2348
- }
2349
- if (modification.fieldsToRemove.length > 0) {
2350
- lines.push(` // Remove fields from ${collectionName}`);
2351
- for (const field of modification.fieldsToRemove) {
2352
- const varName = `collection_${collectionName}_remove_${field.name}`;
2353
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2354
- lines.push(``);
2355
- }
2356
- }
2357
- if (modification.indexesToAdd.length > 0) {
2358
- lines.push(` // Add indexes to ${collectionName}`);
2359
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2360
- const index = modification.indexesToAdd[i];
2361
- const varName = `collection_${collectionName}_addidx_${i}`;
2362
- lines.push(generateIndexAddition(collectionName, index, varName));
2363
- lines.push(``);
2364
- }
2365
- }
2366
- if (modification.indexesToRemove.length > 0) {
2367
- lines.push(` // Remove indexes from ${collectionName}`);
2368
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2369
- const index = modification.indexesToRemove[i];
2370
- const varName = `collection_${collectionName}_rmidx_${i}`;
2371
- lines.push(generateIndexRemoval(collectionName, index, varName));
2372
- lines.push(``);
2373
- }
2374
- }
2375
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2376
- lines.push(` // Update permissions for ${collectionName}`);
2377
- for (const permission of modification.permissionsToUpdate) {
2378
- const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2379
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName));
2380
- lines.push(``);
2381
- }
2382
- } else if (modification.rulesToUpdate.length > 0) {
2383
- lines.push(` // Update rules for ${collectionName}`);
2384
- for (const rule of modification.rulesToUpdate) {
2385
- const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2386
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName));
2387
- lines.push(``);
2388
- }
2389
- }
2469
+ if (operation.type === "create") {
2470
+ const collection = operation.collection;
2471
+ const varName = `collection_${collection.name}`;
2472
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2473
+ } else if (operation.type === "modify") {
2474
+ const modification = operation.modifications;
2475
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2476
+ let operationCount = 0;
2477
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2478
+ for (const field of modification.fieldsToAdd) {
2479
+ operationCount++;
2480
+ const varName = `collection_${collectionName}_add_${field.name}`;
2481
+ const isLast = operationCount === totalOperations;
2482
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2483
+ if (!isLast) lines.push("");
2390
2484
  }
2391
- }
2392
- if (diff.collectionsToDelete.length > 0) {
2393
- lines.push(` // Delete collections`);
2394
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2395
- const collection = diff.collectionsToDelete[i];
2396
- const varName = `collection_${collection.name}_delete`;
2397
- lines.push(generateCollectionDeletion(collection.name, varName));
2398
- lines.push(``);
2399
- }
2400
- }
2401
- if (lines.length === 2) {
2402
- lines.push(` // No changes detected`);
2403
- lines.push(``);
2404
- }
2405
- let code = lines.join("\n");
2406
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2407
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2408
- const saveMatches = [...code.matchAll(savePattern)];
2409
- const deleteMatches = [...code.matchAll(deletePattern)];
2410
- const allMatches = [
2411
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2412
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2413
- ].sort((a, b) => b.index - a.index);
2414
- if (allMatches.length > 0) {
2415
- const lastMatch = allMatches[0];
2416
- if (lastMatch.type === "save") {
2417
- 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);
2418
- } else {
2419
- 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);
2485
+ for (const fieldMod of modification.fieldsToModify) {
2486
+ operationCount++;
2487
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2488
+ const isLast = operationCount === totalOperations;
2489
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
2490
+ if (!isLast) lines.push("");
2420
2491
  }
2492
+ for (const field of modification.fieldsToRemove) {
2493
+ operationCount++;
2494
+ const varName = `collection_${collectionName}_remove_${field.name}`;
2495
+ const isLast = operationCount === totalOperations;
2496
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2497
+ if (!isLast) lines.push("");
2498
+ }
2499
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2500
+ operationCount++;
2501
+ const index = modification.indexesToAdd[i];
2502
+ const varName = `collection_${collectionName}_addidx_${i}`;
2503
+ const isLast = operationCount === totalOperations;
2504
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2505
+ if (!isLast) lines.push("");
2506
+ }
2507
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2508
+ operationCount++;
2509
+ const index = modification.indexesToRemove[i];
2510
+ const varName = `collection_${collectionName}_rmidx_${i}`;
2511
+ const isLast = operationCount === totalOperations;
2512
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2513
+ if (!isLast) lines.push("");
2514
+ }
2515
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2516
+ for (const permission of modification.permissionsToUpdate) {
2517
+ operationCount++;
2518
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2519
+ const isLast = operationCount === totalOperations;
2520
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
2521
+ if (!isLast) lines.push("");
2522
+ }
2523
+ } else if (modification.rulesToUpdate.length > 0) {
2524
+ for (const rule of modification.rulesToUpdate) {
2525
+ operationCount++;
2526
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2527
+ const isLast = operationCount === totalOperations;
2528
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
2529
+ if (!isLast) lines.push("");
2530
+ }
2531
+ }
2532
+ } else if (operation.type === "delete") {
2533
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
2534
+ const varName = `collection_${collectionName}`;
2535
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
2421
2536
  }
2422
- return code;
2537
+ return lines.join("\n");
2423
2538
  }
2424
- function generateDownMigration(diff) {
2539
+ function generateOperationDownMigration(operation, collectionIdMap) {
2425
2540
  const lines = [];
2426
- lines.push(` // DOWN MIGRATION (ROLLBACK)`);
2427
- lines.push(``);
2428
- if (diff.collectionsToDelete.length > 0) {
2429
- lines.push(` // Recreate deleted collections`);
2430
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2431
- const collection = diff.collectionsToDelete[i];
2432
- const varName = `collection_${collection.name}_recreate`;
2433
- lines.push(generateCollectionCreation(collection, varName));
2434
- lines.push(``);
2541
+ if (operation.type === "create") {
2542
+ const collection = operation.collection;
2543
+ const varName = `collection_${collection.name}`;
2544
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
2545
+ } else if (operation.type === "modify") {
2546
+ const modification = operation.modifications;
2547
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2548
+ let operationCount = 0;
2549
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2550
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2551
+ for (const permission of modification.permissionsToUpdate) {
2552
+ operationCount++;
2553
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2554
+ const isLast = operationCount === totalOperations;
2555
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
2556
+ if (!isLast) lines.push("");
2557
+ }
2558
+ } else if (modification.rulesToUpdate.length > 0) {
2559
+ for (const rule of modification.rulesToUpdate) {
2560
+ operationCount++;
2561
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2562
+ const isLast = operationCount === totalOperations;
2563
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
2564
+ if (!isLast) lines.push("");
2565
+ }
2566
+ }
2567
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2568
+ operationCount++;
2569
+ const index = modification.indexesToRemove[i];
2570
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
2571
+ const isLast = operationCount === totalOperations;
2572
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2573
+ if (!isLast) lines.push("");
2574
+ }
2575
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2576
+ operationCount++;
2577
+ const index = modification.indexesToAdd[i];
2578
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
2579
+ const isLast = operationCount === totalOperations;
2580
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2581
+ if (!isLast) lines.push("");
2435
2582
  }
2436
- }
2437
- if (diff.collectionsToModify.length > 0) {
2438
- lines.push(` // Revert modifications`);
2439
- for (const modification of diff.collectionsToModify) {
2440
- const collectionName = modification.collection;
2441
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2442
- lines.push(` // Revert permissions for ${collectionName}`);
2443
- for (const permission of modification.permissionsToUpdate) {
2444
- const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2445
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName));
2446
- lines.push(``);
2447
- }
2448
- } else if (modification.rulesToUpdate.length > 0) {
2449
- lines.push(` // Revert rules for ${collectionName}`);
2450
- for (const rule of modification.rulesToUpdate) {
2451
- const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2452
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName));
2453
- lines.push(``);
2454
- }
2455
- }
2456
- if (modification.indexesToRemove.length > 0) {
2457
- lines.push(` // Restore indexes to ${collectionName}`);
2458
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2459
- const index = modification.indexesToRemove[i];
2460
- const varName = `collection_${collectionName}_restore_idx_${i}`;
2461
- lines.push(generateIndexAddition(collectionName, index, varName));
2462
- lines.push(``);
2463
- }
2464
- }
2465
- if (modification.indexesToAdd.length > 0) {
2466
- lines.push(` // Remove indexes from ${collectionName}`);
2467
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2468
- const index = modification.indexesToAdd[i];
2469
- const varName = `collection_${collectionName}_revert_idx_${i}`;
2470
- lines.push(generateIndexRemoval(collectionName, index, varName));
2471
- lines.push(``);
2472
- }
2473
- }
2474
- if (modification.fieldsToRemove.length > 0) {
2475
- lines.push(` // Restore fields to ${collectionName}`);
2476
- for (const field of modification.fieldsToRemove) {
2477
- const varName = `collection_${collectionName}_restore_${field.name}`;
2478
- lines.push(generateFieldAddition(collectionName, field, varName));
2479
- lines.push(``);
2480
- }
2481
- }
2482
- if (modification.fieldsToModify.length > 0) {
2483
- lines.push(` // Revert field modifications in ${collectionName}`);
2484
- for (const fieldMod of modification.fieldsToModify) {
2485
- const reverseChanges = fieldMod.changes.map((change) => ({
2486
- property: change.property,
2487
- oldValue: change.newValue,
2488
- newValue: change.oldValue
2489
- }));
2490
- const reverseMod = {
2491
- fieldName: fieldMod.fieldName,
2492
- currentDefinition: fieldMod.newDefinition,
2493
- newDefinition: fieldMod.currentDefinition,
2494
- changes: reverseChanges
2495
- };
2496
- const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2497
- lines.push(generateFieldModification(collectionName, reverseMod, varName));
2498
- lines.push(``);
2499
- }
2500
- }
2501
- if (modification.fieldsToAdd.length > 0) {
2502
- lines.push(` // Remove added fields from ${collectionName}`);
2503
- for (const field of modification.fieldsToAdd) {
2504
- const varName = `collection_${collectionName}_revert_add_${field.name}`;
2505
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2506
- lines.push(``);
2507
- }
2508
- }
2583
+ for (const field of modification.fieldsToRemove) {
2584
+ operationCount++;
2585
+ const varName = `collection_${collectionName}_restore_${field.name}`;
2586
+ const isLast = operationCount === totalOperations;
2587
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2588
+ if (!isLast) lines.push("");
2509
2589
  }
2510
- }
2511
- if (diff.collectionsToCreate.length > 0) {
2512
- lines.push(` // Delete created collections`);
2513
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2514
- const collection = diff.collectionsToCreate[i];
2515
- const varName = `collection_${collection.name}_rollback`;
2516
- lines.push(generateCollectionDeletion(collection.name, varName));
2517
- lines.push(``);
2518
- }
2519
- }
2520
- if (lines.length === 2) {
2521
- lines.push(` // No changes to revert`);
2522
- lines.push(``);
2523
- }
2524
- let code = lines.join("\n");
2525
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2526
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2527
- const saveMatches = [...code.matchAll(savePattern)];
2528
- const deleteMatches = [...code.matchAll(deletePattern)];
2529
- const allMatches = [
2530
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2531
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2532
- ].sort((a, b) => b.index - a.index);
2533
- if (allMatches.length > 0) {
2534
- const lastMatch = allMatches[0];
2535
- if (lastMatch.type === "save") {
2536
- 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);
2537
- } else {
2538
- 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);
2590
+ for (const fieldMod of modification.fieldsToModify) {
2591
+ operationCount++;
2592
+ const reverseChanges = fieldMod.changes.map((change) => ({
2593
+ property: change.property,
2594
+ oldValue: change.newValue,
2595
+ newValue: change.oldValue
2596
+ }));
2597
+ const reverseMod = {
2598
+ fieldName: fieldMod.fieldName,
2599
+ currentDefinition: fieldMod.newDefinition,
2600
+ newDefinition: fieldMod.currentDefinition,
2601
+ changes: reverseChanges
2602
+ };
2603
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2604
+ const isLast = operationCount === totalOperations;
2605
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
2606
+ if (!isLast) lines.push("");
2607
+ }
2608
+ for (const field of modification.fieldsToAdd) {
2609
+ operationCount++;
2610
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
2611
+ const isLast = operationCount === totalOperations;
2612
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2613
+ if (!isLast) lines.push("");
2614
+ }
2615
+ } else if (operation.type === "delete") {
2616
+ const collection = operation.collection;
2617
+ if (typeof collection !== "string") {
2618
+ const varName = `collection_${collection.name}`;
2619
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2539
2620
  }
2540
2621
  }
2541
- return code;
2622
+ return lines.join("\n");
2542
2623
  }
2543
2624
  function generate(diff, config) {
2544
2625
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
2545
2626
  try {
2546
2627
  const migrationDir = resolveMigrationDir(normalizedConfig);
2547
- const upCode = generateUpMigration(diff);
2548
- const downCode = generateDownMigration(diff);
2549
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2550
- const filename = generateMigrationFilename(diff, normalizedConfig);
2551
- const filePath = writeMigrationFile(migrationDir, filename, content);
2552
- return filePath;
2628
+ const hasChanges4 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
2629
+ if (!hasChanges4) {
2630
+ return [];
2631
+ }
2632
+ const collectionIdMap = /* @__PURE__ */ new Map();
2633
+ for (const collection of diff.collectionsToCreate) {
2634
+ if (collection.id) {
2635
+ collectionIdMap.set(collection.name, collection.id);
2636
+ }
2637
+ }
2638
+ for (const collection of diff.collectionsToDelete) {
2639
+ if (collection.id) {
2640
+ collectionIdMap.set(collection.name, collection.id);
2641
+ }
2642
+ }
2643
+ const baseTimestamp = generateTimestamp(normalizedConfig);
2644
+ const operations = splitDiffByCollection(diff, baseTimestamp);
2645
+ const filePaths = [];
2646
+ for (const operation of operations) {
2647
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
2648
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
2649
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2650
+ const filename = generateCollectionMigrationFilename(operation);
2651
+ const filePath = writeMigrationFile(migrationDir, filename, content);
2652
+ filePaths.push(filePath);
2653
+ }
2654
+ return filePaths;
2553
2655
  } catch (error) {
2554
2656
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
2555
2657
  throw error;
@@ -3570,15 +3672,25 @@ async function executeGenerate(options) {
3570
3672
  process.exit(1);
3571
3673
  }
3572
3674
  logSection("\u{1F4DD} Generating Migration");
3573
- const migrationPath = await withProgress(
3675
+ const migrationPaths = await withProgress(
3574
3676
  "Creating migration file...",
3575
3677
  () => Promise.resolve(generate(diff, migrationsDir))
3576
3678
  );
3577
- logSuccess(`Migration file created: ${path5__namespace.basename(migrationPath)}`);
3679
+ if (migrationPaths.length === 0) {
3680
+ logWarning("No migration files were generated (no changes detected).");
3681
+ return;
3682
+ }
3683
+ if (migrationPaths.length === 1) {
3684
+ logSuccess(`Migration file created: ${path5__namespace.basename(migrationPaths[0])}`);
3685
+ } else {
3686
+ logSuccess(`Created ${migrationPaths.length} migration files`);
3687
+ }
3578
3688
  logSection("\u2705 Next Steps");
3579
3689
  console.log();
3580
- console.log(" 1. Review the generated migration file:");
3581
- console.log(` ${migrationPath}`);
3690
+ console.log(" 1. Review the generated migration file(s):");
3691
+ migrationPaths.forEach((migrationPath) => {
3692
+ console.log(` ${migrationPath}`);
3693
+ });
3582
3694
  console.log();
3583
3695
  console.log(" 2. Apply the migration by running PocketBase:");
3584
3696
  console.log(" yarn pb");