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
package/dist/cli/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as path5 from 'path';
2
2
  import * as fs5 from 'fs';
3
3
  import { z } from 'zod';
4
+ import { randomBytes } from 'crypto';
4
5
  import chalk from 'chalk';
5
6
  import ora from 'ora';
6
7
 
@@ -36,6 +37,18 @@ function extractRelationMetadata(description) {
36
37
  }
37
38
  return null;
38
39
  }
40
+ var FIELD_METADATA_KEY = "__pocketbase_field__";
41
+ function extractFieldMetadata(description) {
42
+ if (!description) return null;
43
+ try {
44
+ const parsed = JSON.parse(description);
45
+ if (parsed[FIELD_METADATA_KEY]) {
46
+ return parsed[FIELD_METADATA_KEY];
47
+ }
48
+ } catch {
49
+ }
50
+ return null;
51
+ }
39
52
 
40
53
  // src/migration/errors.ts
41
54
  var MigrationError = class _MigrationError extends Error {
@@ -1274,6 +1287,38 @@ function isAuthCollection(fields) {
1274
1287
  return hasEmail && hasPassword;
1275
1288
  }
1276
1289
  function buildFieldDefinition(fieldName, zodType) {
1290
+ const fieldMetadata = extractFieldMetadata(zodType.description);
1291
+ if (fieldMetadata) {
1292
+ let required2;
1293
+ if (fieldMetadata.type === "number") {
1294
+ if (fieldMetadata.options?.required !== void 0) {
1295
+ required2 = fieldMetadata.options.required;
1296
+ } else {
1297
+ required2 = false;
1298
+ }
1299
+ } else {
1300
+ required2 = isFieldRequired(zodType);
1301
+ }
1302
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1303
+ const fieldDef2 = {
1304
+ name: fieldName,
1305
+ type: fieldMetadata.type,
1306
+ required: required2,
1307
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1308
+ };
1309
+ if (fieldMetadata.type === "relation") {
1310
+ const relationMetadata2 = extractRelationMetadata(zodType.description);
1311
+ if (relationMetadata2) {
1312
+ fieldDef2.relation = {
1313
+ collection: relationMetadata2.collection,
1314
+ maxSelect: relationMetadata2.maxSelect,
1315
+ minSelect: relationMetadata2.minSelect,
1316
+ cascadeDelete: relationMetadata2.cascadeDelete
1317
+ };
1318
+ }
1319
+ }
1320
+ return fieldDef2;
1321
+ }
1277
1322
  const fieldType = mapZodTypeToPocketBase(zodType, fieldName);
1278
1323
  const required = isFieldRequired(zodType);
1279
1324
  const options = extractFieldOptions(zodType);
@@ -1429,6 +1474,65 @@ async function buildSchemaDefinition(config) {
1429
1474
  async function parseSchemaFiles(config) {
1430
1475
  return buildSchemaDefinition(config);
1431
1476
  }
1477
+ function generateCollectionId() {
1478
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
1479
+ const idLength = 15;
1480
+ const bytes = randomBytes(idLength);
1481
+ let id = "pb_";
1482
+ for (let i = 0; i < idLength; i++) {
1483
+ const index = bytes[i] % chars.length;
1484
+ id += chars[index];
1485
+ }
1486
+ return id;
1487
+ }
1488
+ var CollectionIdRegistry = class {
1489
+ ids;
1490
+ constructor() {
1491
+ this.ids = /* @__PURE__ */ new Set();
1492
+ }
1493
+ /**
1494
+ * Generates a unique collection ID for a given collection name
1495
+ * Special case: Returns constant "_pb_users_auth_" for users collection
1496
+ * Retries up to 10 times if collision occurs (extremely rare)
1497
+ *
1498
+ * @param collectionName - The name of the collection
1499
+ * @returns A unique collection ID
1500
+ * @throws Error if unable to generate unique ID after max attempts
1501
+ */
1502
+ generate(collectionName) {
1503
+ if (collectionName && collectionName.toLowerCase() === "users") {
1504
+ const usersId = "_pb_users_auth_";
1505
+ this.register(usersId);
1506
+ return usersId;
1507
+ }
1508
+ const maxAttempts = 10;
1509
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1510
+ const id = generateCollectionId();
1511
+ if (!this.has(id)) {
1512
+ this.register(id);
1513
+ return id;
1514
+ }
1515
+ }
1516
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
1517
+ }
1518
+ /**
1519
+ * Checks if an ID has already been registered
1520
+ *
1521
+ * @param id - The collection ID to check
1522
+ * @returns True if the ID exists in the registry
1523
+ */
1524
+ has(id) {
1525
+ return this.ids.has(id);
1526
+ }
1527
+ /**
1528
+ * Registers a collection ID in the registry
1529
+ *
1530
+ * @param id - The collection ID to register
1531
+ */
1532
+ register(id) {
1533
+ this.ids.add(id);
1534
+ }
1535
+ };
1432
1536
 
1433
1537
  // src/migration/diff.ts
1434
1538
  var DEFAULT_CONFIG2 = {
@@ -1760,6 +1864,18 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1760
1864
  const filteredCollectionsToDelete = collectionsToDelete.filter(
1761
1865
  (collection) => !isSystemCollection(collection.name, config)
1762
1866
  );
1867
+ const registry = new CollectionIdRegistry();
1868
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
1869
+ if (collection.id) {
1870
+ registry.register(collection.id);
1871
+ return collection;
1872
+ }
1873
+ const id = registry.generate(collection.name);
1874
+ return {
1875
+ ...collection,
1876
+ id
1877
+ };
1878
+ });
1763
1879
  const collectionsToModify = [];
1764
1880
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
1765
1881
  for (const [currentCollection, previousCollection] of matchedCollections) {
@@ -1769,7 +1885,7 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1769
1885
  }
1770
1886
  }
1771
1887
  return {
1772
- collectionsToCreate: filteredCollectionsToCreate,
1888
+ collectionsToCreate: collectionsWithIds,
1773
1889
  collectionsToDelete: filteredCollectionsToDelete,
1774
1890
  collectionsToModify
1775
1891
  };
@@ -1852,42 +1968,48 @@ function generateTimestamp(config) {
1852
1968
  }
1853
1969
  return Math.floor(Date.now() / 1e3).toString();
1854
1970
  }
1855
- function generateMigrationDescription(diff) {
1856
- const parts = [];
1857
- if (diff.collectionsToCreate.length > 0) {
1858
- if (diff.collectionsToCreate.length === 1) {
1859
- parts.push(`created_${diff.collectionsToCreate[0].name}`);
1860
- } else {
1861
- parts.push(`created_${diff.collectionsToCreate.length}_collections`);
1862
- }
1863
- }
1864
- if (diff.collectionsToDelete.length > 0) {
1865
- if (diff.collectionsToDelete.length === 1) {
1866
- parts.push(`deleted_${diff.collectionsToDelete[0].name}`);
1867
- } else {
1868
- parts.push(`deleted_${diff.collectionsToDelete.length}_collections`);
1869
- }
1870
- }
1871
- if (diff.collectionsToModify.length > 0) {
1872
- if (diff.collectionsToModify.length === 1) {
1873
- parts.push(`updated_${diff.collectionsToModify[0].collection}`);
1874
- } else {
1875
- parts.push(`updated_${diff.collectionsToModify.length}_collections`);
1876
- }
1971
+ function splitDiffByCollection(diff, baseTimestamp) {
1972
+ const operations = [];
1973
+ let currentTimestamp = parseInt(baseTimestamp, 10);
1974
+ for (const collection of diff.collectionsToCreate) {
1975
+ operations.push({
1976
+ type: "create",
1977
+ collection,
1978
+ timestamp: currentTimestamp.toString()
1979
+ });
1980
+ currentTimestamp += 1;
1877
1981
  }
1878
- if (parts.length === 0) {
1879
- return "no_changes";
1982
+ for (const modification of diff.collectionsToModify) {
1983
+ operations.push({
1984
+ type: "modify",
1985
+ collection: modification.collection,
1986
+ modifications: modification,
1987
+ timestamp: currentTimestamp.toString()
1988
+ });
1989
+ currentTimestamp += 1;
1880
1990
  }
1881
- let description = parts.join("_");
1882
- if (description.length > 80) {
1883
- description = description.substring(0, 77) + "...";
1991
+ for (const collection of diff.collectionsToDelete) {
1992
+ operations.push({
1993
+ type: "delete",
1994
+ collection: collection.name || collection,
1995
+ // Handle both object and string
1996
+ timestamp: currentTimestamp.toString()
1997
+ });
1998
+ currentTimestamp += 1;
1884
1999
  }
1885
- return description;
2000
+ return operations;
1886
2001
  }
1887
- function generateMigrationFilename(diff, config) {
1888
- const timestamp = generateTimestamp(config);
1889
- const description = generateMigrationDescription(diff);
1890
- return `${timestamp}_${description}.js`;
2002
+ function generateCollectionMigrationFilename(operation) {
2003
+ const timestamp = operation.timestamp;
2004
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
2005
+ let collectionName;
2006
+ if (typeof operation.collection === "string") {
2007
+ collectionName = operation.collection;
2008
+ } else {
2009
+ collectionName = operation.collection.name;
2010
+ }
2011
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
2012
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
1891
2013
  }
1892
2014
  function createMigrationFileStructure(upCode, downCode, config) {
1893
2015
  const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
@@ -1959,14 +2081,13 @@ function formatValue(value) {
1959
2081
  return "null";
1960
2082
  }
1961
2083
  if (typeof value === "string") {
1962
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
2084
+ return JSON.stringify(value);
1963
2085
  }
1964
2086
  if (typeof value === "number" || typeof value === "boolean") {
1965
2087
  return String(value);
1966
2088
  }
1967
2089
  if (Array.isArray(value)) {
1968
- const items = value.map((v) => formatValue(v)).join(", ");
1969
- return `[${items}]`;
2090
+ return JSON.stringify(value).replace(/","/g, '", "');
1970
2091
  }
1971
2092
  if (typeof value === "object") {
1972
2093
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -1974,7 +2095,7 @@ function formatValue(value) {
1974
2095
  }
1975
2096
  return String(value);
1976
2097
  }
1977
- function generateFieldDefinitionObject(field) {
2098
+ function generateFieldDefinitionObject(field, collectionIdMap) {
1978
2099
  const parts = [];
1979
2100
  parts.push(` name: "${field.name}"`);
1980
2101
  parts.push(` type: "${field.type}"`);
@@ -1982,34 +2103,47 @@ function generateFieldDefinitionObject(field) {
1982
2103
  if (field.unique !== void 0) {
1983
2104
  parts.push(` unique: ${field.unique}`);
1984
2105
  }
2106
+ if (field.type === "select") {
2107
+ const maxSelect = field.options?.maxSelect ?? 1;
2108
+ parts.push(` maxSelect: ${maxSelect}`);
2109
+ const values = field.options?.values ?? [];
2110
+ parts.push(` values: ${formatValue(values)}`);
2111
+ }
1985
2112
  if (field.options && Object.keys(field.options).length > 0) {
1986
2113
  for (const [key, value] of Object.entries(field.options)) {
2114
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2115
+ continue;
2116
+ }
1987
2117
  parts.push(` ${key}: ${formatValue(value)}`);
1988
2118
  }
1989
2119
  }
1990
2120
  if (field.relation) {
1991
2121
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
1992
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
1993
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
1994
- if (field.relation.maxSelect !== void 0) {
1995
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
1996
- }
1997
- if (field.relation.minSelect !== void 0) {
1998
- parts.push(` minSelect: ${field.relation.minSelect}`);
1999
- }
2000
- if (field.relation.cascadeDelete !== void 0) {
2001
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2122
+ let collectionIdValue;
2123
+ if (isUsersCollection) {
2124
+ collectionIdValue = '"_pb_users_auth_"';
2125
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2126
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2127
+ } else {
2128
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2002
2129
  }
2130
+ parts.push(` collectionId: ${collectionIdValue}`);
2131
+ const maxSelect = field.relation.maxSelect ?? 1;
2132
+ parts.push(` maxSelect: ${maxSelect}`);
2133
+ const minSelect = field.relation.minSelect ?? null;
2134
+ parts.push(` minSelect: ${minSelect}`);
2135
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2136
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2003
2137
  }
2004
2138
  return ` {
2005
2139
  ${parts.join(",\n")},
2006
2140
  }`;
2007
2141
  }
2008
- function generateFieldsArray(fields) {
2142
+ function generateFieldsArray(fields, collectionIdMap) {
2009
2143
  if (fields.length === 0) {
2010
2144
  return "[]";
2011
2145
  }
2012
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
2146
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
2013
2147
  return `[
2014
2148
  ${fieldObjects.join(",\n")},
2015
2149
  ]`;
@@ -2068,7 +2202,7 @@ function generateIndexesArray(indexes) {
2068
2202
  if (!indexes || indexes.length === 0) {
2069
2203
  return "[]";
2070
2204
  }
2071
- const indexStrings = indexes.map((idx) => `"${idx}"`);
2205
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
2072
2206
  return `[
2073
2207
  ${indexStrings.join(",\n ")},
2074
2208
  ]`;
@@ -2122,7 +2256,7 @@ function getSystemFields() {
2122
2256
  }
2123
2257
  ];
2124
2258
  }
2125
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2259
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
2126
2260
  const lines = [];
2127
2261
  lines.push(` const ${varName} = new Collection({`);
2128
2262
  lines.push(` name: "${collection.name}",`);
@@ -2136,7 +2270,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
2136
2270
  }
2137
2271
  const systemFields = getSystemFields();
2138
2272
  const allFields = [...systemFields, ...collection.fields];
2139
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
2273
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
2140
2274
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2141
2275
  lines.push(` });`);
2142
2276
  lines.push(``);
@@ -2158,42 +2292,59 @@ function getFieldConstructorName(fieldType) {
2158
2292
  };
2159
2293
  return constructorMap[fieldType] || "TextField";
2160
2294
  }
2161
- function generateFieldConstructorOptions(field) {
2295
+ function generateFieldConstructorOptions(field, collectionIdMap) {
2162
2296
  const parts = [];
2163
2297
  parts.push(` name: "${field.name}"`);
2164
2298
  parts.push(` required: ${field.required}`);
2165
2299
  if (field.unique !== void 0) {
2166
2300
  parts.push(` unique: ${field.unique}`);
2167
2301
  }
2302
+ if (field.type === "select") {
2303
+ const maxSelect = field.options?.maxSelect ?? 1;
2304
+ parts.push(` maxSelect: ${maxSelect}`);
2305
+ const values = field.options?.values ?? [];
2306
+ parts.push(` values: ${formatValue(values)}`);
2307
+ }
2168
2308
  if (field.options && Object.keys(field.options).length > 0) {
2169
2309
  for (const [key, value] of Object.entries(field.options)) {
2170
- parts.push(` ${key}: ${formatValue(value)}`);
2310
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2311
+ continue;
2312
+ }
2313
+ if (field.type === "number" && key === "noDecimal") {
2314
+ parts.push(` onlyInt: ${formatValue(value)}`);
2315
+ } else {
2316
+ parts.push(` ${key}: ${formatValue(value)}`);
2317
+ }
2171
2318
  }
2172
2319
  }
2173
2320
  if (field.relation && field.type === "relation") {
2174
2321
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2175
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2176
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2177
- if (field.relation.maxSelect !== void 0) {
2178
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2179
- }
2180
- if (field.relation.minSelect !== void 0) {
2181
- parts.push(` minSelect: ${field.relation.minSelect}`);
2182
- }
2183
- if (field.relation.cascadeDelete !== void 0) {
2184
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2322
+ let collectionIdValue;
2323
+ if (isUsersCollection) {
2324
+ collectionIdValue = '"_pb_users_auth_"';
2325
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2326
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2327
+ } else {
2328
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2185
2329
  }
2330
+ parts.push(` collectionId: ${collectionIdValue}`);
2331
+ const maxSelect = field.relation.maxSelect ?? 1;
2332
+ parts.push(` maxSelect: ${maxSelect}`);
2333
+ const minSelect = field.relation.minSelect ?? null;
2334
+ parts.push(` minSelect: ${minSelect}`);
2335
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2336
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2186
2337
  }
2187
2338
  return parts.join(",\n");
2188
2339
  }
2189
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
2340
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
2190
2341
  const lines = [];
2191
2342
  const constructorName = getFieldConstructorName(field.type);
2192
2343
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
2193
2344
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2194
2345
  lines.push(``);
2195
2346
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
2196
- lines.push(generateFieldConstructorOptions(field));
2347
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
2197
2348
  lines.push(` }));`);
2198
2349
  lines.push(``);
2199
2350
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -2243,7 +2394,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2243
2394
  const lines = [];
2244
2395
  const collectionVar = varName || `collection_${collectionName}_idx`;
2245
2396
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2246
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
2397
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
2247
2398
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2248
2399
  return lines.join("\n");
2249
2400
  }
@@ -2252,7 +2403,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2252
2403
  const collectionVar = varName || `collection_${collectionName}_idx`;
2253
2404
  const indexVar = `${collectionVar}_indexToRemove`;
2254
2405
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2255
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
2406
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
2256
2407
  lines.push(` if (${indexVar} !== -1) {`);
2257
2408
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2258
2409
  lines.push(` }`);
@@ -2281,243 +2432,194 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
2281
2432
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2282
2433
  return lines.join("\n");
2283
2434
  }
2284
- function generateUpMigration(diff) {
2435
+ function generateOperationUpMigration(operation, collectionIdMap) {
2285
2436
  const lines = [];
2286
- lines.push(` // UP MIGRATION`);
2287
- lines.push(``);
2288
- if (diff.collectionsToCreate.length > 0) {
2289
- lines.push(` // Create new collections`);
2290
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2291
- const collection = diff.collectionsToCreate[i];
2292
- const varName = `collection_${collection.name}_create`;
2293
- lines.push(generateCollectionCreation(collection, varName));
2294
- lines.push(``);
2295
- }
2296
- }
2297
- if (diff.collectionsToModify.length > 0) {
2298
- lines.push(` // Modify existing collections`);
2299
- for (const modification of diff.collectionsToModify) {
2300
- const collectionName = modification.collection;
2301
- if (modification.fieldsToAdd.length > 0) {
2302
- lines.push(` // Add fields to ${collectionName}`);
2303
- for (const field of modification.fieldsToAdd) {
2304
- const varName = `collection_${collectionName}_add_${field.name}`;
2305
- lines.push(generateFieldAddition(collectionName, field, varName));
2306
- lines.push(``);
2307
- }
2308
- }
2309
- if (modification.fieldsToModify.length > 0) {
2310
- lines.push(` // Modify fields in ${collectionName}`);
2311
- for (const fieldMod of modification.fieldsToModify) {
2312
- const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2313
- lines.push(generateFieldModification(collectionName, fieldMod, varName));
2314
- lines.push(``);
2315
- }
2316
- }
2317
- if (modification.fieldsToRemove.length > 0) {
2318
- lines.push(` // Remove fields from ${collectionName}`);
2319
- for (const field of modification.fieldsToRemove) {
2320
- const varName = `collection_${collectionName}_remove_${field.name}`;
2321
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2322
- lines.push(``);
2323
- }
2324
- }
2325
- if (modification.indexesToAdd.length > 0) {
2326
- lines.push(` // Add indexes to ${collectionName}`);
2327
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2328
- const index = modification.indexesToAdd[i];
2329
- const varName = `collection_${collectionName}_addidx_${i}`;
2330
- lines.push(generateIndexAddition(collectionName, index, varName));
2331
- lines.push(``);
2332
- }
2333
- }
2334
- if (modification.indexesToRemove.length > 0) {
2335
- lines.push(` // Remove indexes from ${collectionName}`);
2336
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2337
- const index = modification.indexesToRemove[i];
2338
- const varName = `collection_${collectionName}_rmidx_${i}`;
2339
- lines.push(generateIndexRemoval(collectionName, index, varName));
2340
- lines.push(``);
2341
- }
2342
- }
2343
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2344
- lines.push(` // Update permissions for ${collectionName}`);
2345
- for (const permission of modification.permissionsToUpdate) {
2346
- const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2347
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName));
2348
- lines.push(``);
2349
- }
2350
- } else if (modification.rulesToUpdate.length > 0) {
2351
- lines.push(` // Update rules for ${collectionName}`);
2352
- for (const rule of modification.rulesToUpdate) {
2353
- const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2354
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName));
2355
- lines.push(``);
2356
- }
2357
- }
2437
+ if (operation.type === "create") {
2438
+ const collection = operation.collection;
2439
+ const varName = `collection_${collection.name}`;
2440
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2441
+ } else if (operation.type === "modify") {
2442
+ const modification = operation.modifications;
2443
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2444
+ let operationCount = 0;
2445
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2446
+ for (const field of modification.fieldsToAdd) {
2447
+ operationCount++;
2448
+ const varName = `collection_${collectionName}_add_${field.name}`;
2449
+ const isLast = operationCount === totalOperations;
2450
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2451
+ if (!isLast) lines.push("");
2358
2452
  }
2359
- }
2360
- if (diff.collectionsToDelete.length > 0) {
2361
- lines.push(` // Delete collections`);
2362
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2363
- const collection = diff.collectionsToDelete[i];
2364
- const varName = `collection_${collection.name}_delete`;
2365
- lines.push(generateCollectionDeletion(collection.name, varName));
2366
- lines.push(``);
2367
- }
2368
- }
2369
- if (lines.length === 2) {
2370
- lines.push(` // No changes detected`);
2371
- lines.push(``);
2372
- }
2373
- let code = lines.join("\n");
2374
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2375
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2376
- const saveMatches = [...code.matchAll(savePattern)];
2377
- const deleteMatches = [...code.matchAll(deletePattern)];
2378
- const allMatches = [
2379
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2380
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2381
- ].sort((a, b) => b.index - a.index);
2382
- if (allMatches.length > 0) {
2383
- const lastMatch = allMatches[0];
2384
- if (lastMatch.type === "save") {
2385
- 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);
2386
- } else {
2387
- 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);
2453
+ for (const fieldMod of modification.fieldsToModify) {
2454
+ operationCount++;
2455
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2456
+ const isLast = operationCount === totalOperations;
2457
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
2458
+ if (!isLast) lines.push("");
2388
2459
  }
2460
+ for (const field of modification.fieldsToRemove) {
2461
+ operationCount++;
2462
+ const varName = `collection_${collectionName}_remove_${field.name}`;
2463
+ const isLast = operationCount === totalOperations;
2464
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2465
+ if (!isLast) lines.push("");
2466
+ }
2467
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2468
+ operationCount++;
2469
+ const index = modification.indexesToAdd[i];
2470
+ const varName = `collection_${collectionName}_addidx_${i}`;
2471
+ const isLast = operationCount === totalOperations;
2472
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2473
+ if (!isLast) lines.push("");
2474
+ }
2475
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2476
+ operationCount++;
2477
+ const index = modification.indexesToRemove[i];
2478
+ const varName = `collection_${collectionName}_rmidx_${i}`;
2479
+ const isLast = operationCount === totalOperations;
2480
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2481
+ if (!isLast) lines.push("");
2482
+ }
2483
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2484
+ for (const permission of modification.permissionsToUpdate) {
2485
+ operationCount++;
2486
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2487
+ const isLast = operationCount === totalOperations;
2488
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
2489
+ if (!isLast) lines.push("");
2490
+ }
2491
+ } else if (modification.rulesToUpdate.length > 0) {
2492
+ for (const rule of modification.rulesToUpdate) {
2493
+ operationCount++;
2494
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2495
+ const isLast = operationCount === totalOperations;
2496
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
2497
+ if (!isLast) lines.push("");
2498
+ }
2499
+ }
2500
+ } else if (operation.type === "delete") {
2501
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
2502
+ const varName = `collection_${collectionName}`;
2503
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
2389
2504
  }
2390
- return code;
2505
+ return lines.join("\n");
2391
2506
  }
2392
- function generateDownMigration(diff) {
2507
+ function generateOperationDownMigration(operation, collectionIdMap) {
2393
2508
  const lines = [];
2394
- lines.push(` // DOWN MIGRATION (ROLLBACK)`);
2395
- lines.push(``);
2396
- if (diff.collectionsToDelete.length > 0) {
2397
- lines.push(` // Recreate deleted collections`);
2398
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2399
- const collection = diff.collectionsToDelete[i];
2400
- const varName = `collection_${collection.name}_recreate`;
2401
- lines.push(generateCollectionCreation(collection, varName));
2402
- lines.push(``);
2509
+ if (operation.type === "create") {
2510
+ const collection = operation.collection;
2511
+ const varName = `collection_${collection.name}`;
2512
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
2513
+ } else if (operation.type === "modify") {
2514
+ const modification = operation.modifications;
2515
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2516
+ let operationCount = 0;
2517
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2518
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2519
+ for (const permission of modification.permissionsToUpdate) {
2520
+ operationCount++;
2521
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2522
+ const isLast = operationCount === totalOperations;
2523
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
2524
+ if (!isLast) lines.push("");
2525
+ }
2526
+ } else if (modification.rulesToUpdate.length > 0) {
2527
+ for (const rule of modification.rulesToUpdate) {
2528
+ operationCount++;
2529
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2530
+ const isLast = operationCount === totalOperations;
2531
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
2532
+ if (!isLast) lines.push("");
2533
+ }
2534
+ }
2535
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2536
+ operationCount++;
2537
+ const index = modification.indexesToRemove[i];
2538
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
2539
+ const isLast = operationCount === totalOperations;
2540
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2541
+ if (!isLast) lines.push("");
2542
+ }
2543
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2544
+ operationCount++;
2545
+ const index = modification.indexesToAdd[i];
2546
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
2547
+ const isLast = operationCount === totalOperations;
2548
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2549
+ if (!isLast) lines.push("");
2403
2550
  }
2404
- }
2405
- if (diff.collectionsToModify.length > 0) {
2406
- lines.push(` // Revert modifications`);
2407
- for (const modification of diff.collectionsToModify) {
2408
- const collectionName = modification.collection;
2409
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2410
- lines.push(` // Revert permissions for ${collectionName}`);
2411
- for (const permission of modification.permissionsToUpdate) {
2412
- const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2413
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName));
2414
- lines.push(``);
2415
- }
2416
- } else if (modification.rulesToUpdate.length > 0) {
2417
- lines.push(` // Revert rules for ${collectionName}`);
2418
- for (const rule of modification.rulesToUpdate) {
2419
- const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2420
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName));
2421
- lines.push(``);
2422
- }
2423
- }
2424
- if (modification.indexesToRemove.length > 0) {
2425
- lines.push(` // Restore indexes to ${collectionName}`);
2426
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2427
- const index = modification.indexesToRemove[i];
2428
- const varName = `collection_${collectionName}_restore_idx_${i}`;
2429
- lines.push(generateIndexAddition(collectionName, index, varName));
2430
- lines.push(``);
2431
- }
2432
- }
2433
- if (modification.indexesToAdd.length > 0) {
2434
- lines.push(` // Remove indexes from ${collectionName}`);
2435
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2436
- const index = modification.indexesToAdd[i];
2437
- const varName = `collection_${collectionName}_revert_idx_${i}`;
2438
- lines.push(generateIndexRemoval(collectionName, index, varName));
2439
- lines.push(``);
2440
- }
2441
- }
2442
- if (modification.fieldsToRemove.length > 0) {
2443
- lines.push(` // Restore fields to ${collectionName}`);
2444
- for (const field of modification.fieldsToRemove) {
2445
- const varName = `collection_${collectionName}_restore_${field.name}`;
2446
- lines.push(generateFieldAddition(collectionName, field, varName));
2447
- lines.push(``);
2448
- }
2449
- }
2450
- if (modification.fieldsToModify.length > 0) {
2451
- lines.push(` // Revert field modifications in ${collectionName}`);
2452
- for (const fieldMod of modification.fieldsToModify) {
2453
- const reverseChanges = fieldMod.changes.map((change) => ({
2454
- property: change.property,
2455
- oldValue: change.newValue,
2456
- newValue: change.oldValue
2457
- }));
2458
- const reverseMod = {
2459
- fieldName: fieldMod.fieldName,
2460
- currentDefinition: fieldMod.newDefinition,
2461
- newDefinition: fieldMod.currentDefinition,
2462
- changes: reverseChanges
2463
- };
2464
- const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2465
- lines.push(generateFieldModification(collectionName, reverseMod, varName));
2466
- lines.push(``);
2467
- }
2468
- }
2469
- if (modification.fieldsToAdd.length > 0) {
2470
- lines.push(` // Remove added fields from ${collectionName}`);
2471
- for (const field of modification.fieldsToAdd) {
2472
- const varName = `collection_${collectionName}_revert_add_${field.name}`;
2473
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2474
- lines.push(``);
2475
- }
2476
- }
2551
+ for (const field of modification.fieldsToRemove) {
2552
+ operationCount++;
2553
+ const varName = `collection_${collectionName}_restore_${field.name}`;
2554
+ const isLast = operationCount === totalOperations;
2555
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2556
+ if (!isLast) lines.push("");
2477
2557
  }
2478
- }
2479
- if (diff.collectionsToCreate.length > 0) {
2480
- lines.push(` // Delete created collections`);
2481
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2482
- const collection = diff.collectionsToCreate[i];
2483
- const varName = `collection_${collection.name}_rollback`;
2484
- lines.push(generateCollectionDeletion(collection.name, varName));
2485
- lines.push(``);
2486
- }
2487
- }
2488
- if (lines.length === 2) {
2489
- lines.push(` // No changes to revert`);
2490
- lines.push(``);
2491
- }
2492
- let code = lines.join("\n");
2493
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2494
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2495
- const saveMatches = [...code.matchAll(savePattern)];
2496
- const deleteMatches = [...code.matchAll(deletePattern)];
2497
- const allMatches = [
2498
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2499
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2500
- ].sort((a, b) => b.index - a.index);
2501
- if (allMatches.length > 0) {
2502
- const lastMatch = allMatches[0];
2503
- if (lastMatch.type === "save") {
2504
- 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);
2505
- } else {
2506
- 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);
2558
+ for (const fieldMod of modification.fieldsToModify) {
2559
+ operationCount++;
2560
+ const reverseChanges = fieldMod.changes.map((change) => ({
2561
+ property: change.property,
2562
+ oldValue: change.newValue,
2563
+ newValue: change.oldValue
2564
+ }));
2565
+ const reverseMod = {
2566
+ fieldName: fieldMod.fieldName,
2567
+ currentDefinition: fieldMod.newDefinition,
2568
+ newDefinition: fieldMod.currentDefinition,
2569
+ changes: reverseChanges
2570
+ };
2571
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2572
+ const isLast = operationCount === totalOperations;
2573
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
2574
+ if (!isLast) lines.push("");
2575
+ }
2576
+ for (const field of modification.fieldsToAdd) {
2577
+ operationCount++;
2578
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
2579
+ const isLast = operationCount === totalOperations;
2580
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2581
+ if (!isLast) lines.push("");
2582
+ }
2583
+ } else if (operation.type === "delete") {
2584
+ const collection = operation.collection;
2585
+ if (typeof collection !== "string") {
2586
+ const varName = `collection_${collection.name}`;
2587
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2507
2588
  }
2508
2589
  }
2509
- return code;
2590
+ return lines.join("\n");
2510
2591
  }
2511
2592
  function generate(diff, config) {
2512
2593
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
2513
2594
  try {
2514
2595
  const migrationDir = resolveMigrationDir(normalizedConfig);
2515
- const upCode = generateUpMigration(diff);
2516
- const downCode = generateDownMigration(diff);
2517
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2518
- const filename = generateMigrationFilename(diff, normalizedConfig);
2519
- const filePath = writeMigrationFile(migrationDir, filename, content);
2520
- return filePath;
2596
+ const hasChanges4 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
2597
+ if (!hasChanges4) {
2598
+ return [];
2599
+ }
2600
+ const collectionIdMap = /* @__PURE__ */ new Map();
2601
+ for (const collection of diff.collectionsToCreate) {
2602
+ if (collection.id) {
2603
+ collectionIdMap.set(collection.name, collection.id);
2604
+ }
2605
+ }
2606
+ for (const collection of diff.collectionsToDelete) {
2607
+ if (collection.id) {
2608
+ collectionIdMap.set(collection.name, collection.id);
2609
+ }
2610
+ }
2611
+ const baseTimestamp = generateTimestamp(normalizedConfig);
2612
+ const operations = splitDiffByCollection(diff, baseTimestamp);
2613
+ const filePaths = [];
2614
+ for (const operation of operations) {
2615
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
2616
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
2617
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2618
+ const filename = generateCollectionMigrationFilename(operation);
2619
+ const filePath = writeMigrationFile(migrationDir, filename, content);
2620
+ filePaths.push(filePath);
2621
+ }
2622
+ return filePaths;
2521
2623
  } catch (error) {
2522
2624
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
2523
2625
  throw error;
@@ -3598,15 +3700,25 @@ async function executeGenerate(options) {
3598
3700
  process.exit(1);
3599
3701
  }
3600
3702
  logSection("\u{1F4DD} Generating Migration");
3601
- const migrationPath = await withProgress(
3703
+ const migrationPaths = await withProgress(
3602
3704
  "Creating migration file...",
3603
3705
  () => Promise.resolve(generate(diff, migrationsDir))
3604
3706
  );
3605
- logSuccess(`Migration file created: ${path5.basename(migrationPath)}`);
3707
+ if (migrationPaths.length === 0) {
3708
+ logWarning("No migration files were generated (no changes detected).");
3709
+ return;
3710
+ }
3711
+ if (migrationPaths.length === 1) {
3712
+ logSuccess(`Migration file created: ${path5.basename(migrationPaths[0])}`);
3713
+ } else {
3714
+ logSuccess(`Created ${migrationPaths.length} migration files`);
3715
+ }
3606
3716
  logSection("\u2705 Next Steps");
3607
3717
  console.log();
3608
- console.log(" 1. Review the generated migration file:");
3609
- console.log(` ${migrationPath}`);
3718
+ console.log(" 1. Review the generated migration file(s):");
3719
+ migrationPaths.forEach((migrationPath) => {
3720
+ console.log(` ${migrationPath}`);
3721
+ });
3610
3722
  console.log();
3611
3723
  console.log(" 2. Apply the migration by running PocketBase:");
3612
3724
  console.log(" yarn pb");