miragejs-orm 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/lib/dist/index.cjs +17 -0
- package/lib/dist/index.cjs.map +1 -0
- package/lib/dist/index.d.cts +3044 -0
- package/lib/dist/index.d.ts +3044 -0
- package/lib/dist/index.js +17 -0
- package/lib/dist/index.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/MirageError.ts","../src/utils/Logger.ts","../src/id-manager/IdentityManager.ts","../src/db/QueryManager.ts","../src/db/DbCollection.ts","../src/db/DB.ts","../src/model/BaseModel.ts","../src/model/ModelCollection.ts","../src/model/typeGuards.ts","../src/model/RelationshipsManager.ts","../src/model/Model.ts","../src/model/ModelBuilder.ts","../src/factory/AssociationsManager.ts","../src/factory/Factory.ts","../src/factory/FactoryBuilder.ts","../src/associations/belongsTo.ts","../src/associations/create.ts","../src/associations/createMany.ts","../src/associations/hasMany.ts","../src/associations/link.ts","../src/associations/linkMany.ts","../src/associations/associations.ts","../src/schema/BaseCollection.ts","../src/schema/Collection.ts","../src/serializer/Serializer.ts","../src/schema/CollectionBuilder.ts","../src/schema/Schema.ts","../src/schema/SchemaBuilder.ts"],"sourcesContent":["/**\n * A custom error class for MirageJS.\n * @augments Error\n * @param message - The error message.\n * @example\n * const error = new MirageError('Something went wrong');\n * console.log(error.message); // => '[Mirage]: Something went wrong'\n */\nexport default class MirageError extends Error {\n constructor(message: string) {\n super(`[Mirage]: ${message}`);\n }\n}\n","/**\n * Logger class for outputting structured log messages.\n *\n * Provides debug, info, warn, and error logging methods with automatic level filtering.\n * Used internally by the ORM to log database operations, seed/fixture loading, and errors.\n * @example\n * ```typescript\n * const logger = new Logger({\n * enabled: true,\n * level: 'debug',\n * prefix: '[MyApp]'\n * });\n *\n * logger.debug('Operation started', { userId: '123' });\n * logger.info('Loaded 10 records');\n * logger.warn('Missing optional field', { field: 'email' });\n * logger.error('Validation failed', { errors: [...] });\n * ```\n */\nexport default class Logger {\n private _config: LoggerConfig;\n\n /**\n * Creates a new Logger instance with the specified configuration.\n * @param config - Logger configuration including enabled state, log level, and optional prefix\n * @example\n * ```typescript\n * const logger = new Logger({\n * enabled: true,\n * level: 'debug',\n * prefix: '[Mirage]'\n * });\n * ```\n */\n constructor(config: LoggerConfig) {\n this._config = {\n prefix: '[Mirage]',\n ...config,\n };\n }\n\n /**\n * Logs a debug message with optional context.\n *\n * Debug logs are the most verbose and show low-level operational details.\n * Use for troubleshooting, understanding flow, and performance analysis.\n *\n * Only outputs if logger is enabled and level is set to 'debug'.\n * @param message - The debug message to log\n * @param context - Optional context object with additional information\n * @example\n * ```typescript\n * logger.debug('Query executed', { collection: 'users', query: { name: 'John' } });\n * // Output: [Mirage] DEBUG: Query executed { collection: 'users', query: { name: 'John' } }\n * ```\n */\n debug(message: string, context?: Record<string, unknown>): void {\n this._log('debug', message, context);\n }\n\n /**\n * Logs an info message with optional context.\n *\n * Info logs show normal operations and important events.\n * Use for high-level actions, successful operations, and summaries.\n *\n * Only outputs if logger is enabled and level is 'debug' or 'info'.\n * @param message - The info message to log\n * @param context - Optional context object with additional information\n * @example\n * ```typescript\n * logger.info('Fixtures loaded', { collection: 'users', count: 50 });\n * // Output: [Mirage] INFO: Fixtures loaded { collection: 'users', count: 50 }\n * ```\n */\n info(message: string, context?: Record<string, unknown>): void {\n this._log('info', message, context);\n }\n\n /**\n * Logs a warning message with optional context.\n *\n * Warning logs indicate something unexpected but not breaking.\n * Use for deprecated features, unusual patterns, and potential issues.\n *\n * Only outputs if logger is enabled and level is 'debug', 'info', or 'warn'.\n * @param message - The warning message to log\n * @param context - Optional context object with additional information\n * @example\n * ```typescript\n * logger.warn('Foreign key mismatch', { postId: '1', authorId: '999' });\n * // Output: [Mirage] WARN: Foreign key mismatch { postId: '1', authorId: '999' }\n * ```\n */\n warn(message: string, context?: Record<string, unknown>): void {\n this._log('warn', message, context);\n }\n\n /**\n * Logs an error message with optional context.\n *\n * Error logs indicate something failed or broke.\n * Use for operations that couldn't complete and validation failures.\n *\n * Only outputs if logger is enabled and level is not 'silent'.\n * @param message - The error message to log\n * @param context - Optional context object with additional information\n * @example\n * ```typescript\n * logger.error('Validation failed', { field: 'email', reason: 'required' });\n * // Output: [Mirage] ERROR: Validation failed { field: 'email', reason: 'required' }\n * ```\n */\n error(message: string, context?: Record<string, unknown>): void {\n this._log('error', message, context);\n }\n\n /**\n * Internal method to handle actual logging with level filtering.\n *\n * Checks if logging is enabled and if the message level meets the configured threshold.\n * Routes messages to appropriate console methods based on severity.\n * @param level - The log level of the message\n * @param message - The message to log\n * @param context - Optional context object\n * @private\n */\n private _log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\n if (!this._config.enabled) {\n return;\n }\n\n if (LOG_LEVELS[level] < LOG_LEVELS[this._config.level]) {\n return;\n }\n\n const prefix = this._config.prefix;\n const levelLabel = level.toUpperCase();\n const logMessage = `${prefix} ${levelLabel}: ${message}`;\n\n switch (level) {\n case 'debug':\n case 'info':\n console.log(logMessage, context || '');\n break;\n case 'warn':\n console.warn(logMessage, context || '');\n break;\n case 'error':\n console.error(logMessage, context || '');\n break;\n }\n }\n}\n\n/**\n * Log level type defining the severity of log messages.\n *\n * Log levels follow a hierarchy where setting a level shows that level and everything above it:\n * - `silent`: No logging\n * - `error`: Only errors\n * - `warn`: Warnings and errors\n * - `info`: Info, warnings, and errors\n * - `debug`: All messages (most verbose)\n * @example\n * ```typescript\n * // Debug level - see everything\n * schema().logging({ enabled: true, level: 'debug' })\n *\n * // Info level - only important operations\n * schema().logging({ enabled: true, level: 'info' })\n *\n * // Error level - only failures\n * schema().logging({ enabled: true, level: 'error' })\n * ```\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\n/**\n * Logger configuration options.\n */\nexport interface LoggerConfig {\n /**\n * Whether logging is enabled. When false, no logs are output regardless of level.\n * @default false\n */\n enabled: boolean;\n\n /**\n * The minimum log level to output. Messages below this level are filtered out.\n * @default 'info'\n */\n level: LogLevel;\n\n /**\n * Custom prefix for log messages. Useful for distinguishing ORM logs from other output.\n * @default '[Mirage]'\n */\n prefix?: string;\n}\n\n/**\n * Numeric values for log levels to enable level comparison.\n * Lower numbers = more verbose, higher numbers = less verbose.\n * @internal\n */\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n","import { MirageError } from '../utils';\n\nimport type { IdType, IdGenerator, IdentityManagerConfig } from './types';\n\n/**\n * Manages unique identifiers for database records.\n * Handles different types of IDs, ensuring uniqueness and proper sequencing.\n * @template T - The type of ID to manage (defaults to string)\n * @param options - Configuration options for the identity manager.\n * @param options.initialCounter - The initial counter value.\n * @param options.initialUsedIds - A set of initial used IDs.\n * @param options.idGenerator - Custom function to generate the next ID.\n * @example\n * const identityManager = new IdentityManager();\n * identityManager.get(); // => \"1\"\n * identityManager.set(\"1\");\n * identityManager.get(); // => \"2\"\n * identityManager.fetch(); // => \"2\"\n * identityManager.get(); // => \"3\"\n * identityManager.reset(); // => \"1\"\n */\nexport default class IdentityManager<T extends IdType = string> {\n private _counter: T;\n private _idGenerator: IdGenerator<T>;\n private _initialCounter: T;\n private _usedIds: Set<T>;\n\n constructor(options: IdentityManagerConfig<T>) {\n this._initialCounter = options.initialCounter;\n this._counter = this._initialCounter;\n this._usedIds = options.initialUsedIds ? new Set<T>(options.initialUsedIds) : new Set<T>();\n this._idGenerator = options.idGenerator ?? this.getDefaultGenerator();\n }\n\n /**\n * Gets the next ID without incrementing the counter.\n * @returns The next ID.\n */\n get(): T {\n let nextId = this._counter;\n\n while (this._usedIds.has(nextId)) {\n nextId = this._idGenerator(nextId);\n }\n\n return nextId;\n }\n\n /**\n * Gets the next ID and increments the counter.\n * @returns The next ID.\n */\n fetch(): T {\n const nextId = this.get();\n this._usedIds.add(nextId);\n this._counter = this._idGenerator(nextId);\n return nextId;\n }\n\n /**\n * Marks an ID as used and updates the counter if necessary.\n * @param id - The ID to mark as used.\n */\n set(id: T): void {\n this._usedIds.add(id);\n }\n\n /**\n * Increments the counter.\n */\n inc(): void {\n this._counter = this._idGenerator(this._counter);\n }\n\n /**\n * Resets the counter to its initial value and clears used IDs.\n */\n reset(): void {\n this._counter = this._initialCounter;\n this._usedIds.clear();\n }\n\n /**\n * Gets the default generator for the ID type.\n * @returns The default generator function.\n */\n private getDefaultGenerator(): IdGenerator<T> {\n if (typeof this._initialCounter === 'string') {\n if (isNaN(Number(this._initialCounter))) {\n throw new MirageError('Default ID generator only works with numeric string IDs');\n }\n return ((current: string) => {\n if (isNaN(Number(current))) {\n throw new MirageError('Default ID generator only works with numeric string IDs');\n }\n return String(Number(current) + 1);\n }) as unknown as IdGenerator<T>;\n } else if (typeof this._initialCounter === 'number') {\n return ((current: number) => current + 1) as unknown as IdGenerator<T>;\n } else {\n throw new MirageError('Unknown ID type. Please provide a custom idGenerator.');\n }\n }\n}\n\n/**\n * String-based identity manager with sensible defaults.\n * @example\n * const identityManager = new StringIdentityManager();\n * identityManager.fetch(); // => \"1\"\n * identityManager.fetch(); // => \"2\"\n */\nexport class StringIdentityManager extends IdentityManager<string> {\n constructor(options?: Partial<IdentityManagerConfig<string>>) {\n super({\n initialCounter: '1',\n ...options,\n });\n }\n}\n\n/**\n * Number-based identity manager with sensible defaults.\n * @example\n * const identityManager = new NumberIdentityManager();\n * identityManager.fetch(); // => 1\n * identityManager.fetch(); // => 2\n */\nexport class NumberIdentityManager extends IdentityManager<number> {\n constructor(options?: Partial<IdentityManagerConfig<number>>) {\n super({\n initialCounter: 1,\n ...options,\n });\n }\n}\n","import type {\n DbRecord,\n FieldOps,\n OrderBy,\n Primitive,\n QueryOptions,\n Where,\n WhereHelperFns,\n} from './types';\n\n/**\n * QueryManager handles advanced querying, filtering, sorting, and pagination for DbCollection.\n * Separates query logic from storage management.\n * @template TRecord - The record type\n */\nexport default class QueryManager<TRecord extends DbRecord> {\n /**\n * Executes a query against a set of records.\n * @param records - All records to query\n * @param options - Query options\n * @returns Filtered, sorted, and paginated records\n */\n query(records: TRecord[], options: QueryOptions<TRecord>): TRecord[] {\n let results = records;\n const { cursor, limit, offset, orderBy, where } = options;\n\n // Apply where filter\n if (where) {\n if (typeof where === 'function') {\n const helpers = this._buildWhereHelpers();\n results = results.filter((record) => where(record, helpers));\n } else {\n results = results.filter((record) => this.matchesWhere(record, where));\n }\n }\n\n // Apply sorting\n if (orderBy) {\n results = this._applyOrder(results, orderBy);\n }\n\n // Apply cursor-based pagination (keyset)\n if (cursor && orderBy) {\n results = this._applyCursor(results, orderBy, cursor);\n }\n\n // Apply offset pagination\n if (typeof offset === 'number' && offset > 0) {\n results = results.slice(offset);\n }\n\n // Apply limit\n if (typeof limit === 'number') {\n if (limit === 0) {\n return [];\n }\n if (limit > 0) {\n results = results.slice(0, limit);\n }\n }\n\n return results;\n }\n\n /**\n * Checks if a record matches a simple predicate object (equality matching).\n * @param record - The record to check\n * @param predicate - The predicate object with field-value pairs\n * @returns True if all predicate fields match the record\n */\n matchesPredicateObject(record: TRecord, predicate: Partial<TRecord>): boolean {\n return Object.entries(predicate).every(\n ([key, value]) => String(record[key as keyof TRecord]) === String(value),\n );\n }\n\n /**\n * Checks if a record matches a Where clause with logical operators.\n * @param record - The record to check\n * @param where - The where clause\n * @returns True if the record matches\n */\n matchesWhere(record: TRecord, where: Where<TRecord>): boolean {\n const { AND, OR, NOT, ...fields } = where as Record<string, unknown>;\n\n // Check field-level conditions\n for (const key in fields) {\n const fieldValue = record[key as keyof TRecord];\n const condition = fields[key];\n\n // Direct equality check or field operations\n if (condition !== null && typeof condition === 'object' && !Array.isArray(condition)) {\n if (!this._matchesField(fieldValue, condition as FieldOps<unknown>)) {\n return false;\n }\n } else {\n // Simple equality\n if (fieldValue !== condition) {\n return false;\n }\n }\n }\n\n // Logical AND\n if (Array.isArray(AND) && !AND.every((w: Where<TRecord>) => this.matchesWhere(record, w))) {\n return false;\n }\n\n // Logical OR\n if (Array.isArray(OR) && !OR.some((w: Where<TRecord>) => this.matchesWhere(record, w))) {\n return false;\n }\n\n // Logical NOT\n if (NOT && this.matchesWhere(record, NOT)) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Checks if a field value matches field operations (eq, ne, gt, lt, like, etc.).\n * @param value - The field value\n * @param ops - The field operations\n * @returns True if the value matches the operations\n */\n private _matchesField(value: unknown, ops: FieldOps<unknown>): boolean {\n // Equality operations\n if ('eq' in ops && value !== ops.eq) return false;\n if ('ne' in ops && value === ops.ne) return false;\n if ('in' in ops && ops.in && !ops.in.includes(value as never)) return false;\n if ('nin' in ops && ops.nin && ops.nin.includes(value as never)) return false;\n if ('isNull' in ops) {\n const isNull = value == null;\n if (ops.isNull && !isNull) return false;\n if (!ops.isNull && isNull) return false;\n }\n\n // Range operations\n if ('lt' in ops && ops.lt != null && !this._compareValues(value, ops.lt, '<')) return false;\n if ('lte' in ops && ops.lte != null && !this._compareValues(value, ops.lte, '<=')) return false;\n if ('gt' in ops && ops.gt != null && !this._compareValues(value, ops.gt, '>')) return false;\n if ('gte' in ops && ops.gte != null && !this._compareValues(value, ops.gte, '>=')) return false;\n if ('between' in ops && ops.between && Array.isArray(ops.between) && ops.between.length === 2) {\n const [min, max] = ops.between;\n if (!this._compareValues(value, min, '>=') || !this._compareValues(value, max, '<=')) {\n return false;\n }\n }\n\n // String operations\n if ('like' in ops && typeof ops.like === 'string') {\n const regex = this._likeToRegex(ops.like, false);\n if (!regex.test(String(value))) return false;\n }\n if ('ilike' in ops && typeof ops.ilike === 'string') {\n const regex = this._likeToRegex(ops.ilike, true);\n if (!regex.test(String(value))) return false;\n }\n if ('startsWith' in ops && typeof ops.startsWith === 'string') {\n if (!String(value).startsWith(ops.startsWith)) return false;\n }\n if ('endsWith' in ops && typeof ops.endsWith === 'string') {\n if (!String(value).endsWith(ops.endsWith)) return false;\n }\n if ('contains' in ops && ops.contains && typeof ops.contains === 'string') {\n if (!String(value).includes(ops.contains)) return false;\n }\n\n // Array operations\n if ('contains' in ops && Array.isArray(value)) {\n const needle = ops.contains;\n if (Array.isArray(needle)) {\n if (!needle.every((item) => value.includes(item))) return false;\n } else {\n if (!value.includes(needle as never)) return false;\n }\n }\n if ('length' in ops && ops.length && Array.isArray(value)) {\n if (!this._matchesField(value.length, ops.length as FieldOps<unknown>)) return false;\n }\n\n return true;\n }\n\n /**\n * Compare two values with an operator.\n * @param a - First value\n * @param b - Second value\n * @param op - Comparison operator\n * @returns True if the comparison holds\n */\n private _compareValues(a: unknown, b: unknown, op: '<' | '<=' | '>' | '>='): boolean {\n // Handle null/undefined\n if (a == null || b == null) return false;\n\n // Handle dates\n if (a instanceof Date && b instanceof Date) {\n const aTime = a.getTime();\n const bTime = b.getTime();\n switch (op) {\n case '<':\n return aTime < bTime;\n case '<=':\n return aTime <= bTime;\n case '>':\n return aTime > bTime;\n case '>=':\n return aTime >= bTime;\n }\n }\n\n // Handle numbers and strings\n switch (op) {\n case '<':\n return (a as Primitive) < (b as Primitive);\n case '<=':\n return (a as Primitive) <= (b as Primitive);\n case '>':\n return (a as Primitive) > (b as Primitive);\n case '>=':\n return (a as Primitive) >= (b as Primitive);\n }\n }\n\n /**\n * Converts SQL LIKE pattern to RegExp.\n * @param pattern - SQL LIKE pattern with % wildcards\n * @param caseInsensitive - Whether to match case-insensitively\n * @returns Regular expression\n */\n private _likeToRegex(pattern: string, caseInsensitive: boolean): RegExp {\n // Escape special regex characters except %\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n // Replace % with .*\n const regexPattern = '^' + escaped.replace(/%/g, '.*') + '$';\n return new RegExp(regexPattern, caseInsensitive ? 'i' : '');\n }\n\n /**\n * Sorts records by orderBy specification.\n * @param records - Records to sort\n * @param orderBy - Order specification\n * @returns Sorted records\n */\n private _applyOrder(records: TRecord[], orderBy: OrderBy<TRecord>): TRecord[] {\n // Convert orderBy to array of [field, direction] tuples\n const pairs: Array<readonly [keyof TRecord, 'asc' | 'desc']> = Array.isArray(orderBy)\n ? orderBy\n : (() => {\n const result: Array<[keyof TRecord, 'asc' | 'desc']> = [];\n for (const key in orderBy) {\n const direction = orderBy[key];\n if (direction) {\n result.push([key as keyof TRecord, direction]);\n }\n }\n return result;\n })();\n\n return [...records].sort((a, b) => {\n for (const [field, direction] of pairs) {\n const aVal = a[field];\n const bVal = b[field];\n\n // Handle nulls - nulls last\n if (aVal == null && bVal == null) continue;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n\n // Handle dates\n if (aVal instanceof Date && bVal instanceof Date) {\n const diff = aVal.getTime() - bVal.getTime();\n if (diff !== 0) return direction === 'desc' ? -diff : diff;\n continue;\n }\n\n // Handle primitives\n if (aVal < bVal) return direction === 'desc' ? 1 : -1;\n if (aVal > bVal) return direction === 'desc' ? -1 : 1;\n }\n return 0;\n });\n }\n\n /**\n * Applies cursor-based (keyset) pagination.\n * Filters records to those that come after the cursor position in the sort order.\n * @param records - Sorted records\n * @param orderBy - Order specification (must match cursor fields)\n * @param cursor - Cursor position\n * @returns Records after the cursor\n */\n private _applyCursor(\n records: TRecord[],\n orderBy: OrderBy<TRecord>,\n cursor: Partial<TRecord>,\n ): TRecord[] {\n const pairs: Array<readonly [keyof TRecord, 'asc' | 'desc']> = Array.isArray(orderBy)\n ? orderBy\n : (() => {\n const result: Array<[keyof TRecord, 'asc' | 'desc']> = [];\n for (const key in orderBy) {\n const direction = orderBy[key];\n if (direction) {\n result.push([key as keyof TRecord, direction]);\n }\n }\n return result;\n })();\n\n return records.filter((record) => {\n for (const [field, direction] of pairs) {\n const recordVal = record[field];\n const cursorVal = cursor[field];\n\n // If cursor doesn't specify this field, continue to next\n if (cursorVal === undefined) continue;\n\n // Compare values\n if (recordVal == null && cursorVal == null) continue;\n if (recordVal == null) return false; // null comes last, so it's not after cursor\n if (cursorVal == null) return true; // record is after null cursor\n\n // Handle dates\n if (recordVal instanceof Date && cursorVal instanceof Date) {\n const diff = recordVal.getTime() - cursorVal.getTime();\n if (diff === 0) continue; // Equal, check next field\n return direction === 'desc' ? diff < 0 : diff > 0;\n }\n\n // Handle primitives\n if (recordVal === cursorVal) continue; // Equal, check next field\n if (recordVal < cursorVal) return direction === 'desc'; // desc: keep smaller, asc: skip smaller\n if (recordVal > cursorVal) return direction === 'asc'; // asc: keep larger, desc: skip larger\n }\n\n // All fields matched cursor exactly, exclude this record (cursor is inclusive boundary)\n return false;\n });\n }\n\n /**\n * Builds helper functions for where callback predicates.\n * These are pure comparison utilities - users access record values themselves.\n * @returns Helper functions for comparisons\n */\n private _buildWhereHelpers(): WhereHelperFns<TRecord> {\n return {\n // Logical operators\n and: (...conditions) => conditions.every((c) => c),\n or: (...conditions) => conditions.some((c) => c),\n not: (condition) => !condition,\n\n // Comparison operators\n eq: (value, compareWith) => value === compareWith,\n ne: (value, compareWith) => value !== compareWith,\n gt: (value, compareWith) =>\n value != null && compareWith != null && (value as Primitive) > (compareWith as Primitive),\n gte: (value, compareWith) =>\n value != null && compareWith != null && (value as Primitive) >= (compareWith as Primitive),\n lt: (value, compareWith) =>\n value != null && compareWith != null && (value as Primitive) < (compareWith as Primitive),\n lte: (value, compareWith) =>\n value != null && compareWith != null && (value as Primitive) <= (compareWith as Primitive),\n between: (value, min, max) =>\n value != null &&\n min != null &&\n max != null &&\n (value as Primitive) >= (min as Primitive) &&\n (value as Primitive) <= (max as Primitive),\n\n // String operators\n like: (value, pattern) => {\n const regex = this._likeToRegex(pattern, false);\n return regex.test(String(value));\n },\n ilike: (value, pattern) => {\n const regex = this._likeToRegex(pattern, true);\n return regex.test(String(value));\n },\n startsWith: (value, prefix) => String(value).startsWith(prefix),\n endsWith: (value, suffix) => String(value).endsWith(suffix),\n containsText: (value, substring) => String(value).includes(substring),\n\n // Array operators\n inArray: (value, values) => values.some((v) => v === value),\n notInArray: (value, values) => !values.some((v) => v === value),\n\n // Null checks\n isNull: (value) => value == null,\n isNotNull: (value) => value != null,\n };\n }\n}\n","import { IdentityManager, StringIdentityManager } from '@src/id-manager';\n\nimport QueryManager from './QueryManager';\nimport type {\n DbCollectionConfig,\n DbRecord,\n DbRecordInput,\n NewDbRecord,\n QueryOptions,\n Where,\n} from './types';\n\n/**\n * A collection of records in a database. Think of it as a table in a relational database.\n * @param config - Configuration for the collection.\n * @param config.name - The name of the collection.\n * @param config.identityManager - The identity manager for the collection.\n * @param config.initialData - Initial data for the collection.\n * @example\n * const users = new DbCollection({ name: 'users' });\n * users.insert({ name: 'John' }); // => { id: \"1\", name: 'John' }\n */\nexport default class DbCollection<TRecord extends DbRecord = DbRecord> {\n name: string;\n identityManager: IdentityManager<TRecord['id']>;\n\n private _records: Map<TRecord['id'], TRecord> = new Map();\n private _queryManager: QueryManager<TRecord> = new QueryManager<TRecord>();\n\n constructor(name: string, config?: DbCollectionConfig<TRecord>) {\n this.name = name;\n this.identityManager =\n config?.identityManager ?? (new StringIdentityManager() as IdentityManager<TRecord['id']>);\n\n if (config?.initialData) {\n this.insertMany(config.initialData);\n }\n }\n\n // -- UTILITY METHODS --\n\n /**\n * The next ID for the collection.\n * @returns The next ID for the collection.\n */\n get nextId(): TRecord['id'] {\n return this.identityManager.fetch();\n }\n\n /**\n * Returns the number of records in the collection.\n * @returns The number of records in the collection.\n */\n get size(): number {\n return this._records.size;\n }\n\n /**\n * Checks if the collection is empty.\n * @returns `true` if the collection is empty, `false` otherwise.\n */\n get isEmpty(): boolean {\n return this._records.size === 0;\n }\n\n // -- RECORDS ACCESSOR --\n\n /**\n * Returns all records in the collection\n * @returns An array of all records in the collection.\n */\n all(): TRecord[] {\n return Array.from(this._records.values());\n }\n\n /**\n * Gets a record by its index position in the collection.\n * @param index - The index of the record to get.\n * @returns The record at the specified index, or `undefined` if out of bounds.\n */\n at(index: number): TRecord | undefined {\n return this.all()[index];\n }\n\n /**\n * Returns the first record in the collection.\n * @returns The first record in the collection, or `undefined` if the collection is empty.\n */\n first(): TRecord | undefined {\n return this.all()[0];\n }\n\n /**\n * Returns the last record in the collection.\n * @returns The last record in the collection, or `undefined` if the collection is empty.\n */\n last(): TRecord | undefined {\n const records = this.all();\n return records[records.length - 1];\n }\n\n /**\n * Checks if a record exists in the collection.\n * @param id - The ID of the record to check.\n * @returns `true` if the record exists, `false` otherwise.\n */\n has(id: TRecord['id']): boolean {\n return this._records.has(id);\n }\n\n // -- QUERY METHODS --\n\n /**\n * Finds the first record that matches the given ID, predicate object, or query options.\n * @param input - ID, predicate object, or query options\n * @returns The first record that matches, or `null` if not found.\n * @example\n * ```typescript\n * // By ID\n * collection.find('user-1');\n *\n * // By predicate object (simple equality)\n * collection.find({ email: 'user@example.com' });\n *\n * // By query options (advanced filtering, sorting)\n * collection.find({\n * where: { email: { ilike: '%@example.com' } },\n * orderBy: { createdAt: 'desc' },\n * });\n * ```\n */\n find(input: TRecord['id'] | DbRecordInput<TRecord> | QueryOptions<TRecord>): TRecord | null {\n // 1. Handle ID-based lookup\n if (typeof input === 'string' || typeof input === 'number') {\n return this._records.get(input as TRecord['id']) ?? null;\n }\n\n // 2. Check if it's QueryOptions (has where, orderBy, cursor, offset, or limit)\n const hasQueryKeys =\n typeof input === 'object' &&\n ('where' in input ||\n 'orderBy' in input ||\n 'cursor' in input ||\n 'offset' in input ||\n 'limit' in input);\n\n if (hasQueryKeys) {\n const results = this._queryManager.query(this.all(), { ...input, limit: 1 });\n return results[0] ?? null;\n }\n\n // 3. Handle predicate object (simple equality matching)\n const predicate = input as DbRecordInput<TRecord>;\n return (\n this.all().find((record) => this._queryManager.matchesPredicateObject(record, predicate)) ??\n null\n );\n }\n\n /**\n * Finds multiple records by IDs, predicate object, or query options.\n * @param input - Array of IDs, predicate object, or query options\n * @returns An array of records that match\n * @example\n * ```typescript\n * // By IDs\n * collection.findMany(['user-1', 'user-2']);\n *\n * // By predicate object (simple equality)\n * collection.findMany({ active: true });\n *\n * // By query options (advanced filtering, sorting, pagination)\n * collection.findMany({\n * where: { age: { gte: 18 }, status: { in: ['active', 'pending'] } },\n * orderBy: { createdAt: 'desc' },\n * limit: 10,\n * });\n * ```\n */\n findMany(input: TRecord['id'][] | DbRecordInput<TRecord> | QueryOptions<TRecord>): TRecord[] {\n // 1. Handle array of IDs\n if (Array.isArray(input)) {\n return input.map((id) => this._records.get(id)).filter(Boolean) as TRecord[];\n }\n\n // 2. Check if it's QueryOptions (has where, orderBy, cursor, offset, or limit)\n const hasQueryKeys =\n typeof input === 'object' &&\n ('where' in input ||\n 'orderBy' in input ||\n 'cursor' in input ||\n 'offset' in input ||\n 'limit' in input);\n\n if (hasQueryKeys) {\n const query = input as QueryOptions<TRecord>;\n return this._queryManager.query(this.all(), query);\n }\n\n // 3. Handle predicate object (simple equality matching)\n const predicate = input as DbRecordInput<TRecord>;\n return this.all().filter((record) =>\n this._queryManager.matchesPredicateObject(record, predicate),\n );\n }\n\n /**\n * Count records matching a where clause.\n * @param where - Optional where clause to filter records\n * @returns Number of matching records\n * @example\n * ```typescript\n * collection.count({ status: 'active' });\n * collection.count({ age: { gte: 18, lte: 65 } });\n * ```\n */\n count(where?: Where<TRecord>): number {\n if (!where) {\n return this.size;\n }\n return this._queryManager.query(this.all(), { where }).length;\n }\n\n /**\n * Check if any records match a where clause.\n * @param where - Optional where clause to filter records\n * @returns True if at least one record matches\n * @example\n * ```typescript\n * collection.exists({ email: 'user@example.com' });\n * collection.exists({ status: { in: ['active', 'pending'] } });\n * ```\n */\n exists(where?: Where<TRecord>): boolean {\n if (!where) {\n return !this.isEmpty;\n }\n return this._queryManager.query(this.all(), { where, limit: 1 }).length > 0;\n }\n\n // -- MUTATION METHODS --\n\n /**\n * Inserts a new record into the collection.\n * @param data - The record data to insert.\n * @returns The inserted record.\n */\n insert(data: NewDbRecord<TRecord>): TRecord {\n const record = this._prepareRecord(data);\n this._records.set(record.id, record);\n return record;\n }\n\n /**\n * Inserts multiple records into the collection.\n * @param data - An array of record data to insert.\n * @returns An array of the inserted records.\n */\n insertMany(data: NewDbRecord<TRecord>[]): TRecord[] {\n return data.map((record) => this.insert(record));\n }\n\n /**\n * Updates a single record by ID.\n * @param id - The record ID to update.\n * @param patch - The data to update the record with.\n * @returns The updated record, or `null` if the record was not found.\n */\n update(id: TRecord['id'], patch: TRecord | DbRecordInput<TRecord>): TRecord | null {\n const existingRecord = this._records.get(id);\n if (!existingRecord) {\n return null;\n }\n const updatedRecord = { ...existingRecord, ...patch } as TRecord;\n this._records.set(id, updatedRecord);\n return updatedRecord;\n }\n\n /**\n * Updates multiple records by IDs, predicate object, or query options.\n * @param input - Array of IDs, predicate object, or query options to find records.\n * @param patch - The data to update the records with.\n * @returns Array of updated records.\n */\n updateMany(\n input: TRecord['id'][] | DbRecordInput<TRecord> | QueryOptions<TRecord>,\n patch: TRecord | DbRecordInput<TRecord>,\n ): TRecord[] {\n const recordsToUpdate = this.findMany(input);\n const updatedRecords = recordsToUpdate.map((record) => {\n const updated = { ...record, ...patch } as TRecord;\n this._records.set(record.id, updated);\n return updated;\n });\n return updatedRecords;\n }\n\n /**\n * Deletes a record from the collection by its ID.\n * @param id - The ID of the record to delete.\n * @returns `true` if the record was deleted, `false` if it was not found.\n */\n delete(id: TRecord['id']): boolean {\n return this._records.delete(id);\n }\n\n /**\n * Deletes multiple records by IDs, predicate object, or query options.\n * @param input - Array of IDs, predicate object, or query options to find records.\n * @returns The number of records that were deleted.\n */\n deleteMany(input: TRecord['id'][] | DbRecordInput<TRecord> | QueryOptions<TRecord>): number {\n const recordsToDelete = this.findMany(input);\n recordsToDelete.forEach((record) => this.delete(record.id));\n return recordsToDelete.length;\n }\n\n /**\n * Removes all records from the collection.\n */\n clear(): void {\n this._records.clear();\n this.identityManager.reset();\n }\n\n // -- PRIVATE METHODS --\n\n /**\n * Prepares a record for insertion by generating an ID if it doesn't exist.\n * @param data - The record to prepare.\n * @returns The prepared record.\n */\n private _prepareRecord(data: NewDbRecord<TRecord>): TRecord {\n const record = { ...data } as TRecord;\n if (!record.id) {\n record.id = this.identityManager.fetch();\n } else {\n this.identityManager.set(record.id);\n }\n return record;\n }\n}\n","import { IdentityManager, type IdType } from '@src/id-manager';\nimport { MirageError } from '@src/utils';\n\nimport DbCollection from './DbCollection';\nimport type {\n DbCollectionConfig,\n DbCollectionData,\n DbCollections,\n DbCollectionsFromStaticData,\n DbConfig,\n DbData,\n DbRecord,\n} from './types';\n\n/**\n * A database for storing and managing collections of records.\n * @template TCollections - The type of collections in the database\n * @example\n * const db = DB.create({\n * users: [{ id: \"1\", name: 'John' }],\n * posts: [{ id: \"1\", title: 'Hello' }]\n * });\n * db.users.records;\n */\nexport default class DB<TCollections extends DbCollections> {\n private _collections: Map<keyof TCollections, DbCollection<any>> = new Map();\n\n constructor(config: DbConfig<TCollections> = {}) {\n const { initialData } = config;\n\n if (initialData) {\n this.loadData(initialData);\n }\n\n this.initCollectionAccessors();\n }\n\n // -- COLLECTION MANAGEMENT --\n\n /**\n * Checks if a collection exists.\n * @param name - The name of the collection to check.\n * @returns `true` if the collection exists, `false` otherwise.\n */\n hasCollection(name: keyof TCollections): boolean {\n return this._collections.has(name);\n }\n\n /**\n * Creates a new collection with the given name and initial data.\n * @param name - The name of the collection to create.\n * @param config - The configuration for the collection.\n * @param config.initialData - The initial data to populate the collection with.\n * @param config.identityManager - The identity manager for the collection.\n * @returns The DB instance.\n */\n createCollection<TAttrs extends DbRecord>(\n name: keyof TCollections,\n config?: Omit<DbCollectionConfig<TAttrs>, 'name'>,\n ): DbInstance<TCollections & { [K in keyof TCollections]: DbCollection<TAttrs> }> {\n const collectionName = String(name);\n\n if (this._collections.has(collectionName)) {\n throw new MirageError(`Collection ${collectionName} already exists`);\n }\n\n const collection = new DbCollection<TAttrs>(collectionName, {\n identityManager: config?.identityManager,\n initialData: config?.initialData,\n });\n this._collections.set(collectionName, collection);\n this.initCollectionAccessors();\n\n return this as unknown as DbInstance<\n TCollections & { [K in keyof TCollections]: DbCollection<TAttrs> }\n >;\n }\n\n /**\n * Retrieves a collection by its name.\n * @template T - The collection key\n * @param name - The name of the collection to retrieve.\n * @returns The collection with the specified name.\n * @throws {Error} If the collection does not exist.\n */\n getCollection<T extends keyof TCollections>(name: T): TCollections[T] {\n const collection = this._collections.get(name);\n\n if (!collection) {\n throw new MirageError(`Collection ${String(name)} does not exist`);\n }\n\n return collection as TCollections[T];\n }\n\n /**\n * Retrieves the identity manager for a given collection name.\n * @param collectionName - The name of the collection to get the identity manager for.\n * @returns The identity manager for the given collection name.\n * @throws {Error} If the collection does not exist.\n */\n identityManagerFor(collectionName: keyof TCollections): IdentityManager<IdType> {\n const collection = this.getCollection(collectionName);\n return collection.identityManager;\n }\n\n // -- DATA MANAGEMENT --\n\n /**\n * Loads collections data from a record into the database.\n * @template TData - The type of data to load\n * @param data - Record of collection names and their initial data\n * @returns The DB instance with collection accessors for the loaded data.\n */\n loadData<TData extends Record<string, DbRecord[]>>(\n data: TData,\n ): DbInstance<TCollections & DbCollectionsFromStaticData<TData>> {\n type CollectionNames = keyof TCollections;\n\n for (const key in data) {\n const records = data[key];\n const name = key as CollectionNames;\n\n if (this.hasCollection(name)) {\n this.getCollection(name).insertMany(records);\n } else {\n this.createCollection(name, { initialData: records });\n }\n }\n\n return this as unknown as DbInstance<TCollections & DbCollectionsFromStaticData<TData>>;\n }\n\n /**\n * Empties the data from all collections in the database.\n */\n emptyData(): void {\n this._collections.forEach((collection) => collection.clear());\n }\n\n /**\n * Dumps the data from all collections in the database.\n * @returns A record of collection names and their data.\n */\n dump(): DbData<TCollections> {\n const data = {} as DbData<TCollections>;\n\n this._collections.forEach((collection, name) => {\n data[name] = collection.all() as DbCollectionData<TCollections[typeof name]>;\n });\n\n return data;\n }\n\n // -- PRIVATE METHODS --\n\n private initCollectionAccessors(): void {\n this._collections.forEach((collection, name) => {\n if (!Object.prototype.hasOwnProperty.call(this, name)) {\n Object.defineProperty(this, name, {\n get: () => collection,\n enumerable: true,\n configurable: true,\n });\n }\n });\n }\n}\n\n/**\n * Factory function for creating a DB instance with collection accessors\n * @template TCollections - The type of collections in the database\n * @param config - The configuration for the database\n * @param config.initialData - The initial data to populate collections\n * @returns A DB instance with typed collection accessors\n * @example\n * const db = createDatabase({\n * users: [{ id: \"1\", name: 'John' }],\n * posts: [{ id: \"1\", title: 'Hello world' }]\n * });\n *\n * db.users.records; // [{ id: \"1\", name: 'John' }]\n * db.posts.records; // [{ id: \"1\", title: 'Hello world' }]\n */\nexport function createDatabase<TCollections extends Record<string, DbCollection<any>>>(\n config?: DbConfig<TCollections>,\n): DbInstance<TCollections> {\n return new DB<TCollections>(config) as DbInstance<TCollections>;\n}\n\n/**\n * Type for a DB instance with collection accessors\n * @template TCollections - The type of collections in the database\n */\nexport type DbInstance<TCollections extends Record<string, DbCollection<any>>> =\n DB<TCollections> & {\n [K in keyof TCollections]: TCollections[K];\n };\n","import { DbCollection } from '@src/db';\n\nimport type { BaseModelInstance, ModelStatus, NewBaseModelInstance, NewModelAttrs } from './types';\n\n/**\n * BaseModel class for managing basic model operations without schema dependencies\n * Handles basic CRUD operations, db updates, attribute management, and status tracking\n * @template TAttrs - The model attributes type (e.g., { id: string, name: string })\n * @template TSerializer - The serializer type for custom JSON serialization\n */\nexport default class BaseModel<TAttrs extends { id: any }, TSerializer = undefined> {\n public readonly modelName: string;\n public readonly collectionName: string;\n\n protected _attrs: NewModelAttrs<TAttrs>;\n protected _dbCollection: DbCollection<TAttrs>;\n protected _serializer?: TSerializer;\n protected _status: ModelStatus;\n\n constructor(\n modelName: string,\n collectionName: string,\n attrs: NewModelAttrs<TAttrs>,\n dbCollection?: DbCollection<TAttrs>,\n serializer?: TSerializer,\n ) {\n this.modelName = modelName;\n this.collectionName = collectionName;\n\n this._attrs = { ...attrs, id: attrs.id ?? null } as NewModelAttrs<TAttrs>;\n this._dbCollection = dbCollection ?? new DbCollection<TAttrs>(collectionName);\n this._serializer = serializer;\n this._status = this._checkStatus();\n }\n\n // -- GETTERS --\n\n /**\n * Getter for the protected id attribute\n * @returns The id of the model\n */\n get id(): TAttrs['id'] | null {\n return this._attrs.id;\n }\n\n /**\n * Getter for the model attributes\n * @returns A copy of the model attributes\n */\n get attrs(): NewModelAttrs<TAttrs> {\n return { ...this._attrs };\n }\n\n // -- DATABASE OPERATIONS --\n\n /**\n * Save the model to the database\n * @returns The model with saved instance type\n */\n save(): this & BaseModelInstance<TAttrs, TSerializer> {\n if (this.isNew() || !this.id) {\n const record = this._dbCollection.insert(this._attrs);\n\n this._attrs = record;\n this._status = 'saved';\n } else {\n this._dbCollection.update(this.id, this._attrs as TAttrs);\n }\n\n return this as this & BaseModelInstance<TAttrs>;\n }\n\n /**\n * Update the model attributes and save the model\n * @param attrs - The attributes to update\n * @returns The model instance for chaining\n */\n update(attrs: Partial<TAttrs>): this & BaseModelInstance<TAttrs, TSerializer> {\n Object.assign(this._attrs, attrs);\n return this.save();\n }\n\n /**\n * Reload the model from the database\n * @returns The model with saved instance type\n */\n reload(): this & BaseModelInstance<TAttrs, TSerializer> {\n if (this._attrs.id) {\n const record = this._dbCollection.find(this.id);\n\n if (record) {\n this._attrs = record;\n this._status = 'saved';\n }\n } else {\n return this.save();\n }\n\n return this as this & BaseModelInstance<TAttrs>;\n }\n\n /**\n * Destroy the model from the database\n * @returns The model with new instance type\n */\n destroy(): this & NewBaseModelInstance<TAttrs, TSerializer> {\n if (this.isSaved() && this.id) {\n this._dbCollection.delete(this.id);\n this._attrs = { ...this._attrs, id: null };\n this._status = 'new';\n }\n\n return this as this & NewBaseModelInstance<TAttrs, TSerializer>;\n }\n\n // -- STATUS CHECKS --\n\n /**\n * Check if the model is new\n * @returns True if the model is new, false otherwise\n */\n isNew(): boolean {\n return this._status === 'new';\n }\n\n /**\n * Check if the model is saved\n * @returns True if the model is saved, false otherwise\n */\n isSaved(): boolean {\n return this._status === 'saved';\n }\n\n // -- SERIALIZATION --\n\n /**\n * Serialize the model to a JSON object\n * @returns The serialized model using the configured serializer or raw attributes\n */\n toJSON(): TSerializer extends { serialize(model: any): infer TSerializedModel }\n ? TSerializedModel\n : TAttrs {\n if (\n this._serializer &&\n typeof this._serializer === 'object' &&\n 'serialize' in this._serializer &&\n typeof this._serializer.serialize === 'function'\n ) {\n return this._serializer.serialize(this);\n }\n // Type assertion needed due to conditional return type complexity\n // TypeScript can't verify TAttrs matches the conditional type in all cases\n return { ...this._attrs } as any;\n }\n\n /**\n * Serialize the model to a string\n * @returns The simple string representation of the model and its id\n */\n toString(): string {\n const idLabel = this.id ? `(${this.id})` : '';\n return `model:${this.modelName}${idLabel}`;\n }\n\n // -- PRIVATE METHODS --\n\n private _checkStatus(): ModelStatus {\n // Check if this model already exists in the database\n if (this.id && this._dbCollection.find(this.id)) {\n return 'saved';\n }\n return 'new';\n }\n}\n","import type { SchemaCollections } from '@src/schema';\n\nimport type { ModelAttrs, ModelInstance, ModelTemplate, ModelUpdateAttrs } from './types';\n\n/**\n * A collection of models with array-like interface\n * @template TTemplate - The model template (most important for users)\n * @template TSchema - The schema collections type for enhanced type inference\n * @template TSerializer - The serializer type\n */\nexport default class ModelCollection<\n TTemplate extends ModelTemplate = ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TSerializer = undefined,\n> {\n private readonly _template: TTemplate;\n public readonly collectionName: string;\n public models: Array<ModelInstance<TTemplate, TSchema, TSerializer>>;\n protected _serializer?: TSerializer;\n\n constructor(\n template: TTemplate,\n models?: Array<ModelInstance<TTemplate, TSchema, TSerializer>>,\n serializer?: TSerializer,\n ) {\n this._template = template;\n this._serializer = serializer;\n\n this.collectionName = template.collectionName;\n this.models = [...(models ?? [])];\n }\n\n // -- GETTERS --\n\n /**\n * Get the length of the collection\n * @returns The number of models in the collection\n */\n get length(): number {\n return this.models.length;\n }\n\n /**\n * Check if the collection is empty\n * @returns True if the collection is empty, false otherwise\n */\n get isEmpty(): boolean {\n return this.models.length === 0;\n }\n\n /**\n * Get a model by index\n * @param index - The index of the model to get\n * @returns The model at the given index or undefined\n */\n at(index: number): ModelInstance<TTemplate, TSchema, TSerializer> | undefined {\n return this.models[index];\n }\n\n /**\n * Get the first model in the collection\n * @returns The first model or null if the collection is empty\n */\n first(): ModelInstance<TTemplate, TSchema, TSerializer> | null {\n return this.models[0] || null;\n }\n\n /**\n * Get the last model in the collection\n * @returns The last model or null if the collection is empty\n */\n last(): ModelInstance<TTemplate, TSchema, TSerializer> | null {\n return this.models[this.models.length - 1] || null;\n }\n\n // -- ARRAY-LIKE ITERATION METHODS --\n\n /**\n * Execute a function for each model in the collection\n * @param cb - The function to execute for each model\n */\n forEach(\n cb: (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n index: number,\n collection: this,\n ) => void,\n ): void {\n this.models.forEach((model, index) => cb(model, index, this));\n }\n\n /**\n * Create a new array with the results of calling a function for each model\n * @param cb - The function to call for each model\n * @returns A new array with the results\n */\n map(\n cb: (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n index: number,\n collection: this,\n ) => ModelInstance<TTemplate, TSchema, TSerializer>,\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n const mappedModels = this.models.map((model, index) => cb(model, index, this));\n return new ModelCollection(this._template, mappedModels, this._serializer);\n }\n\n /**\n * Create a new collection with models that pass a test\n * @param cb - The test function\n * @returns A new ModelCollection with the filtered models\n */\n filter(\n cb: (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n index: number,\n collection: this,\n ) => boolean,\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n const filteredModels = this.models.filter((model, index) => cb(model, index, this));\n return new ModelCollection(this._template, filteredModels, this._serializer);\n }\n\n /**\n * Find the first model that satisfies a test\n * @param cb - The test function\n * @returns The first model that passes the test, or undefined\n */\n find(\n cb: (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n index: number,\n collection: this,\n ) => boolean,\n ): ModelInstance<TTemplate, TSchema, TSerializer> | undefined {\n return this.models.find((model, index) => cb(model, index, this));\n }\n\n /**\n * Check if at least one model satisfies a test\n * @param cb - The test function\n * @returns True if at least one model passes the test\n */\n some(\n cb: (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n index: number,\n collection: this,\n ) => boolean,\n ): boolean {\n return this.models.some((model, index) => cb(model, index, this));\n }\n\n /**\n * Check if all models satisfy a test\n * @param cb - The test function\n * @returns True if all models pass the test\n */\n every(\n cb: (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n index: number,\n collection: this,\n ) => boolean,\n ): boolean {\n return this.models.every((model, index) => cb(model, index, this));\n }\n\n // -- ARRAY-LIKE UTILITY METHODS --\n\n /**\n * Concatenate this collection with other collections or arrays\n * @param others - Other collections or arrays to concatenate\n * @returns A new ModelCollection with all models\n */\n concat(\n ...others: (\n | ModelCollection<TTemplate, TSchema, TSerializer>\n | ModelInstance<TTemplate, TSchema, TSerializer>[]\n )[]\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n const allModels = [\n ...this.models,\n ...others.flatMap((other) => (Array.isArray(other) ? other : other.models)),\n ];\n return new ModelCollection(this._template, allModels, this._serializer);\n }\n\n /**\n * Check if the collection includes a specific model\n * @param model - The model to check for\n * @returns True if the model is in the collection\n */\n includes(model: ModelInstance<TTemplate, TSchema, TSerializer>): boolean {\n return this.models.includes(model);\n }\n\n /**\n * Find the index of a model in the collection\n * @param model - The model to find\n * @returns The index of the model, or -1 if not found\n */\n indexOf(model: ModelInstance<TTemplate, TSchema, TSerializer>): number {\n return this.models.indexOf(model);\n }\n\n /**\n * Sort the models in the collection\n * @param compareFn - The comparison function\n * @returns A new sorted ModelCollection\n */\n sort(\n compareFn?: (\n a: ModelInstance<TTemplate, TSchema, TSerializer>,\n b: ModelInstance<TTemplate, TSchema, TSerializer>,\n ) => number,\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n const sortedModels = [...this.models].sort(compareFn);\n return new ModelCollection(this._template, sortedModels, this._serializer);\n }\n\n /**\n * Reverse the order of models in the collection\n * @returns A new reversed ModelCollection\n */\n reverse(): ModelCollection<TTemplate, TSchema, TSerializer> {\n const reversedModels = [...this.models].reverse();\n return new ModelCollection(this._template, reversedModels, this._serializer);\n }\n\n // -- CRUD OPERATIONS --\n\n /**\n * Add a model to the end of the collection (alias for push)\n * @param model - The model to add\n */\n add(model: ModelInstance<TTemplate, TSchema, TSerializer>): void {\n this.models.push(model);\n }\n\n /**\n * Remove a model from the collection\n * @param model - The model to remove\n * @returns True if the model was removed, false if not found\n */\n remove(model: ModelInstance<TTemplate, TSchema, TSerializer>): boolean {\n const index = this.indexOf(model);\n if (index !== -1) {\n this.models.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Save all models in the collection\n * @returns The collection instance for chaining\n */\n save(): this {\n this.models = this.models.map(\n (model) => model.save() as ModelInstance<TTemplate, TSchema, TSerializer>,\n );\n return this;\n }\n\n /**\n * Destroy all models in the collection\n * @returns The collection instance for chaining\n */\n destroy(): this {\n this.models.forEach((model) => model.destroy());\n this.models = [];\n return this;\n }\n\n /**\n * Reload all models in the collection from the database\n * @returns The collection instance for chaining\n */\n reload(): this {\n this.models = this.models.map(\n (model) => model.reload() as ModelInstance<TTemplate, TSchema, TSerializer>,\n );\n return this;\n }\n\n /**\n * Update all models in the collection with the given attributes\n * @param attrs - The attributes to update\n * @returns The collection instance for chaining\n */\n update(attrs: ModelUpdateAttrs<TTemplate, TSchema>): this {\n this.models = this.models.map(\n (model) => model.update(attrs) as ModelInstance<TTemplate, TSchema, TSerializer>,\n );\n return this;\n }\n\n // -- SERIALIZATION --\n\n /**\n * Convert the collection to a plain array\n * @returns An array of the models\n */\n toArray(): ModelInstance<TTemplate, TSchema, TSerializer>[] {\n return [...this.models];\n }\n\n /**\n * Get a string representation of the collection\n * @returns A string representation showing the collection name and count\n */\n toString(): string {\n return `collection:${this.collectionName}(${this.models.map((model) => model.toString()).join(', ')})`;\n }\n\n /**\n * Convert the collection to JSON\n * Uses serializer if configured, otherwise returns array of raw attributes\n * @returns A serialized representation of the collection\n */\n toJSON(): TSerializer extends {\n serializeCollection(collection: any): infer TSerializedCollection;\n }\n ? TSerializedCollection\n : ModelAttrs<TTemplate, TSchema>[] {\n if (\n this._serializer &&\n typeof this._serializer === 'object' &&\n 'serializeCollection' in this._serializer &&\n typeof this._serializer.serializeCollection === 'function'\n ) {\n return this._serializer.serializeCollection(this);\n }\n // Type assertion needed: Array of attrs may not exactly match conditional return type\n return this.models.map((model) => model.attrs) as any;\n }\n\n /**\n * Make the collection iterable\n * @returns An iterator for the models\n */\n [Symbol.iterator](): Iterator<ModelInstance<TTemplate, TSchema, TSerializer>> {\n return this.models[Symbol.iterator]();\n }\n}\n","import type { SchemaCollections } from '@src/schema';\n\nimport type ModelCollection from './ModelCollection';\nimport type { ModelInstance, ModelTemplate } from './types';\n\n/**\n * Type guard to check if a value is a model instance\n * @param value - The value to check\n * @returns True if the value is a model instance\n */\nexport function isModelInstance<TSchema extends SchemaCollections>(\n value: unknown,\n): value is ModelInstance<ModelTemplate, TSchema> {\n return value !== null && typeof value === 'object' && 'id' in value && 'modelName' in value;\n}\n\n/**\n * Type guard to check if a value is an array of model instances\n * @param value - The value to check\n * @returns True if the value is an array of model instances\n */\nexport function isModelInstanceArray<TSchema extends SchemaCollections>(\n value: unknown,\n): value is ModelInstance<ModelTemplate, TSchema>[] {\n return Array.isArray(value) && value.every((item) => isModelInstance<TSchema>(item));\n}\n\n/**\n * Type guard to check if a value is a model collection\n * @param value - The value to check\n * @returns True if the value is a model collection\n */\nexport function isModelCollection<TSchema extends SchemaCollections>(\n value: unknown,\n): value is ModelCollection<ModelTemplate, TSchema> {\n return (\n value !== null &&\n typeof value === 'object' &&\n 'models' in value &&\n Array.isArray((value as { models: unknown }).models)\n );\n}\n\n/**\n * Type guard to check if a value is an array (of any type)\n * @param value - The value to check\n * @returns True if the value is an array\n */\nexport function isArray(value: unknown): value is unknown[] {\n return Array.isArray(value);\n}\n","import type { Relationships } from '@src/associations';\nimport type { SchemaCollections, SchemaInstance } from '@src/schema';\nimport { MirageError } from '@src/utils';\n\nimport type Model from './Model';\nimport ModelCollection from './ModelCollection';\nimport { isArray, isModelCollection, isModelInstance } from './typeGuards';\nimport type {\n ForeignKeyValue,\n ModelAttrs,\n ModelId,\n ModelInstance,\n ModelRelationships,\n ModelTemplate,\n PendingRelationshipOperation,\n RelatedModelAttrs,\n RelationshipDef,\n RelationshipDefs,\n RelationshipNames,\n RelationshipTargetModel,\n RelationshipUpdateResult,\n RelationshipsByTemplate,\n} from './types';\n\n// TODO: Improve type inference for related models accross the helper methods\n/**\n * ModelRelationshipsManager - Handles all relationship operations\n * Separated from core model logic for better maintainability\n * @template TTemplate - The model template\n * @template TSchema - The schema collections type\n * @template TRelationships - The relationships configuration\n */\nexport default class RelationshipsManager<\n TTemplate extends ModelTemplate,\n TSchema extends SchemaCollections,\n TRelationships extends ModelRelationships = RelationshipsByTemplate<TTemplate, TSchema>,\n> {\n public isApplyingPendingUpdates: boolean = false;\n\n private _model: Model<TTemplate, TSchema>;\n private _pendingRelationshipOperations: PendingRelationshipOperation[] = [];\n private _relationshipDefs?: RelationshipDefs<TRelationships>;\n private _inverseMap: Map<string, string | null | undefined> = new Map();\n private _schema: SchemaInstance<TSchema>;\n\n constructor(\n model: Model<TTemplate, TSchema>,\n schema: SchemaInstance<TSchema>,\n relationships?: TRelationships,\n ) {\n this._model = model;\n this._schema = schema;\n this._relationshipDefs = this._parseRelationshipDefs(relationships);\n }\n\n // -- GETTERS --\n\n /**\n * Getter for the relationship definitions\n * @returns The relationship definitions\n */\n get relationshipDefs(): RelationshipDefs<TRelationships> | undefined {\n return this._relationshipDefs;\n }\n\n /**\n * Getter for the schema\n * @returns The schema\n */\n get schema(): SchemaInstance<TSchema> {\n return this._schema;\n }\n\n // -- PENDING RELATIONSHIP UPDATES --\n\n /**\n * Set pending relationship updates by analyzing changes\n * Handles both model instances and raw foreign key values\n * Should be called BEFORE updating model attributes so we can access old FKs\n * @param relationshipUpdates - Raw relationship values from attrs (can be models or FK values)\n */\n setPendingRelationshipUpdates(\n relationshipUpdates: Partial<RelatedModelAttrs<TSchema, TRelationships>>,\n ): void {\n for (const relationshipName in relationshipUpdates) {\n const relationshipDef = this._relationshipDefs?.[relationshipName];\n if (!relationshipDef) continue;\n\n const relationship = relationshipDef.relationship;\n const { foreignKey, type, targetModel } = relationship;\n const rawValue = relationshipUpdates[relationshipName];\n let processedValue: unknown = rawValue;\n\n // If rawValue is a raw FK value (string or string[]), fetch the actual model(s)\n if (\n typeof rawValue === 'string' ||\n typeof rawValue === 'number' ||\n (isArray(rawValue) &&\n (rawValue as unknown[]).every(\n (v: unknown) => typeof v === 'string' || typeof v === 'number',\n ))\n ) {\n const targetCollection = this._schema.getCollection(targetModel.collectionName);\n\n if (type === 'belongsTo') {\n const id = rawValue as RelatedModelAttrs<TSchema, TRelationships>['id'];\n processedValue = targetCollection.find(id);\n } else if (type === 'hasMany') {\n const ids = rawValue as RelatedModelAttrs<TSchema, TRelationships>['id'][];\n const models = ids\n .map((id) => targetCollection.find(id))\n .filter((m): m is ModelInstance<ModelTemplate, TSchema> => isModelInstance<TSchema>(m));\n processedValue = new ModelCollection(targetModel, models);\n }\n }\n\n // Get CURRENT foreign key value from model (the OLD value before update)\n const currentForeignKey = this._getForeignKeyValue(foreignKey);\n // Extract NEW foreign key from the relationship value\n const newForeignKey = this._extractForeignKeyFromValue(relationship, processedValue);\n // Check if the relationship is actually changing\n const hasChanged = this._hasForeignKeyChanged(currentForeignKey, newForeignKey);\n\n // For saved models with changed relationships:\n if (this._model.isSaved()) {\n // unlink old\n if (hasChanged) {\n const oldValue = this.related(relationshipName);\n this._pendingRelationshipOperations.push({\n relationshipName,\n link: false,\n unlink: true,\n value: oldValue,\n });\n }\n // link new\n if (this._hasForeignKeyValue(newForeignKey)) {\n this._pendingRelationshipOperations.push({\n relationshipName,\n link: true,\n unlink: false,\n value: processedValue,\n });\n }\n } else if (this._model.isNew() && this._hasForeignKeyValue(newForeignKey)) {\n // For new models: only link (no unlink needed)\n this._pendingRelationshipOperations.push({\n relationshipName,\n link: true,\n unlink: false,\n value: processedValue,\n });\n }\n }\n }\n\n /**\n * Apply inverse relationship updates for pending operations\n * Should be called AFTER saving the model (when FK changes are in the database)\n * Note: FK updates are already applied to the model by _processAttrs or link/unlink methods\n */\n applyPendingInverseUpdates(): void {\n if (this._pendingRelationshipOperations.length === 0) return;\n\n this.isApplyingPendingUpdates = true;\n\n try {\n // Step 1: Process all unlinks first - update inverse relationships on old targets\n for (const operation of this._pendingRelationshipOperations) {\n if (operation.unlink && operation.value !== undefined) {\n // Update inverse relationships on the old related models\n if (isModelInstance<TSchema>(operation.value)) {\n this._updateInverseRelationship(operation.relationshipName, operation.value, 'unlink');\n } else if (isModelCollection<TSchema>(operation.value)) {\n operation.value.models.forEach((model) => {\n this._updateInverseRelationship(operation.relationshipName, model, 'unlink');\n });\n }\n }\n }\n\n // Step 2: Process all links - update inverse relationships\n for (const operation of this._pendingRelationshipOperations) {\n if (operation.link && operation.value !== undefined) {\n const relationshipDef = this._relationshipDefs?.[operation.relationshipName];\n if (!relationshipDef) continue;\n\n // Update inverse relationships on new targets\n if (isModelInstance<TSchema>(operation.value)) {\n this._updateInverseRelationship(operation.relationshipName, operation.value, 'link');\n } else if (isModelCollection<TSchema>(operation.value)) {\n operation.value.models.forEach((model) => {\n this._updateInverseRelationship(operation.relationshipName, model, 'link');\n });\n } else if (isArray(operation.value)) {\n const models = operation.value.filter(\n (item): item is ModelInstance<ModelTemplate, TSchema> =>\n isModelInstance<TSchema>(item),\n );\n models.forEach((model) => {\n this._updateInverseRelationship(operation.relationshipName, model, 'link');\n });\n }\n }\n }\n\n // Clear pending operations\n this._pendingRelationshipOperations = [];\n } finally {\n this.isApplyingPendingUpdates = false;\n }\n }\n\n // -- PUBLIC RELATIONSHIP METHODS --\n\n /**\n * Link this model to another model via a relationship\n * Returns foreign key updates without mutating model state\n * @param relationshipName - The name of the relationship\n * @param targetModel - The model to link to (or null to unlink)\n * @returns FK updates to apply and inverse relationship updates\n */\n link<K extends RelationshipNames<TRelationships>>(\n relationshipName: K,\n targetModel: RelationshipTargetModel<TSchema, TRelationships, K>,\n ): RelationshipUpdateResult {\n const foreignKeyUpdates: Record<string, ForeignKeyValue> = {};\n const relationshipNameString = String(relationshipName);\n\n if (!this._relationshipDefs || !this._schema) {\n return { foreignKeyUpdates };\n }\n\n const relationshipDef = this._getRelationshipDef(relationshipNameString);\n if (!relationshipDef) {\n return { foreignKeyUpdates };\n }\n\n const relationship = relationshipDef.relationship;\n const { type, foreignKey } = relationship;\n\n if (type === 'belongsTo') {\n const currentForeignKeyValue = this._getForeignKeyValue(foreignKey);\n const newTargetId = isModelInstance<TSchema>(targetModel) ? (targetModel.id as string) : null;\n\n // Only unlink if the relationship is actually changing\n if (currentForeignKeyValue && currentForeignKeyValue !== newTargetId) {\n // Get old target for inverse update\n const targetCollectionName = relationship.targetModel.collectionName;\n const targetCollection = this._schema.getCollection(targetCollectionName);\n // Type assertion needed: find() accepts complex union of ID types\n const oldTarget = targetCollection.find(currentForeignKeyValue as any);\n\n // Update inverse relationship on old target\n if (oldTarget && isModelInstance<TSchema>(oldTarget)) {\n this._updateInverseRelationship(relationshipNameString, oldTarget, 'unlink');\n }\n }\n\n // Set new FK value\n foreignKeyUpdates[foreignKey] = newTargetId;\n\n // Update inverse relationship on new target\n if (isModelInstance<TSchema>(targetModel)) {\n this._updateInverseRelationship(relationshipNameString, targetModel, 'link');\n }\n } else if (type === 'hasMany') {\n // Extract new target IDs\n let newTargetIds: (string | number)[] = [];\n let newTargetModels: ModelInstance<ModelTemplate, TSchema>[] = [];\n\n if (isModelCollection<TSchema>(targetModel)) {\n newTargetModels = targetModel.models;\n newTargetIds = newTargetModels.map((m) => m.id as string);\n } else if (isArray(targetModel)) {\n newTargetModels = targetModel.filter(\n (item): item is ModelInstance<ModelTemplate, TSchema> => isModelInstance<TSchema>(item),\n );\n newTargetIds = newTargetModels.map((m) => m.id as string);\n }\n\n // Get current IDs\n const currentForeignKeyValues = this._getForeignKeyValue(foreignKey);\n const currentIds = this._extractIdsArray(currentForeignKeyValues);\n\n // Check if the relationship is actually changing\n const idsChanged =\n currentIds.length !== newTargetIds.length ||\n !currentIds.every((id, index) => id === newTargetIds[index]);\n\n if (currentIds.length > 0 && idsChanged) {\n // Get old targets for inverse updates\n const targetCollectionName = relationship.targetModel.collectionName;\n const targetCollection = this._schema.getCollection(targetCollectionName);\n\n currentIds.forEach((id) => {\n // Type assertion needed: find() accepts complex union of ID types\n const oldTarget = targetCollection.find(id as any);\n if (oldTarget && isModelInstance<TSchema>(oldTarget)) {\n this._updateInverseRelationship(relationshipNameString, oldTarget, 'unlink');\n }\n });\n }\n\n // Set new FK value\n foreignKeyUpdates[foreignKey] = newTargetIds as string[];\n\n // Update inverse relationships on new targets\n newTargetModels.forEach((model) => {\n this._updateInverseRelationship(relationshipNameString, model, 'link');\n });\n }\n\n return { foreignKeyUpdates };\n }\n\n /**\n * Unlink this model from another model via a relationship\n * Returns foreign key updates without mutating model state\n * @param relationshipName - The name of the relationship\n * @param targetModel - The specific model to unlink (optional for hasMany, unlinks all if not provided)\n * @returns FK updates to apply\n */\n unlink<K extends RelationshipNames<TRelationships>>(\n relationshipName: K,\n targetModel?: RelationshipTargetModel<TSchema, TRelationships, K>,\n ): RelationshipUpdateResult {\n const foreignKeyUpdates: Record<string, ForeignKeyValue> = {};\n const relationshipNameString = String(relationshipName);\n\n if (!this._relationshipDefs || !this._schema) {\n return { foreignKeyUpdates };\n }\n\n const relationshipDef = this._getRelationshipDef(relationshipNameString);\n if (!relationshipDef) {\n return { foreignKeyUpdates };\n }\n\n const relationship = relationshipDef.relationship;\n const { type, foreignKey } = relationship;\n\n if (type === 'belongsTo') {\n // Get the current target model before unlinking for inverse update\n const currentTarget = this.related(relationshipNameString);\n\n // Set FK to null\n foreignKeyUpdates[foreignKey] = null;\n\n // Update inverse relationship\n if (isModelInstance<TSchema>(currentTarget)) {\n this._updateInverseRelationship(relationshipNameString, currentTarget, 'unlink');\n }\n } else if (type === 'hasMany') {\n if (targetModel) {\n // Unlink specific item(s)\n const currentIds = this._extractIdsArray(this._getForeignKeyValue(foreignKey));\n let modelsToUnlink: ModelInstance<ModelTemplate, TSchema>[] = [];\n\n if (isModelInstance<TSchema>(targetModel)) {\n // Single model\n modelsToUnlink = [targetModel];\n } else if (isModelCollection<TSchema>(targetModel)) {\n // ModelCollection\n modelsToUnlink = targetModel.models;\n } else if (isArray(targetModel)) {\n // Array of models\n modelsToUnlink = targetModel.filter(\n (item): item is ModelInstance<ModelTemplate, TSchema> => isModelInstance<TSchema>(item),\n );\n }\n\n // Filter out the IDs of models to unlink\n const idsToRemove = new Set<string>(modelsToUnlink.map((m) => m.id));\n const newIds = currentIds.filter((id) => !idsToRemove.has(id));\n foreignKeyUpdates[foreignKey] = newIds;\n\n // Update inverse relationships\n modelsToUnlink.forEach((model) => {\n this._updateInverseRelationship(relationshipNameString, model, 'unlink');\n });\n } else {\n // Unlink all items (no targetModel provided)\n const currentTargets = this.related(relationshipNameString);\n foreignKeyUpdates[foreignKey] = [];\n\n // Update inverse relationships\n if (isModelCollection<TSchema>(currentTargets)) {\n currentTargets.models.forEach((target) => {\n this._updateInverseRelationship(relationshipNameString, target, 'unlink');\n });\n }\n }\n }\n\n return { foreignKeyUpdates };\n }\n\n /**\n * Get related model(s) for a relationship with proper typing\n * @param relationshipName - The relationship name\n * @returns The related model(s) or null/empty collection\n */\n related<K extends RelationshipNames<TRelationships>>(\n relationshipName: K,\n ): RelationshipTargetModel<TSchema, TRelationships, K> | null {\n if (!this._schema || !this._relationshipDefs) return null;\n\n const relationshipDef = this._getRelationshipDef(relationshipName);\n if (!relationshipDef) return null;\n\n const relationship = relationshipDef.relationship;\n const { type, foreignKey, targetModel } = relationship;\n\n const targetCollection = this._schema.getCollection(targetModel.collectionName);\n if (!targetCollection) {\n throw new MirageError(`Collection for model ${targetModel.modelName} not found in schema`);\n }\n\n if (type === 'belongsTo') {\n const foreignKeyValue = this._getForeignKeyValue(foreignKey);\n const singleId = this._extractSingleId(foreignKeyValue);\n\n if (singleId === null) {\n return null;\n }\n\n return targetCollection.find(singleId) as RelationshipTargetModel<TSchema, TRelationships, K>;\n }\n\n if (type === 'hasMany') {\n const foreignKeyValues = this._getForeignKeyValue(foreignKey);\n const idsArray = this._extractIdsArray(foreignKeyValues);\n\n if (idsArray.length === 0) {\n return new ModelCollection(targetModel, []) as RelationshipTargetModel<\n TSchema,\n TRelationships,\n K\n >;\n }\n\n const relatedModels = idsArray\n .map((id) => targetCollection.find(id))\n .filter((model): model is ModelInstance<ModelTemplate, TSchema> => model !== null);\n\n return new ModelCollection(targetModel, relatedModels) as RelationshipTargetModel<\n TSchema,\n TRelationships,\n K\n >;\n }\n\n return null;\n }\n\n // -- PRIVATE HELPER METHODS --\n\n /**\n * Get a relationship definition by name\n * @param relationshipName - The relationship name\n * @returns The relationship definition or undefined\n */\n private _getRelationshipDef<K extends RelationshipNames<TRelationships>>(\n relationshipName: K,\n ): RelationshipDef<TRelationships[K]> | undefined {\n if (!this._relationshipDefs) return undefined;\n return this._relationshipDefs[relationshipName];\n }\n\n /**\n * Get model attributes with proper typing\n * @returns Model attributes\n */\n private _getModelAttrs(): ModelAttrs<TTemplate, TSchema> {\n // Access private property - we know this exists\n return this._model.attrs as ModelAttrs<TTemplate, TSchema>;\n }\n\n /**\n * Get foreign key value from model attrs\n * @param foreignKey - The foreign key to retrieve\n * @returns The foreign key value\n */\n private _getForeignKeyValue(foreignKey: string): ForeignKeyValue {\n const attrs = this._getModelAttrs();\n return attrs[foreignKey as keyof typeof attrs] as ForeignKeyValue;\n }\n\n /**\n * Extract a single ID from foreign key value (for belongsTo relationships)\n * @param foreignKeyValue - The foreign key value\n * @returns A single ID or null\n */\n private _extractSingleId(foreignKeyValue: ForeignKeyValue): ModelId<TTemplate> | null {\n if (foreignKeyValue === null || foreignKeyValue === undefined) {\n return null;\n }\n if (Array.isArray(foreignKeyValue)) {\n // Arrays should not be used for belongsTo relationships\n return null;\n }\n return foreignKeyValue as ModelId<TTemplate>;\n }\n\n /**\n * Extract array of IDs from foreign key value\n * @param foreignKeyValue - The foreign key value\n * @returns Array of IDs\n */\n private _extractIdsArray(foreignKeyValue: ForeignKeyValue): ModelId<TTemplate>[] {\n if (Array.isArray(foreignKeyValue)) {\n return foreignKeyValue as ModelId<TTemplate>[];\n }\n return [];\n }\n\n /**\n * Parse relationships configuration into internal relationship definitions\n * This includes finding inverse relationships for bidirectional updates\n * @param relationships - The relationships configuration from schema\n * @returns Parsed relationship definitions with inverse information\n */\n private _parseRelationshipDefs(\n relationships?: TRelationships,\n ): RelationshipDefs<TRelationships> | undefined {\n if (!relationships || !this._schema) return undefined;\n\n const relationshipDefs: RelationshipDefs<TRelationships> =\n {} as RelationshipDefs<TRelationships>;\n\n // Parse each relationship and find its inverse\n for (const relationshipName in relationships) {\n const relationship = relationships[relationshipName];\n const relationshipDef: RelationshipDef<typeof relationship> = {\n relationship,\n };\n\n // Store the explicit inverse setting in the map\n if ('inverse' in relationship) {\n this._inverseMap.set(relationshipName, relationship.inverse);\n }\n\n // Handle inverse relationship based on the inverse option\n const inverseOption = 'inverse' in relationship ? relationship.inverse : undefined;\n\n if (inverseOption === null) {\n // Explicitly disabled inverse - don't set inverse relationship\n relationshipDef.inverse = undefined;\n } else if (typeof inverseOption === 'string') {\n // Explicit inverse relationship name provided\n const targetCollectionName = relationship.targetModel.collectionName;\n const targetCollection = this._schema.getCollection(targetCollectionName);\n\n if (targetCollection.relationships && targetCollection.relationships[inverseOption]) {\n const inverseRelationship = targetCollection.relationships[inverseOption];\n relationshipDef.inverse = {\n foreignKey: inverseRelationship.foreignKey,\n relationshipName: inverseOption,\n targetModel: relationship.targetModel,\n type: inverseRelationship.type,\n };\n }\n } else {\n // Auto-detect inverse relationship (undefined or not specified)\n const targetCollectionName = relationship.targetModel.collectionName;\n const targetCollection = this._schema.getCollection(targetCollectionName);\n\n if (targetCollection.relationships) {\n for (const inverseRelationshipName in targetCollection.relationships) {\n const inverseRelationship = targetCollection.relationships[inverseRelationshipName];\n if (inverseRelationship.targetModel.modelName === this._model.modelName) {\n relationshipDef.inverse = {\n foreignKey: inverseRelationship.foreignKey,\n relationshipName: inverseRelationshipName,\n targetModel: relationship.targetModel,\n type: inverseRelationship.type,\n };\n break;\n }\n }\n }\n }\n\n relationshipDefs[relationshipName] = relationshipDef;\n }\n\n return relationshipDefs;\n }\n\n /**\n * Update the inverse relationship on the target model\n * @param relationshipName - The name of the relationship being updated\n * @param targetModel - The target model to update\n * @param action - Whether to 'link' or 'unlink'\n */\n private _updateInverseRelationship(\n relationshipName: string,\n targetModel: ModelInstance<ModelTemplate, TSchema>,\n action: 'link' | 'unlink',\n ): void {\n if (!this._relationshipDefs || !this._schema) return;\n\n const relationshipDef = this._relationshipDefs[relationshipName];\n if (!relationshipDef?.inverse) {\n // No inverse relationship defined, so don't update the target model\n return;\n }\n\n const modelId = this._model.id;\n if (!modelId) return;\n\n const { inverse } = relationshipDef;\n const { type, foreignKey, relationshipName: inverseRelName } = inverse;\n\n // Check if the target model's inverse relationship has inverse: null\n // If so, don't sync (respect the explicit disable)\n // Type assertion needed: Accessing private _relationshipsManager property on model\n // This is internal logic for inverse sync, accessing implementation details\n const targetRelManager = (targetModel as any)._relationshipsManager as RelationshipsManager<\n any,\n TSchema\n >;\n if (targetRelManager?._inverseMap.has(inverseRelName)) {\n const targetInverseSetting = targetRelManager._inverseMap.get(inverseRelName);\n if (targetInverseSetting === null) {\n // Target relationship explicitly disabled inverse sync\n return;\n }\n }\n\n // Get the target model's ID to find it in the database\n const targetModelId = targetModel.id;\n if (!targetModelId) return;\n\n // Get the target collection from schema\n const targetCollectionName = relationshipDef.relationship.targetModel.collectionName;\n\n // Find the target model in the database\n const targetDbRecord = this._schema.db.getCollection(targetCollectionName).find(targetModelId);\n if (!targetDbRecord) return;\n\n // Prepare the update object for the target model\n const updateAttrs: Record<string, ForeignKeyValue> = {};\n\n if (type === 'hasMany') {\n // Update hasMany relationship - add/remove this model's ID\n const currentValue = targetDbRecord[foreignKey as keyof typeof targetDbRecord];\n const currentIds = this._extractIdsArray(currentValue as ForeignKeyValue);\n\n if (action === 'link') {\n // Add this model's ID if not already present\n if (!currentIds.includes(modelId as ModelId<TTemplate>)) {\n updateAttrs[foreignKey] = [...currentIds, modelId as string];\n }\n } else {\n // Remove this model's ID\n updateAttrs[foreignKey] = currentIds.filter((id) => id !== modelId);\n }\n } else if (type === 'belongsTo') {\n // Update belongsTo relationship - set/unset this model's ID\n updateAttrs[foreignKey] = action === 'link' ? (modelId as string) : null;\n }\n\n // Update the target model directly in the database to avoid recursion\n if (Object.keys(updateAttrs).length > 0) {\n // Type assertion needed: Partial update object with dynamic foreign keys\n // TypeScript can't verify all possible foreign key patterns match record type\n this._schema.db.getCollection(targetCollectionName).update(targetModelId, updateAttrs as any);\n }\n }\n\n /**\n * Extract foreign key value from a relationship value (model/array/collection)\n * @param relationship - The relationship configuration\n * @param value - The relationship value\n * @returns The extracted foreign key value\n */\n private _extractForeignKeyFromValue(\n relationship: Relationships,\n value: unknown,\n ): ForeignKeyValue {\n const { type } = relationship;\n\n if (type === 'belongsTo') {\n if (isModelInstance<TSchema>(value)) {\n return value.id as string;\n }\n return null;\n }\n\n if (type === 'hasMany') {\n if (isModelCollection<TSchema>(value)) {\n return value.models.map((m) => m.id as string);\n }\n if (isArray(value)) {\n return value\n .filter((item): item is ModelInstance<ModelTemplate, TSchema> =>\n isModelInstance<TSchema>(item),\n )\n .map((m) => m.id as string);\n }\n return [];\n }\n\n return null;\n }\n\n /**\n * Check if foreign key has a meaningful value\n * @param fk - The foreign key value to check\n * @returns True if the foreign key has a value\n */\n private _hasForeignKeyValue(fk: ForeignKeyValue): boolean {\n if (fk === null || fk === undefined) return false;\n if (Array.isArray(fk)) return fk.length > 0;\n return true;\n }\n\n /**\n * Check if foreign key has changed\n * @param currentFk - The current foreign key value\n * @param newFk - The new foreign key value\n * @returns True if the foreign keys are different\n */\n private _hasForeignKeyChanged(currentFk: ForeignKeyValue, newFk: ForeignKeyValue): boolean {\n // Both null/undefined\n if (!this._hasForeignKeyValue(currentFk) && !this._hasForeignKeyValue(newFk)) {\n return false;\n }\n\n // One is null, other isn't\n if (!this._hasForeignKeyValue(currentFk) || !this._hasForeignKeyValue(newFk)) {\n return true;\n }\n\n // Both are arrays (hasMany)\n if (Array.isArray(currentFk) && Array.isArray(newFk)) {\n return !this._arraysEqual(currentFk, newFk);\n }\n\n // Simple comparison\n return currentFk !== newFk;\n }\n\n /**\n * Compare two arrays for equality\n * @param arr1 - First array\n * @param arr2 - Second array\n * @returns True if arrays are equal\n */\n private _arraysEqual(arr1: (string | number)[], arr2: (string | number)[]): boolean {\n if (arr1.length !== arr2.length) return false;\n return arr1.every((val, index) => val === arr2[index]);\n }\n}\n","import { Relationships } from '@src/associations';\nimport type { DbCollection } from '@src/db';\nimport type { SchemaCollections } from '@src/schema';\n\nimport BaseModel from './BaseModel';\nimport RelationshipsManager from './RelationshipsManager';\nimport { isModelCollection, isModelInstance, isModelInstanceArray } from './typeGuards';\nimport type {\n ModelAttrs,\n ModelClass,\n ModelConfig,\n ModelCreateAttrs,\n ModelInstance,\n ModelRelationships,\n ModelTemplate,\n ModelUpdateAttrs,\n NewModelAttrs,\n NewModelInstance,\n RelatedModelAttrs,\n RelationshipNames,\n RelationshipTargetModel,\n RelationshipsByTemplate,\n} from './types';\n\n/**\n * Model class for managing model instances with relationships\n * @template TTemplate - The model template (most important for users)\n * @template TSchema - The schema collections type for enhanced type inference\n * @template TSerializer - The serializer type\n */\nexport default class Model<\n TTemplate extends ModelTemplate = ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TSerializer = undefined,\n> extends BaseModel<ModelAttrs<TTemplate, TSchema>, TSerializer> {\n public readonly relationships?: RelationshipsByTemplate<TTemplate, TSchema>;\n protected _relationshipsManager?: RelationshipsManager<TTemplate, TSchema>;\n\n constructor(\n template: TTemplate,\n config: ModelConfig<\n TTemplate,\n TSchema,\n RelationshipsByTemplate<TTemplate, TSchema>,\n TSerializer\n >,\n ) {\n const { attrs, relationships, schema, serializer } = config;\n\n const dbCollection = schema.db.getCollection(\n template.collectionName,\n ) as unknown as DbCollection<ModelAttrs<TTemplate, TSchema>>;\n\n // Process attributes to separate relationship model instances from regular attributes\n const { modelAttrs, relationshipUpdates } = Model._processAttrs<TTemplate, TSchema>(\n attrs,\n relationships,\n );\n\n // Initialize BaseModel with regular attributes, db collection, and serializer\n super(\n template.modelName,\n template.collectionName,\n modelAttrs as NewModelAttrs<ModelAttrs<TTemplate, TSchema>>,\n dbCollection,\n serializer,\n );\n\n this.relationships = relationships;\n if (schema && relationships) {\n // Cast to base Model type since RelationshipsManager doesn't need serializer type info\n this._relationshipsManager = new RelationshipsManager(\n this as unknown as Model<TTemplate, TSchema>,\n schema,\n relationships,\n );\n\n // Set pending relationship updates only if the model is new (not already in the database)\n if (this._status === 'new' && Object.keys(relationshipUpdates).length > 0) {\n this._relationshipsManager.setPendingRelationshipUpdates(relationshipUpdates);\n }\n\n this._initAttributeAccessors();\n this._initForeignKeys();\n this._initRelationshipAccessors();\n } else {\n this._initAttributeAccessors();\n }\n }\n\n /**\n * Define a model class with attribute accessors\n * @template TTemplate - The model template (most important for users)\n * @template TSchema - The schema collections type for enhanced type inference\n * @template TSerializer - The serializer type\n * @param template - The model template to define\n * @returns A model class that can be instantiated with 'new'\n */\n static define<\n TTemplate extends ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TSerializer = undefined,\n >(template: TTemplate): ModelClass<TTemplate, TSchema, TSerializer> {\n return class extends Model<TTemplate, TSchema, TSerializer> {\n constructor(\n config: ModelConfig<\n TTemplate,\n TSchema,\n RelationshipsByTemplate<TTemplate, TSchema>,\n TSerializer\n >,\n ) {\n super(template, config);\n }\n } as unknown as ModelClass<TTemplate, TSchema, TSerializer>;\n }\n\n // -- CRUD METHODS --\n\n /**\n * Save the model to the database and apply pending relationship updates\n * @returns The model with saved instance type\n */\n save(): this & ModelInstance<TTemplate, TSchema, TSerializer> {\n // Save the model (FK changes are in _attrs from _processAttrs or link/unlink)\n super.save();\n\n // Update inverse relationships on other models (now that this model is saved)\n if (this._relationshipsManager) {\n this._relationshipsManager.applyPendingInverseUpdates();\n }\n\n return this as this & ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n /**\n * Update the model attributes and save the model\n * @param attrs - The attributes to update\n * @returns The model with saved instance type\n */\n update(\n attrs: ModelUpdateAttrs<TTemplate, TSchema>,\n ): this & ModelInstance<TTemplate, TSchema, TSerializer> {\n // Process attributes to separate relationship model instances from regular attributes\n const { modelAttrs, relationshipUpdates } = Model._processAttrs<TTemplate, TSchema>(\n attrs,\n this.relationships,\n );\n\n // Set pending relationship updates (handles both model instances and FK values)\n if (this._relationshipsManager && Object.keys(relationshipUpdates).length > 0) {\n this._relationshipsManager.setPendingRelationshipUpdates(relationshipUpdates);\n }\n\n return super.update(modelAttrs as Partial<ModelAttrs<TTemplate, TSchema>>) as this &\n ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n /**\n * Reload the model from the database\n * @returns The model with saved instance type\n */\n reload(): this & ModelInstance<TTemplate, TSchema, TSerializer> {\n return super.reload() as this & ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n /**\n * Destroy the model from the database\n * @returns The model with new instance type\n */\n destroy(): this & NewModelInstance<TTemplate, TSchema, TSerializer> {\n return super.destroy() as this & NewModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n // -- RELATIONSHIP METHODS --\n\n /**\n * Link this model to another model via a relationship\n * @param relationshipName - The name of the relationship\n * @param targetModel - The model to link to (or null to unlink)\n * @returns This model instance for chaining\n */\n link<K extends RelationshipNames<RelationshipsByTemplate<TTemplate, TSchema>>>(\n relationshipName: K,\n targetModel: RelationshipTargetModel<TSchema, RelationshipsByTemplate<TTemplate, TSchema>, K>,\n ): this & ModelInstance<TTemplate, TSchema, TSerializer> {\n if (this._relationshipsManager) {\n // Get FK updates from manager (which also handles inverse relationships)\n const result = this._relationshipsManager.link(relationshipName, targetModel);\n\n // Apply FK updates to this model's attributes\n Object.assign(this._attrs, result.foreignKeyUpdates);\n\n // Save if this model was already saved\n if (this.isSaved()) {\n this.save();\n }\n }\n return this as this & ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n /**\n * Unlink this model from another model via a relationship\n * @param relationshipName - The name of the relationship\n * @param targetModel - The specific model to unlink (optional for hasMany, unlinks all if not provided)\n * @returns This model instance for chaining\n */\n unlink<K extends RelationshipNames<RelationshipsByTemplate<TTemplate, TSchema>>>(\n relationshipName: K,\n targetModel?: RelationshipTargetModel<TSchema, RelationshipsByTemplate<TTemplate, TSchema>, K>,\n ): this & ModelInstance<TTemplate, TSchema, TSerializer> {\n if (this._relationshipsManager) {\n // Get FK updates from manager (which also handles inverse relationships)\n const result = this._relationshipsManager.unlink(relationshipName, targetModel);\n\n // Apply FK updates to this model's attributes\n Object.assign(this._attrs, result.foreignKeyUpdates);\n\n // Save if this model was already saved\n if (this.isSaved()) {\n this.save();\n }\n }\n return this as this & ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n /**\n * Get related model(s) for a relationship with proper typing\n * @param relationshipName - The relationship name\n * @returns The related model(s) or null/empty collection\n */\n related<K extends RelationshipNames<RelationshipsByTemplate<TTemplate, TSchema>>>(\n relationshipName: K,\n ): RelationshipTargetModel<TSchema, RelationshipsByTemplate<TTemplate, TSchema>, K> | null {\n return this._relationshipsManager?.related(relationshipName) ?? null;\n }\n\n // -- ATTRIBUTES PROCESSING METHODS --\n\n /**\n * Extract foreign key value from a relationship value\n * @param relationship - The relationship configuration\n * @param value - The value to extract the foreign key from\n * @returns The foreign key value\n */\n private static _extractForeignKey<TSchema extends SchemaCollections>(\n relationship: Relationships,\n value: unknown,\n ): string | string[] | null {\n const { type } = relationship;\n\n if (type === 'belongsTo') {\n if (isModelInstance<TSchema>(value)) {\n return value.id as string;\n }\n return null;\n }\n\n if (type === 'hasMany') {\n if (isModelInstanceArray<TSchema>(value)) {\n return value.map((model) => model.id as string);\n }\n if (isModelCollection<TSchema>(value)) {\n return value.models.map((model) => model.id as string);\n }\n return [];\n }\n\n return null;\n }\n\n /**\n * Separate attributes into model attributes and relationship updates\n * Extracts foreign keys from relationship model instances and initializes default values\n * @param attrs - The attributes to separate\n * @param relationships - The relationships configuration\n * @returns Object containing:\n * - regularAttrs: Regular attributes (may include explicit FK values)\n * - relationshipValues: Relationship values\n * - foreignKeys: Foreign keys (extracted from relationship models or defaults)\n */\n private static _separateAttrs<\n _TTemplate extends ModelTemplate,\n TSchema extends SchemaCollections,\n TRelationships extends ModelRelationships,\n >(\n attrs: Record<string, unknown>,\n relationships: TRelationships,\n ): {\n regularAttrs: Record<string, unknown>;\n relationshipValues: Record<string, unknown>;\n foreignKeys: Record<string, string | string[] | null>;\n } {\n const regularAttrs: Record<string, unknown> = {};\n const relationshipValues: Record<string, unknown> = {};\n const foreignKeys: Record<string, string | string[] | null> = {};\n\n // Step 1: Initialize all foreign keys with default values\n for (const relationshipName in relationships) {\n const relationship = relationships[relationshipName];\n const { type, foreignKey } = relationship;\n foreignKeys[foreignKey] = type === 'belongsTo' ? null : [];\n }\n\n // Step 2: Process attributes\n for (const key in attrs) {\n const value = attrs[key];\n if (key in relationships) {\n // Relationship attribute (model instance)\n const relationship = relationships[key];\n relationshipValues[key] = value;\n\n // Extract FK from relationship model (overrides default if present)\n const foreignKeyValue = Model._extractForeignKey<TSchema>(relationship, value);\n if (foreignKeyValue !== null) {\n foreignKeys[relationship.foreignKey] = foreignKeyValue;\n }\n } else {\n // Check if this is a foreign key attribute\n let isForeignKey = false;\n for (const relationshipName in relationships) {\n const relationship = relationships[relationshipName];\n if (relationship.foreignKey === key) {\n isForeignKey = true;\n foreignKeys[key] = value as string | string[] | null;\n // Add FK value to relationshipValues so it's processed by setPendingRelationshipUpdates\n relationshipValues[relationshipName] = value;\n break;\n }\n }\n\n if (!isForeignKey) {\n // Regular attribute\n regularAttrs[key] = value;\n }\n }\n }\n\n return { regularAttrs, relationshipValues, foreignKeys };\n }\n\n /**\n * Process constructor/update attributes before model initialization\n * Separates relationship model instances from regular attributes and extracts foreign keys\n * @param attrs - The attributes to process (can include both regular attrs and relationship instances)\n * @param relationships - The relationships configuration (optional)\n * @returns Object containing:\n * - modelAttrs: Regular attributes and foreign keys ready for the database\n * - relationshipUpdates: Relationship model instances to be linked after save\n * @example\n * // Input: { title: 'Post', author: authorModelInstance }\n * // Output: {\n * // modelAttrs: { title: 'Post', authorId: '1' },\n * // relationshipUpdates: { author: authorModelInstance }\n * // }\n */\n static _processAttrs<\n TTemplate extends ModelTemplate,\n TSchema extends SchemaCollections,\n TRelationships extends ModelRelationships = RelationshipsByTemplate<TTemplate, TSchema>,\n >(\n attrs:\n | ModelCreateAttrs<TTemplate, TSchema, TRelationships>\n | ModelUpdateAttrs<TTemplate, TSchema, TRelationships>\n | Partial<ModelCreateAttrs<TTemplate, TSchema, TRelationships>>\n | Record<string, unknown>,\n relationships?: TRelationships,\n ): {\n modelAttrs:\n | NewModelAttrs<ModelAttrs<TTemplate, TSchema>>\n | Partial<ModelAttrs<TTemplate, TSchema>>;\n relationshipUpdates: Partial<RelatedModelAttrs<TSchema, TRelationships>>;\n } {\n // Early return if no relationships are defined\n if (!relationships) {\n return {\n modelAttrs: attrs as\n | NewModelAttrs<ModelAttrs<TTemplate, TSchema>>\n | Partial<ModelAttrs<TTemplate, TSchema>>,\n relationshipUpdates: {},\n };\n }\n\n // Step 1: Separate regular attributes, relationship values, and extracted foreign keys\n // (default FK values are also initialized here)\n const { regularAttrs, relationshipValues, foreignKeys } = Model._separateAttrs<\n TTemplate,\n TSchema,\n TRelationships\n >(attrs as Record<string, unknown>, relationships);\n\n // Step 2: Combine foreign keys (defaults) with regular attributes\n // regularAttrs comes second to allow explicit FK values to override defaults\n const modelAttrs = { ...foreignKeys, ...regularAttrs };\n\n return {\n modelAttrs: modelAttrs as\n | NewModelAttrs<ModelAttrs<TTemplate, TSchema>>\n | Partial<ModelAttrs<TTemplate, TSchema>>,\n relationshipUpdates: relationshipValues as Partial<\n RelatedModelAttrs<TSchema, TRelationships>\n >,\n };\n }\n\n // -- ACCESSOR INITIALIZATION METHODS --\n\n /**\n * Initialize attribute accessors for all attributes except id\n */\n private _initAttributeAccessors(): void {\n // Remove old accessors\n for (const key in this._attrs) {\n if (key !== 'id' && Object.hasOwn(this, key)) {\n delete this[key as keyof this];\n }\n }\n\n // Set up new accessors\n for (const key in this._attrs) {\n const attrKey = key as keyof typeof this._attrs;\n if (attrKey !== 'id' && !Object.hasOwn(this, attrKey)) {\n Object.defineProperty(this, key, {\n get: () => {\n return this._attrs[attrKey];\n },\n set: (value: any) => {\n this._attrs[attrKey] = value;\n },\n enumerable: false,\n configurable: true,\n });\n }\n }\n }\n\n /**\n * Initialize foreign key attributes if they don't exist\n */\n private _initForeignKeys(): void {\n if (!this._relationshipsManager?.relationshipDefs) return;\n\n for (const name in this._relationshipsManager.relationshipDefs) {\n const relationshipName = name as RelationshipNames<\n RelationshipsByTemplate<TTemplate, TSchema>\n >;\n const relationshipDef = this._relationshipsManager.relationshipDefs[relationshipName];\n const { foreignKey, type } = relationshipDef.relationship as Relationships;\n\n // Initialize foreign key if it doesn't exist in attrs\n if (!(foreignKey in this._attrs)) {\n if (type === 'belongsTo') {\n (this._attrs as Record<string, unknown>)[foreignKey] = null;\n } else if (type === 'hasMany') {\n (this._attrs as Record<string, unknown>)[foreignKey] = [];\n }\n }\n }\n }\n\n /**\n * Initialize relationship accessors for all relationships\n */\n private _initRelationshipAccessors(): void {\n if (!this._relationshipsManager?.relationshipDefs) return;\n\n for (const name in this._relationshipsManager.relationshipDefs) {\n const relationshipName = name as RelationshipNames<\n RelationshipsByTemplate<TTemplate, TSchema>\n >;\n\n if (!Object.hasOwn(this, relationshipName)) {\n Object.defineProperty(this, relationshipName, {\n get: () => {\n return this.related(relationshipName);\n },\n set: (value: any | any[] | null) => {\n this.link(relationshipName, value);\n },\n enumerable: false,\n configurable: true,\n });\n }\n }\n }\n}\n","import { MirageError } from '@src/utils';\n\nimport type { ModelTemplate } from './types';\n\n/**\n * A fluent builder for creating strongly-typed model templates.\n *\n * The ModelBuilder provides a type-safe way to construct ModelTemplate instances with\n * configurable model attributes. It follows the builder pattern, allowing method chaining\n * to progressively refine the template's type parameters.\n * @template TModelAttrs - The model attributes type (must have an 'id' property)\n * @template TModelName - The string literal type for the model name\n * @template TCollectionName - The string literal type for the collection name\n * @template TSerializedModel - The serialized model type (for toJSON)\n * @template TSerializedCollection - The serialized collection type (for toJSON)\n * @example\n * ```typescript\n * // Create a basic template\n * const userTemplate = model().name('user').collection('users').create();\n *\n * // Create a template with specific model attributes\n * interface User { id: string; name: string; email: string; }\n * const typedUserTemplate = model()\n * .name('user')\n * .collection('users')\n * .attrs<UserAttrs>()\n * .create();\n *\n * // With custom JSON types\n * const jsonTypedTemplate = model()\n * .name('user')\n * .collection('users')\n * .attrs<UserAttrs>()\n * .json<UserJSON, UsersJSON>()\n * .create();\n * ```\n */\nexport default class ModelBuilder<\n TModelAttrs extends { id: any } = { id: string },\n TModelName extends string = string,\n TCollectionName extends string = string,\n TSerializedModel = TModelAttrs,\n TSerializedCollection = TSerializedModel[],\n> {\n private _modelName?: TModelName;\n private _collectionName?: TCollectionName;\n\n /**\n * Sets the model name identifier.\n * @template T - The string literal type for the model name\n * @param modelName - The name identifier for the model (e.g., 'user', 'post')\n * @returns A new ModelBuilder instance with the model name set\n * @example\n * ```typescript\n * const builder = model().name('user');\n * ```\n */\n name<T extends string>(\n modelName: T,\n ): ModelBuilder<TModelAttrs, T, TCollectionName, TSerializedModel, TSerializedCollection> {\n // Validate model name\n if (!modelName || typeof modelName !== 'string' || modelName.trim() === '') {\n throw new MirageError('Model name must be a non-empty string.');\n }\n\n const builder = new ModelBuilder<\n TModelAttrs,\n T,\n TCollectionName,\n TSerializedModel,\n TSerializedCollection\n >();\n builder._modelName = modelName;\n builder._collectionName = this._collectionName as TCollectionName;\n return builder;\n }\n\n /**\n * Sets the collection name identifier.\n * @template T - The string literal type for the collection name\n * @param collectionName - The name identifier for the collection (e.g., 'users', 'posts')\n * @returns A new ModelBuilder instance with the collection name set\n * @example\n * ```typescript\n * const builder = model().name('user').collection('users');\n * ```\n */\n collection<T extends string>(\n collectionName: T,\n ): ModelBuilder<TModelAttrs, TModelName, T, TSerializedModel, TSerializedCollection> {\n // Validate collection name\n if (!collectionName || typeof collectionName !== 'string' || collectionName.trim() === '') {\n throw new MirageError('Collection name must be a non-empty string.');\n }\n\n const builder = new ModelBuilder<\n TModelAttrs,\n TModelName,\n T,\n TSerializedModel,\n TSerializedCollection\n >();\n builder._modelName = this._modelName as TModelName;\n builder._collectionName = collectionName;\n return builder;\n }\n\n /**\n * Sets the model attributes type.\n *\n * This method allows you to specify the exact shape of your model attributes,\n * providing strong typing for the resulting template. Serialization types are\n * reset to default to the new attributes type (use .json() to override).\n * @template T - The model attributes type (must extend { id: any })\n * @returns A new ModelBuilder instance with the specified model type\n * @example\n * ```typescript\n * interface User { id: string; name: string; email: string; }\n * const builder = model().name('user').collection('users').attrs<UserAttrs>();\n * ```\n */\n attrs<T extends { id: any }>(): ModelBuilder<T, TModelName, TCollectionName, T, T[]> {\n const builder = new ModelBuilder<T, TModelName, TCollectionName, T, T[]>();\n builder._modelName = this._modelName as TModelName;\n builder._collectionName = this._collectionName as TCollectionName;\n return builder;\n }\n\n /**\n * Specifies serialization types for this model.\n *\n * This method sets the types returned by model.toJSON() and collection.toJSON().\n * If not called, toJSON() will return the model attributes type.\n * @template TModel - The serialized model type (single instance)\n * @template TCollection - The serialized collection type (array)\n * @returns A new ModelBuilder with serialization types\n * @example\n * ```typescript\n * interface UserJSON {\n * id: string;\n * name: string;\n * email: string;\n * }\n *\n * interface UsersJSON {\n * users: UserJSON[];\n * }\n *\n * const userModel = model()\n * .name('user')\n * .collection('users')\n * .attrs<UserAttrs>()\n * .json<UserJSON, UsersJSON>() // Specify serialization types\n * .create();\n * ```\n */\n json<TModel = TModelAttrs, TCollection = TModel[]>(): ModelBuilder<\n TModelAttrs,\n TModelName,\n TCollectionName,\n TModel,\n TCollection\n > {\n const builder = new ModelBuilder<\n TModelAttrs,\n TModelName,\n TCollectionName,\n TModel,\n TCollection\n >();\n builder._modelName = this._modelName as TModelName;\n builder._collectionName = this._collectionName as TCollectionName;\n return builder;\n }\n\n /**\n * Creates the final ModelTemplate with all configured types and metadata.\n *\n * This method produces the immutable ModelTemplate that can be used throughout\n * your application for type-safe model operations. The template includes the\n * model and collection names, a unique symbol key, and hidden type properties\n * for attributes and serialization.\n * @returns The configured ModelTemplate instance with hidden type properties\n * @throws Error if model name or collection name is not set\n * @example\n * ```typescript\n * const userTemplate = model()\n * .name('user')\n * .collection('users')\n * .attrs<UserAttrs>()\n * .create();\n * ```\n */\n create(): ModelTemplate<TModelName, TCollectionName> & {\n __attrs: TModelAttrs;\n __json: {\n model: TSerializedModel;\n collection: TSerializedCollection;\n };\n } {\n if (!this._modelName) {\n throw new MirageError('Model name is required. Call .name() before .create()');\n }\n if (!this._collectionName) {\n throw new MirageError('Collection name is required. Call .collection() before .create()');\n }\n\n return {\n key: Symbol(`Model:${this._modelName}`),\n modelName: this._modelName,\n collectionName: this._collectionName,\n // Type-only property: Placeholder for type inference only, never accessed at runtime\n // Using 'undefined as unknown as Type' to carry type information through the template\n __attrs: undefined as unknown as TModelAttrs,\n // Type-only property: Placeholder for serialized type information\n // Using 'undefined as unknown as Type' to carry type information through the template\n __json: undefined as unknown as {\n model: TSerializedModel;\n collection: TSerializedCollection;\n },\n };\n }\n}\n\n/**\n * Creates a new ModelBuilder for constructing type-safe model templates.\n *\n * This is the primary entry point for creating model templates. It provides a fluent\n * interface for configuring model attributes and metadata.\n * @returns A new ModelBuilder instance ready for configuration\n * @example\n * ```typescript\n * // Basic usage\n * const userTemplate = model()\n * .name('user')\n * .collection('users')\n * .create();\n *\n * // With typed attributes\n * interface UserAttrs { id: string; name: string; email: string; }\n * const typedUserTemplate = model()\n * .name('user')\n * .collection('users')\n * .attrs<UserAttrs>()\n * .create();\n *\n * // With custom JSON types\n * interface UserJSON { id: string; name: string; email: string; }\n * interface UsersJSON { users: UserJSON[]; }\n * const jsonTypedTemplate = model()\n * .name('user')\n * .collection('users')\n * .attrs<UserAttrs>()\n * .json<UserJSON, UsersJSON>()\n * .create();\n * ```\n */\nexport function model(): ModelBuilder<{ id: string }, string, string> {\n return new ModelBuilder();\n}\n","import type {\n Association,\n AssociationQuery,\n AssociationTraitsAndDefaults,\n FactoryAssociations,\n} from '@src/associations';\nimport { ModelCollection, type ModelInstance, type ModelTemplate } from '@src/model';\nimport type {\n SchemaInstance,\n Collection,\n SchemaCollections,\n CollectionCreateAttrs,\n} from '@src/schema';\nimport { MirageError } from '@src/utils';\n\n/**\n * Manages factory associations - creates and links related models\n */\nexport default class AssociationsManager<\n TTemplate extends ModelTemplate,\n TSchema extends SchemaCollections,\n> {\n /**\n * Process all associations and return relationship values\n * @param schema - The schema instance\n * @param associations - The associations to process\n * @param skipKeys - Optional list of relationship keys to skip (e.g., if user provided them)\n * @returns A record of relationship values\n */\n processAssociations(\n schema: SchemaInstance<TSchema>,\n associations: FactoryAssociations<TTemplate, TSchema>,\n skipKeys?: string[],\n ): Record<string, ModelInstance<any, TSchema> | ModelCollection<any, TSchema>> {\n const relationshipValues: Record<string, any> = {};\n const keysToSkip = new Set(skipKeys || []);\n\n for (const relationshipName in associations) {\n const association = associations[relationshipName] as Association<ModelTemplate>;\n if (!association) continue;\n\n // Skip if user provided this relationship\n if (keysToSkip.has(relationshipName)) continue;\n\n // Get the collection directly from the model's collectionName\n const collectionName = association.model.collectionName;\n const collection = schema.getCollection(collectionName);\n\n if (!collection) {\n throw new MirageError(\n `Collection '${collectionName}' not found in schema during associations processing`,\n );\n }\n\n switch (association.type) {\n case 'create':\n relationshipValues[relationshipName] = this._processCreate(\n collection,\n association.traitsAndDefaults,\n );\n break;\n\n case 'createMany':\n relationshipValues[relationshipName] = this._processCreateMany(\n collection,\n association.count,\n association.traitsAndDefaults,\n );\n break;\n\n case 'link':\n relationshipValues[relationshipName] = this._processLink(\n collection,\n association.query,\n association.traitsAndDefaults,\n );\n break;\n\n case 'linkMany':\n relationshipValues[relationshipName] = this._processLinkMany(\n collection,\n association.model,\n association.count,\n association.query,\n association.traitsAndDefaults,\n );\n break;\n }\n }\n\n return relationshipValues;\n }\n\n private _processCreate(\n collection: Collection<TSchema, any, any, any, any>,\n traitsAndDefaults?: AssociationTraitsAndDefaults,\n ): ModelInstance<any, TSchema, any> {\n return collection.create(\n ...((traitsAndDefaults ?? []) as CollectionCreateAttrs<TTemplate, TSchema>[]),\n );\n }\n\n private _processCreateMany(\n collection: Collection<TSchema, any, any, any, any>,\n count: number,\n traitsAndDefaults?: AssociationTraitsAndDefaults,\n ): ModelCollection<any, TSchema, any> {\n return collection.createList(\n count,\n ...((traitsAndDefaults ?? []) as CollectionCreateAttrs<TTemplate, TSchema>[]),\n );\n }\n\n private _processLink(\n collection: Collection<TSchema, any, any, any, any>,\n query?: AssociationQuery,\n traitsAndDefaults?: AssociationTraitsAndDefaults,\n ): ModelInstance<any, TSchema, any> {\n // Try to find existing\n let model: ModelInstance<any, TSchema, any> | null = null;\n\n if (query) {\n // Use findMany to get matches, then shuffle and pick first\n const matches =\n typeof query === 'function'\n ? collection.findMany({ where: query } as any)\n : collection.findMany(query as any);\n if (matches.length > 0) {\n const shuffled = this._shuffle(matches.models);\n model = shuffled[0];\n }\n } else {\n // Get all and shuffle, then pick first\n const all = collection.all();\n if (all.length > 0) {\n const shuffled = this._shuffle(all.models);\n model = shuffled[0];\n }\n }\n\n // Create if not found (with traits and defaults) // Create if not found (with traits and defaults)\n if (!model) {\n model = collection.create(\n ...((traitsAndDefaults ?? []) as CollectionCreateAttrs<TTemplate, TSchema>[]),\n );\n }\n\n return model;\n }\n\n private _processLinkMany(\n collection: Collection<TSchema, any, any, any, any>,\n template: ModelTemplate,\n count: number,\n query?: AssociationQuery,\n traitsAndDefaults?: AssociationTraitsAndDefaults,\n ): ModelCollection<any, TSchema, any> {\n // Try to find existing\n let models: ModelCollection<any, TSchema, any>;\n\n if (query) {\n models =\n typeof query === 'function'\n ? collection.findMany({ where: query } as any)\n : collection.findMany(query as any);\n } else {\n models = collection.all();\n }\n\n // Shuffle before selecting\n const shuffledModels = this._shuffle(models.models);\n\n // Create more if needed (with traits and defaults)\n const needed = count - shuffledModels.length;\n if (needed > 0) {\n const newModels = collection.createList(\n needed,\n ...((traitsAndDefaults ?? []) as CollectionCreateAttrs<TTemplate, TSchema>[]),\n );\n // Combine shuffled existing with new ones\n const allModels = [...shuffledModels, ...newModels.models];\n return new ModelCollection(template, allModels);\n } else {\n // Trim to requested count\n const selectedModels = shuffledModels.slice(0, count);\n return new ModelCollection(template, selectedModels);\n }\n }\n\n /**\n * Fisher-Yates shuffle algorithm\n * @param array - The array to shuffle\n * @returns The shuffled array\n */\n private _shuffle<T>(array: T[]): T[] {\n const shuffled = [...array];\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];\n }\n return shuffled;\n }\n}\n","import { type FactoryAssociations } from '@src/associations';\nimport type {\n ModelAttrs,\n ModelId,\n ModelInstance,\n ModelTemplate,\n PartialModelAttrs,\n} from '@src/model';\nimport type { ModelCollection } from '@src/model';\nimport type { SchemaCollections, SchemaInstance } from '@src/schema';\nimport { MirageError } from '@src/utils';\n\nimport AssociationsManager from './AssociationsManager';\nimport type { FactoryAttrs, FactoryAfterCreateHook, ModelTraits, TraitName } from './types';\n\n/**\n * Factory that builds model attributes with optional schema support.\n * @template TTemplate - The model template (inferred from constructor)\n * @template TSchema - The schema collections type (never = sch)\n * @template TTraits - The factory traits (inferred from constructor)\n */\nexport default class Factory<\n TTemplate extends ModelTemplate = ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TTraits extends ModelTraits<TSchema, TTemplate> = {},\n> {\n readonly attributes: FactoryAttrs<TTemplate>;\n readonly traits: TTraits;\n readonly associations?: FactoryAssociations<TTemplate, TSchema>;\n readonly afterCreate?: FactoryAfterCreateHook<TSchema, TTemplate>;\n\n private readonly _template: TTemplate;\n private readonly _associationsManager: AssociationsManager<TTemplate, TSchema>;\n\n constructor(\n template: TTemplate,\n attributes: FactoryAttrs<TTemplate>,\n traits: TTraits = {} as TTraits,\n associations?: FactoryAssociations<TTemplate, TSchema>,\n afterCreate?: FactoryAfterCreateHook<TSchema, TTemplate>,\n ) {\n this._template = template;\n this.attributes = attributes;\n this.traits = traits;\n this.associations = associations;\n this.afterCreate = afterCreate;\n\n // Create manager for processing associations\n this._associationsManager = new AssociationsManager<TTemplate, TSchema>();\n }\n\n /**\n * Get the model template\n * @returns The model template\n */\n get template(): TTemplate {\n return this._template;\n }\n\n /**\n * Build a model with the given model ID and trait names or default values.\n * @param modelId - The ID of the model to build.\n * @param traitsAndDefaults - The names of the traits to apply or default values for attributes.\n * @returns The built model.\n */\n build(\n modelId: ModelId<TTemplate>,\n ...traitsAndDefaults: (TraitName<TTraits> | PartialModelAttrs<TTemplate, TSchema>)[]\n ): ModelAttrs<TTemplate, TSchema> {\n const traitNames: string[] = [];\n const defaults: PartialModelAttrs<TTemplate, TSchema> = {};\n\n // Separate trait names from default values\n traitsAndDefaults.forEach((arg) => {\n if (typeof arg === 'string') {\n traitNames.push(arg);\n } else {\n Object.assign(defaults, arg);\n }\n });\n\n const processedAttributes = this._processAttributes(this.attributes, modelId);\n const traitAttributes = this._buildWithTraits(traitNames, modelId);\n\n // Merge attributes in order: defaults override traits, traits override base attributes\n const mergedAttributes = this._mergeAttributes(processedAttributes, traitAttributes);\n\n // Add user defaults and the autogenerated id\n return {\n ...mergedAttributes,\n ...defaults,\n id: modelId,\n } as ModelAttrs<TTemplate, TSchema>;\n }\n\n /**\n * Process associations and return relationship values\n * This runs with schema context and creates/links related models\n * @param schema - The schema instance\n * @param skipKeys - Optional list of relationship keys to skip (e.g., if user provided them)\n * @param traitsAndDefaults - Optional trait names to include trait associations\n * @returns A record of relationship values\n */\n processAssociations(\n schema: SchemaInstance<TSchema>,\n skipKeys?: string[],\n traitsAndDefaults?: (TraitName<TTraits> | PartialModelAttrs<TTemplate, TSchema>)[],\n ): Record<string, ModelInstance<any, TSchema> | ModelCollection<any, TSchema>> {\n // Get trait associations\n const traitAssociations = this._getTraitAssociations(traitsAndDefaults);\n\n // Merge factory associations with trait associations (factory associations take precedence)\n const mergedAssociations = {\n ...traitAssociations,\n ...(this.associations || {}),\n };\n\n // If no associations, return empty\n if (Object.keys(mergedAssociations).length === 0) {\n return {};\n }\n\n // Use the manager instance to process merged associations\n return this._associationsManager.processAssociations(schema, mergedAssociations, skipKeys);\n }\n\n /**\n * Process the afterCreate hook and the trait hooks.\n * This method is intended to be called internally by schema collections.\n * @param schema - The schema instance.\n * @param model - The model to process.\n * @param traitsAndDefaults - The traits and defaults that were applied.\n * @returns The processed model.\n */\n processAfterCreateHooks(\n schema: SchemaInstance<TSchema>,\n model: ModelInstance<TTemplate, TSchema>,\n ...traitsAndDefaults: (TraitName<TTraits> | PartialModelAttrs<TTemplate, TSchema>)[]\n ): ModelInstance<TTemplate, TSchema> {\n const traitNames: string[] = traitsAndDefaults.filter((arg) => typeof arg === 'string');\n const hooks: FactoryAfterCreateHook<TSchema, TTemplate>[] = [];\n\n if (this.afterCreate) {\n hooks.push(this.afterCreate);\n }\n\n traitNames.forEach((name) => {\n const trait = this.traits[name as TraitName<TTraits>];\n\n if (trait?.afterCreate) {\n hooks.push(trait.afterCreate);\n }\n });\n\n // Execute hooks with the properly typed model instance and schema\n hooks.forEach((hook) => {\n (hook as (model: ModelInstance<TTemplate, TSchema>, schema: SchemaInstance<TSchema>) => void)(\n model,\n schema,\n );\n });\n\n return model;\n }\n\n // -- PRIVATE METHODS --\n\n private _processAttributes(\n attrs: FactoryAttrs<TTemplate>,\n modelId?: ModelId<TTemplate>,\n ): PartialModelAttrs<TTemplate, TSchema> {\n const attributes = attrs as PartialModelAttrs<TTemplate, TSchema>;\n const keys = this._sortAttrs(attributes, modelId);\n\n const result = keys.reduce((acc, key) => {\n const value = attributes[key];\n\n if (typeof value === 'function') {\n return {\n ...acc,\n [key]: value.call(attrs, modelId),\n };\n }\n\n return acc;\n }, attributes);\n\n return result;\n }\n\n private _buildWithTraits(\n traitNames: string[],\n modelId?: ModelId<TTemplate>,\n ): PartialModelAttrs<TTemplate> {\n const result = traitNames.reduce(\n (traitAttributes, name) => {\n const trait = this.traits[name as TraitName<TTraits>];\n\n if (trait) {\n const { afterCreate: _, ...extension } = trait;\n\n for (const key in extension) {\n const value = extension[key as keyof typeof extension];\n // Skip id and association objects\n if (key !== 'id' && !this._isAssociation(value)) {\n traitAttributes[key] =\n typeof value === 'function' ? value.call(this.attributes, modelId) : value;\n }\n }\n }\n\n return traitAttributes;\n },\n {} as Record<string, any>,\n );\n\n return result as PartialModelAttrs<TTemplate>;\n }\n\n /**\n * Check if a value is an association object\n * @param value - The value to check\n * @returns True if the value is an association\n */\n private _isAssociation(value: any): boolean {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n 'model' in value &&\n ['create', 'createMany', 'link', 'linkMany'].includes(value.type)\n );\n }\n\n /**\n * Extract associations from traits\n * @param traitsAndDefaults - The trait names to extract associations from\n * @returns The merged associations from all traits\n */\n private _getTraitAssociations(\n traitsAndDefaults?: (TraitName<TTraits> | PartialModelAttrs<TTemplate, TSchema>)[],\n ): FactoryAssociations<TTemplate, TSchema> {\n if (!traitsAndDefaults) {\n return {};\n }\n\n const traitNames = traitsAndDefaults.filter((arg) => typeof arg === 'string') as string[];\n const associations: Record<string, any> = {};\n\n traitNames.forEach((name) => {\n const trait = this.traits[name as TraitName<TTraits>];\n\n if (trait) {\n for (const key in trait) {\n const value = trait[key];\n if (this._isAssociation(value)) {\n associations[key] = value;\n }\n }\n }\n });\n\n return associations as FactoryAssociations<TTemplate, TSchema>;\n }\n\n private _mergeAttributes(\n baseAttributes: PartialModelAttrs<TTemplate>,\n overrideAttributes: PartialModelAttrs<TTemplate>,\n ): PartialModelAttrs<TTemplate> {\n return {\n ...baseAttributes,\n ...overrideAttributes,\n };\n }\n\n private _sortAttrs(\n attrs: PartialModelAttrs<TTemplate, TSchema>,\n modelId?: ModelId<TTemplate>,\n ): (keyof PartialModelAttrs<TTemplate, TSchema>)[] {\n const visited = new Set<string>();\n const processing = new Set<string>();\n\n const detectCycle = (key: string): boolean => {\n if (processing.has(key)) {\n throw new MirageError(`Circular dependency detected: ${String(key)}`);\n }\n if (visited.has(key)) {\n return false;\n }\n\n processing.add(key);\n\n const value = attrs[key as keyof PartialModelAttrs<TTemplate, TSchema>];\n if (typeof value === 'function') {\n // Create a proxy to track property access\n const proxy = new Proxy(attrs, {\n get(target, prop) {\n if (typeof prop === 'string' && prop in target) {\n detectCycle(prop);\n }\n return target[prop as keyof typeof target];\n },\n });\n\n // Call the function with the proxy as this context\n (value as Function).call(proxy, modelId);\n }\n\n processing.delete(key);\n visited.add(key);\n return false;\n };\n\n // Check each attribute for cycles\n Object.keys(attrs).forEach(detectCycle);\n\n // Return keys in their original order\n return Object.keys(attrs) as (keyof PartialModelAttrs<TTemplate, TSchema>)[];\n }\n}\n","import type { FactoryAssociations } from '@src/associations';\nimport type { ModelTemplate } from '@src/model';\nimport type { SchemaCollections } from '@src/schema';\nimport { MirageError } from '@src/utils';\n\nimport Factory from './Factory';\nimport type { FactoryAttrs, FactoryAfterCreateHook, ModelTraits } from './types';\n\n/**\n * Builder class for creating factories with fluent API\n * @template TTemplate - The model template\n * @template TSchema - The schema collections type\n * @template TTraits - The traits type\n */\nexport default class FactoryBuilder<\n TTemplate extends ModelTemplate = ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TTraits extends ModelTraits<TSchema, TTemplate> = {},\n> {\n protected _template?: TTemplate;\n protected _attributes: FactoryAttrs<TTemplate> = {} as FactoryAttrs<TTemplate>;\n protected _traits: TTraits = {} as TTraits;\n protected _associations?: FactoryAssociations<TTemplate, TSchema>;\n protected _afterCreate?: FactoryAfterCreateHook<TSchema, TTemplate>;\n\n constructor() {}\n\n /**\n * Set the model template for the factory\n * @template T - The model template type\n * @param template - The model template\n * @returns A new builder instance with the specified template\n */\n model<T extends ModelTemplate>(template: T): FactoryBuilder<T, TSchema, {}> {\n const builder = new FactoryBuilder<T, TSchema, {}>();\n builder._template = template;\n return builder;\n }\n\n /**\n * Set the attributes for the factory\n * @param attributes - The factory attributes\n * @returns The builder instance for chaining\n */\n attrs(attributes: FactoryAttrs<TTemplate>): this {\n this._attributes = { ...this._attributes, ...attributes };\n return this;\n }\n\n /**\n * Add traits to the factory\n * @param traits - The traits to add\n * @returns A new builder instance with the added traits\n */\n traits<T extends ModelTraits<TSchema, TTemplate>>(\n traits: T,\n ): FactoryBuilder<TTemplate, TSchema, TTraits & T> {\n const builder = new FactoryBuilder<TTemplate, TSchema, TTraits & T>();\n builder._template = this._template;\n builder._attributes = this._attributes;\n builder._traits = { ...this._traits, ...traits } as TTraits & T;\n builder._afterCreate = this._afterCreate;\n builder._associations = this._associations;\n return builder;\n }\n\n /**\n * Set the afterCreate hook\n * @param hook - The afterCreate hook function\n * @returns The builder instance for chaining\n */\n afterCreate(hook: FactoryAfterCreateHook<TSchema, TTemplate>): this {\n this._afterCreate = hook;\n return this;\n }\n\n /**\n * Set factory associations for automatic relationship creation\n * @param associations - The associations configuration\n * @returns The builder instance for chaining\n */\n associations(associations: FactoryAssociations<TTemplate, TSchema>): this {\n this._associations = associations;\n return this;\n }\n\n /**\n * Create a factory builder by extending an existing factory\n * @param originalFactory - The factory to extend\n * @returns A new factory builder based on the existing factory\n */\n extend<\n TTemplate extends ModelTemplate = ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TTraits extends ModelTraits<TSchema, TTemplate> = {},\n >(\n originalFactory: Factory<TTemplate, TSchema, TTraits>,\n ): FactoryBuilder<TTemplate, TSchema, TTraits> {\n const builder = new FactoryBuilder<TTemplate, TSchema, TTraits>();\n builder._template = originalFactory.template;\n builder._attributes = { ...originalFactory.attributes };\n builder._traits = { ...originalFactory.traits };\n builder._associations = { ...originalFactory.associations };\n builder._afterCreate = originalFactory.afterCreate;\n return builder;\n }\n\n /**\n * Build the final factory instance\n * @returns The factory instance\n */\n create(): Factory<TTemplate, TSchema, TTraits> {\n if (!this._template) {\n throw new MirageError(\n 'Model template must be set before creating factory. Call .model() first.',\n );\n }\n\n return new Factory(\n this._template,\n this._attributes,\n this._traits,\n this._associations,\n this._afterCreate,\n );\n }\n}\n\n/**\n * Create a factory builder with optional schema type\n * @template TSchema - The schema collections type (optional)\n * @returns Factory builder instance ready for model specification\n */\nexport function factory<TSchema extends SchemaCollections>(): FactoryBuilder<\n ModelTemplate,\n TSchema,\n {}\n> {\n return new FactoryBuilder<ModelTemplate, TSchema, {}>();\n}\n","import type { InferModelName, ModelTemplate } from '../model';\n\nimport type { BelongsTo } from './types';\n\n/**\n * Define a belongs-to relationship.\n * @param targetModel - The template of the model that is being related to.\n * @param opts - The options for the relationship.\n * @param opts.foreignKey - The foreign key of the relationship.\n * @param opts.inverse - The name of the inverse relationship on the target model, or null to disable.\n * @returns The relationship definition object.\n * @example\n * ```typescript\n * // Auto-detect inverse\n * author: belongsTo(userModel)\n *\n * // Explicit inverse\n * author: belongsTo(userModel, { inverse: 'authoredPosts' })\n *\n * // No inverse (no synchronization)\n * reviewer: belongsTo(userModel, { inverse: null })\n * ```\n */\nexport default function belongsTo<\n TTarget extends ModelTemplate,\n const TOpts extends { foreignKey?: string; inverse?: string | null } | undefined = undefined,\n>(\n targetModel: TTarget,\n opts?: TOpts,\n): BelongsTo<\n TTarget,\n TOpts extends { foreignKey: infer F extends string } ? F : `${InferModelName<TTarget>}Id`\n> {\n type ForeignKey = TOpts extends { foreignKey: infer F extends string }\n ? F\n : `${InferModelName<TTarget>}Id`;\n\n const defaultForeignKey = `${targetModel.modelName}Id` as const;\n const foreignKey = (opts?.foreignKey ?? defaultForeignKey) as ForeignKey;\n\n return {\n foreignKey,\n targetModel,\n type: 'belongsTo',\n inverse: opts?.inverse,\n };\n}\n","import type { ModelTemplate } from '@src/model';\nimport type { SchemaCollections } from '@src/schema';\n\nimport type {\n AssociationTraitsAndDefaults,\n CreateAssociation,\n TypedAssociationTraitsAndDefaults,\n} from './types';\n\n/**\n * Always create one new related model and link it (with schema type for trait validation)\n * @template TSchema - The schema collections type\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to create\n * @param traitsAndDefaults - Traits and/or defaults to apply (variadic) - trait names are validated against schema\n * @returns The create association\n */\nexport default function create<TSchema extends SchemaCollections, TModel extends ModelTemplate>(\n model: TModel,\n ...traitsAndDefaults: TypedAssociationTraitsAndDefaults<TSchema, TModel>\n): CreateAssociation<TModel>;\n\n/**\n * Always create one new related model and link it (without schema type - traits not validated)\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to create\n * @param traitsAndDefaults - Traits and/or defaults to apply (variadic) - trait names are strings\n * @returns The create association\n */\nexport default function create<TModel extends ModelTemplate>(\n model: TModel,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): CreateAssociation<TModel>;\n\n/**\n * Implementation\n * @param model - Model template\n * @param traitsAndDefaults - Traits and defaults\n * @returns Create association\n */\nexport default function create(\n model: ModelTemplate,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): CreateAssociation<ModelTemplate> {\n return {\n type: 'create',\n model,\n traitsAndDefaults:\n traitsAndDefaults.length > 0\n ? (traitsAndDefaults as AssociationTraitsAndDefaults)\n : undefined,\n };\n}\n","import type { ModelTemplate } from '@src/model';\nimport type { SchemaCollections } from '@src/schema';\n\nimport type {\n AssociationTraitsAndDefaults,\n CreateManyAssociation,\n TypedAssociationTraitsAndDefaults,\n} from './types';\n\n/**\n * Always create N new related models and link them (with schema type for trait validation)\n * @template TSchema - The schema collections type\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to create\n * @param count - Number of models to create\n * @param traitsAndDefaults - Traits and/or defaults to apply (variadic) - trait names are validated against schema\n * @returns The create many association\n */\nexport default function createMany<TSchema extends SchemaCollections, TModel extends ModelTemplate>(\n model: TModel,\n count: number,\n ...traitsAndDefaults: TypedAssociationTraitsAndDefaults<TSchema, TModel>\n): CreateManyAssociation<TModel>;\n\n/**\n * Always create N new related models and link them (without schema type - traits not validated)\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to create\n * @param count - Number of models to create\n * @param traitsAndDefaults - Traits and/or defaults to apply (variadic) - trait names are strings\n * @returns The create many association\n */\nexport default function createMany<TModel extends ModelTemplate>(\n model: TModel,\n count: number,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): CreateManyAssociation<TModel>;\n\n/**\n * Implementation\n * @param model - Model template\n * @param count - Number of models\n * @param traitsAndDefaults - Traits and defaults\n * @returns Create many association\n */\nexport default function createMany(\n model: ModelTemplate,\n count: number,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): CreateManyAssociation<ModelTemplate> {\n return {\n type: 'createMany',\n model,\n count,\n traitsAndDefaults:\n traitsAndDefaults.length > 0\n ? (traitsAndDefaults as AssociationTraitsAndDefaults)\n : undefined,\n };\n}\n","import type { InferModelName, ModelTemplate } from '@src/model';\n\nimport type { HasMany } from './types';\n\n/**\n * Define a has-many relationship.\n * @param targetModel - The template of the model that is being related to.\n * @param opts - The options for the relationship.\n * @param opts.foreignKey - The foreign key of the relationship.\n * @param opts.inverse - The name of the inverse relationship on the target model, or null to disable.\n * @returns The relationship definition object.\n * @example\n * ```typescript\n * // Auto-detect inverse\n * posts: hasMany(postModel)\n *\n * // Explicit inverse\n * authoredPosts: hasMany(postModel, { inverse: 'author' })\n *\n * // No inverse (no synchronization)\n * archivedPosts: hasMany(postModel, { inverse: null })\n * ```\n */\nexport default function hasMany<\n TTarget extends ModelTemplate,\n const TOpts extends { foreignKey?: string; inverse?: string | null } | undefined = undefined,\n>(\n targetModel: TTarget,\n opts?: TOpts,\n): HasMany<\n TTarget,\n TOpts extends { foreignKey: infer F extends string } ? F : `${InferModelName<TTarget>}Ids`\n> {\n type ForeignKey = TOpts extends { foreignKey: infer F extends string }\n ? F\n : `${InferModelName<TTarget>}Ids`;\n\n const defaultForeignKey = `${targetModel.modelName}Ids` as const;\n const foreignKey = (opts?.foreignKey ?? defaultForeignKey) as ForeignKey;\n\n return {\n foreignKey,\n targetModel,\n type: 'hasMany',\n inverse: opts?.inverse,\n };\n}\n","import type { InferModelAttrs, ModelTemplate } from '@src/model';\nimport type { SchemaCollections } from '@src/schema';\n\nimport type {\n AssociationQuery,\n AssociationTraitsAndDefaults,\n LinkAssociation,\n TypedAssociationTraitsAndDefaults,\n} from './types';\n\n/**\n * Try to find existing model, else create one (with schema type for trait validation)\n * @template TSchema - The schema collections type\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to use\n * @param query - Optional query to filter models (attributes object or predicate function)\n * @param traitsAndDefaults - Traits and/or defaults to apply when creating (variadic) - trait names are validated against schema\n * @returns The link association\n */\nexport default function link<TSchema extends SchemaCollections, TModel extends ModelTemplate>(\n model: TModel,\n query?: AssociationQuery<InferModelAttrs<TModel>>,\n ...traitsAndDefaults: TypedAssociationTraitsAndDefaults<TSchema, TModel>\n): LinkAssociation<TModel>;\n\n/**\n * Try to find existing model, else create one (without schema type - traits not validated)\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to use\n * @param query - Optional query to filter models (attributes object or predicate function)\n * @param traitsAndDefaults - Traits and/or defaults to apply when creating (variadic) - trait names are strings\n * @returns The link association\n */\nexport default function link<TModel extends ModelTemplate>(\n model: TModel,\n query?: AssociationQuery<InferModelAttrs<TModel>>,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): LinkAssociation<TModel>;\n\n/**\n * Implementation\n * @param model - Model template\n * @param query - Optional query\n * @param traitsAndDefaults - Traits and defaults\n * @returns Link association\n */\nexport default function link(\n model: ModelTemplate,\n query?: AssociationQuery,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): LinkAssociation<ModelTemplate> {\n return {\n type: 'link',\n model,\n query,\n traitsAndDefaults:\n traitsAndDefaults.length > 0\n ? (traitsAndDefaults as AssociationTraitsAndDefaults)\n : undefined,\n };\n}\n","import type { InferModelAttrs, ModelTemplate } from '@src/model';\nimport type { SchemaCollections } from '@src/schema';\n\nimport type {\n AssociationQuery,\n AssociationTraitsAndDefaults,\n LinkManyAssociation,\n TypedAssociationTraitsAndDefaults,\n} from './types';\n\n/**\n * Try to find N existing models, else create more as needed (with schema type for trait validation)\n * @template TSchema - The schema collections type\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to use\n * @param count - Number of models needed\n * @param query - Optional query to filter models (attributes object or predicate function)\n * @param traitsAndDefaults - Traits and/or defaults to apply when creating (variadic) - trait names are validated against schema\n * @returns The link many association\n */\nexport default function linkMany<TSchema extends SchemaCollections, TModel extends ModelTemplate>(\n model: TModel,\n count: number,\n query?: AssociationQuery<InferModelAttrs<TModel>>,\n ...traitsAndDefaults: TypedAssociationTraitsAndDefaults<TSchema, TModel>\n): LinkManyAssociation<TModel>;\n\n/**\n * Try to find N existing models, else create more as needed (without schema type - traits not validated)\n * @template TModel - The model template (inferred from model parameter)\n * @param model - Model template to use\n * @param count - Number of models needed\n * @param query - Optional query to filter models (attributes object or predicate function)\n * @param traitsAndDefaults - Traits and/or defaults to apply when creating (variadic) - trait names are strings\n * @returns The link many association\n */\nexport default function linkMany<TModel extends ModelTemplate>(\n model: TModel,\n count: number,\n query?: AssociationQuery<InferModelAttrs<TModel>>,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): LinkManyAssociation<TModel>;\n\n/**\n * Implementation\n * @param model - Model template\n * @param count - Number of models\n * @param query - Optional query\n * @param traitsAndDefaults - Traits and defaults\n * @returns Link many association\n */\nexport default function linkMany(\n model: ModelTemplate,\n count: number,\n query?: AssociationQuery,\n ...traitsAndDefaults: AssociationTraitsAndDefaults\n): LinkManyAssociation<ModelTemplate> {\n return {\n type: 'linkMany',\n model,\n count,\n query,\n traitsAndDefaults:\n traitsAndDefaults.length > 0\n ? (traitsAndDefaults as AssociationTraitsAndDefaults)\n : undefined,\n };\n}\n","import belongsTo from './belongsTo';\nimport create from './create';\nimport createMany from './createMany';\nimport hasMany from './hasMany';\nimport link from './link';\nimport linkMany from './linkMany';\n\n/**\n * Associations object with all helper functions:\n * - belongsTo, hasMany: for model relationship definitions\n * - create, createMany, link, linkMany: for factory associations\n */\nconst associations = {\n belongsTo,\n hasMany,\n create,\n createMany,\n link,\n linkMany,\n} as const;\n\nexport default associations;\n","import type { DbCollection, DbRecordInput, QueryOptions, WhereHelperFns } from '@src/db';\nimport type { Factory, ModelTraits } from '@src/factory';\nimport type { IdentityManager } from '@src/id-manager';\nimport {\n Model,\n ModelCollection,\n ModelCreateAttrs,\n ModelId,\n type ModelAttrs,\n type ModelClass,\n type ModelConfig,\n type ModelInstance,\n type ModelRelationships,\n type ModelTemplate,\n type RelationshipsByTemplate,\n} from '@src/model';\nimport type { Logger } from '@src/utils';\n\nimport type { SchemaInstance } from './Schema';\nimport type { FixtureConfig, SchemaCollections, Seeds } from './types';\n\n/**\n * Base collection class with query functionality.\n * @template TSchema - The schema collections type for enhanced type inference\n * @template TTemplate - The model template type (most important for users)\n * @template TRelationships - The raw relationships configuration for this collection (inferred from config)\n * @template TFactory - The factory type (inferred from config)\n * @template TSerializer - The serializer type (inferred from config)\n */\nexport abstract class BaseCollection<\n TSchema extends SchemaCollections = SchemaCollections,\n TTemplate extends ModelTemplate = ModelTemplate,\n TRelationships extends ModelRelationships = {},\n TFactory extends\n | Factory<TTemplate, TSchema, ModelTraits<TSchema, TTemplate>>\n | undefined = undefined,\n TSerializer = undefined,\n> {\n readonly modelName: string;\n readonly collectionName: string;\n protected readonly Model: ModelClass<TTemplate, TSchema, TSerializer>;\n\n protected readonly _template: TTemplate;\n protected readonly _schema: SchemaInstance<TSchema>;\n protected readonly _dbCollection: DbCollection<ModelAttrs<TTemplate, TSchema>>;\n protected readonly _identityManager: IdentityManager<ModelAttrs<TTemplate, TSchema>['id']>;\n protected readonly _logger?: Logger;\n\n protected readonly _factory?: TFactory;\n protected readonly _relationships?: TRelationships;\n protected readonly _serializer?: TSerializer;\n protected readonly _seeds?: Seeds<TSchema>;\n protected readonly _fixtures?: FixtureConfig<TTemplate, TRelationships>;\n\n constructor(\n schema: SchemaInstance<TSchema>,\n config: {\n factory?: TFactory;\n identityManager?: IdentityManager<ModelId<TTemplate>>;\n model: TTemplate;\n relationships?: TRelationships;\n serializer?: TSerializer;\n seeds?: Seeds<TSchema>;\n fixtures?: FixtureConfig<TTemplate, TRelationships>;\n },\n ) {\n const {\n factory,\n identityManager,\n model,\n relationships = {} as TRelationships,\n serializer,\n seeds,\n fixtures,\n } = config;\n\n this.modelName = model.modelName;\n this.collectionName = model.collectionName;\n this.Model = Model.define<TTemplate, TSchema, TSerializer>(model);\n\n this._template = model;\n this._schema = schema;\n this._logger = schema.logger;\n this._dbCollection = this._initializeDbCollection(identityManager);\n this._identityManager = this._dbCollection.identityManager;\n\n this._relationships = relationships;\n this._factory = factory;\n this._serializer = serializer;\n this._seeds = seeds;\n this._fixtures = fixtures;\n\n this._logger?.debug(`Collection '${this.collectionName}' initialized`, {\n modelName: this.modelName,\n hasFactory: !!factory,\n hasRelationships: !!relationships && Object.keys(relationships).length > 0,\n hasSeeds: !!seeds,\n hasFixtures: !!fixtures,\n });\n }\n\n /**\n * Get the collection relationships.\n * @returns The collection relationships configuration.\n */\n get relationships(): TRelationships | undefined {\n return this._relationships;\n }\n\n /**\n * Get the serializer for the collection.\n * @returns The collection serializer instance.\n */\n get serializer(): TSerializer | undefined {\n return this._serializer;\n }\n\n /**\n * Get a model by index.\n * @param index - The index of the model to get.\n * @returns The model instance or undefined if not found.\n */\n at(index: number): ModelInstance<TTemplate, TSchema, TSerializer> | undefined {\n const record = this._dbCollection.at(index);\n return record ? this._createModelFromRecord(record) : undefined;\n }\n\n /**\n * Returns all model instances in the collection.\n * @returns All model instances in the collection.\n */\n all(): ModelCollection<TTemplate, TSchema, TSerializer> {\n this._logger?.debug(`Query '${this.collectionName}': all()`, {\n operation: 'all',\n });\n\n const records = this._dbCollection.all();\n this._logger?.debug(`Query '${this.collectionName}' returned ${records.length} records`);\n\n const models = records.map((record) => this._createModelFromRecord(record));\n return new ModelCollection(this._template, models, this._serializer);\n }\n\n /**\n * Returns the first model in the collection.\n * @returns The first model in the collection or null if the collection is empty.\n */\n first(): ModelInstance<TTemplate, TSchema, TSerializer> | null {\n this._logger?.debug(`Query '${this.collectionName}': first()`);\n\n const record = this._dbCollection.first();\n if (record) {\n this._logger?.debug(`Query '${this.collectionName}' found record`, { id: record.id });\n }\n return record ? this._createModelFromRecord(record) : null;\n }\n\n /**\n * Returns the last model in the collection.\n * @returns The last model in the collection or null if the collection is empty.\n */\n last(): ModelInstance<TTemplate, TSchema, TSerializer> | null {\n this._logger?.debug(`Query '${this.collectionName}': last()`);\n\n const record = this._dbCollection.last();\n if (record) {\n this._logger?.debug(`Query '${this.collectionName}' found record`, { id: record.id });\n }\n return record ? this._createModelFromRecord(record) : null;\n }\n\n /**\n * Finds a model by ID, predicate object, or query options.\n * @param input - The ID, predicate object, or query options to find by.\n * @returns The model instance or null if not found.\n * @example\n * ```typescript\n * // Find by ID\n * collection.find('1');\n *\n * // Find by predicate object\n * collection.find({ email: 'user@example.com' });\n *\n * // Find with query options\n * collection.find({ where: { age: { gte: 18 } }, orderBy: { name: 'asc' } });\n * ```\n */\n find(\n input:\n | ModelAttrs<TTemplate, TSchema>['id']\n | DbRecordInput<ModelAttrs<TTemplate, TSchema>>\n | QueryOptions<ModelAttrs<TTemplate, TSchema>>,\n ): ModelInstance<TTemplate, TSchema, TSerializer> | null {\n this._logger?.debug(`Find in '${this.collectionName}'`, {\n query: typeof input === 'object' && 'where' in input ? 'QueryOptions' : input,\n });\n\n // Handle QueryOptions with callback where clause\n if (typeof input === 'object' && 'where' in input && typeof input.where === 'function') {\n const queryOptions = this._convertQueryOptionsCallback(input);\n const record = this._dbCollection.find(queryOptions);\n if (record) {\n this._logger?.debug(`Find in '${this.collectionName}' found record`, { id: record.id });\n }\n return record ? this._createModelFromRecord(record) : null;\n }\n\n const record = this._dbCollection.find(input);\n if (record) {\n this._logger?.debug(`Find in '${this.collectionName}' found record`, { id: record.id });\n }\n return record ? this._createModelFromRecord(record) : null;\n }\n\n /**\n * Finds multiple models by IDs, predicate object, or query options.\n * @param input - The array of IDs, predicate object, or query options to find by.\n * @returns A collection of matching model instances.\n * @example\n * ```typescript\n * // Find by IDs\n * collection.findMany(['1', '2', '3']);\n *\n * // Find by predicate object\n * collection.findMany({ status: 'active' });\n *\n * // Find with query options\n * collection.findMany({\n * where: { age: { gte: 18 } },\n * orderBy: { name: 'asc' },\n * limit: 10\n * });\n *\n * // Find with callback where clause\n * collection.findMany({\n * where: (model) => model.age >= 18 && model.status === 'active'\n * });\n * ```\n */\n findMany(\n input:\n | ModelAttrs<TTemplate, TSchema>['id'][]\n | DbRecordInput<ModelAttrs<TTemplate, TSchema>>\n | QueryOptions<ModelAttrs<TTemplate, TSchema>>,\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n this._logger?.debug(`Query '${this.collectionName}': findMany`, {\n query: Array.isArray(input)\n ? `${input.length} IDs`\n : typeof input === 'object' && 'where' in input\n ? 'QueryOptions'\n : input,\n });\n\n // Handle QueryOptions with callback where clause\n if (typeof input === 'object' && 'where' in input && typeof input.where === 'function') {\n const queryOptions = this._convertQueryOptionsCallback(input);\n const records = this._dbCollection.findMany(queryOptions);\n\n this._logger?.debug(`Query '${this.collectionName}' returned ${records.length} records`);\n\n const models = records.map((record) => this._createModelFromRecord(record));\n return new ModelCollection(this._template, models, this._serializer);\n }\n\n const records = this._dbCollection.findMany(input);\n this._logger?.debug(`Query '${this.collectionName}' returned ${records.length} records`);\n\n const models = records.map((record) => this._createModelFromRecord(record));\n return new ModelCollection(this._template, models, this._serializer);\n }\n\n /**\n * Deletes a model from the collection.\n * @param id - The id of the model to delete.\n */\n delete(id: ModelAttrs<TTemplate, TSchema>['id']): void {\n this._logger?.debug(`Delete from '${this.collectionName}'`, { id });\n this._dbCollection.delete(id);\n }\n\n /**\n * Deletes multiple models by IDs, predicate object, or query options.\n * @param input - The array of IDs, predicate object, or query options to delete by.\n * @returns The number of records that were deleted.\n * @example\n * ```typescript\n * // Delete by IDs\n * collection.deleteMany(['1', '2', '3']);\n *\n * // Delete by predicate object\n * collection.deleteMany({ status: 'inactive' });\n *\n * // Delete with query options\n * collection.deleteMany({\n * where: { age: { lt: 18 } },\n * limit: 10\n * });\n * ```\n */\n deleteMany(\n input:\n | ModelAttrs<TTemplate, TSchema>['id'][]\n | DbRecordInput<ModelAttrs<TTemplate, TSchema>>\n | QueryOptions<ModelAttrs<TTemplate, TSchema>>,\n ): number {\n this._logger?.debug(`Delete many from '${this.collectionName}'`, {\n query: Array.isArray(input) ? `${input.length} IDs` : input,\n });\n\n // Handle QueryOptions with callback where clause\n if (typeof input === 'object' && 'where' in input && typeof input.where === 'function') {\n const queryOptions = this._convertQueryOptionsCallback(input);\n const count = this._dbCollection.deleteMany(queryOptions);\n\n this._logger?.debug(`Deleted ${count} records from '${this.collectionName}'`);\n\n return count;\n }\n\n const count = this._dbCollection.deleteMany(input);\n this._logger?.debug(`Deleted ${count} records from '${this.collectionName}'`);\n return count;\n }\n\n // -- PRIVATE METHODS --\n\n /**\n * Converts QueryOptions with a callback where clause to work with models instead of raw records.\n * @param options - The query options with a callback where clause\n * @returns Query options with converted where clause\n */\n private _convertQueryOptionsCallback(\n options: QueryOptions<ModelAttrs<TTemplate, TSchema>>,\n ): QueryOptions<ModelAttrs<TTemplate, TSchema>> {\n if (!options.where || typeof options.where !== 'function') {\n return options;\n }\n\n const modelWhereCallback = options.where as (\n model: ModelInstance<TTemplate, TSchema, TSerializer>,\n helpers: WhereHelperFns<ModelAttrs<TTemplate, TSchema>>,\n ) => boolean;\n\n // Convert to a callback that works with records\n const recordWhereCallback = (\n record: ModelAttrs<TTemplate, TSchema>,\n helpers: WhereHelperFns<ModelAttrs<TTemplate, TSchema>>,\n ): boolean => {\n const model = this._createModelFromRecord(record);\n return modelWhereCallback(model, helpers);\n };\n\n return {\n ...options,\n where: recordWhereCallback,\n };\n }\n\n /**\n * Helper to create a model instance from a database record.\n * @param record - The database record to create the model from (must have ID).\n * @returns The model instance.\n */\n protected _createModelFromRecord(\n record: ModelAttrs<TTemplate, TSchema>,\n ): ModelInstance<TTemplate, TSchema, TSerializer> {\n return new this.Model({\n attrs: record as ModelCreateAttrs<TTemplate, TSchema>,\n relationships: this._relationships as unknown as RelationshipsByTemplate<TTemplate, TSchema>,\n schema: this._schema,\n serializer: this._serializer,\n } as unknown as ModelConfig<\n TTemplate,\n TSchema,\n RelationshipsByTemplate<TTemplate, TSchema>,\n TSerializer\n >) as ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n /**\n * Initialize and create the database collection if needed\n * @param identityManager - The identity manager to use for the collection\n * @returns The database collection instance\n * @private\n */\n private _initializeDbCollection(\n identityManager?: IdentityManager<ModelId<TTemplate>>,\n ): DbCollection<ModelAttrs<TTemplate, TSchema>> {\n if (!this._schema.db.hasCollection(this.collectionName)) {\n this._schema.db.createCollection(this.collectionName, {\n identityManager: identityManager,\n });\n }\n return this._schema.db.getCollection(this._template.collectionName) as unknown as DbCollection<\n ModelAttrs<TTemplate, TSchema>\n >;\n }\n}\n","import type { DbRecordInput, NewDbRecord, QueryOptions } from '@src/db';\nimport type { Factory, FactoryTraitNames, ModelTraits } from '@src/factory';\nimport {\n Model,\n ModelCollection,\n ModelCreateAttrs,\n PartialModelAttrs,\n type ModelAttrs,\n type ModelConfig,\n type ModelInstance,\n type ModelRelationships,\n type ModelTemplate,\n type NewModelInstance,\n type RelationshipsByTemplate,\n} from '@src/model';\nimport { MirageError } from '@src/utils';\n\nimport { BaseCollection } from './BaseCollection';\nimport type { SchemaInstance } from './Schema';\nimport type { CollectionConfig, CollectionCreateAttrs, SchemaCollections } from './types';\n\n/**\n * Collection for managing models of a specific type\n * @template TSchema - The schema collections type for enhanced type inference\n * @template TTemplate - The model template type (most important for users)\n * @template TRelationships - The raw relationships configuration for this collection (inferred from config)\n * @template TFactory - The factory type (inferred from config)\n * @template TSerializer - The serializer type (inferred from config)\n */\nexport default class Collection<\n TSchema extends SchemaCollections = SchemaCollections,\n TTemplate extends ModelTemplate = ModelTemplate,\n TRelationships extends ModelRelationships = {},\n TFactory extends\n | Factory<TTemplate, TSchema, ModelTraits<TSchema, TTemplate>>\n | undefined = undefined,\n TSerializer = undefined,\n> extends BaseCollection<TSchema, TTemplate, TRelationships, TFactory, TSerializer> {\n /**\n * Creates a new model instance (not persisted in the database).\n * @param attrs - The attributes to create the model with. All required attributes must be provided.\n * @returns The new model instance.\n */\n new(\n attrs: ModelCreateAttrs<TTemplate, TSchema>,\n ): NewModelInstance<TTemplate, TSchema, TSerializer> {\n return new this.Model({\n attrs: attrs,\n relationships: this._relationships,\n schema: this._schema,\n serializer: this._serializer,\n } as unknown as ModelConfig<\n TTemplate,\n TSchema,\n RelationshipsByTemplate<TTemplate, TSchema>,\n TSerializer\n >);\n }\n\n /**\n * Create a new model for the collection.\n * @param traitsAndDefaults - The traits or default values to use for the model.\n * @returns The new model instance.\n */\n create(\n ...traitsAndDefaults: (\n | FactoryTraitNames<TFactory>\n | CollectionCreateAttrs<TTemplate, TSchema>\n )[]\n ): ModelInstance<TTemplate, TSchema, TSerializer> {\n this._logger?.debug(`Creating ${this.modelName}`, {\n collection: this.collectionName,\n traitsAndDefaults,\n });\n\n // Extract traits and defaults\n const traits: FactoryTraitNames<TFactory>[] = [];\n let defaults: CollectionCreateAttrs<TTemplate, TSchema> = {};\n\n traitsAndDefaults.forEach((arg) => {\n if (typeof arg === 'string') {\n traits.push(arg);\n } else {\n defaults = { ...defaults, ...arg };\n }\n });\n\n // Separate regular attributes from relationship values\n const { modelAttrs, relationshipUpdates } = Model._processAttrs<TTemplate, TSchema>(\n defaults,\n this._relationships as unknown as RelationshipsByTemplate<TTemplate, TSchema>,\n );\n\n const nextId = modelAttrs.id ?? this._dbCollection.nextId;\n\n // Cache the id if provided\n if (modelAttrs.id) {\n this._identityManager?.set(modelAttrs.id as ModelAttrs<TTemplate, TSchema>['id']);\n }\n\n if (this._factory) {\n // Build factory attrs with only regular attributes (no relationships)\n const factoryAttrs = this._factory.build(\n nextId,\n ...traits,\n modelAttrs as PartialModelAttrs<TTemplate, TSchema>,\n ) as ModelAttrs<TTemplate, TSchema>;\n\n // Skip associations for relationships that the user provided\n const userProvidedRelationshipKeys = Object.keys(relationshipUpdates);\n\n // Process associations to get relationship values (including from traits)\n const associationValues = this._factory.processAssociations(\n this._schema,\n userProvidedRelationshipKeys,\n traitsAndDefaults as (\n | FactoryTraitNames<TFactory>\n | PartialModelAttrs<TTemplate, TSchema>\n )[],\n );\n\n // Merge: user defaults override associations, associations override factory attrs\n const completeAttrs = {\n ...factoryAttrs,\n ...associationValues,\n ...relationshipUpdates, // User-provided relationships have highest priority\n } as ModelCreateAttrs<TTemplate, TSchema>;\n\n const model = this.new(completeAttrs).save();\n this._logger?.debug(`Created ${this.modelName} with factory`, {\n id: model.id,\n collection: this.collectionName,\n });\n // Type assertion needed: factory hook expects specific model instance type\n // but we're working with a more general model type at this point\n return this._factory.processAfterCreateHooks(\n this._schema,\n model as any,\n ...(traitsAndDefaults as (\n | FactoryTraitNames<TFactory>\n | PartialModelAttrs<TTemplate, TSchema>\n )[]),\n ) as ModelInstance<TTemplate, TSchema, TSerializer>;\n }\n\n // No factory - merge modelAttrs with relationship values\n const attrs = { ...modelAttrs, ...relationshipUpdates, id: nextId } as ModelCreateAttrs<\n TTemplate,\n TSchema\n >;\n const model = this.new(attrs).save();\n this._logger?.debug(`Created ${this.modelName}`, {\n id: model.id,\n collection: this.collectionName,\n });\n\n return model;\n }\n\n /**\n * Create a list of models for the collection.\n * @param count - The number of models to create.\n * @param traitsAndDefaults - The traits or default values to use for the models.\n * @returns A list of model instances.\n */\n createList(\n count: number,\n ...traitsAndDefaults: (\n | FactoryTraitNames<TFactory>\n | CollectionCreateAttrs<TTemplate, TSchema>\n )[]\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n const models = Array.from({ length: count }, () => this.create(...traitsAndDefaults));\n return new ModelCollection(this._template, models, this._serializer);\n }\n\n /**\n * Finds the first model matching the query or creates a new one.\n * @param query - The query to find the model by.\n * @param traitsAndDefaults - The traits or default values to use when creating a new model.\n * @returns The model instance.\n */\n findOrCreateBy(\n query: DbRecordInput<ModelAttrs<TTemplate, TSchema>>,\n ...traitsAndDefaults: (\n | FactoryTraitNames<TFactory>\n | CollectionCreateAttrs<TTemplate, TSchema>\n )[]\n ): ModelInstance<TTemplate, TSchema, TSerializer> {\n const existingModel = this.find(query);\n if (existingModel) {\n return existingModel;\n }\n\n const newModel = this.create(\n ...traitsAndDefaults,\n query as CollectionCreateAttrs<TTemplate, TSchema>,\n );\n return newModel;\n }\n\n /**\n * Finds or creates a specific number of models matching the query.\n * @param count - The number of models to find or create.\n * @param query - The query to find the models by (object or predicate function).\n * @param traitsAndDefaults - The traits or default values to use when creating new models.\n * @returns A collection of models.\n */\n findManyOrCreateBy(\n count: number,\n query:\n | DbRecordInput<ModelAttrs<TTemplate, TSchema>>\n | ((model: ModelInstance<TTemplate, TSchema, TSerializer>) => boolean),\n ...traitsAndDefaults: (\n | FactoryTraitNames<TFactory>\n | CollectionCreateAttrs<TTemplate, TSchema>\n )[]\n ): ModelCollection<TTemplate, TSchema, TSerializer> {\n // Find existing models matching the query\n const existingModels =\n typeof query === 'function'\n ? this.findMany({ where: query } as unknown as QueryOptions<ModelAttrs<TTemplate, TSchema>>)\n : this.findMany(query as DbRecordInput<ModelAttrs<TTemplate, TSchema>>);\n\n // If we have enough models, return the requested count\n if (existingModels.length >= count) {\n return new ModelCollection(\n this._template,\n existingModels.models.slice(0, count),\n this._serializer,\n );\n }\n\n // Calculate how many more models we need to create\n const needed = count - existingModels.length;\n\n // Create the remaining models\n // If query is an object, include it in the creation attributes\n const queryAttrs =\n typeof query === 'function' ? {} : (query as CollectionCreateAttrs<TTemplate, TSchema>);\n\n const newModels = Array.from({ length: needed }, () =>\n this.create(...traitsAndDefaults, queryAttrs),\n );\n\n // Combine existing and new models\n return new ModelCollection(\n this._template,\n [...existingModels.models, ...newModels],\n this._serializer,\n );\n }\n\n /**\n * Load seeds for this collection.\n * If scenarioId is not provided, all seeds will be loaded (or 'default' if seeds is a function).\n * If scenarioId is provided, only that specific seed scenario will be loaded.\n * @param scenarioId - Optional scenario ID to load a specific seed\n * @throws {MirageError} If the specified scenarioId does not exist\n * @example\n * ```typescript\n * // Load all seeds\n * collection.loadSeeds();\n *\n * // Load specific scenario\n * collection.loadSeeds('userForm');\n * ```\n */\n async loadSeeds(scenarioId?: string): Promise<void> {\n if (!this._seeds) {\n this._logger?.debug(`No seeds configured for collection '${this.collectionName}'`);\n return;\n }\n\n // Normalize seeds to always be an object\n const seedScenarios: Record<string, (schema: SchemaInstance<TSchema>) => void | Promise<void>> =\n typeof this._seeds === 'function' ? { default: this._seeds } : this._seeds;\n\n // If no scenarioId provided, run all scenarios\n if (!scenarioId) {\n this._logger?.info(`Loading all seeds for collection '${this.collectionName}'`, {\n scenarios: Object.keys(seedScenarios),\n });\n for (const name in seedScenarios) {\n const seedFn = seedScenarios[name];\n this._logger?.debug(`Running seed scenario '${name}' for '${this.collectionName}'`);\n await seedFn(this._schema);\n }\n this._logger?.info(`Seeds loaded successfully for '${this.collectionName}'`);\n return;\n }\n\n // If scenarioId provided, run only that scenario\n if (!(scenarioId in seedScenarios)) {\n const availableScenarios = Object.keys(seedScenarios).join(', ');\n this._logger?.error(`Seed scenario '${scenarioId}' not found`, {\n collection: this.collectionName,\n requested: scenarioId,\n available: Object.keys(seedScenarios),\n });\n throw new MirageError(\n `Seed scenario '${scenarioId}' does not exist in collection '${this.collectionName}'. ` +\n `Available scenarios: ${availableScenarios}`,\n );\n }\n\n this._logger?.info(`Loading seed scenario '${scenarioId}' for '${this.collectionName}'`);\n await seedScenarios[scenarioId](this._schema);\n this._logger?.info(`Seed scenario '${scenarioId}' loaded successfully`);\n }\n\n /**\n * Load fixtures for this collection.\n * Fixtures are static data records that will be inserted into the collection.\n * This method will insert all fixture records into the database.\n * @example\n * ```typescript\n * // Load all fixtures\n * await collection.loadFixtures();\n * ```\n */\n async loadFixtures(): Promise<void> {\n if (!this._fixtures || !this._fixtures.records.length) {\n this._logger?.debug(`No fixtures configured for collection '${this.collectionName}'`);\n return;\n }\n\n this._logger?.info(`Loading fixtures for collection '${this.collectionName}'`, {\n count: this._fixtures.records.length,\n });\n\n // Check for ID conflicts with existing records\n const fixtureIds = this._fixtures.records.map((r) => r.id);\n const existingIds = this._dbCollection.all().map((r) => r.id);\n const conflicts = fixtureIds.filter((id) => existingIds.includes(id));\n\n if (conflicts.length > 0) {\n this._logger?.error('Fixture loading failed: ID conflicts detected', {\n collection: this.collectionName,\n conflicts,\n });\n throw new MirageError(\n `Cannot load fixtures for '${this.collectionName}': ID conflicts detected. ` +\n `The following fixture IDs already exist in the database: ${conflicts.join(', ')}. ` +\n `Clear the database with db.emptyData() before loading fixtures, or use different IDs.`,\n );\n }\n\n // Insert all fixture records into the database at once\n // Fixtures are typed as FixtureAttrs which includes all model attributes\n // The type assertion bridges FixtureAttrs (all attrs including id) with\n // NewDbRecord (Omit<Record, 'id'> & { id?: id }), which are structurally compatible\n this._dbCollection.insertMany(\n this._fixtures.records as NewDbRecord<ModelAttrs<TTemplate, TSchema>>[],\n );\n\n this._logger?.info(`Fixtures loaded successfully for '${this.collectionName}'`, {\n count: this._fixtures.records.length,\n });\n }\n}\n\n/**\n * Create a collection with relationship support.\n * @param schema - The schema instance\n * @param config - The collection configuration\n * @returns A collection with inferred types\n */\nexport function createCollection<\n TSchema extends SchemaCollections,\n TConfig extends CollectionConfig<any, any, any, any, any>,\n>(\n schema: SchemaInstance<TSchema>,\n config: TConfig,\n): TConfig extends CollectionConfig<\n infer TTemplate,\n infer TRelationships,\n infer TFactory,\n infer TSerializer,\n any\n>\n ? Collection<\n TSchema,\n TTemplate,\n TRelationships extends ModelRelationships ? TRelationships : {},\n TFactory,\n TSerializer\n >\n : never {\n // Type assertion needed: Factory function with complex conditional return type\n // TypeScript can't verify generic parameters match the conditional type structure\n return new Collection(schema, config) as any;\n}\n","import type { InferModelAttrs, ModelInstance, ModelTemplate } from '@src/model';\nimport type ModelCollection from '@src/model/ModelCollection';\nimport type { SchemaCollections } from '@src/schema';\n\nimport type { SerializerOptions } from './types';\n\n/**\n * Serializer class that handles model serialization with custom JSON types\n * @template TTemplate - The model template\n * @template TSerializedModel - The serialized model type (for single model)\n * @template TSerializedCollection - The serialized collection type (for array of models)\n * @template TOptions - The serializer options type\n * @example\n * ```typescript\n * interface UserJSON {\n * id: string;\n * name: string;\n * }\n *\n * interface UsersJSON {\n * users: UserJSON[];\n * }\n *\n * const serializer = new Serializer<UserTemplate, UserJSON, UsersJSON>(\n * userTemplate,\n * {\n * attrs: ['id', 'name'],\n * root: 'user'\n * }\n * );\n * ```\n */\nexport default class Serializer<\n TTemplate extends ModelTemplate,\n TSerializedModel = InferModelAttrs<TTemplate>,\n TSerializedCollection = TSerializedModel[],\n TOptions extends SerializerOptions<TTemplate> = SerializerOptions<TTemplate>,\n> {\n protected _template: TTemplate;\n protected _modelName: string;\n protected _collectionName: string;\n protected _attrs: TOptions['attrs'];\n protected _root: TOptions['root'];\n protected _embed: TOptions['embed'];\n protected _include: TOptions['include'];\n\n constructor(template: TTemplate, options?: TOptions) {\n this._template = template;\n this._modelName = template.modelName;\n this._collectionName = template.collectionName;\n this._attrs = options?.attrs;\n this._root = options?.root;\n this._embed = options?.embed;\n this._include = options?.include;\n }\n\n /**\n * Get the model name\n * @returns The model name\n */\n get modelName(): string {\n return this._modelName;\n }\n\n /**\n * Get the collection name\n * @returns The collection name\n */\n get collectionName(): string {\n return this._collectionName;\n }\n\n /**\n * Serialize raw data from a model without structural wrapping\n * This method extracts and returns the data (attributes + relationships)\n * without applying any root wrapping. Used for embedding relationships.\n * @param model - The model instance to serialize\n * @returns The serialized model data without root wrapping\n */\n serializeData<TSchema extends SchemaCollections>(\n model: ModelInstance<TTemplate, TSchema>,\n ): Record<string, any> {\n const attrs = this._getAttributes(model);\n\n if (this._embed) {\n // Embed mode: merge embedded relationships, removing foreign keys\n const { embedded, foreignKeys } = this._getRelationships(model);\n const filteredAttrs = { ...attrs };\n\n // Remove foreign keys from attributes when embedding\n for (const fk of foreignKeys) {\n delete filteredAttrs[fk];\n }\n\n return { ...filteredAttrs, ...embedded };\n }\n\n // Default: return attributes as-is (foreign keys remain)\n return attrs;\n }\n\n /**\n * Serialize a single model instance with structural formatting\n * Applies root wrapping and side-loading if configured\n * @param model - The model instance to serialize\n * @returns The serialized model with structural formatting applied\n */\n serialize<TSchema extends SchemaCollections>(\n model: ModelInstance<TTemplate, TSchema>,\n ): TSerializedModel {\n const data = this.serializeData(model);\n\n // Get side-loaded relationships (only when embed: false)\n const { sideLoaded } = this._embed ? { sideLoaded: {} } : this._getRelationships(model);\n\n if (this._root) {\n const rootKey = typeof this._root === 'string' ? this._root : this._modelName;\n // With root wrapping: merge side-loaded at root level\n return { [rootKey]: data, ...sideLoaded } as TSerializedModel;\n }\n\n // Without root wrapping: merge side-loaded at same level\n return { ...data, ...sideLoaded } as TSerializedModel;\n }\n\n /**\n * Serialize raw data from a collection without structural wrapping\n * Returns an array of serialized model data without root wrapping\n * @param collection - The model collection to serialize\n * @returns Array of serialized model data\n */\n serializeCollectionData<TSchema extends SchemaCollections>(\n collection: ModelCollection<TTemplate, TSchema>,\n ): Record<string, any>[] {\n return collection.models.map((model) => this.serializeData(model));\n }\n\n /**\n * Serialize a model collection with structural formatting\n * Applies root wrapping and side-loading if configured\n * @param collection - The model collection to serialize\n * @returns The serialized collection with structural formatting applied\n */\n serializeCollection<TSchema extends SchemaCollections>(\n collection: ModelCollection<TTemplate, TSchema>,\n ): TSerializedCollection {\n const data = this.serializeCollectionData(collection);\n\n // Collect all side-loaded relationships from all models\n const allSideLoaded: Record<string, any[]> = {};\n if (!this._embed && this._include) {\n for (const model of collection.models) {\n const { sideLoaded } = this._getRelationships(model);\n\n // Merge side-loaded data, deduplicating by ID\n for (const key in sideLoaded) {\n const value = sideLoaded[key];\n if (!allSideLoaded[key]) {\n allSideLoaded[key] = [];\n }\n\n // Add to side-loaded array if not already present (dedupe by id)\n if (Array.isArray(value)) {\n for (const item of value) {\n if (!allSideLoaded[key].some((existing) => existing.id === item.id)) {\n allSideLoaded[key].push(item);\n }\n }\n } else if (value && !allSideLoaded[key].some((existing) => existing.id === value.id)) {\n allSideLoaded[key].push(value);\n }\n }\n }\n }\n\n if (this._root) {\n const rootKey = typeof this._root === 'string' ? this._root : this._collectionName;\n // With root wrapping: merge side-loaded at root level\n return { [rootKey]: data, ...allSideLoaded } as TSerializedCollection;\n }\n\n // Without root wrapping: return array (side-loading not supported without root)\n return data as TSerializedCollection;\n }\n\n /**\n * Get the attributes to include in serialization\n * Can be overridden in subclasses for custom serialization logic\n * @param model - The model instance\n * @returns Object with attributes\n */\n protected _getAttributes<TSchema extends SchemaCollections>(\n model: ModelInstance<TTemplate, TSchema>,\n ): Record<string, any> {\n // If specific attributes are configured, only include those\n if (this._attrs && this._attrs.length > 0) {\n return this._attrs.reduce(\n (acc, attr) => {\n acc[attr as string] = model.attrs[attr as keyof typeof model.attrs];\n return acc;\n },\n {} as Record<string, any>,\n );\n }\n\n // Default: return raw attributes (no embedding, no filtering)\n return { ...model.attrs };\n }\n\n /**\n * Get relationships to include in serialization\n * Returns embedded relationships, side-loaded relationships, and foreign keys\n * @param model - The model instance\n * @returns Object with embedded, sideLoaded, and foreignKeys\n */\n protected _getRelationships<TSchema extends SchemaCollections>(\n model: ModelInstance<TTemplate, TSchema>,\n ): { embedded: Record<string, any>; sideLoaded: Record<string, any>; foreignKeys: string[] } {\n // If no relationships are configured to include, return empty\n if (!this._include || this._include.length === 0) {\n return { embedded: {}, sideLoaded: {}, foreignKeys: [] };\n }\n\n const embedded: Record<string, any> = {};\n const sideLoaded: Record<string, any> = {};\n const foreignKeys: string[] = [];\n\n for (const relName of this._include) {\n // Access the relationship from the model (dynamic property - models/collections)\n const relatedData = (model as any)[relName];\n\n // Skip if relationship doesn't exist or is undefined\n if (relatedData === undefined) {\n continue;\n }\n\n // Handle null relationships (belongsTo with no related model)\n if (relatedData === null) {\n if (this._embed) {\n embedded[relName] = null;\n // Track foreign key to remove even when null (e.g., 'authorId')\n foreignKeys.push(`${relName}Id`);\n } else {\n sideLoaded[relName] = null;\n }\n continue;\n }\n\n // Check if it's a ModelCollection (hasMany)\n const isCollection =\n relatedData && typeof relatedData === 'object' && 'models' in relatedData;\n\n if (this._embed) {\n // Embed mode: replace foreign keys with full models\n if (isCollection) {\n // HasMany: serialize each model in the collection\n embedded[relName] = relatedData.models.map((relModel: any) => {\n return { ...relModel.attrs };\n });\n // Track foreign key to remove (e.g., 'postIds')\n foreignKeys.push(`${relName.replace(/s$/, '')}Ids`);\n } else {\n // BelongsTo: serialize single model\n embedded[relName] = { ...relatedData.attrs };\n // Track foreign key to remove (e.g., 'authorId')\n foreignKeys.push(`${relName}Id`);\n }\n } else {\n // Side-load mode: keep foreign keys, add full models separately\n if (isCollection) {\n // HasMany: side-load all models as array\n sideLoaded[relName] = relatedData.models.map((relModel: any) => {\n return { ...relModel.attrs };\n });\n } else {\n // BelongsTo: side-load single model\n sideLoaded[relName] = { ...relatedData.attrs };\n }\n }\n }\n\n return { embedded, sideLoaded, foreignKeys };\n }\n}\n","import type { Factory, ModelTraits } from '@src/factory';\nimport type { IdentityManager, StringIdentityManager } from '@src/id-manager';\nimport type { ModelTemplate, ModelRelationships } from '@src/model';\nimport { Serializer, type SerializerOptions } from '@src/serializer';\nimport { MirageError } from '@src/utils';\n\nimport type {\n CollectionConfig,\n FixtureConfig,\n FixtureLoadStrategy,\n FixtureAttrs,\n SchemaCollections,\n Seeds,\n} from './types';\n\n/**\n * A fluent builder for creating schema collection configurations.\n *\n * The CollectionBuilder provides a type-safe way to construct CollectionConfig instances\n * with configurable model template, factory, relationships, serializer, and identity manager. It follows\n * the builder pattern, allowing method chaining to progressively configure the collection.\n * @template TTemplate - The model template type\n * @template TRelationships - The model relationships configuration\n * @template TFactory - The factory type\n * @template TIdentityManager - The identity manager type\n * @example\n * ```typescript\n * const userCollection = collection(UserModel)\n * .relationships({\n * posts: associations.hasMany(PostModel),\n * })\n * .factory(userFactory)\n * .identityManager(userIdentityManager)\n * .create();\n * ```\n */\nexport default class CollectionBuilder<\n TTemplate extends ModelTemplate = ModelTemplate,\n TSchema extends SchemaCollections = SchemaCollections,\n TRelationships extends ModelRelationships = {},\n TFactory extends\n | Factory<TTemplate, TSchema, ModelTraits<TSchema, TTemplate>>\n | undefined = undefined,\n TIdentityManager extends IdentityManager = StringIdentityManager,\n TSerializer = undefined,\n> {\n private _template?: TTemplate;\n private _factory?: TFactory;\n private _relationships?: TRelationships;\n private _identityManager?: TIdentityManager;\n private _serializerConfig?: SerializerOptions<TTemplate>;\n private _serializerInstance?: TSerializer;\n private _seeds?: Seeds<TSchema>;\n private _fixtures?: FixtureConfig<TTemplate, TRelationships>;\n\n /**\n * Creates a new CollectionBuilder instance.\n * @private\n */\n constructor() {}\n\n /**\n * Sets the model template for this collection.\n *\n * The template defines the model type, and collection name\n * for this collection configuration.\n * @template T - The model template type\n * @param template - The model template instance\n * @returns A new CollectionBuilder instance with the specified template\n * @example\n * ```typescript\n * const builder = collection.model(userTemplate);\n * ```\n */\n model<T extends ModelTemplate>(\n template: T,\n ): CollectionBuilder<\n T,\n TSchema,\n TRelationships,\n TFactory extends undefined ? undefined : Factory<T, TSchema, ModelTraits<TSchema, T>>,\n TIdentityManager,\n TSerializer\n > {\n // Validate model template structure\n if (!template || typeof template !== 'object') {\n throw new MirageError(\n 'Invalid model template. Expected a ModelTemplate object created with model().name(...).collection(...).create().',\n );\n }\n if (!template.modelName) {\n throw new MirageError(\n 'Model template is missing modelName property. Ensure you called .name() when building the model.',\n );\n }\n if (!template.collectionName) {\n throw new MirageError(\n 'Model template is missing collectionName property. Ensure you called .collection() when building the model.',\n );\n }\n\n const builder = new CollectionBuilder<\n T,\n TSchema,\n TRelationships,\n TFactory extends undefined ? undefined : Factory<T, TSchema, ModelTraits<TSchema, T>>,\n TIdentityManager,\n TSerializer\n >();\n builder._template = template;\n // Preserve factory if it exists, casting it to the new template type\n // This allows for flexibility in the builder pattern while maintaining type safety at build time\n builder._factory = this._factory as unknown as typeof builder._factory;\n builder._relationships = this._relationships;\n builder._serializerConfig = this\n ._serializerConfig as unknown as typeof builder._serializerConfig;\n builder._serializerInstance = this._serializerInstance;\n builder._identityManager = this._identityManager;\n builder._seeds = this._seeds;\n builder._fixtures = this._fixtures as unknown as typeof builder._fixtures;\n return builder;\n }\n\n /**\n * Sets the factory for creating model instances.\n *\n * The factory provides default attributes and traits for creating new model instances\n * in this collection.\n * @template F - The factory type\n * @param factory - The factory instance\n * @returns A new CollectionBuilder instance with the specified factory\n * @example\n * ```typescript\n * const builder = collection(UserModel).factory(userFactory);\n * ```\n */\n factory<F extends Factory<any, any, any>>(\n factory: F,\n ): CollectionBuilder<TTemplate, TSchema, TRelationships, F, TIdentityManager, TSerializer> {\n const builder = new CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n F,\n TIdentityManager,\n TSerializer\n >();\n builder._template = this._template;\n builder._factory = factory;\n builder._relationships = this._relationships;\n builder._identityManager = this._identityManager;\n builder._serializerConfig = this._serializerConfig;\n builder._serializerInstance = this._serializerInstance;\n builder._seeds = this._seeds;\n builder._fixtures = this._fixtures;\n return builder;\n }\n\n /**\n * Sets the relationships configuration for this collection.\n *\n * Relationships define how this model relates to other models in the schema,\n * including belongsTo and hasMany associations.\n * @template R - The relationships type\n * @param relationships - The relationships configuration object\n * @returns A new CollectionBuilder instance with the specified relationships\n * @example\n * ```typescript\n * const builder = collection(UserModel)\n * .relationships({\n * posts: associations.hasMany(PostModel),\n * profile: associations.belongsTo(profileTemplate),\n * });\n * ```\n */\n relationships<R extends ModelRelationships>(\n relationships: R,\n ): CollectionBuilder<TTemplate, TSchema, R, TFactory, TIdentityManager, TSerializer> {\n // Validate relationships configuration\n if (!relationships || typeof relationships !== 'object') {\n throw new MirageError(\n 'Invalid relationships configuration. Expected an object with relationship definitions.',\n );\n }\n\n const SUPPORTED_RELATIONSHIP_TYPES = ['hasMany', 'belongsTo'];\n\n for (const key in relationships) {\n const relationship = relationships[key];\n if (!relationship || typeof relationship !== 'object') {\n throw new MirageError(\n `Invalid relationship '${key}'. Expected a relationship object created with hasMany() or belongsTo().`,\n );\n }\n\n if (!relationship.type) {\n throw new MirageError(\n `Relationship '${key}' is missing type property. Use hasMany() or belongsTo() to create relationships.`,\n );\n }\n\n if (!SUPPORTED_RELATIONSHIP_TYPES.includes(relationship.type)) {\n throw new MirageError(\n `Relationship '${key}' has unsupported type '${relationship.type}'.\\n\\n` +\n `Supported relationship types:\\n` +\n ` - hasMany: One-to-many relationship (e.g., user.posts)\\n` +\n ` - belongsTo: Many-to-one relationship (e.g., post.author)\\n\\n` +\n `Use hasMany() or belongsTo() helpers to create relationships.`,\n );\n }\n\n // Validate model reference exists\n if (!relationship.targetModel) {\n throw new MirageError(\n `Relationship '${key}' is missing model reference. Ensure the relationship was created with hasMany(model) or belongsTo(model).`,\n );\n }\n }\n\n const builder = new CollectionBuilder<\n TTemplate,\n TSchema,\n R,\n TFactory,\n TIdentityManager,\n TSerializer\n >();\n builder._template = this._template;\n builder._factory = this._factory;\n builder._relationships = relationships;\n builder._identityManager = this._identityManager;\n builder._serializerConfig = this._serializerConfig;\n builder._serializerInstance = this._serializerInstance;\n builder._seeds = this._seeds;\n builder._fixtures = this._fixtures as unknown as typeof builder._fixtures;\n return builder;\n }\n\n /**\n * Sets the serializer configuration or instance for this collection.\n *\n * Accepts either a configuration object (attrs, root, embed, include) or a custom\n * serializer instance. The config will be merged with global schema config if present.\n * @param configOrSerializer - The serializer configuration object or instance\n * @returns A new CollectionBuilder instance with the specified serializer\n * @example\n * ```typescript\n * // With config\n * const builder = collection()\n * .model(UserModel)\n * .serializer({ attrs: ['id', 'name'], root: true });\n *\n * // With custom serializer instance\n * const builder = collection()\n * .model(UserModel)\n * .serializer(new CustomUserSerializer(userModel));\n * ```\n */\n serializer(\n configOrSerializer: SerializerOptions<TTemplate> | any,\n ): CollectionBuilder<TTemplate, TSchema, TRelationships, TFactory, TIdentityManager, any>;\n\n /**\n * Sets the serializer configuration or instance for this collection.\n * @param configOrSerializer - The serializer configuration object or instance\n * @returns A new CollectionBuilder instance with the specified serializer\n */\n serializer(\n configOrSerializer: any,\n ): CollectionBuilder<TTemplate, TSchema, TRelationships, TFactory, TIdentityManager, any> {\n const builder = new CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n TFactory,\n TIdentityManager,\n any\n >();\n builder._template = this._template;\n builder._factory = this._factory;\n builder._relationships = this._relationships;\n builder._identityManager = this._identityManager;\n builder._seeds = this._seeds;\n builder._fixtures = this._fixtures;\n\n // Determine if it's a config object or a serializer instance\n if (configOrSerializer instanceof Serializer) {\n builder._serializerInstance = configOrSerializer;\n } else {\n builder._serializerConfig = configOrSerializer;\n }\n\n return builder;\n }\n\n /**\n * Sets the identity manager for this collection.\n *\n * The identity manager handles ID generation and management for model instances\n * in this collection. If not specified, the schema's global identity manager will be used.\n * @template I - The identity manager type\n * @param identityManager - The identity manager instance\n * @returns A new CollectionBuilder instance with the specified identity manager\n * @example\n * ```typescript\n * const builder = collection\n * .model(UserModel)\n * .identityManager(new StringIdentityManager());\n * ```\n */\n identityManager<I extends IdentityManager<any>>(\n identityManager: I,\n ): CollectionBuilder<TTemplate, TSchema, TRelationships, TFactory, I, TSerializer> {\n const builder = new CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n TFactory,\n I,\n TSerializer\n >();\n builder._template = this._template;\n builder._factory = this._factory;\n builder._relationships = this._relationships;\n builder._identityManager = identityManager;\n builder._serializerConfig = this._serializerConfig;\n builder._serializerInstance = this._serializerInstance;\n builder._seeds = this._seeds;\n builder._fixtures = this._fixtures;\n return builder;\n }\n\n /**\n * Sets the seeds configuration for this collection.\n *\n * Seeds can be either a function or an object with named seed scenarios.\n * The function/methods receive the schema instance as a parameter.\n * @param seeds - A seed function or object with named seed scenarios\n * @returns A new CollectionBuilder instance with the specified seeds\n * @example\n * ```typescript\n * // With function\n * const builder = collection()\n * .model(UserModel)\n * .seeds((schema) => {\n * schema.users.create({ name: 'John' });\n * });\n *\n * // With named scenarios\n * const builder = collection()\n * .model(UserModel)\n * .seeds({\n * userForm: (schema) => {\n * schema.users.create({ name: 'John' });\n * },\n * userPosts: (schema) => {\n * const user = schema.users.create({ name: 'John' });\n * schema.posts.create({ title: 'Post 1', authorId: user.id });\n * },\n * });\n * ```\n */\n seeds(\n seeds: Seeds<TSchema>,\n ): CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n TFactory,\n TIdentityManager,\n TSerializer\n > {\n // Validate that 'default' is not used as a scenario name\n if (typeof seeds === 'object' && !Array.isArray(seeds) && 'default' in seeds) {\n throw new MirageError(\n \"The 'default' scenario name is reserved. Please use a different name for your seed scenario.\",\n );\n }\n\n const builder = new CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n TFactory,\n TIdentityManager,\n TSerializer\n >();\n builder._template = this._template;\n builder._factory = this._factory;\n builder._relationships = this._relationships;\n builder._identityManager = this._identityManager;\n builder._serializerConfig = this._serializerConfig;\n builder._serializerInstance = this._serializerInstance;\n builder._seeds = seeds;\n builder._fixtures = this._fixtures;\n return builder;\n }\n\n /**\n * Sets the fixtures configuration for this collection.\n *\n * Fixtures are static data records that can be loaded into the collection.\n * @param records - Array of fixture records to load\n * @param options - Configuration options for loading fixtures\n * @param options.strategy - The strategy to use for loading fixtures (default: 'manual')\n * @returns A new CollectionBuilder instance with the specified fixtures\n * @example\n * ```typescript\n * // Manual loading (default)\n * const builder = collection()\n * .model(UserModel)\n * .fixtures([\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ]);\n *\n * // Auto-load fixtures during schema setup\n * const builder = collection()\n * .model(UserModel)\n * .fixtures(\n * [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ],\n * { strategy: 'auto' }\n * );\n * ```\n */\n fixtures(\n records: FixtureAttrs<TTemplate, TRelationships>[],\n options?: { strategy?: FixtureLoadStrategy },\n ): CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n TFactory,\n TIdentityManager,\n TSerializer\n > {\n // Validate fixtures\n if (!Array.isArray(records)) {\n throw new MirageError(\n 'Fixtures must be an array of records. Pass an array of fixture objects with model attributes.',\n );\n }\n\n // Validate each fixture record has an id\n records.forEach((record, index) => {\n if (!record || typeof record !== 'object') {\n throw new MirageError(\n `Fixture at index ${index} is invalid. Expected an object with model attributes.`,\n );\n }\n if (record.id === undefined || record.id === null) {\n throw new MirageError(\n `Fixture at index ${index} is missing required 'id' property. All fixtures must have explicit IDs.`,\n );\n }\n });\n\n const builder = new CollectionBuilder<\n TTemplate,\n TSchema,\n TRelationships,\n TFactory,\n TIdentityManager,\n TSerializer\n >();\n\n builder._template = this._template;\n builder._factory = this._factory;\n builder._relationships = this._relationships;\n builder._identityManager = this._identityManager;\n builder._serializerConfig = this._serializerConfig;\n builder._serializerInstance = this._serializerInstance;\n builder._seeds = this._seeds;\n builder._fixtures = {\n records,\n strategy: options?.strategy ?? 'manual',\n };\n\n return builder;\n }\n\n /**\n * Creates the final schema collection configuration.\n * @returns The schema collection configuration\n */\n create(): CollectionConfig<TTemplate, TRelationships, TFactory, TSerializer, TSchema> {\n if (!this._template) {\n throw new MirageError(\n 'Model template must be set before creating collection. Call .model() first.',\n );\n }\n\n return {\n model: this._template,\n relationships: this._relationships,\n factory: this._factory,\n identityManager: this._identityManager,\n serializerConfig: this._serializerConfig,\n serializerInstance: this._serializerInstance,\n seeds: this._seeds,\n fixtures: this._fixtures,\n };\n }\n}\n\n/**\n * Creates a new CollectionBuilder instance for building collection configurations.\n * @template TSchema - The schema collections type (optional)\n * @returns A new CollectionBuilder instance ready for model specification\n * @example\n * ```typescript\n * // Schema-typed collection\n * const userCollection = collection<TestSchema>()\n * .model(UserModel)\n * .factory(userFactory)\n * .create();\n *\n * // Schema-less collection\n * const userCollection = collection()\n * .model(UserModel)\n * .create();\n * ```\n */\nexport function collection<\n TSchema extends SchemaCollections = SchemaCollections,\n>(): CollectionBuilder<ModelTemplate, TSchema, {}, undefined, StringIdentityManager, undefined> {\n return new CollectionBuilder<\n ModelTemplate,\n TSchema,\n {},\n undefined,\n StringIdentityManager,\n undefined\n >();\n}\n","import { createDatabase, type DbInstance } from '@src/db';\nimport { StringIdentityManager } from '@src/id-manager';\nimport type { ModelTemplate } from '@src/model';\nimport {\n Serializer,\n type SerializerOptions,\n type StructuralSerializerOptions,\n} from '@src/serializer';\nimport { Logger, MirageError } from '@src/utils';\n\nimport Collection, { createCollection } from './Collection';\nimport type {\n SchemaCollectionAccessors,\n CollectionConfig,\n SchemaCollections,\n SchemaConfig,\n SchemaDbCollections,\n} from './types';\n\n/**\n * Schema class that manages database and collections\n * @template TCollections - The type map of collection names to their configurations\n * @template TConfig - The schema configuration type with identity manager and global serializer config\n */\nexport default class Schema<\n TCollections extends SchemaCollections,\n TConfig extends SchemaConfig<any, any> = SchemaConfig<StringIdentityManager, undefined>,\n> {\n public readonly db: DbInstance<SchemaDbCollections<TCollections>>;\n public readonly identityManager: TConfig extends SchemaConfig<infer TIdentityManager, any>\n ? TIdentityManager\n : StringIdentityManager;\n public readonly logger?: Logger;\n\n private _collections: Map<string, Collection<any, any, any, any, any>> = new Map();\n private _globalSerializerConfig?: StructuralSerializerOptions;\n\n constructor(collections: TCollections, config?: TConfig) {\n this.db = createDatabase<SchemaDbCollections<TCollections>>();\n this.identityManager = config?.identityManager ?? new StringIdentityManager();\n this._globalSerializerConfig = config?.globalSerializerConfig;\n\n // Create logger if logging is enabled\n if (config?.logging?.enabled) {\n this.logger = new Logger(config.logging);\n this.logger.debug('Schema initialized', {\n collections: Object.keys(collections),\n });\n }\n\n this._registerCollections(collections);\n }\n\n /**\n * Get a schema collection by collection name\n * @param collectionName - The name of the collection\n * @returns The schema collection for the collection with proper typing\n */\n getCollection<K extends keyof TCollections>(\n collectionName: K,\n ): TCollections[K] extends CollectionConfig<\n infer TTemplate,\n infer TRelationships,\n infer TFactory,\n infer TSerializer,\n any\n >\n ? Collection<TCollections, TTemplate, TRelationships, TFactory, TSerializer>\n : never {\n const collection = this._collections.get(collectionName as string);\n if (!collection) {\n throw new MirageError(`Collection '${String(collectionName)}' not found`);\n }\n // Type assertion needed: Map storage loses specific generic parameters\n // TypeScript can't verify stored collection matches complex conditional return type\n return collection as any;\n }\n\n /**\n * Load all seeds for all collections in the schema.\n * This will run all seed scenarios for each collection.\n * To load specific scenarios, use collection.loadSeeds(scenarioId) on individual collections.\n * @example\n * ```typescript\n * // Load all seeds for all collections\n * await schema.loadSeeds();\n *\n * // Or load specific scenario for a single collection\n * await schema.users.loadSeeds('development');\n * ```\n */\n async loadSeeds(): Promise<void> {\n this.logger?.info('Loading seeds for all collections', {\n collections: Array.from(this._collections.keys()),\n });\n\n for (const collection of this._collections.values()) {\n await collection.loadSeeds();\n }\n\n this.logger?.info('All seeds loaded successfully');\n }\n\n /**\n * Load all fixtures for all collections in the schema.\n * This will insert all fixture records into each collection's database.\n * @example\n * ```typescript\n * // Load all fixtures for all collections\n * await schema.loadFixtures();\n * ```\n */\n async loadFixtures(): Promise<void> {\n this.logger?.info('Loading fixtures for all collections', {\n collections: Array.from(this._collections.keys()),\n });\n\n for (const collection of this._collections.values()) {\n await collection.loadFixtures();\n }\n\n this.logger?.info('All fixtures loaded successfully');\n }\n\n /**\n * Register collections from the configuration\n * @param collections - Collection configurations to register\n */\n private _registerCollections(collections: TCollections): void {\n this.logger?.debug('Registering collections', {\n count: Object.keys(collections).length,\n names: Object.keys(collections),\n });\n\n // Track collections with auto-loading fixtures\n const autoLoadCollections: any[] = [];\n\n for (const collectionName in collections) {\n const collectionConfig = collections[collectionName];\n const {\n model,\n factory,\n relationships,\n serializerConfig,\n serializerInstance,\n seeds,\n fixtures,\n } = collectionConfig;\n const identityManager = collectionConfig.identityManager ?? this.identityManager;\n\n // Determine the final serializer to use\n let finalSerializer: any;\n\n if (serializerInstance) {\n // 1. Collection-level instance has highest priority (no merging)\n finalSerializer = serializerInstance;\n } else {\n // 2. Merge global config with collection config\n const mergedConfig = this._mergeConfigs(model, serializerConfig);\n\n // Only create serializer if there's a config\n if (mergedConfig) {\n finalSerializer = new Serializer(model, mergedConfig);\n }\n }\n\n const collection = createCollection(this as SchemaInstance<TCollections, TConfig>, {\n model,\n factory,\n identityManager,\n relationships,\n serializer: finalSerializer,\n seeds,\n fixtures,\n });\n this._collections.set(collectionName, collection);\n\n Object.defineProperty(this, collectionName, {\n configurable: true,\n enumerable: true,\n get: () => this._collections.get(collectionName),\n });\n\n // Track if fixtures should be auto-loaded\n if (fixtures?.strategy === 'auto') {\n autoLoadCollections.push(collection);\n }\n }\n\n this.logger?.debug('Collections registered successfully', {\n count: this._collections.size,\n });\n\n // Validate inverse relationships after all collections are registered\n this._validateInverseRelationships(collections);\n\n // Auto-load fixtures for collections with 'auto' strategy\n // This is done synchronously after all collections are registered\n // to ensure relationships are set up properly\n if (autoLoadCollections.length > 0) {\n this.logger?.info('Auto-loading fixtures', {\n collections: autoLoadCollections.map((c) => c.collectionName),\n });\n for (const collection of autoLoadCollections) {\n // Load fixtures synchronously using a non-async approach\n // Since we're in a constructor context, we need to handle this carefully\n void collection.loadFixtures();\n }\n }\n }\n\n /**\n * Validate that all inverse relationships are correctly defined\n * This checks that explicit inverse relationships exist and point back correctly\n * @param collections - The schema collections to validate\n * @private\n */\n private _validateInverseRelationships(collections: TCollections): void {\n for (const collectionName in collections) {\n const collectionConfig = collections[collectionName];\n const relationships = collectionConfig.relationships;\n if (!relationships) continue;\n\n for (const relName in relationships) {\n const relationship = relationships[relName];\n\n // Skip if no explicit inverse is specified\n if (relationship.inverse === undefined) {\n continue;\n }\n\n // Skip if inverse is explicitly disabled\n if (relationship.inverse === null) {\n continue;\n }\n\n const inverseName = relationship.inverse as string;\n const targetCollectionName = relationship.targetModel.collectionName;\n const targetCollectionConfig = collections[targetCollectionName as keyof TCollections];\n\n if (!targetCollectionConfig) {\n throw new MirageError(\n `Invalid inverse relationship: '${collectionName}.${relName}' ` +\n `declares inverse '${inverseName}' but target collection '${targetCollectionName}' does not exist.`,\n );\n }\n\n const targetRelationships = targetCollectionConfig.relationships;\n if (!targetRelationships || !targetRelationships[inverseName]) {\n throw new MirageError(\n `Invalid inverse relationship: '${collectionName}.${relName}' ` +\n `declares inverse '${inverseName}' but '${targetCollectionName}.${inverseName}' does not exist.`,\n );\n }\n\n // Validate that inverse relationship points back to this collection\n const inverseRel = targetRelationships[inverseName];\n if (inverseRel.targetModel.collectionName !== collectionConfig.model.collectionName) {\n throw new MirageError(\n `Invalid inverse relationship: '${collectionName}.${relName}' ` +\n `declares inverse '${inverseName}', but '${targetCollectionName}.${inverseName}' ` +\n `points to '${inverseRel.targetModel.collectionName}', not '${collectionName}'.`,\n );\n }\n\n // Warn about asymmetric inverse relationships\n if (\n inverseRel.inverse !== undefined &&\n inverseRel.inverse !== null &&\n inverseRel.inverse !== relName\n ) {\n this.logger?.warn(\n `Asymmetric inverse relationship: '${collectionName}.${relName}' → '${inverseName}', ` +\n `but '${targetCollectionName}.${inverseName}' → '${inverseRel.inverse || 'auto'}'. ` +\n `Consider making inverses mutual for consistency.`,\n );\n }\n }\n }\n }\n\n /**\n * Merge global serializer config with collection-specific config\n * Collection config values override global config values\n * @param _template - The model template (used for type inference only)\n * @param collectionConfig - Collection-specific serializer config\n * @returns Merged serializer config or undefined if both are undefined\n */\n private _mergeConfigs<TTemplate extends ModelTemplate>(\n _template: TTemplate,\n collectionConfig?: SerializerOptions<TTemplate>,\n ): SerializerOptions<TTemplate> | undefined {\n const global = this._globalSerializerConfig;\n\n if (!global && !collectionConfig) {\n return undefined;\n }\n\n return {\n // Structural options: collection overrides global\n root: collectionConfig?.root ?? global?.root,\n embed: collectionConfig?.embed ?? global?.embed,\n // Model-specific options: only from collection level\n attrs: collectionConfig?.attrs,\n include: collectionConfig?.include,\n };\n }\n}\n\n/**\n * Type for a complete schema instance with collections\n * Provides both string-based property access and symbol-based relationship resolution\n */\nexport type SchemaInstance<\n TCollections extends SchemaCollections,\n TConfig extends SchemaConfig<any, any> = SchemaConfig<StringIdentityManager, undefined>,\n> = Schema<TCollections, TConfig> & SchemaCollectionAccessors<TCollections>;\n","import type { IdentityManager, StringIdentityManager } from '@src/id-manager';\nimport type { StructuralSerializerOptions } from '@src/serializer';\nimport { MirageError, type LoggerConfig } from '@src/utils';\n\nimport Schema, { type SchemaInstance } from './Schema';\nimport type { SchemaCollections, SchemaConfig } from './types';\n\n/**\n * A fluent builder for creating schema instances.\n *\n * The SchemaBuilder provides a type-safe way to construct Schema instances with\n * configurable collections, identity manager, and global serializer. It follows the builder\n * pattern, allowing method chaining to progressively configure the schema.\n * @template TCollections - The schema collections configuration type\n * @template TIdentityManager - The global identity manager type\n * @template TGlobalConfig - The global serializer configuration type\n * @example\n * ```typescript\n * const appSchema = schema()\n * .collections({\n * users: userCollection,\n * posts: postCollection,\n * })\n * .serializer({ root: true })\n * .identityManager(appIdentityManager)\n * .setup();\n * ```\n */\nexport default class SchemaBuilder<\n TCollections extends SchemaCollections = SchemaCollections,\n TIdentityManager extends IdentityManager<any> = StringIdentityManager,\n TGlobalConfig extends StructuralSerializerOptions | undefined = undefined,\n> {\n private _collections?: TCollections;\n private _identityManager?: TIdentityManager;\n private _globalSerializerConfig?: StructuralSerializerOptions;\n private _loggingConfig?: LoggerConfig;\n\n /**\n * Creates a new SchemaBuilder instance.\n * @private\n */\n constructor() {}\n\n /**\n * Sets the collections configuration for this schema.\n *\n * Collections define the models, factories, relationships, and other configuration\n * for each collection in the schema. Each collection is keyed by its collection name.\n * @template C - The collections configuration type\n * @param collections - The collections configuration object\n * @returns A new SchemaBuilder instance with the specified collections\n * @example\n * ```typescript\n * const builder = schema().collections({\n * users: userCollection,\n * posts: postCollection,\n * comments: commentCollection,\n * });\n * ```\n */\n collections<C extends SchemaCollections>(\n collections: C,\n ): SchemaBuilder<C, TIdentityManager, TGlobalConfig> {\n // Validate collections is not empty\n if (Object.keys(collections).length === 0) {\n throw new MirageError(\n 'Schema must have at least one collection. Provide collection configurations in the collections() method.',\n );\n }\n\n // Validate collection names don't conflict with reserved Schema properties\n const RESERVED_SCHEMA_PROPS = [\n 'db',\n 'identityManager',\n 'getCollection',\n 'loadSeeds',\n 'loadFixtures',\n ];\n\n for (const name of Object.keys(collections)) {\n if (RESERVED_SCHEMA_PROPS.includes(name)) {\n throw new MirageError(\n `Collection name '${name}' conflicts with existing Schema property or method.\\n\\n` +\n `The Schema instance has the following built-in properties and methods:\\n` +\n ` - schema.db: Database instance\\n` +\n ` - schema.identityManager: ID generation manager\\n` +\n ` - schema.getCollection(): Method to access collections\\n` +\n ` - schema.loadSeeds(): Method to load seed data\\n` +\n ` - schema.loadFixtures(): Method to load fixture data\\n\\n` +\n `Please use a different collection name. Reserved names: ${RESERVED_SCHEMA_PROPS.join(', ')}`,\n );\n }\n\n if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {\n throw new MirageError(\n `Collection name '${name}' is not a valid JavaScript identifier. Use only letters, numbers, underscores, and dollar signs.`,\n );\n }\n }\n\n const builder = new SchemaBuilder<C, TIdentityManager, TGlobalConfig>();\n builder._collections = collections;\n builder._identityManager = this._identityManager;\n builder._globalSerializerConfig = this._globalSerializerConfig;\n builder._loggingConfig = this._loggingConfig;\n return builder;\n }\n\n /**\n * Sets the global identity manager for this schema.\n *\n * The identity manager handles ID generation and management for model instances\n * across all collections in the schema. Individual collections can override this\n * with their own identity managers.\n * @template I - The identity manager type\n * @param identityManager - The identity manager instance\n * @returns A new SchemaBuilder instance with the specified identity manager\n * @example\n * ```typescript\n * const builder = schema()\n * .collections({ users: userCollection })\n * .identityManager(new StringIdentityManager());\n * ```\n */\n identityManager<I extends IdentityManager<any>>(\n identityManager: I,\n ): SchemaBuilder<TCollections, I, TGlobalConfig> {\n const builder = new SchemaBuilder<TCollections, I, TGlobalConfig>();\n builder._collections = this._collections;\n builder._identityManager = identityManager;\n builder._globalSerializerConfig = this._globalSerializerConfig;\n builder._loggingConfig = this._loggingConfig;\n return builder;\n }\n\n /**\n * Sets the global serializer configuration for this schema.\n *\n * The global serializer config defines structural serialization options (root, embed)\n * that apply to all collections by default. Individual collections can override\n * these settings or provide model-specific configuration (attrs, include).\n * @template TConfig - The global serializer configuration type\n * @param config - The global serializer configuration (only root and embed)\n * @returns A new SchemaBuilder instance with the specified global serializer config\n * @example\n * ```typescript\n * const builder = schema()\n * .serializer({ root: true, embed: false })\n * .collections({ users: userCollection });\n * ```\n */\n serializer<TConfig extends StructuralSerializerOptions>(\n config: TConfig,\n ): SchemaBuilder<TCollections, TIdentityManager, TConfig> {\n const builder = new SchemaBuilder<TCollections, TIdentityManager, TConfig>();\n builder._collections = this._collections;\n builder._identityManager = this._identityManager;\n builder._globalSerializerConfig = config;\n builder._loggingConfig = this._loggingConfig;\n return builder;\n }\n\n /**\n * Sets the logging configuration for this schema.\n *\n * The logging config enables debug output for database operations, validations,\n * and other schema behavior. This is useful for debugging test setup and understanding\n * how the ORM is behaving.\n * @param config - The logging configuration (enabled, level, prefix)\n * @returns A new SchemaBuilder instance with the specified logging config\n * @example\n * ```typescript\n * const builder = schema()\n * .logging({ enabled: true, level: 'debug' })\n * .collections({ users: userCollection });\n * ```\n */\n logging(config: LoggerConfig): SchemaBuilder<TCollections, TIdentityManager, TGlobalConfig> {\n const builder = new SchemaBuilder<TCollections, TIdentityManager, TGlobalConfig>();\n builder._collections = this._collections;\n builder._identityManager = this._identityManager;\n builder._globalSerializerConfig = this._globalSerializerConfig;\n builder._loggingConfig = config;\n return builder;\n }\n\n /**\n * Sets up the final Schema instance with all configured options.\n *\n * This method produces the complete schema instance that can be used throughout\n * your application. Collections must be set before calling setup().\n * @returns The configured Schema instance with collection accessors\n * @throws Error if no collections have been configured\n * @example\n * ```typescript\n * const appSchema = schema()\n * .collections({\n * users: userCollection,\n * posts: postCollection,\n * })\n * .serializer({ root: true })\n * .identityManager(appIdentityManager)\n * .setup();\n *\n * // Use the schema\n * const userCollection = appSchema.getCollection('users');\n * const user = appSchema.users.create({ name: 'John' });\n * ```\n */\n setup(): SchemaInstance<TCollections, SchemaConfig<TIdentityManager, TGlobalConfig>> {\n if (!this._collections) {\n throw new MirageError(\n 'SchemaBuilder: collections are required. Call .collections() before .setup()',\n );\n }\n\n const config = {\n identityManager: this._identityManager,\n globalSerializerConfig: this._globalSerializerConfig,\n logging: this._loggingConfig,\n } as SchemaConfig<TIdentityManager, TGlobalConfig>;\n\n return new Schema(this._collections, config) as SchemaInstance<\n TCollections,\n SchemaConfig<TIdentityManager, TGlobalConfig>\n >;\n }\n}\n\n/**\n * Creates a new SchemaBuilder instance for building schema configurations.\n *\n * This is the main entry point for creating schemas in the builder-based API.\n * The returned SchemaBuilder can be configured with collections and identity manager\n * before setting up the final schema instance.\n * @returns A new SchemaBuilder instance ready for configuration\n * @example\n * ```typescript\n * // Basic schema creation\n * const appSchema = schema()\n * .collections({\n * users: userCollection,\n * })\n * .setup();\n *\n * // Full schema configuration\n * const appSchema = schema()\n * .collections({\n * users: userCollection,\n * posts: postCollection,\n * })\n * .identityManager(new StringIdentityManager())\n * .setup();\n * ```\n * @see {@link SchemaBuilder} for available configuration methods\n */\nexport function schema(): SchemaBuilder {\n return new SchemaBuilder();\n}\n"],"mappings":"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","names":["MirageError","message","Logger","config","message","context","level","LOG_LEVELS","prefix","levelLabel","logMessage","IdentityManager","options","nextId","id","MirageError","current","StringIdentityManager","NumberIdentityManager","QueryManager","records","options","results","cursor","limit","offset","orderBy","where","helpers","record","predicate","key","value","AND","OR","NOT","fields","fieldValue","condition","w","ops","isNull","min","max","needle","item","a","b","op","aTime","bTime","pattern","caseInsensitive","regexPattern","pairs","result","direction","field","aVal","bVal","diff","recordVal","cursorVal","conditions","c","compareWith","prefix","suffix","substring","values","v","DbCollection","QueryManager","name","config","StringIdentityManager","index","records","id","input","predicate","record","query","where","data","patch","existingRecord","updatedRecord","updated","recordsToDelete","DB","config","initialData","name","collectionName","MirageError","collection","DbCollection","data","key","records","createDatabase","BaseModel","modelName","collectionName","attrs","dbCollection","serializer","DbCollection","record","idLabel","ModelCollection","_ModelCollection","template","models","serializer","index","cb","model","mappedModels","filteredModels","others","allModels","other","compareFn","sortedModels","reversedModels","attrs","isModelInstance","value","isModelInstanceArray","item","isModelCollection","isArray","RelationshipsManager","model","schema","relationships","relationshipUpdates","relationshipName","relationshipDef","relationship","foreignKey","type","targetModel","rawValue","processedValue","isArray","v","targetCollection","id","models","m","isModelInstance","ModelCollection","currentForeignKey","newForeignKey","hasChanged","oldValue","operation","isModelCollection","item","foreignKeyUpdates","relationshipNameString","currentForeignKeyValue","newTargetId","targetCollectionName","oldTarget","newTargetIds","newTargetModels","currentForeignKeyValues","currentIds","idsChanged","index","currentTarget","modelsToUnlink","idsToRemove","newIds","currentTargets","target","MirageError","foreignKeyValue","singleId","foreignKeyValues","idsArray","relatedModels","relationshipDefs","inverseOption","inverseRelationship","inverseRelationshipName","action","modelId","inverse","inverseRelName","targetRelManager","targetModelId","targetDbRecord","updateAttrs","currentValue","value","fk","currentFk","newFk","arr1","arr2","val","Model","_Model","BaseModel","template","config","attrs","relationships","schema","serializer","dbCollection","modelAttrs","relationshipUpdates","RelationshipsManager","relationshipName","targetModel","result","relationship","value","type","isModelInstance","isModelInstanceArray","model","isModelCollection","regularAttrs","relationshipValues","foreignKeys","foreignKey","key","foreignKeyValue","isForeignKey","attrKey","name","relationshipDef","ModelBuilder","_ModelBuilder","modelName","MirageError","builder","collectionName","model","AssociationsManager","schema","associations","skipKeys","relationshipValues","keysToSkip","relationshipName","association","collectionName","collection","MirageError","traitsAndDefaults","count","query","model","matches","all","template","models","shuffledModels","needed","newModels","allModels","ModelCollection","selectedModels","array","shuffled","j","Factory","template","attributes","traits","associations","afterCreate","AssociationsManager","modelId","traitsAndDefaults","traitNames","defaults","arg","processedAttributes","traitAttributes","schema","skipKeys","mergedAssociations","model","hooks","name","trait","hook","attrs","acc","key","value","_","extension","baseAttributes","overrideAttributes","visited","processing","detectCycle","MirageError","proxy","target","prop","FactoryBuilder","_FactoryBuilder","template","builder","attributes","traits","hook","associations","originalFactory","MirageError","Factory","factory","belongsTo","targetModel","opts","defaultForeignKey","create","model","traitsAndDefaults","createMany","model","count","traitsAndDefaults","hasMany","targetModel","opts","defaultForeignKey","link","model","query","traitsAndDefaults","linkMany","model","count","query","traitsAndDefaults","associations","belongsTo","hasMany","create","createMany","link","linkMany","associations_default","BaseCollection","schema","config","factory","identityManager","model","relationships","serializer","seeds","fixtures","Model","index","record","records","models","ModelCollection","input","queryOptions","id","count","options","modelWhereCallback","helpers","Collection","BaseCollection","attrs","traitsAndDefaults","traits","defaults","arg","modelAttrs","relationshipUpdates","Model","nextId","factoryAttrs","userProvidedRelationshipKeys","associationValues","completeAttrs","model","count","models","ModelCollection","query","existingModel","existingModels","needed","queryAttrs","newModels","scenarioId","seedScenarios","name","seedFn","availableScenarios","MirageError","fixtureIds","r","existingIds","conflicts","id","createCollection","schema","config","Serializer","template","options","model","attrs","embedded","foreignKeys","filteredAttrs","fk","data","sideLoaded","collection","allSideLoaded","key","value","item","existing","acc","attr","relName","relatedData","isCollection","relModel","CollectionBuilder","_CollectionBuilder","template","MirageError","builder","factory","relationships","SUPPORTED_RELATIONSHIP_TYPES","key","relationship","configOrSerializer","Serializer","identityManager","seeds","records","options","record","index","collection","Schema","collections","config","createDatabase","StringIdentityManager","Logger","collectionName","collection","MirageError","autoLoadCollections","collectionConfig","model","factory","relationships","serializerConfig","serializerInstance","seeds","fixtures","identityManager","finalSerializer","mergedConfig","Serializer","createCollection","c","relName","relationship","inverseName","targetCollectionName","targetCollectionConfig","targetRelationships","inverseRel","_template","global","SchemaBuilder","_SchemaBuilder","collections","MirageError","RESERVED_SCHEMA_PROPS","name","builder","identityManager","config","Schema","schema"]}
|