pocketbase-zod-schema 0.1.2 → 0.1.3

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 (63) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +329 -99
  3. package/dist/cli/index.cjs +176 -55
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.js +176 -55
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/migrate.cjs +196 -58
  8. package/dist/cli/migrate.cjs.map +1 -1
  9. package/dist/cli/migrate.js +194 -57
  10. package/dist/cli/migrate.js.map +1 -1
  11. package/dist/cli/utils/index.cjs +1 -1
  12. package/dist/cli/utils/index.cjs.map +1 -1
  13. package/dist/cli/utils/index.js +1 -1
  14. package/dist/cli/utils/index.js.map +1 -1
  15. package/dist/index.cjs +197 -96
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +3 -3
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +197 -95
  20. package/dist/index.js.map +1 -1
  21. package/dist/migration/analyzer.cjs +101 -28
  22. package/dist/migration/analyzer.cjs.map +1 -1
  23. package/dist/migration/analyzer.js +101 -28
  24. package/dist/migration/analyzer.js.map +1 -1
  25. package/dist/migration/generator.cjs +60 -25
  26. package/dist/migration/generator.cjs.map +1 -1
  27. package/dist/migration/generator.d.cts +9 -5
  28. package/dist/migration/generator.d.ts +9 -5
  29. package/dist/migration/generator.js +60 -25
  30. package/dist/migration/generator.js.map +1 -1
  31. package/dist/migration/index.cjs +162 -53
  32. package/dist/migration/index.cjs.map +1 -1
  33. package/dist/migration/index.js +162 -53
  34. package/dist/migration/index.js.map +1 -1
  35. package/dist/migration/snapshot.cjs +1 -0
  36. package/dist/migration/snapshot.cjs.map +1 -1
  37. package/dist/migration/snapshot.js +1 -0
  38. package/dist/migration/snapshot.js.map +1 -1
  39. package/dist/migration/utils/index.cjs +19 -17
  40. package/dist/migration/utils/index.cjs.map +1 -1
  41. package/dist/migration/utils/index.d.cts +3 -1
  42. package/dist/migration/utils/index.d.ts +3 -1
  43. package/dist/migration/utils/index.js +19 -17
  44. package/dist/migration/utils/index.js.map +1 -1
  45. package/dist/mutator.cjs +9 -11
  46. package/dist/mutator.cjs.map +1 -1
  47. package/dist/mutator.d.cts +5 -9
  48. package/dist/mutator.d.ts +5 -9
  49. package/dist/mutator.js +9 -11
  50. package/dist/mutator.js.map +1 -1
  51. package/dist/schema.cjs +50 -53
  52. package/dist/schema.cjs.map +1 -1
  53. package/dist/schema.d.cts +94 -12
  54. package/dist/schema.d.ts +94 -12
  55. package/dist/schema.js +50 -52
  56. package/dist/schema.js.map +1 -1
  57. package/dist/types.d.cts +2 -5
  58. package/dist/types.d.ts +2 -5
  59. package/dist/user-C39DQ40N.d.cts +53 -0
  60. package/dist/user-C39DQ40N.d.ts +53 -0
  61. package/package.json +2 -3
  62. package/dist/user-jS1aYoeD.d.cts +0 -123
  63. package/dist/user-jS1aYoeD.d.ts +0 -123
package/dist/index.cjs CHANGED
@@ -104,14 +104,48 @@ function filesField(options) {
104
104
  if (options?.max !== void 0) schema = schema.max(options.max);
105
105
  return schema;
106
106
  }
107
- function relationField() {
108
- return zod.z.string();
107
+ var RELATION_METADATA_KEY = "__pocketbase_relation__";
108
+ function relationField(config) {
109
+ const metadata = {
110
+ [RELATION_METADATA_KEY]: {
111
+ type: "single",
112
+ collection: config.collection,
113
+ cascadeDelete: config.cascadeDelete ?? false,
114
+ maxSelect: 1,
115
+ minSelect: 0
116
+ }
117
+ };
118
+ return zod.z.string().describe(JSON.stringify(metadata));
109
119
  }
110
- function relationsField(options) {
120
+ function relationsField(config) {
121
+ const metadata = {
122
+ [RELATION_METADATA_KEY]: {
123
+ type: "multiple",
124
+ collection: config.collection,
125
+ cascadeDelete: config.cascadeDelete ?? false,
126
+ maxSelect: config.maxSelect ?? 999,
127
+ minSelect: config.minSelect ?? 0
128
+ }
129
+ };
111
130
  let schema = zod.z.array(zod.z.string());
112
- if (options?.min !== void 0) schema = schema.min(options.min);
113
- if (options?.max !== void 0) schema = schema.max(options.max);
114
- return schema;
131
+ if (config.minSelect !== void 0) {
132
+ schema = schema.min(config.minSelect);
133
+ }
134
+ if (config.maxSelect !== void 0) {
135
+ schema = schema.max(config.maxSelect);
136
+ }
137
+ return schema.describe(JSON.stringify(metadata));
138
+ }
139
+ function extractRelationMetadata(description) {
140
+ if (!description) return null;
141
+ try {
142
+ const parsed = JSON.parse(description);
143
+ if (parsed[RELATION_METADATA_KEY]) {
144
+ return parsed[RELATION_METADATA_KEY];
145
+ }
146
+ } catch {
147
+ }
148
+ return null;
115
149
  }
116
150
  function editorField() {
117
151
  return zod.z.string();
@@ -143,7 +177,7 @@ function withIndexes(schema, indexes) {
143
177
  return schema.describe(JSON.stringify(metadata));
144
178
  }
145
179
 
146
- // src/schema/permission-templates.ts
180
+ // src/utils/permission-templates.ts
147
181
  var PermissionTemplates = {
148
182
  /**
149
183
  * Public access - anyone can perform all operations
@@ -362,44 +396,22 @@ function mergePermissions(...schemas) {
362
396
  }
363
397
  return merged;
364
398
  }
365
- var ProjectInputSchema = zod.z.object({
366
- // Required fields
367
- title: zod.z.string(),
368
- content: zod.z.string(),
369
- status: StatusEnum,
370
- summary: zod.z.string().optional(),
371
- User: zod.z.string().nonempty("User ID is missing"),
372
- SubscriberUsers: zod.z.array(zod.z.string())
373
- }).extend(inputImageFileSchema);
374
- var ProjectSchema = withPermissions(
375
- ProjectInputSchema.omit(omitImageFilesSchema).extend(baseImageFileSchema),
376
- {
377
- template: "owner-only",
378
- ownerField: "User",
379
- customRules: {
380
- // Override list rule to allow authenticated users to see all projects
381
- listRule: '@request.auth.id != ""',
382
- // Allow viewing if user is owner OR a subscriber
383
- viewRule: '@request.auth.id != "" && (User = @request.auth.id || SubscriberUsers ?= @request.auth.id)'
384
- }
385
- }
386
- );
387
399
  var UserInputSchema = zod.z.object({
388
- name: zod.z.string().min(2, "Name must be longer").optional(),
400
+ name: zod.z.string().optional(),
389
401
  email: zod.z.string().email(),
390
402
  password: zod.z.string().min(8, "Password must be at least 8 characters"),
391
403
  passwordConfirm: zod.z.string(),
392
404
  avatar: zod.z.instanceof(File).optional()
393
405
  });
394
406
  var UserDatabaseSchema = zod.z.object({
395
- name: zod.z.string().min(2, "Name must be longer").optional(),
407
+ name: zod.z.string().optional(),
396
408
  email: zod.z.string().email(),
397
409
  password: zod.z.string().min(8, "Password must be at least 8 characters"),
398
410
  avatar: zod.z.instanceof(File).optional()
399
411
  });
400
412
  var UserSchema = withIndexes(
401
413
  withPermissions(UserDatabaseSchema.extend(baseSchema), {
402
- // Users can list other users (for mentions, user search, etc.)
414
+ // Users can list their own profile
403
415
  listRule: "id = @request.auth.id",
404
416
  // Users can view their own profile
405
417
  viewRule: "id = @request.auth.id",
@@ -408,15 +420,13 @@ var UserSchema = withIndexes(
408
420
  // Users can only update their own profile
409
421
  updateRule: "id = @request.auth.id",
410
422
  // Users can only delete their own account
411
- deleteRule: "id = @request.auth.id",
412
- // Users can only manage their own account (change email, password, etc.)
413
- manageRule: "id = @request.auth.id"
423
+ deleteRule: "id = @request.auth.id"
424
+ // manageRule is null in PocketBase default (not set)
414
425
  }),
415
426
  [
416
- // Email should be unique for authentication
417
- "CREATE UNIQUE INDEX idx_users_email ON users (email)",
418
- // Index on name for user search and sorting
419
- "CREATE INDEX idx_users_name ON users (name)"
427
+ // PocketBase's default indexes for auth collections
428
+ "CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
429
+ "CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''"
420
430
  ]
421
431
  );
422
432
  var BaseMutator = class {
@@ -757,7 +767,7 @@ var UserMutator = class extends BaseMutator {
757
767
  };
758
768
  }
759
769
  getCollection() {
760
- return this.pb.collection("Projects");
770
+ return this.pb.collection("Users");
761
771
  }
762
772
  async validateInput(input) {
763
773
  return UserInputSchema.parse(input);
@@ -1523,26 +1533,28 @@ function getMaxSelect(fieldName, zodType) {
1523
1533
  return 1;
1524
1534
  }
1525
1535
  function getMinSelect(fieldName, zodType) {
1526
- if (!isMultipleRelationField(fieldName, zodType)) {
1527
- return void 0;
1528
- }
1529
- let unwrappedType = zodType;
1530
- if (zodType instanceof zod.z.ZodOptional) {
1531
- unwrappedType = zodType._def.innerType;
1532
- }
1533
- if (unwrappedType instanceof zod.z.ZodNullable) {
1534
- unwrappedType = unwrappedType._def.innerType;
1535
- }
1536
- if (unwrappedType instanceof zod.z.ZodDefault) {
1537
- unwrappedType = unwrappedType._def.innerType;
1536
+ if (isSingleRelationField(fieldName, zodType)) {
1537
+ return 0;
1538
1538
  }
1539
- if (unwrappedType instanceof zod.z.ZodArray) {
1540
- const arrayDef = unwrappedType._def;
1541
- if (arrayDef.minLength) {
1542
- return arrayDef.minLength.value;
1539
+ if (isMultipleRelationField(fieldName, zodType)) {
1540
+ let unwrappedType = zodType;
1541
+ if (zodType instanceof zod.z.ZodOptional) {
1542
+ unwrappedType = zodType._def.innerType;
1543
+ }
1544
+ if (unwrappedType instanceof zod.z.ZodNullable) {
1545
+ unwrappedType = unwrappedType._def.innerType;
1546
+ }
1547
+ if (unwrappedType instanceof zod.z.ZodDefault) {
1548
+ unwrappedType = unwrappedType._def.innerType;
1549
+ }
1550
+ if (unwrappedType instanceof zod.z.ZodArray) {
1551
+ const arrayDef = unwrappedType._def;
1552
+ if (arrayDef.minLength) {
1553
+ return arrayDef.minLength.value;
1554
+ }
1543
1555
  }
1544
1556
  }
1545
- return void 0;
1557
+ return 0;
1546
1558
  }
1547
1559
  var POCKETBASE_FIELD_TYPES = [
1548
1560
  "text",
@@ -2025,13 +2037,32 @@ async function importSchemaModule(filePath, config) {
2025
2037
  if (config?.pathTransformer) {
2026
2038
  importPath = config.pathTransformer(filePath);
2027
2039
  }
2028
- if (!importPath.endsWith(".js")) {
2029
- importPath = `${importPath}.js`;
2040
+ let resolvedPath = null;
2041
+ const jsPath = `${importPath}.js`;
2042
+ const tsPath = `${importPath}.ts`;
2043
+ if (fs2__namespace.existsSync(jsPath)) {
2044
+ resolvedPath = jsPath;
2045
+ } else if (fs2__namespace.existsSync(tsPath)) {
2046
+ resolvedPath = tsPath;
2047
+ } else {
2048
+ resolvedPath = jsPath;
2030
2049
  }
2031
- const fileUrl = new URL(`file://${path4__namespace.resolve(importPath)}`);
2050
+ const fileUrl = new URL(`file://${path4__namespace.resolve(resolvedPath)}`);
2032
2051
  const module = await import(fileUrl.href);
2033
2052
  return module;
2034
2053
  } catch (error) {
2054
+ const tsPath = `${filePath}.ts`;
2055
+ const isTypeScriptFile = fs2__namespace.existsSync(tsPath);
2056
+ if (isTypeScriptFile) {
2057
+ throw new SchemaParsingError(
2058
+ `Failed to import TypeScript schema file. Node.js cannot import TypeScript files directly.
2059
+ Please either:
2060
+ 1. Compile your schema files to JavaScript first, or
2061
+ 2. Use tsx to run the migration tool (e.g., "npx tsx package/dist/cli/migrate.js status" or "tsx package/dist/cli/migrate.js status")`,
2062
+ filePath,
2063
+ error
2064
+ );
2065
+ }
2035
2066
  throw new SchemaParsingError(
2036
2067
  `Failed to import schema module. Make sure the schema files are compiled to JavaScript.`,
2037
2068
  filePath,
@@ -2094,7 +2125,17 @@ function buildFieldDefinition(fieldName, zodType) {
2094
2125
  required,
2095
2126
  options
2096
2127
  };
2097
- if (isRelationField(fieldName, zodType)) {
2128
+ const relationMetadata = extractRelationMetadata(zodType.description);
2129
+ if (relationMetadata) {
2130
+ fieldDef.type = "relation";
2131
+ fieldDef.relation = {
2132
+ collection: relationMetadata.collection,
2133
+ maxSelect: relationMetadata.maxSelect,
2134
+ minSelect: relationMetadata.minSelect,
2135
+ cascadeDelete: relationMetadata.cascadeDelete
2136
+ };
2137
+ fieldDef.options = void 0;
2138
+ } else if (isRelationField(fieldName, zodType)) {
2098
2139
  fieldDef.type = "relation";
2099
2140
  const targetCollection = resolveTargetCollection(fieldName);
2100
2141
  const maxSelect = getMaxSelect(fieldName, zodType);
@@ -2106,6 +2147,13 @@ function buildFieldDefinition(fieldName, zodType) {
2106
2147
  cascadeDelete: false
2107
2148
  // Default to false, can be configured later
2108
2149
  };
2150
+ if (fieldDef.options) {
2151
+ const { min, max, pattern, ...relationSafeOptions } = fieldDef.options;
2152
+ console.log("min", min);
2153
+ console.log("max", max);
2154
+ console.log("pattern", pattern);
2155
+ fieldDef.options = Object.keys(relationSafeOptions).length > 0 ? relationSafeOptions : void 0;
2156
+ }
2109
2157
  }
2110
2158
  return fieldDef;
2111
2159
  }
@@ -2158,11 +2206,12 @@ function convertZodSchemaToCollectionSchema(collectionName, zodSchema) {
2158
2206
  fields,
2159
2207
  indexes,
2160
2208
  rules: {
2161
- listRule: null,
2162
- viewRule: null,
2163
- createRule: null,
2164
- updateRule: null,
2165
- deleteRule: null
2209
+ listRule: permissions?.listRule ?? null,
2210
+ viewRule: permissions?.viewRule ?? null,
2211
+ createRule: permissions?.createRule ?? null,
2212
+ updateRule: permissions?.updateRule ?? null,
2213
+ deleteRule: permissions?.deleteRule ?? null,
2214
+ manageRule: permissions?.manageRule ?? null
2166
2215
  },
2167
2216
  permissions
2168
2217
  };
@@ -2186,7 +2235,12 @@ async function buildSchemaDefinition(config) {
2186
2235
  if (normalizedConfig.pathTransformer) {
2187
2236
  importPath = normalizedConfig.pathTransformer(filePath);
2188
2237
  } else if (mergedConfig.useCompiledFiles) {
2189
- importPath = filePath.replace(/\/src\//, "/dist/");
2238
+ const distPath = filePath.replace(/\/src\//, "/dist/");
2239
+ if (fs2__namespace.existsSync(`${distPath}.js`) || fs2__namespace.existsSync(`${distPath}.mjs`)) {
2240
+ importPath = distPath;
2241
+ } else {
2242
+ importPath = filePath;
2243
+ }
2190
2244
  }
2191
2245
  const module = await importSchemaModule(importPath, normalizedConfig);
2192
2246
  const schemas = extractSchemaDefinitions(module, mergedConfig.schemaPatterns);
@@ -2563,6 +2617,7 @@ function convertPocketBaseCollection(pbCollection) {
2563
2617
  if (pbCollection.manageRule !== void 0) rules.manageRule = pbCollection.manageRule;
2564
2618
  if (Object.keys(rules).length > 0) {
2565
2619
  schema.rules = rules;
2620
+ schema.permissions = { ...rules };
2566
2621
  }
2567
2622
  return schema;
2568
2623
  }
@@ -3258,10 +3313,8 @@ var DiffEngine = class {
3258
3313
  var DEFAULT_TEMPLATE = `/// <reference path="{{TYPES_PATH}}" />
3259
3314
  migrate((app) => {
3260
3315
  {{UP_CODE}}
3261
- return true;
3262
3316
  }, (app) => {
3263
3317
  {{DOWN_CODE}}
3264
- return true;
3265
3318
  });
3266
3319
  `;
3267
3320
  var DEFAULT_CONFIG4 = {
@@ -3426,7 +3479,8 @@ function generateFieldDefinitionObject(field) {
3426
3479
  }
3427
3480
  }
3428
3481
  if (field.relation) {
3429
- const collectionIdPlaceholder = field.relation.collection === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3482
+ const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3483
+ const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3430
3484
  parts.push(` collectionId: ${collectionIdPlaceholder}`);
3431
3485
  if (field.relation.maxSelect !== void 0) {
3432
3486
  parts.push(` maxSelect: ${field.relation.maxSelect}`);
@@ -3510,7 +3564,7 @@ function generateIndexesArray(indexes) {
3510
3564
  ${indexStrings.join(",\n ")},
3511
3565
  ]`;
3512
3566
  }
3513
- function generateCollectionCreation(collection, varName = "collection") {
3567
+ function generateCollectionCreation(collection, varName = "collection", isLast = false) {
3514
3568
  const lines = [];
3515
3569
  lines.push(` const ${varName} = new Collection({`);
3516
3570
  lines.push(` name: "${collection.name}",`);
@@ -3526,7 +3580,7 @@ function generateCollectionCreation(collection, varName = "collection") {
3526
3580
  lines.push(` indexes: ${generateIndexesArray(collection.indexes)},`);
3527
3581
  lines.push(` });`);
3528
3582
  lines.push(``);
3529
- lines.push(` app.save(${varName});`);
3583
+ lines.push(isLast ? ` return app.save(${varName});` : ` app.save(${varName});`);
3530
3584
  return lines.join("\n");
3531
3585
  }
3532
3586
  function getFieldConstructorName(fieldType) {
@@ -3557,7 +3611,8 @@ function generateFieldConstructorOptions(field) {
3557
3611
  }
3558
3612
  }
3559
3613
  if (field.relation && field.type === "relation") {
3560
- const collectionIdPlaceholder = field.relation.collection === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3614
+ const isUsersCollection = field.relation.collection.toLowerCase() === "users";
3615
+ const collectionIdPlaceholder = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${field.relation.collection}").id`;
3561
3616
  parts.push(` collectionId: ${collectionIdPlaceholder}`);
3562
3617
  if (field.relation.maxSelect !== void 0) {
3563
3618
  parts.push(` maxSelect: ${field.relation.maxSelect}`);
@@ -3571,7 +3626,7 @@ function generateFieldConstructorOptions(field) {
3571
3626
  }
3572
3627
  return parts.join(",\n");
3573
3628
  }
3574
- function generateFieldAddition(collectionName, field, varName) {
3629
+ function generateFieldAddition(collectionName, field, varName, isLast = false) {
3575
3630
  const lines = [];
3576
3631
  const constructorName = getFieldConstructorName(field.type);
3577
3632
  const collectionVar = varName || `collection_${collectionName}_${field.name}`;
@@ -3581,10 +3636,10 @@ function generateFieldAddition(collectionName, field, varName) {
3581
3636
  lines.push(generateFieldConstructorOptions(field));
3582
3637
  lines.push(` }));`);
3583
3638
  lines.push(``);
3584
- lines.push(` app.save(${collectionVar});`);
3639
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3585
3640
  return lines.join("\n");
3586
3641
  }
3587
- function generateFieldModification(collectionName, modification, varName) {
3642
+ function generateFieldModification(collectionName, modification, varName, isLast = false) {
3588
3643
  const lines = [];
3589
3644
  const collectionVar = varName || `collection_${collectionName}_${modification.fieldName}`;
3590
3645
  const fieldVar = `${collectionVar}_field`;
@@ -3598,7 +3653,8 @@ function generateFieldModification(collectionName, modification, varName) {
3598
3653
  } else if (change.property.startsWith("relation.")) {
3599
3654
  const relationKey = change.property.replace("relation.", "");
3600
3655
  if (relationKey === "collection") {
3601
- const collectionIdValue = change.newValue === "Users" ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${change.newValue}").id`;
3656
+ const isUsersCollection = String(change.newValue).toLowerCase() === "users";
3657
+ const collectionIdValue = isUsersCollection ? '"_pb_users_auth_"' : `app.findCollectionByNameOrId("${change.newValue}").id`;
3602
3658
  lines.push(` ${fieldVar}.collectionId = ${collectionIdValue};`);
3603
3659
  } else {
3604
3660
  lines.push(` ${fieldVar}.${relationKey} = ${formatValue(change.newValue)};`);
@@ -3608,10 +3664,10 @@ function generateFieldModification(collectionName, modification, varName) {
3608
3664
  }
3609
3665
  }
3610
3666
  lines.push(``);
3611
- lines.push(` app.save(${collectionVar});`);
3667
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3612
3668
  return lines.join("\n");
3613
3669
  }
3614
- function generateFieldDeletion(collectionName, fieldName, varName) {
3670
+ function generateFieldDeletion(collectionName, fieldName, varName, isLast = false) {
3615
3671
  const lines = [];
3616
3672
  const collectionVar = varName || `collection_${collectionName}_${fieldName}`;
3617
3673
  const fieldVar = `${collectionVar}_field`;
@@ -3620,18 +3676,18 @@ function generateFieldDeletion(collectionName, fieldName, varName) {
3620
3676
  lines.push(``);
3621
3677
  lines.push(` ${collectionVar}.fields.remove(${fieldVar}.id);`);
3622
3678
  lines.push(``);
3623
- lines.push(` app.save(${collectionVar});`);
3679
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3624
3680
  return lines.join("\n");
3625
3681
  }
3626
- function generateIndexAddition(collectionName, index, varName) {
3682
+ function generateIndexAddition(collectionName, index, varName, isLast = false) {
3627
3683
  const lines = [];
3628
3684
  const collectionVar = varName || `collection_${collectionName}_idx`;
3629
3685
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3630
3686
  lines.push(` ${collectionVar}.indexes.push("${index}");`);
3631
- lines.push(` app.save(${collectionVar});`);
3687
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3632
3688
  return lines.join("\n");
3633
3689
  }
3634
- function generateIndexRemoval(collectionName, index, varName) {
3690
+ function generateIndexRemoval(collectionName, index, varName, isLast = false) {
3635
3691
  const lines = [];
3636
3692
  const collectionVar = varName || `collection_${collectionName}_idx`;
3637
3693
  const indexVar = `${collectionVar}_indexToRemove`;
@@ -3640,29 +3696,29 @@ function generateIndexRemoval(collectionName, index, varName) {
3640
3696
  lines.push(` if (${indexVar} !== -1) {`);
3641
3697
  lines.push(` ${collectionVar}.indexes.splice(${indexVar}, 1);`);
3642
3698
  lines.push(` }`);
3643
- lines.push(` app.save(${collectionVar});`);
3699
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3644
3700
  return lines.join("\n");
3645
3701
  }
3646
- function generateRuleUpdate(collectionName, ruleType, newValue, varName) {
3702
+ function generateRuleUpdate(collectionName, ruleType, newValue, varName, isLast = false) {
3647
3703
  const lines = [];
3648
3704
  const collectionVar = varName || `collection_${collectionName}_${ruleType}`;
3649
3705
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3650
3706
  lines.push(` ${collectionVar}.${ruleType} = ${formatValue(newValue)};`);
3651
- lines.push(` app.save(${collectionVar});`);
3707
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3652
3708
  return lines.join("\n");
3653
3709
  }
3654
- function generatePermissionUpdate(collectionName, ruleType, newValue, varName) {
3710
+ function generatePermissionUpdate(collectionName, ruleType, newValue, varName, isLast = false) {
3655
3711
  const lines = [];
3656
3712
  const collectionVar = varName || `collection_${collectionName}_${ruleType}`;
3657
3713
  lines.push(` const ${collectionVar} = app.findCollectionByNameOrId("${collectionName}");`);
3658
3714
  lines.push(` ${collectionVar}.${ruleType} = ${formatValue(newValue)};`);
3659
- lines.push(` app.save(${collectionVar});`);
3715
+ lines.push(isLast ? ` return app.save(${collectionVar});` : ` app.save(${collectionVar});`);
3660
3716
  return lines.join("\n");
3661
3717
  }
3662
- function generateCollectionDeletion(collectionName, varName = "collection") {
3718
+ function generateCollectionDeletion(collectionName, varName = "collection", isLast = false) {
3663
3719
  const lines = [];
3664
3720
  lines.push(` const ${varName} = app.findCollectionByNameOrId("${collectionName}");`);
3665
- lines.push(` app.delete(${varName});`);
3721
+ lines.push(isLast ? ` return app.delete(${varName});` : ` app.delete(${varName});`);
3666
3722
  return lines.join("\n");
3667
3723
  }
3668
3724
  function generateUpMigration(diff) {
@@ -3754,7 +3810,24 @@ function generateUpMigration(diff) {
3754
3810
  lines.push(` // No changes detected`);
3755
3811
  lines.push(``);
3756
3812
  }
3757
- return lines.join("\n");
3813
+ let code = lines.join("\n");
3814
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3815
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3816
+ const saveMatches = [...code.matchAll(savePattern)];
3817
+ const deleteMatches = [...code.matchAll(deletePattern)];
3818
+ const allMatches = [
3819
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3820
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3821
+ ].sort((a, b) => b.index - a.index);
3822
+ if (allMatches.length > 0) {
3823
+ const lastMatch = allMatches[0];
3824
+ if (lastMatch.type === "save") {
3825
+ 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);
3826
+ } else {
3827
+ 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);
3828
+ }
3829
+ }
3830
+ return code;
3758
3831
  }
3759
3832
  function generateDownMigration(diff) {
3760
3833
  const lines = [];
@@ -3856,7 +3929,24 @@ function generateDownMigration(diff) {
3856
3929
  lines.push(` // No changes to revert`);
3857
3930
  lines.push(``);
3858
3931
  }
3859
- return lines.join("\n");
3932
+ let code = lines.join("\n");
3933
+ const savePattern = /^(\s*)app\.save\((\w+)\);$/gm;
3934
+ const deletePattern = /^(\s*)app\.delete\((\w+)\);$/gm;
3935
+ const saveMatches = [...code.matchAll(savePattern)];
3936
+ const deleteMatches = [...code.matchAll(deletePattern)];
3937
+ const allMatches = [
3938
+ ...saveMatches.map((m) => ({ match: m, type: "save", index: m.index })),
3939
+ ...deleteMatches.map((m) => ({ match: m, type: "delete", index: m.index }))
3940
+ ].sort((a, b) => b.index - a.index);
3941
+ if (allMatches.length > 0) {
3942
+ const lastMatch = allMatches[0];
3943
+ if (lastMatch.type === "save") {
3944
+ 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);
3945
+ } else {
3946
+ 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);
3947
+ }
3948
+ }
3949
+ return code;
3860
3950
  }
3861
3951
  function generate(diff, config) {
3862
3952
  const normalizedConfig = typeof config === "string" ? { migrationDir: config } : config;
@@ -4457,7 +4547,13 @@ async function executeGenerate(options) {
4457
4547
  const schemaDir = getSchemaDirectory(config);
4458
4548
  const migrationsDir = getMigrationsDirectory(config);
4459
4549
  logSection("\u{1F50D} Analyzing Schema");
4460
- const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(schemaDir));
4550
+ const analyzerConfig = {
4551
+ schemaDir,
4552
+ excludePatterns: config.schema.exclude,
4553
+ useCompiledFiles: false
4554
+ // Use source files since we're in development/testing
4555
+ };
4556
+ const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
4461
4557
  logSuccess(`Found ${currentSchema.collections.size} collection(s)`);
4462
4558
  logInfo("Loading previous snapshot...");
4463
4559
  const previousSnapshot = loadSnapshotIfExists({
@@ -4644,7 +4740,13 @@ async function executeStatus(options) {
4644
4740
  const schemaDir = getSchemaDirectory(config);
4645
4741
  const migrationsDir = getMigrationsDirectory(config);
4646
4742
  logSection("\u{1F50D} Checking Migration Status");
4647
- const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(schemaDir));
4743
+ const analyzerConfig = {
4744
+ schemaDir,
4745
+ excludePatterns: config.schema.exclude,
4746
+ useCompiledFiles: false
4747
+ // Use source files since we're in development/testing
4748
+ };
4749
+ const currentSchema = await withProgress("Parsing Zod schemas...", () => parseSchemaFiles(analyzerConfig));
4648
4750
  logSuccess(`Found ${currentSchema.collections.size} collection(s) in schema`);
4649
4751
  logInfo("Loading previous snapshot...");
4650
4752
  const previousSnapshot = loadSnapshotIfExists({
@@ -4758,8 +4860,6 @@ exports.MigrationGenerationError = MigrationGenerationError;
4758
4860
  exports.MigrationGenerator = MigrationGenerator;
4759
4861
  exports.POCKETBASE_FIELD_TYPES = POCKETBASE_FIELD_TYPES;
4760
4862
  exports.PermissionTemplates = PermissionTemplates;
4761
- exports.ProjectInputSchema = ProjectInputSchema;
4762
- exports.ProjectSchema = ProjectSchema;
4763
4863
  exports.SchemaAnalyzer = SchemaAnalyzer;
4764
4864
  exports.SchemaParsingError = SchemaParsingError;
4765
4865
  exports.SnapshotError = SnapshotError;
@@ -4796,6 +4896,7 @@ exports.extractComprehensiveFieldOptions = extractComprehensiveFieldOptions;
4796
4896
  exports.extractFieldDefinitions = extractFieldDefinitions;
4797
4897
  exports.extractFieldOptions = extractFieldOptions;
4798
4898
  exports.extractIndexes = extractIndexes;
4899
+ exports.extractRelationMetadata = extractRelationMetadata;
4799
4900
  exports.extractSchemaDefinitions = extractSchemaDefinitions;
4800
4901
  exports.fileField = fileField;
4801
4902
  exports.filesField = filesField;