@happyvertical/smrt-core 0.34.0 → 0.34.1
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/dist/collection.d.ts +19 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +26 -0
- package/dist/collection.js.map +1 -1
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +10 -2
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +2 -2
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
- package/dist/manifest/test-manifest-stub.js +81 -2
- package/dist/manifest/test-manifest-stub.js.map +1 -1
- package/dist/manifest.json +10 -2
- package/dist/smrt-knowledge.json +5 -4
- package/package.json +4 -4
package/dist/collection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.js","sources":["../src/collection.ts"],"sourcesContent":["import { createLogger } from '@happyvertical/logger';\nimport { buildWhere } from '@happyvertical/sql';\nimport type { SmrtClassOptions } from './class';\nimport { SmrtClass } from './class';\nimport {\n buildQueryCacheKey,\n type CollectionCacheConfig,\n ensureCacheInvalidationListener,\n getCachedRows,\n getCacheGeneration,\n invalidateCollectionCache,\n registerCrossProcessCacheInterest,\n resolveDbCacheKey,\n setCachedRows,\n} from './collection-cache';\nimport { EmbeddingProvider } from './embeddings/provider';\nimport { EmbeddingStorage } from './embeddings/storage';\nimport {\n createInterceptorContext,\n GlobalInterceptors,\n type ListOptions as InterceptorListOptions,\n} from './interceptors';\nimport type { SmrtObject } from './object';\nimport { ObjectRegistry } from './registry';\nimport { verifyPersistenceTable } from './schema/table-verifier';\nimport {\n classnameToTablename,\n fieldsFromClass,\n formatDataJs,\n toCamelCase,\n toSnakeCase,\n} from './utils';\nimport { chunkArray, IN_LIST_CHUNK_SIZE } from './utils/chunk';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Resolve _meta_type in WHERE clause from simple class name to qualified name (Issue #713)\n *\n * This helper function allows queries like { _meta_type: 'Image' } to work correctly\n * when the database stores qualified names like '@happyvertical/smrt-assets:Image'.\n *\n * @param where - The original WHERE clause object\n * @returns Modified WHERE clause with qualified _meta_type, or original if no resolution needed\n */\nfunction resolveMetaTypeInWhere<T extends Record<string, unknown>>(\n where: T | undefined,\n): T | undefined {\n if (!where?._meta_type || typeof where._meta_type !== 'string') {\n return where;\n }\n\n const metaTypeValue = where._meta_type as string;\n\n // Only resolve if it's a simple class name (no ':' means not qualified)\n if (metaTypeValue.includes(':')) {\n return where;\n }\n\n const registeredClass = ObjectRegistry.getClass(metaTypeValue);\n if (registeredClass?.qualifiedName) {\n return {\n ...where,\n _meta_type: registeredClass.qualifiedName,\n };\n }\n\n return where;\n}\n\n/**\n * Keys from SmrtObject and SmrtClass that should not appear as user-facing\n * create/update input fields. These are framework-internal properties.\n */\ntype SmrtInternalKeys =\n | keyof SmrtClass\n | '_tableName'\n | '_loadedRelationships'\n | '_id'\n | '_slug'\n | '_context'\n | '_ai'\n | '_fs'\n | '_db'\n | '_className'\n | 'options'\n | '_skipLoad'\n | '_skipAutoEmbeddings'\n | '_extractingFields'\n | 'initialize'\n | 'save'\n | 'delete'\n | 'loadFromId'\n | 'loadFromSlug'\n | 'is'\n | 'do'\n | 'toJSON'\n | 'transformJSON';\n\n/**\n * Input type for `collection.create()`. Constrains input to valid model fields\n * while preserving backwards compatibility via an index signature escape hatch.\n *\n * Provides IDE autocompletion for known fields from the model class while still\n * accepting arbitrary keys for dynamic usage patterns (e.g., STI meta fields).\n *\n * @example\n * ```typescript\n * // IDE will suggest: name, price, quantity, categoryId, etc.\n * await productCollection.create({\n * name: 'Widget',\n * price: 9.99,\n * _meta_type: 'SpecialProduct', // STI discriminator\n * });\n * ```\n */\nexport type SmrtCreateInput<T extends SmrtObject> = Partial<\n Omit<\n {\n [K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K];\n },\n SmrtInternalKeys\n >\n> & {\n /** STI discriminator for polymorphic creation */\n _meta_type?: string;\n /** Skip database loading (framework internal) */\n _skipLoad?: boolean;\n /** Skip save-time embedding auto-generation (framework internal) */\n _skipAutoEmbeddings?: boolean;\n /** Allow arbitrary additional fields for dynamic usage */\n [key: string]: unknown;\n};\n\n/**\n * WHERE clause type for `collection.list()`.\n *\n * Uses `Record<string, unknown>` because WHERE keys often include operator\n * suffixes (e.g., `'price >'`, `'name like'`, `'status in'`) which can't be\n * expressed as mapped types from model properties. Runtime validation in\n * `convertWhereKeys()` handles field and operator checking.\n */\nexport type SmrtWhereClause<T extends SmrtObject> = Record<string, unknown>;\n\n/**\n * Configuration options for SmrtCollection\n */\nexport interface SmrtCollectionOptions extends SmrtClassOptions {}\n\n/**\n * Typed CRUD collection for a specific `SmrtObject` subclass.\n *\n * Each concrete collection pairs with exactly one model class via the required\n * `static readonly _itemClass` property. Use the static `create()` factory —\n * not `new` — to get a fully initialized, ready-to-query instance.\n *\n * Key capabilities:\n * - **Query**: `list()`, `get()`, `count()`, `query()` (raw SQL), `listByIds()`\n * - **Mutation**: `create()`, `getOrUpsert()`, `delete()`\n * - **Eager loading**: pass `include: ['fieldName']` to `list()` to avoid N+1 queries\n * - **STI support**: automatically filters by `_meta_type` for child collections;\n * polymorphically hydrates the correct subclass when listing/getting\n * - **Interceptors**: all read/write paths run `GlobalInterceptors` hooks\n * (used by multi-tenancy, audit logging, etc.)\n *\n * @typeParam ModelType - The `SmrtObject` subclass managed by this collection\n *\n * @example\n * ```typescript\n * @smrt()\n * class Product extends SmrtObject {\n * name: string = '';\n * price: number = 0.0;\n * }\n *\n * @smrt()\n * class Products extends SmrtCollection<Product> {\n * static readonly _itemClass = Product;\n * }\n *\n * const products = await Products.create({ db: myDb });\n * const widget = await products.create({ name: 'Widget', price: 9.99 });\n * const all = await products.list({ where: { 'price >': 5 }, orderBy: 'price ASC' });\n * ```\n */\nexport class SmrtCollection<ModelType extends SmrtObject> extends SmrtClass {\n /**\n * Cached fields for sync access during queries.\n * Populated during create() to avoid async getFields() calls on every query.\n * @private\n */\n private _cachedFields: Record<string, any> | null = null;\n\n private getRegisteredItemClass() {\n return (\n ObjectRegistry.getClassByConstructor(this._itemClass as any) ||\n ObjectRegistry.getClass(this._itemClass.name)\n );\n }\n\n private getResolvedItemClassName(): string {\n return this.getRegisteredItemClass()?.name || this._itemClass.name;\n }\n\n private getResolvedItemQualifiedName(): string {\n const registered = this.getRegisteredItemClass();\n return (\n registered?.qualifiedName || registered?.name || this._itemClass.name\n );\n }\n\n /**\n * Convert WHERE clause field names from camelCase to snake_case while preserving operators.\n * Validates operators and field names to prevent SQL injection and invalid queries.\n *\n * Uses cached fields for sync access (issue #663) to avoid async overhead on every query.\n *\n * @param where - WHERE clause object with camelCase field names\n * @returns WHERE clause object with snake_case field names\n * @private\n *\n * @example\n * ```typescript\n * // Input: { 'typeId': 'foo', 'categoryId >': 100 }\n * // Output: { 'type_id': 'foo', 'category_id >': 100 }\n * ```\n */\n private convertWhereKeys(where: Record<string, any>): Record<string, any> {\n // Whitelist of allowed SQL operators\n const VALID_OPERATORS = [\n '=',\n '>',\n '<',\n '>=',\n '<=',\n '!=',\n 'in',\n 'not in',\n 'like',\n 'contains',\n ];\n\n // Get schema fields for validation (sync from cache)\n const fields = this.getFieldsSync();\n const validFieldNames = new Set(\n Object.keys(fields).map((f) => toSnakeCase(f)),\n );\n\n // Security (#1540): collect snake_case names of `@field({ sensitive: true })`\n // columns so they can't be used as `where` filter keys. Filtering on a\n // secret column turns the generated REST/MCP `where` surface into a value\n // oracle (e.g. `apiSecret[like]=sk-%`), exfiltrating the secret one\n // character at a time even though it's excluded from serialization.\n const sensitiveFieldNames = new Set<string>();\n const collectSensitive = (\n fieldMap: Record<string, any> | Map<string, any>,\n ) => {\n const entries =\n fieldMap instanceof Map ? fieldMap.entries() : Object.entries(fieldMap);\n for (const [fieldName, def] of entries) {\n if (def && (def.sensitive === true || def._meta?.sensitive === true)) {\n // Store both the snake_case column form and the raw property name so\n // STI `@meta` fields probed via `_meta_data.<prop>` JSON paths (which\n // keep the camelCase key) are also rejected.\n sensitiveFieldNames.add(toSnakeCase(fieldName));\n sensitiveFieldNames.add(fieldName);\n }\n }\n };\n collectSensitive(fields);\n\n // Add standard SMRT fields that are always valid\n validFieldNames.add('id');\n validFieldNames.add('slug');\n validFieldNames.add('context');\n validFieldNames.add('created_at');\n validFieldNames.add('updated_at');\n\n // Add STI discriminator field for polymorphic queries.\n // R5-canon: use the qualified item name as the lookup key so a\n // same-simple-name class in another package can't yield the wrong\n // tableStrategy.\n const itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n if (tableStrategy === 'sti') {\n // Add both with and without leading underscore (toSnakeCase strips leading _)\n validFieldNames.add('_meta_type');\n validFieldNames.add('meta_type');\n validFieldNames.add('_meta_data');\n validFieldNames.add('meta_data');\n\n // Issue #869: For STI classes, also include fields from all ancestor classes\n // This ensures child class collections can filter by parent class fields.\n //\n // inheritanceChain entries are qualified names (when the\n // registration has one). Compare self against the qualified form;\n // the framework-base sentinels stay as simple names since they're\n // never registered. The simple-name `itemClassName` is also checked\n // as a defensive fall-through for unqualified registrations.\n const inheritanceChain =\n ObjectRegistry.getInheritanceChain(itemQualifiedName);\n for (const ancestorName of inheritanceChain) {\n if (\n ancestorName === 'SmrtObject' ||\n ancestorName === 'SmrtClass' ||\n ancestorName === itemQualifiedName ||\n ancestorName === itemClassName\n ) {\n continue; // Skip framework base classes and self (already included)\n }\n const ancestorFields = ObjectRegistry.getFields(ancestorName);\n for (const fieldName of ancestorFields.keys()) {\n validFieldNames.add(toSnakeCase(fieldName));\n }\n collectSensitive(ancestorFields);\n }\n\n // Security (#1540): a base collection serializes (and so must protect)\n // STI descendant fields too — a child's `@meta` sensitive field lives in\n // `_meta_data` and would otherwise be probe-able via `_meta_data.<prop>`\n // from the base collection. Mirror toJSON()/getSensitiveFieldNames().\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase) {\n for (const descendant of ObjectRegistry.getDescendants(stiBase)) {\n collectSensitive(ObjectRegistry.getFields(descendant));\n }\n }\n }\n\n // Issue #869: If no fields are registered (no manifest for test classes),\n // skip field validation. Invalid fields will fail at SQL level instead.\n // This commonly happens for inline test classes decorated with @smrt().\n const hasRegisteredFields = Object.keys(fields).length > 0;\n const skipFieldValidation = !hasRegisteredFields;\n\n const converted: Record<string, any> = {};\n\n // Check for prototype pollution attempts using own properties only\n // __proto__ is a special property that doesn't show up in Object.entries()\n // but can still be checked with hasOwnProperty\n if (\n Object.hasOwn(where, '__proto__') ||\n Object.hasOwn(where, 'constructor') ||\n Object.hasOwn(where, 'prototype')\n ) {\n throw new Error(\n `Invalid WHERE clause: Prototype pollution attempts are not allowed. ` +\n `Detected dangerous properties in WHERE clause.`,\n );\n }\n\n for (const [key, value] of Object.entries(where)) {\n // Split field name and operator (e.g., \"typeId >\" → [\"typeId\", \">\"])\n const parts = key.trim().split(/\\s+/);\n const fieldName = parts[0];\n const operator = parts.slice(1).join(' ') || '=';\n\n // Check for dangerous prototype pollution field names\n if (\n fieldName === '__proto__' ||\n fieldName === 'constructor' ||\n fieldName === 'prototype' ||\n fieldName.includes('__proto__') ||\n fieldName.includes('constructor') ||\n fieldName.includes('prototype')\n ) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Prototype pollution attempts are not allowed.`,\n );\n }\n\n // Support dot-notation for JSON path queries (e.g., 'metadata.userId')\n // Validate against the first segment (the column name), pass full path through\n const dotIndex = fieldName.indexOf('.');\n const baseFieldName =\n dotIndex >= 0 ? fieldName.substring(0, dotIndex) : fieldName;\n const jsonPath = dotIndex >= 0 ? fieldName.substring(dotIndex) : null;\n\n // Convert base field name to snake_case, preserving leading underscores\n const snakeBaseFieldName = baseFieldName.startsWith('_')\n ? `_${toSnakeCase(baseFieldName.slice(1))}`\n : toSnakeCase(baseFieldName);\n\n // Full snake_case field name with JSON path preserved\n const snakeFieldName = jsonPath\n ? `${snakeBaseFieldName}${jsonPath}`\n : snakeBaseFieldName;\n\n // Security (#1379): the field identifier — base column plus any\n // dot-notation JSON-path segments — is interpolated UNPARAMETERIZED into\n // the SQL field position by the downstream query builder. The manifest\n // whitelist below only validates the base column, and is skipped entirely\n // when a class has no registered fields (skipFieldValidation), so without\n // this guard a crafted key such as\n // `metadata.x))/**/UNION/**/SELECT/**/secret/**/FROM/**/users--`\n // (SQL comments substituting for blocked whitespace) injects arbitrary SQL\n // via the JSON-path suffix — remotely reachable through the generated\n // REST/MCP `where` surfaces and able to defeat the tenant filter. Enforce\n // that the whole identifier is a strict dot-separated identifier so no\n // parens/quotes/operators/whitespace can survive, regardless of whether\n // the manifest whitelist below runs.\n if (!/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z0-9_]+)*$/.test(snakeFieldName)) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Field names must be identifiers (letters, digits, underscore) ` +\n `with optional dot-separated JSON-path segments.`,\n );\n }\n\n // Security (#1540): reject filters that target a sensitive column. This\n // runs before operator/field validation so the secret value-probing\n // oracle is closed even for otherwise-valid operators. We reject when the\n // base column itself is sensitive, OR when an STI `@meta` sensitive field\n // is probed via a `_meta_data.<prop>` JSON path (the prop keeps its\n // camelCase name). The JSON-path check is scoped to the `_meta_data`\n // column so legitimate filters on unrelated JSON fields (e.g.\n // `metadata.apiSecret` on a non-sensitive `metadata` column) aren't\n // falsely rejected.\n // Use the NORMALIZED column name so camelCase aliases (`_metaData`,\n // `metaData`) that also resolve to the `_meta_data` column are scoped in —\n // checking raw `baseFieldName` here let `_metaData.apiSecret` slip past\n // the segment check while still resolving to the meta column.\n const baseIsMetaData =\n snakeBaseFieldName === '_meta_data' ||\n snakeBaseFieldName === 'meta_data';\n const metaPathTargetsSensitive =\n baseIsMetaData &&\n !!jsonPath &&\n jsonPath\n .split('.')\n .filter(Boolean)\n .some(\n (segment) =>\n sensitiveFieldNames.has(segment) ||\n sensitiveFieldNames.has(toSnakeCase(segment)),\n );\n if (\n sensitiveFieldNames.has(snakeBaseFieldName) ||\n metaPathTargetsSensitive\n ) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Filtering on sensitive fields is not allowed.`,\n );\n }\n\n // Auto-detect IN operator when value is an array without explicit operator\n const effectiveOperator =\n operator === '=' && Array.isArray(value) ? 'in' : operator;\n\n // Validate operator\n if (!VALID_OPERATORS.includes(effectiveOperator)) {\n throw new Error(\n `Invalid WHERE clause operator: '${operator}'. ` +\n `Valid operators: ${VALID_OPERATORS.join(', ')}`,\n );\n }\n\n // Validate field name exists in schema (validate base column name only)\n // Issue #869: Skip validation when no fields are registered (manifest-less test classes)\n if (!skipFieldValidation && !validFieldNames.has(snakeBaseFieldName)) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Field does not exist on ${itemClassName}. ` +\n `Valid fields: ${Array.from(validFieldNames).sort().join(', ')}`,\n );\n }\n\n // Validate operator-specific value types\n if (\n (effectiveOperator === 'in' || effectiveOperator === 'not in') &&\n !Array.isArray(value)\n ) {\n throw new Error(\n `WHERE clause operator '${effectiveOperator}' requires an array value for field '${fieldName}', ` +\n `got ${typeof value}`,\n );\n }\n\n // Reject empty arrays for IN/NOT IN operators (generates invalid SQL)\n if (\n (effectiveOperator === 'in' || effectiveOperator === 'not in') &&\n Array.isArray(value) &&\n value.length === 0\n ) {\n throw new Error(\n `WHERE clause operator '${effectiveOperator}' requires a non-empty array for field '${fieldName}'. ` +\n `Use listByIds([]) for graceful empty array handling.`,\n );\n }\n\n if (effectiveOperator === 'like' && typeof value !== 'string') {\n throw new Error(\n `WHERE clause operator 'like' requires a string value for field '${fieldName}', ` +\n `got ${typeof value}`,\n );\n }\n\n // Reconstruct key with operator\n const newKey =\n effectiveOperator === '='\n ? snakeFieldName\n : `${snakeFieldName} ${effectiveOperator}`;\n\n converted[newKey] = value;\n }\n\n return converted;\n }\n\n /**\n * Gets the class constructor for items in this collection\n */\n protected get _itemClass(): (new (\n options: any,\n ) => ModelType) & {\n create(options: any): ModelType | Promise<ModelType>;\n } {\n const ctor = this.constructor as {\n readonly _itemClass?: (new (\n options: any,\n ) => ModelType) & {\n create(options: any): ModelType | Promise<ModelType>;\n };\n };\n if (!ctor._itemClass) {\n const className = this.constructor.name;\n const errorMessage = [\n `Collection \"${className}\" must define a static _itemClass property.`,\n '',\n 'Example:',\n ` class ${className} extends SmrtCollection<YourItemClass> {`,\n ' static readonly _itemClass = YourItemClass;',\n ' }',\n '',\n 'Make sure your item class is imported and defined before the collection class.',\n ].join('\\n');\n\n throw new Error(errorMessage);\n }\n return ctor._itemClass;\n }\n\n /**\n * Static reference to the item class constructor\n */\n static readonly _itemClass: any;\n\n /**\n * Validates that the collection is properly configured\n * Call this during development to catch configuration issues early\n */\n static validate(): void {\n if (!SmrtCollection._itemClass) {\n const className = SmrtCollection.name;\n const errorMessage = [\n `Collection \"${className}\" is missing required static _itemClass property.`,\n '',\n 'Fix by adding:',\n ` class ${className} extends SmrtCollection<YourItemClass> {`,\n ' static readonly _itemClass = YourItemClass;',\n ' }',\n ].join('\\n');\n throw new Error(errorMessage);\n }\n\n // Validate that _itemClass has required methods\n if (typeof SmrtCollection._itemClass !== 'function') {\n throw new Error(\n `Collection \"${SmrtCollection.name}\"._itemClass must be a constructor function`,\n );\n }\n\n // Check if it has a create method (static or prototype)\n const hasCreateMethod =\n typeof SmrtCollection._itemClass.create === 'function' ||\n typeof SmrtCollection._itemClass.prototype?.create === 'function';\n\n if (!hasCreateMethod) {\n logger.warn(\n `Collection \"${SmrtCollection.name}\"._itemClass should have a create() method for optimal functionality`,\n );\n }\n }\n\n /**\n * Database table name for this collection\n */\n public _tableName!: string;\n\n /**\n * Creates a new SmrtCollection instance\n *\n * @deprecated Use the static create() factory method instead\n * @param options - Configuration options\n */\n constructor(options: SmrtCollectionOptions = {}) {\n super(options);\n\n // Auto-register the collection if it's not the base SmrtCollection and has an _itemClass\n if (\n this.constructor !== SmrtCollection &&\n (this.constructor as any)._itemClass\n ) {\n const itemClass = (this.constructor as any)._itemClass;\n const itemClassName =\n ObjectRegistry.getClassByConstructor(itemClass)?.name || itemClass.name;\n ObjectRegistry.registerCollection(itemClassName, this.constructor as any);\n }\n }\n\n /**\n * Factory method — the recommended way to instantiate a collection.\n *\n * Creates the collection instance, calls `initialize()`, and pre-populates\n * the field cache used by synchronous query helpers.\n *\n * Pass the same `options` object you received in a `SmrtObject` constructor\n * or a `SvelteKit` load function to share the database connection.\n *\n * @param options - Database, AI, filesystem, and other configuration options.\n * At minimum, provide `db` (or `persistence`) to connect to a database.\n * @returns A fully initialized, ready-to-query collection instance\n *\n * @example\n * ```typescript\n * // Share a DB connection from a SvelteKit load function\n * const products = await Products.create(event.locals.smrtOptions);\n *\n * // Explicit configuration\n * const products = await Products.create({\n * db: myDb,\n * ai: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY },\n * });\n * ```\n */\n static async create<T extends SmrtCollection<any>>(\n this: new (\n options?: SmrtCollectionOptions,\n ) => T,\n options: SmrtClassOptions = {},\n ): Promise<T> {\n // Extract only collection-compatible options from broader SmrtClassOptions\n const {\n _className,\n db,\n persistence, // Also extract persistence alias\n ai,\n fs,\n logging,\n metrics,\n pubsub,\n sanitization,\n signals,\n } = options;\n\n const collectionOptions: SmrtCollectionOptions = {\n _className,\n db,\n persistence, // Pass persistence through so initialize() can map it to db\n ai,\n fs,\n logging,\n metrics,\n pubsub,\n sanitization,\n signals,\n };\n\n // Defense-in-depth: SmrtJunction subclasses MUST have their ITEM class\n // registered with ObjectRegistry — that's where the field metadata,\n // tableName, and conflictColumns come from, which byLeft/byRight/\n // attach/detach/setLinks all depend on. The scanner is supposed to\n // catch every junction subclass via the FRAMEWORK_BASE_CLASSES list\n // in packages/scanner, but if a future refactor adds a new abstract\n // junction base without updating that list, or if a class is loaded\n // outside the normal manifest pipeline, we'd silently fall through to\n // empty-field-metadata behavior (the issue #1132 class of bug). Fail\n // loudly here instead of producing wrong results later.\n //\n // We check the ITEM class registration (not the collection class) —\n // collection constructors are stored separately via\n // registerCollection(itemClassName, ctor), not in the classes map.\n if ((this as any)._isJunctionBase === true) {\n const itemCtor = (this as any)._itemClass;\n const itemRegistered =\n itemCtor && ObjectRegistry.getClassByConstructor(itemCtor);\n if (!itemRegistered) {\n throw new Error(\n `SmrtJunction subclass \"${this.name}\" has no registered item class. ` +\n `The scanner likely didn't pick up its model — usually because the ` +\n `consuming package's manifest doesn't include \"${itemCtor?.name ?? '<unknown>'}\". ` +\n `Check that the scanner's FRAMEWORK_BASE_CLASSES ` +\n `(packages/scanner/src/inheritance-resolver.ts) recognizes every ` +\n `framework abstract base in your inheritance chain, that the package ` +\n `has been built (manifest.json present in dist/), and that runtime ` +\n `manifest loading (__smrt-register__.ts) runs before any junction ` +\n `is instantiated.`,\n );\n }\n }\n\n // Create instance using protected constructor\n const instance = new this(collectionOptions);\n\n // Perform async initialization\n await instance.initialize();\n\n // Cache fields for sync access during queries (issue #663)\n // This eliminates async getFields() calls on every query\n const fields = await instance.getFields();\n instance._cachedFields = fields;\n\n // Return fully initialized instance\n return instance;\n }\n\n /**\n * Async initialization hook for the collection.\n *\n * Called automatically by the static `create()` factory. In most cases you\n * do not need to call this directly — use `create()` instead.\n *\n * Runtime schema checks are deferred until a query actually touches the\n * backing table. This keeps collection construction safe during SSR and\n * import-time module evaluation without mutating application schema.\n *\n * @returns This instance (enables chaining)\n */\n public async initialize(): Promise<this> {\n await super.initialize();\n\n // Defer table verification until the first real query. That keeps\n // construction lightweight while ensuring runtime never auto-creates app\n // tables behind the scenes.\n\n return this;\n }\n\n /**\n * Verify that the collection's backing table exists before running a query.\n *\n * This is a fail-fast check only. It does not create or alter schema.\n */\n public async ensureStorageReady(): Promise<void> {\n await verifyPersistenceTable(\n this.db,\n this.tableName,\n this.getResolvedItemClassName(),\n );\n }\n\n /**\n * Find a single record by criteria (convenience method - delegates to get())\n *\n * @param options - Query options with where clause\n * @returns Promise resolving to the object or null if not found\n *\n * @example\n * ```typescript\n * const councils = await Councils.create({ persistence: { type: 'sql', url: 'db.sqlite' } });\n * const council = await councils.findOne({ where: { name: 'Example Council' } });\n * ```\n */\n public async findOne(options: {\n where: Record<string, any>;\n }): Promise<ModelType | null> {\n return await this.get(options.where);\n }\n\n /**\n * Find a record by ID (convenience method - delegates to get())\n *\n * @param id - Record ID to find (string or Field instance)\n * @returns Promise resolving to the object or null if not found\n *\n * @example\n * ```typescript\n * const councils = await Councils.create({ persistence: { type: 'sql', url: 'db.sqlite' } });\n * const council = await councils.findById('uuid-123');\n *\n * // Also works with Field instances (e.g., from foreignKey fields)\n * const meeting = new Meeting({ councilId: 'uuid-123' });\n * const council = await councils.findById(meeting.councilId);\n * ```\n */\n public async findById(id: string): Promise<ModelType | null> {\n return await this.get(id);\n }\n\n /**\n * Find all records matching criteria (convenience method - delegates to list())\n *\n * @param options - Query options (where, orderBy, limit, etc.)\n * @returns Promise resolving to array of objects\n *\n * @example\n * ```typescript\n * const councils = await Councils.create({ persistence: { type: 'sql', url: 'db.sqlite' } });\n * const active = await councils.findAll({ where: { status: 'active' } });\n * ```\n */\n public async findAll(\n options: {\n where?: SmrtWhereClause<ModelType>;\n orderBy?: string | string[];\n limit?: number;\n offset?: number;\n include?: string[];\n } = {},\n ): Promise<ModelType[]> {\n return await this.list(options);\n }\n\n /**\n * Find multiple objects by their IDs in a single query.\n *\n * This is a convenience method that avoids N+1 queries when you have\n * a list of IDs and need to fetch the corresponding records.\n *\n * @param ids - Array of UUIDs to fetch\n * @returns Promise resolving to array of objects (order not guaranteed)\n *\n * @example\n * ```typescript\n * const profiles = await profileCollection.listByIds(['id1', 'id2', 'id3']);\n * ```\n */\n public async listByIds(ids: string[]): Promise<ModelType[]> {\n if (ids.length === 0) return [];\n return this.list({ where: { id: ids } });\n }\n\n /**\n * Resolve the effective cache config for a read (issue #1498).\n *\n * Per-call options win: `false` forces a fresh read, `{ ttl }` enables\n * caching for this call. Otherwise falls back to the model-level\n * `@smrt({ cache })` config (inheritance-aware). Returns undefined when\n * the read should go straight to the database — the default.\n */\n private resolveReadCacheConfig(\n perCall?: CollectionCacheConfig | false,\n ): CollectionCacheConfig | undefined {\n if (perCall === false) return undefined;\n if (perCall) return perCall.ttl > 0 ? perCall : undefined;\n return ObjectRegistry.resolveCollectionCacheConfig(\n this.getResolvedItemQualifiedName(),\n );\n }\n\n /**\n * Execute a SELECT, optionally through the collection read cache.\n *\n * The cache key is the final SQL + bound parameters — computed after\n * interceptors (tenancy filters) and STI discriminators are applied, so\n * differently-scoped queries can never share an entry. Cached values are\n * raw rows; hydration and read interceptors still run on every call.\n */\n private async queryRowsWithCache(\n sql: string,\n params: unknown[],\n cacheConfig: CollectionCacheConfig | undefined,\n ): Promise<Record<string, any>[]> {\n if (!cacheConfig) {\n const result = await this.db.query(sql, ...params);\n return result.rows;\n }\n\n const dbKey = resolveDbCacheKey(this.db);\n const queryKey = buildQueryCacheKey(sql, params);\n\n // Start consuming peer invalidations before the first cached read so\n // a remote write can't go unseen for longer than necessary, and record\n // table-scoped interest so this process's own writes broadcast even\n // when the crossProcess opt-in only exists at the call site.\n if (cacheConfig.crossProcess) {\n ensureCacheInvalidationListener(this.db);\n registerCrossProcessCacheInterest(dbKey, this.tableName);\n }\n\n const cached = getCachedRows(dbKey, this.tableName, queryKey);\n if (cached) {\n return cached;\n }\n\n // Capture the table's invalidation generation BEFORE the round-trip; if a\n // concurrent write invalidates while this SELECT is in flight, setCachedRows\n // sees the bumped generation and drops the now-stale result instead of\n // caching it for the full TTL.\n const generation = getCacheGeneration(dbKey, this.tableName);\n const result = await this.db.query(sql, ...params);\n setCachedRows(\n dbKey,\n this.tableName,\n queryKey,\n result.rows,\n cacheConfig.ttl,\n generation,\n );\n return result.rows;\n }\n\n /**\n * Retrieves a single object from the collection by ID, slug, or a custom filter.\n *\n * Filter resolution:\n * - UUID string → `WHERE id = ?`\n * - Non-UUID string → `WHERE slug = ? AND context = ''`\n * - Object → `WHERE <key> = <value> [AND ...]`\n *\n * For STI child collections, an `AND _meta_type = '<qualifiedName>'` clause is\n * automatically appended so you only receive the correct subclass.\n *\n * Runs `beforeGet` / `afterGet` interceptors (used by multi-tenancy, etc.).\n *\n * @param filter - UUID string, slug string, or a WHERE conditions object\n * @param options.cache - Opt-in read-through cache for this call\n * (`{ ttl }` in milliseconds), or `false` to force a fresh read when the\n * model opted in via `@smrt({ cache })`. Defaults to the model config.\n * @returns The matching object instance, or `null` if not found\n *\n * @example\n * ```typescript\n * // By UUID\n * const product = await products.get('550e8400-e29b-41d4-a716-446655440000');\n *\n * // By slug\n * const product = await products.get('my-widget');\n *\n * // By custom filter\n * const product = await products.get({ sku: 'WID-001' });\n *\n * // Cached read (memoized for 60s, invalidated on writes)\n * const product = await products.get('my-widget', { cache: { ttl: 60_000 } });\n * ```\n *\n * @see {@link list} for multiple results\n * @see {@link findById} / {@link findOne} for convenience aliases\n */\n public async get(\n filter: string | Record<string, any>,\n options: { cache?: CollectionCacheConfig | false } = {},\n ): Promise<ModelType | null> {\n await this.ensureStorageReady();\n const itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n\n // Execute beforeGet interceptors (e.g., tenancy validation)\n const interceptorContext = createInterceptorContext(\n itemClassName,\n 'get',\n this.constructor.name,\n );\n const interceptedFilter = await GlobalInterceptors.executeBeforeGet(\n itemClassName,\n filter,\n interceptorContext,\n );\n\n let where =\n typeof interceptedFilter === 'string'\n ? /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(\n interceptedFilter,\n )\n ? { id: interceptedFilter }\n : { slug: interceptedFilter, context: '' }\n : interceptedFilter;\n\n // Fix for issue #386: Add _meta_type filter for STI child collections.\n // R5-canon: use the qualified item name as the LOOKUP KEY too, not\n // just for the comparison — passing the simple `itemClassName` can\n // resolve to another package's same-simple-name class via\n // `findClass`'s multi-strategy lookup, which would yield the WRONG\n // table-strategy / STI base.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n const isSTI = tableStrategy === 'sti';\n\n if (isSTI) {\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase && stiBase !== itemQualifiedName) {\n where = {\n _meta_type: itemQualifiedName,\n ...where,\n };\n }\n }\n\n // convertWhereKeys is now sync (issue #663) - no await needed\n const convertedWhere = this.convertWhereKeys(where);\n const { sql: whereSql, values: whereValues } = buildWhere(convertedWhere);\n\n const fullSQL = `SELECT * FROM ${this.tableName} ${whereSql}`;\n const rows = await this.queryRowsWithCache(\n fullSQL,\n whereValues,\n this.resolveReadCacheConfig(options.cache),\n );\n\n if (!rows?.[0]) {\n // Execute afterGet with null result\n // Explicitly specify type parameter since TypeScript can't infer from null\n return await GlobalInterceptors.executeAfterGet<ModelType>(\n itemClassName,\n null,\n interceptorContext,\n );\n }\n\n const fields = this.getFieldsSync();\n const instance = await this.hydrateResultRow(rows[0], fields, isSTI);\n\n // Execute afterGet interceptors (e.g., tenant validation)\n return await GlobalInterceptors.executeAfterGet(\n itemClassName,\n instance,\n interceptorContext,\n );\n }\n\n /**\n * Lists records from the collection with flexible filtering options\n *\n * @param options - Query options object\n * @param options.where - Record of conditions to filter results. Each key can include an operator\n * separated by a space (e.g., 'price >', 'name like'). Default operator is '='.\n * @param options.offset - Number of records to skip\n * @param options.limit - Maximum number of records to return\n * @param options.orderBy - Field(s) to order results by, with optional direction\n *\n * @example\n * ```typescript\n * // Find active products priced between $100-$200\n * await collection.list({\n * where: {\n * 'price >': 100,\n * 'price <=': 200,\n * 'status': 'active', // equals operator is default\n * 'category in': ['A', 'B', 'C'], // IN operator for arrays\n * 'name like': '%shirt%', // LIKE for pattern matching\n * 'deleted_at !=': null // exclude deleted items\n * },\n * limit: 10,\n * offset: 0\n * });\n *\n * // Find users matching pattern but not in specific roles\n * await users.list({\n * where: {\n * 'email like': '%@company.com',\n * 'active': true,\n * 'role in': ['guest', 'blocked'],\n * 'last_login <': lastMonth\n * }\n * });\n * ```\n *\n * @returns Promise resolving to an array of model instances\n */\n public async list(\n options: {\n where?: SmrtWhereClause<ModelType>;\n offset?: number;\n limit?: number;\n orderBy?: string | string[];\n /**\n * Relationships to eagerly load (avoids N+1 query problem)\n * @example\n * ```typescript\n * // Load orders with their customers pre-loaded\n * const orders = await orderCollection.list({\n * include: ['customerId']\n * });\n * // Access customer without additional query\n * orders[0].getRelated('customerId');\n * ```\n */\n include?: string[];\n /**\n * Opt-in read-through cache for this call (issue #1498).\n *\n * Pass `{ ttl }` (milliseconds) to memoize the result rows keyed by\n * the final query shape. Mutations through SMRT invalidate the\n * table's entries automatically. Pass `false` to force a fresh read\n * when the model opted in via `@smrt({ cache })`. Defaults to the\n * model-level config; uncached when neither is set.\n *\n * @example\n * ```typescript\n * const published = await resumes.list({\n * where: { status: 'published' },\n * cache: { ttl: 60_000 },\n * });\n * ```\n */\n cache?: CollectionCacheConfig | false;\n } = {},\n ): Promise<ModelType[]> {\n await this.ensureStorageReady();\n const itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n\n // Execute beforeList interceptors (e.g., tenancy filtering)\n const interceptorContext = createInterceptorContext(\n itemClassName,\n 'list',\n this.constructor.name,\n );\n const interceptedOptions =\n (await GlobalInterceptors.executeBeforeList(\n itemClassName,\n options as InterceptorListOptions,\n interceptorContext,\n )) ??\n (options as InterceptorListOptions | undefined) ??\n {};\n\n let { where, offset, limit, orderBy } = interceptedOptions;\n\n // STI: Child collections should automatically filter by _meta_type.\n // R5-canon: qualified-key lookup so a same-simple-name class in\n // another package can't yield the wrong table strategy / STI base.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n const isSTI = tableStrategy === 'sti';\n\n if (isSTI) {\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase && stiBase !== itemQualifiedName) {\n where = {\n _meta_type: itemQualifiedName,\n ...(where || {}),\n };\n }\n }\n\n // Resolve _meta_type to qualified name if user provided simple class name (Issue #713)\n where = resolveMetaTypeInWhere(where);\n\n // convertWhereKeys is now sync (issue #663) - no await needed\n const convertedWhere = this.convertWhereKeys(where || {});\n const { sql: whereSql, values: whereValues } = buildWhere(convertedWhere);\n\n let orderBySql = '';\n if (orderBy) {\n orderBySql = ' ORDER BY ';\n const orderByItems = Array.isArray(orderBy) ? orderBy : [orderBy];\n\n orderBySql += orderByItems\n .map((item) => {\n const [field, direction = 'ASC'] = item.split(' ');\n\n // Validate field name\n if (!/^[a-zA-Z0-9_]+$/.test(field)) {\n throw new Error(`Invalid field name for ordering: ${field}`);\n }\n\n // Validate direction\n const normalizedDirection = direction.toUpperCase();\n if (normalizedDirection !== 'ASC' && normalizedDirection !== 'DESC') {\n throw new Error(\n `Invalid sort direction: ${direction}. Must be ASC or DESC.`,\n );\n }\n\n // Convert field name to snake_case\n const snakeField = toSnakeCase(field);\n return `${snakeField} ${normalizedDirection}`;\n })\n .join(', ');\n }\n\n let limitOffsetSql = '';\n const limitOffsetValues: (number | undefined)[] = [];\n let paramIndex = whereValues.length + 1;\n\n if (limit !== undefined) {\n limitOffsetSql += ` LIMIT $${paramIndex++}`;\n limitOffsetValues.push(limit);\n }\n\n if (offset !== undefined) {\n limitOffsetSql += ` OFFSET $${paramIndex++}`;\n limitOffsetValues.push(offset);\n }\n\n const sql = `SELECT * FROM ${this.tableName} ${whereSql} ${orderBySql} ${limitOffsetSql}`;\n const params = [...whereValues, ...limitOffsetValues];\n\n // Resolve the cache preference from the post-interceptor options —\n // beforeList interceptors may legally set or clear `cache` (e.g. force\n // read-through on admin paths, enable caching per tenant).\n const rows = await this.queryRowsWithCache(\n sql,\n params,\n this.resolveReadCacheConfig(\n (interceptedOptions as { cache?: CollectionCacheConfig | false }).cache,\n ),\n );\n const fields = this.getFieldsSync();\n\n // STI: Hydrate instances polymorphically based on _meta_type\n // Reuse tableStrategy and isSTI from earlier in the function\n const instances = await Promise.all(\n rows.map((item: any) => this.hydrateResultRow(item, fields, isSTI)),\n );\n\n // Eager load specified relationships\n if (interceptedOptions.include && interceptedOptions.include.length > 0) {\n // Cast to ModelType[] - instances are guaranteed to be subtypes of ModelType\n // even in STI mode where they may be different subclasses\n await this.eagerLoadRelationships(\n instances as ModelType[],\n interceptedOptions.include,\n );\n }\n\n // Execute afterList interceptors (e.g., tenant validation)\n const finalInstances = await GlobalInterceptors.executeAfterList(\n itemClassName,\n instances,\n interceptorContext,\n );\n\n return finalInstances;\n }\n\n /**\n * Eagerly load relationships for a collection of instances\n *\n * Optimizes loading by batching queries for foreignKey relationships to avoid N+1 queries.\n *\n * @param instances - Array of object instances to load relationships for\n * @param relationships - Array of relationship field names to load\n * @private\n */\n private async eagerLoadRelationships(\n instances: ModelType[],\n relationships: string[],\n ): Promise<void> {\n if (instances.length === 0) return;\n\n for (const fieldName of relationships) {\n // Get relationship metadata\n const relationshipMeta = ObjectRegistry.getRelationships(\n this._itemClass.name,\n );\n const relationship = relationshipMeta.find(\n (r) => r.fieldName === fieldName,\n );\n\n if (!relationship) {\n logger.warn(\n `Relationship ${fieldName} not found on ${this._itemClass.name}, skipping eager load`,\n );\n continue;\n }\n\n if (\n relationship.type === 'foreignKey' ||\n relationship.type === 'crossPackageRef'\n ) {\n // crossPackageRef target lives in another package — make sure its\n // manifest is loaded before we try to instantiate the target collection.\n if (relationship.type === 'crossPackageRef') {\n await ObjectRegistry.ensureManifestLoaded(relationship.targetClass);\n }\n // Batch load foreignKey / crossPackageRef relationships\n await this.batchLoadForeignKeys(instances, fieldName, relationship);\n } else if (relationship.type === 'oneToMany') {\n // Load oneToMany relationships (less optimizable)\n await this.batchLoadOneToMany(instances, fieldName, relationship);\n } else if (relationship.type === 'manyToMany') {\n await this.batchLoadManyToMany(instances, fieldName, relationship);\n }\n }\n }\n\n /**\n * Batch load foreignKey relationships to avoid N+1 queries\n *\n * @param instances - Instances to load relationships for\n * @param fieldName - Name of the foreignKey field\n * @param relationship - Relationship metadata\n * @private\n */\n private async batchLoadForeignKeys(\n instances: ModelType[],\n fieldName: string,\n relationship: import('./registry').RelationshipMetadata,\n ): Promise<void> {\n // Collect all unique foreign key values\n const foreignKeyValues = new Set<string>();\n for (const instance of instances) {\n const value = instance[fieldName as keyof ModelType];\n if (value && typeof value === 'string') {\n foreignKeyValues.add(value);\n }\n }\n\n if (foreignKeyValues.size === 0) return;\n\n // Get or create cached collection instance\n let targetCollection: SmrtCollection<any> | undefined;\n try {\n targetCollection = await ObjectRegistry.getCollection(\n relationship.targetClass,\n this.options,\n );\n } catch (error) {\n logger.warn(`Could not get collection for ${relationship.targetClass}`, {\n error,\n });\n return;\n }\n\n // Load all related objects, chunked to stay under SQLITE_MAX_VARIABLE_NUMBER (999).\n const fkValueList = Array.from(foreignKeyValues);\n const relatedObjects: any[] = [];\n for (const idChunk of chunkArray(fkValueList, IN_LIST_CHUNK_SIZE)) {\n const batch = await targetCollection.list({\n where: { 'id in': idChunk },\n });\n relatedObjects.push(...batch);\n }\n\n // Build a map of ID to object for quick lookup\n const relatedMap = new Map();\n for (const obj of relatedObjects) {\n relatedMap.set(obj.id, obj);\n }\n\n // Assign loaded objects to instances\n for (const instance of instances) {\n const foreignKeyValue = instance[fieldName as keyof ModelType];\n if (foreignKeyValue && typeof foreignKeyValue === 'string') {\n const relatedObject = relatedMap.get(foreignKeyValue);\n if (relatedObject) {\n // Set in the relationship cache\n (instance as any)._loadedRelationships.set(fieldName, relatedObject);\n }\n }\n }\n }\n\n /**\n * Batch load oneToMany relationships\n *\n * @param instances - Instances to load relationships for\n * @param fieldName - Name of the oneToMany field\n * @param relationship - Relationship metadata\n * @private\n */\n private async batchLoadOneToMany(\n instances: ModelType[],\n fieldName: string,\n relationship: import('./registry').RelationshipMetadata,\n ): Promise<void> {\n // Find the inverse foreignKey field. An instance can satisfy an inverse FK\n // that targets its own class or any (STI) ancestor it inherits the\n // oneToMany from. Mirrors loadRelatedMany so lazy and eager (`include:`)\n // loading resolve the same inverse side.\n const inverseRelationships = ObjectRegistry.getInverseRelationshipsForSelf(\n this._itemClass.name,\n );\n const inverseCandidates = inverseRelationships.filter(\n (r) =>\n r.sourceClass === relationship.targetClass && r.type === 'foreignKey',\n );\n // Honor an explicit `@oneToMany(Target, { foreignKey })` when the target\n // declares multiple foreign keys back to this class; otherwise fall back\n // to the first match (legacy behavior).\n const explicitForeignKey = relationship.options?.foreignKey as\n | string\n | undefined;\n const matchedForeignKey = explicitForeignKey\n ? inverseCandidates.find((r) => r.fieldName === explicitForeignKey)\n : undefined;\n if (explicitForeignKey && !matchedForeignKey) {\n // A misspelled / stale `foreignKey` is a configuration error, not a\n // recoverable data condition — fail loudly here too so eager (`include:`)\n // loading behaves identically to lazy loadRelatedMany rather than\n // silently producing empty arrays.\n throw new Error(\n `oneToMany ${fieldName} specifies foreignKey '${explicitForeignKey}', but ${relationship.targetClass} has no matching inverse foreignKey. Candidates: ${inverseCandidates.map((r) => r.fieldName).join(', ') || '(none)'}`,\n );\n }\n // Prefer an inverse FK that targets this exact class before falling back\n // to an ancestor's (mirrors loadRelatedMany).\n const inverseForeignKey =\n matchedForeignKey ??\n inverseCandidates.find((r) => r.targetClass === this._itemClass.name) ??\n inverseCandidates[0];\n\n if (!inverseForeignKey) {\n logger.warn(\n `Could not find inverse foreignKey for oneToMany ${fieldName}`,\n );\n return;\n }\n\n // Collect all instance IDs\n const instanceIds = instances\n .map((i) => i.id)\n .filter((id): id is string => !!id);\n\n if (instanceIds.length === 0) return;\n\n // Get or create cached collection instance\n let targetCollection: SmrtCollection<any> | undefined;\n try {\n targetCollection = await ObjectRegistry.getCollection(\n relationship.targetClass,\n this.options,\n );\n } catch (error) {\n logger.warn(`Could not get collection for ${relationship.targetClass}`, {\n error,\n });\n return;\n }\n\n // Load all related objects, chunked to stay under SQLITE_MAX_VARIABLE_NUMBER (999).\n const relatedObjects: any[] = [];\n for (const idChunk of chunkArray(instanceIds, IN_LIST_CHUNK_SIZE)) {\n const batch = await targetCollection.list({\n where: { [`${inverseForeignKey.fieldName} in`]: idChunk },\n });\n relatedObjects.push(...batch);\n }\n\n // Group related objects by the foreign key value\n const relatedMap = new Map<string, any[]>();\n for (const obj of relatedObjects) {\n // Access dynamic property safely - obj is a SmrtObject with dynamic fields\n const foreignKeyValue = (obj as Record<string, any>)[\n inverseForeignKey.fieldName\n ];\n if (!relatedMap.has(foreignKeyValue)) {\n relatedMap.set(foreignKeyValue, []);\n }\n relatedMap.get(foreignKeyValue)?.push(obj);\n }\n\n // Assign loaded objects to instances\n for (const instance of instances) {\n const relatedArray = relatedMap.get(instance.id as string) || [];\n (instance as any)._loadedRelationships.set(fieldName, relatedArray);\n }\n }\n\n /**\n * Batch-load manyToMany relationships through a junction table.\n *\n * Issues two queries instead of N: one against the junction table to map\n * source IDs to target IDs, and one against the target table to hydrate\n * the related rows. Results are grouped by source instance.\n *\n * @param instances - Instances whose manyToMany field should be populated\n * @param fieldName - Name of the @manyToMany decorated field\n * @param relationship - Relationship metadata from the registry\n * @private\n */\n private async batchLoadManyToMany(\n instances: ModelType[],\n fieldName: string,\n relationship: import('./registry').RelationshipMetadata,\n ): Promise<void> {\n const instanceIds = instances\n .map((i) => i.id)\n .filter((id): id is string => !!id);\n if (instanceIds.length === 0) return;\n\n // Delegate join-coordinate resolution to a sample instance — it shares the\n // same registry metadata as every other instance in this batch.\n let through: string;\n let sourceColumn: string;\n let targetColumn: string;\n let targetClassName: string;\n try {\n const sample: any = instances[0];\n const join = await sample.resolveManyToManyJoin(fieldName, relationship);\n through = join.through;\n sourceColumn = join.sourceColumn;\n targetColumn = join.targetColumn;\n targetClassName = join.targetClassName;\n } catch (error) {\n logger.warn(\n `Could not resolve manyToMany join for ${fieldName} on ${this._itemClass.name}`,\n { error },\n );\n return;\n }\n\n // Default empty arrays for every instance so callers always see an array\n for (const instance of instances) {\n (instance as any)._loadedRelationships.set(fieldName, []);\n }\n\n // Pull junction rows in chunks. SQLite's default SQLITE_MAX_VARIABLE_NUMBER\n // is 999, so we cap each IN-list at IN_LIST_CHUNK_SIZE to stay well below\n // the limit on the supported backends (sqlite/libsql/postgres).\n const junctionRowsAll: Array<{\n [key: string]: unknown;\n }> = [];\n for (const idChunk of chunkArray(instanceIds, IN_LIST_CHUNK_SIZE)) {\n const placeholders = idChunk.map(() => '?').join(', ');\n const result = await this.db.query(\n `SELECT \"${sourceColumn}\", \"${targetColumn}\" FROM \"${through}\" WHERE \"${sourceColumn}\" IN (${placeholders})`,\n idChunk,\n );\n junctionRowsAll.push(...result.rows);\n }\n\n if (junctionRowsAll.length === 0) return;\n\n // sourceId -> [targetIds...]\n const sourceToTargets = new Map<string, string[]>();\n const allTargetIds = new Set<string>();\n for (const row of junctionRowsAll as any[]) {\n const sId = row[sourceColumn];\n const tId = row[targetColumn];\n if (typeof sId !== 'string' || typeof tId !== 'string') continue;\n allTargetIds.add(tId);\n const list = sourceToTargets.get(sId) ?? [];\n list.push(tId);\n sourceToTargets.set(sId, list);\n }\n\n if (allTargetIds.size === 0) return;\n\n // Hydrate all target objects. Also chunked so we don't blow the\n // placeholder limit on a wide manyToMany.\n let targetCollection: SmrtCollection<any>;\n try {\n targetCollection = await ObjectRegistry.getCollection(\n targetClassName,\n this.options,\n );\n } catch (error) {\n logger.warn(\n `Could not get collection for manyToMany target ${targetClassName}`,\n { error },\n );\n return;\n }\n const targetIdList = Array.from(allTargetIds);\n const targetObjects: any[] = [];\n for (const idChunk of chunkArray(targetIdList, IN_LIST_CHUNK_SIZE)) {\n const batch = await targetCollection.list({\n where: { 'id in': idChunk },\n });\n targetObjects.push(...batch);\n }\n\n const targetById = new Map<string, any>();\n for (const obj of targetObjects) {\n if (obj.id) targetById.set(obj.id, obj);\n }\n\n // Assign grouped results\n for (const instance of instances) {\n const targetIds = sourceToTargets.get(instance.id as string) ?? [];\n const objects = targetIds\n .map((id) => targetById.get(id))\n .filter((o) => o !== undefined);\n (instance as any)._loadedRelationships.set(fieldName, objects);\n }\n }\n\n /**\n * Creates and persists a new instance of the collection's item class.\n *\n * Instantiates the model with the given field values, calls `initialize()`,\n * assigns a UUID if none is provided, then calls `save()` to write the row\n * to the database.\n *\n * For STI collections, pass `_meta_type` to create a specific subclass.\n * The correct constructor is resolved via `ObjectRegistry` (polymorphic creation).\n *\n * @param options - Field values for the new object. Accepts any public field on\n * the model class plus the STI `_meta_type` discriminator.\n * @returns The newly created and saved model instance\n * @throws {ValidationError} If a `required` field is missing or a unique constraint is violated\n * @throws {DatabaseError} If the write fails\n *\n * @example\n * ```typescript\n * // Regular creation\n * const product = await products.create({ name: 'Widget', price: 9.99 });\n * console.log(product.id); // UUID assigned during save\n *\n * // STI polymorphic creation\n * const article = await contents.create({\n * _meta_type: '@happyvertical/smrt-content:Article',\n * title: 'Hello World',\n * });\n * ```\n *\n * @see {@link getOrUpsert} to avoid duplicates by finding-or-creating\n */\n public async create(options: SmrtCreateInput<ModelType>) {\n let itemClassName = this.getResolvedItemClassName();\n\n // Ensure manifest is loaded before creating instance metadata; save() will\n // ensure the actual table exists right before persistence.\n await ObjectRegistry.ensureManifestLoaded(itemClassName);\n itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n\n // STI: Check for polymorphic instantiation.\n // R5-canon: qualified-key lookup so colliding simple names across\n // packages don't yield the wrong table strategy.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n\n if (tableStrategy === 'sti' && options._meta_type) {\n // Use polymorphic instantiation for STI child classes\n // Pass raw options (not params) - createPolymorphic() will add ai, db, _skipLoad\n const instance = await this.createPolymorphic(\n options._meta_type,\n options,\n );\n // Generate ID if not provided, then save\n if (!instance.id) {\n (instance as any)._id = crypto.randomUUID();\n }\n await instance.save();\n return instance;\n }\n\n // For non-STI or STI base class without _meta_type, proceed with direct instantiation\n // Schema already initialized in Collection.create() static factory\n const params = {\n ai: this.options.ai,\n // Pass the actual database instance, not options\n // This ensures objects share the same connection as the collection\n // Critical for in-memory databases like DuckDB :memory: where each\n // connection gets a separate database\n db: this.db,\n _skipLoad: true, // Don't try to load from DB - this is a new object\n ...options,\n };\n\n // Direct instantiation - all SmrtObject classes support this pattern\n const instance = new this._itemClass(params);\n await instance.initialize();\n\n // For STI collections, set _meta_type to the qualified class name (fix for issue #442)\n // This ensures _meta_type is available immediately after creation, not just after DB load\n // Use constructor-based lookup to avoid name collision issues (issue #713)\n // When classes are registered with qualified names as keys (e.g., from consumer plugin),\n // name-based lookup fails. Constructor lookup uses a WeakMap index for O(1) lookups.\n if (tableStrategy === 'sti') {\n (instance as any)._meta_type = itemQualifiedName;\n }\n // Generate ID if not provided, then save\n if (!instance.id) {\n (instance as any)._id = crypto.randomUUID();\n }\n await instance.save();\n return instance;\n }\n\n /**\n * Creates an instance of the correct subclass for STI polymorphic queries\n *\n * @param className - Name of the class to instantiate (from _meta_type)\n * @param options - Data to initialize the instance with\n * @returns Promise resolving to the instance of the correct subclass\n * @private\n */\n private async createPolymorphic(\n className: string | null | undefined,\n options: any,\n hydrationOptions: { hydrateOnly?: boolean } = {},\n ): Promise<ModelType> {\n // Schema already initialized in Collection.create() static factory\n\n // Validate discriminator is present\n if (!className || className === null || className === undefined) {\n const { DatabaseError } = await import('./errors.js');\n throw DatabaseError.missingDiscriminator(\n this._itemClass.name,\n options?.id,\n );\n }\n\n // Get the correct class constructor from ObjectRegistry\n // Support both qualified names (new format: @pkg:Class) and simple names (legacy)\n let registeredClass = ObjectRegistry.getClassByQualifiedName(className);\n if (!registeredClass) {\n // Fall back to simple name lookup for legacy data\n registeredClass = ObjectRegistry.getClass(className);\n }\n\n if (!registeredClass) {\n await ObjectRegistry.ensureManifestLoaded(className);\n registeredClass = ObjectRegistry.getClassByQualifiedName(className);\n if (!registeredClass) {\n registeredClass = ObjectRegistry.getClass(className);\n }\n }\n\n if (!registeredClass) {\n throw new Error(\n `STI polymorphic query failed: Class '${className}' not found in ObjectRegistry. ` +\n `Ensure the class is registered with @smrt() decorator or available via an installed SMRT manifest.`,\n );\n }\n\n const params = {\n ai: this.options.ai,\n db: this.db,\n _skipLoad: true,\n ...(hydrationOptions.hydrateOnly\n ? {\n _reuseInitializedDb: true,\n _deferRuntimeInitialization: true,\n }\n : {}),\n ...options,\n };\n\n // Instantiate the correct subclass\n const instance = new registeredClass.constructor(params);\n await instance.initialize();\n\n // Ensure _meta_type is set on the instance (fix for issue #442)\n // Use qualified name if available, otherwise keep the input className\n // (which may already be qualified for new data or simple for legacy data)\n (instance as any)._meta_type = registeredClass?.qualifiedName || className;\n\n return instance as ModelType;\n }\n\n /**\n * Finds an existing record matching `data` or creates it if not found.\n *\n * Look-up priority:\n * 1. `data.id` — query by primary key\n * 2. `data.slug` — query by slug + context\n * 3. Fallback — query by the full `data` object as a WHERE clause\n *\n * If a matching record is found, it is updated with any changed fields from\n * `data` (diffed against the existing record) and saved. If no match is\n * found, `defaults` are merged with `data` and a new record is created.\n *\n * @param data - The field values to find or upsert\n * @param defaults - Extra default values applied only when creating a new record\n * @returns The existing (possibly updated) or newly created object instance\n *\n * @example\n * ```typescript\n * // Find-or-create a tag by slug\n * const tag = await tags.getOrUpsert({ slug: 'javascript', name: 'JavaScript' });\n *\n * // With defaults applied only on creation\n * const user = await users.getOrUpsert(\n * { email: 'alice@example.com' },\n * { role: 'member', active: true },\n * );\n * ```\n *\n * @see {@link create} for always-insert semantics\n * @see {@link get} for read-only lookup\n */\n public async getOrUpsert(data: any, defaults: any = {}) {\n const logicalData = this.normalizeLogicalData(data);\n const logicalDefaults = this.normalizeLogicalData(defaults);\n let where: any = {};\n const diffData = { ...logicalData };\n if (logicalData.id) {\n where = { id: logicalData.id };\n delete diffData.id;\n delete diffData.slug;\n delete diffData.context;\n } else if (logicalData.slug) {\n where = { slug: logicalData.slug, context: logicalData.context || '' };\n delete diffData.slug;\n delete diffData.context;\n } else {\n where = logicalData;\n }\n // Force a fresh read: this probe decides create-vs-update, so a stale cache\n // hit could update a row that no longer exists or miss one that does.\n const existing = await this.get(where, { cache: false });\n if (existing) {\n const diff = this.getDiffSync(existing, diffData);\n if (diff) {\n Object.assign(existing, diff);\n await existing.save();\n }\n return existing;\n }\n const createData = {\n ...logicalDefaults,\n ...logicalData,\n } as SmrtCreateInput<ModelType>;\n\n return await this.create(createData);\n }\n\n /**\n * Gets differences between an existing object and new data\n *\n * @param existing - Existing object\n * @param data - New data\n * @returns Object containing only the changed fields\n */\n async getDiff(\n existing: Record<string, any>,\n data: Record<string, any>,\n ): Promise<Record<string, any> | null> {\n return this.getDiffSync(existing, data);\n }\n\n private getDiffSync(\n existing: Record<string, any>,\n data: Record<string, any>,\n ): Record<string, any> | null {\n const fields = this.getFieldsSync();\n const validKeys = new Set([\n ...Object.keys(fields),\n 'id',\n 'slug',\n 'context',\n ]);\n const diff = Object.keys(data).reduce(\n (acc, key) => {\n if (\n validKeys.has(key) &&\n !this.areEquivalentValues(existing[key], data[key])\n ) {\n acc[key] = data[key];\n }\n return acc;\n },\n {} as Record<string, any>,\n );\n\n return Object.keys(diff).length > 0 ? diff : null;\n }\n\n /**\n * Gets field definitions for the collection's item class\n *\n * @returns Object containing field definitions\n */\n async getFields() {\n return await fieldsFromClass(this._itemClass);\n }\n\n /**\n * Normalize user input into the model's logical field names before diffing or creation.\n *\n * Accepts both camelCase and snake_case keys while preserving framework meta fields.\n */\n private normalizeLogicalData(data: Record<string, any>): Record<string, any> {\n const fields = this.getFieldsSync();\n const normalized: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) {\n normalized[key] = value;\n continue;\n }\n\n const camelKey = key.includes('_') ? toCamelCase(key) : key;\n const snakeKey = key.includes('_') ? key : toSnakeCase(key);\n const outputKey =\n key in fields\n ? key\n : camelKey in fields\n ? camelKey\n : snakeKey in fields\n ? snakeKey\n : key;\n\n normalized[outputKey] = value;\n }\n\n return normalized;\n }\n\n /**\n * Preserve persisted core timestamp fields during lightweight hydration.\n */\n private withHydratedCoreFields(\n data: Record<string, any>,\n ): Record<string, any> {\n const hydratedData = { ...data };\n\n if (\n hydratedData.createdAt !== undefined &&\n hydratedData.created_at === undefined\n ) {\n hydratedData.created_at = hydratedData.createdAt;\n }\n\n if (\n hydratedData.updatedAt !== undefined &&\n hydratedData.updated_at === undefined\n ) {\n hydratedData.updated_at = hydratedData.updatedAt;\n }\n\n return hydratedData;\n }\n\n /**\n * Treat date-equivalent values as unchanged even if their runtime types differ.\n */\n private areEquivalentValues(\n existingValue: unknown,\n nextValue: unknown,\n ): boolean {\n if (existingValue instanceof Date || nextValue instanceof Date) {\n const existingTime = this.toComparableTime(existingValue);\n const nextTime = this.toComparableTime(nextValue);\n\n if (existingTime !== null && nextTime !== null) {\n return existingTime === nextTime;\n }\n }\n\n return existingValue === nextValue;\n }\n\n /**\n * Convert supported date inputs into comparable timestamps.\n */\n private toComparableTime(value: unknown): number | null {\n if (value instanceof Date) {\n const timestamp = value.getTime();\n return Number.isNaN(timestamp) ? null : timestamp;\n }\n\n if (typeof value === 'string' && value.trim()) {\n const parsedDate = new Date(value);\n const timestamp = parsedDate.getTime();\n return Number.isNaN(timestamp) ? null : timestamp;\n }\n\n return null;\n }\n\n /**\n * Gets field definitions synchronously from cache.\n *\n * This method provides sync access to fields for use in query methods,\n * avoiding the async overhead of getFields() on every query.\n * Fields are cached during create() initialization.\n *\n * @returns Map containing field definitions\n * @private\n */\n getFieldsSync(): Record<string, any> {\n if (this._cachedFields) {\n return this._cachedFields;\n }\n // Fallback to ObjectRegistry sync method if cache not populated\n // This handles edge cases where collection wasn't created via static create()\n const className = this.getResolvedItemClassName();\n const fields = ObjectRegistry.getFields(className);\n // Convert Map to Record for consistency with getFields() return type\n return Object.fromEntries(fields);\n }\n\n /**\n * Generates database schema for the collection's item class\n *\n * Leverages ObjectRegistry's cached schema for instant retrieval.\n *\n * @returns Schema object for database setup\n */\n async generateSchema() {\n // Always generate fresh schema to ensure latest field mapping is used\n const { generateSchema } = await import('./schema/utils.js');\n return await generateSchema(this._itemClass);\n }\n\n /**\n * Gets the database table name for this collection\n */\n get tableName() {\n if (!this._tableName) {\n // For STI, use the base class's table name from schema (manifest-derived).\n // R5-canon: use the qualified item name as the lookup key so a\n // colliding simple name in another package can't yield the wrong\n // tableStrategy / STI base and route every downstream query\n // (get/list/count/create) to the wrong table.\n const className = this.getResolvedItemClassName();\n const qualifiedName = this.getResolvedItemQualifiedName();\n const tableStrategy = ObjectRegistry.getTableStrategy(qualifiedName);\n const fallbackTableName =\n (this._itemClass as any).SMRT_TABLE_NAME ||\n classnameToTablename(className);\n\n if (tableStrategy === 'sti') {\n const stiBase = ObjectRegistry.getSTIBase(qualifiedName);\n if (stiBase) {\n // Use base class's schema tableName (from manifest)\n const baseSchema = ObjectRegistry.getSchema(stiBase);\n if (baseSchema?.tableName) {\n this._tableName = baseSchema.tableName;\n } else {\n // Fallback to own schema tableName\n const ownSchema = ObjectRegistry.getSchema(className);\n this._tableName = ownSchema?.tableName || fallbackTableName;\n }\n } else {\n // Fallback to own schema tableName\n const ownSchema = ObjectRegistry.getSchema(className);\n this._tableName = ownSchema?.tableName || fallbackTableName;\n }\n } else {\n // CTI: Use own schema tableName\n const ownSchema = ObjectRegistry.getSchema(className);\n this._tableName = ownSchema?.tableName || fallbackTableName;\n }\n }\n return this._tableName;\n }\n\n /**\n * Generates a table name from the collection class name\n *\n * @returns Generated table name\n */\n generateTableName() {\n // Convert camelCase/PascalCase to snake_case and pluralize\n const tableName = this._className\n // Insert underscore between lower & upper case letters\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n // Convert to lowercase\n .toLowerCase()\n // Handle basic pluralization rules\n .replace(/([^s])$/, '$1s')\n // Handle special cases ending in 'y'\n .replace(/y$/, 'ies');\n\n return tableName;\n }\n\n /**\n * Deletes a record from the collection by ID\n *\n * Loads the object and calls its delete() method, ensuring all interceptors\n * and lifecycle hooks (beforeDelete/afterDelete) are executed correctly.\n *\n * @param id - The ID of the record to delete\n * @returns Promise resolving to true if deleted, false if not found\n *\n * @example\n * ```typescript\n * const success = await collection.delete('some-uuid');\n * if (success) {\n * console.log('Record deleted');\n * }\n * ```\n */\n public async delete(id: string): Promise<boolean> {\n // Schema already initialized in Collection.create() static factory\n\n // Ensure manifest is loaded for external packages\n await ObjectRegistry.ensureManifestLoaded(this.getResolvedItemClassName());\n\n // Load the object first - if it doesn't exist, return false immediately.\n // Force a fresh read: this probe gates a mutation and decides the return\n // value, so a stale cache hit could delete a phantom (returning true for a\n // row another process already removed) or skip a real delete.\n const instance = await this.get(id, { cache: false });\n if (!instance) {\n return false;\n }\n\n // Call the object's delete() method, which handles:\n // - beforeDelete interceptors\n // - beforeDelete lifecycle hooks\n // - Database deletion\n // - afterDelete lifecycle hooks\n // - afterDelete interceptors\n await instance.delete();\n\n return true;\n }\n\n /**\n * Returns the number of records matching the given filter conditions.\n *\n * Executes a `SELECT COUNT(*)` query with the same WHERE conversion as\n * `list()` (camelCase field names, operator suffixes, STI auto-filtering).\n * `limit`, `offset`, and `orderBy` are not applicable and are ignored.\n *\n * Note: `count()` is never served from the read cache (issue #1498) — only\n * `list()`/`get()` are. On a page that caches `list()`, a `count()` issued\n * in the same request reflects the live database and may briefly diverge\n * from the cached rows within the TTL window.\n *\n * @param options.where - Filter conditions (same syntax as `list()`)\n * @returns Total count of matching records as an integer\n *\n * @example\n * ```typescript\n * const total = await products.count();\n * const activeCount = await products.count({ where: { status: 'active' } });\n * const expensiveCount = await products.count({ where: { 'price >': 100 } });\n * ```\n *\n * @see {@link list} for retrieving the actual records\n */\n public async count(options: { where?: Record<string, any> } = {}) {\n await this.ensureStorageReady();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n const itemClassName = this.getResolvedItemClassName();\n\n // Security (#1540): count() is a list-shaped read, so run the same\n // `beforeList` interceptors (tenant filtering, etc.). Without this, count()\n // bypasses tenant scoping and returns a cross-tenant total even when the\n // matching list() is correctly filtered — leaking row counts across tenants.\n const interceptorContext = createInterceptorContext(\n itemClassName,\n 'list',\n this.constructor.name,\n );\n const interceptedOptions =\n (await GlobalInterceptors.executeBeforeList(\n itemClassName,\n options as InterceptorListOptions,\n interceptorContext,\n )) ??\n (options as InterceptorListOptions | undefined) ??\n {};\n\n let { where } = interceptedOptions;\n\n // Fix for issue #386: Add _meta_type filter for STI child collections.\n // R5-canon: qualified-key lookup throughout — pass `itemQualifiedName`\n // to both `getTableStrategy` and `getSTIBase` so a colliding\n // simple-name class in another package can't yield the wrong table\n // strategy / STI base.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n const isSTI = tableStrategy === 'sti';\n\n if (isSTI) {\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase && stiBase !== itemQualifiedName) {\n where = {\n _meta_type: itemQualifiedName,\n ...(where || {}),\n };\n }\n }\n\n // Resolve _meta_type to qualified name if user provided simple class name (Issue #713)\n where = resolveMetaTypeInWhere(where);\n\n // convertWhereKeys is now sync (issue #663) - no await needed\n const { sql: whereSql, values: whereValues } = buildWhere(\n this.convertWhereKeys(where || {}),\n );\n\n const result = await this.db.query(\n `SELECT COUNT(*) as count FROM ${this.tableName} ${whereSql}`,\n ...whereValues,\n );\n\n return Number.parseInt(result.rows[0].count, 10);\n }\n\n /**\n * Execute a raw SQL query and hydrate results as collection item instances\n *\n * Provides full SQL power for complex queries (JOINs, CTEs, NOT EXISTS, etc.)\n * while still returning properly hydrated SMRT objects.\n *\n * @param sql - Raw SQL query string (should select from this.tableName)\n * @param params - Query parameters for prepared statement\n * @returns Promise resolving to array of hydrated model instances\n *\n * @example\n * ```typescript\n * // Find meetings without corresponding recaps (NOT EXISTS pattern)\n * const meetings = await meetingCollection.query(`\n * SELECT m.* FROM meetings m\n * WHERE m.start_date < datetime('now')\n * AND NOT EXISTS (\n * SELECT 1 FROM contents c\n * WHERE c.meeting_id = m.id\n * AND c._meta_type = 'MeetingRecap'\n * )\n * ORDER BY m.start_date DESC\n * LIMIT ?\n * `, [10]);\n *\n * // Complex JOIN query\n * const products = await productCollection.query(`\n * SELECT p.* FROM products p\n * INNER JOIN categories c ON p.category_id = c.id\n * WHERE c.name = ? AND p.price > ?\n * ORDER BY p.price ASC\n * `, ['Electronics', 100]);\n * ```\n */\n public async query(\n sql: string,\n params: any[] = [],\n options: { allowRawOnTenantScoped?: boolean } = {},\n ): Promise<ModelType[]> {\n await this.ensureStorageReady();\n\n // Execute beforeQuery interceptors (e.g., tenant raw SQL policy)\n const interceptorContext = createInterceptorContext(\n this._itemClass.name,\n 'query',\n this.constructor.name,\n );\n const interceptedQuery = await GlobalInterceptors.executeBeforeQuery(\n this._itemClass.name,\n { sql, params, allowRawOnTenantScoped: options.allowRawOnTenantScoped },\n interceptorContext,\n );\n\n const result = await this.db.query(\n interceptedQuery.sql,\n ...interceptedQuery.params,\n );\n\n // query() is a raw escape hatch documented for SELECTs, but it can run any\n // SQL. A write issued through it bypasses the save()/delete() invalidation\n // hooks, so conservatively bust this table's cache when the statement looks\n // like a mutation (issue #1498). The word-boundary match ignores column\n // names like `updated_at`/`deleted_at`; a false positive (e.g. SELECT ...\n // FOR UPDATE) only costs a cache miss, never a stale read.\n if (\n /\\b(?:insert|update|delete|merge|truncate|replace)\\b/i.test(\n interceptedQuery.sql,\n )\n ) {\n invalidateCollectionCache(resolveDbCacheKey(this.db), this.tableName);\n }\n\n const fields = this.getFieldsSync();\n\n // STI: Check if we need polymorphic hydration.\n // R5-canon: pass the qualified item name so a colliding simple\n // name in another package can't yield the wrong tableStrategy\n // and feed the wrong `isSTI` boolean into `hydrateResultRow`.\n const tableStrategy = ObjectRegistry.getTableStrategy(\n this.getResolvedItemQualifiedName(),\n );\n const isSTI = tableStrategy === 'sti';\n\n const instances = await Promise.all(\n result.rows.map((row: any) => this.hydrateResultRow(row, fields, isSTI)),\n );\n\n // Execute afterQuery interceptors\n return await GlobalInterceptors.executeAfterQuery(\n this._itemClass.name,\n instances,\n interceptorContext,\n );\n }\n\n private async hydrateResultRow(\n row: Record<string, any>,\n fields: Record<string, any>,\n isSTI: boolean,\n ): Promise<ModelType> {\n const formattedData = this.withHydratedCoreFields(\n formatDataJs(row, fields),\n );\n\n if (isSTI && formattedData._meta_type) {\n const polymorphicInstance = await this.createPolymorphic(\n formattedData._meta_type,\n formattedData,\n { hydrateOnly: true },\n );\n // Hydrated from an existing row — save() must update by primary key\n // even if natural-key fields change (issue #1472).\n polymorphicInstance.markAsPersisted();\n return polymorphicInstance;\n }\n\n const instanceParams = {\n ai: this.options.ai,\n db: this.db,\n _skipLoad: true,\n _reuseInitializedDb: true,\n _deferRuntimeInitialization: true,\n ...formattedData,\n };\n\n const instance = new this._itemClass(instanceParams);\n await instance.initialize();\n\n if (isSTI) {\n const registeredClass = ObjectRegistry.getClass(this._itemClass.name);\n (instance as any)._meta_type =\n registeredClass?.qualifiedName || this._itemClass.name;\n }\n\n // Hydrated from an existing row — save() must update by primary key\n // even if natural-key fields change (issue #1472).\n instance.markAsPersisted();\n return instance;\n }\n\n /**\n * Remember collection-level context\n *\n * Stores context applicable to all instances of this collection type.\n * Use for patterns that apply to the entire collection (e.g., default parsing strategies).\n *\n * @param options - Context options\n * @returns Promise that resolves when context is stored\n * @example\n * ```typescript\n * // Remember a default parsing strategy for all documents\n * await documentCollection.remember({\n * scope: 'parser/default',\n * key: 'selector',\n * value: { pattern: '.content article' },\n * confidence: 0.8\n * });\n *\n * // Update an existing context entry by specifying id\n * await documentCollection.remember({\n * id: 'existing-context-id',\n * scope: 'parser/default',\n * key: 'selector',\n * value: { pattern: '.content main article' },\n * confidence: 0.85\n * });\n * ```\n */\n public async remember(options: {\n id?: string;\n scope: string;\n key: string;\n value: any;\n metadata?: any;\n confidence?: number;\n version?: number;\n expiresAt?: Date;\n }): Promise<void> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n const id = options.id || crypto.randomUUID();\n const now = new Date();\n\n // Use upsert() for database-agnostic INSERT OR REPLACE\n // SQLite: INSERT OR REPLACE\n // Postgres/DuckDB: INSERT ... ON CONFLICT ... DO UPDATE\n await this.systemDb.upsert(\n '_smrt_contexts',\n // UNIQUE constraint: (owner_class, owner_id, scope, key, version)\n ['owner_class', 'owner_id', 'scope', 'key', 'version'],\n {\n id,\n owner_class: this._itemClass.name,\n owner_id: '__collection__',\n scope: options.scope,\n key: options.key,\n value: JSON.stringify(options.value),\n metadata: options.metadata ? JSON.stringify(options.metadata) : null,\n version: options.version ?? 1,\n confidence: options.confidence ?? 1.0,\n success_count: 0,\n failure_count: 0,\n created_at: now,\n updated_at: now,\n last_used_at: now,\n expires_at: options.expiresAt ?? null,\n },\n );\n }\n\n /**\n * Recall collection-level context\n *\n * Retrieves context that applies to all instances of this collection.\n *\n * @param options - Recall options\n * @returns Promise resolving to the context value or null if not found\n * @example\n * ```typescript\n * // Recall default parsing strategy\n * const strategy = await documentCollection.recall({\n * scope: 'parser/default',\n * key: 'selector',\n * minConfidence: 0.5\n * });\n * ```\n */\n public async recall(options: {\n scope: string;\n key: string;\n includeAncestors?: boolean;\n minConfidence?: number;\n }): Promise<any | null> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n // Use single() with template literals for custom SQL query\n let result: Record<string, any> | null;\n if (options.minConfidence !== undefined) {\n result = await this.systemDb.single`\n SELECT value, confidence\n FROM _smrt_contexts\n WHERE owner_class = ${this._itemClass.name}\n AND owner_id = ${'__collection__'}\n AND scope = ${options.scope}\n AND key = ${options.key}\n AND confidence >= ${options.minConfidence}\n ORDER BY confidence DESC, version DESC\n LIMIT 1\n `;\n } else {\n result = await this.systemDb.single`\n SELECT value, confidence\n FROM _smrt_contexts\n WHERE owner_class = ${this._itemClass.name}\n AND owner_id = ${'__collection__'}\n AND scope = ${options.scope}\n AND key = ${options.key}\n ORDER BY confidence DESC, version DESC\n LIMIT 1\n `;\n }\n\n if (result) {\n // Guard against a corrupted _smrt_contexts row: a single malformed value\n // must not throw an uncaught SyntaxError out of recall(). Treat it as a\n // miss and continue to the ancestor fallback (#1378).\n try {\n return JSON.parse(result.value);\n } catch (error) {\n logger.warn('Skipping corrupted _smrt_contexts value in recall()', {\n ownerClass: this._itemClass.name,\n scope: options.scope,\n key: options.key,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Hierarchical fallback to parent scopes\n if (options.includeAncestors) {\n const scopeParts = options.scope.split('/');\n while (scopeParts.length > 0) {\n scopeParts.pop();\n const parentScope = scopeParts.join('/') || 'global';\n\n const parentResult = await this.recall({\n ...options,\n scope: parentScope,\n includeAncestors: false,\n });\n\n if (parentResult) return parentResult;\n }\n }\n\n return null;\n }\n\n /**\n * Recall all collection-level context in a scope\n *\n * Returns a Map of key -> value for all collection contexts matching the criteria.\n *\n * @param options - Recall options\n * @returns Promise resolving to Map of key -> value pairs\n * @example\n * ```typescript\n * // Get all default strategies\n * const strategies = await documentCollection.recallAll({\n * scope: 'parser/default',\n * minConfidence: 0.5\n * });\n * ```\n */\n public async recallAll(\n options: {\n scope?: string;\n includeDescendants?: boolean;\n minConfidence?: number;\n } = {},\n ): Promise<Map<string, any>> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n const results = new Map<string, any>();\n\n let query = `\n SELECT key, value, confidence\n FROM _smrt_contexts\n WHERE owner_class = ? AND owner_id = ?\n `;\n const params: any[] = [this._itemClass.name, '__collection__'];\n\n if (options.scope) {\n if (options.includeDescendants) {\n query += ` AND (scope = ? OR scope LIKE ?)`;\n params.push(options.scope, `${options.scope}/%`);\n } else {\n query += ` AND scope = ?`;\n params.push(options.scope);\n }\n }\n\n if (options.minConfidence !== undefined) {\n query += ` AND confidence >= ?`;\n params.push(options.minConfidence);\n }\n\n query += ` ORDER BY confidence DESC`;\n\n const { rows } = await this.systemDb.query(query, ...params);\n\n for (const row of rows) {\n // Skip a corrupted row rather than aborting the whole recallAll() (#1378).\n try {\n results.set(row.key, JSON.parse(row.value));\n } catch (error) {\n logger.warn('Skipping corrupted _smrt_contexts value in recallAll()', {\n ownerClass: this._itemClass.name,\n scope: options.scope,\n key: row.key,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Forget collection-level context\n *\n * Deletes collection context by scope and key.\n *\n * @param options - Context identification\n * @returns Promise that resolves when context is deleted\n * @example\n * ```typescript\n * // Remove a default strategy\n * await documentCollection.forget({\n * scope: 'parser/default',\n * key: 'selector'\n * });\n * ```\n */\n public async forget(options: { scope: string; key: string }): Promise<void> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n await this.systemDb.query(\n `DELETE FROM _smrt_contexts\n WHERE owner_class = ? AND owner_id = ? AND scope = ? AND key = ?`,\n this._itemClass.name,\n '__collection__',\n options.scope,\n options.key,\n );\n }\n\n /**\n * Forget all collection-level context in a scope\n *\n * Deletes all collection contexts matching the scope pattern.\n *\n * @param options - Scope options\n * @returns Promise resolving to number of contexts deleted\n * @example\n * ```typescript\n * // Clear all default strategies\n * const count = await documentCollection.forgetScope({\n * scope: 'parser/default',\n * includeDescendants: true\n * });\n * ```\n */\n public async forgetScope(options: {\n scope: string;\n includeDescendants?: boolean;\n }): Promise<number> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n let query = `\n DELETE FROM _smrt_contexts\n WHERE owner_class = ? AND owner_id = ?\n `;\n const params: any[] = [this._itemClass.name, '__collection__'];\n\n if (options.includeDescendants) {\n query += ` AND (scope = ? OR scope LIKE ?)`;\n params.push(options.scope, `${options.scope}/%`);\n } else {\n query += ` AND scope = ?`;\n params.push(options.scope);\n }\n\n const { rowCount } = await this.systemDb.query(query, ...params);\n return rowCount || 0;\n }\n\n // ============================================================================\n // Semantic Search Methods\n // ============================================================================\n\n /**\n * Semantic search by text query\n *\n * Generates an embedding for the query text and finds similar objects\n * based on cosine similarity of stored embeddings.\n *\n * @param query - Text to search for\n * @param options - Search options\n * @param options.field - Specific field to search (defaults to first embedding field)\n * @param options.limit - Maximum results to return (default: 10)\n * @param options.minSimilarity - Minimum similarity threshold 0-1 (default: 0)\n * @param options.where - Additional WHERE filters to apply\n * @returns Promise resolving to array of objects with _similarity score\n *\n * @example\n * ```typescript\n * const results = await articles.semanticSearch('machine learning trends', {\n * limit: 10,\n * minSimilarity: 0.7\n * });\n *\n * for (const article of results) {\n * console.log(`${article.title} (similarity: ${article._similarity})`);\n * }\n * ```\n */\n public async semanticSearch(\n query: string,\n options: {\n field?: string;\n limit?: number;\n minSimilarity?: number;\n where?: Record<string, any>;\n } = {},\n ): Promise<Array<ModelType & { _similarity: number }>> {\n const { field, limit = 10, minSimilarity = 0, where } = options;\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}. ` +\n `Add embeddings config to @smrt() decorator.`,\n );\n }\n\n // Determine which field to search\n const searchField = field || embeddingConfig.fields[0];\n if (!embeddingConfig.fields.includes(searchField)) {\n throw new Error(\n `Field '${searchField}' is not configured for embeddings on ${this._itemClass.name}. ` +\n `Available fields: ${embeddingConfig.fields.join(', ')}`,\n );\n }\n\n // Create embedding provider from the resolved class/project config so\n // query embeddings use the same model as stored object embeddings.\n const provider = new EmbeddingProvider(\n {\n dimensions: embeddingConfig.dimensions,\n provider: embeddingConfig.provider,\n localModel: embeddingConfig.localModel,\n aiModel: embeddingConfig.aiModel,\n fallbackToAI: embeddingConfig.fallbackToAI,\n },\n this.ai,\n );\n\n // Generate embedding for query\n const [queryEmbedding] = await provider.embed(query);\n\n // Find similar embeddings\n return this.findSimilarToEmbedding(queryEmbedding, {\n field: searchField,\n limit,\n minSimilarity,\n where,\n });\n }\n\n /**\n * Find objects similar to a given object\n *\n * Uses stored embeddings to find objects most similar to the provided object.\n *\n * @param object - Object to find similar items for (or object ID)\n * @param options - Search options\n * @param options.field - Specific field to compare (defaults to first embedding field)\n * @param options.limit - Maximum results to return (default: 5)\n * @param options.excludeSelf - Whether to exclude the source object (default: true)\n * @returns Promise resolving to array of similar objects with _similarity score\n *\n * @example\n * ```typescript\n * const article = await articles.get('some-article-id');\n * const similar = await articles.findSimilar(article, {\n * limit: 5,\n * excludeSelf: true\n * });\n * ```\n */\n public async findSimilar(\n object: ModelType | string,\n options: {\n field?: string;\n limit?: number;\n excludeSelf?: boolean;\n } = {},\n ): Promise<Array<ModelType & { _similarity: number }>> {\n const { field, limit = 5, excludeSelf = true } = options;\n\n // Get the object if ID was provided\n let sourceObject: ModelType;\n if (typeof object === 'string') {\n const found = await this.get(object);\n if (!found) {\n throw new Error(`Object not found: ${object}`);\n }\n sourceObject = found;\n } else {\n sourceObject = object;\n }\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}.`,\n );\n }\n\n // Determine which field to use\n const searchField = field || embeddingConfig.fields[0];\n\n // Get the source object's embedding using consistent model name from EmbeddingProvider\n const provider = new EmbeddingProvider(\n {\n dimensions: embeddingConfig.dimensions,\n provider: embeddingConfig.provider,\n localModel: embeddingConfig.localModel,\n aiModel: embeddingConfig.aiModel,\n fallbackToAI: embeddingConfig.fallbackToAI,\n },\n this.ai,\n );\n const model = provider.getModelName();\n\n const storedEmbedding = await EmbeddingStorage.get(\n this.systemDb,\n this._itemClass.name,\n sourceObject.id as string,\n searchField,\n model,\n );\n\n if (!storedEmbedding) {\n throw new Error(\n `No embedding found for object ${sourceObject.id} field '${searchField}'. ` +\n `Generate embeddings first with object.generateEmbeddings().`,\n );\n }\n\n // Find similar using the embedding\n const where = excludeSelf ? { 'id !=': sourceObject.id } : undefined;\n\n return this.findSimilarToEmbedding(storedEmbedding.embedding, {\n field: searchField,\n limit,\n minSimilarity: 0,\n where,\n });\n }\n\n /**\n * Find objects similar to a raw embedding vector\n *\n * Low-level method for finding objects by embedding similarity.\n *\n * @param embedding - Embedding vector to compare against\n * @param options - Search options\n * @param options.field - Field name to search (defaults to first embedding field)\n * @param options.limit - Maximum results to return (default: 10)\n * @param options.minSimilarity - Minimum similarity threshold 0-1 (default: 0)\n * @param options.where - Additional WHERE filters to apply\n * @returns Promise resolving to array of objects with _similarity score\n *\n * @example\n * ```typescript\n * // Using a pre-computed embedding\n * const results = await articles.findSimilarToEmbedding(myEmbedding, {\n * limit: 10,\n * minSimilarity: 0.5\n * });\n * ```\n */\n public async findSimilarToEmbedding(\n embedding: number[],\n options: {\n field?: string;\n limit?: number;\n minSimilarity?: number;\n where?: Record<string, any>;\n } = {},\n ): Promise<Array<ModelType & { _similarity: number }>> {\n const { field, limit = 10, minSimilarity = 0, where } = options;\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}.`,\n );\n }\n\n // Determine which field to search\n const searchField = field || embeddingConfig.fields[0];\n\n // Get model name using EmbeddingProvider for consistency\n const provider = new EmbeddingProvider(\n {\n dimensions: embeddingConfig.dimensions,\n provider: embeddingConfig.provider,\n localModel: embeddingConfig.localModel,\n aiModel: embeddingConfig.aiModel,\n fallbackToAI: embeddingConfig.fallbackToAI,\n },\n this.ai,\n );\n const model = provider.getModelName();\n\n // Resolve storage strategy\n const projectConfig = ObjectRegistry.getProjectEmbeddingConfig();\n const storage = projectConfig?.storage || 'json';\n const vector = storage === 'native' ? this.systemDb.vector : undefined;\n\n // Use unified search method (delegates to native or in-memory)\n const scored = await EmbeddingStorage.searchSimilar(\n this.systemDb,\n this._itemClass.name,\n embedding,\n {\n field: searchField,\n model,\n limit,\n minSimilarity,\n },\n vector,\n );\n\n if (scored.length === 0) {\n return [];\n }\n\n // Load the objects\n const objectIds = scored.map((s) => s.objectId);\n const similarityMap = new Map(\n scored.map((s) => [s.objectId, s.similarity]),\n );\n\n // Build where clause with additional filters\n const whereClause = where\n ? { 'id in': objectIds, ...where }\n : { 'id in': objectIds };\n\n const objects = await this.list({\n where: whereClause as SmrtWhereClause<ModelType>,\n });\n\n // Add similarity scores to objects and sort by similarity\n // We directly assign _similarity to avoid losing class properties when spreading\n const results = objects.map((obj) => {\n (obj as any)._similarity = similarityMap.get(obj.id as string) || 0;\n return obj as ModelType & { _similarity: number };\n });\n\n results.sort((a, b) => b._similarity - a._similarity);\n\n return results;\n }\n\n /**\n * Generate missing embeddings for all objects in the collection\n *\n * Batch generates embeddings for objects that don't have them yet\n * or have stale embeddings.\n *\n * @param options - Generation options\n * @param options.batchSize - Number of objects to process at once (default: 50)\n * @param options.onProgress - Progress callback\n * @returns Promise resolving to generation statistics\n *\n * @example\n * ```typescript\n * const stats = await articles.generateMissingEmbeddings({\n * batchSize: 100,\n * onProgress: ({ completed, total }) => {\n * console.log(`Progress: ${completed}/${total}`);\n * }\n * });\n *\n * console.log(`Generated: ${stats.generated}, Skipped: ${stats.skipped}`);\n * ```\n */\n public async generateMissingEmbeddings(\n options: {\n batchSize?: number;\n onProgress?: (progress: { completed: number; total: number }) => void;\n } = {},\n ): Promise<{ generated: number; skipped: number }> {\n const { batchSize = 50, onProgress } = options;\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}.`,\n );\n }\n\n // Count total objects\n const total = await this.count({});\n let completed = 0;\n let generated = 0;\n let skipped = 0;\n\n // Process in batches\n let offset = 0;\n while (offset < total) {\n const batch = await this.list({ limit: batchSize, offset });\n\n for (const obj of batch) {\n try {\n // Check if embeddings are stale\n const hasStale = await (obj as any).hasStaleEmbeddings();\n if (hasStale) {\n await (obj as any).generateEmbeddings();\n generated++;\n } else {\n skipped++;\n }\n } catch (error) {\n logger.warn(`Failed to generate embeddings for ${obj.id}`, {\n error: error instanceof Error ? error.message : error,\n });\n skipped++;\n }\n\n completed++;\n if (onProgress) {\n onProgress({ completed, total });\n }\n }\n\n offset += batchSize;\n }\n\n return { generated, skipped };\n }\n}\n"],"names":["result","instance"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collection.js","sources":["../src/collection.ts"],"sourcesContent":["import { createLogger } from '@happyvertical/logger';\nimport { buildWhere } from '@happyvertical/sql';\nimport type { SmrtClassOptions } from './class';\nimport { SmrtClass } from './class';\nimport {\n buildQueryCacheKey,\n type CollectionCacheConfig,\n ensureCacheInvalidationListener,\n getCachedRows,\n getCacheGeneration,\n invalidateCollectionCache,\n registerCrossProcessCacheInterest,\n resolveDbCacheKey,\n setCachedRows,\n} from './collection-cache';\nimport { EmbeddingProvider } from './embeddings/provider';\nimport { EmbeddingStorage } from './embeddings/storage';\nimport {\n createInterceptorContext,\n GlobalInterceptors,\n type ListOptions as InterceptorListOptions,\n} from './interceptors';\nimport type { SmrtObject } from './object';\nimport { ObjectRegistry } from './registry';\nimport { verifyPersistenceTable } from './schema/table-verifier';\nimport {\n classnameToTablename,\n fieldsFromClass,\n formatDataJs,\n toCamelCase,\n toSnakeCase,\n} from './utils';\nimport { chunkArray, IN_LIST_CHUNK_SIZE } from './utils/chunk';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Resolve _meta_type in WHERE clause from simple class name to qualified name (Issue #713)\n *\n * This helper function allows queries like { _meta_type: 'Image' } to work correctly\n * when the database stores qualified names like '@happyvertical/smrt-assets:Image'.\n *\n * @param where - The original WHERE clause object\n * @returns Modified WHERE clause with qualified _meta_type, or original if no resolution needed\n */\nfunction resolveMetaTypeInWhere<T extends Record<string, unknown>>(\n where: T | undefined,\n): T | undefined {\n if (!where?._meta_type || typeof where._meta_type !== 'string') {\n return where;\n }\n\n const metaTypeValue = where._meta_type as string;\n\n // Only resolve if it's a simple class name (no ':' means not qualified)\n if (metaTypeValue.includes(':')) {\n return where;\n }\n\n const registeredClass = ObjectRegistry.getClass(metaTypeValue);\n if (registeredClass?.qualifiedName) {\n return {\n ...where,\n _meta_type: registeredClass.qualifiedName,\n };\n }\n\n return where;\n}\n\n/**\n * Keys from SmrtObject and SmrtClass that should not appear as user-facing\n * create/update input fields. These are framework-internal properties.\n */\ntype SmrtInternalKeys =\n | keyof SmrtClass\n | '_tableName'\n | '_loadedRelationships'\n | '_id'\n | '_slug'\n | '_context'\n | '_ai'\n | '_fs'\n | '_db'\n | '_className'\n | 'options'\n | '_skipLoad'\n | '_skipAutoEmbeddings'\n | '_extractingFields'\n | 'initialize'\n | 'save'\n | 'delete'\n | 'loadFromId'\n | 'loadFromSlug'\n | 'is'\n | 'do'\n | 'toJSON'\n | 'transformJSON';\n\n/**\n * Input type for `collection.create()`. Constrains input to valid model fields\n * while preserving backwards compatibility via an index signature escape hatch.\n *\n * Provides IDE autocompletion for known fields from the model class while still\n * accepting arbitrary keys for dynamic usage patterns (e.g., STI meta fields).\n *\n * @example\n * ```typescript\n * // IDE will suggest: name, price, quantity, categoryId, etc.\n * await productCollection.create({\n * name: 'Widget',\n * price: 9.99,\n * _meta_type: 'SpecialProduct', // STI discriminator\n * });\n * ```\n */\nexport type SmrtCreateInput<T extends SmrtObject> = Partial<\n Omit<\n {\n [K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K];\n },\n SmrtInternalKeys\n >\n> & {\n /** STI discriminator for polymorphic creation */\n _meta_type?: string;\n /** Skip database loading (framework internal) */\n _skipLoad?: boolean;\n /** Skip save-time embedding auto-generation (framework internal) */\n _skipAutoEmbeddings?: boolean;\n /** Allow arbitrary additional fields for dynamic usage */\n [key: string]: unknown;\n};\n\n/**\n * WHERE clause type for `collection.list()`.\n *\n * Uses `Record<string, unknown>` because WHERE keys often include operator\n * suffixes (e.g., `'price >'`, `'name like'`, `'status in'`) which can't be\n * expressed as mapped types from model properties. Runtime validation in\n * `convertWhereKeys()` handles field and operator checking.\n */\nexport type SmrtWhereClause<T extends SmrtObject> = Record<string, unknown>;\n\n/**\n * Configuration options for SmrtCollection\n */\nexport interface SmrtCollectionOptions extends SmrtClassOptions {}\n\n/**\n * Typed CRUD collection for a specific `SmrtObject` subclass.\n *\n * Each concrete collection pairs with exactly one model class via the required\n * `static readonly _itemClass` property. Use the static `create()` factory —\n * not `new` — to get a fully initialized, ready-to-query instance.\n *\n * Key capabilities:\n * - **Query**: `list()`, `get()`, `count()`, `query()` (raw SQL), `listByIds()`\n * - **Mutation**: `create()`, `getOrUpsert()`, `delete()`\n * - **Eager loading**: pass `include: ['fieldName']` to `list()` to avoid N+1 queries\n * - **STI support**: automatically filters by `_meta_type` for child collections;\n * polymorphically hydrates the correct subclass when listing/getting\n * - **Interceptors**: all read/write paths run `GlobalInterceptors` hooks\n * (used by multi-tenancy, audit logging, etc.)\n *\n * @typeParam ModelType - The `SmrtObject` subclass managed by this collection\n *\n * @example\n * ```typescript\n * @smrt()\n * class Product extends SmrtObject {\n * name: string = '';\n * price: number = 0.0;\n * }\n *\n * @smrt()\n * class Products extends SmrtCollection<Product> {\n * static readonly _itemClass = Product;\n * }\n *\n * const products = await Products.create({ db: myDb });\n * const widget = await products.create({ name: 'Widget', price: 9.99 });\n * const all = await products.list({ where: { 'price >': 5 }, orderBy: 'price ASC' });\n * ```\n */\nexport class SmrtCollection<ModelType extends SmrtObject> extends SmrtClass {\n /**\n * Cached fields for sync access during queries.\n * Populated during create() to avoid async getFields() calls on every query.\n * @private\n */\n private _cachedFields: Record<string, any> | null = null;\n\n private getRegisteredItemClass() {\n return (\n ObjectRegistry.getClassByConstructor(this._itemClass as any) ||\n ObjectRegistry.getClass(this._itemClass.name)\n );\n }\n\n private getResolvedItemClassName(): string {\n return this.getRegisteredItemClass()?.name || this._itemClass.name;\n }\n\n private getResolvedItemQualifiedName(): string {\n const registered = this.getRegisteredItemClass();\n return (\n registered?.qualifiedName || registered?.name || this._itemClass.name\n );\n }\n\n /**\n * Convert WHERE clause field names from camelCase to snake_case while preserving operators.\n * Validates operators and field names to prevent SQL injection and invalid queries.\n *\n * Uses cached fields for sync access (issue #663) to avoid async overhead on every query.\n *\n * @param where - WHERE clause object with camelCase field names\n * @returns WHERE clause object with snake_case field names\n * @private\n *\n * @example\n * ```typescript\n * // Input: { 'typeId': 'foo', 'categoryId >': 100 }\n * // Output: { 'type_id': 'foo', 'category_id >': 100 }\n * ```\n */\n private convertWhereKeys(where: Record<string, any>): Record<string, any> {\n // Whitelist of allowed SQL operators\n const VALID_OPERATORS = [\n '=',\n '>',\n '<',\n '>=',\n '<=',\n '!=',\n 'in',\n 'not in',\n 'like',\n 'contains',\n ];\n\n // Get schema fields for validation (sync from cache)\n const fields = this.getFieldsSync();\n const validFieldNames = new Set(\n Object.keys(fields).map((f) => toSnakeCase(f)),\n );\n\n // Security (#1540): collect snake_case names of `@field({ sensitive: true })`\n // columns so they can't be used as `where` filter keys. Filtering on a\n // secret column turns the generated REST/MCP `where` surface into a value\n // oracle (e.g. `apiSecret[like]=sk-%`), exfiltrating the secret one\n // character at a time even though it's excluded from serialization.\n const sensitiveFieldNames = new Set<string>();\n const collectSensitive = (\n fieldMap: Record<string, any> | Map<string, any>,\n ) => {\n const entries =\n fieldMap instanceof Map ? fieldMap.entries() : Object.entries(fieldMap);\n for (const [fieldName, def] of entries) {\n if (def && (def.sensitive === true || def._meta?.sensitive === true)) {\n // Store both the snake_case column form and the raw property name so\n // STI `@meta` fields probed via `_meta_data.<prop>` JSON paths (which\n // keep the camelCase key) are also rejected.\n sensitiveFieldNames.add(toSnakeCase(fieldName));\n sensitiveFieldNames.add(fieldName);\n }\n }\n };\n collectSensitive(fields);\n\n // Add standard SMRT fields that are always valid\n validFieldNames.add('id');\n validFieldNames.add('slug');\n validFieldNames.add('context');\n validFieldNames.add('created_at');\n validFieldNames.add('updated_at');\n\n // Add STI discriminator field for polymorphic queries.\n // R5-canon: use the qualified item name as the lookup key so a\n // same-simple-name class in another package can't yield the wrong\n // tableStrategy.\n const itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n if (tableStrategy === 'sti') {\n // Add both with and without leading underscore (toSnakeCase strips leading _)\n validFieldNames.add('_meta_type');\n validFieldNames.add('meta_type');\n validFieldNames.add('_meta_data');\n validFieldNames.add('meta_data');\n\n // Issue #869: For STI classes, also include fields from all ancestor classes\n // This ensures child class collections can filter by parent class fields.\n //\n // inheritanceChain entries are qualified names (when the\n // registration has one). Compare self against the qualified form;\n // the framework-base sentinels stay as simple names since they're\n // never registered. The simple-name `itemClassName` is also checked\n // as a defensive fall-through for unqualified registrations.\n const inheritanceChain =\n ObjectRegistry.getInheritanceChain(itemQualifiedName);\n for (const ancestorName of inheritanceChain) {\n if (\n ancestorName === 'SmrtObject' ||\n ancestorName === 'SmrtClass' ||\n ancestorName === itemQualifiedName ||\n ancestorName === itemClassName\n ) {\n continue; // Skip framework base classes and self (already included)\n }\n const ancestorFields = ObjectRegistry.getFields(ancestorName);\n for (const fieldName of ancestorFields.keys()) {\n validFieldNames.add(toSnakeCase(fieldName));\n }\n collectSensitive(ancestorFields);\n }\n\n // Security (#1540): a base collection serializes (and so must protect)\n // STI descendant fields too — a child's `@meta` sensitive field lives in\n // `_meta_data` and would otherwise be probe-able via `_meta_data.<prop>`\n // from the base collection. Mirror toJSON()/getSensitiveFieldNames().\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase) {\n for (const descendant of ObjectRegistry.getDescendants(stiBase)) {\n collectSensitive(ObjectRegistry.getFields(descendant));\n }\n }\n }\n\n // Issue #869: If no fields are registered (no manifest for test classes),\n // skip field validation. Invalid fields will fail at SQL level instead.\n // This commonly happens for inline test classes decorated with @smrt().\n const hasRegisteredFields = Object.keys(fields).length > 0;\n const skipFieldValidation = !hasRegisteredFields;\n\n const converted: Record<string, any> = {};\n\n // Check for prototype pollution attempts using own properties only\n // __proto__ is a special property that doesn't show up in Object.entries()\n // but can still be checked with hasOwnProperty\n if (\n Object.hasOwn(where, '__proto__') ||\n Object.hasOwn(where, 'constructor') ||\n Object.hasOwn(where, 'prototype')\n ) {\n throw new Error(\n `Invalid WHERE clause: Prototype pollution attempts are not allowed. ` +\n `Detected dangerous properties in WHERE clause.`,\n );\n }\n\n for (const [key, value] of Object.entries(where)) {\n // Split field name and operator (e.g., \"typeId >\" → [\"typeId\", \">\"])\n const parts = key.trim().split(/\\s+/);\n const fieldName = parts[0];\n const operator = parts.slice(1).join(' ') || '=';\n\n // Check for dangerous prototype pollution field names\n if (\n fieldName === '__proto__' ||\n fieldName === 'constructor' ||\n fieldName === 'prototype' ||\n fieldName.includes('__proto__') ||\n fieldName.includes('constructor') ||\n fieldName.includes('prototype')\n ) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Prototype pollution attempts are not allowed.`,\n );\n }\n\n // Support dot-notation for JSON path queries (e.g., 'metadata.userId')\n // Validate against the first segment (the column name), pass full path through\n const dotIndex = fieldName.indexOf('.');\n const baseFieldName =\n dotIndex >= 0 ? fieldName.substring(0, dotIndex) : fieldName;\n const jsonPath = dotIndex >= 0 ? fieldName.substring(dotIndex) : null;\n\n // Convert base field name to snake_case, preserving leading underscores\n const snakeBaseFieldName = baseFieldName.startsWith('_')\n ? `_${toSnakeCase(baseFieldName.slice(1))}`\n : toSnakeCase(baseFieldName);\n\n // Full snake_case field name with JSON path preserved\n const snakeFieldName = jsonPath\n ? `${snakeBaseFieldName}${jsonPath}`\n : snakeBaseFieldName;\n\n // Security (#1379): the field identifier — base column plus any\n // dot-notation JSON-path segments — is interpolated UNPARAMETERIZED into\n // the SQL field position by the downstream query builder. The manifest\n // whitelist below only validates the base column, and is skipped entirely\n // when a class has no registered fields (skipFieldValidation), so without\n // this guard a crafted key such as\n // `metadata.x))/**/UNION/**/SELECT/**/secret/**/FROM/**/users--`\n // (SQL comments substituting for blocked whitespace) injects arbitrary SQL\n // via the JSON-path suffix — remotely reachable through the generated\n // REST/MCP `where` surfaces and able to defeat the tenant filter. Enforce\n // that the whole identifier is a strict dot-separated identifier so no\n // parens/quotes/operators/whitespace can survive, regardless of whether\n // the manifest whitelist below runs.\n if (!/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z0-9_]+)*$/.test(snakeFieldName)) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Field names must be identifiers (letters, digits, underscore) ` +\n `with optional dot-separated JSON-path segments.`,\n );\n }\n\n // Security (#1540): reject filters that target a sensitive column. This\n // runs before operator/field validation so the secret value-probing\n // oracle is closed even for otherwise-valid operators. We reject when the\n // base column itself is sensitive, OR when an STI `@meta` sensitive field\n // is probed via a `_meta_data.<prop>` JSON path (the prop keeps its\n // camelCase name). The JSON-path check is scoped to the `_meta_data`\n // column so legitimate filters on unrelated JSON fields (e.g.\n // `metadata.apiSecret` on a non-sensitive `metadata` column) aren't\n // falsely rejected.\n // Use the NORMALIZED column name so camelCase aliases (`_metaData`,\n // `metaData`) that also resolve to the `_meta_data` column are scoped in —\n // checking raw `baseFieldName` here let `_metaData.apiSecret` slip past\n // the segment check while still resolving to the meta column.\n const baseIsMetaData =\n snakeBaseFieldName === '_meta_data' ||\n snakeBaseFieldName === 'meta_data';\n const metaPathTargetsSensitive =\n baseIsMetaData &&\n !!jsonPath &&\n jsonPath\n .split('.')\n .filter(Boolean)\n .some(\n (segment) =>\n sensitiveFieldNames.has(segment) ||\n sensitiveFieldNames.has(toSnakeCase(segment)),\n );\n if (\n sensitiveFieldNames.has(snakeBaseFieldName) ||\n metaPathTargetsSensitive\n ) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Filtering on sensitive fields is not allowed.`,\n );\n }\n\n // Auto-detect IN operator when value is an array without explicit operator\n const effectiveOperator =\n operator === '=' && Array.isArray(value) ? 'in' : operator;\n\n // Validate operator\n if (!VALID_OPERATORS.includes(effectiveOperator)) {\n throw new Error(\n `Invalid WHERE clause operator: '${operator}'. ` +\n `Valid operators: ${VALID_OPERATORS.join(', ')}`,\n );\n }\n\n // Validate field name exists in schema (validate base column name only)\n // Issue #869: Skip validation when no fields are registered (manifest-less test classes)\n if (!skipFieldValidation && !validFieldNames.has(snakeBaseFieldName)) {\n throw new Error(\n `Invalid WHERE clause field: '${fieldName}'. ` +\n `Field does not exist on ${itemClassName}. ` +\n `Valid fields: ${Array.from(validFieldNames).sort().join(', ')}`,\n );\n }\n\n // Validate operator-specific value types\n if (\n (effectiveOperator === 'in' || effectiveOperator === 'not in') &&\n !Array.isArray(value)\n ) {\n throw new Error(\n `WHERE clause operator '${effectiveOperator}' requires an array value for field '${fieldName}', ` +\n `got ${typeof value}`,\n );\n }\n\n // Reject empty arrays for IN/NOT IN operators (generates invalid SQL)\n if (\n (effectiveOperator === 'in' || effectiveOperator === 'not in') &&\n Array.isArray(value) &&\n value.length === 0\n ) {\n throw new Error(\n `WHERE clause operator '${effectiveOperator}' requires a non-empty array for field '${fieldName}'. ` +\n `Use listByIds([]) for graceful empty array handling.`,\n );\n }\n\n if (effectiveOperator === 'like' && typeof value !== 'string') {\n throw new Error(\n `WHERE clause operator 'like' requires a string value for field '${fieldName}', ` +\n `got ${typeof value}`,\n );\n }\n\n // Reconstruct key with operator\n const newKey =\n effectiveOperator === '='\n ? snakeFieldName\n : `${snakeFieldName} ${effectiveOperator}`;\n\n converted[newKey] = value;\n }\n\n return converted;\n }\n\n /**\n * Gets the class constructor for items in this collection\n */\n protected get _itemClass(): (new (\n options: any,\n ) => ModelType) & {\n create(options: any): ModelType | Promise<ModelType>;\n } {\n const ctor = this.constructor as {\n readonly _itemClass?: (new (\n options: any,\n ) => ModelType) & {\n create(options: any): ModelType | Promise<ModelType>;\n };\n };\n if (!ctor._itemClass) {\n const className = this.constructor.name;\n const errorMessage = [\n `Collection \"${className}\" must define a static _itemClass property.`,\n '',\n 'Example:',\n ` class ${className} extends SmrtCollection<YourItemClass> {`,\n ' static readonly _itemClass = YourItemClass;',\n ' }',\n '',\n 'Make sure your item class is imported and defined before the collection class.',\n ].join('\\n');\n\n throw new Error(errorMessage);\n }\n return ctor._itemClass;\n }\n\n /**\n * Static reference to the item class constructor\n */\n static readonly _itemClass: any;\n\n /**\n * Validates that the collection is properly configured\n * Call this during development to catch configuration issues early\n */\n static validate(): void {\n if (!SmrtCollection._itemClass) {\n const className = SmrtCollection.name;\n const errorMessage = [\n `Collection \"${className}\" is missing required static _itemClass property.`,\n '',\n 'Fix by adding:',\n ` class ${className} extends SmrtCollection<YourItemClass> {`,\n ' static readonly _itemClass = YourItemClass;',\n ' }',\n ].join('\\n');\n throw new Error(errorMessage);\n }\n\n // Validate that _itemClass has required methods\n if (typeof SmrtCollection._itemClass !== 'function') {\n throw new Error(\n `Collection \"${SmrtCollection.name}\"._itemClass must be a constructor function`,\n );\n }\n\n // Check if it has a create method (static or prototype)\n const hasCreateMethod =\n typeof SmrtCollection._itemClass.create === 'function' ||\n typeof SmrtCollection._itemClass.prototype?.create === 'function';\n\n if (!hasCreateMethod) {\n logger.warn(\n `Collection \"${SmrtCollection.name}\"._itemClass should have a create() method for optimal functionality`,\n );\n }\n }\n\n /**\n * Database table name for this collection\n */\n public _tableName!: string;\n\n /**\n * Creates a new SmrtCollection instance\n *\n * @deprecated Use the static create() factory method instead\n * @param options - Configuration options\n */\n constructor(options: SmrtCollectionOptions = {}) {\n super(options);\n\n // Auto-register the collection if it's not the base SmrtCollection and has an _itemClass\n if (\n this.constructor !== SmrtCollection &&\n (this.constructor as any)._itemClass\n ) {\n const itemClass = (this.constructor as any)._itemClass;\n const itemClassName =\n ObjectRegistry.getClassByConstructor(itemClass)?.name || itemClass.name;\n ObjectRegistry.registerCollection(itemClassName, this.constructor as any);\n }\n }\n\n /**\n * Factory method — the recommended way to instantiate a collection.\n *\n * Creates the collection instance, calls `initialize()`, and pre-populates\n * the field cache used by synchronous query helpers.\n *\n * Pass the same `options` object you received in a `SmrtObject` constructor\n * or a `SvelteKit` load function to share the database connection.\n *\n * @param options - Database, AI, filesystem, and other configuration options.\n * At minimum, provide `db` (or `persistence`) to connect to a database.\n * @returns A fully initialized, ready-to-query collection instance\n *\n * @example\n * ```typescript\n * // Share a DB connection from a SvelteKit load function\n * const products = await Products.create(event.locals.smrtOptions);\n *\n * // Explicit configuration\n * const products = await Products.create({\n * db: myDb,\n * ai: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY },\n * });\n * ```\n */\n static async create<T extends SmrtCollection<any>>(\n this: new (\n options?: SmrtCollectionOptions,\n ) => T,\n options: SmrtClassOptions = {},\n ): Promise<T> {\n // Extract only collection-compatible options from broader SmrtClassOptions\n const {\n _className,\n db,\n persistence, // Also extract persistence alias\n ai,\n fs,\n logging,\n metrics,\n pubsub,\n sanitization,\n signals,\n } = options;\n\n const collectionOptions: SmrtCollectionOptions = {\n _className,\n db,\n persistence, // Pass persistence through so initialize() can map it to db\n ai,\n fs,\n logging,\n metrics,\n pubsub,\n sanitization,\n signals,\n };\n\n // Defense-in-depth: SmrtJunction subclasses MUST have their ITEM class\n // registered with ObjectRegistry — that's where the field metadata,\n // tableName, and conflictColumns come from, which byLeft/byRight/\n // attach/detach/setLinks all depend on. The scanner is supposed to\n // catch every junction subclass via the FRAMEWORK_BASE_CLASSES list\n // in packages/scanner, but if a future refactor adds a new abstract\n // junction base without updating that list, or if a class is loaded\n // outside the normal manifest pipeline, we'd silently fall through to\n // empty-field-metadata behavior (the issue #1132 class of bug). Fail\n // loudly here instead of producing wrong results later.\n //\n // We check the ITEM class registration (not the collection class) —\n // collection constructors are stored separately via\n // registerCollection(itemClassName, ctor), not in the classes map.\n if ((this as any)._isJunctionBase === true) {\n const itemCtor = (this as any)._itemClass;\n const itemRegistered =\n itemCtor && ObjectRegistry.getClassByConstructor(itemCtor);\n if (!itemRegistered) {\n throw new Error(\n `SmrtJunction subclass \"${this.name}\" has no registered item class. ` +\n `The scanner likely didn't pick up its model — usually because the ` +\n `consuming package's manifest doesn't include \"${itemCtor?.name ?? '<unknown>'}\". ` +\n `Check that the scanner's FRAMEWORK_BASE_CLASSES ` +\n `(packages/scanner/src/inheritance-resolver.ts) recognizes every ` +\n `framework abstract base in your inheritance chain, that the package ` +\n `has been built (manifest.json present in dist/), and that runtime ` +\n `manifest loading (__smrt-register__.ts) runs before any junction ` +\n `is instantiated.`,\n );\n }\n }\n\n // Create instance using protected constructor\n const instance = new this(collectionOptions);\n\n // Perform async initialization\n await instance.initialize();\n\n // Cache fields for sync access during queries (issue #663)\n // This eliminates async getFields() calls on every query\n const fields = await instance.getFields();\n instance._cachedFields = fields;\n\n // Return fully initialized instance\n return instance;\n }\n\n /**\n * Async initialization hook for the collection.\n *\n * Called automatically by the static `create()` factory. In most cases you\n * do not need to call this directly — use `create()` instead.\n *\n * Runtime schema checks are deferred until a query actually touches the\n * backing table. This keeps collection construction safe during SSR and\n * import-time module evaluation without mutating application schema.\n *\n * @returns This instance (enables chaining)\n */\n public async initialize(): Promise<this> {\n await super.initialize();\n\n // Defer table verification until the first real query. That keeps\n // construction lightweight while ensuring runtime never auto-creates app\n // tables behind the scenes.\n\n return this;\n }\n\n /**\n * Verify that the collection's backing table exists before running a query.\n *\n * This is a fail-fast check only. It does not create or alter schema.\n */\n public async ensureStorageReady(): Promise<void> {\n await verifyPersistenceTable(\n this.db,\n this.tableName,\n this.getResolvedItemClassName(),\n );\n }\n\n /**\n * Find a single record by criteria (convenience method - delegates to get())\n *\n * @param options - Query options with where clause\n * @returns Promise resolving to the object or null if not found\n *\n * @example\n * ```typescript\n * const councils = await Councils.create({ persistence: { type: 'sql', url: 'db.sqlite' } });\n * const council = await councils.findOne({ where: { name: 'Example Council' } });\n * ```\n */\n public async findOne(options: {\n where: Record<string, any>;\n }): Promise<ModelType | null> {\n return await this.get(options.where);\n }\n\n /**\n * Find a record by ID (convenience method - delegates to get())\n *\n * @param id - Record ID to find (string or Field instance)\n * @returns Promise resolving to the object or null if not found\n *\n * @example\n * ```typescript\n * const councils = await Councils.create({ persistence: { type: 'sql', url: 'db.sqlite' } });\n * const council = await councils.findById('uuid-123');\n *\n * // Also works with Field instances (e.g., from foreignKey fields)\n * const meeting = new Meeting({ councilId: 'uuid-123' });\n * const council = await councils.findById(meeting.councilId);\n * ```\n */\n public async findById(id: string): Promise<ModelType | null> {\n return await this.get(id);\n }\n\n /**\n * Find all records matching criteria (convenience method - delegates to list())\n *\n * @param options - Query options (where, orderBy, limit, etc.)\n * @returns Promise resolving to array of objects\n *\n * @example\n * ```typescript\n * const councils = await Councils.create({ persistence: { type: 'sql', url: 'db.sqlite' } });\n * const active = await councils.findAll({ where: { status: 'active' } });\n * ```\n */\n public async findAll(\n options: {\n where?: SmrtWhereClause<ModelType>;\n orderBy?: string | string[];\n limit?: number;\n offset?: number;\n include?: string[];\n } = {},\n ): Promise<ModelType[]> {\n return await this.list(options);\n }\n\n /**\n * Find multiple objects by their IDs in a single query.\n *\n * This is a convenience method that avoids N+1 queries when you have\n * a list of IDs and need to fetch the corresponding records.\n *\n * @param ids - Array of UUIDs to fetch\n * @returns Promise resolving to array of objects (order not guaranteed)\n *\n * @example\n * ```typescript\n * const profiles = await profileCollection.listByIds(['id1', 'id2', 'id3']);\n * ```\n */\n public async listByIds(ids: string[]): Promise<ModelType[]> {\n if (ids.length === 0) return [];\n return this.list({ where: { id: ids } });\n }\n\n /**\n * Resolve the effective cache config for a read (issue #1498).\n *\n * Per-call options win: `false` forces a fresh read, `{ ttl }` enables\n * caching for this call. Otherwise falls back to the model-level\n * `@smrt({ cache })` config (inheritance-aware). Returns undefined when\n * the read should go straight to the database — the default.\n */\n private resolveReadCacheConfig(\n perCall?: CollectionCacheConfig | false,\n ): CollectionCacheConfig | undefined {\n if (perCall === false) return undefined;\n if (perCall) return perCall.ttl > 0 ? perCall : undefined;\n return ObjectRegistry.resolveCollectionCacheConfig(\n this.getResolvedItemQualifiedName(),\n );\n }\n\n /**\n * Execute a SELECT, optionally through the collection read cache.\n *\n * The cache key is the final SQL + bound parameters — computed after\n * interceptors (tenancy filters) and STI discriminators are applied, so\n * differently-scoped queries can never share an entry. Cached values are\n * raw rows; hydration and read interceptors still run on every call.\n */\n private async queryRowsWithCache(\n sql: string,\n params: unknown[],\n cacheConfig: CollectionCacheConfig | undefined,\n ): Promise<Record<string, any>[]> {\n if (!cacheConfig) {\n const result = await this.db.query(sql, ...params);\n return result.rows;\n }\n\n const dbKey = resolveDbCacheKey(this.db);\n const queryKey = buildQueryCacheKey(sql, params);\n\n // Start consuming peer invalidations before the first cached read so\n // a remote write can't go unseen for longer than necessary, and record\n // table-scoped interest so this process's own writes broadcast even\n // when the crossProcess opt-in only exists at the call site.\n if (cacheConfig.crossProcess) {\n ensureCacheInvalidationListener(this.db);\n registerCrossProcessCacheInterest(dbKey, this.tableName);\n }\n\n const cached = getCachedRows(dbKey, this.tableName, queryKey);\n if (cached) {\n return cached;\n }\n\n // Capture the table's invalidation generation BEFORE the round-trip; if a\n // concurrent write invalidates while this SELECT is in flight, setCachedRows\n // sees the bumped generation and drops the now-stale result instead of\n // caching it for the full TTL.\n const generation = getCacheGeneration(dbKey, this.tableName);\n const result = await this.db.query(sql, ...params);\n setCachedRows(\n dbKey,\n this.tableName,\n queryKey,\n result.rows,\n cacheConfig.ttl,\n generation,\n );\n return result.rows;\n }\n\n /**\n * Retrieves a single object from the collection by ID, slug, or a custom filter.\n *\n * Filter resolution:\n * - UUID string → `WHERE id = ?`\n * - Non-UUID string → `WHERE slug = ? AND context = ''`\n * - Object → `WHERE <key> = <value> [AND ...]`\n *\n * For STI child collections, an `AND _meta_type = '<qualifiedName>'` clause is\n * automatically appended so you only receive the correct subclass.\n *\n * Runs `beforeGet` / `afterGet` interceptors (used by multi-tenancy, etc.).\n *\n * @param filter - UUID string, slug string, or a WHERE conditions object\n * @param options.cache - Opt-in read-through cache for this call\n * (`{ ttl }` in milliseconds), or `false` to force a fresh read when the\n * model opted in via `@smrt({ cache })`. Defaults to the model config.\n * @returns The matching object instance, or `null` if not found\n *\n * @example\n * ```typescript\n * // By UUID\n * const product = await products.get('550e8400-e29b-41d4-a716-446655440000');\n *\n * // By slug\n * const product = await products.get('my-widget');\n *\n * // By custom filter\n * const product = await products.get({ sku: 'WID-001' });\n *\n * // Cached read (memoized for 60s, invalidated on writes)\n * const product = await products.get('my-widget', { cache: { ttl: 60_000 } });\n * ```\n *\n * @see {@link list} for multiple results\n * @see {@link findById} / {@link findOne} for convenience aliases\n */\n public async get(\n filter: string | Record<string, any>,\n options: { cache?: CollectionCacheConfig | false } = {},\n ): Promise<ModelType | null> {\n await this.ensureStorageReady();\n const itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n\n // Execute beforeGet interceptors (e.g., tenancy validation)\n const interceptorContext = createInterceptorContext(\n itemClassName,\n 'get',\n this.constructor.name,\n );\n const interceptedFilter = await GlobalInterceptors.executeBeforeGet(\n itemClassName,\n filter,\n interceptorContext,\n );\n\n let where =\n typeof interceptedFilter === 'string'\n ? /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(\n interceptedFilter,\n )\n ? { id: interceptedFilter }\n : { slug: interceptedFilter, context: '' }\n : interceptedFilter;\n\n // Fix for issue #386: Add _meta_type filter for STI child collections.\n // R5-canon: use the qualified item name as the LOOKUP KEY too, not\n // just for the comparison — passing the simple `itemClassName` can\n // resolve to another package's same-simple-name class via\n // `findClass`'s multi-strategy lookup, which would yield the WRONG\n // table-strategy / STI base.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n const isSTI = tableStrategy === 'sti';\n\n if (isSTI) {\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase && stiBase !== itemQualifiedName) {\n where = {\n _meta_type: itemQualifiedName,\n ...where,\n };\n }\n }\n\n // convertWhereKeys is now sync (issue #663) - no await needed\n const convertedWhere = this.convertWhereKeys(where);\n const { sql: whereSql, values: whereValues } = buildWhere(convertedWhere);\n\n const fullSQL = `SELECT * FROM ${this.tableName} ${whereSql}`;\n const rows = await this.queryRowsWithCache(\n fullSQL,\n whereValues,\n this.resolveReadCacheConfig(options.cache),\n );\n\n if (!rows?.[0]) {\n // Execute afterGet with null result\n // Explicitly specify type parameter since TypeScript can't infer from null\n return await GlobalInterceptors.executeAfterGet<ModelType>(\n itemClassName,\n null,\n interceptorContext,\n );\n }\n\n const fields = this.getFieldsSync();\n const instance = await this.hydrateResultRow(rows[0], fields, isSTI);\n\n // Execute afterGet interceptors (e.g., tenant validation)\n return await GlobalInterceptors.executeAfterGet(\n itemClassName,\n instance,\n interceptorContext,\n );\n }\n\n /**\n * Lists records from the collection with flexible filtering options\n *\n * @param options - Query options object\n * @param options.where - Record of conditions to filter results. Each key can include an operator\n * separated by a space (e.g., 'price >', 'name like'). Default operator is '='.\n * @param options.offset - Number of records to skip\n * @param options.limit - Maximum number of records to return\n * @param options.orderBy - Field(s) to order results by, with optional direction\n *\n * @example\n * ```typescript\n * // Find active products priced between $100-$200\n * await collection.list({\n * where: {\n * 'price >': 100,\n * 'price <=': 200,\n * 'status': 'active', // equals operator is default\n * 'category in': ['A', 'B', 'C'], // IN operator for arrays\n * 'name like': '%shirt%', // LIKE for pattern matching\n * 'deleted_at !=': null // exclude deleted items\n * },\n * limit: 10,\n * offset: 0\n * });\n *\n * // Find users matching pattern but not in specific roles\n * await users.list({\n * where: {\n * 'email like': '%@company.com',\n * 'active': true,\n * 'role in': ['guest', 'blocked'],\n * 'last_login <': lastMonth\n * }\n * });\n * ```\n *\n * @returns Promise resolving to an array of model instances\n */\n public async list(\n options: {\n where?: SmrtWhereClause<ModelType>;\n offset?: number;\n limit?: number;\n orderBy?: string | string[];\n /**\n * Relationships to eagerly load (avoids N+1 query problem)\n * @example\n * ```typescript\n * // Load orders with their customers pre-loaded\n * const orders = await orderCollection.list({\n * include: ['customerId']\n * });\n * // Access customer without additional query\n * orders[0].getRelated('customerId');\n * ```\n */\n include?: string[];\n /**\n * Opt-in read-through cache for this call (issue #1498).\n *\n * Pass `{ ttl }` (milliseconds) to memoize the result rows keyed by\n * the final query shape. Mutations through SMRT invalidate the\n * table's entries automatically. Pass `false` to force a fresh read\n * when the model opted in via `@smrt({ cache })`. Defaults to the\n * model-level config; uncached when neither is set.\n *\n * @example\n * ```typescript\n * const published = await resumes.list({\n * where: { status: 'published' },\n * cache: { ttl: 60_000 },\n * });\n * ```\n */\n cache?: CollectionCacheConfig | false;\n } = {},\n ): Promise<ModelType[]> {\n await this.ensureStorageReady();\n const itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n\n // Execute beforeList interceptors (e.g., tenancy filtering)\n const interceptorContext = createInterceptorContext(\n itemClassName,\n 'list',\n this.constructor.name,\n );\n const interceptedOptions =\n (await GlobalInterceptors.executeBeforeList(\n itemClassName,\n options as InterceptorListOptions,\n interceptorContext,\n )) ??\n (options as InterceptorListOptions | undefined) ??\n {};\n\n let { where, offset, limit, orderBy } = interceptedOptions;\n\n // STI: Child collections should automatically filter by _meta_type.\n // R5-canon: qualified-key lookup so a same-simple-name class in\n // another package can't yield the wrong table strategy / STI base.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n const isSTI = tableStrategy === 'sti';\n\n if (isSTI) {\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase && stiBase !== itemQualifiedName) {\n where = {\n _meta_type: itemQualifiedName,\n ...(where || {}),\n };\n }\n }\n\n // Resolve _meta_type to qualified name if user provided simple class name (Issue #713)\n where = resolveMetaTypeInWhere(where);\n\n // convertWhereKeys is now sync (issue #663) - no await needed\n const convertedWhere = this.convertWhereKeys(where || {});\n const { sql: whereSql, values: whereValues } = buildWhere(convertedWhere);\n\n let orderBySql = '';\n if (orderBy) {\n orderBySql = ' ORDER BY ';\n const orderByItems = Array.isArray(orderBy) ? orderBy : [orderBy];\n\n orderBySql += orderByItems\n .map((item) => {\n const [field, direction = 'ASC'] = item.split(' ');\n\n // Validate field name\n if (!/^[a-zA-Z0-9_]+$/.test(field)) {\n throw new Error(`Invalid field name for ordering: ${field}`);\n }\n\n // Validate direction\n const normalizedDirection = direction.toUpperCase();\n if (normalizedDirection !== 'ASC' && normalizedDirection !== 'DESC') {\n throw new Error(\n `Invalid sort direction: ${direction}. Must be ASC or DESC.`,\n );\n }\n\n // Convert field name to snake_case\n const snakeField = toSnakeCase(field);\n return `${snakeField} ${normalizedDirection}`;\n })\n .join(', ');\n }\n\n let limitOffsetSql = '';\n const limitOffsetValues: (number | undefined)[] = [];\n let paramIndex = whereValues.length + 1;\n\n if (limit !== undefined) {\n limitOffsetSql += ` LIMIT $${paramIndex++}`;\n limitOffsetValues.push(limit);\n }\n\n if (offset !== undefined) {\n limitOffsetSql += ` OFFSET $${paramIndex++}`;\n limitOffsetValues.push(offset);\n }\n\n const sql = `SELECT * FROM ${this.tableName} ${whereSql} ${orderBySql} ${limitOffsetSql}`;\n const params = [...whereValues, ...limitOffsetValues];\n\n // Resolve the cache preference from the post-interceptor options —\n // beforeList interceptors may legally set or clear `cache` (e.g. force\n // read-through on admin paths, enable caching per tenant).\n const rows = await this.queryRowsWithCache(\n sql,\n params,\n this.resolveReadCacheConfig(\n (interceptedOptions as { cache?: CollectionCacheConfig | false }).cache,\n ),\n );\n const fields = this.getFieldsSync();\n\n // STI: Hydrate instances polymorphically based on _meta_type\n // Reuse tableStrategy and isSTI from earlier in the function\n const instances = await Promise.all(\n rows.map((item: any) => this.hydrateResultRow(item, fields, isSTI)),\n );\n\n // Eager load specified relationships\n if (interceptedOptions.include && interceptedOptions.include.length > 0) {\n // Cast to ModelType[] - instances are guaranteed to be subtypes of ModelType\n // even in STI mode where they may be different subclasses\n await this.eagerLoadRelationships(\n instances as ModelType[],\n interceptedOptions.include,\n );\n }\n\n // Execute afterList interceptors (e.g., tenant validation)\n const finalInstances = await GlobalInterceptors.executeAfterList(\n itemClassName,\n instances,\n interceptorContext,\n );\n\n return finalInstances;\n }\n\n /**\n * Eagerly load relationships for a collection of instances\n *\n * Optimizes loading by batching queries for foreignKey relationships to avoid N+1 queries.\n *\n * @param instances - Array of object instances to load relationships for\n * @param relationships - Array of relationship field names to load\n * @private\n */\n private async eagerLoadRelationships(\n instances: ModelType[],\n relationships: string[],\n ): Promise<void> {\n if (instances.length === 0) return;\n\n for (const fieldName of relationships) {\n // Get relationship metadata\n const relationshipMeta = ObjectRegistry.getRelationships(\n this._itemClass.name,\n );\n const relationship = relationshipMeta.find(\n (r) => r.fieldName === fieldName,\n );\n\n if (!relationship) {\n logger.warn(\n `Relationship ${fieldName} not found on ${this._itemClass.name}, skipping eager load`,\n );\n continue;\n }\n\n if (\n relationship.type === 'foreignKey' ||\n relationship.type === 'crossPackageRef'\n ) {\n // crossPackageRef target lives in another package — make sure its\n // manifest is loaded before we try to instantiate the target collection.\n if (relationship.type === 'crossPackageRef') {\n await ObjectRegistry.ensureManifestLoaded(relationship.targetClass);\n }\n // Batch load foreignKey / crossPackageRef relationships\n await this.batchLoadForeignKeys(instances, fieldName, relationship);\n } else if (relationship.type === 'oneToMany') {\n // Load oneToMany relationships (less optimizable)\n await this.batchLoadOneToMany(instances, fieldName, relationship);\n } else if (relationship.type === 'manyToMany') {\n await this.batchLoadManyToMany(instances, fieldName, relationship);\n }\n }\n }\n\n /**\n * Batch load foreignKey relationships to avoid N+1 queries\n *\n * @param instances - Instances to load relationships for\n * @param fieldName - Name of the foreignKey field\n * @param relationship - Relationship metadata\n * @private\n */\n private async batchLoadForeignKeys(\n instances: ModelType[],\n fieldName: string,\n relationship: import('./registry').RelationshipMetadata,\n ): Promise<void> {\n // Collect all unique foreign key values\n const foreignKeyValues = new Set<string>();\n for (const instance of instances) {\n const value = instance[fieldName as keyof ModelType];\n if (value && typeof value === 'string') {\n foreignKeyValues.add(value);\n }\n }\n\n if (foreignKeyValues.size === 0) return;\n\n // Get or create cached collection instance\n let targetCollection: SmrtCollection<any> | undefined;\n try {\n targetCollection = await ObjectRegistry.getCollection(\n relationship.targetClass,\n this.options,\n );\n } catch (error) {\n logger.warn(`Could not get collection for ${relationship.targetClass}`, {\n error,\n });\n return;\n }\n\n // Load all related objects, chunked to stay under SQLITE_MAX_VARIABLE_NUMBER (999).\n const fkValueList = Array.from(foreignKeyValues);\n const relatedObjects: any[] = [];\n for (const idChunk of chunkArray(fkValueList, IN_LIST_CHUNK_SIZE)) {\n const batch = await targetCollection.list({\n where: { 'id in': idChunk },\n });\n relatedObjects.push(...batch);\n }\n\n // Build a map of ID to object for quick lookup\n const relatedMap = new Map();\n for (const obj of relatedObjects) {\n relatedMap.set(obj.id, obj);\n }\n\n // Assign loaded objects to instances\n for (const instance of instances) {\n const foreignKeyValue = instance[fieldName as keyof ModelType];\n if (foreignKeyValue && typeof foreignKeyValue === 'string') {\n const relatedObject = relatedMap.get(foreignKeyValue);\n if (relatedObject) {\n // Set in the relationship cache\n (instance as any)._loadedRelationships.set(fieldName, relatedObject);\n }\n }\n }\n }\n\n /**\n * Batch load oneToMany relationships\n *\n * @param instances - Instances to load relationships for\n * @param fieldName - Name of the oneToMany field\n * @param relationship - Relationship metadata\n * @private\n */\n private async batchLoadOneToMany(\n instances: ModelType[],\n fieldName: string,\n relationship: import('./registry').RelationshipMetadata,\n ): Promise<void> {\n // Find the inverse foreignKey field. An instance can satisfy an inverse FK\n // that targets its own class or any (STI) ancestor it inherits the\n // oneToMany from. Mirrors loadRelatedMany so lazy and eager (`include:`)\n // loading resolve the same inverse side.\n const inverseRelationships = ObjectRegistry.getInverseRelationshipsForSelf(\n this._itemClass.name,\n );\n const inverseCandidates = inverseRelationships.filter(\n (r) =>\n r.sourceClass === relationship.targetClass && r.type === 'foreignKey',\n );\n // Honor an explicit `@oneToMany(Target, { foreignKey })` when the target\n // declares multiple foreign keys back to this class; otherwise fall back\n // to the first match (legacy behavior).\n const explicitForeignKey = relationship.options?.foreignKey as\n | string\n | undefined;\n const matchedForeignKey = explicitForeignKey\n ? inverseCandidates.find((r) => r.fieldName === explicitForeignKey)\n : undefined;\n if (explicitForeignKey && !matchedForeignKey) {\n // A misspelled / stale `foreignKey` is a configuration error, not a\n // recoverable data condition — fail loudly here too so eager (`include:`)\n // loading behaves identically to lazy loadRelatedMany rather than\n // silently producing empty arrays.\n throw new Error(\n `oneToMany ${fieldName} specifies foreignKey '${explicitForeignKey}', but ${relationship.targetClass} has no matching inverse foreignKey. Candidates: ${inverseCandidates.map((r) => r.fieldName).join(', ') || '(none)'}`,\n );\n }\n // Prefer an inverse FK that targets this exact class before falling back\n // to an ancestor's (mirrors loadRelatedMany).\n const inverseForeignKey =\n matchedForeignKey ??\n inverseCandidates.find((r) => r.targetClass === this._itemClass.name) ??\n inverseCandidates[0];\n\n if (!inverseForeignKey) {\n logger.warn(\n `Could not find inverse foreignKey for oneToMany ${fieldName}`,\n );\n return;\n }\n\n // Collect all instance IDs\n const instanceIds = instances\n .map((i) => i.id)\n .filter((id): id is string => !!id);\n\n if (instanceIds.length === 0) return;\n\n // Get or create cached collection instance\n let targetCollection: SmrtCollection<any> | undefined;\n try {\n targetCollection = await ObjectRegistry.getCollection(\n relationship.targetClass,\n this.options,\n );\n } catch (error) {\n logger.warn(`Could not get collection for ${relationship.targetClass}`, {\n error,\n });\n return;\n }\n\n // Load all related objects, chunked to stay under SQLITE_MAX_VARIABLE_NUMBER (999).\n const relatedObjects: any[] = [];\n for (const idChunk of chunkArray(instanceIds, IN_LIST_CHUNK_SIZE)) {\n const batch = await targetCollection.list({\n where: { [`${inverseForeignKey.fieldName} in`]: idChunk },\n });\n relatedObjects.push(...batch);\n }\n\n // Group related objects by the foreign key value\n const relatedMap = new Map<string, any[]>();\n for (const obj of relatedObjects) {\n // Access dynamic property safely - obj is a SmrtObject with dynamic fields\n const foreignKeyValue = (obj as Record<string, any>)[\n inverseForeignKey.fieldName\n ];\n if (!relatedMap.has(foreignKeyValue)) {\n relatedMap.set(foreignKeyValue, []);\n }\n relatedMap.get(foreignKeyValue)?.push(obj);\n }\n\n // Assign loaded objects to instances\n for (const instance of instances) {\n const relatedArray = relatedMap.get(instance.id as string) || [];\n (instance as any)._loadedRelationships.set(fieldName, relatedArray);\n }\n }\n\n /**\n * Batch-load manyToMany relationships through a junction table.\n *\n * Issues two queries instead of N: one against the junction table to map\n * source IDs to target IDs, and one against the target table to hydrate\n * the related rows. Results are grouped by source instance.\n *\n * @param instances - Instances whose manyToMany field should be populated\n * @param fieldName - Name of the @manyToMany decorated field\n * @param relationship - Relationship metadata from the registry\n * @private\n */\n private async batchLoadManyToMany(\n instances: ModelType[],\n fieldName: string,\n relationship: import('./registry').RelationshipMetadata,\n ): Promise<void> {\n const instanceIds = instances\n .map((i) => i.id)\n .filter((id): id is string => !!id);\n if (instanceIds.length === 0) return;\n\n // Delegate join-coordinate resolution to a sample instance — it shares the\n // same registry metadata as every other instance in this batch.\n let through: string;\n let sourceColumn: string;\n let targetColumn: string;\n let targetClassName: string;\n try {\n const sample: any = instances[0];\n const join = await sample.resolveManyToManyJoin(fieldName, relationship);\n through = join.through;\n sourceColumn = join.sourceColumn;\n targetColumn = join.targetColumn;\n targetClassName = join.targetClassName;\n } catch (error) {\n logger.warn(\n `Could not resolve manyToMany join for ${fieldName} on ${this._itemClass.name}`,\n { error },\n );\n return;\n }\n\n // Default empty arrays for every instance so callers always see an array\n for (const instance of instances) {\n (instance as any)._loadedRelationships.set(fieldName, []);\n }\n\n // Pull junction rows in chunks. SQLite's default SQLITE_MAX_VARIABLE_NUMBER\n // is 999, so we cap each IN-list at IN_LIST_CHUNK_SIZE to stay well below\n // the limit on the supported backends (sqlite/libsql/postgres).\n const junctionRowsAll: Array<{\n [key: string]: unknown;\n }> = [];\n for (const idChunk of chunkArray(instanceIds, IN_LIST_CHUNK_SIZE)) {\n const placeholders = idChunk.map(() => '?').join(', ');\n const result = await this.db.query(\n `SELECT \"${sourceColumn}\", \"${targetColumn}\" FROM \"${through}\" WHERE \"${sourceColumn}\" IN (${placeholders})`,\n idChunk,\n );\n junctionRowsAll.push(...result.rows);\n }\n\n if (junctionRowsAll.length === 0) return;\n\n // sourceId -> [targetIds...]\n const sourceToTargets = new Map<string, string[]>();\n const allTargetIds = new Set<string>();\n for (const row of junctionRowsAll as any[]) {\n const sId = row[sourceColumn];\n const tId = row[targetColumn];\n if (typeof sId !== 'string' || typeof tId !== 'string') continue;\n allTargetIds.add(tId);\n const list = sourceToTargets.get(sId) ?? [];\n list.push(tId);\n sourceToTargets.set(sId, list);\n }\n\n if (allTargetIds.size === 0) return;\n\n // Hydrate all target objects. Also chunked so we don't blow the\n // placeholder limit on a wide manyToMany.\n let targetCollection: SmrtCollection<any>;\n try {\n targetCollection = await ObjectRegistry.getCollection(\n targetClassName,\n this.options,\n );\n } catch (error) {\n logger.warn(\n `Could not get collection for manyToMany target ${targetClassName}`,\n { error },\n );\n return;\n }\n const targetIdList = Array.from(allTargetIds);\n const targetObjects: any[] = [];\n for (const idChunk of chunkArray(targetIdList, IN_LIST_CHUNK_SIZE)) {\n const batch = await targetCollection.list({\n where: { 'id in': idChunk },\n });\n targetObjects.push(...batch);\n }\n\n const targetById = new Map<string, any>();\n for (const obj of targetObjects) {\n if (obj.id) targetById.set(obj.id, obj);\n }\n\n // Assign grouped results\n for (const instance of instances) {\n const targetIds = sourceToTargets.get(instance.id as string) ?? [];\n const objects = targetIds\n .map((id) => targetById.get(id))\n .filter((o) => o !== undefined);\n (instance as any)._loadedRelationships.set(fieldName, objects);\n }\n }\n\n /**\n * Creates and persists a new instance of the collection's item class.\n *\n * Instantiates the model with the given field values, calls `initialize()`,\n * assigns a UUID if none is provided, then calls `save()` to write the row\n * to the database.\n *\n * For STI collections, pass `_meta_type` to create a specific subclass.\n * The correct constructor is resolved via `ObjectRegistry` (polymorphic creation).\n *\n * @param options - Field values for the new object. Accepts any public field on\n * the model class plus the STI `_meta_type` discriminator.\n * @returns The newly created and saved model instance\n * @throws {ValidationError} If a `required` field is missing or a unique constraint is violated\n * @throws {DatabaseError} If the write fails\n *\n * @example\n * ```typescript\n * // Regular creation\n * const product = await products.create({ name: 'Widget', price: 9.99 });\n * console.log(product.id); // UUID assigned during save\n *\n * // STI polymorphic creation\n * const article = await contents.create({\n * _meta_type: '@happyvertical/smrt-content:Article',\n * title: 'Hello World',\n * });\n * ```\n *\n * @see {@link getOrUpsert} to avoid duplicates by finding-or-creating\n */\n public async create(options: SmrtCreateInput<ModelType>) {\n let itemClassName = this.getResolvedItemClassName();\n\n // Ensure manifest is loaded before creating instance metadata; save() will\n // ensure the actual table exists right before persistence.\n await ObjectRegistry.ensureManifestLoaded(itemClassName);\n itemClassName = this.getResolvedItemClassName();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n\n // STI: Check for polymorphic instantiation.\n // R5-canon: qualified-key lookup so colliding simple names across\n // packages don't yield the wrong table strategy.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n\n if (tableStrategy === 'sti' && options._meta_type) {\n // Use polymorphic instantiation for STI child classes\n // Pass raw options (not params) - createPolymorphic() will add ai, db, _skipLoad\n const instance = await this.createPolymorphic(\n options._meta_type,\n options,\n );\n // Generate ID if not provided, then save\n if (!instance.id) {\n (instance as any)._id = crypto.randomUUID();\n }\n await instance.save();\n return instance;\n }\n\n // For non-STI or STI base class without _meta_type, proceed with direct instantiation\n // Schema already initialized in Collection.create() static factory\n const params = {\n ai: this.options.ai,\n // Pass the actual database instance, not options\n // This ensures objects share the same connection as the collection\n // Critical for in-memory databases like DuckDB :memory: where each\n // connection gets a separate database\n db: this.db,\n _skipLoad: true, // Don't try to load from DB - this is a new object\n ...options,\n };\n\n // Direct instantiation - all SmrtObject classes support this pattern\n const instance = new this._itemClass(params);\n await instance.initialize();\n\n // For STI collections, set _meta_type to the qualified class name (fix for issue #442)\n // This ensures _meta_type is available immediately after creation, not just after DB load\n // Use constructor-based lookup to avoid name collision issues (issue #713)\n // When classes are registered with qualified names as keys (e.g., from consumer plugin),\n // name-based lookup fails. Constructor lookup uses a WeakMap index for O(1) lookups.\n if (tableStrategy === 'sti') {\n (instance as any)._meta_type = itemQualifiedName;\n }\n // Generate ID if not provided, then save\n if (!instance.id) {\n (instance as any)._id = crypto.randomUUID();\n }\n await instance.save();\n return instance;\n }\n\n /**\n * Creates an instance of the correct subclass for STI polymorphic queries\n *\n * @param className - Name of the class to instantiate (from _meta_type)\n * @param options - Data to initialize the instance with\n * @returns Promise resolving to the instance of the correct subclass\n * @private\n */\n private async createPolymorphic(\n className: string | null | undefined,\n options: any,\n hydrationOptions: { hydrateOnly?: boolean } = {},\n ): Promise<ModelType> {\n // Schema already initialized in Collection.create() static factory\n\n // Validate discriminator is present\n if (!className || className === null || className === undefined) {\n const { DatabaseError } = await import('./errors.js');\n throw DatabaseError.missingDiscriminator(\n this._itemClass.name,\n options?.id,\n );\n }\n\n // Get the correct class constructor from ObjectRegistry\n // Support both qualified names (new format: @pkg:Class) and simple names (legacy)\n let registeredClass = ObjectRegistry.getClassByQualifiedName(className);\n if (!registeredClass) {\n // Fall back to simple name lookup for legacy data\n registeredClass = ObjectRegistry.getClass(className);\n }\n\n if (!registeredClass) {\n await ObjectRegistry.ensureManifestLoaded(className);\n registeredClass = ObjectRegistry.getClassByQualifiedName(className);\n if (!registeredClass) {\n registeredClass = ObjectRegistry.getClass(className);\n }\n }\n\n if (!registeredClass) {\n throw new Error(\n `STI polymorphic query failed: Class '${className}' not found in ObjectRegistry. ` +\n `Ensure the class is registered with @smrt() decorator or available via an installed SMRT manifest.`,\n );\n }\n\n const params = {\n ai: this.options.ai,\n db: this.db,\n _skipLoad: true,\n ...(hydrationOptions.hydrateOnly\n ? {\n _reuseInitializedDb: true,\n _deferRuntimeInitialization: true,\n }\n : {}),\n ...options,\n };\n\n // Instantiate the correct subclass\n const instance = new registeredClass.constructor(params);\n await instance.initialize();\n\n // Ensure _meta_type is set on the instance (fix for issue #442)\n // Use qualified name if available, otherwise keep the input className\n // (which may already be qualified for new data or simple for legacy data)\n (instance as any)._meta_type = registeredClass?.qualifiedName || className;\n\n return instance as ModelType;\n }\n\n /**\n * Finds an existing record matching `data` or creates it if not found.\n *\n * Look-up priority:\n * 1. `data.id` — query by primary key\n * 2. `data.slug` — query by slug + context\n * 3. Fallback — query by the full `data` object as a WHERE clause\n *\n * If a matching record is found, it is updated with any changed fields from\n * `data` (diffed against the existing record) and saved. If no match is\n * found, `defaults` are merged with `data` and a new record is created.\n *\n * @param data - The field values to find or upsert\n * @param defaults - Extra default values applied only when creating a new record\n * @returns The existing (possibly updated) or newly created object instance\n *\n * @example\n * ```typescript\n * // Find-or-create a tag by slug\n * const tag = await tags.getOrUpsert({ slug: 'javascript', name: 'JavaScript' });\n *\n * // With defaults applied only on creation\n * const user = await users.getOrUpsert(\n * { email: 'alice@example.com' },\n * { role: 'member', active: true },\n * );\n * ```\n *\n * @see {@link create} for always-insert semantics\n * @see {@link get} for read-only lookup\n */\n public async getOrUpsert(data: any, defaults: any = {}) {\n const logicalData = this.normalizeLogicalData(data);\n const logicalDefaults = this.normalizeLogicalData(defaults);\n let where: any = {};\n const diffData = { ...logicalData };\n if (logicalData.id) {\n where = { id: logicalData.id };\n delete diffData.id;\n delete diffData.slug;\n delete diffData.context;\n } else if (logicalData.slug) {\n where = { slug: logicalData.slug, context: logicalData.context || '' };\n delete diffData.slug;\n delete diffData.context;\n } else {\n where = logicalData;\n }\n // Force a fresh read: this probe decides create-vs-update, so a stale cache\n // hit could update a row that no longer exists or miss one that does.\n const existing = await this.get(where, { cache: false });\n if (existing) {\n const diff = this.getDiffSync(existing, diffData);\n if (diff) {\n Object.assign(existing, diff);\n await existing.save();\n }\n return existing;\n }\n const createData = {\n ...logicalDefaults,\n ...logicalData,\n } as SmrtCreateInput<ModelType>;\n\n return await this.create(createData);\n }\n\n /**\n * Gets differences between an existing object and new data\n *\n * @param existing - Existing object\n * @param data - New data\n * @returns Object containing only the changed fields\n */\n async getDiff(\n existing: Record<string, any>,\n data: Record<string, any>,\n ): Promise<Record<string, any> | null> {\n return this.getDiffSync(existing, data);\n }\n\n private getDiffSync(\n existing: Record<string, any>,\n data: Record<string, any>,\n ): Record<string, any> | null {\n const fields = this.getFieldsSync();\n const validKeys = new Set([\n ...Object.keys(fields),\n 'id',\n 'slug',\n 'context',\n ]);\n const diff = Object.keys(data).reduce(\n (acc, key) => {\n if (\n validKeys.has(key) &&\n !this.areEquivalentValues(existing[key], data[key])\n ) {\n acc[key] = data[key];\n }\n return acc;\n },\n {} as Record<string, any>,\n );\n\n return Object.keys(diff).length > 0 ? diff : null;\n }\n\n /**\n * Gets field definitions for the collection's item class\n *\n * @returns Object containing field definitions\n */\n async getFields() {\n return await fieldsFromClass(this._itemClass);\n }\n\n /**\n * Normalize user input into the model's logical field names before diffing or creation.\n *\n * Accepts both camelCase and snake_case keys while preserving framework meta fields.\n */\n private normalizeLogicalData(data: Record<string, any>): Record<string, any> {\n const fields = this.getFieldsSync();\n const normalized: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) {\n normalized[key] = value;\n continue;\n }\n\n const camelKey = key.includes('_') ? toCamelCase(key) : key;\n const snakeKey = key.includes('_') ? key : toSnakeCase(key);\n const outputKey =\n key in fields\n ? key\n : camelKey in fields\n ? camelKey\n : snakeKey in fields\n ? snakeKey\n : key;\n\n normalized[outputKey] = value;\n }\n\n return normalized;\n }\n\n /**\n * Preserve persisted core timestamp fields during lightweight hydration.\n */\n private withHydratedCoreFields(\n data: Record<string, any>,\n ): Record<string, any> {\n const hydratedData = { ...data };\n\n if (\n hydratedData.createdAt !== undefined &&\n hydratedData.created_at === undefined\n ) {\n hydratedData.created_at = hydratedData.createdAt;\n }\n\n if (\n hydratedData.updatedAt !== undefined &&\n hydratedData.updated_at === undefined\n ) {\n hydratedData.updated_at = hydratedData.updatedAt;\n }\n\n return hydratedData;\n }\n\n /**\n * Treat date-equivalent values as unchanged even if their runtime types differ.\n */\n private areEquivalentValues(\n existingValue: unknown,\n nextValue: unknown,\n ): boolean {\n if (existingValue instanceof Date || nextValue instanceof Date) {\n const existingTime = this.toComparableTime(existingValue);\n const nextTime = this.toComparableTime(nextValue);\n\n if (existingTime !== null && nextTime !== null) {\n return existingTime === nextTime;\n }\n }\n\n return existingValue === nextValue;\n }\n\n /**\n * Convert supported date inputs into comparable timestamps.\n */\n private toComparableTime(value: unknown): number | null {\n if (value instanceof Date) {\n const timestamp = value.getTime();\n return Number.isNaN(timestamp) ? null : timestamp;\n }\n\n if (typeof value === 'string' && value.trim()) {\n const parsedDate = new Date(value);\n const timestamp = parsedDate.getTime();\n return Number.isNaN(timestamp) ? null : timestamp;\n }\n\n return null;\n }\n\n /**\n * Gets field definitions synchronously from cache.\n *\n * This method provides sync access to fields for use in query methods,\n * avoiding the async overhead of getFields() on every query.\n * Fields are cached during create() initialization.\n *\n * @returns Map containing field definitions\n * @private\n */\n getFieldsSync(): Record<string, any> {\n if (this._cachedFields) {\n return this._cachedFields;\n }\n // Fallback to ObjectRegistry sync method if cache not populated\n // This handles edge cases where collection wasn't created via static create()\n const className = this.getResolvedItemClassName();\n const fields = ObjectRegistry.getFields(className);\n // Convert Map to Record for consistency with getFields() return type\n return Object.fromEntries(fields);\n }\n\n /**\n * Generates database schema for the collection's item class\n *\n * Leverages ObjectRegistry's cached schema for instant retrieval.\n *\n * @returns Schema object for database setup\n */\n async generateSchema() {\n // Always generate fresh schema to ensure latest field mapping is used\n const { generateSchema } = await import('./schema/utils.js');\n return await generateSchema(this._itemClass);\n }\n\n /**\n * Gets the database table name for this collection\n */\n get tableName() {\n if (!this._tableName) {\n // For STI, use the base class's table name from schema (manifest-derived).\n // R5-canon: use the qualified item name as the lookup key so a\n // colliding simple name in another package can't yield the wrong\n // tableStrategy / STI base and route every downstream query\n // (get/list/count/create) to the wrong table.\n const className = this.getResolvedItemClassName();\n const qualifiedName = this.getResolvedItemQualifiedName();\n const tableStrategy = ObjectRegistry.getTableStrategy(qualifiedName);\n const fallbackTableName =\n (this._itemClass as any).SMRT_TABLE_NAME ||\n classnameToTablename(className);\n\n if (tableStrategy === 'sti') {\n const stiBase = ObjectRegistry.getSTIBase(qualifiedName);\n if (stiBase) {\n // Use base class's schema tableName (from manifest)\n const baseSchema = ObjectRegistry.getSchema(stiBase);\n if (baseSchema?.tableName) {\n this._tableName = baseSchema.tableName;\n } else {\n // Fallback to own schema tableName\n const ownSchema = ObjectRegistry.getSchema(className);\n this._tableName = ownSchema?.tableName || fallbackTableName;\n }\n } else {\n // Fallback to own schema tableName\n const ownSchema = ObjectRegistry.getSchema(className);\n this._tableName = ownSchema?.tableName || fallbackTableName;\n }\n } else {\n // CTI: Use own schema tableName\n const ownSchema = ObjectRegistry.getSchema(className);\n this._tableName = ownSchema?.tableName || fallbackTableName;\n }\n }\n return this._tableName;\n }\n\n /**\n * Generates a table name from the collection class name\n *\n * @returns Generated table name\n */\n generateTableName() {\n // Convert camelCase/PascalCase to snake_case and pluralize\n const tableName = this._className\n // Insert underscore between lower & upper case letters\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n // Convert to lowercase\n .toLowerCase()\n // Handle basic pluralization rules\n .replace(/([^s])$/, '$1s')\n // Handle special cases ending in 'y'\n .replace(/y$/, 'ies');\n\n return tableName;\n }\n\n /**\n * Deletes a record from the collection by ID\n *\n * Loads the object and calls its delete() method, ensuring all interceptors\n * and lifecycle hooks (beforeDelete/afterDelete) are executed correctly.\n *\n * @param id - The ID of the record to delete\n * @returns Promise resolving to true if deleted, false if not found\n *\n * @example\n * ```typescript\n * const success = await collection.delete('some-uuid');\n * if (success) {\n * console.log('Record deleted');\n * }\n * ```\n */\n public async delete(id: string): Promise<boolean> {\n // Schema already initialized in Collection.create() static factory\n\n // Ensure manifest is loaded for external packages\n await ObjectRegistry.ensureManifestLoaded(this.getResolvedItemClassName());\n\n // Load the object first - if it doesn't exist, return false immediately.\n // Force a fresh read: this probe gates a mutation and decides the return\n // value, so a stale cache hit could delete a phantom (returning true for a\n // row another process already removed) or skip a real delete.\n const instance = await this.get(id, { cache: false });\n if (!instance) {\n return false;\n }\n\n // Call the object's delete() method, which handles:\n // - beforeDelete interceptors\n // - beforeDelete lifecycle hooks\n // - Database deletion\n // - afterDelete lifecycle hooks\n // - afterDelete interceptors\n await instance.delete();\n\n return true;\n }\n\n /**\n * Returns the number of records matching the given filter conditions.\n *\n * Executes a `SELECT COUNT(*)` query with the same WHERE conversion as\n * `list()` (camelCase field names, operator suffixes, STI auto-filtering).\n * `limit`, `offset`, and `orderBy` are not applicable and are ignored.\n *\n * Note: `count()` is never served from the read cache (issue #1498) — only\n * `list()`/`get()` are. On a page that caches `list()`, a `count()` issued\n * in the same request reflects the live database and may briefly diverge\n * from the cached rows within the TTL window.\n *\n * @param options.where - Filter conditions (same syntax as `list()`)\n * @returns Total count of matching records as an integer\n *\n * @example\n * ```typescript\n * const total = await products.count();\n * const activeCount = await products.count({ where: { status: 'active' } });\n * const expensiveCount = await products.count({ where: { 'price >': 100 } });\n * ```\n *\n * @see {@link list} for retrieving the actual records\n */\n public async count(options: { where?: Record<string, any> } = {}) {\n await this.ensureStorageReady();\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n const itemClassName = this.getResolvedItemClassName();\n\n // Security (#1540): count() is a list-shaped read, so run the same\n // `beforeList` interceptors (tenant filtering, etc.). Without this, count()\n // bypasses tenant scoping and returns a cross-tenant total even when the\n // matching list() is correctly filtered — leaking row counts across tenants.\n const interceptorContext = createInterceptorContext(\n itemClassName,\n 'list',\n this.constructor.name,\n );\n const interceptedOptions =\n (await GlobalInterceptors.executeBeforeList(\n itemClassName,\n options as InterceptorListOptions,\n interceptorContext,\n )) ??\n (options as InterceptorListOptions | undefined) ??\n {};\n\n let { where } = interceptedOptions;\n\n // Fix for issue #386: Add _meta_type filter for STI child collections.\n // R5-canon: qualified-key lookup throughout — pass `itemQualifiedName`\n // to both `getTableStrategy` and `getSTIBase` so a colliding\n // simple-name class in another package can't yield the wrong table\n // strategy / STI base.\n const tableStrategy = ObjectRegistry.getTableStrategy(itemQualifiedName);\n const isSTI = tableStrategy === 'sti';\n\n if (isSTI) {\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n if (stiBase && stiBase !== itemQualifiedName) {\n where = {\n _meta_type: itemQualifiedName,\n ...(where || {}),\n };\n }\n }\n\n // Resolve _meta_type to qualified name if user provided simple class name (Issue #713)\n where = resolveMetaTypeInWhere(where);\n\n // convertWhereKeys is now sync (issue #663) - no await needed\n const { sql: whereSql, values: whereValues } = buildWhere(\n this.convertWhereKeys(where || {}),\n );\n\n const result = await this.db.query(\n `SELECT COUNT(*) as count FROM ${this.tableName} ${whereSql}`,\n ...whereValues,\n );\n\n return Number.parseInt(result.rows[0].count, 10);\n }\n\n /**\n * Resolve this collection's STI child discriminator — the qualified item\n * class name to use as a `_meta_type` scope — when the collection's item is\n * an STI **child** (shares a table with sibling subtypes), or `null` for STI\n * bases, CTI, and non-STI item classes.\n *\n * Mirrors the automatic `_meta_type` scoping that `list()` / `get()` apply\n * for STI child collections (#386), using the same `ObjectRegistry` source of\n * truth (`getTableStrategy` + `getSTIBase`). Raw-SQL helpers that bypass\n * `list()` — e.g. the tenant global / with-globals lookups in\n * `@happyvertical/smrt-tenancy` (#1600) — call this to scope a shared STI\n * table to the collection's own subtype, so they never surface sibling rows.\n * Deriving the scope here keeps every consumer consistent with `list()`\n * without each one hand-classifying its collection's table strategy.\n *\n * @returns The qualified `_meta_type` for an STI child collection, or `null`\n * when no `_meta_type` scoping applies (STI base / CTI / non-STI).\n */\n public getStiChildMetaType(): string | null {\n const itemQualifiedName = this.getResolvedItemQualifiedName();\n if (ObjectRegistry.getTableStrategy(itemQualifiedName) !== 'sti') {\n return null;\n }\n const stiBase = ObjectRegistry.getSTIBase(itemQualifiedName);\n return stiBase && stiBase !== itemQualifiedName ? itemQualifiedName : null;\n }\n\n /**\n * Execute a raw SQL query and hydrate results as collection item instances\n *\n * Provides full SQL power for complex queries (JOINs, CTEs, NOT EXISTS, etc.)\n * while still returning properly hydrated SMRT objects.\n *\n * @param sql - Raw SQL query string (should select from this.tableName)\n * @param params - Query parameters for prepared statement\n * @returns Promise resolving to array of hydrated model instances\n *\n * @example\n * ```typescript\n * // Find meetings without corresponding recaps (NOT EXISTS pattern)\n * const meetings = await meetingCollection.query(`\n * SELECT m.* FROM meetings m\n * WHERE m.start_date < datetime('now')\n * AND NOT EXISTS (\n * SELECT 1 FROM contents c\n * WHERE c.meeting_id = m.id\n * AND c._meta_type = 'MeetingRecap'\n * )\n * ORDER BY m.start_date DESC\n * LIMIT ?\n * `, [10]);\n *\n * // Complex JOIN query\n * const products = await productCollection.query(`\n * SELECT p.* FROM products p\n * INNER JOIN categories c ON p.category_id = c.id\n * WHERE c.name = ? AND p.price > ?\n * ORDER BY p.price ASC\n * `, ['Electronics', 100]);\n * ```\n */\n public async query(\n sql: string,\n params: any[] = [],\n options: { allowRawOnTenantScoped?: boolean } = {},\n ): Promise<ModelType[]> {\n await this.ensureStorageReady();\n\n // Execute beforeQuery interceptors (e.g., tenant raw SQL policy)\n const interceptorContext = createInterceptorContext(\n this._itemClass.name,\n 'query',\n this.constructor.name,\n );\n const interceptedQuery = await GlobalInterceptors.executeBeforeQuery(\n this._itemClass.name,\n { sql, params, allowRawOnTenantScoped: options.allowRawOnTenantScoped },\n interceptorContext,\n );\n\n const result = await this.db.query(\n interceptedQuery.sql,\n ...interceptedQuery.params,\n );\n\n // query() is a raw escape hatch documented for SELECTs, but it can run any\n // SQL. A write issued through it bypasses the save()/delete() invalidation\n // hooks, so conservatively bust this table's cache when the statement looks\n // like a mutation (issue #1498). The word-boundary match ignores column\n // names like `updated_at`/`deleted_at`; a false positive (e.g. SELECT ...\n // FOR UPDATE) only costs a cache miss, never a stale read.\n if (\n /\\b(?:insert|update|delete|merge|truncate|replace)\\b/i.test(\n interceptedQuery.sql,\n )\n ) {\n invalidateCollectionCache(resolveDbCacheKey(this.db), this.tableName);\n }\n\n const fields = this.getFieldsSync();\n\n // STI: Check if we need polymorphic hydration.\n // R5-canon: pass the qualified item name so a colliding simple\n // name in another package can't yield the wrong tableStrategy\n // and feed the wrong `isSTI` boolean into `hydrateResultRow`.\n const tableStrategy = ObjectRegistry.getTableStrategy(\n this.getResolvedItemQualifiedName(),\n );\n const isSTI = tableStrategy === 'sti';\n\n const instances = await Promise.all(\n result.rows.map((row: any) => this.hydrateResultRow(row, fields, isSTI)),\n );\n\n // Execute afterQuery interceptors\n return await GlobalInterceptors.executeAfterQuery(\n this._itemClass.name,\n instances,\n interceptorContext,\n );\n }\n\n private async hydrateResultRow(\n row: Record<string, any>,\n fields: Record<string, any>,\n isSTI: boolean,\n ): Promise<ModelType> {\n const formattedData = this.withHydratedCoreFields(\n formatDataJs(row, fields),\n );\n\n if (isSTI && formattedData._meta_type) {\n const polymorphicInstance = await this.createPolymorphic(\n formattedData._meta_type,\n formattedData,\n { hydrateOnly: true },\n );\n // Hydrated from an existing row — save() must update by primary key\n // even if natural-key fields change (issue #1472).\n polymorphicInstance.markAsPersisted();\n return polymorphicInstance;\n }\n\n const instanceParams = {\n ai: this.options.ai,\n db: this.db,\n _skipLoad: true,\n _reuseInitializedDb: true,\n _deferRuntimeInitialization: true,\n ...formattedData,\n };\n\n const instance = new this._itemClass(instanceParams);\n await instance.initialize();\n\n if (isSTI) {\n const registeredClass = ObjectRegistry.getClass(this._itemClass.name);\n (instance as any)._meta_type =\n registeredClass?.qualifiedName || this._itemClass.name;\n }\n\n // Hydrated from an existing row — save() must update by primary key\n // even if natural-key fields change (issue #1472).\n instance.markAsPersisted();\n return instance;\n }\n\n /**\n * Remember collection-level context\n *\n * Stores context applicable to all instances of this collection type.\n * Use for patterns that apply to the entire collection (e.g., default parsing strategies).\n *\n * @param options - Context options\n * @returns Promise that resolves when context is stored\n * @example\n * ```typescript\n * // Remember a default parsing strategy for all documents\n * await documentCollection.remember({\n * scope: 'parser/default',\n * key: 'selector',\n * value: { pattern: '.content article' },\n * confidence: 0.8\n * });\n *\n * // Update an existing context entry by specifying id\n * await documentCollection.remember({\n * id: 'existing-context-id',\n * scope: 'parser/default',\n * key: 'selector',\n * value: { pattern: '.content main article' },\n * confidence: 0.85\n * });\n * ```\n */\n public async remember(options: {\n id?: string;\n scope: string;\n key: string;\n value: any;\n metadata?: any;\n confidence?: number;\n version?: number;\n expiresAt?: Date;\n }): Promise<void> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n const id = options.id || crypto.randomUUID();\n const now = new Date();\n\n // Use upsert() for database-agnostic INSERT OR REPLACE\n // SQLite: INSERT OR REPLACE\n // Postgres/DuckDB: INSERT ... ON CONFLICT ... DO UPDATE\n await this.systemDb.upsert(\n '_smrt_contexts',\n // UNIQUE constraint: (owner_class, owner_id, scope, key, version)\n ['owner_class', 'owner_id', 'scope', 'key', 'version'],\n {\n id,\n owner_class: this._itemClass.name,\n owner_id: '__collection__',\n scope: options.scope,\n key: options.key,\n value: JSON.stringify(options.value),\n metadata: options.metadata ? JSON.stringify(options.metadata) : null,\n version: options.version ?? 1,\n confidence: options.confidence ?? 1.0,\n success_count: 0,\n failure_count: 0,\n created_at: now,\n updated_at: now,\n last_used_at: now,\n expires_at: options.expiresAt ?? null,\n },\n );\n }\n\n /**\n * Recall collection-level context\n *\n * Retrieves context that applies to all instances of this collection.\n *\n * @param options - Recall options\n * @returns Promise resolving to the context value or null if not found\n * @example\n * ```typescript\n * // Recall default parsing strategy\n * const strategy = await documentCollection.recall({\n * scope: 'parser/default',\n * key: 'selector',\n * minConfidence: 0.5\n * });\n * ```\n */\n public async recall(options: {\n scope: string;\n key: string;\n includeAncestors?: boolean;\n minConfidence?: number;\n }): Promise<any | null> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n // Use single() with template literals for custom SQL query\n let result: Record<string, any> | null;\n if (options.minConfidence !== undefined) {\n result = await this.systemDb.single`\n SELECT value, confidence\n FROM _smrt_contexts\n WHERE owner_class = ${this._itemClass.name}\n AND owner_id = ${'__collection__'}\n AND scope = ${options.scope}\n AND key = ${options.key}\n AND confidence >= ${options.minConfidence}\n ORDER BY confidence DESC, version DESC\n LIMIT 1\n `;\n } else {\n result = await this.systemDb.single`\n SELECT value, confidence\n FROM _smrt_contexts\n WHERE owner_class = ${this._itemClass.name}\n AND owner_id = ${'__collection__'}\n AND scope = ${options.scope}\n AND key = ${options.key}\n ORDER BY confidence DESC, version DESC\n LIMIT 1\n `;\n }\n\n if (result) {\n // Guard against a corrupted _smrt_contexts row: a single malformed value\n // must not throw an uncaught SyntaxError out of recall(). Treat it as a\n // miss and continue to the ancestor fallback (#1378).\n try {\n return JSON.parse(result.value);\n } catch (error) {\n logger.warn('Skipping corrupted _smrt_contexts value in recall()', {\n ownerClass: this._itemClass.name,\n scope: options.scope,\n key: options.key,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Hierarchical fallback to parent scopes\n if (options.includeAncestors) {\n const scopeParts = options.scope.split('/');\n while (scopeParts.length > 0) {\n scopeParts.pop();\n const parentScope = scopeParts.join('/') || 'global';\n\n const parentResult = await this.recall({\n ...options,\n scope: parentScope,\n includeAncestors: false,\n });\n\n if (parentResult) return parentResult;\n }\n }\n\n return null;\n }\n\n /**\n * Recall all collection-level context in a scope\n *\n * Returns a Map of key -> value for all collection contexts matching the criteria.\n *\n * @param options - Recall options\n * @returns Promise resolving to Map of key -> value pairs\n * @example\n * ```typescript\n * // Get all default strategies\n * const strategies = await documentCollection.recallAll({\n * scope: 'parser/default',\n * minConfidence: 0.5\n * });\n * ```\n */\n public async recallAll(\n options: {\n scope?: string;\n includeDescendants?: boolean;\n minConfidence?: number;\n } = {},\n ): Promise<Map<string, any>> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n const results = new Map<string, any>();\n\n let query = `\n SELECT key, value, confidence\n FROM _smrt_contexts\n WHERE owner_class = ? AND owner_id = ?\n `;\n const params: any[] = [this._itemClass.name, '__collection__'];\n\n if (options.scope) {\n if (options.includeDescendants) {\n query += ` AND (scope = ? OR scope LIKE ?)`;\n params.push(options.scope, `${options.scope}/%`);\n } else {\n query += ` AND scope = ?`;\n params.push(options.scope);\n }\n }\n\n if (options.minConfidence !== undefined) {\n query += ` AND confidence >= ?`;\n params.push(options.minConfidence);\n }\n\n query += ` ORDER BY confidence DESC`;\n\n const { rows } = await this.systemDb.query(query, ...params);\n\n for (const row of rows) {\n // Skip a corrupted row rather than aborting the whole recallAll() (#1378).\n try {\n results.set(row.key, JSON.parse(row.value));\n } catch (error) {\n logger.warn('Skipping corrupted _smrt_contexts value in recallAll()', {\n ownerClass: this._itemClass.name,\n scope: options.scope,\n key: row.key,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return results;\n }\n\n /**\n * Forget collection-level context\n *\n * Deletes collection context by scope and key.\n *\n * @param options - Context identification\n * @returns Promise that resolves when context is deleted\n * @example\n * ```typescript\n * // Remove a default strategy\n * await documentCollection.forget({\n * scope: 'parser/default',\n * key: 'selector'\n * });\n * ```\n */\n public async forget(options: { scope: string; key: string }): Promise<void> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n await this.systemDb.query(\n `DELETE FROM _smrt_contexts\n WHERE owner_class = ? AND owner_id = ? AND scope = ? AND key = ?`,\n this._itemClass.name,\n '__collection__',\n options.scope,\n options.key,\n );\n }\n\n /**\n * Forget all collection-level context in a scope\n *\n * Deletes all collection contexts matching the scope pattern.\n *\n * @param options - Scope options\n * @returns Promise resolving to number of contexts deleted\n * @example\n * ```typescript\n * // Clear all default strategies\n * const count = await documentCollection.forgetScope({\n * scope: 'parser/default',\n * includeDescendants: true\n * });\n * ```\n */\n public async forgetScope(options: {\n scope: string;\n includeDescendants?: boolean;\n }): Promise<number> {\n if (!this.systemDb) {\n throw new Error('Database not initialized. Call initialize() first.');\n }\n\n let query = `\n DELETE FROM _smrt_contexts\n WHERE owner_class = ? AND owner_id = ?\n `;\n const params: any[] = [this._itemClass.name, '__collection__'];\n\n if (options.includeDescendants) {\n query += ` AND (scope = ? OR scope LIKE ?)`;\n params.push(options.scope, `${options.scope}/%`);\n } else {\n query += ` AND scope = ?`;\n params.push(options.scope);\n }\n\n const { rowCount } = await this.systemDb.query(query, ...params);\n return rowCount || 0;\n }\n\n // ============================================================================\n // Semantic Search Methods\n // ============================================================================\n\n /**\n * Semantic search by text query\n *\n * Generates an embedding for the query text and finds similar objects\n * based on cosine similarity of stored embeddings.\n *\n * @param query - Text to search for\n * @param options - Search options\n * @param options.field - Specific field to search (defaults to first embedding field)\n * @param options.limit - Maximum results to return (default: 10)\n * @param options.minSimilarity - Minimum similarity threshold 0-1 (default: 0)\n * @param options.where - Additional WHERE filters to apply\n * @returns Promise resolving to array of objects with _similarity score\n *\n * @example\n * ```typescript\n * const results = await articles.semanticSearch('machine learning trends', {\n * limit: 10,\n * minSimilarity: 0.7\n * });\n *\n * for (const article of results) {\n * console.log(`${article.title} (similarity: ${article._similarity})`);\n * }\n * ```\n */\n public async semanticSearch(\n query: string,\n options: {\n field?: string;\n limit?: number;\n minSimilarity?: number;\n where?: Record<string, any>;\n } = {},\n ): Promise<Array<ModelType & { _similarity: number }>> {\n const { field, limit = 10, minSimilarity = 0, where } = options;\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}. ` +\n `Add embeddings config to @smrt() decorator.`,\n );\n }\n\n // Determine which field to search\n const searchField = field || embeddingConfig.fields[0];\n if (!embeddingConfig.fields.includes(searchField)) {\n throw new Error(\n `Field '${searchField}' is not configured for embeddings on ${this._itemClass.name}. ` +\n `Available fields: ${embeddingConfig.fields.join(', ')}`,\n );\n }\n\n // Create embedding provider from the resolved class/project config so\n // query embeddings use the same model as stored object embeddings.\n const provider = new EmbeddingProvider(\n {\n dimensions: embeddingConfig.dimensions,\n provider: embeddingConfig.provider,\n localModel: embeddingConfig.localModel,\n aiModel: embeddingConfig.aiModel,\n fallbackToAI: embeddingConfig.fallbackToAI,\n },\n this.ai,\n );\n\n // Generate embedding for query\n const [queryEmbedding] = await provider.embed(query);\n\n // Find similar embeddings\n return this.findSimilarToEmbedding(queryEmbedding, {\n field: searchField,\n limit,\n minSimilarity,\n where,\n });\n }\n\n /**\n * Find objects similar to a given object\n *\n * Uses stored embeddings to find objects most similar to the provided object.\n *\n * @param object - Object to find similar items for (or object ID)\n * @param options - Search options\n * @param options.field - Specific field to compare (defaults to first embedding field)\n * @param options.limit - Maximum results to return (default: 5)\n * @param options.excludeSelf - Whether to exclude the source object (default: true)\n * @returns Promise resolving to array of similar objects with _similarity score\n *\n * @example\n * ```typescript\n * const article = await articles.get('some-article-id');\n * const similar = await articles.findSimilar(article, {\n * limit: 5,\n * excludeSelf: true\n * });\n * ```\n */\n public async findSimilar(\n object: ModelType | string,\n options: {\n field?: string;\n limit?: number;\n excludeSelf?: boolean;\n } = {},\n ): Promise<Array<ModelType & { _similarity: number }>> {\n const { field, limit = 5, excludeSelf = true } = options;\n\n // Get the object if ID was provided\n let sourceObject: ModelType;\n if (typeof object === 'string') {\n const found = await this.get(object);\n if (!found) {\n throw new Error(`Object not found: ${object}`);\n }\n sourceObject = found;\n } else {\n sourceObject = object;\n }\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}.`,\n );\n }\n\n // Determine which field to use\n const searchField = field || embeddingConfig.fields[0];\n\n // Get the source object's embedding using consistent model name from EmbeddingProvider\n const provider = new EmbeddingProvider(\n {\n dimensions: embeddingConfig.dimensions,\n provider: embeddingConfig.provider,\n localModel: embeddingConfig.localModel,\n aiModel: embeddingConfig.aiModel,\n fallbackToAI: embeddingConfig.fallbackToAI,\n },\n this.ai,\n );\n const model = provider.getModelName();\n\n const storedEmbedding = await EmbeddingStorage.get(\n this.systemDb,\n this._itemClass.name,\n sourceObject.id as string,\n searchField,\n model,\n );\n\n if (!storedEmbedding) {\n throw new Error(\n `No embedding found for object ${sourceObject.id} field '${searchField}'. ` +\n `Generate embeddings first with object.generateEmbeddings().`,\n );\n }\n\n // Find similar using the embedding\n const where = excludeSelf ? { 'id !=': sourceObject.id } : undefined;\n\n return this.findSimilarToEmbedding(storedEmbedding.embedding, {\n field: searchField,\n limit,\n minSimilarity: 0,\n where,\n });\n }\n\n /**\n * Find objects similar to a raw embedding vector\n *\n * Low-level method for finding objects by embedding similarity.\n *\n * @param embedding - Embedding vector to compare against\n * @param options - Search options\n * @param options.field - Field name to search (defaults to first embedding field)\n * @param options.limit - Maximum results to return (default: 10)\n * @param options.minSimilarity - Minimum similarity threshold 0-1 (default: 0)\n * @param options.where - Additional WHERE filters to apply\n * @returns Promise resolving to array of objects with _similarity score\n *\n * @example\n * ```typescript\n * // Using a pre-computed embedding\n * const results = await articles.findSimilarToEmbedding(myEmbedding, {\n * limit: 10,\n * minSimilarity: 0.5\n * });\n * ```\n */\n public async findSimilarToEmbedding(\n embedding: number[],\n options: {\n field?: string;\n limit?: number;\n minSimilarity?: number;\n where?: Record<string, any>;\n } = {},\n ): Promise<Array<ModelType & { _similarity: number }>> {\n const { field, limit = 10, minSimilarity = 0, where } = options;\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}.`,\n );\n }\n\n // Determine which field to search\n const searchField = field || embeddingConfig.fields[0];\n\n // Get model name using EmbeddingProvider for consistency\n const provider = new EmbeddingProvider(\n {\n dimensions: embeddingConfig.dimensions,\n provider: embeddingConfig.provider,\n localModel: embeddingConfig.localModel,\n aiModel: embeddingConfig.aiModel,\n fallbackToAI: embeddingConfig.fallbackToAI,\n },\n this.ai,\n );\n const model = provider.getModelName();\n\n // Resolve storage strategy\n const projectConfig = ObjectRegistry.getProjectEmbeddingConfig();\n const storage = projectConfig?.storage || 'json';\n const vector = storage === 'native' ? this.systemDb.vector : undefined;\n\n // Use unified search method (delegates to native or in-memory)\n const scored = await EmbeddingStorage.searchSimilar(\n this.systemDb,\n this._itemClass.name,\n embedding,\n {\n field: searchField,\n model,\n limit,\n minSimilarity,\n },\n vector,\n );\n\n if (scored.length === 0) {\n return [];\n }\n\n // Load the objects\n const objectIds = scored.map((s) => s.objectId);\n const similarityMap = new Map(\n scored.map((s) => [s.objectId, s.similarity]),\n );\n\n // Build where clause with additional filters\n const whereClause = where\n ? { 'id in': objectIds, ...where }\n : { 'id in': objectIds };\n\n const objects = await this.list({\n where: whereClause as SmrtWhereClause<ModelType>,\n });\n\n // Add similarity scores to objects and sort by similarity\n // We directly assign _similarity to avoid losing class properties when spreading\n const results = objects.map((obj) => {\n (obj as any)._similarity = similarityMap.get(obj.id as string) || 0;\n return obj as ModelType & { _similarity: number };\n });\n\n results.sort((a, b) => b._similarity - a._similarity);\n\n return results;\n }\n\n /**\n * Generate missing embeddings for all objects in the collection\n *\n * Batch generates embeddings for objects that don't have them yet\n * or have stale embeddings.\n *\n * @param options - Generation options\n * @param options.batchSize - Number of objects to process at once (default: 50)\n * @param options.onProgress - Progress callback\n * @returns Promise resolving to generation statistics\n *\n * @example\n * ```typescript\n * const stats = await articles.generateMissingEmbeddings({\n * batchSize: 100,\n * onProgress: ({ completed, total }) => {\n * console.log(`Progress: ${completed}/${total}`);\n * }\n * });\n *\n * console.log(`Generated: ${stats.generated}, Skipped: ${stats.skipped}`);\n * ```\n */\n public async generateMissingEmbeddings(\n options: {\n batchSize?: number;\n onProgress?: (progress: { completed: number; total: number }) => void;\n } = {},\n ): Promise<{ generated: number; skipped: number }> {\n const { batchSize = 50, onProgress } = options;\n\n // Get embedding config\n const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(\n this._itemClass.name,\n );\n\n if (!embeddingConfig) {\n throw new Error(\n `No embedding configuration found for ${this._itemClass.name}.`,\n );\n }\n\n // Count total objects\n const total = await this.count({});\n let completed = 0;\n let generated = 0;\n let skipped = 0;\n\n // Process in batches\n let offset = 0;\n while (offset < total) {\n const batch = await this.list({ limit: batchSize, offset });\n\n for (const obj of batch) {\n try {\n // Check if embeddings are stale\n const hasStale = await (obj as any).hasStaleEmbeddings();\n if (hasStale) {\n await (obj as any).generateEmbeddings();\n generated++;\n } else {\n skipped++;\n }\n } catch (error) {\n logger.warn(`Failed to generate embeddings for ${obj.id}`, {\n error: error instanceof Error ? error.message : error,\n });\n skipped++;\n }\n\n completed++;\n if (onProgress) {\n onProgress({ completed, total });\n }\n }\n\n offset += batchSize;\n }\n\n return { generated, skipped };\n }\n}\n"],"names":["result","instance"],"mappings":"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"}
|