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
@@ -7,6 +7,7 @@ import * as path5 from 'path';
7
7
  import { dirname, join } from 'path';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { z } from 'zod';
10
+ import { randomBytes } from 'crypto';
10
11
  import ora from 'ora';
11
12
 
12
13
  ({
@@ -1292,12 +1293,22 @@ function isAuthCollection(fields) {
1292
1293
  function buildFieldDefinition(fieldName, zodType) {
1293
1294
  const fieldMetadata = extractFieldMetadata(zodType.description);
1294
1295
  if (fieldMetadata) {
1295
- const required2 = isFieldRequired(zodType);
1296
+ let required2;
1297
+ if (fieldMetadata.type === "number") {
1298
+ if (fieldMetadata.options?.required !== void 0) {
1299
+ required2 = fieldMetadata.options.required;
1300
+ } else {
1301
+ required2 = false;
1302
+ }
1303
+ } else {
1304
+ required2 = isFieldRequired(zodType);
1305
+ }
1306
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1296
1307
  const fieldDef2 = {
1297
1308
  name: fieldName,
1298
1309
  type: fieldMetadata.type,
1299
1310
  required: required2,
1300
- options: fieldMetadata.options
1311
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1301
1312
  };
1302
1313
  if (fieldMetadata.type === "relation") {
1303
1314
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -1467,6 +1478,65 @@ async function buildSchemaDefinition(config) {
1467
1478
  async function parseSchemaFiles(config) {
1468
1479
  return buildSchemaDefinition(config);
1469
1480
  }
1481
+ function generateCollectionId() {
1482
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
1483
+ const idLength = 15;
1484
+ const bytes = randomBytes(idLength);
1485
+ let id = "pb_";
1486
+ for (let i = 0; i < idLength; i++) {
1487
+ const index = bytes[i] % chars.length;
1488
+ id += chars[index];
1489
+ }
1490
+ return id;
1491
+ }
1492
+ var CollectionIdRegistry = class {
1493
+ ids;
1494
+ constructor() {
1495
+ this.ids = /* @__PURE__ */ new Set();
1496
+ }
1497
+ /**
1498
+ * Generates a unique collection ID for a given collection name
1499
+ * Special case: Returns constant "_pb_users_auth_" for users collection
1500
+ * Retries up to 10 times if collision occurs (extremely rare)
1501
+ *
1502
+ * @param collectionName - The name of the collection
1503
+ * @returns A unique collection ID
1504
+ * @throws Error if unable to generate unique ID after max attempts
1505
+ */
1506
+ generate(collectionName) {
1507
+ if (collectionName && collectionName.toLowerCase() === "users") {
1508
+ const usersId = "_pb_users_auth_";
1509
+ this.register(usersId);
1510
+ return usersId;
1511
+ }
1512
+ const maxAttempts = 10;
1513
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1514
+ const id = generateCollectionId();
1515
+ if (!this.has(id)) {
1516
+ this.register(id);
1517
+ return id;
1518
+ }
1519
+ }
1520
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
1521
+ }
1522
+ /**
1523
+ * Checks if an ID has already been registered
1524
+ *
1525
+ * @param id - The collection ID to check
1526
+ * @returns True if the ID exists in the registry
1527
+ */
1528
+ has(id) {
1529
+ return this.ids.has(id);
1530
+ }
1531
+ /**
1532
+ * Registers a collection ID in the registry
1533
+ *
1534
+ * @param id - The collection ID to register
1535
+ */
1536
+ register(id) {
1537
+ this.ids.add(id);
1538
+ }
1539
+ };
1470
1540
 
1471
1541
  // src/migration/diff.ts
1472
1542
  var DEFAULT_CONFIG2 = {
@@ -1798,6 +1868,18 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1798
1868
  const filteredCollectionsToDelete = collectionsToDelete.filter(
1799
1869
  (collection) => !isSystemCollection(collection.name, config)
1800
1870
  );
1871
+ const registry = new CollectionIdRegistry();
1872
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
1873
+ if (collection.id) {
1874
+ registry.register(collection.id);
1875
+ return collection;
1876
+ }
1877
+ const id = registry.generate(collection.name);
1878
+ return {
1879
+ ...collection,
1880
+ id
1881
+ };
1882
+ });
1801
1883
  const collectionsToModify = [];
1802
1884
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
1803
1885
  for (const [currentCollection, previousCollection] of matchedCollections) {
@@ -1807,7 +1889,7 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
1807
1889
  }
1808
1890
  }
1809
1891
  return {
1810
- collectionsToCreate: filteredCollectionsToCreate,
1892
+ collectionsToCreate: collectionsWithIds,
1811
1893
  collectionsToDelete: filteredCollectionsToDelete,
1812
1894
  collectionsToModify
1813
1895
  };
@@ -1890,42 +1972,48 @@ function generateTimestamp(config) {
1890
1972
  }
1891
1973
  return Math.floor(Date.now() / 1e3).toString();
1892
1974
  }
1893
- function generateMigrationDescription(diff) {
1894
- const parts = [];
1895
- if (diff.collectionsToCreate.length > 0) {
1896
- if (diff.collectionsToCreate.length === 1) {
1897
- parts.push(`created_${diff.collectionsToCreate[0].name}`);
1898
- } else {
1899
- parts.push(`created_${diff.collectionsToCreate.length}_collections`);
1900
- }
1901
- }
1902
- if (diff.collectionsToDelete.length > 0) {
1903
- if (diff.collectionsToDelete.length === 1) {
1904
- parts.push(`deleted_${diff.collectionsToDelete[0].name}`);
1905
- } else {
1906
- parts.push(`deleted_${diff.collectionsToDelete.length}_collections`);
1907
- }
1908
- }
1909
- if (diff.collectionsToModify.length > 0) {
1910
- if (diff.collectionsToModify.length === 1) {
1911
- parts.push(`updated_${diff.collectionsToModify[0].collection}`);
1912
- } else {
1913
- parts.push(`updated_${diff.collectionsToModify.length}_collections`);
1914
- }
1975
+ function splitDiffByCollection(diff, baseTimestamp) {
1976
+ const operations = [];
1977
+ let currentTimestamp = parseInt(baseTimestamp, 10);
1978
+ for (const collection of diff.collectionsToCreate) {
1979
+ operations.push({
1980
+ type: "create",
1981
+ collection,
1982
+ timestamp: currentTimestamp.toString()
1983
+ });
1984
+ currentTimestamp += 1;
1915
1985
  }
1916
- if (parts.length === 0) {
1917
- return "no_changes";
1986
+ for (const modification of diff.collectionsToModify) {
1987
+ operations.push({
1988
+ type: "modify",
1989
+ collection: modification.collection,
1990
+ modifications: modification,
1991
+ timestamp: currentTimestamp.toString()
1992
+ });
1993
+ currentTimestamp += 1;
1918
1994
  }
1919
- let description = parts.join("_");
1920
- if (description.length > 80) {
1921
- description = description.substring(0, 77) + "...";
1995
+ for (const collection of diff.collectionsToDelete) {
1996
+ operations.push({
1997
+ type: "delete",
1998
+ collection: collection.name || collection,
1999
+ // Handle both object and string
2000
+ timestamp: currentTimestamp.toString()
2001
+ });
2002
+ currentTimestamp += 1;
1922
2003
  }
1923
- return description;
2004
+ return operations;
1924
2005
  }
1925
- function generateMigrationFilename(diff, config) {
1926
- const timestamp = generateTimestamp(config);
1927
- const description = generateMigrationDescription(diff);
1928
- return `${timestamp}_${description}.js`;
2006
+ function generateCollectionMigrationFilename(operation) {
2007
+ const timestamp = operation.timestamp;
2008
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
2009
+ let collectionName;
2010
+ if (typeof operation.collection === "string") {
2011
+ collectionName = operation.collection;
2012
+ } else {
2013
+ collectionName = operation.collection.name;
2014
+ }
2015
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
2016
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
1929
2017
  }
1930
2018
  function createMigrationFileStructure(upCode, downCode, config) {
1931
2019
  const mergedConfig = config ? mergeConfig3(config) : DEFAULT_CONFIG3;
@@ -1997,14 +2085,13 @@ function formatValue(value) {
1997
2085
  return "null";
1998
2086
  }
1999
2087
  if (typeof value === "string") {
2000
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
2088
+ return JSON.stringify(value);
2001
2089
  }
2002
2090
  if (typeof value === "number" || typeof value === "boolean") {
2003
2091
  return String(value);
2004
2092
  }
2005
2093
  if (Array.isArray(value)) {
2006
- const items = value.map((v) => formatValue(v)).join(", ");
2007
- return `[${items}]`;
2094
+ return JSON.stringify(value).replace(/","/g, '", "');
2008
2095
  }
2009
2096
  if (typeof value === "object") {
2010
2097
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -2012,7 +2099,7 @@ function formatValue(value) {
2012
2099
  }
2013
2100
  return String(value);
2014
2101
  }
2015
- function generateFieldDefinitionObject(field) {
2102
+ function generateFieldDefinitionObject(field, collectionIdMap) {
2016
2103
  const parts = [];
2017
2104
  parts.push(` name: "${field.name}"`);
2018
2105
  parts.push(` type: "${field.type}"`);
@@ -2020,34 +2107,47 @@ function generateFieldDefinitionObject(field) {
2020
2107
  if (field.unique !== void 0) {
2021
2108
  parts.push(` unique: ${field.unique}`);
2022
2109
  }
2110
+ if (field.type === "select") {
2111
+ const maxSelect = field.options?.maxSelect ?? 1;
2112
+ parts.push(` maxSelect: ${maxSelect}`);
2113
+ const values = field.options?.values ?? [];
2114
+ parts.push(` values: ${formatValue(values)}`);
2115
+ }
2023
2116
  if (field.options && Object.keys(field.options).length > 0) {
2024
2117
  for (const [key, value] of Object.entries(field.options)) {
2118
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2119
+ continue;
2120
+ }
2025
2121
  parts.push(` ${key}: ${formatValue(value)}`);
2026
2122
  }
2027
2123
  }
2028
2124
  if (field.relation) {
2029
2125
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2030
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2031
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2032
- if (field.relation.maxSelect !== void 0) {
2033
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2034
- }
2035
- if (field.relation.minSelect !== void 0) {
2036
- parts.push(` minSelect: ${field.relation.minSelect}`);
2037
- }
2038
- if (field.relation.cascadeDelete !== void 0) {
2039
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2126
+ let collectionIdValue;
2127
+ if (isUsersCollection) {
2128
+ collectionIdValue = '"_pb_users_auth_"';
2129
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2130
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2131
+ } else {
2132
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2040
2133
  }
2134
+ parts.push(` collectionId: ${collectionIdValue}`);
2135
+ const maxSelect = field.relation.maxSelect ?? 1;
2136
+ parts.push(` maxSelect: ${maxSelect}`);
2137
+ const minSelect = field.relation.minSelect ?? null;
2138
+ parts.push(` minSelect: ${minSelect}`);
2139
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2140
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2041
2141
  }
2042
2142
  return ` {
2043
2143
  ${parts.join(",\n")},
2044
2144
  }`;
2045
2145
  }
2046
- function generateFieldsArray(fields) {
2146
+ function generateFieldsArray(fields, collectionIdMap) {
2047
2147
  if (fields.length === 0) {
2048
2148
  return "[]";
2049
2149
  }
2050
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
2150
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
2051
2151
  return `[
2052
2152
  ${fieldObjects.join(",\n")},
2053
2153
  ]`;
@@ -2106,7 +2206,7 @@ function generateIndexesArray(indexes) {
2106
2206
  if (!indexes || indexes.length === 0) {
2107
2207
  return "[]";
2108
2208
  }
2109
- const indexStrings = indexes.map((idx) => `"${idx}"`);
2209
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
2110
2210
  return `[
2111
2211
  ${indexStrings.join(",\n ")},
2112
2212
  ]`;
@@ -2160,7 +2260,7 @@ function getSystemFields() {
2160
2260
  }
2161
2261
  ];
2162
2262
  }
2163
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
2263
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
2164
2264
  const lines = [];
2165
2265
  lines.push(` const ${varName} = new Collection({`);
2166
2266
  lines.push(` name: "${collection.name}",`);
@@ -2174,7 +2274,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
2174
2274
  }
2175
2275
  const systemFields = getSystemFields();
2176
2276
  const allFields = [...systemFields, ...collection.fields];
2177
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
2277
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
2178
2278
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
2179
2279
  lines.push(` });`);
2180
2280
  lines.push(``);
@@ -2196,42 +2296,59 @@ function getFieldConstructorName(fieldType) {
2196
2296
  };
2197
2297
  return constructorMap[fieldType] || "TextField";
2198
2298
  }
2199
- function generateFieldConstructorOptions(field) {
2299
+ function generateFieldConstructorOptions(field, collectionIdMap) {
2200
2300
  const parts = [];
2201
2301
  parts.push(` name: "${field.name}"`);
2202
2302
  parts.push(` required: ${field.required}`);
2203
2303
  if (field.unique !== void 0) {
2204
2304
  parts.push(` unique: ${field.unique}`);
2205
2305
  }
2306
+ if (field.type === "select") {
2307
+ const maxSelect = field.options?.maxSelect ?? 1;
2308
+ parts.push(` maxSelect: ${maxSelect}`);
2309
+ const values = field.options?.values ?? [];
2310
+ parts.push(` values: ${formatValue(values)}`);
2311
+ }
2206
2312
  if (field.options && Object.keys(field.options).length > 0) {
2207
2313
  for (const [key, value] of Object.entries(field.options)) {
2208
- parts.push(` ${key}: ${formatValue(value)}`);
2314
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
2315
+ continue;
2316
+ }
2317
+ if (field.type === "number" && key === "noDecimal") {
2318
+ parts.push(` onlyInt: ${formatValue(value)}`);
2319
+ } else {
2320
+ parts.push(` ${key}: ${formatValue(value)}`);
2321
+ }
2209
2322
  }
2210
2323
  }
2211
2324
  if (field.relation && field.type === "relation") {
2212
2325
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
2213
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2214
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
2215
- if (field.relation.maxSelect !== void 0) {
2216
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
2217
- }
2218
- if (field.relation.minSelect !== void 0) {
2219
- parts.push(` minSelect: ${field.relation.minSelect}`);
2220
- }
2221
- if (field.relation.cascadeDelete !== void 0) {
2222
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
2326
+ let collectionIdValue;
2327
+ if (isUsersCollection) {
2328
+ collectionIdValue = '"_pb_users_auth_"';
2329
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
2330
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
2331
+ } else {
2332
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
2223
2333
  }
2334
+ parts.push(` collectionId: ${collectionIdValue}`);
2335
+ const maxSelect = field.relation.maxSelect ?? 1;
2336
+ parts.push(` maxSelect: ${maxSelect}`);
2337
+ const minSelect = field.relation.minSelect ?? null;
2338
+ parts.push(` minSelect: ${minSelect}`);
2339
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
2340
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
2224
2341
  }
2225
2342
  return parts.join(",\n");
2226
2343
  }
2227
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
2344
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
2228
2345
  const lines = [];
2229
2346
  const constructorName = getFieldConstructorName(field.type);
2230
2347
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
2231
2348
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2232
2349
  lines.push(``);
2233
2350
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
2234
- lines.push(generateFieldConstructorOptions(field));
2351
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
2235
2352
  lines.push(` }));`);
2236
2353
  lines.push(``);
2237
2354
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -2281,7 +2398,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
2281
2398
  const lines = [];
2282
2399
  const collectionVar = varName || `collection_${collectionName}_idx`;
2283
2400
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2284
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
2401
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
2285
2402
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
2286
2403
  return lines.join("\n");
2287
2404
  }
@@ -2290,7 +2407,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
2290
2407
  const collectionVar = varName || `collection_${collectionName}_idx`;
2291
2408
  const indexVar = `${collectionVar}_indexToRemove`;
2292
2409
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
2293
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
2410
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
2294
2411
  lines.push(` if (${indexVar} !== -1) {`);
2295
2412
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
2296
2413
  lines.push(` }`);
@@ -2319,243 +2436,194 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
2319
2436
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
2320
2437
  return lines.join("\n");
2321
2438
  }
2322
- function generateUpMigration(diff) {
2439
+ function generateOperationUpMigration(operation, collectionIdMap) {
2323
2440
  const lines = [];
2324
- lines.push(` // UP MIGRATION`);
2325
- lines.push(``);
2326
- if (diff.collectionsToCreate.length > 0) {
2327
- lines.push(` // Create new collections`);
2328
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2329
- const collection = diff.collectionsToCreate[i];
2330
- const varName = `collection_${collection.name}_create`;
2331
- lines.push(generateCollectionCreation(collection, varName));
2332
- lines.push(``);
2333
- }
2334
- }
2335
- if (diff.collectionsToModify.length > 0) {
2336
- lines.push(` // Modify existing collections`);
2337
- for (const modification of diff.collectionsToModify) {
2338
- const collectionName = modification.collection;
2339
- if (modification.fieldsToAdd.length > 0) {
2340
- lines.push(` // Add fields to ${collectionName}`);
2341
- for (const field of modification.fieldsToAdd) {
2342
- const varName = `collection_${collectionName}_add_${field.name}`;
2343
- lines.push(generateFieldAddition(collectionName, field, varName));
2344
- lines.push(``);
2345
- }
2346
- }
2347
- if (modification.fieldsToModify.length > 0) {
2348
- lines.push(` // Modify fields in ${collectionName}`);
2349
- for (const fieldMod of modification.fieldsToModify) {
2350
- const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2351
- lines.push(generateFieldModification(collectionName, fieldMod, varName));
2352
- lines.push(``);
2353
- }
2354
- }
2355
- if (modification.fieldsToRemove.length > 0) {
2356
- lines.push(` // Remove fields from ${collectionName}`);
2357
- for (const field of modification.fieldsToRemove) {
2358
- const varName = `collection_${collectionName}_remove_${field.name}`;
2359
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2360
- lines.push(``);
2361
- }
2362
- }
2363
- if (modification.indexesToAdd.length > 0) {
2364
- lines.push(` // Add indexes to ${collectionName}`);
2365
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2366
- const index = modification.indexesToAdd[i];
2367
- const varName = `collection_${collectionName}_addidx_${i}`;
2368
- lines.push(generateIndexAddition(collectionName, index, varName));
2369
- lines.push(``);
2370
- }
2371
- }
2372
- if (modification.indexesToRemove.length > 0) {
2373
- lines.push(` // Remove indexes from ${collectionName}`);
2374
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2375
- const index = modification.indexesToRemove[i];
2376
- const varName = `collection_${collectionName}_rmidx_${i}`;
2377
- lines.push(generateIndexRemoval(collectionName, index, varName));
2378
- lines.push(``);
2379
- }
2380
- }
2381
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2382
- lines.push(` // Update permissions for ${collectionName}`);
2383
- for (const permission of modification.permissionsToUpdate) {
2384
- const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2385
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName));
2386
- lines.push(``);
2387
- }
2388
- } else if (modification.rulesToUpdate.length > 0) {
2389
- lines.push(` // Update rules for ${collectionName}`);
2390
- for (const rule of modification.rulesToUpdate) {
2391
- const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2392
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName));
2393
- lines.push(``);
2394
- }
2395
- }
2441
+ if (operation.type === "create") {
2442
+ const collection = operation.collection;
2443
+ const varName = `collection_${collection.name}`;
2444
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2445
+ } else if (operation.type === "modify") {
2446
+ const modification = operation.modifications;
2447
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2448
+ let operationCount = 0;
2449
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2450
+ for (const field of modification.fieldsToAdd) {
2451
+ operationCount++;
2452
+ const varName = `collection_${collectionName}_add_${field.name}`;
2453
+ const isLast = operationCount === totalOperations;
2454
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2455
+ if (!isLast) lines.push("");
2396
2456
  }
2397
- }
2398
- if (diff.collectionsToDelete.length > 0) {
2399
- lines.push(` // Delete collections`);
2400
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2401
- const collection = diff.collectionsToDelete[i];
2402
- const varName = `collection_${collection.name}_delete`;
2403
- lines.push(generateCollectionDeletion(collection.name, varName));
2404
- lines.push(``);
2405
- }
2406
- }
2407
- if (lines.length === 2) {
2408
- lines.push(` // No changes detected`);
2409
- lines.push(``);
2410
- }
2411
- let code = lines.join("\n");
2412
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2413
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2414
- const saveMatches = [...code.matchAll(savePattern)];
2415
- const deleteMatches = [...code.matchAll(deletePattern)];
2416
- const allMatches = [
2417
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2418
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2419
- ].sort((a, b) => b.index - a.index);
2420
- if (allMatches.length > 0) {
2421
- const lastMatch = allMatches[0];
2422
- if (lastMatch.type === "save") {
2423
- 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);
2424
- } else {
2425
- 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);
2457
+ for (const fieldMod of modification.fieldsToModify) {
2458
+ operationCount++;
2459
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
2460
+ const isLast = operationCount === totalOperations;
2461
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
2462
+ if (!isLast) lines.push("");
2426
2463
  }
2464
+ for (const field of modification.fieldsToRemove) {
2465
+ operationCount++;
2466
+ const varName = `collection_${collectionName}_remove_${field.name}`;
2467
+ const isLast = operationCount === totalOperations;
2468
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2469
+ if (!isLast) lines.push("");
2470
+ }
2471
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2472
+ operationCount++;
2473
+ const index = modification.indexesToAdd[i];
2474
+ const varName = `collection_${collectionName}_addidx_${i}`;
2475
+ const isLast = operationCount === totalOperations;
2476
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2477
+ if (!isLast) lines.push("");
2478
+ }
2479
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2480
+ operationCount++;
2481
+ const index = modification.indexesToRemove[i];
2482
+ const varName = `collection_${collectionName}_rmidx_${i}`;
2483
+ const isLast = operationCount === totalOperations;
2484
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2485
+ if (!isLast) lines.push("");
2486
+ }
2487
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2488
+ for (const permission of modification.permissionsToUpdate) {
2489
+ operationCount++;
2490
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
2491
+ const isLast = operationCount === totalOperations;
2492
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
2493
+ if (!isLast) lines.push("");
2494
+ }
2495
+ } else if (modification.rulesToUpdate.length > 0) {
2496
+ for (const rule of modification.rulesToUpdate) {
2497
+ operationCount++;
2498
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
2499
+ const isLast = operationCount === totalOperations;
2500
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
2501
+ if (!isLast) lines.push("");
2502
+ }
2503
+ }
2504
+ } else if (operation.type === "delete") {
2505
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
2506
+ const varName = `collection_${collectionName}`;
2507
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
2427
2508
  }
2428
- return code;
2509
+ return lines.join("\n");
2429
2510
  }
2430
- function generateDownMigration(diff) {
2511
+ function generateOperationDownMigration(operation, collectionIdMap) {
2431
2512
  const lines = [];
2432
- lines.push(` // DOWN MIGRATION (ROLLBACK)`);
2433
- lines.push(``);
2434
- if (diff.collectionsToDelete.length > 0) {
2435
- lines.push(` // Recreate deleted collections`);
2436
- for (let i = 0; i < diff.collectionsToDelete.length; i++) {
2437
- const collection = diff.collectionsToDelete[i];
2438
- const varName = `collection_${collection.name}_recreate`;
2439
- lines.push(generateCollectionCreation(collection, varName));
2440
- lines.push(``);
2513
+ if (operation.type === "create") {
2514
+ const collection = operation.collection;
2515
+ const varName = `collection_${collection.name}`;
2516
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
2517
+ } else if (operation.type === "modify") {
2518
+ const modification = operation.modifications;
2519
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
2520
+ let operationCount = 0;
2521
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
2522
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2523
+ for (const permission of modification.permissionsToUpdate) {
2524
+ operationCount++;
2525
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2526
+ const isLast = operationCount === totalOperations;
2527
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
2528
+ if (!isLast) lines.push("");
2529
+ }
2530
+ } else if (modification.rulesToUpdate.length > 0) {
2531
+ for (const rule of modification.rulesToUpdate) {
2532
+ operationCount++;
2533
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2534
+ const isLast = operationCount === totalOperations;
2535
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
2536
+ if (!isLast) lines.push("");
2537
+ }
2538
+ }
2539
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
2540
+ operationCount++;
2541
+ const index = modification.indexesToRemove[i];
2542
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
2543
+ const isLast = operationCount === totalOperations;
2544
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
2545
+ if (!isLast) lines.push("");
2546
+ }
2547
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
2548
+ operationCount++;
2549
+ const index = modification.indexesToAdd[i];
2550
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
2551
+ const isLast = operationCount === totalOperations;
2552
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
2553
+ if (!isLast) lines.push("");
2441
2554
  }
2442
- }
2443
- if (diff.collectionsToModify.length > 0) {
2444
- lines.push(` // Revert modifications`);
2445
- for (const modification of diff.collectionsToModify) {
2446
- const collectionName = modification.collection;
2447
- if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
2448
- lines.push(` // Revert permissions for ${collectionName}`);
2449
- for (const permission of modification.permissionsToUpdate) {
2450
- const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
2451
- lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName));
2452
- lines.push(``);
2453
- }
2454
- } else if (modification.rulesToUpdate.length > 0) {
2455
- lines.push(` // Revert rules for ${collectionName}`);
2456
- for (const rule of modification.rulesToUpdate) {
2457
- const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
2458
- lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName));
2459
- lines.push(``);
2460
- }
2461
- }
2462
- if (modification.indexesToRemove.length > 0) {
2463
- lines.push(` // Restore indexes to ${collectionName}`);
2464
- for (let i = 0; i < modification.indexesToRemove.length; i++) {
2465
- const index = modification.indexesToRemove[i];
2466
- const varName = `collection_${collectionName}_restore_idx_${i}`;
2467
- lines.push(generateIndexAddition(collectionName, index, varName));
2468
- lines.push(``);
2469
- }
2470
- }
2471
- if (modification.indexesToAdd.length > 0) {
2472
- lines.push(` // Remove indexes from ${collectionName}`);
2473
- for (let i = 0; i < modification.indexesToAdd.length; i++) {
2474
- const index = modification.indexesToAdd[i];
2475
- const varName = `collection_${collectionName}_revert_idx_${i}`;
2476
- lines.push(generateIndexRemoval(collectionName, index, varName));
2477
- lines.push(``);
2478
- }
2479
- }
2480
- if (modification.fieldsToRemove.length > 0) {
2481
- lines.push(` // Restore fields to ${collectionName}`);
2482
- for (const field of modification.fieldsToRemove) {
2483
- const varName = `collection_${collectionName}_restore_${field.name}`;
2484
- lines.push(generateFieldAddition(collectionName, field, varName));
2485
- lines.push(``);
2486
- }
2487
- }
2488
- if (modification.fieldsToModify.length > 0) {
2489
- lines.push(` // Revert field modifications in ${collectionName}`);
2490
- for (const fieldMod of modification.fieldsToModify) {
2491
- const reverseChanges = fieldMod.changes.map((change) => ({
2492
- property: change.property,
2493
- oldValue: change.newValue,
2494
- newValue: change.oldValue
2495
- }));
2496
- const reverseMod = {
2497
- fieldName: fieldMod.fieldName,
2498
- currentDefinition: fieldMod.newDefinition,
2499
- newDefinition: fieldMod.currentDefinition,
2500
- changes: reverseChanges
2501
- };
2502
- const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2503
- lines.push(generateFieldModification(collectionName, reverseMod, varName));
2504
- lines.push(``);
2505
- }
2506
- }
2507
- if (modification.fieldsToAdd.length > 0) {
2508
- lines.push(` // Remove added fields from ${collectionName}`);
2509
- for (const field of modification.fieldsToAdd) {
2510
- const varName = `collection_${collectionName}_revert_add_${field.name}`;
2511
- lines.push(generateFieldDeletion(collectionName, field.name, varName));
2512
- lines.push(``);
2513
- }
2514
- }
2555
+ for (const field of modification.fieldsToRemove) {
2556
+ operationCount++;
2557
+ const varName = `collection_${collectionName}_restore_${field.name}`;
2558
+ const isLast = operationCount === totalOperations;
2559
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
2560
+ if (!isLast) lines.push("");
2515
2561
  }
2516
- }
2517
- if (diff.collectionsToCreate.length > 0) {
2518
- lines.push(` // Delete created collections`);
2519
- for (let i = 0; i < diff.collectionsToCreate.length; i++) {
2520
- const collection = diff.collectionsToCreate[i];
2521
- const varName = `collection_${collection.name}_rollback`;
2522
- lines.push(generateCollectionDeletion(collection.name, varName));
2523
- lines.push(``);
2524
- }
2525
- }
2526
- if (lines.length === 2) {
2527
- lines.push(` // No changes to revert`);
2528
- lines.push(``);
2529
- }
2530
- let code = lines.join("\n");
2531
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
2532
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
2533
- const saveMatches = [...code.matchAll(savePattern)];
2534
- const deleteMatches = [...code.matchAll(deletePattern)];
2535
- const allMatches = [
2536
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
2537
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
2538
- ].sort((a, b) => b.index - a.index);
2539
- if (allMatches.length > 0) {
2540
- const lastMatch = allMatches[0];
2541
- if (lastMatch.type === "save") {
2542
- 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);
2543
- } else {
2544
- 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);
2562
+ for (const fieldMod of modification.fieldsToModify) {
2563
+ operationCount++;
2564
+ const reverseChanges = fieldMod.changes.map((change) => ({
2565
+ property: change.property,
2566
+ oldValue: change.newValue,
2567
+ newValue: change.oldValue
2568
+ }));
2569
+ const reverseMod = {
2570
+ fieldName: fieldMod.fieldName,
2571
+ currentDefinition: fieldMod.newDefinition,
2572
+ newDefinition: fieldMod.currentDefinition,
2573
+ changes: reverseChanges
2574
+ };
2575
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
2576
+ const isLast = operationCount === totalOperations;
2577
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
2578
+ if (!isLast) lines.push("");
2579
+ }
2580
+ for (const field of modification.fieldsToAdd) {
2581
+ operationCount++;
2582
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
2583
+ const isLast = operationCount === totalOperations;
2584
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
2585
+ if (!isLast) lines.push("");
2586
+ }
2587
+ } else if (operation.type === "delete") {
2588
+ const collection = operation.collection;
2589
+ if (typeof collection !== "string") {
2590
+ const varName = `collection_${collection.name}`;
2591
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
2545
2592
  }
2546
2593
  }
2547
- return code;
2594
+ return lines.join("\n");
2548
2595
  }
2549
2596
  function generate(diff, config) {
2550
2597
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
2551
2598
  try {
2552
2599
  const migrationDir = resolveMigrationDir(normalizedConfig);
2553
- const upCode = generateUpMigration(diff);
2554
- const downCode = generateDownMigration(diff);
2555
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2556
- const filename = generateMigrationFilename(diff, normalizedConfig);
2557
- const filePath = writeMigrationFile(migrationDir, filename, content);
2558
- return filePath;
2600
+ const hasChanges4 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
2601
+ if (!hasChanges4) {
2602
+ return [];
2603
+ }
2604
+ const collectionIdMap = /* @__PURE__ */ new Map();
2605
+ for (const collection of diff.collectionsToCreate) {
2606
+ if (collection.id) {
2607
+ collectionIdMap.set(collection.name, collection.id);
2608
+ }
2609
+ }
2610
+ for (const collection of diff.collectionsToDelete) {
2611
+ if (collection.id) {
2612
+ collectionIdMap.set(collection.name, collection.id);
2613
+ }
2614
+ }
2615
+ const baseTimestamp = generateTimestamp(normalizedConfig);
2616
+ const operations = splitDiffByCollection(diff, baseTimestamp);
2617
+ const filePaths = [];
2618
+ for (const operation of operations) {
2619
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
2620
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
2621
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
2622
+ const filename = generateCollectionMigrationFilename(operation);
2623
+ const filePath = writeMigrationFile(migrationDir, filename, content);
2624
+ filePaths.push(filePath);
2625
+ }
2626
+ return filePaths;
2559
2627
  } catch (error) {
2560
2628
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
2561
2629
  throw error;
@@ -3576,15 +3644,25 @@ async function executeGenerate(options) {
3576
3644
  process.exit(1);
3577
3645
  }
3578
3646
  logSection("\u{1F4DD} Generating Migration");
3579
- const migrationPath = await withProgress(
3647
+ const migrationPaths = await withProgress(
3580
3648
  "Creating migration file...",
3581
3649
  () => Promise.resolve(generate(diff, migrationsDir))
3582
3650
  );
3583
- logSuccess(`Migration file created: ${path5.basename(migrationPath)}`);
3651
+ if (migrationPaths.length === 0) {
3652
+ logWarning("No migration files were generated (no changes detected).");
3653
+ return;
3654
+ }
3655
+ if (migrationPaths.length === 1) {
3656
+ logSuccess(`Migration file created: ${path5.basename(migrationPaths[0])}`);
3657
+ } else {
3658
+ logSuccess(`Created ${migrationPaths.length} migration files`);
3659
+ }
3584
3660
  logSection("\u2705 Next Steps");
3585
3661
  console.log();
3586
- console.log(" 1. Review the generated migration file:");
3587
- console.log(` ${migrationPath}`);
3662
+ console.log(" 1. Review the generated migration file(s):");
3663
+ migrationPaths.forEach((migrationPath) => {
3664
+ console.log(` ${migrationPath}`);
3665
+ });
3588
3666
  console.log();
3589
3667
  console.log(" 2. Apply the migration by running PocketBase:");
3590
3668
  console.log(" yarn pb");