import { z } from 'zod'; /** * Permission schema types and interfaces for PocketBase API rules * * This module defines the core types for managing collection-level permissions * that control access to list, view, create, update, delete, and manage operations. */ /** * PocketBase API rule types * * Each rule type corresponds to a specific API operation: * - listRule: Controls who can list/query records * - viewRule: Controls who can view individual records * - createRule: Controls who can create new records * - updateRule: Controls who can update existing records * - deleteRule: Controls who can delete records * - manageRule: Controls who can manage auth records (auth collections only) */ type APIRuleType = "listRule" | "viewRule" | "createRule" | "updateRule" | "deleteRule" | "manageRule"; /** * Rule expression - can be null (locked), empty string (public), or filter expression * * - null: Locked to superusers only * - "" (empty string): Public access - anyone can perform the operation * - string: Filter expression using PocketBase syntax (e.g., "@request.auth.id != ''") */ type RuleExpression = string | null; /** * Permission schema definition * * Defines the complete set of API rules for a collection. * All fields are optional - undefined rules will default to null (locked). */ interface PermissionSchema { listRule?: RuleExpression; viewRule?: RuleExpression; createRule?: RuleExpression; updateRule?: RuleExpression; deleteRule?: RuleExpression; manageRule?: RuleExpression; } /** * Permission template types for common patterns * * Predefined templates that generate standard permission configurations: * - public: All operations are publicly accessible * - authenticated: Requires user authentication for all operations * - owner-only: Users can only manage their own records * - admin-only: Only admin/superusers can perform operations * - read-public: Public read access, authenticated write access * - custom: Fully custom rules defined by the developer */ type PermissionTemplate = "public" | "authenticated" | "owner-only" | "admin-only" | "read-public" | "custom"; /** * Template configuration * * Configuration object for applying permission templates with customization options. * Allows templates to be parameterized (e.g., specifying the owner field name) * and overridden with custom rules. */ interface PermissionTemplateConfig { /** The template to apply */ template: PermissionTemplate; /** Field name for owner relation (default: 'User') - used with 'owner-only' template */ ownerField?: string; /** Field name for role checking - used with 'admin-only' template */ roleField?: string; /** Custom rules that override template-generated rules */ customRules?: Partial; } /** * Base schema fields that PocketBase automatically adds to all records * These fields are managed by PocketBase and should not be set manually */ declare const baseSchema: { id: z.ZodString; collectionId: z.ZodString; collectionName: z.ZodString; expand: z.ZodRecord; created: z.ZodString; updated: z.ZodString; }; /** * Relation field configuration options */ interface RelationConfig { /** * Target collection name (e.g., 'users', 'posts', 'tags') * This is the PocketBase collection that the relation points to */ collection: string; /** * Whether to cascade delete related records when this record is deleted * @default false */ cascadeDelete?: boolean; /** * Fields to display in the admin UI */ displayFields?: string[] | null; } /** * Multiple relation field configuration options */ interface RelationsConfig extends RelationConfig { /** * Minimum number of relations required * @default 0 */ minSelect?: number; /** * Maximum number of relations allowed * @default 999 */ maxSelect?: number; } /** * Creates a single relation field schema with explicit collection target * Maps to PocketBase 'relation' field type with maxSelect=1 * * This is the recommended way to define relations - it's explicit and doesn't * rely on naming conventions. * * @param config - Relation configuration with target collection * @returns Zod string schema with relation metadata * * @example * // Single relation to users collection * const PostSchema = z.object({ * title: z.string(), * author: RelationField({ collection: 'users' }), * }); * * @example * // Relation with cascade delete * const CommentSchema = z.object({ * content: z.string(), * post: RelationField({ collection: 'posts', cascadeDelete: true }), * }); */ declare function RelationField(config: RelationConfig): z.ZodString; /** * Creates a multiple relation field schema with explicit collection target * Maps to PocketBase 'relation' field type with maxSelect>1 * * This is the recommended way to define multi-relations - it's explicit and * doesn't rely on naming conventions. * * @param config - Relations configuration with target collection and limits * @returns Zod array of strings schema with relation metadata * * @example * // Multiple relations to tags collection * const PostSchema = z.object({ * title: z.string(), * tags: RelationsField({ collection: 'tags' }), * }); * * @example * // Relations with min/max constraints * const ProjectSchema = z.object({ * title: z.string(), * collaborators: RelationsField({ * collection: 'users', * minSelect: 1, * maxSelect: 10, * }), * }); */ declare function RelationsField(config: RelationsConfig): z.ZodArray; /** * Extracts relation metadata from a Zod type's description * Used internally by the analyzer to detect explicit relation definitions * * @param description - The Zod type's description string * @returns Relation metadata if present, null otherwise */ declare function extractRelationMetadata(description: string | undefined): { type: "single" | "multiple"; collection: string; cascadeDelete: boolean; maxSelect: number; minSelect: number; displayFields?: string[] | null; } | null; /** * Configuration options for defining a collection */ interface CollectionConfig { /** * The name of the PocketBase collection * This will be used when generating migrations */ collectionName: string; /** * The Zod schema definition for the collection */ schema: z.ZodObject; /** * Optional permission configuration * Can be a template-based config or custom permission rules */ permissions?: PermissionTemplateConfig | PermissionSchema; /** * Optional array of index SQL statements * Example: ['CREATE UNIQUE INDEX idx_users_email ON users (email)'] */ indexes?: string[]; /** * Optional collection type * - "base": Standard collection (default) * - "auth": Authentication collection with system auth fields * - "view": Read-only collection backed by a SQL query (requires viewQuery) * * Defaults to "base". Auth collections must set type: "auth" explicitly. * * Prefer defineView() over type: "view" - it enforces the constraints * PocketBase places on view collections at compile time. */ type?: "base" | "auth" | "view"; /** * SQL SELECT statement backing a view collection * * Only valid when type is "view", where it is required. PocketBase derives * the collection's fields by running this query, so the Zod schema is used * for TypeScript types only. */ viewQuery?: string; } /** * High-level wrapper for defining a PocketBase collection with all metadata * * This is the recommended way to define collections as it provides a single * entry point for collection name, schema, permissions, indexes, and future features. * * @param config - Collection configuration object * @returns The schema with all metadata attached * * @example * // Recommended: Use default export for clarity * const PostCollection = defineCollection({ * collectionName: "posts", * schema: z.object({ * title: z.string(), * content: z.string(), * author: RelationField({ collection: "users" }), * }), * permissions: { * template: "owner-only", * ownerField: "author", * }, * }); * export default PostCollection; * * @example * // Also supported: Named export (backward compatible) * export const PostCollection = defineCollection({ * collectionName: "posts", * schema: z.object({ * title: z.string(), * content: z.string(), * author: RelationField({ collection: "users" }), * }), * permissions: { * template: "owner-only", * ownerField: "author", * }, * }); * * @example * // Collection with permissions and indexes * export const UserSchema = defineCollection({ * collectionName: "users", * schema: z.object({ * name: z.string(), * email: z.string().email(), * }), * permissions: { * listRule: "id = @request.auth.id", * viewRule: "id = @request.auth.id", * createRule: "", * updateRule: "id = @request.auth.id", * deleteRule: "id = @request.auth.id", * }, * indexes: [ * "CREATE UNIQUE INDEX idx_users_email ON users (email)", * ], * }); * * @example * // Collection with template and custom rule overrides * export const ProjectSchema = defineCollection({ * collectionName: "projects", * schema: z.object({ * title: z.string(), * owner: RelationField({ collection: "users" }), * }), * permissions: { * template: "owner-only", * ownerField: "owner", * customRules: { * listRule: '@request.auth.id != ""', * }, * }, * }); */ declare function defineCollection(config: CollectionConfig): z.ZodObject; /** * Internal marker for field metadata * Used by the migration generator to detect explicit field type definitions */ declare const FIELD_METADATA_KEY = "__pocketbase_field__"; /** * PocketBase field types */ type PocketBaseFieldType = "text" | "email" | "url" | "editor" | "number" | "bool" | "date" | "autodate" | "select" | "relation" | "file" | "json" | "geoPoint" | "password"; /** * Field metadata structure embedded in Zod schema descriptions */ interface FieldMetadata { type: PocketBaseFieldType; options?: Record; } /** * Extracts field metadata from a Zod type's description * Used by the migration generator to detect explicit field type definitions * * @param description - The Zod type's description string * @returns Field metadata if present, null otherwise * * @example * const schema = TextField({ min: 1, max: 100 }); * const metadata = extractFieldMetadata(schema.description); * // Returns: { type: "text", options: { min: 1, max: 100 } } */ declare function extractFieldMetadata(description: string | undefined): FieldMetadata | null; /** * Text field configuration options */ interface TextFieldOptions { /** * Minimum length constraint */ min?: number; /** * Maximum length constraint */ max?: number; /** * Pattern constraint (regex) */ pattern?: RegExp | string; /** * Auto-generate pattern for automatic value generation * Example: "[A-Z]{3}-[0-9]{6}" generates values like "ABC-123456" */ autogeneratePattern?: string; } /** * Number field configuration options */ interface NumberFieldOptions { /** * Minimum value constraint */ min?: number; /** * Maximum value constraint */ max?: number; /** * Whether to disallow decimal values (integers only) */ noDecimal?: boolean; /** * Whether the field is required * @default false * * Note: In PocketBase, `required: true` for number fields means the value must be non-zero. * If you want to allow zero values (e.g., for progress: 0-100), keep this as `false`. * Set to `true` only if you want to enforce non-zero values. */ required?: boolean; } /** * Date field configuration options */ interface DateFieldOptions { /** * Minimum date constraint */ min?: Date | string; /** * Maximum date constraint */ max?: Date | string; } /** * Autodate field configuration options */ interface AutodateFieldOptions { /** * Set date automatically on record creation * @default false */ onCreate?: boolean; /** * Update date automatically on record update * @default false */ onUpdate?: boolean; } /** * Select field configuration options */ interface SelectFieldOptions { /** * Maximum number of selections allowed * If > 1, enables multiple selection * @default 1 */ maxSelect?: number; } type EnumFromArray = z.ZodEnum>; /** * Human-friendly byte size input. * * - Use a number for raw bytes (e.g. `5242880`) * - Use a string with unit suffix for kibibytes/mebibytes/gibibytes (e.g. `"5M"`, `"1G"`) * * Supported suffixes: `K`, `M`, `G` (case-insensitive). */ type ByteSize = number | `${number}${"K" | "M" | "G" | "k" | "m" | "g"}`; /** * File field configuration options */ interface FileFieldOptions { /** * Allowed MIME types * Example: ["image/*", "application/pdf"] */ mimeTypes?: string[]; /** * Maximum file size. * * - Provide a number for raw bytes * - Or use a string with `K`, `M`, `G` suffix (case-insensitive) * * Max allowed is `8G`. * * @example * maxSize: 5242880 * maxSize: "5M" * maxSize: "1G" */ maxSize?: ByteSize; /** * Thumbnail sizes to generate * Example: ["100x100", "200x200"] * Set to null to explicitly disable thumbnails */ thumbs?: string[]; /** * Whether the file is protected (requires auth to access) * @default false */ protected?: boolean; } /** * Multiple files field configuration options */ interface FilesFieldOptions extends FileFieldOptions { /** * Minimum number of files required */ minSelect?: number; /** * Maximum number of files allowed */ maxSelect?: number; } /** * JSON field configuration options */ interface JSONFieldOptions { /** * Maximum size of the serialized JSON value. * * - Provide a number for raw bytes * - Or use a string with `K`, `M`, `G` suffix (case-insensitive) * * PocketBase applies a **1MB** default when this is unset (or `0`), so a field * holding anything larger has to declare its own limit. The maximum PocketBase * accepts is 2^53-1 bytes. * * @example * maxSize: 5242880 * maxSize: "5M" * maxSize: "200K" */ maxSize?: ByteSize; } /** * Creates a boolean field schema * Maps to PocketBase 'bool' field type * * @returns Zod boolean schema with PocketBase metadata * * @example * const ProductSchema = z.object({ * active: BoolField(), * featured: BoolField().optional(), * }); */ declare function BoolField(): z.ZodBoolean; /** * Creates a number field schema with optional constraints * Maps to PocketBase 'number' field type * * @param options - Optional constraints for the number field * @returns Zod number schema with PocketBase metadata * * @example * const ProductSchema = z.object({ * price: NumberField({ min: 0 }), * quantity: NumberField({ min: 0, noDecimal: true }), * rating: NumberField({ min: 0, max: 5 }), * progress: NumberField({ min: 0, max: 100 }), // required defaults to false, allowing zero * score: NumberField({ min: 1, max: 10, required: true }), // requires non-zero value * }); * * @remarks * By default, number fields are not required (`required: false`), which allows zero values. * In PocketBase, `required: true` for number fields means the value must be non-zero. * If you set `min: 0` and want to allow zero, keep `required: false` (the default). */ declare function NumberField(options?: NumberFieldOptions): z.ZodNumber; /** * Creates a text field schema with optional constraints * Maps to PocketBase 'text' field type * * @param options - Optional constraints for the text field * @returns Zod string schema with PocketBase metadata * * @example * const ProductSchema = z.object({ * name: TextField({ min: 1, max: 200 }), * sku: TextField({ autogeneratePattern: "[A-Z]{3}-[0-9]{6}" }), * description: TextField({ max: 1000 }), * }); */ declare function TextField(options?: TextFieldOptions): z.ZodString; /** * Creates an email field schema * Maps to PocketBase 'email' field type * * @returns Zod string schema with email validation and PocketBase metadata * * @example * const UserSchema = z.object({ * email: EmailField(), * alternateEmail: EmailField().optional(), * }); */ declare function EmailField(): z.ZodString; /** * Creates a URL field schema * Maps to PocketBase 'url' field type * * @returns Zod string schema with URL validation and PocketBase metadata * * @example * const ProductSchema = z.object({ * website: URLField(), * documentation: URLField().optional(), * }); */ declare function URLField(): z.ZodString; /** * Creates a rich text editor field schema * Maps to PocketBase 'editor' field type * * @returns Zod string schema with PocketBase metadata * * @example * const PostSchema = z.object({ * content: EditorField(), * summary: EditorField().optional(), * }); */ declare function EditorField(): z.ZodString; /** * Creates a date field schema with optional constraints * Maps to PocketBase 'date' field type * * @param options - Optional date constraints * @returns Zod string schema with PocketBase metadata * * @example * const EventSchema = z.object({ * startDate: DateField(), * endDate: DateField({ min: new Date('2024-01-01') }), * releaseDate: DateField().optional(), * }); */ declare function DateField(options?: DateFieldOptions): z.ZodString; /** * Creates an autodate field schema with automatic timestamp management * Maps to PocketBase 'autodate' field type * * @param options - Optional autodate configuration * @returns Zod string schema with PocketBase metadata * * @example * const PostSchema = z.object({ * createdAt: AutodateField({ onCreate: true }), * updatedAt: AutodateField({ onUpdate: true }), * publishedAt: AutodateField({ onCreate: true, onUpdate: false }), * }); */ declare function AutodateField(options?: AutodateFieldOptions): z.ZodString; /** * Creates a select field schema from enum values * Maps to PocketBase 'select' field type * * A `maxSelect` of 1 (or no options) produces a single-select enum schema; * `maxSelect` greater than 1 produces a multi-select array schema. * * Pass `maxSelect` as a literal so the overloads can pick the right return * type — a widened `number` variable resolves to the array overload even when * its runtime value is 1. * * @param values - Array of allowed string values * @param options - Optional select configuration * @returns Zod enum schema (single select) or array schema (multiple select) with PocketBase metadata * * @example * // Single select * const PostSchema = z.object({ * status: SelectField(["draft", "published", "archived"]), * }); * * @example * // Multiple select * const ProductSchema = z.object({ * categories: SelectField(["electronics", "clothing", "food"], { maxSelect: 3 }), * }); */ declare function SelectField(values: T, options?: { maxSelect?: 1; }): EnumFromArray; declare function SelectField(values: T, options: { maxSelect: number; }): z.ZodArray>; /** * Creates a single file field schema * Maps to PocketBase 'file' field type with maxSelect=1 * * @param options - Optional file constraints * @returns Zod schema that accepts File on input and returns string when reading from database * * @example * const ProductSchema = z.object({ * thumbnail: FileField({ mimeTypes: ["image/*"], maxSize: 5242880 }), * document: FileField({ mimeTypes: ["application/pdf"] }), * }); * * @remarks * - When creating/updating records: accepts File objects * - When reading from PocketBase: returns string (filename) */ declare function FileField(options?: FileFieldOptions): z.ZodType; /** * Creates a multiple files field schema * Maps to PocketBase 'file' field type with maxSelect>1 * * @param options - Optional file constraints * @returns Zod array schema that accepts File[] on input and returns string[] when reading from database * * @example * const ProductSchema = z.object({ * images: FilesField({ mimeTypes: ["image/*"], maxSelect: 5 }), * attachments: FilesField({ minSelect: 1, maxSelect: 10 }), * }); * * @remarks * - When creating/updating records: accepts File[] * - When reading from PocketBase: returns string[] (filenames) */ declare function FilesField(options?: FilesFieldOptions): z.ZodType; /** * Creates a JSON field schema with optional inner schema validation * Maps to PocketBase 'json' field type * * @param schema - Optional Zod schema for the JSON structure * @param options - Optional PocketBase constraints (`maxSize`) * @returns Zod schema with PocketBase metadata * * @example * // Any JSON * const ProductSchema = z.object({ * metadata: JSONField(), * }); * * @example * // Typed JSON * const ProductSchema = z.object({ * settings: JSONField(z.object({ * theme: z.string(), * notifications: z.boolean(), * })), * }); * * @example * // A payload larger than PocketBase's 1MB default needs its own limit * const TimelineSchema = z.object({ * timelineData: JSONField({ maxSize: "5M" }), * outputSettings: JSONField(z.object({ fps: z.number() }), { maxSize: "200K" }), * }); */ declare function JSONField(options?: JSONFieldOptions): z.ZodRecord; declare function JSONField(schema: undefined, options?: JSONFieldOptions): z.ZodRecord; declare function JSONField(schema: T, options?: JSONFieldOptions): T; /** * Creates a geographic point field schema * Maps to PocketBase 'geoPoint' field type * * @returns Zod object schema with lon/lat fields and PocketBase metadata * * @example * const LocationSchema = z.object({ * coordinates: GeoPointField(), * homeLocation: GeoPointField().optional(), * }); */ declare function GeoPointField(): z.ZodObject<{ lon: z.ZodNumber; lat: z.ZodNumber; }>; /** * Permission rules that apply to a view collection * * View collections are read-only: PocketBase rejects createRule, updateRule, * deleteRule and manageRule on them, so only the two read rules are accepted. */ interface ViewPermissionSchema { /** Controls who can list/query the view's rows */ listRule?: RuleExpression; /** Controls who can view an individual row */ viewRule?: RuleExpression; } /** * Configuration options for defining a view collection */ interface ViewCollectionConfig { /** * The name of the PocketBase collection */ collectionName: string; /** * The Zod schema describing the shape of a row * * For views this is used for TypeScript type generation and application-side * parsing only - PocketBase derives the collection's actual fields from the * SQL query when the collection is saved. */ schema: z.ZodObject; /** * The SQL SELECT statement backing the view * * Use the `sql` tagged template for consistent formatting. */ viewQuery: string; /** * Optional read permissions (listRule / viewRule) */ permissions?: ViewPermissionSchema; } /** * Removes the common leading indentation from a multi-line string and trims * blank leading/trailing lines * * Keeps generated migrations stable when a query is re-indented in the source * file, since only the relative indentation is preserved. Also used when * reading a query back out of a migration file, where the generator has * indented it to fit the surrounding code. * * @param value - Raw multi-line string * @returns Dedented string */ declare function dedentSql(value: string): string; /** * Tagged template for SQL view queries * * Interpolates values, strips the common leading indentation and trims blank * leading/trailing lines. Returns a plain string, so a regular string literal * works anywhere `sql` does. * * @example * const query = sql` * SELECT p.id AS id, p.title AS title * FROM Projects p * `; * // "SELECT p.id AS id, p.title AS title\n FROM Projects p" */ declare function sql(strings: TemplateStringsArray, ...values: Array): string; /** * Validates a view query, throwing with an actionable message when it can't work * * @param collectionName - Collection name, used in error messages * @param viewQuery - The SQL query to validate */ declare function validateViewQuery(collectionName: string, viewQuery: unknown): asserts viewQuery is string; /** * Defines a read-only PocketBase view collection backed by a SQL query * * PocketBase derives the collection's fields by running the query, so the Zod * schema only describes the shape for TypeScript types and application-side * parsing. Views cannot have indexes and only support listRule / viewRule. * * The generated migration creates the collection with `type: "view"` and the * query; changing the SQL later produces an in-place `viewQuery` update, which * keeps the collection id stable. * * @param config - View collection configuration * @returns The schema with view metadata attached * * @example * export default defineView({ * collectionName: "ProjectStats", * schema: ProjectStatsSchema, * viewQuery: sql` * SELECT p.OwnerUser AS id, * p.OwnerUser AS OwnerUser, * COUNT(*) AS projectCount * FROM Projects p * GROUP BY p.OwnerUser * `, * permissions: { * listRule: "OwnerUser = @request.auth.id", * viewRule: "OwnerUser = @request.auth.id", * }, * }); * * @example * // Requirements PocketBase places on the query: * // - the outermost SELECT must expose an `id` column * // - a relation column must be selected bare (e.g. `p.OwnerUser AS OwnerUser`) * // from the outer table for PocketBase to infer it as a relation field * // - no top-level UNION (wrap unions in a subquery) */ declare function defineView(config: ViewCollectionConfig): z.ZodObject; /** * Predefined permission templates for common access control patterns */ declare const PermissionTemplates: { /** * Public access - anyone can perform all operations */ public: () => PermissionSchema; /** * Authenticated users only - requires valid authentication for all operations */ authenticated: () => PermissionSchema; /** * Owner-only access - users can only manage their own records * @param ownerField - Name of the relation field pointing to user (default: 'User') */ ownerOnly: (ownerField?: string) => PermissionSchema; /** * Admin/superuser only access * Assumes a 'role' field exists with 'admin' value * @param roleField - Name of the role field (default: 'role') */ adminOnly: (roleField?: string) => PermissionSchema; /** * Public read, authenticated write * Anyone can list/view, but only authenticated users can create/update/delete */ readPublic: () => PermissionSchema; /** * Locked access - only superusers can perform operations * All rules are set to null (locked) */ locked: () => PermissionSchema; /** * Read-only authenticated - authenticated users can read, no write access */ readOnlyAuthenticated: () => PermissionSchema; }; /** * Resolve template configuration to concrete permission schema * @param config - Template configuration or direct permission schema * @returns Resolved permission schema with all rules defined */ declare function resolveTemplate(config: PermissionTemplateConfig): PermissionSchema; export { type APIRuleType, AutodateField, type AutodateFieldOptions, BoolField, type ByteSize, type CollectionConfig, DateField, type DateFieldOptions, EditorField, EmailField, type EnumFromArray, FIELD_METADATA_KEY, type FieldMetadata, FileField, type FileFieldOptions, FilesField, type FilesFieldOptions, GeoPointField, JSONField, type JSONFieldOptions, NumberField, type NumberFieldOptions, type PermissionSchema, type PermissionTemplate, type PermissionTemplateConfig, PermissionTemplates, type PocketBaseFieldType, type RelationConfig, RelationField, type RelationsConfig, RelationsField, type RuleExpression, SelectField, type SelectFieldOptions, TextField, type TextFieldOptions, URLField, type ViewCollectionConfig, type ViewPermissionSchema, baseSchema, dedentSql, defineCollection, defineView, extractFieldMetadata, extractRelationMetadata, resolveTemplate, sql, validateViewQuery };