pocketbase-zod-schema 0.2.5 → 0.3.1

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 (68) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli/index.cjs +497 -298
  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 +497 -298
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/migrate.cjs +497 -298
  9. package/dist/cli/migrate.cjs.map +1 -1
  10. package/dist/cli/migrate.js +497 -298
  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-RVj26U-O.d.cts} +17 -0
  15. package/dist/{fields-YjcpBXVp.d.ts → fields-RVj26U-O.d.ts} +17 -0
  16. package/dist/index.cjs +575 -155
  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 +576 -144
  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 +150 -24
  29. package/dist/migration/diff.cjs.map +1 -1
  30. package/dist/migration/diff.d.cts +4 -4
  31. package/dist/migration/diff.d.ts +4 -4
  32. package/dist/migration/diff.js +150 -24
  33. package/dist/migration/diff.js.map +1 -1
  34. package/dist/migration/generator.cjs +360 -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 +356 -47
  39. package/dist/migration/generator.js.map +1 -1
  40. package/dist/migration/index.cjs +561 -90
  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 +561 -90
  45. package/dist/migration/index.js.map +1 -1
  46. package/dist/migration/snapshot.cjs +51 -18
  47. package/dist/migration/snapshot.cjs.map +1 -1
  48. package/dist/migration/snapshot.d.cts +2 -2
  49. package/dist/migration/snapshot.d.ts +2 -2
  50. package/dist/migration/snapshot.js +51 -18
  51. package/dist/migration/snapshot.js.map +1 -1
  52. package/dist/migration/utils/index.cjs +66 -0
  53. package/dist/migration/utils/index.cjs.map +1 -1
  54. package/dist/migration/utils/index.d.cts +39 -202
  55. package/dist/migration/utils/index.d.ts +39 -202
  56. package/dist/migration/utils/index.js +65 -1
  57. package/dist/migration/utils/index.js.map +1 -1
  58. package/dist/schema.cjs +0 -61
  59. package/dist/schema.cjs.map +1 -1
  60. package/dist/schema.d.cts +2 -86
  61. package/dist/schema.d.ts +2 -86
  62. package/dist/schema.js +1 -50
  63. package/dist/schema.js.map +1 -1
  64. package/dist/type-mapper-CZzVeDj7.d.ts +208 -0
  65. package/dist/type-mapper-DaBe-1ph.d.cts +208 -0
  66. package/dist/{types-LFBGHl9Y.d.ts → types-CUVzgZ9k.d.ts} +33 -2
  67. package/dist/{types-mhQXWNi3.d.cts → types-D-Fsdn_O.d.cts} +33 -2
  68. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var zod = require('zod');
4
4
  var fs3 = require('fs');
5
5
  var path5 = require('path');
6
+ var crypto = require('crypto');
6
7
  var chalk = require('chalk');
7
8
  var ora = require('ora');
8
9
 
@@ -393,46 +394,6 @@ var inputImageFileSchema = {
393
394
  var omitImageFilesSchema = {
394
395
  imageFiles: true
395
396
  };
396
- function textField(options) {
397
- let schema = zod.z.string();
398
- if (options?.min !== void 0) schema = schema.min(options.min);
399
- if (options?.max !== void 0) schema = schema.max(options.max);
400
- if (options?.pattern !== void 0) schema = schema.regex(options.pattern);
401
- return schema;
402
- }
403
- function emailField() {
404
- return zod.z.string().email();
405
- }
406
- function urlField() {
407
- return zod.z.string().url();
408
- }
409
- function numberField(options) {
410
- let schema = zod.z.number();
411
- if (options?.min !== void 0) schema = schema.min(options.min);
412
- if (options?.max !== void 0) schema = schema.max(options.max);
413
- return schema;
414
- }
415
- function boolField() {
416
- return zod.z.boolean();
417
- }
418
- function dateField() {
419
- return zod.z.date();
420
- }
421
- function selectField(values) {
422
- return zod.z.enum(values);
423
- }
424
- function jsonField(schema) {
425
- return schema ?? zod.z.record(zod.z.any());
426
- }
427
- function fileField() {
428
- return zod.z.instanceof(File);
429
- }
430
- function filesField(options) {
431
- let schema = zod.z.array(zod.z.instanceof(File));
432
- if (options?.min !== void 0) schema = schema.min(options.min);
433
- if (options?.max !== void 0) schema = schema.max(options.max);
434
- return schema;
435
- }
436
397
  var RELATION_METADATA_KEY = "__pocketbase_relation__";
437
398
  function RelationField(config) {
438
399
  const metadata = {
@@ -476,15 +437,6 @@ function extractRelationMetadata(description) {
476
437
  }
477
438
  return null;
478
439
  }
479
- function editorField() {
480
- return zod.z.string();
481
- }
482
- function geoPointField() {
483
- return zod.z.object({
484
- lon: zod.z.number(),
485
- lat: zod.z.number()
486
- });
487
- }
488
440
  function withPermissions(schema, config) {
489
441
  const metadata = {
490
442
  permissions: config
@@ -2331,12 +2283,22 @@ function isAuthCollection(fields) {
2331
2283
  function buildFieldDefinition(fieldName, zodType) {
2332
2284
  const fieldMetadata = extractFieldMetadata(zodType.description);
2333
2285
  if (fieldMetadata) {
2334
- const required2 = isFieldRequired(zodType);
2286
+ let required2;
2287
+ if (fieldMetadata.type === "number") {
2288
+ if (fieldMetadata.options?.required !== void 0) {
2289
+ required2 = fieldMetadata.options.required;
2290
+ } else {
2291
+ required2 = false;
2292
+ }
2293
+ } else {
2294
+ required2 = isFieldRequired(zodType);
2295
+ }
2296
+ const { required: _required, ...options2 } = fieldMetadata.options || {};
2335
2297
  const fieldDef2 = {
2336
2298
  name: fieldName,
2337
2299
  type: fieldMetadata.type,
2338
2300
  required: required2,
2339
- options: fieldMetadata.options
2301
+ options: Object.keys(options2).length > 0 ? options2 : void 0
2340
2302
  };
2341
2303
  if (fieldMetadata.type === "relation") {
2342
2304
  const relationMetadata2 = extractRelationMetadata(zodType.description);
@@ -2535,7 +2497,7 @@ var SchemaAnalyzer = class {
2535
2497
  var SNAPSHOT_VERSION = "1.0.0";
2536
2498
  function resolveCollectionIdToName(collectionId) {
2537
2499
  if (collectionId === "_pb_users_auth_") {
2538
- return "Users";
2500
+ return "users";
2539
2501
  }
2540
2502
  const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2541
2503
  if (nameMatch) {
@@ -2543,6 +2505,39 @@ function resolveCollectionIdToName(collectionId) {
2543
2505
  }
2544
2506
  return collectionId;
2545
2507
  }
2508
+ function extractFieldOptions2(pbField) {
2509
+ const options = {};
2510
+ if (pbField.options && typeof pbField.options === "object") {
2511
+ Object.assign(options, pbField.options);
2512
+ }
2513
+ const directOptionKeys = [
2514
+ "min",
2515
+ "max",
2516
+ "pattern",
2517
+ "noDecimal",
2518
+ // text/number fields
2519
+ "values",
2520
+ "maxSelect",
2521
+ // select fields
2522
+ "mimeTypes",
2523
+ "maxSize",
2524
+ "thumbs",
2525
+ "protected",
2526
+ // file fields
2527
+ "onCreate",
2528
+ "onUpdate",
2529
+ // autodate fields
2530
+ "exceptDomains",
2531
+ "onlyDomains"
2532
+ // email/url fields
2533
+ ];
2534
+ for (const key of directOptionKeys) {
2535
+ if (pbField[key] !== void 0) {
2536
+ options[key] = pbField[key];
2537
+ }
2538
+ }
2539
+ return options;
2540
+ }
2546
2541
  function convertPocketBaseCollection(pbCollection) {
2547
2542
  const fields = [];
2548
2543
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
@@ -2560,23 +2555,19 @@ function convertPocketBaseCollection(pbCollection) {
2560
2555
  type: pbField.type,
2561
2556
  required: pbField.required || false
2562
2557
  };
2563
- field.options = pbField.options ? { ...pbField.options } : {};
2564
- if (pbField.type === "select") {
2565
- if (pbField.values && Array.isArray(pbField.values)) {
2566
- field.options.values = pbField.values;
2567
- } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2568
- field.options.values = pbField.options.values;
2569
- }
2570
- }
2558
+ field.options = extractFieldOptions2(pbField);
2571
2559
  if (pbField.type === "relation") {
2572
2560
  const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2573
- const collectionName = resolveCollectionIdToName(collectionId);
2561
+ const collectionName = resolveCollectionIdToName(collectionId || "");
2574
2562
  field.relation = {
2575
2563
  collection: collectionName,
2576
2564
  cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2577
2565
  maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2578
2566
  minSelect: pbField.minSelect ?? pbField.options?.minSelect
2579
2567
  };
2568
+ delete field.options.maxSelect;
2569
+ delete field.options.minSelect;
2570
+ delete field.options.cascadeDelete;
2580
2571
  }
2581
2572
  const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2582
2573
  if (Object.keys(field.options).length === 0) {
@@ -2590,17 +2581,21 @@ function convertPocketBaseCollection(pbCollection) {
2590
2581
  type: pbCollection.type || "base",
2591
2582
  fields
2592
2583
  };
2584
+ if (pbCollection.id) {
2585
+ schema.id = pbCollection.id;
2586
+ }
2593
2587
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2594
2588
  schema.indexes = pbCollection.indexes;
2595
2589
  }
2596
- const rules = {};
2597
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2598
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2599
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2600
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2601
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2602
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2603
- if (Object.keys(rules).length > 0) {
2590
+ const hasAnyRule = pbCollection.listRule !== void 0 || pbCollection.viewRule !== void 0 || pbCollection.createRule !== void 0 || pbCollection.updateRule !== void 0 || pbCollection.deleteRule !== void 0 || pbCollection.manageRule !== void 0;
2591
+ if (hasAnyRule) {
2592
+ const rules = {};
2593
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2594
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2595
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2596
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2597
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2598
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2604
2599
  schema.rules = rules;
2605
2600
  schema.permissions = { ...rules };
2606
2601
  }
@@ -3332,6 +3327,67 @@ var SnapshotManager = class {
3332
3327
  return validateSnapshot(snapshot);
3333
3328
  }
3334
3329
  };
3330
+ function generateCollectionId() {
3331
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
3332
+ const idLength = 15;
3333
+ const bytes = crypto.randomBytes(idLength);
3334
+ let id = "pb_";
3335
+ for (let i = 0; i < idLength; i++) {
3336
+ const index = bytes[i] % chars.length;
3337
+ id += chars[index];
3338
+ }
3339
+ return id;
3340
+ }
3341
+ var CollectionIdRegistry = class {
3342
+ ids;
3343
+ constructor() {
3344
+ this.ids = /* @__PURE__ */ new Set();
3345
+ }
3346
+ /**
3347
+ * Generates a unique collection ID for a given collection name
3348
+ * Retries up to 10 times if collision occurs (extremely rare)
3349
+ * Special case: returns "_pb_users_auth_" for users collection
3350
+ *
3351
+ * @param collectionName - The name of the collection (optional)
3352
+ * @returns A unique collection ID
3353
+ * @throws Error if unable to generate unique ID after max attempts
3354
+ */
3355
+ generate(collectionName) {
3356
+ if (collectionName && collectionName.toLowerCase() === "users") {
3357
+ const usersId = "_pb_users_auth_";
3358
+ if (!this.has(usersId)) {
3359
+ this.register(usersId);
3360
+ }
3361
+ return usersId;
3362
+ }
3363
+ const maxAttempts = 10;
3364
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
3365
+ const id = generateCollectionId();
3366
+ if (!this.has(id)) {
3367
+ this.register(id);
3368
+ return id;
3369
+ }
3370
+ }
3371
+ throw new Error("Failed to generate unique collection ID after maximum attempts");
3372
+ }
3373
+ /**
3374
+ * Checks if an ID has already been registered
3375
+ *
3376
+ * @param id - The collection ID to check
3377
+ * @returns True if the ID exists in the registry
3378
+ */
3379
+ has(id) {
3380
+ return this.ids.has(id);
3381
+ }
3382
+ /**
3383
+ * Registers a collection ID in the registry
3384
+ *
3385
+ * @param id - The collection ID to register
3386
+ */
3387
+ register(id) {
3388
+ this.ids.add(id);
3389
+ }
3390
+ };
3335
3391
 
3336
3392
  // src/migration/diff.ts
3337
3393
  var DEFAULT_CONFIG3 = {
@@ -3485,18 +3541,49 @@ function compareFieldConstraints(currentField, previousField) {
3485
3541
  }
3486
3542
  return changes;
3487
3543
  }
3544
+ function normalizeOptionValue(key, value, fieldType) {
3545
+ if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
3546
+ return void 0;
3547
+ }
3548
+ if (key === "maxSize" && value === 0 && fieldType === "file") {
3549
+ return void 0;
3550
+ }
3551
+ if (fieldType === "file") {
3552
+ if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
3553
+ return void 0;
3554
+ }
3555
+ if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
3556
+ return void 0;
3557
+ }
3558
+ if (key === "protected" && value === false) {
3559
+ return void 0;
3560
+ }
3561
+ }
3562
+ if (fieldType === "autodate") {
3563
+ if (key === "onCreate" && value === true) {
3564
+ return void 0;
3565
+ }
3566
+ if (key === "onUpdate" && value === false) {
3567
+ return void 0;
3568
+ }
3569
+ }
3570
+ return value;
3571
+ }
3488
3572
  function compareFieldOptions(currentField, previousField) {
3489
3573
  const changes = [];
3490
3574
  const currentOptions = currentField.options || {};
3491
3575
  const previousOptions = previousField.options || {};
3492
3576
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(currentOptions), ...Object.keys(previousOptions)]);
3577
+ const fieldType = currentField.type;
3493
3578
  for (const key of allKeys) {
3494
3579
  const currentValue = currentOptions[key];
3495
3580
  const previousValue = previousOptions[key];
3496
- if (currentValue === void 0 && previousValue === void 0) {
3581
+ const normalizedCurrent = normalizeOptionValue(key, currentValue, fieldType);
3582
+ const normalizedPrevious = normalizeOptionValue(key, previousValue, fieldType);
3583
+ if (normalizedCurrent === void 0 && normalizedPrevious === void 0) {
3497
3584
  continue;
3498
3585
  }
3499
- if (!areValuesEqual(currentValue, previousValue)) {
3586
+ if (!areValuesEqual(normalizedCurrent, normalizedPrevious)) {
3500
3587
  changes.push({
3501
3588
  property: `options.${key}`,
3502
3589
  oldValue: previousValue,
@@ -3506,7 +3593,7 @@ function compareFieldOptions(currentField, previousField) {
3506
3593
  }
3507
3594
  return changes;
3508
3595
  }
3509
- function compareRelationConfigurations(currentField, previousField) {
3596
+ function compareRelationConfigurations(currentField, previousField, collectionIdToName) {
3510
3597
  const changes = [];
3511
3598
  const currentRelation = currentField.relation;
3512
3599
  const previousRelation = previousField.relation;
@@ -3518,8 +3605,8 @@ function compareRelationConfigurations(currentField, previousField) {
3518
3605
  }
3519
3606
  const normalizeCollection = (collection) => {
3520
3607
  if (!collection) return collection;
3521
- if (collection === "_pb_users_auth_") {
3522
- return "Users";
3608
+ if (collectionIdToName && collectionIdToName.has(collection)) {
3609
+ return collectionIdToName.get(collection);
3523
3610
  }
3524
3611
  const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
3525
3612
  if (nameMatch) {
@@ -3529,13 +3616,11 @@ function compareRelationConfigurations(currentField, previousField) {
3529
3616
  };
3530
3617
  const normalizedCurrent = normalizeCollection(currentRelation.collection);
3531
3618
  const normalizedPrevious = normalizeCollection(previousRelation.collection);
3532
- if (normalizedCurrent !== normalizedPrevious) {
3619
+ if (normalizedCurrent.toLowerCase() !== normalizedPrevious.toLowerCase()) {
3533
3620
  changes.push({
3534
3621
  property: "relation.collection",
3535
- oldValue: normalizedPrevious,
3536
- // Use normalized value for clarity
3537
- newValue: normalizedCurrent
3538
- // Use normalized value for clarity
3622
+ oldValue: previousRelation.collection,
3623
+ newValue: currentRelation.collection
3539
3624
  });
3540
3625
  }
3541
3626
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -3545,14 +3630,20 @@ function compareRelationConfigurations(currentField, previousField) {
3545
3630
  newValue: currentRelation.cascadeDelete
3546
3631
  });
3547
3632
  }
3548
- if (currentRelation.maxSelect !== previousRelation.maxSelect) {
3633
+ const normalizeMax = (val) => val === 1 ? null : val;
3634
+ const currentMax = normalizeMax(currentRelation.maxSelect);
3635
+ const previousMax = normalizeMax(previousRelation.maxSelect);
3636
+ if (currentMax != previousMax) {
3549
3637
  changes.push({
3550
3638
  property: "relation.maxSelect",
3551
3639
  oldValue: previousRelation.maxSelect,
3552
3640
  newValue: currentRelation.maxSelect
3553
3641
  });
3554
3642
  }
3555
- if (currentRelation.minSelect !== previousRelation.minSelect) {
3643
+ const normalizeMin = (val) => val === 0 ? null : val;
3644
+ const currentMin = normalizeMin(currentRelation.minSelect);
3645
+ const previousMin = normalizeMin(previousRelation.minSelect);
3646
+ if (currentMin != previousMin) {
3556
3647
  changes.push({
3557
3648
  property: "relation.minSelect",
3558
3649
  oldValue: previousRelation.minSelect,
@@ -3561,7 +3652,7 @@ function compareRelationConfigurations(currentField, previousField) {
3561
3652
  }
3562
3653
  return changes;
3563
3654
  }
3564
- function detectFieldChanges(currentField, previousField) {
3655
+ function detectFieldChanges(currentField, previousField, collectionIdToName) {
3565
3656
  const changes = [];
3566
3657
  const typeChange = compareFieldTypes(currentField, previousField);
3567
3658
  if (typeChange) {
@@ -3570,7 +3661,7 @@ function detectFieldChanges(currentField, previousField) {
3570
3661
  changes.push(...compareFieldConstraints(currentField, previousField));
3571
3662
  changes.push(...compareFieldOptions(currentField, previousField));
3572
3663
  if (currentField.type === "relation" && previousField.type === "relation") {
3573
- changes.push(...compareRelationConfigurations(currentField, previousField));
3664
+ changes.push(...compareRelationConfigurations(currentField, previousField, collectionIdToName));
3574
3665
  }
3575
3666
  return changes;
3576
3667
  }
@@ -3581,7 +3672,7 @@ function compareIndexes(currentIndexes = [], previousIndexes = []) {
3581
3672
  const indexesToRemove = previousIndexes.filter((idx) => !currentSet.has(idx));
3582
3673
  return { indexesToAdd, indexesToRemove };
3583
3674
  }
3584
- function compareRules(currentRules, previousRules) {
3675
+ function compareRules(currentRules, previousRules, currentPermissions, previousPermissions) {
3585
3676
  const updates = [];
3586
3677
  const ruleTypes = [
3587
3678
  "listRule",
@@ -3592,8 +3683,8 @@ function compareRules(currentRules, previousRules) {
3592
3683
  "manageRule"
3593
3684
  ];
3594
3685
  for (const ruleType of ruleTypes) {
3595
- const currentValue = currentRules?.[ruleType] ?? null;
3596
- const previousValue = previousRules?.[ruleType] ?? null;
3686
+ const currentValue = currentRules?.[ruleType] ?? currentPermissions?.[ruleType] ?? null;
3687
+ const previousValue = previousRules?.[ruleType] ?? previousPermissions?.[ruleType] ?? null;
3597
3688
  if (currentValue !== previousValue) {
3598
3689
  updates.push({
3599
3690
  ruleType,
@@ -3620,7 +3711,7 @@ function comparePermissions(currentPermissions, previousPermissions) {
3620
3711
  }
3621
3712
  return changes;
3622
3713
  }
3623
- function compareCollectionFields(currentCollection, previousCollection, config) {
3714
+ function compareCollectionFields(currentCollection, previousCollection, config, collectionIdToName) {
3624
3715
  let fieldsToAdd = findNewFields(currentCollection.fields, previousCollection.fields);
3625
3716
  const fieldsToRemove = findRemovedFields(currentCollection.fields, previousCollection.fields);
3626
3717
  const fieldsToModify = [];
@@ -3630,7 +3721,7 @@ function compareCollectionFields(currentCollection, previousCollection, config)
3630
3721
  }
3631
3722
  const matchedFields = matchFieldsByName(currentCollection.fields, previousCollection.fields);
3632
3723
  for (const [currentField, previousField] of matchedFields) {
3633
- const changes = detectFieldChanges(currentField, previousField);
3724
+ const changes = detectFieldChanges(currentField, previousField, collectionIdToName);
3634
3725
  if (changes.length > 0) {
3635
3726
  fieldsToModify.push({
3636
3727
  fieldName: currentField.name,
@@ -3642,14 +3733,20 @@ function compareCollectionFields(currentCollection, previousCollection, config)
3642
3733
  }
3643
3734
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
3644
3735
  }
3645
- function buildCollectionModification(currentCollection, previousCollection, config) {
3736
+ function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
3646
3737
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
3647
3738
  currentCollection,
3648
3739
  previousCollection,
3649
- config
3740
+ config,
3741
+ collectionIdToName
3650
3742
  );
3651
3743
  const { indexesToAdd, indexesToRemove } = compareIndexes(currentCollection.indexes, previousCollection.indexes);
3652
- const rulesToUpdate = compareRules(currentCollection.rules, previousCollection.rules);
3744
+ const rulesToUpdate = compareRules(
3745
+ currentCollection.rules,
3746
+ previousCollection.rules,
3747
+ currentCollection.permissions,
3748
+ previousCollection.permissions
3749
+ );
3653
3750
  const permissionsToUpdate = comparePermissions(currentCollection.permissions, previousCollection.permissions);
3654
3751
  return {
3655
3752
  collection: currentCollection.name,
@@ -3666,6 +3763,14 @@ function hasChanges(modification) {
3666
3763
  return modification.fieldsToAdd.length > 0 || modification.fieldsToRemove.length > 0 || modification.fieldsToModify.length > 0 || modification.indexesToAdd.length > 0 || modification.indexesToRemove.length > 0 || modification.rulesToUpdate.length > 0 || modification.permissionsToUpdate.length > 0;
3667
3764
  }
3668
3765
  function aggregateChanges(currentSchema, previousSnapshot, config) {
3766
+ const collectionIdToName = /* @__PURE__ */ new Map();
3767
+ if (previousSnapshot) {
3768
+ for (const [name, collection] of previousSnapshot.collections) {
3769
+ if (collection.id) {
3770
+ collectionIdToName.set(collection.id, name);
3771
+ }
3772
+ }
3773
+ }
3669
3774
  const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
3670
3775
  const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
3671
3776
  const filteredCollectionsToCreate = collectionsToCreate.filter(
@@ -3674,16 +3779,28 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
3674
3779
  const filteredCollectionsToDelete = collectionsToDelete.filter(
3675
3780
  (collection) => !isSystemCollection(collection.name, config)
3676
3781
  );
3782
+ const registry = new CollectionIdRegistry();
3783
+ const collectionsWithIds = filteredCollectionsToCreate.map((collection) => {
3784
+ if (collection.id) {
3785
+ registry.register(collection.id);
3786
+ return collection;
3787
+ }
3788
+ const id = registry.generate(collection.name);
3789
+ return {
3790
+ ...collection,
3791
+ id
3792
+ };
3793
+ });
3677
3794
  const collectionsToModify = [];
3678
3795
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
3679
3796
  for (const [currentCollection, previousCollection] of matchedCollections) {
3680
- const modification = buildCollectionModification(currentCollection, previousCollection, config);
3797
+ const modification = buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName);
3681
3798
  if (hasChanges(modification)) {
3682
3799
  collectionsToModify.push(modification);
3683
3800
  }
3684
3801
  }
3685
3802
  return {
3686
- collectionsToCreate: filteredCollectionsToCreate,
3803
+ collectionsToCreate: collectionsWithIds,
3687
3804
  collectionsToDelete: filteredCollectionsToDelete,
3688
3805
  collectionsToModify
3689
3806
  };
@@ -3919,6 +4036,49 @@ function generateTimestamp(config) {
3919
4036
  }
3920
4037
  return Math.floor(Date.now() / 1e3).toString();
3921
4038
  }
4039
+ function splitDiffByCollection(diff, baseTimestamp) {
4040
+ const operations = [];
4041
+ let currentTimestamp = parseInt(baseTimestamp, 10);
4042
+ for (const collection of diff.collectionsToCreate) {
4043
+ operations.push({
4044
+ type: "create",
4045
+ collection,
4046
+ timestamp: currentTimestamp.toString()
4047
+ });
4048
+ currentTimestamp += 1;
4049
+ }
4050
+ for (const modification of diff.collectionsToModify) {
4051
+ operations.push({
4052
+ type: "modify",
4053
+ collection: modification.collection,
4054
+ modifications: modification,
4055
+ timestamp: currentTimestamp.toString()
4056
+ });
4057
+ currentTimestamp += 1;
4058
+ }
4059
+ for (const collection of diff.collectionsToDelete) {
4060
+ operations.push({
4061
+ type: "delete",
4062
+ collection: collection.name || collection,
4063
+ // Handle both object and string
4064
+ timestamp: currentTimestamp.toString()
4065
+ });
4066
+ currentTimestamp += 1;
4067
+ }
4068
+ return operations;
4069
+ }
4070
+ function generateCollectionMigrationFilename(operation) {
4071
+ const timestamp = operation.timestamp;
4072
+ const operationType = operation.type === "modify" ? "updated" : operation.type === "create" ? "created" : "deleted";
4073
+ let collectionName;
4074
+ if (typeof operation.collection === "string") {
4075
+ collectionName = operation.collection;
4076
+ } else {
4077
+ collectionName = operation.collection.name;
4078
+ }
4079
+ const sanitizedName = collectionName.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
4080
+ return `${timestamp}_${operationType}_${sanitizedName}.js`;
4081
+ }
3922
4082
  function generateMigrationDescription(diff) {
3923
4083
  const parts = [];
3924
4084
  if (diff.collectionsToCreate.length > 0) {
@@ -4026,14 +4186,13 @@ function formatValue(value) {
4026
4186
  return "null";
4027
4187
  }
4028
4188
  if (typeof value === "string") {
4029
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
4189
+ return JSON.stringify(value);
4030
4190
  }
4031
4191
  if (typeof value === "number" || typeof value === "boolean") {
4032
4192
  return String(value);
4033
4193
  }
4034
4194
  if (Array.isArray(value)) {
4035
- const items = value.map((v) => formatValue(v)).join(", ");
4036
- return `[${items}]`;
4195
+ return JSON.stringify(value).replace(/","/g, '", "');
4037
4196
  }
4038
4197
  if (typeof value === "object") {
4039
4198
  const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
@@ -4041,7 +4200,7 @@ function formatValue(value) {
4041
4200
  }
4042
4201
  return String(value);
4043
4202
  }
4044
- function generateFieldDefinitionObject(field) {
4203
+ function generateFieldDefinitionObject(field, collectionIdMap) {
4045
4204
  const parts = [];
4046
4205
  parts.push(` name: "${field.name}"`);
4047
4206
  parts.push(` type: "${field.type}"`);
@@ -4049,34 +4208,47 @@ function generateFieldDefinitionObject(field) {
4049
4208
  if (field.unique !== void 0) {
4050
4209
  parts.push(` unique: ${field.unique}`);
4051
4210
  }
4211
+ if (field.type === "select") {
4212
+ const maxSelect = field.options?.maxSelect ?? 1;
4213
+ parts.push(` maxSelect: ${maxSelect}`);
4214
+ const values = field.options?.values ?? [];
4215
+ parts.push(` values: ${formatValue(values)}`);
4216
+ }
4052
4217
  if (field.options && Object.keys(field.options).length > 0) {
4053
4218
  for (const [key, value] of Object.entries(field.options)) {
4219
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
4220
+ continue;
4221
+ }
4054
4222
  parts.push(` ${key}: ${formatValue(value)}`);
4055
4223
  }
4056
4224
  }
4057
4225
  if (field.relation) {
4058
4226
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
4059
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
4060
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
4061
- if (field.relation.maxSelect !== void 0) {
4062
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
4063
- }
4064
- if (field.relation.minSelect !== void 0) {
4065
- parts.push(` minSelect: ${field.relation.minSelect}`);
4066
- }
4067
- if (field.relation.cascadeDelete !== void 0) {
4068
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
4227
+ let collectionIdValue;
4228
+ if (isUsersCollection) {
4229
+ collectionIdValue = '"_pb_users_auth_"';
4230
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
4231
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
4232
+ } else {
4233
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
4069
4234
  }
4235
+ parts.push(` collectionId: ${collectionIdValue}`);
4236
+ const maxSelect = field.relation.maxSelect ?? 1;
4237
+ parts.push(` maxSelect: ${maxSelect}`);
4238
+ const minSelect = field.relation.minSelect ?? null;
4239
+ parts.push(` minSelect: ${minSelect}`);
4240
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
4241
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
4070
4242
  }
4071
4243
  return ` {
4072
4244
  ${parts.join(",\n")},
4073
4245
  }`;
4074
4246
  }
4075
- function generateFieldsArray(fields) {
4247
+ function generateFieldsArray(fields, collectionIdMap) {
4076
4248
  if (fields.length === 0) {
4077
4249
  return "[]";
4078
4250
  }
4079
- const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field));
4251
+ const fieldObjects = fields.map((field) => generateFieldDefinitionObject(field, collectionIdMap));
4080
4252
  return `[
4081
4253
  ${fieldObjects.join(",\n")},
4082
4254
  ]`;
@@ -4135,7 +4307,7 @@ function generateIndexesArray(indexes) {
4135
4307
  if (!indexes || indexes.length === 0) {
4136
4308
  return "[]";
4137
4309
  }
4138
- const indexStrings = indexes.map((idx) => `"${idx}"`);
4310
+ const indexStrings = indexes.map((idx) => JSON.stringify(idx));
4139
4311
  return `[
4140
4312
  ${indexStrings.join(",\n ")},
4141
4313
  ]`;
@@ -4189,9 +4361,12 @@ function getSystemFields() {
4189
4361
  }
4190
4362
  ];
4191
4363
  }
4192
- function generateCollectionCreation(collection, varName = "collection", isLast = false) {
4364
+ function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
4193
4365
  const lines = [];
4194
4366
  lines.push(` const ${varName} = new Collection({`);
4367
+ if (collection.id) {
4368
+ lines.push(` id: ${formatValue(collection.id)},`);
4369
+ }
4195
4370
  lines.push(` name: "${collection.name}",`);
4196
4371
  lines.push(` type: "${collection.type}",`);
4197
4372
  const permissionsCode = generateCollectionPermissions(collection.permissions);
@@ -4203,7 +4378,7 @@ function generateCollectionCreation(collection, varName = "collection", isLast =
4203
4378
  }
4204
4379
  const systemFields = getSystemFields();
4205
4380
  const allFields = [...systemFields, ...collection.fields];
4206
- lines.push(` fields: ${generateFieldsArray(allFields)},`);
4381
+ lines.push(` fields: ${generateFieldsArray(allFields, collectionIdMap)},`);
4207
4382
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
4208
4383
  lines.push(` });`);
4209
4384
  lines.push(``);
@@ -4225,42 +4400,59 @@ function getFieldConstructorName(fieldType) {
4225
4400
  };
4226
4401
  return constructorMap[fieldType] || "TextField";
4227
4402
  }
4228
- function generateFieldConstructorOptions(field) {
4403
+ function generateFieldConstructorOptions(field, collectionIdMap) {
4229
4404
  const parts = [];
4230
4405
  parts.push(` name: "${field.name}"`);
4231
4406
  parts.push(` required: ${field.required}`);
4232
4407
  if (field.unique !== void 0) {
4233
4408
  parts.push(` unique: ${field.unique}`);
4234
4409
  }
4410
+ if (field.type === "select") {
4411
+ const maxSelect = field.options?.maxSelect ?? 1;
4412
+ parts.push(` maxSelect: ${maxSelect}`);
4413
+ const values = field.options?.values ?? [];
4414
+ parts.push(` values: ${formatValue(values)}`);
4415
+ }
4235
4416
  if (field.options && Object.keys(field.options).length > 0) {
4236
4417
  for (const [key, value] of Object.entries(field.options)) {
4237
- parts.push(` ${key}: ${formatValue(value)}`);
4418
+ if (field.type === "select" && (key === "maxSelect" || key === "values")) {
4419
+ continue;
4420
+ }
4421
+ if (field.type === "number" && key === "noDecimal") {
4422
+ parts.push(` onlyInt: ${formatValue(value)}`);
4423
+ } else {
4424
+ parts.push(` ${key}: ${formatValue(value)}`);
4425
+ }
4238
4426
  }
4239
4427
  }
4240
4428
  if (field.relation && field.type === "relation") {
4241
4429
  const isUsersCollection = field.relation.collection.toLowerCase() === "users";
4242
- const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
4243
- parts.push(` collectionId: ${collectionIdPlaceholder}`);
4244
- if (field.relation.maxSelect !== void 0) {
4245
- parts.push(` maxSelect: ${field.relation.maxSelect}`);
4246
- }
4247
- if (field.relation.minSelect !== void 0) {
4248
- parts.push(` minSelect: ${field.relation.minSelect}`);
4249
- }
4250
- if (field.relation.cascadeDelete !== void 0) {
4251
- parts.push(` cascadeDelete: ${field.relation.cascadeDelete}`);
4430
+ let collectionIdValue;
4431
+ if (isUsersCollection) {
4432
+ collectionIdValue = '"_pb_users_auth_"';
4433
+ } else if (collectionIdMap && collectionIdMap.has(field.relation.collection)) {
4434
+ collectionIdValue = `"${collectionIdMap.get(field.relation.collection)}"`;
4435
+ } else {
4436
+ collectionIdValue = `app.findCollectionByNameOrId("${field.relation.collection}").id`;
4252
4437
  }
4438
+ parts.push(` collectionId: ${collectionIdValue}`);
4439
+ const maxSelect = field.relation.maxSelect ?? 1;
4440
+ parts.push(` maxSelect: ${maxSelect}`);
4441
+ const minSelect = field.relation.minSelect ?? null;
4442
+ parts.push(` minSelect: ${minSelect}`);
4443
+ const cascadeDelete = field.relation.cascadeDelete ?? false;
4444
+ parts.push(` cascadeDelete: ${cascadeDelete}`);
4253
4445
  }
4254
4446
  return parts.join(",\n");
4255
4447
  }
4256
- function generateFieldAddition(collectionName, field, varName, isLast = false) {
4448
+ function generateFieldAddition(collectionName, field, varName, isLast = false, collectionIdMap) {
4257
4449
  const lines = [];
4258
4450
  const constructorName = getFieldConstructorName(field.type);
4259
4451
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
4260
4452
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
4261
4453
  lines.push(``);
4262
4454
  lines.push(` ${collectionVar}.fields.add(new ${constructorName}({`);
4263
- lines.push(generateFieldConstructorOptions(field));
4455
+ lines.push(generateFieldConstructorOptions(field, collectionIdMap));
4264
4456
  lines.push(` }));`);
4265
4457
  lines.push(``);
4266
4458
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
@@ -4310,7 +4502,7 @@ function generateIndexAddition(collectionName, index, varName, isLast = false) {
4310
4502
  const lines = [];
4311
4503
  const collectionVar = varName || `collection_${collectionName}_idx`;
4312
4504
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
4313
- lines.push(` ${collectionVar}.indexes.push("${index}");`);
4505
+ lines.push(` ${collectionVar}.indexes.push(${JSON.stringify(index)});`);
4314
4506
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
4315
4507
  return lines.join("\n");
4316
4508
  }
@@ -4319,7 +4511,7 @@ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
4319
4511
  const collectionVar = varName || `collection_${collectionName}_idx`;
4320
4512
  const indexVar = `${collectionVar}_indexToRemove`;
4321
4513
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
4322
- lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === "${index}");`);
4514
+ lines.push(` const ${indexVar} = ${collectionVar}.indexes.findIndex(idx => idx === ${JSON.stringify(index)});`);
4323
4515
  lines.push(` if (${indexVar} !== -1) {`);
4324
4516
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
4325
4517
  lines.push(` }`);
@@ -4348,16 +4540,213 @@ function generateCollectionDeletion(collectionName, varName = "collection", isLa
4348
4540
  lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
4349
4541
  return lines.join("\n");
4350
4542
  }
4543
+ function generateOperationUpMigration(operation, collectionIdMap) {
4544
+ const lines = [];
4545
+ if (operation.type === "create") {
4546
+ const collection = operation.collection;
4547
+ const varName = `collection_${collection.name}`;
4548
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
4549
+ } else if (operation.type === "modify") {
4550
+ const modification = operation.modifications;
4551
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
4552
+ let operationCount = 0;
4553
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
4554
+ for (const field of modification.fieldsToAdd) {
4555
+ operationCount++;
4556
+ const varName = `collection_${collectionName}_add_${field.name}`;
4557
+ const isLast = operationCount === totalOperations;
4558
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
4559
+ if (!isLast) lines.push("");
4560
+ }
4561
+ for (const fieldMod of modification.fieldsToModify) {
4562
+ operationCount++;
4563
+ const varName = `collection_${collectionName}_modify_${fieldMod.fieldName}`;
4564
+ const isLast = operationCount === totalOperations;
4565
+ lines.push(generateFieldModification(collectionName, fieldMod, varName, isLast));
4566
+ if (!isLast) lines.push("");
4567
+ }
4568
+ for (const field of modification.fieldsToRemove) {
4569
+ operationCount++;
4570
+ const varName = `collection_${collectionName}_remove_${field.name}`;
4571
+ const isLast = operationCount === totalOperations;
4572
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
4573
+ if (!isLast) lines.push("");
4574
+ }
4575
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
4576
+ operationCount++;
4577
+ const index = modification.indexesToAdd[i];
4578
+ const varName = `collection_${collectionName}_addidx_${i}`;
4579
+ const isLast = operationCount === totalOperations;
4580
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
4581
+ if (!isLast) lines.push("");
4582
+ }
4583
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
4584
+ operationCount++;
4585
+ const index = modification.indexesToRemove[i];
4586
+ const varName = `collection_${collectionName}_rmidx_${i}`;
4587
+ const isLast = operationCount === totalOperations;
4588
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
4589
+ if (!isLast) lines.push("");
4590
+ }
4591
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
4592
+ for (const permission of modification.permissionsToUpdate) {
4593
+ operationCount++;
4594
+ const varName = `collection_${collectionName}_perm_${permission.ruleType}`;
4595
+ const isLast = operationCount === totalOperations;
4596
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.newValue, varName, isLast));
4597
+ if (!isLast) lines.push("");
4598
+ }
4599
+ } else if (modification.rulesToUpdate.length > 0) {
4600
+ for (const rule of modification.rulesToUpdate) {
4601
+ operationCount++;
4602
+ const varName = `collection_${collectionName}_rule_${rule.ruleType}`;
4603
+ const isLast = operationCount === totalOperations;
4604
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.newValue, varName, isLast));
4605
+ if (!isLast) lines.push("");
4606
+ }
4607
+ }
4608
+ } else if (operation.type === "delete") {
4609
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection.name;
4610
+ const varName = `collection_${collectionName}`;
4611
+ lines.push(generateCollectionDeletion(collectionName, varName, true));
4612
+ }
4613
+ let code = lines.join("\n");
4614
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4615
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4616
+ const saveMatches = [...code.matchAll(savePattern)];
4617
+ const deleteMatches = [...code.matchAll(deletePattern)];
4618
+ const allMatches = [
4619
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4620
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4621
+ ].sort((a, b) => b.index - a.index);
4622
+ if (allMatches.length > 0) {
4623
+ const lastMatch = allMatches[0];
4624
+ if (lastMatch.type === "save") {
4625
+ 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);
4626
+ } else {
4627
+ 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);
4628
+ }
4629
+ }
4630
+ return code;
4631
+ }
4632
+ function generateOperationDownMigration(operation, collectionIdMap) {
4633
+ const lines = [];
4634
+ if (operation.type === "create") {
4635
+ const collection = operation.collection;
4636
+ const varName = `collection_${collection.name}`;
4637
+ lines.push(generateCollectionDeletion(collection.name, varName, true));
4638
+ } else if (operation.type === "modify") {
4639
+ const modification = operation.modifications;
4640
+ const collectionName = typeof operation.collection === "string" ? operation.collection : operation.collection?.name ?? modification.collection;
4641
+ let operationCount = 0;
4642
+ const totalOperations = modification.fieldsToAdd.length + modification.fieldsToModify.length + modification.fieldsToRemove.length + modification.indexesToAdd.length + modification.indexesToRemove.length + modification.rulesToUpdate.length + modification.permissionsToUpdate.length;
4643
+ if (modification.permissionsToUpdate && modification.permissionsToUpdate.length > 0) {
4644
+ for (const permission of modification.permissionsToUpdate) {
4645
+ operationCount++;
4646
+ const varName = `collection_${collectionName}_revert_perm_${permission.ruleType}`;
4647
+ const isLast = operationCount === totalOperations;
4648
+ lines.push(generatePermissionUpdate(collectionName, permission.ruleType, permission.oldValue, varName, isLast));
4649
+ if (!isLast) lines.push("");
4650
+ }
4651
+ } else if (modification.rulesToUpdate.length > 0) {
4652
+ for (const rule of modification.rulesToUpdate) {
4653
+ operationCount++;
4654
+ const varName = `collection_${collectionName}_revert_rule_${rule.ruleType}`;
4655
+ const isLast = operationCount === totalOperations;
4656
+ lines.push(generateRuleUpdate(collectionName, rule.ruleType, rule.oldValue, varName, isLast));
4657
+ if (!isLast) lines.push("");
4658
+ }
4659
+ }
4660
+ for (let i = 0; i < modification.indexesToRemove.length; i++) {
4661
+ operationCount++;
4662
+ const index = modification.indexesToRemove[i];
4663
+ const varName = `collection_${collectionName}_restore_idx_${i}`;
4664
+ const isLast = operationCount === totalOperations;
4665
+ lines.push(generateIndexAddition(collectionName, index, varName, isLast));
4666
+ if (!isLast) lines.push("");
4667
+ }
4668
+ for (let i = 0; i < modification.indexesToAdd.length; i++) {
4669
+ operationCount++;
4670
+ const index = modification.indexesToAdd[i];
4671
+ const varName = `collection_${collectionName}_revert_idx_${i}`;
4672
+ const isLast = operationCount === totalOperations;
4673
+ lines.push(generateIndexRemoval(collectionName, index, varName, isLast));
4674
+ if (!isLast) lines.push("");
4675
+ }
4676
+ for (const field of modification.fieldsToRemove) {
4677
+ operationCount++;
4678
+ const varName = `collection_${collectionName}_restore_${field.name}`;
4679
+ const isLast = operationCount === totalOperations;
4680
+ lines.push(generateFieldAddition(collectionName, field, varName, isLast, collectionIdMap));
4681
+ if (!isLast) lines.push("");
4682
+ }
4683
+ for (const fieldMod of modification.fieldsToModify) {
4684
+ operationCount++;
4685
+ const reverseChanges = fieldMod.changes.map((change) => ({
4686
+ property: change.property,
4687
+ oldValue: change.newValue,
4688
+ newValue: change.oldValue
4689
+ }));
4690
+ const reverseMod = {
4691
+ fieldName: fieldMod.fieldName,
4692
+ currentDefinition: fieldMod.newDefinition,
4693
+ newDefinition: fieldMod.currentDefinition,
4694
+ changes: reverseChanges
4695
+ };
4696
+ const varName = `collection_${collectionName}_revert_${fieldMod.fieldName}`;
4697
+ const isLast = operationCount === totalOperations;
4698
+ lines.push(generateFieldModification(collectionName, reverseMod, varName, isLast));
4699
+ if (!isLast) lines.push("");
4700
+ }
4701
+ for (const field of modification.fieldsToAdd) {
4702
+ operationCount++;
4703
+ const varName = `collection_${collectionName}_revert_add_${field.name}`;
4704
+ const isLast = operationCount === totalOperations;
4705
+ lines.push(generateFieldDeletion(collectionName, field.name, varName, isLast));
4706
+ if (!isLast) lines.push("");
4707
+ }
4708
+ } else if (operation.type === "delete") {
4709
+ const collection = operation.collection;
4710
+ if (typeof collection !== "string") {
4711
+ const varName = `collection_${collection.name}`;
4712
+ lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
4713
+ }
4714
+ }
4715
+ let code = lines.join("\n");
4716
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4717
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4718
+ const saveMatches = [...code.matchAll(savePattern)];
4719
+ const deleteMatches = [...code.matchAll(deletePattern)];
4720
+ const allMatches = [
4721
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4722
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4723
+ ].sort((a, b) => b.index - a.index);
4724
+ if (allMatches.length > 0) {
4725
+ const lastMatch = allMatches[0];
4726
+ if (lastMatch.type === "save") {
4727
+ 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);
4728
+ } else {
4729
+ 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);
4730
+ }
4731
+ }
4732
+ return code;
4733
+ }
4351
4734
  function generateUpMigration(diff) {
4352
4735
  const lines = [];
4353
4736
  lines.push(` // UP MIGRATION`);
4354
4737
  lines.push(``);
4738
+ const collectionIdMap = /* @__PURE__ */ new Map();
4739
+ for (const collection of diff.collectionsToCreate) {
4740
+ if (collection.id) {
4741
+ collectionIdMap.set(collection.name, collection.id);
4742
+ }
4743
+ }
4355
4744
  if (diff.collectionsToCreate.length > 0) {
4356
4745
  lines.push(` // Create new collections`);
4357
4746
  for (let i = 0; i < diff.collectionsToCreate.length; i++) {
4358
4747
  const collection = diff.collectionsToCreate[i];
4359
4748
  const varName = `collection_${collection.name}_create`;
4360
- lines.push(generateCollectionCreation(collection, varName));
4749
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
4361
4750
  lines.push(``);
4362
4751
  }
4363
4752
  }
@@ -4369,7 +4758,7 @@ function generateUpMigration(diff) {
4369
4758
  lines.push(` // Add fields to ${collectionName}`);
4370
4759
  for (const field of modification.fieldsToAdd) {
4371
4760
  const varName = `collection_${collectionName}_add_${field.name}`;
4372
- lines.push(generateFieldAddition(collectionName, field, varName));
4761
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
4373
4762
  lines.push(``);
4374
4763
  }
4375
4764
  }
@@ -4460,12 +4849,23 @@ function generateDownMigration(diff) {
4460
4849
  const lines = [];
4461
4850
  lines.push(` // DOWN MIGRATION (ROLLBACK)`);
4462
4851
  lines.push(``);
4852
+ const collectionIdMap = /* @__PURE__ */ new Map();
4853
+ for (const collection of diff.collectionsToCreate) {
4854
+ if (collection.id) {
4855
+ collectionIdMap.set(collection.name, collection.id);
4856
+ }
4857
+ }
4858
+ for (const collection of diff.collectionsToDelete) {
4859
+ if (collection.id) {
4860
+ collectionIdMap.set(collection.name, collection.id);
4861
+ }
4862
+ }
4463
4863
  if (diff.collectionsToDelete.length > 0) {
4464
4864
  lines.push(` // Recreate deleted collections`);
4465
4865
  for (let i = 0; i < diff.collectionsToDelete.length; i++) {
4466
4866
  const collection = diff.collectionsToDelete[i];
4467
4867
  const varName = `collection_${collection.name}_recreate`;
4468
- lines.push(generateCollectionCreation(collection, varName));
4868
+ lines.push(generateCollectionCreation(collection, varName, false, collectionIdMap));
4469
4869
  lines.push(``);
4470
4870
  }
4471
4871
  }
@@ -4510,7 +4910,7 @@ function generateDownMigration(diff) {
4510
4910
  lines.push(` // Restore fields to ${collectionName}`);
4511
4911
  for (const field of modification.fieldsToRemove) {
4512
4912
  const varName = `collection_${collectionName}_restore_${field.name}`;
4513
- lines.push(generateFieldAddition(collectionName, field, varName));
4913
+ lines.push(generateFieldAddition(collectionName, field, varName, false, collectionIdMap));
4514
4914
  lines.push(``);
4515
4915
  }
4516
4916
  }
@@ -4579,12 +4979,33 @@ function generate(diff, config) {
4579
4979
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
4580
4980
  try {
4581
4981
  const migrationDir = resolveMigrationDir(normalizedConfig);
4582
- const upCode = generateUpMigration(diff);
4583
- const downCode = generateDownMigration(diff);
4584
- const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
4585
- const filename = generateMigrationFilename(diff, normalizedConfig);
4586
- const filePath = writeMigrationFile(migrationDir, filename, content);
4587
- return filePath;
4982
+ const hasChanges4 = diff.collectionsToCreate.length > 0 || diff.collectionsToModify.length > 0 || diff.collectionsToDelete.length > 0;
4983
+ if (!hasChanges4) {
4984
+ return [];
4985
+ }
4986
+ const collectionIdMap = /* @__PURE__ */ new Map();
4987
+ for (const collection of diff.collectionsToCreate) {
4988
+ if (collection.id) {
4989
+ collectionIdMap.set(collection.name, collection.id);
4990
+ }
4991
+ }
4992
+ for (const collection of diff.collectionsToDelete) {
4993
+ if (collection.id) {
4994
+ collectionIdMap.set(collection.name, collection.id);
4995
+ }
4996
+ }
4997
+ const baseTimestamp = generateTimestamp(normalizedConfig);
4998
+ const operations = splitDiffByCollection(diff, baseTimestamp);
4999
+ const filePaths = [];
5000
+ for (const operation of operations) {
5001
+ const upCode = generateOperationUpMigration(operation, collectionIdMap);
5002
+ const downCode = generateOperationDownMigration(operation, collectionIdMap);
5003
+ const content = createMigrationFileStructure(upCode, downCode, normalizedConfig);
5004
+ const filename = generateCollectionMigrationFilename(operation);
5005
+ const filePath = writeMigrationFile(migrationDir, filename, content);
5006
+ filePaths.push(filePath);
5007
+ }
5008
+ return filePaths;
4588
5009
  } catch (error) {
4589
5010
  if (error instanceof MigrationGenerationError || error instanceof FileSystemError) {
4590
5011
  throw error;
@@ -4602,7 +5023,8 @@ var MigrationGenerator = class {
4602
5023
  this.config = mergeConfig4(config);
4603
5024
  }
4604
5025
  /**
4605
- * Generates a migration file from a schema diff
5026
+ * Generates migration files from a schema diff
5027
+ * Returns array of file paths (one per collection operation)
4606
5028
  */
4607
5029
  generate(diff) {
4608
5030
  return generate(diff, this.config);
@@ -5206,15 +5628,25 @@ async function executeGenerate(options) {
5206
5628
  process.exit(1);
5207
5629
  }
5208
5630
  logSection("\u{1F4DD} Generating Migration");
5209
- const migrationPath = await withProgress(
5631
+ const migrationPaths = await withProgress(
5210
5632
  "Creating migration file...",
5211
5633
  () => Promise.resolve(generate(diff, migrationsDir))
5212
5634
  );
5213
- logSuccess(`Migration file created: ${path5__namespace.basename(migrationPath)}`);
5635
+ if (migrationPaths.length === 0) {
5636
+ logWarning("No migration files were generated (no changes detected).");
5637
+ return;
5638
+ }
5639
+ if (migrationPaths.length === 1) {
5640
+ logSuccess(`Migration file created: ${path5__namespace.basename(migrationPaths[0])}`);
5641
+ } else {
5642
+ logSuccess(`Created ${migrationPaths.length} migration files`);
5643
+ }
5214
5644
  logSection("\u2705 Next Steps");
5215
5645
  console.log();
5216
- console.log(" 1. Review the generated migration file:");
5217
- console.log(` ${migrationPath}`);
5646
+ console.log(" 1. Review the generated migration file(s):");
5647
+ migrationPaths.forEach((migrationPath) => {
5648
+ console.log(` ${migrationPath}`);
5649
+ });
5218
5650
  console.log();
5219
5651
  console.log(" 2. Apply the migration by running PocketBase:");
5220
5652
  console.log(" yarn pb");
@@ -5513,7 +5945,6 @@ exports.aggregateChanges = aggregateChanges;
5513
5945
  exports.baseImageFileSchema = baseImageFileSchema;
5514
5946
  exports.baseSchema = baseSchema;
5515
5947
  exports.baseSchemaWithTimestamps = baseSchemaWithTimestamps;
5516
- exports.boolField = boolField;
5517
5948
  exports.buildFieldDefinition = buildFieldDefinition;
5518
5949
  exports.buildSchemaDefinition = buildSchemaDefinition;
5519
5950
  exports.categorizeChangesBySeverity = categorizeChangesBySeverity;
@@ -5527,13 +5958,10 @@ exports.convertPocketBaseMigration = convertPocketBaseMigration;
5527
5958
  exports.convertZodSchemaToCollectionSchema = convertZodSchemaToCollectionSchema;
5528
5959
  exports.createMigrationFileStructure = createMigrationFileStructure;
5529
5960
  exports.createPermissions = createPermissions;
5530
- exports.dateField = dateField;
5531
5961
  exports.defineCollection = defineCollection;
5532
5962
  exports.detectDestructiveChanges = detectDestructiveChanges;
5533
5963
  exports.detectFieldChanges = detectFieldChanges;
5534
5964
  exports.discoverSchemaFiles = discoverSchemaFiles;
5535
- exports.editorField = editorField;
5536
- exports.emailField = emailField;
5537
5965
  exports.extractComprehensiveFieldOptions = extractComprehensiveFieldOptions;
5538
5966
  exports.extractFieldDefinitions = extractFieldDefinitions;
5539
5967
  exports.extractFieldMetadata = extractFieldMetadata;
@@ -5541,8 +5969,6 @@ exports.extractFieldOptions = extractFieldOptions;
5541
5969
  exports.extractIndexes = extractIndexes;
5542
5970
  exports.extractRelationMetadata = extractRelationMetadata;
5543
5971
  exports.extractSchemaDefinitions = extractSchemaDefinitions;
5544
- exports.fileField = fileField;
5545
- exports.filesField = filesField;
5546
5972
  exports.filterSystemCollections = filterSystemCollections;
5547
5973
  exports.findLatestSnapshot = findLatestSnapshot;
5548
5974
  exports.findNewCollections = findNewCollections;
@@ -5568,7 +5994,6 @@ exports.generateMigrationFilename = generateMigrationFilename;
5568
5994
  exports.generatePermissionUpdate = generatePermissionUpdate;
5569
5995
  exports.generateTimestamp = generateTimestamp;
5570
5996
  exports.generateUpMigration = generateUpMigration;
5571
- exports.geoPointField = geoPointField;
5572
5997
  exports.getArrayElementType = getArrayElementType;
5573
5998
  exports.getCollectionNameFromFile = getCollectionNameFromFile;
5574
5999
  exports.getDefaultValue = getDefaultValue;
@@ -5593,7 +6018,6 @@ exports.isRelationField = isRelationField;
5593
6018
  exports.isSingleRelationField = isSingleRelationField;
5594
6019
  exports.isSystemCollection = isSystemCollection;
5595
6020
  exports.isTemplateConfig = isTemplateConfig;
5596
- exports.jsonField = jsonField;
5597
6021
  exports.loadBaseMigration = loadBaseMigration;
5598
6022
  exports.loadConfig = loadConfig;
5599
6023
  exports.loadSnapshot = loadSnapshot;
@@ -5616,7 +6040,6 @@ exports.matchCollectionsByName = matchCollectionsByName;
5616
6040
  exports.matchFieldsByName = matchFieldsByName;
5617
6041
  exports.mergePermissions = mergePermissions;
5618
6042
  exports.mergeSnapshots = mergeSnapshots;
5619
- exports.numberField = numberField;
5620
6043
  exports.omitImageFilesSchema = omitImageFilesSchema;
5621
6044
  exports.parseSchemaFiles = parseSchemaFiles;
5622
6045
  exports.pluralize = pluralize;
@@ -5624,14 +6047,11 @@ exports.requiresForceFlag = requiresForceFlag;
5624
6047
  exports.resolveTargetCollection = resolveTargetCollection;
5625
6048
  exports.resolveTemplate = resolveTemplate;
5626
6049
  exports.saveSnapshot = saveSnapshot;
5627
- exports.selectField = selectField;
5628
6050
  exports.selectSchemaForCollection = selectSchemaForCollection;
5629
6051
  exports.singularize = singularize;
5630
6052
  exports.snapshotExists = snapshotExists;
5631
- exports.textField = textField;
5632
6053
  exports.toCollectionName = toCollectionName;
5633
6054
  exports.unwrapZodType = unwrapZodType;
5634
- exports.urlField = urlField;
5635
6055
  exports.validatePermissionConfig = validatePermissionConfig;
5636
6056
  exports.validateRuleExpression = validateRuleExpression;
5637
6057
  exports.validateSnapshot = validateSnapshot;