pocketbase-zod-schema 0.2.5 → 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 (66) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cli/index.cjs +374 -296
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.d.cts +2 -2
  5. package/dist/cli/index.d.ts +2 -2
  6. package/dist/cli/index.js +374 -296
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/migrate.cjs +374 -296
  9. package/dist/cli/migrate.cjs.map +1 -1
  10. package/dist/cli/migrate.js +374 -296
  11. package/dist/cli/migrate.js.map +1 -1
  12. package/dist/cli/utils/index.d.cts +2 -2
  13. package/dist/cli/utils/index.d.ts +2 -2
  14. package/dist/{fields-YjcpBXVp.d.cts → fields-UcOPu1OQ.d.cts} +16 -0
  15. package/dist/{fields-YjcpBXVp.d.ts → fields-UcOPu1OQ.d.ts} +16 -0
  16. package/dist/index.cjs +413 -114
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -4
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +414 -103
  21. package/dist/index.js.map +1 -1
  22. package/dist/migration/analyzer.cjs +12 -2
  23. package/dist/migration/analyzer.cjs.map +1 -1
  24. package/dist/migration/analyzer.d.cts +2 -2
  25. package/dist/migration/analyzer.d.ts +2 -2
  26. package/dist/migration/analyzer.js +12 -2
  27. package/dist/migration/analyzer.js.map +1 -1
  28. package/dist/migration/diff.cjs +76 -1
  29. package/dist/migration/diff.cjs.map +1 -1
  30. package/dist/migration/diff.d.cts +2 -2
  31. package/dist/migration/diff.d.ts +2 -2
  32. package/dist/migration/diff.js +76 -1
  33. package/dist/migration/diff.js.map +1 -1
  34. package/dist/migration/generator.cjs +323 -46
  35. package/dist/migration/generator.cjs.map +1 -1
  36. package/dist/migration/generator.d.cts +59 -12
  37. package/dist/migration/generator.d.ts +59 -12
  38. package/dist/migration/generator.js +319 -47
  39. package/dist/migration/generator.js.map +1 -1
  40. package/dist/migration/index.cjs +399 -49
  41. package/dist/migration/index.cjs.map +1 -1
  42. package/dist/migration/index.d.cts +3 -3
  43. package/dist/migration/index.d.ts +3 -3
  44. package/dist/migration/index.js +399 -49
  45. package/dist/migration/index.js.map +1 -1
  46. package/dist/migration/snapshot.cjs.map +1 -1
  47. package/dist/migration/snapshot.d.cts +2 -2
  48. package/dist/migration/snapshot.d.ts +2 -2
  49. package/dist/migration/snapshot.js.map +1 -1
  50. package/dist/migration/utils/index.cjs +64 -0
  51. package/dist/migration/utils/index.cjs.map +1 -1
  52. package/dist/migration/utils/index.d.cts +39 -202
  53. package/dist/migration/utils/index.d.ts +39 -202
  54. package/dist/migration/utils/index.js +63 -1
  55. package/dist/migration/utils/index.js.map +1 -1
  56. package/dist/schema.cjs +0 -61
  57. package/dist/schema.cjs.map +1 -1
  58. package/dist/schema.d.cts +2 -86
  59. package/dist/schema.d.ts +2 -86
  60. package/dist/schema.js +1 -50
  61. package/dist/schema.js.map +1 -1
  62. package/dist/type-mapper-DrQmtznD.d.cts +208 -0
  63. package/dist/type-mapper-n231Fspm.d.ts +208 -0
  64. package/dist/{types-LFBGHl9Y.d.ts → types-Ds3NQvny.d.ts} +33 -2
  65. package/dist/{types-mhQXWNi3.d.cts → types-YoBjsa-A.d.cts} +33 -2
  66. 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
 
@@ -1288,12 +1289,22 @@ function isAuthCollection(fields) {
1288
1289
  function buildFieldDefinition(fieldName, zodType) {
1289
1290
  const fieldMetadata = extractFieldMetadata(zodType.description);
1290
1291
  if (fieldMetadata) {
1291
- const required2 = isFieldRequired(zodType);
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 || {};
1292
1303
  const fieldDef2 = {
1293
1304
  name: fieldName,
1294
1305
  type: fieldMetadata.type,
1295
1306
  required: required2,
1296
- options: fieldMetadata.options
1307
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1297
1308
  };
1298
1309
  if (fieldMetadata.type === "relation") {
1299
1310
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -1463,6 +1474,65 @@ async function buildSchemaDefinition(config) {
1463
1474
  async function parseSchemaFiles(config) {
1464
1475
  return buildSchemaDefinition(config);
1465
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
+ };
1466
1536
 
1467
1537
  // src/migration/diff.ts
1468
1538
  var DEFAULT_CONFIG2 = {
@@ -1794,6 +1864,18 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1794
1864
  const filteredCollectionsToDelete = collectionsToDelete.filter(
1795
1865
  (collection) => !isSystemCollection(collection.name, config)
1796
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
+ });
1797
1879
  const collectionsToModify = [];
1798
1880
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
1799
1881
  for (const [currentCollection, previousCollection] of matchedCollections) {
@@ -1803,7 +1885,7 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1803
1885
  }
1804
1886
  }
1805
1887
  return {
1806
- collectionsToCreate: filteredCollectionsToCreate,
1888
+ collectionsToCreate: collectionsWithIds,
1807
1889
  collectionsToDelete: filteredCollectionsToDelete,
1808
1890
  collectionsToModify
1809
1891
  };
@@ -1886,42 +1968,48 @@ function generateTimestamp(config) {
1886
1968
  }
1887
1969
  return Math.floor(Date.now() / 1e3).toString();
1888
1970
  }
1889
- function generateMigrationDescription(diff) {
1890
- const parts = [];
1891
- if (diff.collectionsToCreate.length > 0) {
1892
- if (diff.collectionsToCreate.length === 1) {
1893
- parts.push(`created_${diff.collectionsToCreate[0].name}`);
1894
- } else {
1895
- parts.push(`created_${diff.collectionsToCreate.length}_collections`);
1896
- }
1897
- }
1898
- if (diff.collectionsToDelete.length > 0) {
1899
- if (diff.collectionsToDelete.length === 1) {
1900
- parts.push(`deleted_${diff.collectionsToDelete[0].name}`);
1901
- } else {
1902
- parts.push(`deleted_${diff.collectionsToDelete.length}_collections`);
1903
- }
1904
- }
1905
- if (diff.collectionsToModify.length > 0) {
1906
- if (diff.collectionsToModify.length === 1) {
1907
- parts.push(`updated_${diff.collectionsToModify[0].collection}`);
1908
- } else {
1909
- parts.push(`updated_${diff.collectionsToModify.length}_collections`);
1910
- }
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;
1911
1981
  }
1912
- if (parts.length === 0) {
1913
- 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;
1914
1990
  }
1915
- let description = parts.join("_");
1916
- if (description.length > 80) {
1917
- 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;
1918
1999
  }
1919
- return description;
2000
+ return operations;
1920
2001
  }
1921
- function generateMigrationFilename(diff, config) {
1922
- const timestamp = generateTimestamp(config);
1923
- const description = generateMigrationDescription(diff);
1924
- 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`;
1925
2013
  }
1926
2014
  function createMigrationFileStructure(upCode, downCode, config) {
1927
2015
  const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
@@ -1993,14 +2081,13 @@ function formatValue(value) {
1993
2081
  return "null";
1994
2082
  }
1995
2083
  if (typeof value === "string") {
1996
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
2084
+ return JSON.stringify(value);
1997
2085
  }
1998
2086
  if (typeof value === "number" || typeof value === "boolean") {
1999
2087
  return String(value);
2000
2088
  }
2001
2089
  if (Array.isArray(value)) {
2002
- const items = value.map((v) => formatValue(v)).join(", ");
2003
- return `[${items}]`;
2090
+ return JSON.stringify(value).replace(/","/g, '", "');
2004
2091
  }
2005
2092
  if (typeof value === "object") {
2006
2093
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -2008,7 +2095,7 @@ function formatValue(value) {
2008
2095
  }
2009
2096
  return String(value);
2010
2097
  }
2011
- function generateFieldDefinitionObject(field) {
2098
+ function generateFieldDefinitionObject(field, collectionIdMap) {
2012
2099
  const parts = [];
2013
2100
  parts.push(` name: "${field.name}"`);
2014
2101
  parts.push(` type: "${field.type}"`);
@@ -2016,34 +2103,47 @@ function generateFieldDefinitionObject(field) {
2016
2103
  if (field.unique !== void 0) {
2017
2104
  parts.push(` unique: ${field.unique}`);
2018
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
+ }
2019
2112
  if (field.options && Object.keys(field.options).length > 0) {
2020
2113
  for (const [key, value] of Object.entries(field.options)) {
2114
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2115
+ continue;
2116
+ }
2021
2117
  parts.push(` ${key}: ${formatValue(value)}`);
2022
2118
  }
2023
2119
  }
2024
2120
  if (field.relation) {
2025
2121
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2026
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2027
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2028
- if (field.relation.maxSelect !== void 0) {
2029
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2030
- }
2031
- if (field.relation.minSelect !== void 0) {
2032
- parts.push(` minSelect: ${field.relation.minSelect}`);
2033
- }
2034
- if (field.relation.cascadeDelete !== void 0) {
2035
- 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`;
2036
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}`);
2037
2137
  }
2038
2138
  return ` {
2039
2139
  ${parts.join(",\n")},
2040
2140
  }`;
2041
2141
  }
2042
- function generateFieldsArray(fields) {
2142
+ function generateFieldsArray(fields, collectionIdMap) {
2043
2143
  if (fields.length === 0) {
2044
2144
  return "[]";
2045
2145
  }
2046
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
2146
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
2047
2147
  return `[
2048
2148
  ${fieldObjects.join(",\n")},
2049
2149
  ]`;
@@ -2102,7 +2202,7 @@ function generateIndexesArray(indexes) {
2102
2202
  if (!indexes || indexes.length === 0) {
2103
2203
  return "[]";
2104
2204
  }
2105
- const indexStrings = indexes.map((idx) => `"${idx}"`);
2205
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
2106
2206
  return `[
2107
2207
  ${indexStrings.join(",\n ")},
2108
2208
  ]`;
@@ -2156,7 +2256,7 @@ function getSystemFields() {
2156
2256
  }
2157
2257
  ];
2158
2258
  }
2159
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2259
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
2160
2260
  const lines = [];
2161
2261
  lines.push(` const ${varName} = new Collection({`);
2162
2262
  lines.push(` name: "${collection.name}",`);
@@ -2170,7 +2270,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
2170
2270
  }
2171
2271
  const systemFields = getSystemFields();
2172
2272
  const allFields = [...systemFields, ...collection.fields];
2173
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
2273
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
2174
2274
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2175
2275
  lines.push(` });`);
2176
2276
  lines.push(``);
@@ -2192,42 +2292,59 @@ function getFieldConstructorName(fieldType) {
2192
2292
  };
2193
2293
  return constructorMap[fieldType] || "TextField";
2194
2294
  }
2195
- function generateFieldConstructorOptions(field) {
2295
+ function generateFieldConstructorOptions(field, collectionIdMap) {
2196
2296
  const parts = [];
2197
2297
  parts.push(` name: "${field.name}"`);
2198
2298
  parts.push(` required: ${field.required}`);
2199
2299
  if (field.unique !== void 0) {
2200
2300
  parts.push(` unique: ${field.unique}`);
2201
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
+ }
2202
2308
  if (field.options && Object.keys(field.options).length > 0) {
2203
2309
  for (const [key, value] of Object.entries(field.options)) {
2204
- 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
+ }
2205
2318
  }
2206
2319
  }
2207
2320
  if (field.relation && field.type === "relation") {
2208
2321
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2209
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2210
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2211
- if (field.relation.maxSelect !== void 0) {
2212
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2213
- }
2214
- if (field.relation.minSelect !== void 0) {
2215
- parts.push(` minSelect: ${field.relation.minSelect}`);
2216
- }
2217
- if (field.relation.cascadeDelete !== void 0) {
2218
- 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`;
2219
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}`);
2220
2337
  }
2221
2338
  return parts.join(",\n");
2222
2339
  }
2223
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
2340
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
2224
2341
  const lines = [];
2225
2342
  const constructorName = getFieldConstructorName(field.type);
2226
2343
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
2227
2344
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2228
2345
  lines.push(``);
2229
2346
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
2230
- lines.push(generateFieldConstructorOptions(field));
2347
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
2231
2348
  lines.push(` }));`);
2232
2349
  lines.push(``);
2233
2350
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -2277,7 +2394,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2277
2394
  const lines = [];
2278
2395
  const collectionVar = varName || `collection_${collectionName}_idx`;
2279
2396
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2280
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
2397
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
2281
2398
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2282
2399
  return lines.join("\n");
2283
2400
  }
@@ -2286,7 +2403,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2286
2403
  const collectionVar = varName || `collection_${collectionName}_idx`;
2287
2404
  const indexVar = `${collectionVar}_indexToRemove`;
2288
2405
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2289
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
2406
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
2290
2407
  lines.push(` if (${indexVar} !== -1) {`);
2291
2408
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2292
2409
  lines.push(` }`);
@@ -2315,243 +2432,194 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
2315
2432
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2316
2433
  return lines.join("\n");
2317
2434
  }
2318
- function generateUpMigration(diff) {
2435
+ function generateOperationUpMigration(operation, collectionIdMap) {
2319
2436
  const lines = [];
2320
- lines.push(` // UP MIGRATION`);
2321
- lines.push(``);
2322
- if (diff.collectionsToCreate.length > 0) {
2323
- lines.push(` // Create new collections`);
2324
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2325
- const collection = diff.collectionsToCreate[i];
2326
- const varName = `collection_${collection.name}_create`;
2327
- lines.push(generateCollectionCreation(collection, varName));
2328
- lines.push(``);
2329
- }
2330
- }
2331
- if (diff.collectionsToModify.length > 0) {
2332
- lines.push(` // Modify existing collections`);
2333
- for (const modification of diff.collectionsToModify) {
2334
- const collectionName = modification.collection;
2335
- if (modification.fieldsToAdd.length > 0) {
2336
- lines.push(` // Add fields to ${collectionName}`);
2337
- for (const field of modification.fieldsToAdd) {
2338
- const varName = `collection_${collectionName}_add_${field.name}`;
2339
- lines.push(generateFieldAddition(collectionName, field, varName));
2340
- lines.push(``);
2341
- }
2342
- }
2343
- if (modification.fieldsToModify.length > 0) {
2344
- lines.push(` // Modify fields in ${collectionName}`);
2345
- for (const fieldMod of modification.fieldsToModify) {
2346
- const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2347
- lines.push(generateFieldModification(collectionName, fieldMod, varName));
2348
- lines.push(``);
2349
- }
2350
- }
2351
- if (modification.fieldsToRemove.length > 0) {
2352
- lines.push(` // Remove fields from ${collectionName}`);
2353
- for (const field of modification.fieldsToRemove) {
2354
- const varName = `collection_${collectionName}_remove_${field.name}`;
2355
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2356
- lines.push(``);
2357
- }
2358
- }
2359
- if (modification.indexesToAdd.length > 0) {
2360
- lines.push(` // Add indexes to ${collectionName}`);
2361
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2362
- const index = modification.indexesToAdd[i];
2363
- const varName = `collection_${collectionName}_addidx_${i}`;
2364
- lines.push(generateIndexAddition(collectionName, index, varName));
2365
- lines.push(``);
2366
- }
2367
- }
2368
- if (modification.indexesToRemove.length > 0) {
2369
- lines.push(` // Remove indexes from ${collectionName}`);
2370
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2371
- const index = modification.indexesToRemove[i];
2372
- const varName = `collection_${collectionName}_rmidx_${i}`;
2373
- lines.push(generateIndexRemoval(collectionName, index, varName));
2374
- lines.push(``);
2375
- }
2376
- }
2377
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2378
- lines.push(` // Update permissions for ${collectionName}`);
2379
- for (const permission of modification.permissionsToUpdate) {
2380
- const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2381
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName));
2382
- lines.push(``);
2383
- }
2384
- } else if (modification.rulesToUpdate.length > 0) {
2385
- lines.push(` // Update rules for ${collectionName}`);
2386
- for (const rule of modification.rulesToUpdate) {
2387
- const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2388
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName));
2389
- lines.push(``);
2390
- }
2391
- }
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("");
2392
2452
  }
2393
- }
2394
- if (diff.collectionsToDelete.length > 0) {
2395
- lines.push(` // Delete collections`);
2396
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2397
- const collection = diff.collectionsToDelete[i];
2398
- const varName = `collection_${collection.name}_delete`;
2399
- lines.push(generateCollectionDeletion(collection.name, varName));
2400
- lines.push(``);
2401
- }
2402
- }
2403
- if (lines.length === 2) {
2404
- lines.push(` // No changes detected`);
2405
- lines.push(``);
2406
- }
2407
- let code = lines.join("\n");
2408
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2409
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2410
- const saveMatches = [...code.matchAll(savePattern)];
2411
- const deleteMatches = [...code.matchAll(deletePattern)];
2412
- const allMatches = [
2413
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2414
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2415
- ].sort((a, b) => b.index - a.index);
2416
- if (allMatches.length > 0) {
2417
- const lastMatch = allMatches[0];
2418
- if (lastMatch.type === "save") {
2419
- 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);
2420
- } else {
2421
- 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("");
2422
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));
2423
2504
  }
2424
- return code;
2505
+ return lines.join("\n");
2425
2506
  }
2426
- function generateDownMigration(diff) {
2507
+ function generateOperationDownMigration(operation, collectionIdMap) {
2427
2508
  const lines = [];
2428
- lines.push(` // DOWN MIGRATION (ROLLBACK)`);
2429
- lines.push(``);
2430
- if (diff.collectionsToDelete.length > 0) {
2431
- lines.push(` // Recreate deleted collections`);
2432
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2433
- const collection = diff.collectionsToDelete[i];
2434
- const varName = `collection_${collection.name}_recreate`;
2435
- lines.push(generateCollectionCreation(collection, varName));
2436
- 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("");
2437
2550
  }
2438
- }
2439
- if (diff.collectionsToModify.length > 0) {
2440
- lines.push(` // Revert modifications`);
2441
- for (const modification of diff.collectionsToModify) {
2442
- const collectionName = modification.collection;
2443
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2444
- lines.push(` // Revert permissions for ${collectionName}`);
2445
- for (const permission of modification.permissionsToUpdate) {
2446
- const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2447
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName));
2448
- lines.push(``);
2449
- }
2450
- } else if (modification.rulesToUpdate.length > 0) {
2451
- lines.push(` // Revert rules for ${collectionName}`);
2452
- for (const rule of modification.rulesToUpdate) {
2453
- const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2454
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName));
2455
- lines.push(``);
2456
- }
2457
- }
2458
- if (modification.indexesToRemove.length > 0) {
2459
- lines.push(` // Restore indexes to ${collectionName}`);
2460
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2461
- const index = modification.indexesToRemove[i];
2462
- const varName = `collection_${collectionName}_restore_idx_${i}`;
2463
- lines.push(generateIndexAddition(collectionName, index, varName));
2464
- lines.push(``);
2465
- }
2466
- }
2467
- if (modification.indexesToAdd.length > 0) {
2468
- lines.push(` // Remove indexes from ${collectionName}`);
2469
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2470
- const index = modification.indexesToAdd[i];
2471
- const varName = `collection_${collectionName}_revert_idx_${i}`;
2472
- lines.push(generateIndexRemoval(collectionName, index, varName));
2473
- lines.push(``);
2474
- }
2475
- }
2476
- if (modification.fieldsToRemove.length > 0) {
2477
- lines.push(` // Restore fields to ${collectionName}`);
2478
- for (const field of modification.fieldsToRemove) {
2479
- const varName = `collection_${collectionName}_restore_${field.name}`;
2480
- lines.push(generateFieldAddition(collectionName, field, varName));
2481
- lines.push(``);
2482
- }
2483
- }
2484
- if (modification.fieldsToModify.length > 0) {
2485
- lines.push(` // Revert field modifications in ${collectionName}`);
2486
- for (const fieldMod of modification.fieldsToModify) {
2487
- const reverseChanges = fieldMod.changes.map((change) => ({
2488
- property: change.property,
2489
- oldValue: change.newValue,
2490
- newValue: change.oldValue
2491
- }));
2492
- const reverseMod = {
2493
- fieldName: fieldMod.fieldName,
2494
- currentDefinition: fieldMod.newDefinition,
2495
- newDefinition: fieldMod.currentDefinition,
2496
- changes: reverseChanges
2497
- };
2498
- const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2499
- lines.push(generateFieldModification(collectionName, reverseMod, varName));
2500
- lines.push(``);
2501
- }
2502
- }
2503
- if (modification.fieldsToAdd.length > 0) {
2504
- lines.push(` // Remove added fields from ${collectionName}`);
2505
- for (const field of modification.fieldsToAdd) {
2506
- const varName = `collection_${collectionName}_revert_add_${field.name}`;
2507
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2508
- lines.push(``);
2509
- }
2510
- }
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("");
2511
2557
  }
2512
- }
2513
- if (diff.collectionsToCreate.length > 0) {
2514
- lines.push(` // Delete created collections`);
2515
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2516
- const collection = diff.collectionsToCreate[i];
2517
- const varName = `collection_${collection.name}_rollback`;
2518
- lines.push(generateCollectionDeletion(collection.name, varName));
2519
- lines.push(``);
2520
- }
2521
- }
2522
- if (lines.length === 2) {
2523
- lines.push(` // No changes to revert`);
2524
- lines.push(``);
2525
- }
2526
- let code = lines.join("\n");
2527
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2528
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2529
- const saveMatches = [...code.matchAll(savePattern)];
2530
- const deleteMatches = [...code.matchAll(deletePattern)];
2531
- const allMatches = [
2532
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2533
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2534
- ].sort((a, b) => b.index - a.index);
2535
- if (allMatches.length > 0) {
2536
- const lastMatch = allMatches[0];
2537
- if (lastMatch.type === "save") {
2538
- 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);
2539
- } else {
2540
- 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));
2541
2588
  }
2542
2589
  }
2543
- return code;
2590
+ return lines.join("\n");
2544
2591
  }
2545
2592
  function generate(diff, config) {
2546
2593
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
2547
2594
  try {
2548
2595
  const migrationDir = resolveMigrationDir(normalizedConfig);
2549
- const upCode = generateUpMigration(diff);
2550
- const downCode = generateDownMigration(diff);
2551
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2552
- const filename = generateMigrationFilename(diff, normalizedConfig);
2553
- const filePath = writeMigrationFile(migrationDir, filename, content);
2554
- 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;
2555
2623
  } catch (error) {
2556
2624
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
2557
2625
  throw error;
@@ -3632,15 +3700,25 @@ async function executeGenerate(options) {
3632
3700
  process.exit(1);
3633
3701
  }
3634
3702
  logSection("\u{1F4DD} Generating Migration");
3635
- const migrationPath = await withProgress(
3703
+ const migrationPaths = await withProgress(
3636
3704
  "Creating migration file...",
3637
3705
  () => Promise.resolve(generate(diff, migrationsDir))
3638
3706
  );
3639
- 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
+ }
3640
3716
  logSection("\u2705 Next Steps");
3641
3717
  console.log();
3642
- console.log(" 1. Review the generated migration file:");
3643
- console.log(` ${migrationPath}`);
3718
+ console.log(" 1. Review the generated migration file(s):");
3719
+ migrationPaths.forEach((migrationPath) => {
3720
+ console.log(` ${migrationPath}`);
3721
+ });
3644
3722
  console.log();
3645
3723
  console.log(" 2. Apply the migration by running PocketBase:");
3646
3724
  console.log(" yarn pb");