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
@@ -3,6 +3,7 @@
3
3
  var fs3 = require('fs');
4
4
  var path = require('path');
5
5
  var zod = require('zod');
6
+ var crypto = require('crypto');
6
7
 
7
8
  function _interopNamespace(e) {
8
9
  if (e && e.__esModule) return e;
@@ -57,6 +58,18 @@ function extractRelationMetadata(description) {
57
58
  }
58
59
  return null;
59
60
  }
61
+ var FIELD_METADATA_KEY = "__pocketbase_field__";
62
+ function extractFieldMetadata(description) {
63
+ if (!description) return null;
64
+ try {
65
+ const parsed = JSON.parse(description);
66
+ if (parsed[FIELD_METADATA_KEY]) {
67
+ return parsed[FIELD_METADATA_KEY];
68
+ }
69
+ } catch {
70
+ }
71
+ return null;
72
+ }
60
73
 
61
74
  // src/migration/errors.ts
62
75
  var MigrationError = class _MigrationError extends Error {
@@ -1567,6 +1580,38 @@ function isAuthCollection(fields) {
1567
1580
  return hasEmail && hasPassword;
1568
1581
  }
1569
1582
  function buildFieldDefinition(fieldName, zodType) {
1583
+ const fieldMetadata = extractFieldMetadata(zodType.description);
1584
+ if (fieldMetadata) {
1585
+ let required2;
1586
+ if (fieldMetadata.type === "number") {
1587
+ if (fieldMetadata.options?.required !== void 0) {
1588
+ required2 = fieldMetadata.options.required;
1589
+ } else {
1590
+ required2 = false;
1591
+ }
1592
+ } else {
1593
+ required2 = isFieldRequired(zodType);
1594
+ }
1595
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
1596
+ const fieldDef2 = {
1597
+ name: fieldName,
1598
+ type: fieldMetadata.type,
1599
+ required: required2,
1600
+ options: Object.keys(options2).length > 0 ? options2 : void 0
1601
+ };
1602
+ if (fieldMetadata.type === "relation") {
1603
+ const relationMetadata2 = extractRelationMetadata(zodType.description);
1604
+ if (relationMetadata2) {
1605
+ fieldDef2.relation = {
1606
+ collection: relationMetadata2.collection,
1607
+ maxSelect: relationMetadata2.maxSelect,
1608
+ minSelect: relationMetadata2.minSelect,
1609
+ cascadeDelete: relationMetadata2.cascadeDelete
1610
+ };
1611
+ }
1612
+ }
1613
+ return fieldDef2;
1614
+ }
1570
1615
  const fieldType = mapZodTypeToPocketBase(zodType, fieldName);
1571
1616
  const required = isFieldRequired(zodType);
1572
1617
  const options = extractFieldOptions(zodType);
@@ -2548,6 +2593,65 @@ var SnapshotManager = class {
2548
2593
  return validateSnapshot(snapshot);
2549
2594
  }
2550
2595
  };
2596
+ function generateCollectionId() {
2597
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
2598
+ const idLength = 15;
2599
+ const bytes = crypto.randomBytes(idLength);
2600
+ let id = "pb_";
2601
+ for (let i = 0; i < idLength; i++) {
2602
+ const index = bytes[i] % chars.length;
2603
+ id += chars[index];
2604
+ }
2605
+ return id;
2606
+ }
2607
+ var CollectionIdRegistry = class {
2608
+ ids;
2609
+ constructor() {
2610
+ this.ids = /* @__PURE__ */ new Set();
2611
+ }
2612
+ /**
2613
+ * Generates a unique collection ID for a given collection name
2614
+ * Special case: Returns constant "_pb_users_auth_" for users collection
2615
+ * Retries up to 10 times if collision occurs (extremely rare)
2616
+ *
2617
+ * @param collectionName - The name of the collection
2618
+ * @returns A unique collection ID
2619
+ * @throws Error if unable to generate unique ID after max attempts
2620
+ */
2621
+ generate(collectionName) {
2622
+ if (collectionName && collectionName.toLowerCase() === "users") {
2623
+ const usersId = "_pb_users_auth_";
2624
+ this.register(usersId);
2625
+ return usersId;
2626
+ }
2627
+ const maxAttempts = 10;
2628
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
2629
+ const id = generateCollectionId();
2630
+ if (!this.has(id)) {
2631
+ this.register(id);
2632
+ return id;
2633
+ }
2634
+ }
2635
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
2636
+ }
2637
+ /**
2638
+ * Checks if an ID has already been registered
2639
+ *
2640
+ * @param id - The collection ID to check
2641
+ * @returns True if the ID exists in the registry
2642
+ */
2643
+ has(id) {
2644
+ return this.ids.has(id);
2645
+ }
2646
+ /**
2647
+ * Registers a collection ID in the registry
2648
+ *
2649
+ * @param id - The collection ID to register
2650
+ */
2651
+ register(id) {
2652
+ this.ids.add(id);
2653
+ }
2654
+ };
2551
2655
 
2552
2656
  // src/migration/diff.ts
2553
2657
  var DEFAULT_CONFIG3 = {
@@ -2890,6 +2994,18 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
2890
2994
  const filteredCollectionsToDelete = collectionsToDelete.filter(
2891
2995
  (collection) => !isSystemCollection(collection.name, config)
2892
2996
  );
2997
+ const registry = new CollectionIdRegistry();
2998
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
2999
+ if (collection.id) {
3000
+ registry.register(collection.id);
3001
+ return collection;
3002
+ }
3003
+ const id = registry.generate(collection.name);
3004
+ return {
3005
+ ...collection,
3006
+ id
3007
+ };
3008
+ });
2893
3009
  const collectionsToModify = [];
2894
3010
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
2895
3011
  for (const [currentCollection, previousCollection] of matchedCollections) {
@@ -2899,7 +3015,7 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
2899
3015
  }
2900
3016
  }
2901
3017
  return {
2902
- collectionsToCreate: filteredCollectionsToCreate,
3018
+ collectionsToCreate: collectionsWithIds,
2903
3019
  collectionsToDelete: filteredCollectionsToDelete,
2904
3020
  collectionsToModify
2905
3021
  };
@@ -3135,6 +3251,49 @@ function generateTimestamp(config) {
3135
3251
  }
3136
3252
  return Math.floor(Date.now() / 1e3).toString();
3137
3253
  }
3254
+ function splitDiffByCollection(diff, baseTimestamp) {
3255
+ const operations = [];
3256
+ let currentTimestamp = parseInt(baseTimestamp, 10);
3257
+ for (const collection of diff.collectionsToCreate) {
3258
+ operations.push({
3259
+ type: "create",
3260
+ collection,
3261
+ timestamp: currentTimestamp.toString()
3262
+ });
3263
+ currentTimestamp += 1;
3264
+ }
3265
+ for (const modification of diff.collectionsToModify) {
3266
+ operations.push({
3267
+ type: "modify",
3268
+ collection: modification.collection,
3269
+ modifications: modification,
3270
+ timestamp: currentTimestamp.toString()
3271
+ });
3272
+ currentTimestamp += 1;
3273
+ }
3274
+ for (const collection of diff.collectionsToDelete) {
3275
+ operations.push({
3276
+ type: "delete",
3277
+ collection: collection.name || collection,
3278
+ // Handle both object and string
3279
+ timestamp: currentTimestamp.toString()
3280
+ });
3281
+ currentTimestamp += 1;
3282
+ }
3283
+ return operations;
3284
+ }
3285
+ function generateCollectionMigrationFilename(operation) {
3286
+ const timestamp = operation.timestamp;
3287
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
3288
+ let collectionName;
3289
+ if (typeof operation.collection === "string") {
3290
+ collectionName = operation.collection;
3291
+ } else {
3292
+ collectionName = operation.collection.name;
3293
+ }
3294
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
3295
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
3296
+ }
3138
3297
  function generateMigrationDescription(diff) {
3139
3298
  const parts = [];
3140
3299
  if (diff.collectionsToCreate.length > 0) {
@@ -3242,14 +3401,13 @@ function formatValue(value) {
3242
3401
  return "null";
3243
3402
  }
3244
3403
  if (typeof value === "string") {
3245
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
3404
+ return JSON.stringify(value);
3246
3405
  }
3247
3406
  if (typeof value === "number" || typeof value === "boolean") {
3248
3407
  return String(value);
3249
3408
  }
3250
3409
  if (Array.isArray(value)) {
3251
- const items = value.map((v) => formatValue(v)).join(", ");
3252
- return `[${items}]`;
3410
+ return JSON.stringify(value).replace(/","/g, '", "');
3253
3411
  }
3254
3412
  if (typeof value === "object") {
3255
3413
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -3257,7 +3415,7 @@ function formatValue(value) {
3257
3415
  }
3258
3416
  return String(value);
3259
3417
  }
3260
- function generateFieldDefinitionObject(field) {
3418
+ function generateFieldDefinitionObject(field, collectionIdMap) {
3261
3419
  const parts = [];
3262
3420
  parts.push(` name: "${field.name}"`);
3263
3421
  parts.push(` type: "${field.type}"`);
@@ -3265,34 +3423,47 @@ function generateFieldDefinitionObject(field) {
3265
3423
  if (field.unique !== void 0) {
3266
3424
  parts.push(` unique: ${field.unique}`);
3267
3425
  }
3426
+ if (field.type === "select") {
3427
+ const maxSelect = field.options?.maxSelect ?? 1;
3428
+ parts.push(` maxSelect: ${maxSelect}`);
3429
+ const values = field.options?.values ?? [];
3430
+ parts.push(` values: ${formatValue(values)}`);
3431
+ }
3268
3432
  if (field.options && Object.keys(field.options).length > 0) {
3269
3433
  for (const [key, value] of Object.entries(field.options)) {
3434
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
3435
+ continue;
3436
+ }
3270
3437
  parts.push(` ${key}: ${formatValue(value)}`);
3271
3438
  }
3272
3439
  }
3273
3440
  if (field.relation) {
3274
3441
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3275
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3276
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
3277
- if (field.relation.maxSelect !== void 0) {
3278
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
3279
- }
3280
- if (field.relation.minSelect !== void 0) {
3281
- parts.push(` minSelect: ${field.relation.minSelect}`);
3282
- }
3283
- if (field.relation.cascadeDelete !== void 0) {
3284
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
3442
+ let collectionIdValue;
3443
+ if (isUsersCollection) {
3444
+ collectionIdValue = '"_pb_users_auth_"';
3445
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
3446
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
3447
+ } else {
3448
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3285
3449
  }
3450
+ parts.push(` collectionId: ${collectionIdValue}`);
3451
+ const maxSelect = field.relation.maxSelect ?? 1;
3452
+ parts.push(` maxSelect: ${maxSelect}`);
3453
+ const minSelect = field.relation.minSelect ?? null;
3454
+ parts.push(` minSelect: ${minSelect}`);
3455
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
3456
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
3286
3457
  }
3287
3458
  return ` {
3288
3459
  ${parts.join(",\n")},
3289
3460
  }`;
3290
3461
  }
3291
- function generateFieldsArray(fields) {
3462
+ function generateFieldsArray(fields, collectionIdMap) {
3292
3463
  if (fields.length === 0) {
3293
3464
  return "[]";
3294
3465
  }
3295
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
3466
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
3296
3467
  return `[
3297
3468
  ${fieldObjects.join(",\n")},
3298
3469
  ]`;
@@ -3351,7 +3522,7 @@ function generateIndexesArray(indexes) {
3351
3522
  if (!indexes || indexes.length === 0) {
3352
3523
  return "[]";
3353
3524
  }
3354
- const indexStrings = indexes.map((idx) => `"${idx}"`);
3525
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
3355
3526
  return `[
3356
3527
  ${indexStrings.join(",\n ")},
3357
3528
  ]`;
@@ -3405,7 +3576,7 @@ function getSystemFields() {
3405
3576
  }
3406
3577
  ];
3407
3578
  }
3408
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
3579
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
3409
3580
  const lines = [];
3410
3581
  lines.push(` const ${varName} = new Collection({`);
3411
3582
  lines.push(` name: "${collection.name}",`);
@@ -3419,7 +3590,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
3419
3590
  }
3420
3591
  const systemFields = getSystemFields();
3421
3592
  const allFields = [...systemFields, ...collection.fields];
3422
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
3593
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
3423
3594
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
3424
3595
  lines.push(` });`);
3425
3596
  lines.push(``);
@@ -3441,42 +3612,59 @@ function getFieldConstructorName(fieldType) {
3441
3612
  };
3442
3613
  return constructorMap[fieldType] || "TextField";
3443
3614
  }
3444
- function generateFieldConstructorOptions(field) {
3615
+ function generateFieldConstructorOptions(field, collectionIdMap) {
3445
3616
  const parts = [];
3446
3617
  parts.push(` name: "${field.name}"`);
3447
3618
  parts.push(` required: ${field.required}`);
3448
3619
  if (field.unique !== void 0) {
3449
3620
  parts.push(` unique: ${field.unique}`);
3450
3621
  }
3622
+ if (field.type === "select") {
3623
+ const maxSelect = field.options?.maxSelect ?? 1;
3624
+ parts.push(` maxSelect: ${maxSelect}`);
3625
+ const values = field.options?.values ?? [];
3626
+ parts.push(` values: ${formatValue(values)}`);
3627
+ }
3451
3628
  if (field.options && Object.keys(field.options).length > 0) {
3452
3629
  for (const [key, value] of Object.entries(field.options)) {
3453
- parts.push(` ${key}: ${formatValue(value)}`);
3630
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
3631
+ continue;
3632
+ }
3633
+ if (field.type === "number" && key === "noDecimal") {
3634
+ parts.push(` onlyInt: ${formatValue(value)}`);
3635
+ } else {
3636
+ parts.push(` ${key}: ${formatValue(value)}`);
3637
+ }
3454
3638
  }
3455
3639
  }
3456
3640
  if (field.relation && field.type === "relation") {
3457
3641
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3458
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3459
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
3460
- if (field.relation.maxSelect !== void 0) {
3461
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
3462
- }
3463
- if (field.relation.minSelect !== void 0) {
3464
- parts.push(` minSelect: ${field.relation.minSelect}`);
3465
- }
3466
- if (field.relation.cascadeDelete !== void 0) {
3467
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
3642
+ let collectionIdValue;
3643
+ if (isUsersCollection) {
3644
+ collectionIdValue = '"_pb_users_auth_"';
3645
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
3646
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
3647
+ } else {
3648
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3468
3649
  }
3650
+ parts.push(` collectionId: ${collectionIdValue}`);
3651
+ const maxSelect = field.relation.maxSelect ?? 1;
3652
+ parts.push(` maxSelect: ${maxSelect}`);
3653
+ const minSelect = field.relation.minSelect ?? null;
3654
+ parts.push(` minSelect: ${minSelect}`);
3655
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
3656
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
3469
3657
  }
3470
3658
  return parts.join(",\n");
3471
3659
  }
3472
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
3660
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
3473
3661
  const lines = [];
3474
3662
  const constructorName = getFieldConstructorName(field.type);
3475
3663
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
3476
3664
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3477
3665
  lines.push(``);
3478
3666
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
3479
- lines.push(generateFieldConstructorOptions(field));
3667
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
3480
3668
  lines.push(` }));`);
3481
3669
  lines.push(``);
3482
3670
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -3526,7 +3714,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
3526
3714
  const lines = [];
3527
3715
  const collectionVar = varName || `collection_${collectionName}_idx`;
3528
3716
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3529
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
3717
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
3530
3718
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3531
3719
  return lines.join("\n");
3532
3720
  }
@@ -3535,7 +3723,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
3535
3723
  const collectionVar = varName || `collection_${collectionName}_idx`;
3536
3724
  const indexVar = `${collectionVar}_indexToRemove`;
3537
3725
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3538
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
3726
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
3539
3727
  lines.push(` if (${indexVar} !== -1) {`);
3540
3728
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
3541
3729
  lines.push(` }`);
@@ -3564,16 +3752,179 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
3564
3752
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
3565
3753
  return lines.join("\n");
3566
3754
  }
3755
+ function generateOperationUpMigration(operation, collectionIdMap) {
3756
+ const lines = [];
3757
+ if (operation.type === "create") {
3758
+ const collection = operation.collection;
3759
+ const varName = `collection_${collection.name}`;
3760
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
3761
+ } else if (operation.type === "modify") {
3762
+ const modification = operation.modifications;
3763
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
3764
+ let operationCount = 0;
3765
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
3766
+ for (const field of modification.fieldsToAdd) {
3767
+ operationCount++;
3768
+ const varName = `collection_${collectionName}_add_${field.name}`;
3769
+ const isLast = operationCount === totalOperations;
3770
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
3771
+ if (!isLast) lines.push("");
3772
+ }
3773
+ for (const fieldMod of modification.fieldsToModify) {
3774
+ operationCount++;
3775
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
3776
+ const isLast = operationCount === totalOperations;
3777
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
3778
+ if (!isLast) lines.push("");
3779
+ }
3780
+ for (const field of modification.fieldsToRemove) {
3781
+ operationCount++;
3782
+ const varName = `collection_${collectionName}_remove_${field.name}`;
3783
+ const isLast = operationCount === totalOperations;
3784
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
3785
+ if (!isLast) lines.push("");
3786
+ }
3787
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
3788
+ operationCount++;
3789
+ const index = modification.indexesToAdd[i];
3790
+ const varName = `collection_${collectionName}_addidx_${i}`;
3791
+ const isLast = operationCount === totalOperations;
3792
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
3793
+ if (!isLast) lines.push("");
3794
+ }
3795
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
3796
+ operationCount++;
3797
+ const index = modification.indexesToRemove[i];
3798
+ const varName = `collection_${collectionName}_rmidx_${i}`;
3799
+ const isLast = operationCount === totalOperations;
3800
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
3801
+ if (!isLast) lines.push("");
3802
+ }
3803
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
3804
+ for (const permission of modification.permissionsToUpdate) {
3805
+ operationCount++;
3806
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
3807
+ const isLast = operationCount === totalOperations;
3808
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
3809
+ if (!isLast) lines.push("");
3810
+ }
3811
+ } else if (modification.rulesToUpdate.length > 0) {
3812
+ for (const rule of modification.rulesToUpdate) {
3813
+ operationCount++;
3814
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
3815
+ const isLast = operationCount === totalOperations;
3816
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
3817
+ if (!isLast) lines.push("");
3818
+ }
3819
+ }
3820
+ } else if (operation.type === "delete") {
3821
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
3822
+ const varName = `collection_${collectionName}`;
3823
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
3824
+ }
3825
+ return lines.join("\n");
3826
+ }
3827
+ function generateOperationDownMigration(operation, collectionIdMap) {
3828
+ const lines = [];
3829
+ if (operation.type === "create") {
3830
+ const collection = operation.collection;
3831
+ const varName = `collection_${collection.name}`;
3832
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
3833
+ } else if (operation.type === "modify") {
3834
+ const modification = operation.modifications;
3835
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
3836
+ let operationCount = 0;
3837
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
3838
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
3839
+ for (const permission of modification.permissionsToUpdate) {
3840
+ operationCount++;
3841
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
3842
+ const isLast = operationCount === totalOperations;
3843
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
3844
+ if (!isLast) lines.push("");
3845
+ }
3846
+ } else if (modification.rulesToUpdate.length > 0) {
3847
+ for (const rule of modification.rulesToUpdate) {
3848
+ operationCount++;
3849
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
3850
+ const isLast = operationCount === totalOperations;
3851
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
3852
+ if (!isLast) lines.push("");
3853
+ }
3854
+ }
3855
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
3856
+ operationCount++;
3857
+ const index = modification.indexesToRemove[i];
3858
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
3859
+ const isLast = operationCount === totalOperations;
3860
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
3861
+ if (!isLast) lines.push("");
3862
+ }
3863
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
3864
+ operationCount++;
3865
+ const index = modification.indexesToAdd[i];
3866
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
3867
+ const isLast = operationCount === totalOperations;
3868
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
3869
+ if (!isLast) lines.push("");
3870
+ }
3871
+ for (const field of modification.fieldsToRemove) {
3872
+ operationCount++;
3873
+ const varName = `collection_${collectionName}_restore_${field.name}`;
3874
+ const isLast = operationCount === totalOperations;
3875
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
3876
+ if (!isLast) lines.push("");
3877
+ }
3878
+ for (const fieldMod of modification.fieldsToModify) {
3879
+ operationCount++;
3880
+ const reverseChanges = fieldMod.changes.map((change) => ({
3881
+ property: change.property,
3882
+ oldValue: change.newValue,
3883
+ newValue: change.oldValue
3884
+ }));
3885
+ const reverseMod = {
3886
+ fieldName: fieldMod.fieldName,
3887
+ currentDefinition: fieldMod.newDefinition,
3888
+ newDefinition: fieldMod.currentDefinition,
3889
+ changes: reverseChanges
3890
+ };
3891
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
3892
+ const isLast = operationCount === totalOperations;
3893
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
3894
+ if (!isLast) lines.push("");
3895
+ }
3896
+ for (const field of modification.fieldsToAdd) {
3897
+ operationCount++;
3898
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
3899
+ const isLast = operationCount === totalOperations;
3900
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
3901
+ if (!isLast) lines.push("");
3902
+ }
3903
+ } else if (operation.type === "delete") {
3904
+ const collection = operation.collection;
3905
+ if (typeof collection !== "string") {
3906
+ const varName = `collection_${collection.name}`;
3907
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
3908
+ }
3909
+ }
3910
+ return lines.join("\n");
3911
+ }
3567
3912
  function generateUpMigration(diff) {
3568
3913
  const lines = [];
3569
3914
  lines.push(` // UP MIGRATION`);
3570
3915
  lines.push(``);
3916
+ const collectionIdMap = /* @__PURE__ */ new Map();
3917
+ for (const collection of diff.collectionsToCreate) {
3918
+ if (collection.id) {
3919
+ collectionIdMap.set(collection.name, collection.id);
3920
+ }
3921
+ }
3571
3922
  if (diff.collectionsToCreate.length > 0) {
3572
3923
  lines.push(` // Create new collections`);
3573
3924
  for (let i = 0; i < diff.collectionsToCreate.length; i++) {
3574
3925
  const collection = diff.collectionsToCreate[i];
3575
3926
  const varName = `collection_${collection.name}_create`;
3576
- lines.push(generateCollectionCreation(collection, varName));
3927
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
3577
3928
  lines.push(``);
3578
3929
  }
3579
3930
  }
@@ -3585,7 +3936,7 @@ function generateUpMigration(diff) {
3585
3936
  lines.push(` // Add fields to ${collectionName}`);
3586
3937
  for (const field of modification.fieldsToAdd) {
3587
3938
  const varName = `collection_${collectionName}_add_${field.name}`;
3588
- lines.push(generateFieldAddition(collectionName, field, varName));
3939
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
3589
3940
  lines.push(``);
3590
3941
  }
3591
3942
  }
@@ -3676,12 +4027,23 @@ function generateDownMigration(diff) {
3676
4027
  const lines = [];
3677
4028
  lines.push(` // DOWN MIGRATION (ROLLBACK)`);
3678
4029
  lines.push(``);
4030
+ const collectionIdMap = /* @__PURE__ */ new Map();
4031
+ for (const collection of diff.collectionsToCreate) {
4032
+ if (collection.id) {
4033
+ collectionIdMap.set(collection.name, collection.id);
4034
+ }
4035
+ }
4036
+ for (const collection of diff.collectionsToDelete) {
4037
+ if (collection.id) {
4038
+ collectionIdMap.set(collection.name, collection.id);
4039
+ }
4040
+ }
3679
4041
  if (diff.collectionsToDelete.length > 0) {
3680
4042
  lines.push(` // Recreate deleted collections`);
3681
4043
  for (let i = 0; i < diff.collectionsToDelete.length; i++) {
3682
4044
  const collection = diff.collectionsToDelete[i];
3683
4045
  const varName = `collection_${collection.name}_recreate`;
3684
- lines.push(generateCollectionCreation(collection, varName));
4046
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
3685
4047
  lines.push(``);
3686
4048
  }
3687
4049
  }
@@ -3726,7 +4088,7 @@ function generateDownMigration(diff) {
3726
4088
  lines.push(` // Restore fields to ${collectionName}`);
3727
4089
  for (const field of modification.fieldsToRemove) {
3728
4090
  const varName = `collection_${collectionName}_restore_${field.name}`;
3729
- lines.push(generateFieldAddition(collectionName, field, varName));
4091
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
3730
4092
  lines.push(``);
3731
4093
  }
3732
4094
  }
@@ -3795,12 +4157,33 @@ function generate(diff, config) {
3795
4157
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
3796
4158
  try {
3797
4159
  const migrationDir = resolveMigrationDir(normalizedConfig);
3798
- const upCode = generateUpMigration(diff);
3799
- const downCode = generateDownMigration(diff);
3800
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
3801
- const filename = generateMigrationFilename(diff, normalizedConfig);
3802
- const filePath = writeMigrationFile(migrationDir, filename, content);
3803
- return filePath;
4160
+ const hasChanges2 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
4161
+ if (!hasChanges2) {
4162
+ return [];
4163
+ }
4164
+ const collectionIdMap = /* @__PURE__ */ new Map();
4165
+ for (const collection of diff.collectionsToCreate) {
4166
+ if (collection.id) {
4167
+ collectionIdMap.set(collection.name, collection.id);
4168
+ }
4169
+ }
4170
+ for (const collection of diff.collectionsToDelete) {
4171
+ if (collection.id) {
4172
+ collectionIdMap.set(collection.name, collection.id);
4173
+ }
4174
+ }
4175
+ const baseTimestamp = generateTimestamp(normalizedConfig);
4176
+ const operations = splitDiffByCollection(diff, baseTimestamp);
4177
+ const filePaths = [];
4178
+ for (const operation of operations) {
4179
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
4180
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
4181
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
4182
+ const filename = generateCollectionMigrationFilename(operation);
4183
+ const filePath = writeMigrationFile(migrationDir, filename, content);
4184
+ filePaths.push(filePath);
4185
+ }
4186
+ return filePaths;
3804
4187
  } catch (error) {
3805
4188
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
3806
4189
  throw error;
@@ -3818,7 +4201,8 @@ var MigrationGenerator = class {
3818
4201
  this.config = mergeConfig4(config);
3819
4202
  }
3820
4203
  /**
3821
- * Generates a migration file from a schema diff
4204
+ * Generates migration files from a schema diff
4205
+ * Returns array of file paths (one per collection operation)
3822
4206
  */
3823
4207
  generate(diff) {
3824
4208
  return generate(diff, this.config);
@@ -3846,6 +4230,7 @@ var MigrationGenerator = class {
3846
4230
  exports.CLIUsageError = CLIUsageError;
3847
4231
  exports.ConfigurationError = ConfigurationError;
3848
4232
  exports.DiffEngine = DiffEngine;
4233
+ exports.FIELD_METADATA_KEY = FIELD_METADATA_KEY;
3849
4234
  exports.FIELD_TYPE_INFO = FIELD_TYPE_INFO;
3850
4235
  exports.FileSystemError = FileSystemError;
3851
4236
  exports.MigrationError = MigrationError;
@@ -3874,6 +4259,7 @@ exports.detectFieldChanges = detectFieldChanges;
3874
4259
  exports.discoverSchemaFiles = discoverSchemaFiles;
3875
4260
  exports.extractComprehensiveFieldOptions = extractComprehensiveFieldOptions;
3876
4261
  exports.extractFieldDefinitions = extractFieldDefinitions;
4262
+ exports.extractFieldMetadata = extractFieldMetadata;
3877
4263
  exports.extractFieldOptions = extractFieldOptions;
3878
4264
  exports.extractIndexes = extractIndexes;
3879
4265
  exports.extractSchemaDefinitions = extractSchemaDefinitions;