pocketbase-zod-schema 0.1.2 → 0.1.4

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 (69) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +329 -99
  3. package/dist/cli/index.cjs +577 -152
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.js +575 -150
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/migrate.cjs +595 -153
  8. package/dist/cli/migrate.cjs.map +1 -1
  9. package/dist/cli/migrate.js +592 -151
  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 +688 -231
  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 +685 -230
  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/diff.cjs +21 -3
  26. package/dist/migration/diff.cjs.map +1 -1
  27. package/dist/migration/diff.js +21 -3
  28. package/dist/migration/diff.js.map +1 -1
  29. package/dist/migration/generator.cjs +60 -25
  30. package/dist/migration/generator.cjs.map +1 -1
  31. package/dist/migration/generator.d.cts +9 -5
  32. package/dist/migration/generator.d.ts +9 -5
  33. package/dist/migration/generator.js +60 -25
  34. package/dist/migration/generator.js.map +1 -1
  35. package/dist/migration/index.cjs +614 -171
  36. package/dist/migration/index.cjs.map +1 -1
  37. package/dist/migration/index.d.cts +1 -1
  38. package/dist/migration/index.d.ts +1 -1
  39. package/dist/migration/index.js +613 -171
  40. package/dist/migration/index.js.map +1 -1
  41. package/dist/migration/snapshot.cjs +432 -117
  42. package/dist/migration/snapshot.cjs.map +1 -1
  43. package/dist/migration/snapshot.d.cts +34 -12
  44. package/dist/migration/snapshot.d.ts +34 -12
  45. package/dist/migration/snapshot.js +430 -116
  46. package/dist/migration/snapshot.js.map +1 -1
  47. package/dist/migration/utils/index.cjs +19 -17
  48. package/dist/migration/utils/index.cjs.map +1 -1
  49. package/dist/migration/utils/index.d.cts +3 -1
  50. package/dist/migration/utils/index.d.ts +3 -1
  51. package/dist/migration/utils/index.js +19 -17
  52. package/dist/migration/utils/index.js.map +1 -1
  53. package/dist/mutator.cjs +9 -11
  54. package/dist/mutator.cjs.map +1 -1
  55. package/dist/mutator.d.cts +5 -9
  56. package/dist/mutator.d.ts +5 -9
  57. package/dist/mutator.js +9 -11
  58. package/dist/mutator.js.map +1 -1
  59. package/dist/schema.cjs +54 -23
  60. package/dist/schema.cjs.map +1 -1
  61. package/dist/schema.d.cts +94 -12
  62. package/dist/schema.d.ts +94 -12
  63. package/dist/schema.js +54 -24
  64. package/dist/schema.js.map +1 -1
  65. package/dist/types.d.cts +1 -1
  66. package/dist/types.d.ts +1 -1
  67. package/dist/{user-jS1aYoeD.d.cts → user-_AM523hb.d.cts} +6 -6
  68. package/dist/{user-jS1aYoeD.d.ts → user-_AM523hb.d.ts} +6 -6
  69. package/package.json +2 -4
package/dist/mutator.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as PocketBase from 'pocketbase';
2
2
  import { RecordModel, RecordService, ListResult, RecordSubscription, UnsubscribeFunc } from 'pocketbase';
3
3
  import { TypedPocketBase, UserType, UserInputType } from './types.js';
4
4
  import 'zod';
5
- import './user-jS1aYoeD.js';
5
+ import './user-_AM523hb.js';
6
6
 
7
7
  interface MutatorOptions {
8
8
  expand: string[];
@@ -165,18 +165,14 @@ declare abstract class BaseMutator<T extends RecordModel, InputType> {
165
165
  declare class UserMutator extends BaseMutator<UserType, UserInputType> {
166
166
  protected setDefaults(): MutatorOptions;
167
167
  protected getCollection(): PocketBase.RecordService<{
168
- status: "draft" | "active" | "complete" | "fail";
169
- User: string;
170
- title: string;
171
- content: string;
172
- SubscriberUsers: string[];
173
- imageFiles: string[];
174
168
  id: string;
175
169
  collectionId: string;
176
170
  collectionName: string;
177
171
  expand: Record<string, any>;
178
- summary?: string | undefined;
179
- thumbnailURL?: string | undefined;
172
+ email: string;
173
+ password: string;
174
+ name?: string | undefined;
175
+ avatar?: File | undefined;
180
176
  }>;
181
177
  protected validateInput(input: UserInputType): Promise<{
182
178
  email: string;
package/dist/mutator.js CHANGED
@@ -41,21 +41,21 @@ function withIndexes(schema, indexes) {
41
41
  return schema.describe(JSON.stringify(metadata));
42
42
  }
43
43
  var UserInputSchema = z.object({
44
- name: z.string().min(2, "Name must be longer").optional(),
44
+ name: z.string().optional(),
45
45
  email: z.string().email(),
46
46
  password: z.string().min(8, "Password must be at least 8 characters"),
47
47
  passwordConfirm: z.string(),
48
48
  avatar: z.instanceof(File).optional()
49
49
  });
50
50
  var UserDatabaseSchema = z.object({
51
- name: z.string().min(2, "Name must be longer").optional(),
51
+ name: z.string().optional(),
52
52
  email: z.string().email(),
53
53
  password: z.string().min(8, "Password must be at least 8 characters"),
54
54
  avatar: z.instanceof(File).optional()
55
55
  });
56
56
  withIndexes(
57
57
  withPermissions(UserDatabaseSchema.extend(baseSchema), {
58
- // Users can list other users (for mentions, user search, etc.)
58
+ // Users can list their own profile
59
59
  listRule: "id = @request.auth.id",
60
60
  // Users can view their own profile
61
61
  viewRule: "id = @request.auth.id",
@@ -64,15 +64,13 @@ withIndexes(
64
64
  // Users can only update their own profile
65
65
  updateRule: "id = @request.auth.id",
66
66
  // Users can only delete their own account
67
- deleteRule: "id = @request.auth.id",
68
- // Users can only manage their own account (change email, password, etc.)
69
- manageRule: "id = @request.auth.id"
67
+ deleteRule: "id = @request.auth.id"
68
+ // manageRule is null in PocketBase default (not set)
70
69
  }),
71
70
  [
72
- // Email should be unique for authentication
73
- "CREATE UNIQUE INDEX idx_users_email ON users (email)",
74
- // Index on name for user search and sorting
75
- "CREATE INDEX idx_users_name ON users (name)"
71
+ // PocketBase's default indexes for auth collections
72
+ "CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
73
+ "CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''"
76
74
  ]
77
75
  );
78
76
  var BaseMutator = class {
@@ -413,7 +411,7 @@ var UserMutator = class extends BaseMutator {
413
411
  };
414
412
  }
415
413
  getCollection() {
416
- return this.pb.collection("Projects");
414
+ return this.pb.collection("Users");
417
415
  }
418
416
  async validateInput(input) {
419
417
  return UserInputSchema.parse(input);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema/base.ts","../src/schema/user.ts","../src/mutator/baseMutator.ts","../src/mutator/userMutator.ts"],"names":["z"],"mappings":"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","file":"mutator.js","sourcesContent":["import { z } from \"zod\";\nimport type { PermissionSchema, PermissionTemplateConfig } from \"./permissions\";\n\n/**\n * Base schema fields that PocketBase automatically adds to all records\n * These fields are managed by PocketBase and should not be set manually\n */\nexport const baseSchema = {\n id: z.string().describe(\"unique id\"),\n collectionId: z.string().describe(\"collection id\"),\n collectionName: z.string().describe(\"collection name\"),\n expand: z.record(z.any()).describe(\"expandable fields\"),\n};\n\n/**\n * Extended base schema with timestamp fields\n * Includes created and updated autodate fields\n */\nexport const baseSchemaWithTimestamps = {\n ...baseSchema,\n created: z.string().describe(\"creation timestamp\"),\n updated: z.string().describe(\"last update timestamp\"),\n};\n\n/**\n * Base schema for image file collections\n * Extends base schema with thumbnail URL and image files array\n */\nexport const baseImageFileSchema = {\n ...baseSchema,\n thumbnailURL: z.string().optional(),\n imageFiles: z.array(z.string()),\n};\n\n/**\n * Input schema for image file uploads\n * Used in forms where users upload File objects\n * Requires Node.js 20+ or browser environment with File API\n */\nexport const inputImageFileSchema = {\n imageFiles: z.array(z.instanceof(File)),\n};\n\n/**\n * Helper constant for omitting image files from schemas\n * Used with Zod's .omit() method\n */\nexport const omitImageFilesSchema = {\n imageFiles: true,\n} as const;\n\n// ============================================================================\n// Common PocketBase Field Type Patterns\n// ============================================================================\n\n/**\n * Creates a text field schema with optional constraints\n * @param options - Optional constraints for the text field\n */\nexport function textField(options?: { min?: number; max?: number; pattern?: RegExp }) {\n let schema = z.string();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n if (options?.pattern !== undefined) schema = schema.regex(options.pattern);\n return schema;\n}\n\n/**\n * Creates an email field schema\n * Maps to PocketBase 'email' field type\n */\nexport function emailField() {\n return z.string().email();\n}\n\n/**\n * Creates a URL field schema\n * Maps to PocketBase 'url' field type\n */\nexport function urlField() {\n return z.string().url();\n}\n\n/**\n * Creates a number field schema with optional constraints\n * @param options - Optional constraints for the number field\n */\nexport function numberField(options?: { min?: number; max?: number }) {\n let schema = z.number();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates a boolean field schema\n * Maps to PocketBase 'bool' field type\n */\nexport function boolField() {\n return z.boolean();\n}\n\n/**\n * Creates a date field schema\n * Maps to PocketBase 'date' field type\n */\nexport function dateField() {\n return z.date();\n}\n\n/**\n * Creates a select field schema from enum values\n * Maps to PocketBase 'select' field type\n * @param values - Array of allowed string values\n */\nexport function selectField<T extends [string, ...string[]]>(values: T) {\n return z.enum(values);\n}\n\n/**\n * Creates a JSON field schema\n * Maps to PocketBase 'json' field type\n * @param schema - Optional Zod schema for the JSON structure\n */\nexport function jsonField<T extends z.ZodTypeAny>(schema?: T) {\n return schema ?? z.record(z.any());\n}\n\n/**\n * Creates a single file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect=1\n * Requires Node.js 20+ or browser environment with File API\n */\nexport function fileField() {\n return z.instanceof(File);\n}\n\n/**\n * Creates a multiple file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect>1\n * Requires Node.js 20+ or browser environment with File API\n * @param options - Optional constraints for the file field\n */\nexport function filesField(options?: { min?: number; max?: number }) {\n let schema = z.array(z.instanceof(File));\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates a single relation field schema\n * Maps to PocketBase 'relation' field type with maxSelect=1\n * Field name should start with uppercase to be detected as relation\n */\nexport function relationField() {\n return z.string();\n}\n\n/**\n * Creates a multiple relation field schema\n * Maps to PocketBase 'relation' field type with maxSelect>1\n * Field name should contain uppercase to be detected as relation\n * @param options - Optional constraints for the relation field\n */\nexport function relationsField(options?: { min?: number; max?: number }) {\n let schema = z.array(z.string());\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates an editor field schema (rich text)\n * Maps to PocketBase 'editor' field type\n */\nexport function editorField() {\n return z.string();\n}\n\n/**\n * Creates a geo point field schema\n * Maps to PocketBase 'geoPoint' field type\n */\nexport function geoPointField() {\n return z.object({\n lon: z.number(),\n lat: z.number(),\n });\n}\n\n/**\n * Attach permission metadata to a Zod schema\n *\n * This helper function allows you to define PocketBase API rules alongside your\n * entity schema definitions. The permissions are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach permissions to\n * @param config - Either a PermissionTemplateConfig (for template-based permissions)\n * or a PermissionSchema (for custom permissions)\n * @returns The schema with permission metadata attached\n *\n * @example\n * // Using a template\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * { template: 'owner-only', ownerField: 'User' }\n * );\n *\n * @example\n * // Using custom rules\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string() }),\n * { listRule: '@request.auth.id != \"\"', viewRule: '' }\n * );\n *\n * @example\n * // Using template with custom rule overrides\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * {\n * template: 'owner-only',\n * ownerField: 'User',\n * customRules: { listRule: '@request.auth.id != \"\"' }\n * }\n * );\n */\nexport function withPermissions<T extends z.ZodTypeAny>(\n schema: T,\n config: PermissionTemplateConfig | PermissionSchema\n): T {\n // Create metadata object with permissions config directly\n // The PermissionAnalyzer will handle resolving templates vs direct schemas\n const metadata = {\n permissions: config,\n };\n\n // Attach permission metadata to schema using Zod's describe() method\n // The metadata is serialized as JSON and stored in the schema's description\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n\n/**\n * Attach index definitions to a Zod schema\n *\n * This helper function allows you to define PocketBase indexes alongside your\n * entity schema definitions. The indexes are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach indexes to\n * @param indexes - Array of PocketBase index SQL statements\n * @returns The schema with index metadata attached\n *\n * @example\n * // Define indexes for a user schema\n * const UserSchema = withIndexes(\n * withPermissions(\n * z.object({ name: z.string(), email: z.string().email() }),\n * userPermissions\n * ),\n * [\n * 'CREATE UNIQUE INDEX idx_users_email ON users (email)',\n * 'CREATE INDEX idx_users_name ON users (name)'\n * ]\n * );\n *\n * @example\n * // Single index\n * const ProjectSchema = withIndexes(\n * ProjectDatabaseSchema,\n * ['CREATE INDEX idx_projects_status ON projects (status)']\n * );\n */\nexport function withIndexes<T extends z.ZodTypeAny>(schema: T, indexes: string[]): T {\n // Extract existing metadata if present\n let existingMetadata: any = {};\n\n if (schema.description) {\n try {\n existingMetadata = JSON.parse(schema.description);\n } catch {\n // If description is not JSON, ignore it\n }\n }\n\n // Merge indexes with existing metadata\n const metadata = {\n ...existingMetadata,\n indexes,\n };\n\n // Attach metadata to schema using Zod's describe() method\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n","import { z } from \"zod\";\nimport { baseSchema, withIndexes, withPermissions } from \"./base\";\n\n/** -- User Collections -- */\n// Input schema for forms (includes passwordConfirm for validation)\nexport const UserInputSchema = z.object({\n name: z.string().min(2, \"Name must be longer\").optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n passwordConfirm: z.string(),\n avatar: z.instanceof(File).optional(),\n});\n\n// Database schema (excludes passwordConfirm, includes avatar as file field)\nconst UserDatabaseSchema = z.object({\n name: z.string().min(2, \"Name must be longer\").optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n avatar: z.instanceof(File).optional(),\n});\n\n// Apply permissions and indexes for auth collection\n// Users can view all profiles, but only manage their own\nexport const UserSchema = withIndexes(\n withPermissions(UserDatabaseSchema.extend(baseSchema), {\n // Users can list other users (for mentions, user search, etc.)\n listRule: \"id = @request.auth.id\",\n // Users can view their own profile\n viewRule: \"id = @request.auth.id\",\n // Anyone can create an account (sign up)\n createRule: \"\",\n // Users can only update their own profile\n updateRule: \"id = @request.auth.id\",\n // Users can only delete their own account\n deleteRule: \"id = @request.auth.id\",\n // Users can only manage their own account (change email, password, etc.)\n manageRule: \"id = @request.auth.id\",\n }),\n [\n // Email should be unique for authentication\n \"CREATE UNIQUE INDEX idx_users_email ON users (email)\",\n // Index on name for user search and sorting\n \"CREATE INDEX idx_users_name ON users (name)\",\n ]\n);\n","import {\n RecordService,\n type ListResult,\n type RecordListOptions,\n type RecordModel,\n type RecordOptions,\n type RecordSubscribeOptions,\n type RecordSubscription,\n type UnsubscribeFunc,\n} from \"pocketbase\";\nimport type { TypedPocketBase } from \"../types\";\n\nexport interface MutatorOptions {\n expand: string[];\n filter: string[];\n sort: string[];\n}\n\n// T represents the output model type that extends RecordModel\n// InputType represents the input type for creation operations\nexport abstract class BaseMutator<T extends RecordModel, InputType> {\n protected pb: TypedPocketBase;\n\n // Define a default property that subclasses will override\n protected options: MutatorOptions = {\n expand: [],\n filter: [],\n sort: [],\n };\n\n constructor(pb: TypedPocketBase, options?: Partial<MutatorOptions>) {\n this.pb = pb;\n\n // Initialize with default options first\n this.initializeOptions();\n if (options) {\n this.overrideOptions(options);\n }\n }\n\n private initializeOptions(): void {\n this.options = this.setDefaults();\n }\n /**\n * Initialize options with class-specific defaults\n * Subclasses should override this instead of directly setting options\n */\n protected setDefaults(): MutatorOptions {\n return {\n expand: [],\n filter: [],\n sort: [],\n };\n }\n\n /**\n * Merge provided options with current options\n */\n protected overrideOptions(newOptions: Partial<MutatorOptions>): void {\n if (newOptions.expand !== undefined) {\n this.options.expand = newOptions.expand;\n }\n if (newOptions.filter !== undefined) {\n this.options.filter = newOptions.filter;\n }\n if (newOptions.sort !== undefined) {\n this.options.sort = newOptions.sort;\n }\n }\n\n /**\n * Get the collection instance\n */\n protected abstract getCollection(): RecordService<T>;\n\n toSnakeCase(str: string): string {\n return str\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n }\n\n /**\n * Create a new entity\n */\n async create(input: InputType): Promise<T> {\n try {\n const data = await this.validateInput(input);\n const record = await this.entityCreate(data);\n return await this.processRecord(record);\n } catch (error) {\n return this.errorWrapper(error);\n }\n }\n\n /**\n * Update an existing entity\n */\n async update(id: string, input: Partial<T>): Promise<T> {\n try {\n const record = await this.entityUpdate(id, input);\n return await this.processRecord(record);\n } catch (error) {\n return this.errorWrapper(error);\n }\n }\n\n /**\n * Create or update entity (upsert)\n */\n async upsert(input: InputType & { id?: string }): Promise<T> {\n if (input?.id) {\n return await this.update(input.id, input as Partial<T>);\n }\n\n // Implementations should override this method if they need\n // more specific upsert logic like checking for existing entities\n return await this.create(input);\n }\n\n /**\n * Get entity by ID\n */\n async getById(id: string, expand?: string | string[]): Promise<T | null> {\n try {\n const record = await this.entityGetById(id, expand);\n return await this.processRecord(record);\n } catch (error) {\n return this.handleError(error, { allowNotFound: true });\n }\n }\n\n /**\n * Get first entity by filter\n */\n async getFirstByFilter(filter: string | string[], expand?: string | string[], sort?: string): Promise<T | null> {\n try {\n const record = await this.entityGetFirstByFilter(filter, expand, sort);\n return await this.processRecord(record);\n } catch (error) {\n return this.handleError(error, { allowNotFound: true });\n }\n }\n\n /**\n * Get list of entities\n */\n async getList(\n page = 1,\n perPage = 100,\n filter?: string | string[],\n sort?: string,\n expand?: string | string[]\n ): Promise<ListResult<T>> {\n try {\n const result = await this.entityGetList(page, perPage, filter, sort, expand);\n return await this.processListResult(result);\n } catch (error) {\n return this.errorWrapper(error);\n }\n }\n\n /**\n * Delete entity by ID\n */\n async delete(id: string): Promise<boolean> {\n try {\n return await this.entityDelete(id);\n } catch (error) {\n return this.handleError(error, { returnValue: false });\n }\n }\n\n /**\n * Process a single record before returning it\n * Can be overridden to handle special cases like mapped entities\n */\n protected async processRecord(record: T): Promise<T> {\n return record;\n }\n\n /**\n * Process a list result before returning it\n * Can be overridden to handle special cases like mapped entities\n */\n protected async processListResult(result: ListResult<T>): Promise<ListResult<T>> {\n // Process each item in the list\n const processedItems = await Promise.all(result.items.map((item) => this.processRecord(item)));\n\n return {\n ...result,\n items: processedItems,\n };\n }\n\n /**\n * Prepare expand parameter\n * Combines default expands with provided expands\n */\n protected prepareExpand(expand?: string | string[]): string | undefined {\n // Handle empty defaults case\n if (!this.options.expand.length && !expand) {\n return undefined;\n }\n\n // Convert all inputs to arrays for easy processing\n let expandArray: string[] = [...this.options.expand];\n\n if (expand) {\n // If expand is a string, split it and add the parts\n if (typeof expand === \"string\") {\n expandArray = expandArray.concat(expand.split(\",\").map((e) => e.trim()));\n }\n // If expand is already an array, concatenate\n else {\n expandArray = expandArray.concat(expand);\n }\n }\n\n // Filter out duplicates, empty strings, and undefined values\n const uniqueExpands = [...new Set(expandArray)].filter((e) => e !== \"\" && e !== undefined);\n\n // If no valid expands, return undefined\n if (!uniqueExpands.length) {\n return undefined;\n }\n\n // Join with comma and space\n return uniqueExpands.join(\",\");\n }\n\n /**\n * Prepare filter parameter\n * Combines default filters with provided filters\n */\n protected prepareFilter(filter?: string | string[]): string | undefined {\n // Handle empty case\n if (!this.options.filter.length && !filter) {\n return undefined;\n }\n\n // Convert all inputs to arrays for easy processing\n let filterArray: string[] = [...this.options.filter];\n\n if (filter) {\n // If filter is a string, add it as is (it might contain && already)\n if (typeof filter === \"string\") {\n if (filter) filterArray.push(filter);\n }\n // If filter is an array, concatenate\n else {\n filterArray = filterArray.concat(filter);\n }\n }\n\n // Filter out empty strings and undefined values\n const validFilters = filterArray.filter((f) => f !== \"\" && f !== undefined);\n\n // If no valid filters, return undefined\n if (!validFilters.length) {\n return undefined;\n }\n\n // Join with AND operator\n return validFilters.join(\"&&\");\n }\n\n /**\n * Prepare sort parameter\n * Uses provided sort or falls back to default sort\n */\n protected prepareSort(sort?: string): string | undefined {\n // If explicit sort is provided and not empty, use it (overriding defaults)\n if (sort && sort !== \"\") {\n return sort;\n }\n\n // If no explicit sort but we have defaults\n if (this.options.sort.length) {\n // Filter out empty and undefined values\n const validSorts = this.options.sort.filter((s) => s !== \"\" && s !== undefined);\n\n // If we have valid sort items after filtering\n if (validSorts.length) {\n return validSorts.join(\",\");\n }\n }\n\n // No sort specified\n return undefined;\n }\n\n /**\n * Perform the actual create operation\n */\n protected async entityCreate(data: InputType): Promise<T> {\n return await this.getCollection().create(data as Record<string, any>);\n }\n\n /**\n * Perform the actual update operation\n */\n protected async entityUpdate(id: string, data: Partial<T>): Promise<T> {\n return await this.getCollection().update(id, data);\n }\n\n /**\n * Perform the actual getById operation\n */\n protected async entityGetById(id: string, expand?: string | string[]): Promise<T> {\n const finalExpand = this.prepareExpand(expand);\n const options: RecordOptions = finalExpand ? { expand: finalExpand } : {};\n return await this.getCollection().getOne(id, options);\n }\n\n /**\n * Perform the actual getFirstByFilter operation\n */\n protected async entityGetFirstByFilter(\n filter: string | string[],\n expand?: string | string[],\n sort?: string\n ): Promise<T> {\n const finalFilter = this.prepareFilter(filter);\n const finalExpand = this.prepareExpand(expand);\n const finalSort = this.prepareSort(sort);\n\n const options: RecordListOptions = {};\n if (finalExpand) options.expand = finalExpand;\n if (finalSort) options.sort = finalSort;\n\n return await this.getCollection().getFirstListItem(finalFilter || \"\", options);\n }\n\n /**\n * Perform the actual getList operation\n * Returns a list result with items of type T\n */\n protected async entityGetList(\n page: number,\n perPage: number,\n filter?: string | string[],\n sort?: string,\n expand?: string | string[]\n ): Promise<ListResult<T>> {\n const finalFilter = this.prepareFilter(filter);\n const finalExpand = this.prepareExpand(expand);\n const finalSort = this.prepareSort(sort);\n\n const options: RecordListOptions = {};\n if (finalFilter) options.filter = finalFilter;\n if (finalExpand) options.expand = finalExpand;\n if (finalSort) options.sort = finalSort;\n\n return await this.getCollection().getList(page, perPage, options);\n }\n\n /**\n * Perform the actual delete operation\n */\n protected async entityDelete(id: string): Promise<boolean> {\n await this.getCollection().delete(id);\n return true;\n }\n\n /**\n * Error handler for common errors\n * @param error The error to handle\n * @param options Handler options\n * @returns The value to return if the error is handled, or throws if not handled\n */\n protected handleError<R>(\n error: any,\n options: {\n allowNotFound?: boolean;\n returnValue?: R;\n logError?: boolean;\n } = { logError: true }\n ): R {\n const { allowNotFound = false, returnValue, logError = true } = options;\n\n // Log the error if requested\n if (logError) {\n console.error(`Error in ${this.constructor.name}:`, error);\n }\n\n // Handle 404 errors if allowed\n if (allowNotFound && this.isNotFoundError(error)) {\n return null as R;\n }\n\n // Return specified value or rethrow\n if (returnValue !== undefined) {\n return returnValue;\n }\n\n // Rethrow the error\n throw error;\n }\n\n /**\n * Check if an error is a \"not found\" error\n */\n protected isNotFoundError(error: any): boolean {\n return (\n error instanceof Error && (error.message.includes(\"404\") || error.message.toLowerCase().includes(\"not found\"))\n );\n }\n\n /**\n * Standard error handling wrapper (legacy method, consider using handleError instead)\n */\n protected errorWrapper(error: any): never {\n console.error(`Error in ${this.constructor.name}:`, error);\n throw error;\n }\n\n /**\n * Validate input data before creating/updating\n * Should be implemented by child classes\n */\n protected abstract validateInput(input: InputType): Promise<InputType>;\n\n /**\n * Subscribe to changes on a specific record\n * @param id The ID of the record to subscribe to\n * @param callback Function to call when changes occur\n * @param expand Optional expand parameters\n * @returns Promise that resolves to an unsubscribe function\n */\n async subscribeToRecord(\n id: string,\n callback: (data: RecordSubscription<T>) => void,\n expand?: string | string[]\n ): Promise<UnsubscribeFunc> {\n const finalExpand = this.prepareExpand(expand);\n const options: RecordSubscribeOptions = finalExpand ? { expand: finalExpand } : {};\n\n return this.getCollection().subscribe(id, callback, options);\n }\n\n /**\n * Subscribe to changes on the entire collection\n * @param callback Function to call when changes occur\n * @param expand Optional expand parameters\n * @returns Promise that resolves to an unsubscribe function\n */\n async subscribeToCollection(\n callback: (data: RecordSubscription<T>) => void,\n expand?: string | string[]\n ): Promise<UnsubscribeFunc> {\n const finalExpand = this.prepareExpand(expand);\n const options: RecordSubscribeOptions = finalExpand ? { expand: finalExpand } : {};\n\n return this.getCollection().subscribe(\"*\", callback, options);\n }\n\n /**\n * Unsubscribe from a specific record's changes\n * @param id The ID of the record to unsubscribe from\n */\n unsubscribeFromRecord(id: string): void {\n this.getCollection().unsubscribe(id);\n }\n\n /**\n * Unsubscribe from collection-wide changes\n */\n unsubscribeFromCollection(): void {\n this.getCollection().unsubscribe(\"*\");\n }\n\n /**\n * Unsubscribe from all subscriptions in this collection\n */\n unsubscribeAll(): void {\n this.getCollection().unsubscribe();\n }\n}\n","import { UserInputSchema } from \"../schema\";\nimport type { UserInputType, UserType } from \"../types\";\nimport { BaseMutator, type MutatorOptions } from \"./baseMutator\";\n\nexport class UserMutator extends BaseMutator<UserType, UserInputType> {\n protected setDefaults(): MutatorOptions {\n return {\n expand: [],\n filter: [],\n sort: [\"-updated\"],\n };\n }\n\n protected getCollection() {\n return this.pb.collection(\"Projects\");\n }\n\n protected async validateInput(input: UserInputType) {\n return UserInputSchema.parse(input);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/schema/base.ts","../src/schema/user.ts","../src/mutator/baseMutator.ts","../src/mutator/userMutator.ts"],"names":["z"],"mappings":"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","file":"mutator.js","sourcesContent":["import { z } from \"zod\";\nimport type { PermissionSchema, PermissionTemplateConfig } from \"../utils/permissions\";\n\n/**\n * Base schema fields that PocketBase automatically adds to all records\n * These fields are managed by PocketBase and should not be set manually\n */\nexport const baseSchema = {\n id: z.string().describe(\"unique id\"),\n collectionId: z.string().describe(\"collection id\"),\n collectionName: z.string().describe(\"collection name\"),\n expand: z.record(z.any()).describe(\"expandable fields\"),\n};\n\n/**\n * Extended base schema with timestamp fields\n * Includes created and updated autodate fields\n */\nexport const baseSchemaWithTimestamps = {\n ...baseSchema,\n created: z.string().describe(\"creation timestamp\"),\n updated: z.string().describe(\"last update timestamp\"),\n};\n\n/**\n * Base schema for image file collections\n * Extends base schema with thumbnail URL and image files array\n */\nexport const baseImageFileSchema = {\n ...baseSchema,\n thumbnailURL: z.string().optional(),\n imageFiles: z.array(z.string()),\n};\n\n/**\n * Input schema for image file uploads\n * Used in forms where users upload File objects\n * Requires Node.js 20+ or browser environment with File API\n */\nexport const inputImageFileSchema = {\n imageFiles: z.array(z.instanceof(File)),\n};\n\n/**\n * Helper constant for omitting image files from schemas\n * Used with Zod's .omit() method\n */\nexport const omitImageFilesSchema = {\n imageFiles: true,\n} as const;\n\n// ============================================================================\n// Common PocketBase Field Type Patterns\n// ============================================================================\n\n/**\n * Creates a text field schema with optional constraints\n * @param options - Optional constraints for the text field\n */\nexport function textField(options?: { min?: number; max?: number; pattern?: RegExp }) {\n let schema = z.string();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n if (options?.pattern !== undefined) schema = schema.regex(options.pattern);\n return schema;\n}\n\n/**\n * Creates an email field schema\n * Maps to PocketBase 'email' field type\n */\nexport function emailField() {\n return z.string().email();\n}\n\n/**\n * Creates a URL field schema\n * Maps to PocketBase 'url' field type\n */\nexport function urlField() {\n return z.string().url();\n}\n\n/**\n * Creates a number field schema with optional constraints\n * @param options - Optional constraints for the number field\n */\nexport function numberField(options?: { min?: number; max?: number }) {\n let schema = z.number();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates a boolean field schema\n * Maps to PocketBase 'bool' field type\n */\nexport function boolField() {\n return z.boolean();\n}\n\n/**\n * Creates a date field schema\n * Maps to PocketBase 'date' field type\n */\nexport function dateField() {\n return z.date();\n}\n\n/**\n * Creates a select field schema from enum values\n * Maps to PocketBase 'select' field type\n * @param values - Array of allowed string values\n */\nexport function selectField<T extends [string, ...string[]]>(values: T) {\n return z.enum(values);\n}\n\n/**\n * Creates a JSON field schema\n * Maps to PocketBase 'json' field type\n * @param schema - Optional Zod schema for the JSON structure\n */\nexport function jsonField<T extends z.ZodTypeAny>(schema?: T) {\n return schema ?? z.record(z.any());\n}\n\n/**\n * Creates a single file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect=1\n * Requires Node.js 20+ or browser environment with File API\n */\nexport function fileField() {\n return z.instanceof(File);\n}\n\n/**\n * Creates a multiple file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect>1\n * Requires Node.js 20+ or browser environment with File API\n * @param options - Optional constraints for the file field\n */\nexport function filesField(options?: { min?: number; max?: number }) {\n let schema = z.array(z.instanceof(File));\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n// ============================================================================\n// Relation Field Helpers - Explicit Relationship Definitions\n// ============================================================================\n\n/**\n * Relation field configuration options\n */\nexport interface RelationConfig {\n /**\n * Target collection name (e.g., 'users', 'posts', 'tags')\n * This is the PocketBase collection that the relation points to\n */\n collection: string;\n\n /**\n * Whether to cascade delete related records when this record is deleted\n * @default false\n */\n cascadeDelete?: boolean;\n}\n\n/**\n * Multiple relation field configuration options\n */\nexport interface RelationsConfig extends RelationConfig {\n /**\n * Minimum number of relations required\n * @default 0\n */\n minSelect?: number;\n\n /**\n * Maximum number of relations allowed\n * @default 999\n */\n maxSelect?: number;\n}\n\n/**\n * Internal marker for relation metadata\n * Used by the analyzer to detect explicit relation definitions\n */\nconst RELATION_METADATA_KEY = \"__pocketbase_relation__\";\n\n/**\n * Creates a single relation field schema with explicit collection target\n * Maps to PocketBase 'relation' field type with maxSelect=1\n *\n * This is the recommended way to define relations - it's explicit and doesn't\n * rely on naming conventions.\n *\n * @param config - Relation configuration with target collection\n * @returns Zod string schema with relation metadata\n *\n * @example\n * // Single relation to users collection\n * const PostSchema = z.object({\n * title: z.string(),\n * author: relationField({ collection: 'users' }),\n * });\n *\n * @example\n * // Relation with cascade delete\n * const CommentSchema = z.object({\n * content: z.string(),\n * post: relationField({ collection: 'posts', cascadeDelete: true }),\n * });\n */\nexport function relationField(config: RelationConfig) {\n const metadata = {\n [RELATION_METADATA_KEY]: {\n type: \"single\",\n collection: config.collection,\n cascadeDelete: config.cascadeDelete ?? false,\n maxSelect: 1,\n minSelect: 0,\n },\n };\n\n return z.string().describe(JSON.stringify(metadata));\n}\n\n/**\n * Creates a multiple relation field schema with explicit collection target\n * Maps to PocketBase 'relation' field type with maxSelect>1\n *\n * This is the recommended way to define multi-relations - it's explicit and\n * doesn't rely on naming conventions.\n *\n * @param config - Relations configuration with target collection and limits\n * @returns Zod array of strings schema with relation metadata\n *\n * @example\n * // Multiple relations to tags collection\n * const PostSchema = z.object({\n * title: z.string(),\n * tags: relationsField({ collection: 'tags' }),\n * });\n *\n * @example\n * // Relations with min/max constraints\n * const ProjectSchema = z.object({\n * title: z.string(),\n * collaborators: relationsField({\n * collection: 'users',\n * minSelect: 1,\n * maxSelect: 10,\n * }),\n * });\n */\nexport function relationsField(config: RelationsConfig) {\n const metadata = {\n [RELATION_METADATA_KEY]: {\n type: \"multiple\",\n collection: config.collection,\n cascadeDelete: config.cascadeDelete ?? false,\n maxSelect: config.maxSelect ?? 999,\n minSelect: config.minSelect ?? 0,\n },\n };\n\n let schema = z.array(z.string());\n\n // Apply array constraints for Zod validation\n if (config.minSelect !== undefined) {\n schema = schema.min(config.minSelect);\n }\n if (config.maxSelect !== undefined) {\n schema = schema.max(config.maxSelect);\n }\n\n return schema.describe(JSON.stringify(metadata));\n}\n\n/**\n * Extracts relation metadata from a Zod type's description\n * Used internally by the analyzer to detect explicit relation definitions\n *\n * @param description - The Zod type's description string\n * @returns Relation metadata if present, null otherwise\n */\nexport function extractRelationMetadata(description: string | undefined): {\n type: \"single\" | \"multiple\";\n collection: string;\n cascadeDelete: boolean;\n maxSelect: number;\n minSelect: number;\n} | null {\n if (!description) return null;\n\n try {\n const parsed = JSON.parse(description);\n if (parsed[RELATION_METADATA_KEY]) {\n return parsed[RELATION_METADATA_KEY];\n }\n } catch {\n // Not JSON, ignore\n }\n\n return null;\n}\n\n/**\n * Creates an editor field schema (rich text)\n * Maps to PocketBase 'editor' field type\n */\nexport function editorField() {\n return z.string();\n}\n\n/**\n * Creates a geo point field schema\n * Maps to PocketBase 'geoPoint' field type\n */\nexport function geoPointField() {\n return z.object({\n lon: z.number(),\n lat: z.number(),\n });\n}\n\n/**\n * Attach permission metadata to a Zod schema\n *\n * This helper function allows you to define PocketBase API rules alongside your\n * entity schema definitions. The permissions are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach permissions to\n * @param config - Either a PermissionTemplateConfig (for template-based permissions)\n * or a PermissionSchema (for custom permissions)\n * @returns The schema with permission metadata attached\n *\n * @example\n * // Using a template\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * { template: 'owner-only', ownerField: 'User' }\n * );\n *\n * @example\n * // Using custom rules\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string() }),\n * { listRule: '@request.auth.id != \"\"', viewRule: '' }\n * );\n *\n * @example\n * // Using template with custom rule overrides\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * {\n * template: 'owner-only',\n * ownerField: 'User',\n * customRules: { listRule: '@request.auth.id != \"\"' }\n * }\n * );\n */\nexport function withPermissions<T extends z.ZodTypeAny>(\n schema: T,\n config: PermissionTemplateConfig | PermissionSchema\n): T {\n // Create metadata object with permissions config directly\n // The PermissionAnalyzer will handle resolving templates vs direct schemas\n const metadata = {\n permissions: config,\n };\n\n // Attach permission metadata to schema using Zod's describe() method\n // The metadata is serialized as JSON and stored in the schema's description\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n\n/**\n * Attach index definitions to a Zod schema\n *\n * This helper function allows you to define PocketBase indexes alongside your\n * entity schema definitions. The indexes are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach indexes to\n * @param indexes - Array of PocketBase index SQL statements\n * @returns The schema with index metadata attached\n *\n * @example\n * // Define indexes for a user schema\n * const UserSchema = withIndexes(\n * withPermissions(\n * z.object({ name: z.string(), email: z.string().email() }),\n * userPermissions\n * ),\n * [\n * 'CREATE UNIQUE INDEX idx_users_email ON users (email)',\n * 'CREATE INDEX idx_users_name ON users (name)'\n * ]\n * );\n *\n * @example\n * // Single index\n * const ProjectSchema = withIndexes(\n * ProjectDatabaseSchema,\n * ['CREATE INDEX idx_projects_status ON projects (status)']\n * );\n */\nexport function withIndexes<T extends z.ZodTypeAny>(schema: T, indexes: string[]): T {\n // Extract existing metadata if present\n let existingMetadata: any = {};\n\n if (schema.description) {\n try {\n existingMetadata = JSON.parse(schema.description);\n } catch {\n // If description is not JSON, ignore it\n }\n }\n\n // Merge indexes with existing metadata\n const metadata = {\n ...existingMetadata,\n indexes,\n };\n\n // Attach metadata to schema using Zod's describe() method\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n","import { z } from \"zod\";\nimport { baseSchema, withIndexes, withPermissions } from \"./base\";\n\n/** -- User Collections -- */\n// Input schema for forms (includes passwordConfirm for validation)\nexport const UserInputSchema = z.object({\n name: z.string().optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n passwordConfirm: z.string(),\n avatar: z.instanceof(File).optional(),\n});\n\n// Database schema (excludes passwordConfirm, includes avatar as file field)\n// Matches PocketBase's default users auth collection structure\n// Note: PocketBase has min: 0, max: 255 for name, but our snapshot loader\n// doesn't extract field-level options properly yet (TODO: fix snapshot loader)\nconst UserDatabaseSchema = z.object({\n name: z.string().optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n avatar: z.instanceof(File).optional(),\n});\n\n// Apply permissions and indexes for auth collection\n// Matches PocketBase's default users collection configuration\nexport const UserSchema = withIndexes(\n withPermissions(UserDatabaseSchema.extend(baseSchema), {\n // Users can list their own profile\n listRule: \"id = @request.auth.id\",\n // Users can view their own profile\n viewRule: \"id = @request.auth.id\",\n // Anyone can create an account (sign up)\n createRule: \"\",\n // Users can only update their own profile\n updateRule: \"id = @request.auth.id\",\n // Users can only delete their own account\n deleteRule: \"id = @request.auth.id\",\n // manageRule is null in PocketBase default (not set)\n }),\n [\n // PocketBase's default indexes for auth collections\n \"CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)\",\n \"CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''\",\n ]\n);\n","import {\n RecordService,\n type ListResult,\n type RecordListOptions,\n type RecordModel,\n type RecordOptions,\n type RecordSubscribeOptions,\n type RecordSubscription,\n type UnsubscribeFunc,\n} from \"pocketbase\";\nimport type { TypedPocketBase } from \"../types\";\n\nexport interface MutatorOptions {\n expand: string[];\n filter: string[];\n sort: string[];\n}\n\n// T represents the output model type that extends RecordModel\n// InputType represents the input type for creation operations\nexport abstract class BaseMutator<T extends RecordModel, InputType> {\n protected pb: TypedPocketBase;\n\n // Define a default property that subclasses will override\n protected options: MutatorOptions = {\n expand: [],\n filter: [],\n sort: [],\n };\n\n constructor(pb: TypedPocketBase, options?: Partial<MutatorOptions>) {\n this.pb = pb;\n\n // Initialize with default options first\n this.initializeOptions();\n if (options) {\n this.overrideOptions(options);\n }\n }\n\n private initializeOptions(): void {\n this.options = this.setDefaults();\n }\n /**\n * Initialize options with class-specific defaults\n * Subclasses should override this instead of directly setting options\n */\n protected setDefaults(): MutatorOptions {\n return {\n expand: [],\n filter: [],\n sort: [],\n };\n }\n\n /**\n * Merge provided options with current options\n */\n protected overrideOptions(newOptions: Partial<MutatorOptions>): void {\n if (newOptions.expand !== undefined) {\n this.options.expand = newOptions.expand;\n }\n if (newOptions.filter !== undefined) {\n this.options.filter = newOptions.filter;\n }\n if (newOptions.sort !== undefined) {\n this.options.sort = newOptions.sort;\n }\n }\n\n /**\n * Get the collection instance\n */\n protected abstract getCollection(): RecordService<T>;\n\n toSnakeCase(str: string): string {\n return str\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n }\n\n /**\n * Create a new entity\n */\n async create(input: InputType): Promise<T> {\n try {\n const data = await this.validateInput(input);\n const record = await this.entityCreate(data);\n return await this.processRecord(record);\n } catch (error) {\n return this.errorWrapper(error);\n }\n }\n\n /**\n * Update an existing entity\n */\n async update(id: string, input: Partial<T>): Promise<T> {\n try {\n const record = await this.entityUpdate(id, input);\n return await this.processRecord(record);\n } catch (error) {\n return this.errorWrapper(error);\n }\n }\n\n /**\n * Create or update entity (upsert)\n */\n async upsert(input: InputType & { id?: string }): Promise<T> {\n if (input?.id) {\n return await this.update(input.id, input as Partial<T>);\n }\n\n // Implementations should override this method if they need\n // more specific upsert logic like checking for existing entities\n return await this.create(input);\n }\n\n /**\n * Get entity by ID\n */\n async getById(id: string, expand?: string | string[]): Promise<T | null> {\n try {\n const record = await this.entityGetById(id, expand);\n return await this.processRecord(record);\n } catch (error) {\n return this.handleError(error, { allowNotFound: true });\n }\n }\n\n /**\n * Get first entity by filter\n */\n async getFirstByFilter(filter: string | string[], expand?: string | string[], sort?: string): Promise<T | null> {\n try {\n const record = await this.entityGetFirstByFilter(filter, expand, sort);\n return await this.processRecord(record);\n } catch (error) {\n return this.handleError(error, { allowNotFound: true });\n }\n }\n\n /**\n * Get list of entities\n */\n async getList(\n page = 1,\n perPage = 100,\n filter?: string | string[],\n sort?: string,\n expand?: string | string[]\n ): Promise<ListResult<T>> {\n try {\n const result = await this.entityGetList(page, perPage, filter, sort, expand);\n return await this.processListResult(result);\n } catch (error) {\n return this.errorWrapper(error);\n }\n }\n\n /**\n * Delete entity by ID\n */\n async delete(id: string): Promise<boolean> {\n try {\n return await this.entityDelete(id);\n } catch (error) {\n return this.handleError(error, { returnValue: false });\n }\n }\n\n /**\n * Process a single record before returning it\n * Can be overridden to handle special cases like mapped entities\n */\n protected async processRecord(record: T): Promise<T> {\n return record;\n }\n\n /**\n * Process a list result before returning it\n * Can be overridden to handle special cases like mapped entities\n */\n protected async processListResult(result: ListResult<T>): Promise<ListResult<T>> {\n // Process each item in the list\n const processedItems = await Promise.all(result.items.map((item) => this.processRecord(item)));\n\n return {\n ...result,\n items: processedItems,\n };\n }\n\n /**\n * Prepare expand parameter\n * Combines default expands with provided expands\n */\n protected prepareExpand(expand?: string | string[]): string | undefined {\n // Handle empty defaults case\n if (!this.options.expand.length && !expand) {\n return undefined;\n }\n\n // Convert all inputs to arrays for easy processing\n let expandArray: string[] = [...this.options.expand];\n\n if (expand) {\n // If expand is a string, split it and add the parts\n if (typeof expand === \"string\") {\n expandArray = expandArray.concat(expand.split(\",\").map((e) => e.trim()));\n }\n // If expand is already an array, concatenate\n else {\n expandArray = expandArray.concat(expand);\n }\n }\n\n // Filter out duplicates, empty strings, and undefined values\n const uniqueExpands = [...new Set(expandArray)].filter((e) => e !== \"\" && e !== undefined);\n\n // If no valid expands, return undefined\n if (!uniqueExpands.length) {\n return undefined;\n }\n\n // Join with comma and space\n return uniqueExpands.join(\",\");\n }\n\n /**\n * Prepare filter parameter\n * Combines default filters with provided filters\n */\n protected prepareFilter(filter?: string | string[]): string | undefined {\n // Handle empty case\n if (!this.options.filter.length && !filter) {\n return undefined;\n }\n\n // Convert all inputs to arrays for easy processing\n let filterArray: string[] = [...this.options.filter];\n\n if (filter) {\n // If filter is a string, add it as is (it might contain && already)\n if (typeof filter === \"string\") {\n if (filter) filterArray.push(filter);\n }\n // If filter is an array, concatenate\n else {\n filterArray = filterArray.concat(filter);\n }\n }\n\n // Filter out empty strings and undefined values\n const validFilters = filterArray.filter((f) => f !== \"\" && f !== undefined);\n\n // If no valid filters, return undefined\n if (!validFilters.length) {\n return undefined;\n }\n\n // Join with AND operator\n return validFilters.join(\"&&\");\n }\n\n /**\n * Prepare sort parameter\n * Uses provided sort or falls back to default sort\n */\n protected prepareSort(sort?: string): string | undefined {\n // If explicit sort is provided and not empty, use it (overriding defaults)\n if (sort && sort !== \"\") {\n return sort;\n }\n\n // If no explicit sort but we have defaults\n if (this.options.sort.length) {\n // Filter out empty and undefined values\n const validSorts = this.options.sort.filter((s) => s !== \"\" && s !== undefined);\n\n // If we have valid sort items after filtering\n if (validSorts.length) {\n return validSorts.join(\",\");\n }\n }\n\n // No sort specified\n return undefined;\n }\n\n /**\n * Perform the actual create operation\n */\n protected async entityCreate(data: InputType): Promise<T> {\n return await this.getCollection().create(data as Record<string, any>);\n }\n\n /**\n * Perform the actual update operation\n */\n protected async entityUpdate(id: string, data: Partial<T>): Promise<T> {\n return await this.getCollection().update(id, data);\n }\n\n /**\n * Perform the actual getById operation\n */\n protected async entityGetById(id: string, expand?: string | string[]): Promise<T> {\n const finalExpand = this.prepareExpand(expand);\n const options: RecordOptions = finalExpand ? { expand: finalExpand } : {};\n return await this.getCollection().getOne(id, options);\n }\n\n /**\n * Perform the actual getFirstByFilter operation\n */\n protected async entityGetFirstByFilter(\n filter: string | string[],\n expand?: string | string[],\n sort?: string\n ): Promise<T> {\n const finalFilter = this.prepareFilter(filter);\n const finalExpand = this.prepareExpand(expand);\n const finalSort = this.prepareSort(sort);\n\n const options: RecordListOptions = {};\n if (finalExpand) options.expand = finalExpand;\n if (finalSort) options.sort = finalSort;\n\n return await this.getCollection().getFirstListItem(finalFilter || \"\", options);\n }\n\n /**\n * Perform the actual getList operation\n * Returns a list result with items of type T\n */\n protected async entityGetList(\n page: number,\n perPage: number,\n filter?: string | string[],\n sort?: string,\n expand?: string | string[]\n ): Promise<ListResult<T>> {\n const finalFilter = this.prepareFilter(filter);\n const finalExpand = this.prepareExpand(expand);\n const finalSort = this.prepareSort(sort);\n\n const options: RecordListOptions = {};\n if (finalFilter) options.filter = finalFilter;\n if (finalExpand) options.expand = finalExpand;\n if (finalSort) options.sort = finalSort;\n\n return await this.getCollection().getList(page, perPage, options);\n }\n\n /**\n * Perform the actual delete operation\n */\n protected async entityDelete(id: string): Promise<boolean> {\n await this.getCollection().delete(id);\n return true;\n }\n\n /**\n * Error handler for common errors\n * @param error The error to handle\n * @param options Handler options\n * @returns The value to return if the error is handled, or throws if not handled\n */\n protected handleError<R>(\n error: any,\n options: {\n allowNotFound?: boolean;\n returnValue?: R;\n logError?: boolean;\n } = { logError: true }\n ): R {\n const { allowNotFound = false, returnValue, logError = true } = options;\n\n // Log the error if requested\n if (logError) {\n console.error(`Error in ${this.constructor.name}:`, error);\n }\n\n // Handle 404 errors if allowed\n if (allowNotFound && this.isNotFoundError(error)) {\n return null as R;\n }\n\n // Return specified value or rethrow\n if (returnValue !== undefined) {\n return returnValue;\n }\n\n // Rethrow the error\n throw error;\n }\n\n /**\n * Check if an error is a \"not found\" error\n */\n protected isNotFoundError(error: any): boolean {\n return (\n error instanceof Error && (error.message.includes(\"404\") || error.message.toLowerCase().includes(\"not found\"))\n );\n }\n\n /**\n * Standard error handling wrapper (legacy method, consider using handleError instead)\n */\n protected errorWrapper(error: any): never {\n console.error(`Error in ${this.constructor.name}:`, error);\n throw error;\n }\n\n /**\n * Validate input data before creating/updating\n * Should be implemented by child classes\n */\n protected abstract validateInput(input: InputType): Promise<InputType>;\n\n /**\n * Subscribe to changes on a specific record\n * @param id The ID of the record to subscribe to\n * @param callback Function to call when changes occur\n * @param expand Optional expand parameters\n * @returns Promise that resolves to an unsubscribe function\n */\n async subscribeToRecord(\n id: string,\n callback: (data: RecordSubscription<T>) => void,\n expand?: string | string[]\n ): Promise<UnsubscribeFunc> {\n const finalExpand = this.prepareExpand(expand);\n const options: RecordSubscribeOptions = finalExpand ? { expand: finalExpand } : {};\n\n return this.getCollection().subscribe(id, callback, options);\n }\n\n /**\n * Subscribe to changes on the entire collection\n * @param callback Function to call when changes occur\n * @param expand Optional expand parameters\n * @returns Promise that resolves to an unsubscribe function\n */\n async subscribeToCollection(\n callback: (data: RecordSubscription<T>) => void,\n expand?: string | string[]\n ): Promise<UnsubscribeFunc> {\n const finalExpand = this.prepareExpand(expand);\n const options: RecordSubscribeOptions = finalExpand ? { expand: finalExpand } : {};\n\n return this.getCollection().subscribe(\"*\", callback, options);\n }\n\n /**\n * Unsubscribe from a specific record's changes\n * @param id The ID of the record to unsubscribe from\n */\n unsubscribeFromRecord(id: string): void {\n this.getCollection().unsubscribe(id);\n }\n\n /**\n * Unsubscribe from collection-wide changes\n */\n unsubscribeFromCollection(): void {\n this.getCollection().unsubscribe(\"*\");\n }\n\n /**\n * Unsubscribe from all subscriptions in this collection\n */\n unsubscribeAll(): void {\n this.getCollection().unsubscribe();\n }\n}\n","import { UserInputSchema } from \"../schema\";\nimport type { UserInputType, UserType } from \"../types\";\nimport { BaseMutator, type MutatorOptions } from \"./baseMutator\";\n\nexport class UserMutator extends BaseMutator<UserType, UserInputType> {\n protected setDefaults(): MutatorOptions {\n return {\n expand: [],\n filter: [],\n sort: [\"-updated\"],\n };\n }\n\n protected getCollection() {\n return this.pb.collection(\"Users\");\n }\n\n protected async validateInput(input: UserInputType) {\n return UserInputSchema.parse(input);\n }\n}\n"]}
package/dist/schema.cjs CHANGED
@@ -65,14 +65,48 @@ function filesField(options) {
65
65
  if (options?.max !== void 0) schema = schema.max(options.max);
66
66
  return schema;
67
67
  }
68
- function relationField() {
69
- return zod.z.string();
68
+ var RELATION_METADATA_KEY = "__pocketbase_relation__";
69
+ function relationField(config) {
70
+ const metadata = {
71
+ [RELATION_METADATA_KEY]: {
72
+ type: "single",
73
+ collection: config.collection,
74
+ cascadeDelete: config.cascadeDelete ?? false,
75
+ maxSelect: 1,
76
+ minSelect: 0
77
+ }
78
+ };
79
+ return zod.z.string().describe(JSON.stringify(metadata));
70
80
  }
71
- function relationsField(options) {
81
+ function relationsField(config) {
82
+ const metadata = {
83
+ [RELATION_METADATA_KEY]: {
84
+ type: "multiple",
85
+ collection: config.collection,
86
+ cascadeDelete: config.cascadeDelete ?? false,
87
+ maxSelect: config.maxSelect ?? 999,
88
+ minSelect: config.minSelect ?? 0
89
+ }
90
+ };
72
91
  let schema = zod.z.array(zod.z.string());
73
- if (options?.min !== void 0) schema = schema.min(options.min);
74
- if (options?.max !== void 0) schema = schema.max(options.max);
75
- return schema;
92
+ if (config.minSelect !== void 0) {
93
+ schema = schema.min(config.minSelect);
94
+ }
95
+ if (config.maxSelect !== void 0) {
96
+ schema = schema.max(config.maxSelect);
97
+ }
98
+ return schema.describe(JSON.stringify(metadata));
99
+ }
100
+ function extractRelationMetadata(description) {
101
+ if (!description) return null;
102
+ try {
103
+ const parsed = JSON.parse(description);
104
+ if (parsed[RELATION_METADATA_KEY]) {
105
+ return parsed[RELATION_METADATA_KEY];
106
+ }
107
+ } catch {
108
+ }
109
+ return null;
76
110
  }
77
111
  function editorField() {
78
112
  return zod.z.string();
@@ -104,7 +138,7 @@ function withIndexes(schema, indexes) {
104
138
  return schema.describe(JSON.stringify(metadata));
105
139
  }
106
140
 
107
- // src/schema/permission-templates.ts
141
+ // src/utils/permission-templates.ts
108
142
  var PermissionTemplates = {
109
143
  /**
110
144
  * Public access - anyone can perform all operations
@@ -341,38 +375,36 @@ var ProjectInputSchema = zod.z.object({
341
375
  content: zod.z.string(),
342
376
  status: StatusEnum,
343
377
  summary: zod.z.string().optional(),
344
- User: zod.z.string().nonempty("User ID is missing"),
345
- SubscriberUsers: zod.z.array(zod.z.string())
378
+ OwnerUser: relationField({ collection: "Users" }),
379
+ SubscriberUsers: relationsField({ collection: "Users" })
346
380
  }).extend(inputImageFileSchema);
347
381
  var ProjectSchema = withPermissions(
348
382
  ProjectInputSchema.omit(omitImageFilesSchema).extend(baseImageFileSchema),
349
383
  {
350
384
  template: "owner-only",
351
- ownerField: "User",
385
+ ownerField: "OwnerUser",
352
386
  customRules: {
353
- // Override list rule to allow authenticated users to see all projects
354
387
  listRule: '@request.auth.id != ""',
355
- // Allow viewing if user is owner OR a subscriber
356
- viewRule: '@request.auth.id != "" && (User = @request.auth.id || SubscriberUsers ?= @request.auth.id)'
388
+ viewRule: '@request.auth.id != "" && (OwnerUser = @request.auth.id || SubscriberUsers ?= @request.auth.id)'
357
389
  }
358
390
  }
359
391
  );
360
392
  var UserInputSchema = zod.z.object({
361
- name: zod.z.string().min(2, "Name must be longer").optional(),
393
+ name: zod.z.string().optional(),
362
394
  email: zod.z.string().email(),
363
395
  password: zod.z.string().min(8, "Password must be at least 8 characters"),
364
396
  passwordConfirm: zod.z.string(),
365
397
  avatar: zod.z.instanceof(File).optional()
366
398
  });
367
399
  var UserDatabaseSchema = zod.z.object({
368
- name: zod.z.string().min(2, "Name must be longer").optional(),
400
+ name: zod.z.string().optional(),
369
401
  email: zod.z.string().email(),
370
402
  password: zod.z.string().min(8, "Password must be at least 8 characters"),
371
403
  avatar: zod.z.instanceof(File).optional()
372
404
  });
373
405
  var UserSchema = withIndexes(
374
406
  withPermissions(UserDatabaseSchema.extend(baseSchema), {
375
- // Users can list other users (for mentions, user search, etc.)
407
+ // Users can list their own profile
376
408
  listRule: "id = @request.auth.id",
377
409
  // Users can view their own profile
378
410
  viewRule: "id = @request.auth.id",
@@ -381,15 +413,13 @@ var UserSchema = withIndexes(
381
413
  // Users can only update their own profile
382
414
  updateRule: "id = @request.auth.id",
383
415
  // Users can only delete their own account
384
- deleteRule: "id = @request.auth.id",
385
- // Users can only manage their own account (change email, password, etc.)
386
- manageRule: "id = @request.auth.id"
416
+ deleteRule: "id = @request.auth.id"
417
+ // manageRule is null in PocketBase default (not set)
387
418
  }),
388
419
  [
389
- // Email should be unique for authentication
390
- "CREATE UNIQUE INDEX idx_users_email ON users (email)",
391
- // Index on name for user search and sorting
392
- "CREATE INDEX idx_users_name ON users (name)"
420
+ // PocketBase's default indexes for auth collections
421
+ "CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
422
+ "CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''"
393
423
  ]
394
424
  );
395
425
 
@@ -406,6 +436,7 @@ exports.createPermissions = createPermissions;
406
436
  exports.dateField = dateField;
407
437
  exports.editorField = editorField;
408
438
  exports.emailField = emailField;
439
+ exports.extractRelationMetadata = extractRelationMetadata;
409
440
  exports.fileField = fileField;
410
441
  exports.filesField = filesField;
411
442
  exports.geoPointField = geoPointField;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema/base.ts","../src/schema/permission-templates.ts","../src/enums.ts","../src/schema/project.ts","../src/schema/user.ts"],"names":["z"],"mappings":"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","file":"schema.cjs","sourcesContent":["import { z } from \"zod\";\nimport type { PermissionSchema, PermissionTemplateConfig } from \"./permissions\";\n\n/**\n * Base schema fields that PocketBase automatically adds to all records\n * These fields are managed by PocketBase and should not be set manually\n */\nexport const baseSchema = {\n id: z.string().describe(\"unique id\"),\n collectionId: z.string().describe(\"collection id\"),\n collectionName: z.string().describe(\"collection name\"),\n expand: z.record(z.any()).describe(\"expandable fields\"),\n};\n\n/**\n * Extended base schema with timestamp fields\n * Includes created and updated autodate fields\n */\nexport const baseSchemaWithTimestamps = {\n ...baseSchema,\n created: z.string().describe(\"creation timestamp\"),\n updated: z.string().describe(\"last update timestamp\"),\n};\n\n/**\n * Base schema for image file collections\n * Extends base schema with thumbnail URL and image files array\n */\nexport const baseImageFileSchema = {\n ...baseSchema,\n thumbnailURL: z.string().optional(),\n imageFiles: z.array(z.string()),\n};\n\n/**\n * Input schema for image file uploads\n * Used in forms where users upload File objects\n * Requires Node.js 20+ or browser environment with File API\n */\nexport const inputImageFileSchema = {\n imageFiles: z.array(z.instanceof(File)),\n};\n\n/**\n * Helper constant for omitting image files from schemas\n * Used with Zod's .omit() method\n */\nexport const omitImageFilesSchema = {\n imageFiles: true,\n} as const;\n\n// ============================================================================\n// Common PocketBase Field Type Patterns\n// ============================================================================\n\n/**\n * Creates a text field schema with optional constraints\n * @param options - Optional constraints for the text field\n */\nexport function textField(options?: { min?: number; max?: number; pattern?: RegExp }) {\n let schema = z.string();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n if (options?.pattern !== undefined) schema = schema.regex(options.pattern);\n return schema;\n}\n\n/**\n * Creates an email field schema\n * Maps to PocketBase 'email' field type\n */\nexport function emailField() {\n return z.string().email();\n}\n\n/**\n * Creates a URL field schema\n * Maps to PocketBase 'url' field type\n */\nexport function urlField() {\n return z.string().url();\n}\n\n/**\n * Creates a number field schema with optional constraints\n * @param options - Optional constraints for the number field\n */\nexport function numberField(options?: { min?: number; max?: number }) {\n let schema = z.number();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates a boolean field schema\n * Maps to PocketBase 'bool' field type\n */\nexport function boolField() {\n return z.boolean();\n}\n\n/**\n * Creates a date field schema\n * Maps to PocketBase 'date' field type\n */\nexport function dateField() {\n return z.date();\n}\n\n/**\n * Creates a select field schema from enum values\n * Maps to PocketBase 'select' field type\n * @param values - Array of allowed string values\n */\nexport function selectField<T extends [string, ...string[]]>(values: T) {\n return z.enum(values);\n}\n\n/**\n * Creates a JSON field schema\n * Maps to PocketBase 'json' field type\n * @param schema - Optional Zod schema for the JSON structure\n */\nexport function jsonField<T extends z.ZodTypeAny>(schema?: T) {\n return schema ?? z.record(z.any());\n}\n\n/**\n * Creates a single file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect=1\n * Requires Node.js 20+ or browser environment with File API\n */\nexport function fileField() {\n return z.instanceof(File);\n}\n\n/**\n * Creates a multiple file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect>1\n * Requires Node.js 20+ or browser environment with File API\n * @param options - Optional constraints for the file field\n */\nexport function filesField(options?: { min?: number; max?: number }) {\n let schema = z.array(z.instanceof(File));\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates a single relation field schema\n * Maps to PocketBase 'relation' field type with maxSelect=1\n * Field name should start with uppercase to be detected as relation\n */\nexport function relationField() {\n return z.string();\n}\n\n/**\n * Creates a multiple relation field schema\n * Maps to PocketBase 'relation' field type with maxSelect>1\n * Field name should contain uppercase to be detected as relation\n * @param options - Optional constraints for the relation field\n */\nexport function relationsField(options?: { min?: number; max?: number }) {\n let schema = z.array(z.string());\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates an editor field schema (rich text)\n * Maps to PocketBase 'editor' field type\n */\nexport function editorField() {\n return z.string();\n}\n\n/**\n * Creates a geo point field schema\n * Maps to PocketBase 'geoPoint' field type\n */\nexport function geoPointField() {\n return z.object({\n lon: z.number(),\n lat: z.number(),\n });\n}\n\n/**\n * Attach permission metadata to a Zod schema\n *\n * This helper function allows you to define PocketBase API rules alongside your\n * entity schema definitions. The permissions are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach permissions to\n * @param config - Either a PermissionTemplateConfig (for template-based permissions)\n * or a PermissionSchema (for custom permissions)\n * @returns The schema with permission metadata attached\n *\n * @example\n * // Using a template\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * { template: 'owner-only', ownerField: 'User' }\n * );\n *\n * @example\n * // Using custom rules\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string() }),\n * { listRule: '@request.auth.id != \"\"', viewRule: '' }\n * );\n *\n * @example\n * // Using template with custom rule overrides\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * {\n * template: 'owner-only',\n * ownerField: 'User',\n * customRules: { listRule: '@request.auth.id != \"\"' }\n * }\n * );\n */\nexport function withPermissions<T extends z.ZodTypeAny>(\n schema: T,\n config: PermissionTemplateConfig | PermissionSchema\n): T {\n // Create metadata object with permissions config directly\n // The PermissionAnalyzer will handle resolving templates vs direct schemas\n const metadata = {\n permissions: config,\n };\n\n // Attach permission metadata to schema using Zod's describe() method\n // The metadata is serialized as JSON and stored in the schema's description\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n\n/**\n * Attach index definitions to a Zod schema\n *\n * This helper function allows you to define PocketBase indexes alongside your\n * entity schema definitions. The indexes are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach indexes to\n * @param indexes - Array of PocketBase index SQL statements\n * @returns The schema with index metadata attached\n *\n * @example\n * // Define indexes for a user schema\n * const UserSchema = withIndexes(\n * withPermissions(\n * z.object({ name: z.string(), email: z.string().email() }),\n * userPermissions\n * ),\n * [\n * 'CREATE UNIQUE INDEX idx_users_email ON users (email)',\n * 'CREATE INDEX idx_users_name ON users (name)'\n * ]\n * );\n *\n * @example\n * // Single index\n * const ProjectSchema = withIndexes(\n * ProjectDatabaseSchema,\n * ['CREATE INDEX idx_projects_status ON projects (status)']\n * );\n */\nexport function withIndexes<T extends z.ZodTypeAny>(schema: T, indexes: string[]): T {\n // Extract existing metadata if present\n let existingMetadata: any = {};\n\n if (schema.description) {\n try {\n existingMetadata = JSON.parse(schema.description);\n } catch {\n // If description is not JSON, ignore it\n }\n }\n\n // Merge indexes with existing metadata\n const metadata = {\n ...existingMetadata,\n indexes,\n };\n\n // Attach metadata to schema using Zod's describe() method\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n","import type { PermissionSchema, PermissionTemplateConfig, RuleExpression } from \"./permissions\";\n\n/**\n * Predefined permission templates for common access control patterns\n */\nexport const PermissionTemplates = {\n /**\n * Public access - anyone can perform all operations\n */\n public: (): PermissionSchema => ({\n listRule: \"\",\n viewRule: \"\",\n createRule: \"\",\n updateRule: \"\",\n deleteRule: \"\",\n }),\n\n /**\n * Authenticated users only - requires valid authentication for all operations\n */\n authenticated: (): PermissionSchema => ({\n listRule: '@request.auth.id != \"\"',\n viewRule: '@request.auth.id != \"\"',\n createRule: '@request.auth.id != \"\"',\n updateRule: '@request.auth.id != \"\"',\n deleteRule: '@request.auth.id != \"\"',\n }),\n\n /**\n * Owner-only access - users can only manage their own records\n * @param ownerField - Name of the relation field pointing to user (default: 'User')\n */\n ownerOnly: (ownerField: string = \"User\"): PermissionSchema => ({\n listRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n viewRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n createRule: '@request.auth.id != \"\"',\n updateRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n deleteRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n }),\n\n /**\n * Admin/superuser only access\n * Assumes a 'role' field exists with 'admin' value\n * @param roleField - Name of the role field (default: 'role')\n */\n adminOnly: (roleField: string = \"role\"): PermissionSchema => ({\n listRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n viewRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n createRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n updateRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n deleteRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n }),\n\n /**\n * Public read, authenticated write\n * Anyone can list/view, but only authenticated users can create/update/delete\n */\n readPublic: (): PermissionSchema => ({\n listRule: \"\",\n viewRule: \"\",\n createRule: '@request.auth.id != \"\"',\n updateRule: '@request.auth.id != \"\"',\n deleteRule: '@request.auth.id != \"\"',\n }),\n\n /**\n * Locked access - only superusers can perform operations\n * All rules are set to null (locked)\n */\n locked: (): PermissionSchema => ({\n listRule: null,\n viewRule: null,\n createRule: null,\n updateRule: null,\n deleteRule: null,\n }),\n\n /**\n * Read-only authenticated - authenticated users can read, no write access\n */\n readOnlyAuthenticated: (): PermissionSchema => ({\n listRule: '@request.auth.id != \"\"',\n viewRule: '@request.auth.id != \"\"',\n createRule: null,\n updateRule: null,\n deleteRule: null,\n }),\n};\n\n/**\n * Resolve template configuration to concrete permission schema\n * @param config - Template configuration or direct permission schema\n * @returns Resolved permission schema with all rules defined\n */\nexport function resolveTemplate(config: PermissionTemplateConfig): PermissionSchema {\n let baseRules: PermissionSchema;\n\n switch (config.template) {\n case \"public\":\n baseRules = PermissionTemplates.public();\n break;\n case \"authenticated\":\n baseRules = PermissionTemplates.authenticated();\n break;\n case \"owner-only\":\n baseRules = PermissionTemplates.ownerOnly(config.ownerField);\n break;\n case \"admin-only\":\n baseRules = PermissionTemplates.adminOnly(config.roleField);\n break;\n case \"read-public\":\n baseRules = PermissionTemplates.readPublic();\n break;\n case \"custom\":\n baseRules = {};\n break;\n default: {\n // Exhaustive check - TypeScript will error if we miss a template type\n const _exhaustive: never = config.template;\n throw new Error(`Unknown template type: ${_exhaustive}`);\n }\n }\n\n // Merge with custom rules if provided (custom rules override template rules)\n return {\n ...baseRules,\n ...config.customRules,\n };\n}\n\n/**\n * Check if a configuration is a template config or direct permission schema\n * @param config - Configuration to check\n * @returns True if it's a template configuration\n */\nexport function isTemplateConfig(\n config: PermissionTemplateConfig | PermissionSchema\n): config is PermissionTemplateConfig {\n return \"template\" in config;\n}\n\n/**\n * Check if a configuration is a direct permission schema\n * @param config - Configuration to check\n * @returns True if it's a direct permission schema\n */\nexport function isPermissionSchema(config: PermissionTemplateConfig | PermissionSchema): config is PermissionSchema {\n return (\n \"listRule\" in config ||\n \"viewRule\" in config ||\n \"createRule\" in config ||\n \"updateRule\" in config ||\n \"deleteRule\" in config ||\n \"manageRule\" in config\n );\n}\n\n/**\n * Validation result for permission configuration\n */\nexport interface PermissionValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n/**\n * Validate a permission configuration\n * @param config - Permission configuration to validate\n * @param isAuthCollection - Whether this is for an auth collection\n * @returns Validation result\n */\nexport function validatePermissionConfig(\n config: PermissionTemplateConfig | PermissionSchema,\n isAuthCollection: boolean = false\n): PermissionValidationResult {\n const result: PermissionValidationResult = {\n valid: true,\n errors: [],\n warnings: [],\n };\n\n // Resolve to permission schema\n let permissions: PermissionSchema;\n if (isTemplateConfig(config)) {\n // Validate template config\n if (config.template === \"owner-only\" && !config.ownerField) {\n result.warnings.push(\"owner-only template without ownerField specified - using default 'User'\");\n }\n if (config.template === \"admin-only\" && !config.roleField) {\n result.warnings.push(\"admin-only template without roleField specified - using default 'role'\");\n }\n permissions = resolveTemplate(config);\n } else {\n permissions = config;\n }\n\n // Validate manageRule usage\n if (permissions.manageRule !== undefined && !isAuthCollection) {\n result.errors.push(\"manageRule is only valid for auth collections\");\n result.valid = false;\n }\n\n // Validate rule expressions\n const ruleTypes: (keyof PermissionSchema)[] = [\"listRule\", \"viewRule\", \"createRule\", \"updateRule\", \"deleteRule\"];\n if (isAuthCollection) {\n ruleTypes.push(\"manageRule\");\n }\n\n for (const ruleType of ruleTypes) {\n const rule = permissions[ruleType];\n if (rule !== undefined && rule !== null && rule !== \"\") {\n const ruleValidation = validateRuleExpression(rule);\n if (!ruleValidation.valid) {\n result.errors.push(`${ruleType}: ${ruleValidation.errors.join(\", \")}`);\n result.valid = false;\n }\n result.warnings.push(...ruleValidation.warnings.map((w) => `${ruleType}: ${w}`));\n }\n }\n\n return result;\n}\n\n/**\n * Validate a single rule expression for basic syntax\n * @param expression - Rule expression to validate\n * @returns Validation result\n */\nexport function validateRuleExpression(expression: RuleExpression): PermissionValidationResult {\n const result: PermissionValidationResult = {\n valid: true,\n errors: [],\n warnings: [],\n };\n\n // Null and empty string are always valid\n if (expression === null || expression === \"\") {\n return result;\n }\n\n // Check for balanced parentheses\n let parenCount = 0;\n for (const char of expression) {\n if (char === \"(\") parenCount++;\n if (char === \")\") parenCount--;\n if (parenCount < 0) {\n result.errors.push(\"Unbalanced parentheses\");\n result.valid = false;\n return result;\n }\n }\n if (parenCount !== 0) {\n result.errors.push(\"Unbalanced parentheses\");\n result.valid = false;\n }\n\n // Check for common mistakes\n if (expression.includes(\"==\")) {\n result.warnings.push(\"Use '=' instead of '==' for equality comparison\");\n }\n\n // Check for valid @request references\n const requestRefs = expression.match(/@request\\.[a-zA-Z_][a-zA-Z0-9_.]*/g) || [];\n for (const ref of requestRefs) {\n const isValid =\n ref.startsWith(\"@request.auth.\") ||\n ref === \"@request.method\" ||\n ref === \"@request.context\" ||\n ref.startsWith(\"@request.body.\") ||\n ref.startsWith(\"@request.query.\") ||\n ref.startsWith(\"@request.headers.\");\n\n if (!isValid) {\n result.errors.push(`Invalid @request reference: '${ref}'`);\n result.valid = false;\n }\n }\n\n return result;\n}\n\n/**\n * Create a custom permission schema with type safety\n * @param permissions - Partial permission schema\n * @returns Complete permission schema with null defaults\n */\nexport function createPermissions(permissions: Partial<PermissionSchema>): PermissionSchema {\n return {\n listRule: permissions.listRule ?? null,\n viewRule: permissions.viewRule ?? null,\n createRule: permissions.createRule ?? null,\n updateRule: permissions.updateRule ?? null,\n deleteRule: permissions.deleteRule ?? null,\n manageRule: permissions.manageRule ?? null,\n };\n}\n\n/**\n * Merge multiple permission schemas, with later schemas taking precedence\n * @param schemas - Permission schemas to merge\n * @returns Merged permission schema\n */\nexport function mergePermissions(...schemas: Partial<PermissionSchema>[]): PermissionSchema {\n const merged: PermissionSchema = {\n listRule: null,\n viewRule: null,\n createRule: null,\n updateRule: null,\n deleteRule: null,\n manageRule: null,\n };\n\n for (const schema of schemas) {\n if (schema.listRule !== undefined) merged.listRule = schema.listRule;\n if (schema.viewRule !== undefined) merged.viewRule = schema.viewRule;\n if (schema.createRule !== undefined) merged.createRule = schema.createRule;\n if (schema.updateRule !== undefined) merged.updateRule = schema.updateRule;\n if (schema.deleteRule !== undefined) merged.deleteRule = schema.deleteRule;\n if (schema.manageRule !== undefined) merged.manageRule = schema.manageRule;\n }\n\n return merged;\n}\n","import { z } from \"zod\";\n\nexport const StatusEnum = z.enum([\n \"draft\", // Initial proposal stage (RequestDraft, ProjectDraft)\n \"active\", // Work in progress\n \"complete\", // Fully completed project\n \"fail\", // Failed project at any stage\n]);\nexport type StatusEnumType = z.infer<typeof StatusEnum>;\n","import { z } from \"zod\";\nimport { StatusEnum } from \"../enums\";\nimport { baseImageFileSchema, inputImageFileSchema, omitImageFilesSchema, withPermissions } from \"./base\";\n\nexport const ProjectInputSchema = z\n .object({\n // Required fields\n title: z.string(),\n content: z.string(),\n status: StatusEnum,\n summary: z.string().optional(),\n\n User: z.string().nonempty(\"User ID is missing\"),\n SubscriberUsers: z.array(z.string()),\n })\n .extend(inputImageFileSchema);\n\n// Apply permissions using template with custom overrides\n// Uses 'owner-only' template but allows all authenticated users to list projects\n// This allows users to see all projects but only manage their own\nexport const ProjectSchema = withPermissions(\n ProjectInputSchema.omit(omitImageFilesSchema).extend(baseImageFileSchema),\n {\n template: \"owner-only\",\n ownerField: \"User\",\n customRules: {\n // Override list rule to allow authenticated users to see all projects\n listRule: '@request.auth.id != \"\"',\n // Allow viewing if user is owner OR a subscriber\n viewRule: '@request.auth.id != \"\" && (User = @request.auth.id || SubscriberUsers ?= @request.auth.id)',\n },\n }\n);\n","import { z } from \"zod\";\nimport { baseSchema, withIndexes, withPermissions } from \"./base\";\n\n/** -- User Collections -- */\n// Input schema for forms (includes passwordConfirm for validation)\nexport const UserInputSchema = z.object({\n name: z.string().min(2, \"Name must be longer\").optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n passwordConfirm: z.string(),\n avatar: z.instanceof(File).optional(),\n});\n\n// Database schema (excludes passwordConfirm, includes avatar as file field)\nconst UserDatabaseSchema = z.object({\n name: z.string().min(2, \"Name must be longer\").optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n avatar: z.instanceof(File).optional(),\n});\n\n// Apply permissions and indexes for auth collection\n// Users can view all profiles, but only manage their own\nexport const UserSchema = withIndexes(\n withPermissions(UserDatabaseSchema.extend(baseSchema), {\n // Users can list other users (for mentions, user search, etc.)\n listRule: \"id = @request.auth.id\",\n // Users can view their own profile\n viewRule: \"id = @request.auth.id\",\n // Anyone can create an account (sign up)\n createRule: \"\",\n // Users can only update their own profile\n updateRule: \"id = @request.auth.id\",\n // Users can only delete their own account\n deleteRule: \"id = @request.auth.id\",\n // Users can only manage their own account (change email, password, etc.)\n manageRule: \"id = @request.auth.id\",\n }),\n [\n // Email should be unique for authentication\n \"CREATE UNIQUE INDEX idx_users_email ON users (email)\",\n // Index on name for user search and sorting\n \"CREATE INDEX idx_users_name ON users (name)\",\n ]\n);\n"]}
1
+ {"version":3,"sources":["../src/schema/base.ts","../src/utils/permission-templates.ts","../src/enums.ts","../src/schema/project.ts","../src/schema/user.ts"],"names":["z"],"mappings":"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","file":"schema.cjs","sourcesContent":["import { z } from \"zod\";\nimport type { PermissionSchema, PermissionTemplateConfig } from \"../utils/permissions\";\n\n/**\n * Base schema fields that PocketBase automatically adds to all records\n * These fields are managed by PocketBase and should not be set manually\n */\nexport const baseSchema = {\n id: z.string().describe(\"unique id\"),\n collectionId: z.string().describe(\"collection id\"),\n collectionName: z.string().describe(\"collection name\"),\n expand: z.record(z.any()).describe(\"expandable fields\"),\n};\n\n/**\n * Extended base schema with timestamp fields\n * Includes created and updated autodate fields\n */\nexport const baseSchemaWithTimestamps = {\n ...baseSchema,\n created: z.string().describe(\"creation timestamp\"),\n updated: z.string().describe(\"last update timestamp\"),\n};\n\n/**\n * Base schema for image file collections\n * Extends base schema with thumbnail URL and image files array\n */\nexport const baseImageFileSchema = {\n ...baseSchema,\n thumbnailURL: z.string().optional(),\n imageFiles: z.array(z.string()),\n};\n\n/**\n * Input schema for image file uploads\n * Used in forms where users upload File objects\n * Requires Node.js 20+ or browser environment with File API\n */\nexport const inputImageFileSchema = {\n imageFiles: z.array(z.instanceof(File)),\n};\n\n/**\n * Helper constant for omitting image files from schemas\n * Used with Zod's .omit() method\n */\nexport const omitImageFilesSchema = {\n imageFiles: true,\n} as const;\n\n// ============================================================================\n// Common PocketBase Field Type Patterns\n// ============================================================================\n\n/**\n * Creates a text field schema with optional constraints\n * @param options - Optional constraints for the text field\n */\nexport function textField(options?: { min?: number; max?: number; pattern?: RegExp }) {\n let schema = z.string();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n if (options?.pattern !== undefined) schema = schema.regex(options.pattern);\n return schema;\n}\n\n/**\n * Creates an email field schema\n * Maps to PocketBase 'email' field type\n */\nexport function emailField() {\n return z.string().email();\n}\n\n/**\n * Creates a URL field schema\n * Maps to PocketBase 'url' field type\n */\nexport function urlField() {\n return z.string().url();\n}\n\n/**\n * Creates a number field schema with optional constraints\n * @param options - Optional constraints for the number field\n */\nexport function numberField(options?: { min?: number; max?: number }) {\n let schema = z.number();\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n/**\n * Creates a boolean field schema\n * Maps to PocketBase 'bool' field type\n */\nexport function boolField() {\n return z.boolean();\n}\n\n/**\n * Creates a date field schema\n * Maps to PocketBase 'date' field type\n */\nexport function dateField() {\n return z.date();\n}\n\n/**\n * Creates a select field schema from enum values\n * Maps to PocketBase 'select' field type\n * @param values - Array of allowed string values\n */\nexport function selectField<T extends [string, ...string[]]>(values: T) {\n return z.enum(values);\n}\n\n/**\n * Creates a JSON field schema\n * Maps to PocketBase 'json' field type\n * @param schema - Optional Zod schema for the JSON structure\n */\nexport function jsonField<T extends z.ZodTypeAny>(schema?: T) {\n return schema ?? z.record(z.any());\n}\n\n/**\n * Creates a single file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect=1\n * Requires Node.js 20+ or browser environment with File API\n */\nexport function fileField() {\n return z.instanceof(File);\n}\n\n/**\n * Creates a multiple file field schema for form input\n * Maps to PocketBase 'file' field type with maxSelect>1\n * Requires Node.js 20+ or browser environment with File API\n * @param options - Optional constraints for the file field\n */\nexport function filesField(options?: { min?: number; max?: number }) {\n let schema = z.array(z.instanceof(File));\n if (options?.min !== undefined) schema = schema.min(options.min);\n if (options?.max !== undefined) schema = schema.max(options.max);\n return schema;\n}\n\n// ============================================================================\n// Relation Field Helpers - Explicit Relationship Definitions\n// ============================================================================\n\n/**\n * Relation field configuration options\n */\nexport interface RelationConfig {\n /**\n * Target collection name (e.g., 'users', 'posts', 'tags')\n * This is the PocketBase collection that the relation points to\n */\n collection: string;\n\n /**\n * Whether to cascade delete related records when this record is deleted\n * @default false\n */\n cascadeDelete?: boolean;\n}\n\n/**\n * Multiple relation field configuration options\n */\nexport interface RelationsConfig extends RelationConfig {\n /**\n * Minimum number of relations required\n * @default 0\n */\n minSelect?: number;\n\n /**\n * Maximum number of relations allowed\n * @default 999\n */\n maxSelect?: number;\n}\n\n/**\n * Internal marker for relation metadata\n * Used by the analyzer to detect explicit relation definitions\n */\nconst RELATION_METADATA_KEY = \"__pocketbase_relation__\";\n\n/**\n * Creates a single relation field schema with explicit collection target\n * Maps to PocketBase 'relation' field type with maxSelect=1\n *\n * This is the recommended way to define relations - it's explicit and doesn't\n * rely on naming conventions.\n *\n * @param config - Relation configuration with target collection\n * @returns Zod string schema with relation metadata\n *\n * @example\n * // Single relation to users collection\n * const PostSchema = z.object({\n * title: z.string(),\n * author: relationField({ collection: 'users' }),\n * });\n *\n * @example\n * // Relation with cascade delete\n * const CommentSchema = z.object({\n * content: z.string(),\n * post: relationField({ collection: 'posts', cascadeDelete: true }),\n * });\n */\nexport function relationField(config: RelationConfig) {\n const metadata = {\n [RELATION_METADATA_KEY]: {\n type: \"single\",\n collection: config.collection,\n cascadeDelete: config.cascadeDelete ?? false,\n maxSelect: 1,\n minSelect: 0,\n },\n };\n\n return z.string().describe(JSON.stringify(metadata));\n}\n\n/**\n * Creates a multiple relation field schema with explicit collection target\n * Maps to PocketBase 'relation' field type with maxSelect>1\n *\n * This is the recommended way to define multi-relations - it's explicit and\n * doesn't rely on naming conventions.\n *\n * @param config - Relations configuration with target collection and limits\n * @returns Zod array of strings schema with relation metadata\n *\n * @example\n * // Multiple relations to tags collection\n * const PostSchema = z.object({\n * title: z.string(),\n * tags: relationsField({ collection: 'tags' }),\n * });\n *\n * @example\n * // Relations with min/max constraints\n * const ProjectSchema = z.object({\n * title: z.string(),\n * collaborators: relationsField({\n * collection: 'users',\n * minSelect: 1,\n * maxSelect: 10,\n * }),\n * });\n */\nexport function relationsField(config: RelationsConfig) {\n const metadata = {\n [RELATION_METADATA_KEY]: {\n type: \"multiple\",\n collection: config.collection,\n cascadeDelete: config.cascadeDelete ?? false,\n maxSelect: config.maxSelect ?? 999,\n minSelect: config.minSelect ?? 0,\n },\n };\n\n let schema = z.array(z.string());\n\n // Apply array constraints for Zod validation\n if (config.minSelect !== undefined) {\n schema = schema.min(config.minSelect);\n }\n if (config.maxSelect !== undefined) {\n schema = schema.max(config.maxSelect);\n }\n\n return schema.describe(JSON.stringify(metadata));\n}\n\n/**\n * Extracts relation metadata from a Zod type's description\n * Used internally by the analyzer to detect explicit relation definitions\n *\n * @param description - The Zod type's description string\n * @returns Relation metadata if present, null otherwise\n */\nexport function extractRelationMetadata(description: string | undefined): {\n type: \"single\" | \"multiple\";\n collection: string;\n cascadeDelete: boolean;\n maxSelect: number;\n minSelect: number;\n} | null {\n if (!description) return null;\n\n try {\n const parsed = JSON.parse(description);\n if (parsed[RELATION_METADATA_KEY]) {\n return parsed[RELATION_METADATA_KEY];\n }\n } catch {\n // Not JSON, ignore\n }\n\n return null;\n}\n\n/**\n * Creates an editor field schema (rich text)\n * Maps to PocketBase 'editor' field type\n */\nexport function editorField() {\n return z.string();\n}\n\n/**\n * Creates a geo point field schema\n * Maps to PocketBase 'geoPoint' field type\n */\nexport function geoPointField() {\n return z.object({\n lon: z.number(),\n lat: z.number(),\n });\n}\n\n/**\n * Attach permission metadata to a Zod schema\n *\n * This helper function allows you to define PocketBase API rules alongside your\n * entity schema definitions. The permissions are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach permissions to\n * @param config - Either a PermissionTemplateConfig (for template-based permissions)\n * or a PermissionSchema (for custom permissions)\n * @returns The schema with permission metadata attached\n *\n * @example\n * // Using a template\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * { template: 'owner-only', ownerField: 'User' }\n * );\n *\n * @example\n * // Using custom rules\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string() }),\n * { listRule: '@request.auth.id != \"\"', viewRule: '' }\n * );\n *\n * @example\n * // Using template with custom rule overrides\n * const ProjectSchema = withPermissions(\n * z.object({ title: z.string(), User: z.string() }),\n * {\n * template: 'owner-only',\n * ownerField: 'User',\n * customRules: { listRule: '@request.auth.id != \"\"' }\n * }\n * );\n */\nexport function withPermissions<T extends z.ZodTypeAny>(\n schema: T,\n config: PermissionTemplateConfig | PermissionSchema\n): T {\n // Create metadata object with permissions config directly\n // The PermissionAnalyzer will handle resolving templates vs direct schemas\n const metadata = {\n permissions: config,\n };\n\n // Attach permission metadata to schema using Zod's describe() method\n // The metadata is serialized as JSON and stored in the schema's description\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n\n/**\n * Attach index definitions to a Zod schema\n *\n * This helper function allows you to define PocketBase indexes alongside your\n * entity schema definitions. The indexes are stored as metadata using Zod's\n * describe() method and will be extracted during migration generation.\n *\n * @param schema - The Zod schema to attach indexes to\n * @param indexes - Array of PocketBase index SQL statements\n * @returns The schema with index metadata attached\n *\n * @example\n * // Define indexes for a user schema\n * const UserSchema = withIndexes(\n * withPermissions(\n * z.object({ name: z.string(), email: z.string().email() }),\n * userPermissions\n * ),\n * [\n * 'CREATE UNIQUE INDEX idx_users_email ON users (email)',\n * 'CREATE INDEX idx_users_name ON users (name)'\n * ]\n * );\n *\n * @example\n * // Single index\n * const ProjectSchema = withIndexes(\n * ProjectDatabaseSchema,\n * ['CREATE INDEX idx_projects_status ON projects (status)']\n * );\n */\nexport function withIndexes<T extends z.ZodTypeAny>(schema: T, indexes: string[]): T {\n // Extract existing metadata if present\n let existingMetadata: any = {};\n\n if (schema.description) {\n try {\n existingMetadata = JSON.parse(schema.description);\n } catch {\n // If description is not JSON, ignore it\n }\n }\n\n // Merge indexes with existing metadata\n const metadata = {\n ...existingMetadata,\n indexes,\n };\n\n // Attach metadata to schema using Zod's describe() method\n return schema.describe(JSON.stringify(metadata)) as T;\n}\n","import type { PermissionSchema, PermissionTemplateConfig, RuleExpression } from \"../utils/permissions\";\n\n/**\n * Predefined permission templates for common access control patterns\n */\nexport const PermissionTemplates = {\n /**\n * Public access - anyone can perform all operations\n */\n public: (): PermissionSchema => ({\n listRule: \"\",\n viewRule: \"\",\n createRule: \"\",\n updateRule: \"\",\n deleteRule: \"\",\n }),\n\n /**\n * Authenticated users only - requires valid authentication for all operations\n */\n authenticated: (): PermissionSchema => ({\n listRule: '@request.auth.id != \"\"',\n viewRule: '@request.auth.id != \"\"',\n createRule: '@request.auth.id != \"\"',\n updateRule: '@request.auth.id != \"\"',\n deleteRule: '@request.auth.id != \"\"',\n }),\n\n /**\n * Owner-only access - users can only manage their own records\n * @param ownerField - Name of the relation field pointing to user (default: 'User')\n */\n ownerOnly: (ownerField: string = \"User\"): PermissionSchema => ({\n listRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n viewRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n createRule: '@request.auth.id != \"\"',\n updateRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n deleteRule: `@request.auth.id != \"\" && ${ownerField} = @request.auth.id`,\n }),\n\n /**\n * Admin/superuser only access\n * Assumes a 'role' field exists with 'admin' value\n * @param roleField - Name of the role field (default: 'role')\n */\n adminOnly: (roleField: string = \"role\"): PermissionSchema => ({\n listRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n viewRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n createRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n updateRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n deleteRule: `@request.auth.id != \"\" && @request.auth.${roleField} = \"admin\"`,\n }),\n\n /**\n * Public read, authenticated write\n * Anyone can list/view, but only authenticated users can create/update/delete\n */\n readPublic: (): PermissionSchema => ({\n listRule: \"\",\n viewRule: \"\",\n createRule: '@request.auth.id != \"\"',\n updateRule: '@request.auth.id != \"\"',\n deleteRule: '@request.auth.id != \"\"',\n }),\n\n /**\n * Locked access - only superusers can perform operations\n * All rules are set to null (locked)\n */\n locked: (): PermissionSchema => ({\n listRule: null,\n viewRule: null,\n createRule: null,\n updateRule: null,\n deleteRule: null,\n }),\n\n /**\n * Read-only authenticated - authenticated users can read, no write access\n */\n readOnlyAuthenticated: (): PermissionSchema => ({\n listRule: '@request.auth.id != \"\"',\n viewRule: '@request.auth.id != \"\"',\n createRule: null,\n updateRule: null,\n deleteRule: null,\n }),\n};\n\n/**\n * Resolve template configuration to concrete permission schema\n * @param config - Template configuration or direct permission schema\n * @returns Resolved permission schema with all rules defined\n */\nexport function resolveTemplate(config: PermissionTemplateConfig): PermissionSchema {\n let baseRules: PermissionSchema;\n\n switch (config.template) {\n case \"public\":\n baseRules = PermissionTemplates.public();\n break;\n case \"authenticated\":\n baseRules = PermissionTemplates.authenticated();\n break;\n case \"owner-only\":\n baseRules = PermissionTemplates.ownerOnly(config.ownerField);\n break;\n case \"admin-only\":\n baseRules = PermissionTemplates.adminOnly(config.roleField);\n break;\n case \"read-public\":\n baseRules = PermissionTemplates.readPublic();\n break;\n case \"custom\":\n baseRules = {};\n break;\n default: {\n // Exhaustive check - TypeScript will error if we miss a template type\n const _exhaustive: never = config.template;\n throw new Error(`Unknown template type: ${_exhaustive}`);\n }\n }\n\n // Merge with custom rules if provided (custom rules override template rules)\n return {\n ...baseRules,\n ...config.customRules,\n };\n}\n\n/**\n * Check if a configuration is a template config or direct permission schema\n * @param config - Configuration to check\n * @returns True if it's a template configuration\n */\nexport function isTemplateConfig(\n config: PermissionTemplateConfig | PermissionSchema\n): config is PermissionTemplateConfig {\n return \"template\" in config;\n}\n\n/**\n * Check if a configuration is a direct permission schema\n * @param config - Configuration to check\n * @returns True if it's a direct permission schema\n */\nexport function isPermissionSchema(config: PermissionTemplateConfig | PermissionSchema): config is PermissionSchema {\n return (\n \"listRule\" in config ||\n \"viewRule\" in config ||\n \"createRule\" in config ||\n \"updateRule\" in config ||\n \"deleteRule\" in config ||\n \"manageRule\" in config\n );\n}\n\n/**\n * Validation result for permission configuration\n */\nexport interface PermissionValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n/**\n * Validate a permission configuration\n * @param config - Permission configuration to validate\n * @param isAuthCollection - Whether this is for an auth collection\n * @returns Validation result\n */\nexport function validatePermissionConfig(\n config: PermissionTemplateConfig | PermissionSchema,\n isAuthCollection: boolean = false\n): PermissionValidationResult {\n const result: PermissionValidationResult = {\n valid: true,\n errors: [],\n warnings: [],\n };\n\n // Resolve to permission schema\n let permissions: PermissionSchema;\n if (isTemplateConfig(config)) {\n // Validate template config\n if (config.template === \"owner-only\" && !config.ownerField) {\n result.warnings.push(\"owner-only template without ownerField specified - using default 'User'\");\n }\n if (config.template === \"admin-only\" && !config.roleField) {\n result.warnings.push(\"admin-only template without roleField specified - using default 'role'\");\n }\n permissions = resolveTemplate(config);\n } else {\n permissions = config;\n }\n\n // Validate manageRule usage\n if (permissions.manageRule !== undefined && !isAuthCollection) {\n result.errors.push(\"manageRule is only valid for auth collections\");\n result.valid = false;\n }\n\n // Validate rule expressions\n const ruleTypes: (keyof PermissionSchema)[] = [\"listRule\", \"viewRule\", \"createRule\", \"updateRule\", \"deleteRule\"];\n if (isAuthCollection) {\n ruleTypes.push(\"manageRule\");\n }\n\n for (const ruleType of ruleTypes) {\n const rule = permissions[ruleType];\n if (rule !== undefined && rule !== null && rule !== \"\") {\n const ruleValidation = validateRuleExpression(rule);\n if (!ruleValidation.valid) {\n result.errors.push(`${ruleType}: ${ruleValidation.errors.join(\", \")}`);\n result.valid = false;\n }\n result.warnings.push(...ruleValidation.warnings.map((w) => `${ruleType}: ${w}`));\n }\n }\n\n return result;\n}\n\n/**\n * Validate a single rule expression for basic syntax\n * @param expression - Rule expression to validate\n * @returns Validation result\n */\nexport function validateRuleExpression(expression: RuleExpression): PermissionValidationResult {\n const result: PermissionValidationResult = {\n valid: true,\n errors: [],\n warnings: [],\n };\n\n // Null and empty string are always valid\n if (expression === null || expression === \"\") {\n return result;\n }\n\n // Check for balanced parentheses\n let parenCount = 0;\n for (const char of expression) {\n if (char === \"(\") parenCount++;\n if (char === \")\") parenCount--;\n if (parenCount < 0) {\n result.errors.push(\"Unbalanced parentheses\");\n result.valid = false;\n return result;\n }\n }\n if (parenCount !== 0) {\n result.errors.push(\"Unbalanced parentheses\");\n result.valid = false;\n }\n\n // Check for common mistakes\n if (expression.includes(\"==\")) {\n result.warnings.push(\"Use '=' instead of '==' for equality comparison\");\n }\n\n // Check for valid @request references\n const requestRefs = expression.match(/@request\\.[a-zA-Z_][a-zA-Z0-9_.]*/g) || [];\n for (const ref of requestRefs) {\n const isValid =\n ref.startsWith(\"@request.auth.\") ||\n ref === \"@request.method\" ||\n ref === \"@request.context\" ||\n ref.startsWith(\"@request.body.\") ||\n ref.startsWith(\"@request.query.\") ||\n ref.startsWith(\"@request.headers.\");\n\n if (!isValid) {\n result.errors.push(`Invalid @request reference: '${ref}'`);\n result.valid = false;\n }\n }\n\n return result;\n}\n\n/**\n * Create a custom permission schema with type safety\n * @param permissions - Partial permission schema\n * @returns Complete permission schema with null defaults\n */\nexport function createPermissions(permissions: Partial<PermissionSchema>): PermissionSchema {\n return {\n listRule: permissions.listRule ?? null,\n viewRule: permissions.viewRule ?? null,\n createRule: permissions.createRule ?? null,\n updateRule: permissions.updateRule ?? null,\n deleteRule: permissions.deleteRule ?? null,\n manageRule: permissions.manageRule ?? null,\n };\n}\n\n/**\n * Merge multiple permission schemas, with later schemas taking precedence\n * @param schemas - Permission schemas to merge\n * @returns Merged permission schema\n */\nexport function mergePermissions(...schemas: Partial<PermissionSchema>[]): PermissionSchema {\n const merged: PermissionSchema = {\n listRule: null,\n viewRule: null,\n createRule: null,\n updateRule: null,\n deleteRule: null,\n manageRule: null,\n };\n\n for (const schema of schemas) {\n if (schema.listRule !== undefined) merged.listRule = schema.listRule;\n if (schema.viewRule !== undefined) merged.viewRule = schema.viewRule;\n if (schema.createRule !== undefined) merged.createRule = schema.createRule;\n if (schema.updateRule !== undefined) merged.updateRule = schema.updateRule;\n if (schema.deleteRule !== undefined) merged.deleteRule = schema.deleteRule;\n if (schema.manageRule !== undefined) merged.manageRule = schema.manageRule;\n }\n\n return merged;\n}\n","import { z } from \"zod\";\n\nexport const StatusEnum = z.enum([\n \"draft\", // Initial proposal stage (RequestDraft, ProjectDraft)\n \"active\", // Work in progress\n \"complete\", // Fully completed project\n \"fail\", // Failed project at any stage\n]);\nexport type StatusEnumType = z.infer<typeof StatusEnum>;\n","import { z } from \"zod\";\nimport { StatusEnum } from \"../enums\";\nimport {\n baseImageFileSchema,\n inputImageFileSchema,\n omitImageFilesSchema,\n relationField,\n relationsField,\n withPermissions,\n} from \"./base\";\n\nexport const ProjectInputSchema = z\n .object({\n // Required fields\n title: z.string(),\n content: z.string(),\n status: StatusEnum,\n summary: z.string().optional(),\n\n OwnerUser: relationField({ collection: \"Users\" }),\n SubscriberUsers: relationsField({ collection: \"Users\" }),\n })\n .extend(inputImageFileSchema);\n\n// Apply permissions using template with custom overrides\n// Uses 'owner-only' template but allows all authenticated users to list projects\n// This allows users to see all projects but only manage their own\nexport const ProjectSchema = withPermissions(\n ProjectInputSchema.omit(omitImageFilesSchema).extend(baseImageFileSchema),\n {\n template: \"owner-only\",\n ownerField: \"OwnerUser\",\n customRules: {\n listRule: '@request.auth.id != \"\"',\n viewRule: '@request.auth.id != \"\" && (OwnerUser = @request.auth.id || SubscriberUsers ?= @request.auth.id)',\n },\n }\n);\n","import { z } from \"zod\";\nimport { baseSchema, withIndexes, withPermissions } from \"./base\";\n\n/** -- User Collections -- */\n// Input schema for forms (includes passwordConfirm for validation)\nexport const UserInputSchema = z.object({\n name: z.string().optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n passwordConfirm: z.string(),\n avatar: z.instanceof(File).optional(),\n});\n\n// Database schema (excludes passwordConfirm, includes avatar as file field)\n// Matches PocketBase's default users auth collection structure\n// Note: PocketBase has min: 0, max: 255 for name, but our snapshot loader\n// doesn't extract field-level options properly yet (TODO: fix snapshot loader)\nconst UserDatabaseSchema = z.object({\n name: z.string().optional(),\n email: z.string().email(),\n password: z.string().min(8, \"Password must be at least 8 characters\"),\n avatar: z.instanceof(File).optional(),\n});\n\n// Apply permissions and indexes for auth collection\n// Matches PocketBase's default users collection configuration\nexport const UserSchema = withIndexes(\n withPermissions(UserDatabaseSchema.extend(baseSchema), {\n // Users can list their own profile\n listRule: \"id = @request.auth.id\",\n // Users can view their own profile\n viewRule: \"id = @request.auth.id\",\n // Anyone can create an account (sign up)\n createRule: \"\",\n // Users can only update their own profile\n updateRule: \"id = @request.auth.id\",\n // Users can only delete their own account\n deleteRule: \"id = @request.auth.id\",\n // manageRule is null in PocketBase default (not set)\n }),\n [\n // PocketBase's default indexes for auth collections\n \"CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)\",\n \"CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''\",\n ]\n);\n"]}