pocketbase-zod-schema 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli/index.cjs +175 -50
  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 +175 -50
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/migrate.cjs +175 -50
  9. package/dist/cli/migrate.cjs.map +1 -1
  10. package/dist/cli/migrate.js +175 -50
  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-UcOPu1OQ.d.cts → fields-DBBm06VU.d.cts} +35 -7
  15. package/dist/{fields-UcOPu1OQ.d.ts → fields-DBBm06VU.d.ts} +35 -7
  16. package/dist/index.cjs +264 -84
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +3 -3
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +264 -84
  21. package/dist/index.js.map +1 -1
  22. package/dist/migration/analyzer.cjs.map +1 -1
  23. package/dist/migration/analyzer.d.cts +2 -2
  24. package/dist/migration/analyzer.d.ts +2 -2
  25. package/dist/migration/analyzer.js.map +1 -1
  26. package/dist/migration/diff.cjs +77 -26
  27. package/dist/migration/diff.cjs.map +1 -1
  28. package/dist/migration/diff.d.cts +4 -4
  29. package/dist/migration/diff.d.ts +4 -4
  30. package/dist/migration/diff.js +77 -26
  31. package/dist/migration/diff.js.map +1 -1
  32. package/dist/migration/generator.cjs +81 -34
  33. package/dist/migration/generator.cjs.map +1 -1
  34. package/dist/migration/generator.d.cts +2 -2
  35. package/dist/migration/generator.d.ts +2 -2
  36. package/dist/migration/generator.js +81 -34
  37. package/dist/migration/generator.js.map +1 -1
  38. package/dist/migration/index.cjs +209 -78
  39. package/dist/migration/index.cjs.map +1 -1
  40. package/dist/migration/index.d.cts +3 -3
  41. package/dist/migration/index.d.ts +3 -3
  42. package/dist/migration/index.js +209 -78
  43. package/dist/migration/index.js.map +1 -1
  44. package/dist/migration/snapshot.cjs +51 -18
  45. package/dist/migration/snapshot.cjs.map +1 -1
  46. package/dist/migration/snapshot.d.cts +2 -2
  47. package/dist/migration/snapshot.d.ts +2 -2
  48. package/dist/migration/snapshot.js +51 -18
  49. package/dist/migration/snapshot.js.map +1 -1
  50. package/dist/migration/utils/index.cjs +5 -3
  51. package/dist/migration/utils/index.cjs.map +1 -1
  52. package/dist/migration/utils/index.d.cts +4 -4
  53. package/dist/migration/utils/index.d.ts +4 -4
  54. package/dist/migration/utils/index.js +5 -3
  55. package/dist/migration/utils/index.js.map +1 -1
  56. package/dist/schema.cjs +55 -6
  57. package/dist/schema.cjs.map +1 -1
  58. package/dist/schema.d.cts +1 -1
  59. package/dist/schema.d.ts +1 -1
  60. package/dist/schema.js +55 -6
  61. package/dist/schema.js.map +1 -1
  62. package/dist/{type-mapper-DrQmtznD.d.cts → type-mapper-DsGgZwUo.d.cts} +1 -1
  63. package/dist/{type-mapper-n231Fspm.d.ts → type-mapper-Dvh4QTM-.d.ts} +1 -1
  64. package/dist/{types-YoBjsa-A.d.cts → types-CVxPCgWX.d.cts} +1 -1
  65. package/dist/{types-Ds3NQvny.d.ts → types-Dfp-NP2D.d.ts} +1 -1
  66. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -488,6 +488,45 @@ function extractFieldMetadata(description) {
488
488
  }
489
489
  return null;
490
490
  }
491
+ var MAX_FILE_SIZE_BYTES = 8 * 1024 * 1024 * 1024;
492
+ function parseByteSizeToBytes(value, context) {
493
+ let bytes;
494
+ if (typeof value === "number") {
495
+ if (!Number.isFinite(value)) {
496
+ throw new Error(`${context}: maxSize must be a finite number of bytes`);
497
+ }
498
+ bytes = Math.round(value);
499
+ } else {
500
+ const trimmed = value.trim();
501
+ const match = /^(\d+(?:\.\d+)?)\s*([KMG])$/i.exec(trimmed);
502
+ if (!match) {
503
+ throw new Error(`${context}: maxSize string must be like "10K", "5M", or "1G" (case-insensitive)`);
504
+ }
505
+ const amount = Number(match[1]);
506
+ const unit = match[2].toUpperCase();
507
+ if (!Number.isFinite(amount)) {
508
+ throw new Error(`${context}: maxSize must be a valid number`);
509
+ }
510
+ const multiplier = unit === "K" ? 1024 : unit === "M" ? 1024 * 1024 : 1024 * 1024 * 1024;
511
+ bytes = Math.round(amount * multiplier);
512
+ }
513
+ if (bytes < 0) {
514
+ throw new Error(`${context}: maxSize must be >= 0`);
515
+ }
516
+ if (bytes > MAX_FILE_SIZE_BYTES) {
517
+ throw new Error(`${context}: maxSize cannot exceed 8G (${MAX_FILE_SIZE_BYTES} bytes)`);
518
+ }
519
+ return bytes;
520
+ }
521
+ function normalizeFileFieldOptions(options, context) {
522
+ if (!options) return options;
523
+ if (options.maxSize === void 0) return options;
524
+ return {
525
+ ...options,
526
+ // PocketBase expects bytes; normalize any human-friendly inputs to bytes here.
527
+ maxSize: parseByteSizeToBytes(options.maxSize, context)
528
+ };
529
+ }
491
530
  function BoolField() {
492
531
  const metadata = {
493
532
  [FIELD_METADATA_KEY]: {
@@ -610,11 +649,14 @@ function SelectField(values, options) {
610
649
  return enumSchema.describe(JSON.stringify(metadata));
611
650
  }
612
651
  function FileField(options) {
613
- const schema = zod.z.instanceof(File);
652
+ const schema = zod.z.preprocess((val) => {
653
+ return val instanceof File ? val.name || "" : val;
654
+ }, zod.z.string());
655
+ const normalizedOptions = normalizeFileFieldOptions(options, "FileField");
614
656
  const metadata = {
615
657
  [FIELD_METADATA_KEY]: {
616
658
  type: "file",
617
- options: options || {}
659
+ options: normalizedOptions || {}
618
660
  }
619
661
  };
620
662
  return schema.describe(JSON.stringify(metadata));
@@ -625,17 +667,24 @@ function FilesField(options) {
625
667
  throw new Error("FilesField: minSelect cannot be greater than maxSelect");
626
668
  }
627
669
  }
628
- let schema = zod.z.array(zod.z.instanceof(File));
670
+ let baseArraySchema = zod.z.array(zod.z.string());
629
671
  if (options?.minSelect !== void 0) {
630
- schema = schema.min(options.minSelect);
672
+ baseArraySchema = baseArraySchema.min(options.minSelect);
631
673
  }
632
674
  if (options?.maxSelect !== void 0) {
633
- schema = schema.max(options.maxSelect);
675
+ baseArraySchema = baseArraySchema.max(options.maxSelect);
634
676
  }
677
+ const schema = zod.z.preprocess((val) => {
678
+ if (Array.isArray(val)) {
679
+ return val.map((item) => item instanceof File ? item.name || "" : item);
680
+ }
681
+ return val;
682
+ }, baseArraySchema);
683
+ const normalizedOptions = normalizeFileFieldOptions(options, "FilesField");
635
684
  const metadata = {
636
685
  [FIELD_METADATA_KEY]: {
637
686
  type: "file",
638
- options: options || {}
687
+ options: normalizedOptions || {}
639
688
  }
640
689
  };
641
690
  return schema.describe(JSON.stringify(metadata));
@@ -2497,7 +2546,7 @@ var SchemaAnalyzer = class {
2497
2546
  var SNAPSHOT_VERSION = "1.0.0";
2498
2547
  function resolveCollectionIdToName(collectionId) {
2499
2548
  if (collectionId === "_pb_users_auth_") {
2500
- return "Users";
2549
+ return "users";
2501
2550
  }
2502
2551
  const nameMatch = collectionId.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
2503
2552
  if (nameMatch) {
@@ -2505,6 +2554,39 @@ function resolveCollectionIdToName(collectionId) {
2505
2554
  }
2506
2555
  return collectionId;
2507
2556
  }
2557
+ function extractFieldOptions2(pbField) {
2558
+ const options = {};
2559
+ if (pbField.options && typeof pbField.options === "object") {
2560
+ Object.assign(options, pbField.options);
2561
+ }
2562
+ const directOptionKeys = [
2563
+ "min",
2564
+ "max",
2565
+ "pattern",
2566
+ "noDecimal",
2567
+ // text/number fields
2568
+ "values",
2569
+ "maxSelect",
2570
+ // select fields
2571
+ "mimeTypes",
2572
+ "maxSize",
2573
+ "thumbs",
2574
+ "protected",
2575
+ // file fields
2576
+ "onCreate",
2577
+ "onUpdate",
2578
+ // autodate fields
2579
+ "exceptDomains",
2580
+ "onlyDomains"
2581
+ // email/url fields
2582
+ ];
2583
+ for (const key of directOptionKeys) {
2584
+ if (pbField[key] !== void 0) {
2585
+ options[key] = pbField[key];
2586
+ }
2587
+ }
2588
+ return options;
2589
+ }
2508
2590
  function convertPocketBaseCollection(pbCollection) {
2509
2591
  const fields = [];
2510
2592
  const systemFieldNames = ["id", "created", "updated", "collectionId", "collectionName", "expand"];
@@ -2522,23 +2604,19 @@ function convertPocketBaseCollection(pbCollection) {
2522
2604
  type: pbField.type,
2523
2605
  required: pbField.required || false
2524
2606
  };
2525
- field.options = pbField.options ? { ...pbField.options } : {};
2526
- if (pbField.type === "select") {
2527
- if (pbField.values && Array.isArray(pbField.values)) {
2528
- field.options.values = pbField.values;
2529
- } else if (pbField.options?.values && Array.isArray(pbField.options.values)) {
2530
- field.options.values = pbField.options.values;
2531
- }
2532
- }
2607
+ field.options = extractFieldOptions2(pbField);
2533
2608
  if (pbField.type === "relation") {
2534
2609
  const collectionId = pbField.collectionId || pbField.options?.collectionId || "";
2535
- const collectionName = resolveCollectionIdToName(collectionId);
2610
+ const collectionName = resolveCollectionIdToName(collectionId || "");
2536
2611
  field.relation = {
2537
2612
  collection: collectionName,
2538
2613
  cascadeDelete: pbField.cascadeDelete ?? pbField.options?.cascadeDelete ?? false,
2539
2614
  maxSelect: pbField.maxSelect ?? pbField.options?.maxSelect,
2540
2615
  minSelect: pbField.minSelect ?? pbField.options?.minSelect
2541
2616
  };
2617
+ delete field.options.maxSelect;
2618
+ delete field.options.minSelect;
2619
+ delete field.options.cascadeDelete;
2542
2620
  }
2543
2621
  const hasOnlyValues = Object.keys(field.options).length === 1 && field.options.values !== void 0;
2544
2622
  if (Object.keys(field.options).length === 0) {
@@ -2552,17 +2630,21 @@ function convertPocketBaseCollection(pbCollection) {
2552
2630
  type: pbCollection.type || "base",
2553
2631
  fields
2554
2632
  };
2633
+ if (pbCollection.id) {
2634
+ schema.id = pbCollection.id;
2635
+ }
2555
2636
  if (pbCollection.indexes && Array.isArray(pbCollection.indexes)) {
2556
2637
  schema.indexes = pbCollection.indexes;
2557
2638
  }
2558
- const rules = {};
2559
- if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2560
- if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2561
- if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2562
- if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2563
- if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2564
- if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2565
- if (Object.keys(rules).length > 0) {
2639
+ 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;
2640
+ if (hasAnyRule) {
2641
+ const rules = {};
2642
+ if (pbCollection.listRule !== void 0) rules.listRule = pbCollection.listRule;
2643
+ if (pbCollection.viewRule !== void 0) rules.viewRule = pbCollection.viewRule;
2644
+ if (pbCollection.createRule !== void 0) rules.createRule = pbCollection.createRule;
2645
+ if (pbCollection.updateRule !== void 0) rules.updateRule = pbCollection.updateRule;
2646
+ if (pbCollection.deleteRule !== void 0) rules.deleteRule = pbCollection.deleteRule;
2647
+ if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2566
2648
  schema.rules = rules;
2567
2649
  schema.permissions = { ...rules };
2568
2650
  }
@@ -3312,17 +3394,19 @@ var CollectionIdRegistry = class {
3312
3394
  }
3313
3395
  /**
3314
3396
  * Generates a unique collection ID for a given collection name
3315
- * Special case: Returns constant "_pb_users_auth_" for users collection
3316
3397
  * Retries up to 10 times if collision occurs (extremely rare)
3398
+ * Special case: returns "_pb_users_auth_" for users collection
3317
3399
  *
3318
- * @param collectionName - The name of the collection
3400
+ * @param collectionName - The name of the collection (optional)
3319
3401
  * @returns A unique collection ID
3320
3402
  * @throws Error if unable to generate unique ID after max attempts
3321
3403
  */
3322
3404
  generate(collectionName) {
3323
3405
  if (collectionName && collectionName.toLowerCase() === "users") {
3324
3406
  const usersId = "_pb_users_auth_";
3325
- this.register(usersId);
3407
+ if (!this.has(usersId)) {
3408
+ this.register(usersId);
3409
+ }
3326
3410
  return usersId;
3327
3411
  }
3328
3412
  const maxAttempts = 10;
@@ -3506,18 +3590,49 @@ function compareFieldConstraints(currentField, previousField) {
3506
3590
  }
3507
3591
  return changes;
3508
3592
  }
3593
+ function normalizeOptionValue(key, value, fieldType) {
3594
+ if (key === "maxSelect" && value === 1 && (fieldType === "select" || fieldType === "file")) {
3595
+ return void 0;
3596
+ }
3597
+ if (key === "maxSize" && value === 0 && fieldType === "file") {
3598
+ return void 0;
3599
+ }
3600
+ if (fieldType === "file") {
3601
+ if (key === "mimeTypes" && Array.isArray(value) && value.length === 0) {
3602
+ return void 0;
3603
+ }
3604
+ if (key === "thumbs" && Array.isArray(value) && value.length === 0) {
3605
+ return void 0;
3606
+ }
3607
+ if (key === "protected" && value === false) {
3608
+ return void 0;
3609
+ }
3610
+ }
3611
+ if (fieldType === "autodate") {
3612
+ if (key === "onCreate" && value === true) {
3613
+ return void 0;
3614
+ }
3615
+ if (key === "onUpdate" && value === false) {
3616
+ return void 0;
3617
+ }
3618
+ }
3619
+ return value;
3620
+ }
3509
3621
  function compareFieldOptions(currentField, previousField) {
3510
3622
  const changes = [];
3511
3623
  const currentOptions = currentField.options || {};
3512
3624
  const previousOptions = previousField.options || {};
3513
3625
  const allKeys = /* @__PURE__ */ new Set([...Object.keys(currentOptions), ...Object.keys(previousOptions)]);
3626
+ const fieldType = currentField.type;
3514
3627
  for (const key of allKeys) {
3515
3628
  const currentValue = currentOptions[key];
3516
3629
  const previousValue = previousOptions[key];
3517
- if (currentValue === void 0 && previousValue === void 0) {
3630
+ const normalizedCurrent = normalizeOptionValue(key, currentValue, fieldType);
3631
+ const normalizedPrevious = normalizeOptionValue(key, previousValue, fieldType);
3632
+ if (normalizedCurrent === void 0 && normalizedPrevious === void 0) {
3518
3633
  continue;
3519
3634
  }
3520
- if (!areValuesEqual(currentValue, previousValue)) {
3635
+ if (!areValuesEqual(normalizedCurrent, normalizedPrevious)) {
3521
3636
  changes.push({
3522
3637
  property: `options.${key}`,
3523
3638
  oldValue: previousValue,
@@ -3527,7 +3642,7 @@ function compareFieldOptions(currentField, previousField) {
3527
3642
  }
3528
3643
  return changes;
3529
3644
  }
3530
- function compareRelationConfigurations(currentField, previousField) {
3645
+ function compareRelationConfigurations(currentField, previousField, collectionIdToName) {
3531
3646
  const changes = [];
3532
3647
  const currentRelation = currentField.relation;
3533
3648
  const previousRelation = previousField.relation;
@@ -3539,8 +3654,8 @@ function compareRelationConfigurations(currentField, previousField) {
3539
3654
  }
3540
3655
  const normalizeCollection = (collection) => {
3541
3656
  if (!collection) return collection;
3542
- if (collection === "_pb_users_auth_") {
3543
- return "Users";
3657
+ if (collectionIdToName && collectionIdToName.has(collection)) {
3658
+ return collectionIdToName.get(collection);
3544
3659
  }
3545
3660
  const nameMatch = collection.match(/app\.findCollectionByNameOrId\s*\(\s*["']([^"']+)["']\s*\)/);
3546
3661
  if (nameMatch) {
@@ -3550,13 +3665,11 @@ function compareRelationConfigurations(currentField, previousField) {
3550
3665
  };
3551
3666
  const normalizedCurrent = normalizeCollection(currentRelation.collection);
3552
3667
  const normalizedPrevious = normalizeCollection(previousRelation.collection);
3553
- if (normalizedCurrent !== normalizedPrevious) {
3668
+ if (normalizedCurrent.toLowerCase() !== normalizedPrevious.toLowerCase()) {
3554
3669
  changes.push({
3555
3670
  property: "relation.collection",
3556
- oldValue: normalizedPrevious,
3557
- // Use normalized value for clarity
3558
- newValue: normalizedCurrent
3559
- // Use normalized value for clarity
3671
+ oldValue: previousRelation.collection,
3672
+ newValue: currentRelation.collection
3560
3673
  });
3561
3674
  }
3562
3675
  if (currentRelation.cascadeDelete !== previousRelation.cascadeDelete) {
@@ -3566,14 +3679,20 @@ function compareRelationConfigurations(currentField, previousField) {
3566
3679
  newValue: currentRelation.cascadeDelete
3567
3680
  });
3568
3681
  }
3569
- if (currentRelation.maxSelect !== previousRelation.maxSelect) {
3682
+ const normalizeMax = (val) => val === 1 ? null : val;
3683
+ const currentMax = normalizeMax(currentRelation.maxSelect);
3684
+ const previousMax = normalizeMax(previousRelation.maxSelect);
3685
+ if (currentMax != previousMax) {
3570
3686
  changes.push({
3571
3687
  property: "relation.maxSelect",
3572
3688
  oldValue: previousRelation.maxSelect,
3573
3689
  newValue: currentRelation.maxSelect
3574
3690
  });
3575
3691
  }
3576
- if (currentRelation.minSelect !== previousRelation.minSelect) {
3692
+ const normalizeMin = (val) => val === 0 ? null : val;
3693
+ const currentMin = normalizeMin(currentRelation.minSelect);
3694
+ const previousMin = normalizeMin(previousRelation.minSelect);
3695
+ if (currentMin != previousMin) {
3577
3696
  changes.push({
3578
3697
  property: "relation.minSelect",
3579
3698
  oldValue: previousRelation.minSelect,
@@ -3582,7 +3701,7 @@ function compareRelationConfigurations(currentField, previousField) {
3582
3701
  }
3583
3702
  return changes;
3584
3703
  }
3585
- function detectFieldChanges(currentField, previousField) {
3704
+ function detectFieldChanges(currentField, previousField, collectionIdToName) {
3586
3705
  const changes = [];
3587
3706
  const typeChange = compareFieldTypes(currentField, previousField);
3588
3707
  if (typeChange) {
@@ -3591,7 +3710,7 @@ function detectFieldChanges(currentField, previousField) {
3591
3710
  changes.push(...compareFieldConstraints(currentField, previousField));
3592
3711
  changes.push(...compareFieldOptions(currentField, previousField));
3593
3712
  if (currentField.type === "relation" && previousField.type === "relation") {
3594
- changes.push(...compareRelationConfigurations(currentField, previousField));
3713
+ changes.push(...compareRelationConfigurations(currentField, previousField, collectionIdToName));
3595
3714
  }
3596
3715
  return changes;
3597
3716
  }
@@ -3602,7 +3721,7 @@ function compareIndexes(currentIndexes = [], previousIndexes = []) {
3602
3721
  const indexesToRemove = previousIndexes.filter((idx) => !currentSet.has(idx));
3603
3722
  return { indexesToAdd, indexesToRemove };
3604
3723
  }
3605
- function compareRules(currentRules, previousRules) {
3724
+ function compareRules(currentRules, previousRules, currentPermissions, previousPermissions) {
3606
3725
  const updates = [];
3607
3726
  const ruleTypes = [
3608
3727
  "listRule",
@@ -3613,8 +3732,8 @@ function compareRules(currentRules, previousRules) {
3613
3732
  "manageRule"
3614
3733
  ];
3615
3734
  for (const ruleType of ruleTypes) {
3616
- const currentValue = currentRules?.[ruleType] ?? null;
3617
- const previousValue = previousRules?.[ruleType] ?? null;
3735
+ const currentValue = currentRules?.[ruleType] ?? currentPermissions?.[ruleType] ?? null;
3736
+ const previousValue = previousRules?.[ruleType] ?? previousPermissions?.[ruleType] ?? null;
3618
3737
  if (currentValue !== previousValue) {
3619
3738
  updates.push({
3620
3739
  ruleType,
@@ -3641,7 +3760,7 @@ function comparePermissions(currentPermissions, previousPermissions) {
3641
3760
  }
3642
3761
  return changes;
3643
3762
  }
3644
- function compareCollectionFields(currentCollection, previousCollection, config) {
3763
+ function compareCollectionFields(currentCollection, previousCollection, config, collectionIdToName) {
3645
3764
  let fieldsToAdd = findNewFields(currentCollection.fields, previousCollection.fields);
3646
3765
  const fieldsToRemove = findRemovedFields(currentCollection.fields, previousCollection.fields);
3647
3766
  const fieldsToModify = [];
@@ -3651,7 +3770,7 @@ function compareCollectionFields(currentCollection, previousCollection, config)
3651
3770
  }
3652
3771
  const matchedFields = matchFieldsByName(currentCollection.fields, previousCollection.fields);
3653
3772
  for (const [currentField, previousField] of matchedFields) {
3654
- const changes = detectFieldChanges(currentField, previousField);
3773
+ const changes = detectFieldChanges(currentField, previousField, collectionIdToName);
3655
3774
  if (changes.length > 0) {
3656
3775
  fieldsToModify.push({
3657
3776
  fieldName: currentField.name,
@@ -3663,14 +3782,20 @@ function compareCollectionFields(currentCollection, previousCollection, config)
3663
3782
  }
3664
3783
  return { fieldsToAdd, fieldsToRemove, fieldsToModify };
3665
3784
  }
3666
- function buildCollectionModification(currentCollection, previousCollection, config) {
3785
+ function buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName) {
3667
3786
  const { fieldsToAdd, fieldsToRemove, fieldsToModify } = compareCollectionFields(
3668
3787
  currentCollection,
3669
3788
  previousCollection,
3670
- config
3789
+ config,
3790
+ collectionIdToName
3671
3791
  );
3672
3792
  const { indexesToAdd, indexesToRemove } = compareIndexes(currentCollection.indexes, previousCollection.indexes);
3673
- const rulesToUpdate = compareRules(currentCollection.rules, previousCollection.rules);
3793
+ const rulesToUpdate = compareRules(
3794
+ currentCollection.rules,
3795
+ previousCollection.rules,
3796
+ currentCollection.permissions,
3797
+ previousCollection.permissions
3798
+ );
3674
3799
  const permissionsToUpdate = comparePermissions(currentCollection.permissions, previousCollection.permissions);
3675
3800
  return {
3676
3801
  collection: currentCollection.name,
@@ -3687,6 +3812,14 @@ function hasChanges(modification) {
3687
3812
  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;
3688
3813
  }
3689
3814
  function aggregateChanges(currentSchema, previousSnapshot, config) {
3815
+ const collectionIdToName = /* @__PURE__ */ new Map();
3816
+ if (previousSnapshot) {
3817
+ for (const [name, collection] of previousSnapshot.collections) {
3818
+ if (collection.id) {
3819
+ collectionIdToName.set(collection.id, name);
3820
+ }
3821
+ }
3822
+ }
3690
3823
  const collectionsToCreate = findNewCollections(currentSchema, previousSnapshot);
3691
3824
  const collectionsToDelete = findRemovedCollections(currentSchema, previousSnapshot);
3692
3825
  const filteredCollectionsToCreate = collectionsToCreate.filter(
@@ -3710,7 +3843,7 @@ function aggregateChanges(currentSchema, previousSnapshot, config) {
3710
3843
  const collectionsToModify = [];
3711
3844
  const matchedCollections = matchCollectionsByName(currentSchema, previousSnapshot);
3712
3845
  for (const [currentCollection, previousCollection] of matchedCollections) {
3713
- const modification = buildCollectionModification(currentCollection, previousCollection, config);
3846
+ const modification = buildCollectionModification(currentCollection, previousCollection, config, collectionIdToName);
3714
3847
  if (hasChanges(modification)) {
3715
3848
  collectionsToModify.push(modification);
3716
3849
  }
@@ -4111,7 +4244,7 @@ function formatValue(value) {
4111
4244
  return JSON.stringify(value).replace(/","/g, '", "');
4112
4245
  }
4113
4246
  if (typeof value === "object") {
4114
- const entries = Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
4247
+ const entries = Object.entries(value).filter(([_k, v]) => v !== void 0).map(([k, v]) => `${k}: ${formatValue(v)}`).join(", ");
4115
4248
  return `{ ${entries} }`;
4116
4249
  }
4117
4250
  return String(value);
@@ -4280,6 +4413,9 @@ function getSystemFields() {
4280
4413
  function generateCollectionCreation(collection, varName = "collection", isLast = false, collectionIdMap) {
4281
4414
  const lines = [];
4282
4415
  lines.push(` const ${varName} = new Collection({`);
4416
+ if (collection.id) {
4417
+ lines.push(` id: ${formatValue(collection.id)},`);
4418
+ }
4283
4419
  lines.push(` name: "${collection.name}",`);
4284
4420
  lines.push(` type: "${collection.type}",`);
4285
4421
  const permissionsCode = generateCollectionPermissions(collection.permissions);
@@ -4402,11 +4538,9 @@ function generateFieldModification(collectionName, modification, varName, isLast
4402
4538
  function generateFieldDeletion(collectionName, fieldName, varName, isLast = false) {
4403
4539
  const lines = [];
4404
4540
  const collectionVar = varName || `collection_${collectionName}_${fieldName}`;
4405
- const fieldVar = `${collectionVar}_field`;
4406
4541
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
4407
- lines.push(` const ${fieldVar} = ${collectionVar}.fields.getByName("${fieldName}");`);
4408
4542
  lines.push(``);
4409
- lines.push(` ${collectionVar}.fields.remove(${fieldVar}.id);`);
4543
+ lines.push(` ${collectionVar}.fields.removeByName("${fieldName}");`);
4410
4544
  lines.push(``);
4411
4545
  lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
4412
4546
  return lines.join("\n");
@@ -4523,7 +4657,27 @@ function generateOperationUpMigration(operation, collectionIdMap) {
4523
4657
  const varName = `collection_${collectionName}`;
4524
4658
  lines.push(generateCollectionDeletion(collectionName, varName, true));
4525
4659
  }
4526
- return lines.join("\n");
4660
+ let code = lines.join("\n");
4661
+ const hasReturnStatement = /return\s+app\.(save|delete)\(/m.test(code);
4662
+ if (!hasReturnStatement) {
4663
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4664
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4665
+ const saveMatches = [...code.matchAll(savePattern)];
4666
+ const deleteMatches = [...code.matchAll(deletePattern)];
4667
+ const allMatches = [
4668
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4669
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4670
+ ].sort((a, b) => b.index - a.index);
4671
+ if (allMatches.length > 0) {
4672
+ const lastMatch = allMatches[0];
4673
+ if (lastMatch.type === "save") {
4674
+ 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);
4675
+ } else {
4676
+ 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);
4677
+ }
4678
+ }
4679
+ }
4680
+ return code;
4527
4681
  }
4528
4682
  function generateOperationDownMigration(operation, collectionIdMap) {
4529
4683
  const lines = [];
@@ -4608,7 +4762,27 @@ function generateOperationDownMigration(operation, collectionIdMap) {
4608
4762
  lines.push(generateCollectionCreation(collection, varName, true, collectionIdMap));
4609
4763
  }
4610
4764
  }
4611
- return lines.join("\n");
4765
+ let code = lines.join("\n");
4766
+ const hasReturnStatement = /return\s+app\.(save|delete)\(/m.test(code);
4767
+ if (!hasReturnStatement) {
4768
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4769
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4770
+ const saveMatches = [...code.matchAll(savePattern)];
4771
+ const deleteMatches = [...code.matchAll(deletePattern)];
4772
+ const allMatches = [
4773
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4774
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4775
+ ].sort((a, b) => b.index - a.index);
4776
+ if (allMatches.length > 0) {
4777
+ const lastMatch = allMatches[0];
4778
+ if (lastMatch.type === "save") {
4779
+ 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);
4780
+ } else {
4781
+ 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);
4782
+ }
4783
+ }
4784
+ }
4785
+ return code;
4612
4786
  }
4613
4787
  function generateUpMigration(diff) {
4614
4788
  const lines = [];
@@ -4706,20 +4880,23 @@ function generateUpMigration(diff) {
4706
4880
  lines.push(``);
4707
4881
  }
4708
4882
  let code = lines.join("\n");
4709
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4710
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4711
- const saveMatches = [...code.matchAll(savePattern)];
4712
- const deleteMatches = [...code.matchAll(deletePattern)];
4713
- const allMatches = [
4714
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4715
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4716
- ].sort((a, b) => b.index - a.index);
4717
- if (allMatches.length > 0) {
4718
- const lastMatch = allMatches[0];
4719
- if (lastMatch.type === "save") {
4720
- 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);
4721
- } else {
4722
- 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);
4883
+ const hasReturnStatement = /return\s+app\.(save|delete)\(/m.test(code);
4884
+ if (!hasReturnStatement) {
4885
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4886
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4887
+ const saveMatches = [...code.matchAll(savePattern)];
4888
+ const deleteMatches = [...code.matchAll(deletePattern)];
4889
+ const allMatches = [
4890
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4891
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4892
+ ].sort((a, b) => b.index - a.index);
4893
+ if (allMatches.length > 0) {
4894
+ const lastMatch = allMatches[0];
4895
+ if (lastMatch.type === "save") {
4896
+ 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);
4897
+ } else {
4898
+ 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);
4899
+ }
4723
4900
  }
4724
4901
  }
4725
4902
  return code;
@@ -4836,20 +5013,23 @@ function generateDownMigration(diff) {
4836
5013
  lines.push(``);
4837
5014
  }
4838
5015
  let code = lines.join("\n");
4839
- const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
4840
- const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
4841
- const saveMatches = [...code.matchAll(savePattern)];
4842
- const deleteMatches = [...code.matchAll(deletePattern)];
4843
- const allMatches = [
4844
- ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
4845
- ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
4846
- ].sort((a, b) => b.index - a.index);
4847
- if (allMatches.length > 0) {
4848
- const lastMatch = allMatches[0];
4849
- if (lastMatch.type === "save") {
4850
- 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);
4851
- } else {
4852
- 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);
5016
+ const hasReturnStatement = /return\s+app\.(save|delete)\(/m.test(code);
5017
+ if (!hasReturnStatement) {
5018
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
5019
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
5020
+ const saveMatches = [...code.matchAll(savePattern)];
5021
+ const deleteMatches = [...code.matchAll(deletePattern)];
5022
+ const allMatches = [
5023
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
5024
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
5025
+ ].sort((a, b) => b.index - a.index);
5026
+ if (allMatches.length > 0) {
5027
+ const lastMatch = allMatches[0];
5028
+ if (lastMatch.type === "save") {
5029
+ 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);
5030
+ } else {
5031
+ 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);
5032
+ }
4853
5033
  }
4854
5034
  }
4855
5035
  return code;