Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@kysera/executor

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@kysera/executor - npm Package Compare versions

Comparing version
0.8.5
to
0.8.6
+1
-1
dist/index.js

@@ -1,2 +0,2 @@

function b(t){return typeof t=="object"&&t!==null&&"tableName"in t&&"executor"in t&&typeof t.tableName=="string"}var T=["selectFrom","insertInto","updateTable","deleteFrom","replaceInto","mergeInto"],C=new Set(T),N={selectFrom:"select",insertInto:"insert",updateTable:"update",deleteFrom:"delete",replaceInto:"replace",mergeInto:"merge"},h=class extends Error{constructor(n,i,r){super(n);this.type=i;this.details=r;this.name="PluginValidationError";}};function _(t){let e=new Set;for(let n of t){if(e.has(n.name))throw new h(`Duplicate plugin: "${n.name}"`,"DUPLICATE_NAME",{pluginName:n.name});e.add(n.name);}for(let n of t){if(n.dependencies){for(let i of n.dependencies)if(!e.has(i))throw new h(`Plugin "${n.name}" requires "${i}" which is not registered`,"MISSING_DEPENDENCY",{pluginName:n.name,missingDependency:i})}if(n.conflictsWith){for(let i of n.conflictsWith)if(e.has(i))throw new h(`Plugin "${n.name}" conflicts with "${i}"`,"CONFLICT",{pluginName:n.name,conflictingPlugin:i})}}v(t);}function v(t){let e=new Map(t.map(i=>[i.name,i])),n=new Set;for(let i of t){if(n.has(i.name))continue;let r=[],u=new Set,l=[];for(r.push({name:i.name,deps:i.dependencies??[],depIndex:0}),u.add(i.name),l.push(i.name);r.length>0;){let o=r[r.length-1];if(o.depIndex>=o.deps.length){r.pop(),u.delete(o.name),l.pop(),n.add(o.name);continue}let c=o.deps[o.depIndex];if(o.depIndex++,u.has(c)){let a=l.indexOf(c),s=[...l.slice(a),c];throw new h(`Circular dependency: ${s.join(" -> ")}`,"CIRCULAR_DEPENDENCY",{pluginName:o.name,cycle:s})}if(!n.has(c)){let a=e.get(c);a&&(r.push({name:c,deps:a.dependencies??[],depIndex:0}),u.add(c),l.push(c));}}}}function P(t){if(t.length===0)return [];let e=new Map(t.map(o=>[o.name,o])),n=new Map,i=new Map;for(let o of t)n.set(o.name,0),i.set(o.name,new Set);for(let o of t)if(o.dependencies)for(let c of o.dependencies)n.set(o.name,(n.get(o.name)??0)+1),i.get(c)?.add(o.name);let r=[],u=t.filter(o=>(n.get(o.name)??0)===0),l=(o,c)=>{let a=c.priority??0,s=0,p=o.length;for(;s<p;){let f=s+p>>>1,d=o[f].priority??0;d>a||d===a&&o[f].name<c.name?s=f+1:p=f;}o.splice(s,0,c);};for(u.sort((o,c)=>{let a=o.priority??0,s=c.priority??0;return a!==s?s-a:o.name.localeCompare(c.name)});u.length>0;){let o=u.shift();if(!o)break;r.push(o);let c=i.get(o.name);if(c)for(let a of c){let s=(n.get(a)??0)-1;if(n.set(a,s),s===0){let p=e.get(a);p&&l(u,p);}}}return r}function R(t,e,n,i){let r=N[e];return u=>{let l=t[e];if(!l)throw new Error(`Method ${e} not found on Kysely instance`);let o=l.call(t,u),c=i!==void 0?{operation:r,table:u,schema:i,metadata:{}}:{operation:r,table:u,metadata:{}};for(let a of n)a.interceptQuery&&(o=a.interceptQuery(o,c));return o}}var M=new Set(["__kysera","__plugins","__rawDb"]),Q=100,K=Symbol("UNDEFINED_SENTINEL"),B=class{cache;maxSize;constructor(e){this.cache=new Map,this.maxSize=e;}get(e){let n=this.cache.get(e);if(n!==void 0)return this.cache.delete(e),this.cache.set(e,n),n===K?void 0:n}set(e,n){let i=n===void 0?K:n;if(this.cache.has(e)&&this.cache.delete(e),this.cache.set(e,i),this.cache.size>this.maxSize){let r=this.cache.keys().next().value;r!==void 0&&this.cache.delete(r);}}has(e){return this.cache.has(e)}};function y(t,e,n,i){let r=new Map,u=new Map,l=null,o=new B(Q),c={has(a,s){return M.has(s)||s==="__schema"?true:Reflect.has(a,s)},get(a,s,p){if(s==="__kysera")return true;if(s==="__plugins")return n;if(s==="__rawDb")return a;if(s==="__schema")return i;if(typeof s=="string"&&C.has(s)){let d=u.get(s);return d||(d=R(a,s,e,i),u.set(s,d)),d}if(s==="withSchema")return d=>{let g=o.get(d);if(g)return g;let m=a.withSchema(d),D=y(m,e,n,d);return o.set(d,D),D};if(s==="with"){if(!r.has("with")){let d=(g,m)=>{let D=E=>m(y(E,e,n)),x=Reflect.get(a,"with").call(a,g,D);return y(x,e,n)};r.set("with",d);}return r.get("with")}if(s==="withRecursive"){if(!r.has("withRecursive")){let d=(g,m)=>{let D=E=>m(y(E,e,n)),x=Reflect.get(a,"withRecursive").call(a,g,D);return y(x,e,n)};r.set("withRecursive",d);}return r.get("withRecursive")}if(s==="transaction")return l||(l=()=>({execute:async d=>await a.transaction().execute(async g=>{let m=y(g,e,n);return await d(m)})})),l;if(r.has(s))return r.get(s);let f=Reflect.get(a,s,p);if(typeof f=="function"){let d=f.bind(a);return r.set(s,d),d}return f}};return new Proxy(t,c)}async function V(t,e=[],n={}){let{enabled:i=true}=n;if(e.length===0||!i)return Object.assign(t,{__kysera:true,__plugins:e,__rawDb:t});_(e);let r=P(e);for(let l of r)try{await l.onInit?.(t);}catch(o){throw new h(`Plugin "${l.name}" failed to initialize: ${o instanceof Error?o.message:String(o)}`,"INITIALIZATION_FAILED",{pluginName:l.name})}let u=r.filter(l=>l.interceptQuery);return u.length===0?Object.assign(t,{__kysera:true,__plugins:r,__rawDb:t}):y(t,u,r)}function O(t,e=[],n={}){let{enabled:i=true}=n;if(e.length===0||!i)return Object.assign(t,{__kysera:true,__plugins:e,__rawDb:t});_(e);let r=P(e),u=r.filter(l=>l.interceptQuery);return u.length===0?Object.assign(t,{__kysera:true,__plugins:r,__rawDb:t}):y(t,u,r)}function A(t){return "__kysera"in t&&t.__kysera}function L(t){return t.__plugins}function $(t,e){let n=e.filter(i=>i.interceptQuery);return n.length===0?Object.assign(t,{__kysera:true,__plugins:e,__rawDb:t}):y(t,n,e)}function F(t,e,n){let i=t;for(let r of e)r.interceptQuery&&(i=r.interceptQuery(i,n));return i}function j(t){return t.__rawDb??t}async function H(t){let e=t.__plugins;for(let n=e.length-1;n>=0;n--){let i=e[n];i?.onDestroy&&await i.onDestroy();}}var w=class extends Error{constructor(n,i,r){super(n);this.schema=i;this.allowedSchemas=r;this.name="SchemaValidationError";}};function k(t={}){let{defaultSchema:e="public",resolveSchema:n,validateSchema:i,allowedSchemas:r,strictValidation:u=true}=t,l=r?new Set(r):null;return {name:"@kysera/schema",version:"1.0.0",priority:1e3,async onInit(o){if(i&&!await i(e))throw new w(`Invalid default schema: ${e}`,e);if(l&&!l.has(e)){let c=r?r.join(", "):"";throw new w(`Default schema "${e}" is not in allowed list: [${c}]`,e,r)}},interceptQuery(o,c){let a=n?.(c)??c.schema??e;if(l&&!l.has(a)){if(u){let s=r?r.join(", "):"";throw new w(`Schema "${a}" is not in allowed list: [${s}]`,a,r)}a=e;}return c.metadata.__resolvedSchema=a,o}}}function I(t){return t.metadata.__resolvedSchema}export{T as INTERCEPTED_METHODS,h as PluginValidationError,w as SchemaValidationError,F as applyPlugins,V as createExecutor,O as createExecutorSync,H as destroyExecutor,L as getPlugins,j as getRawDb,I as getResolvedSchema,A as isKyseraExecutor,b as isRepositoryLike,P as resolvePluginOrder,k as schemaPlugin,_ as validatePlugins,$ as wrapTransaction};//# sourceMappingURL=index.js.map
function b(t){return typeof t=="object"&&t!==null&&"tableName"in t&&"executor"in t&&typeof t.tableName=="string"}var T=["selectFrom","insertInto","updateTable","deleteFrom","replaceInto","mergeInto"],C=new Set(T),N={selectFrom:"select",insertInto:"insert",updateTable:"update",deleteFrom:"delete",replaceInto:"replace",mergeInto:"merge"},h=class extends Error{constructor(n,i,o){super(n);this.type=i;this.details=o;this.name="PluginValidationError";}};function _(t){let e=new Set;for(let n of t){if(e.has(n.name))throw new h(`Duplicate plugin: "${n.name}"`,"DUPLICATE_NAME",{pluginName:n.name});e.add(n.name);}for(let n of t){if(n.dependencies){for(let i of n.dependencies)if(!e.has(i))throw new h(`Plugin "${n.name}" requires "${i}" which is not registered`,"MISSING_DEPENDENCY",{pluginName:n.name,missingDependency:i})}if(n.conflictsWith){for(let i of n.conflictsWith)if(e.has(i))throw new h(`Plugin "${n.name}" conflicts with "${i}"`,"CONFLICT",{pluginName:n.name,conflictingPlugin:i})}}v(t);}function v(t){let e=new Map(t.map(i=>[i.name,i])),n=new Set;for(let i of t){if(n.has(i.name))continue;let o=[],u=new Set,l=[];for(o.push({name:i.name,deps:i.dependencies??[],depIndex:0}),u.add(i.name),l.push(i.name);o.length>0;){let r=o[o.length-1];if(r.depIndex>=r.deps.length){o.pop(),u.delete(r.name),l.pop(),n.add(r.name);continue}let c=r.deps[r.depIndex];if(r.depIndex++,u.has(c)){let a=l.indexOf(c),s=[...l.slice(a),c];throw new h(`Circular dependency: ${s.join(" -> ")}`,"CIRCULAR_DEPENDENCY",{pluginName:r.name,cycle:s})}if(!n.has(c)){let a=e.get(c);a&&(o.push({name:c,deps:a.dependencies??[],depIndex:0}),u.add(c),l.push(c));}}}}function P(t){if(t.length===0)return [];let e=new Map(t.map(r=>[r.name,r])),n=new Map,i=new Map;for(let r of t)n.set(r.name,0),i.set(r.name,new Set);for(let r of t)if(r.dependencies)for(let c of r.dependencies)n.set(r.name,(n.get(r.name)??0)+1),i.get(c)?.add(r.name);let o=[],u=t.filter(r=>(n.get(r.name)??0)===0),l=(r,c)=>{let a=c.priority??0,s=0,y=r.length;for(;s<y;){let p=s+y>>>1,d=r[p].priority??0;d>a||d===a&&r[p].name<c.name?s=p+1:y=p;}r.splice(s,0,c);};for(u.sort((r,c)=>{let a=r.priority??0,s=c.priority??0;return a!==s?s-a:r.name.localeCompare(c.name)});u.length>0;){let r=u.shift();if(!r)break;o.push(r);let c=i.get(r.name);if(c)for(let a of c){let s=(n.get(a)??0)-1;if(n.set(a,s),s===0){let y=e.get(a);y&&l(u,y);}}}return o}function R(t,e,n,i){let o=N[e];return u=>{let l=t[e];if(!l)throw new Error(`Method ${e} not found on Kysely instance`);let r=l.call(t,u),c=i!==void 0?{operation:o,table:u,schema:i,metadata:{}}:{operation:o,table:u,metadata:{}};for(let a of n)if(a.interceptQuery)try{r=a.interceptQuery(r,c);}catch(s){let y=s instanceof Error?s.message:String(s);throw new Error(`Plugin "${a.name}" threw during interceptQuery for ${c.operation} on "${c.table}": ${y}`)}return r}}var M=new Set(["__kysera","__plugins","__rawDb"]),Q=100,K=Symbol("UNDEFINED_SENTINEL"),B=class{cache;maxSize;constructor(e){this.cache=new Map,this.maxSize=e;}get(e){let n=this.cache.get(e);if(n!==void 0)return this.cache.delete(e),this.cache.set(e,n),n===K?void 0:n}set(e,n){let i=n===void 0?K:n;if(this.cache.has(e)&&this.cache.delete(e),this.cache.set(e,i),this.cache.size>this.maxSize){let o=this.cache.keys().next().value;o!==void 0&&this.cache.delete(o);}}has(e){return this.cache.has(e)}};function g(t,e,n,i){let o=new Map,u=new Map,l=null,r=new B(Q),c={has(a,s){return M.has(s)||s==="__schema"?true:Reflect.has(a,s)},get(a,s,y){if(s==="__kysera")return true;if(s==="__plugins")return n;if(s==="__rawDb")return a;if(s==="__schema")return i;if(typeof s=="string"&&C.has(s)){let d=u.get(s);return d||(d=R(a,s,e,i),u.set(s,d)),d}if(s==="withSchema")return d=>{let f=r.get(d);if(f)return f;let m=a.withSchema(d),D=g(m,e,n,d);return r.set(d,D),D};if(s==="with"){if(!o.has("with")){let d=(f,m)=>{let D=E=>m(g(E,e,n)),x=Reflect.get(a,"with").call(a,f,D);return g(x,e,n)};o.set("with",d);}return o.get("with")}if(s==="withRecursive"){if(!o.has("withRecursive")){let d=(f,m)=>{let D=E=>m(g(E,e,n)),x=Reflect.get(a,"withRecursive").call(a,f,D);return g(x,e,n)};o.set("withRecursive",d);}return o.get("withRecursive")}if(s==="transaction")return l||(l=()=>({execute:async d=>await a.transaction().execute(async f=>{let m=g(f,e,n);return await d(m)})})),l;if(o.has(s))return o.get(s);let p=Reflect.get(a,s,y);if(typeof p=="function"){let d=p.bind(a);return o.set(s,d),d}return p}};return new Proxy(t,c)}async function V(t,e=[],n={}){let{enabled:i=true}=n;if(e.length===0||!i)return Object.assign(t,{__kysera:true,__plugins:e,__rawDb:t});_(e);let o=P(e);for(let l of o)try{await l.onInit?.(t);}catch(r){throw new h(`Plugin "${l.name}" failed to initialize: ${r instanceof Error?r.message:String(r)}`,"INITIALIZATION_FAILED",{pluginName:l.name})}let u=o.filter(l=>l.interceptQuery);return u.length===0?Object.assign(t,{__kysera:true,__plugins:o,__rawDb:t}):g(t,u,o)}function O(t,e=[],n={}){let{enabled:i=true}=n;if(e.length===0||!i)return Object.assign(t,{__kysera:true,__plugins:e,__rawDb:t});_(e);let o=P(e),u=o.filter(l=>l.interceptQuery);return u.length===0?Object.assign(t,{__kysera:true,__plugins:o,__rawDb:t}):g(t,u,o)}function $(t){return "__kysera"in t&&t.__kysera}function A(t){return t.__plugins}function L(t,e){let n=e.filter(i=>i.interceptQuery);return n.length===0?Object.assign(t,{__kysera:true,__plugins:e,__rawDb:t}):g(t,n,e)}function F(t,e,n){let i=t;for(let o of e)if(o.interceptQuery)try{i=o.interceptQuery(i,n);}catch(u){let l=u instanceof Error?u.message:String(u);throw new Error(`Plugin "${o.name}" threw during interceptQuery for ${n.operation} on "${n.table}": ${l}`)}return i}function j(t){return t.__rawDb??t}async function H(t){let e=t.__plugins;for(let n=e.length-1;n>=0;n--){let i=e[n];i?.onDestroy&&await i.onDestroy();}}var w=class extends Error{constructor(n,i,o){super(n);this.schema=i;this.allowedSchemas=o;this.name="SchemaValidationError";}};function k(t={}){let{defaultSchema:e="public",resolveSchema:n,validateSchema:i,allowedSchemas:o,strictValidation:u=true}=t,l=o?new Set(o):null;return {name:"@kysera/schema",version:"1.0.0",priority:1e3,async onInit(r){if(i&&!await i(e))throw new w(`Invalid default schema: ${e}`,e);if(l&&!l.has(e)){let c=o.join(", ");throw new w(`Default schema "${e}" is not in allowed list: [${c}]`,e,o)}},interceptQuery(r,c){let a=n?.(c)??c.schema??e;if(l&&!l.has(a)){if(u){let s=o.join(", ");throw new w(`Schema "${a}" is not in allowed list: [${s}]`,a,o)}a=e;}return c.metadata.__resolvedSchema=a,r}}}function I(t){return t.metadata.__resolvedSchema}export{T as INTERCEPTED_METHODS,h as PluginValidationError,w as SchemaValidationError,F as applyPlugins,V as createExecutor,O as createExecutorSync,H as destroyExecutor,A as getPlugins,j as getRawDb,I as getResolvedSchema,$ as isKyseraExecutor,b as isRepositoryLike,P as resolvePluginOrder,k as schemaPlugin,_ as validatePlugins,L as wrapTransaction};//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts","../src/executor.ts","../src/plugins/schema-plugin.ts"],"names":["isRepositoryLike","obj","INTERCEPTED_METHODS","INTERCEPTED_METHODS_SET","METHOD_TO_OPERATION","PluginValidationError","message","type","details","validatePlugins","plugins","names","plugin","dep","conflict","detectCircularDependencies","map","p","visited","stack","inStack","path","frame","start","cycle","depPlugin","resolvePluginOrder","inDegree","dependents","result","available","insertSorted","arr","priority","left","right","mid","midPriority","a","b","pA","pB","current","deps","newDegree","createInterceptedMethod","db","method","interceptors","currentSchema","operation","table","originalMethod","qb","context","MARKER_PROPS","MAX_CACHE_SIZE","UNDEFINED_SENTINEL","LRUCache","maxSize","key","value","wrappedValue","firstKey","createProxy","allPlugins","methodCache","interceptedCache","cachedTransactionWrapper","schemaProxyCache","handler","target","prop","receiver","intercepted","schema","cachedSchemaProxy","schemaDb","newProxy","withWrapper","name","fn","wrappedFn","innerDb","withRecursiveWrapper","trx","wrappedTrx","bound","createExecutor","config","enabled","sorted","error","createExecutorSync","isKyseraExecutor","getPlugins","executor","wrapTransaction","applyPlugins","getRawDb","destroyExecutor","i","SchemaValidationError","allowedSchemas","schemaPlugin","options","defaultSchema","resolveSchema","validateSchema","strictValidation","allowedSet","_db","allowedList","getResolvedSchema"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @kysera/executor - Unified Execution Layer Types\n * @module @kysera/executor\n */\n\nimport type { Kysely, Transaction } from 'kysely'\n\n/**\n * Query builder context passed to plugin interceptors\n */\nexport interface QueryBuilderContext {\n /** Type of operation */\n readonly operation: 'select' | 'insert' | 'update' | 'delete' | 'replace' | 'merge'\n /** Table name */\n readonly table: string\n /**\n * Current schema context (if withSchema was called).\n * undefined means default schema is being used.\n */\n readonly schema?: string\n /** Additional metadata */\n readonly metadata: Record<string, unknown>\n}\n\n/**\n * Plugin interface - unified for both Repository and DAL patterns\n *\n * Plugins can:\n * - Intercept queries before execution (interceptQuery)\n * - Extend repositories with additional methods (extendRepository)\n * - Initialize resources on startup (onInit)\n * - Cleanup resources on shutdown (onDestroy)\n */\nexport interface Plugin {\n /** Unique plugin name */\n readonly name: string\n /** Plugin version */\n readonly version: string\n /** Plugin dependencies (must be loaded first) */\n readonly dependencies?: readonly string[]\n /** Higher priority = runs first (default: 0) */\n readonly priority?: number\n /** Plugins that conflict with this one */\n readonly conflictsWith?: readonly string[]\n\n /**\n * Lifecycle: Called once when plugin is initialized\n * @param db - Kysely database instance (not the executor wrapper)\n */\n onInit?<DB>(db: Kysely<DB>): Promise<void> | void\n\n /**\n * Lifecycle: Called when executor is being destroyed\n * Use for cleanup, closing connections, clearing timers, etc.\n */\n onDestroy?(): Promise<void> | void\n\n /**\n * Query interception: Modify query builder before execution\n * Works in both Repository and DAL patterns via @kysera/executor\n *\n * @typeParam QB - Query builder type (intentionally unconstrained)\n *\n * **Type Safety Note:**\n * QB is intentionally unconstrained (not constrained to any base type) because:\n *\n * 1. **Kysely's query builders lack a shared interface**: SelectQueryBuilder,\n * InsertQueryBuilder, UpdateQueryBuilder, DeleteQueryBuilder, and MergeQueryBuilder\n * don't share a common base interface that includes query modification methods\n * like where(), and(), etc.\n *\n * 2. **Each builder has unique generic parameters**: Even if they implemented\n * Compilable<unknown>, their type parameters differ (DB, TB, O, UT, etc.),\n * making it impossible to express a shared constraint.\n *\n * 3. **Type inference requirement**: The QB type must be preserved exactly as-is\n * through the plugin chain. Any constraint would break this preservation.\n *\n * **Plugin Implementation Pattern:**\n * Plugins must handle type safety internally by:\n * 1. Checking the operation type from context.operation\n * 2. Casting to the appropriate specific builder type\n * 3. Using type assertions (documented in plugin code)\n *\n * @example\n * ```typescript\n * interceptQuery<QB>(qb: QB, context: QueryBuilderContext): QB {\n * if (context.operation === 'select') {\n * // Cast to SelectQueryBuilder for type-safe WHERE clause\n * type GenericSelect = SelectQueryBuilder<Record<string, unknown>, string, Record<string, unknown>>;\n * return (qb as unknown as GenericSelect)\n * .where('deleted_at', 'is', null) as QB;\n * }\n * return qb;\n * }\n * ```\n */\n interceptQuery?<QB>(qb: QB, context: QueryBuilderContext): QB\n\n /**\n * Repository extensions: Add methods to repositories (Repository pattern only)\n */\n extendRepository?<T extends object>(repo: T): T\n}\n\n/**\n * Marker interface for KyseraExecutor\n */\nexport interface KyseraExecutorMarker<DB = unknown> {\n readonly __kysera: true\n readonly __plugins: readonly Plugin[]\n /** Raw Kysely instance bypassing plugin interceptors (for internal plugin use) */\n readonly __rawDb: Kysely<DB>\n /**\n * Current schema context (if withSchema was called).\n * undefined means default schema is being used.\n */\n readonly __schema?: string\n}\n\n/**\n * Plugin-aware Kysely wrapper type\n * Extends Kysely with plugin interception capabilities\n */\nexport type KyseraExecutor<DB> = Kysely<DB> & KyseraExecutorMarker<DB>\n\n/**\n * Plugin-aware Transaction wrapper type\n */\nexport type KyseraTransaction<DB> = Transaction<DB> & KyseraExecutorMarker<DB>\n\n/**\n * Union type for any Kysera executor (database or transaction)\n */\nexport type AnyKyseraExecutor<DB> = KyseraExecutor<DB> | KyseraTransaction<DB>\n\n/**\n * Configuration for executor creation\n */\nexport interface ExecutorConfig {\n /** Enable/disable plugin interception at runtime */\n readonly enabled?: boolean\n}\n\n/**\n * Plugin validation error details\n */\nexport interface PluginValidationDetails {\n readonly pluginName: string\n readonly missingDependency?: string\n readonly conflictingPlugin?: string\n readonly cycle?: readonly string[]\n}\n\n/**\n * Plugin validation error types\n */\nexport type PluginValidationErrorType =\n | 'DUPLICATE_NAME'\n | 'MISSING_DEPENDENCY'\n | 'CONFLICT'\n | 'CIRCULAR_DEPENDENCY'\n | 'INITIALIZATION_FAILED'\n\n/**\n * Base repository interface for plugin extensions.\n * Plugins should use this interface to type-check repository objects.\n *\n * This interface represents the minimum contract that a repository-like object\n * must fulfill to be extended by plugins. It's designed to work with both\n * the @kysera/repository pattern and custom repository implementations.\n *\n * @template DB - The database schema type (defaults to unknown for flexibility)\n *\n * @example\n * ```typescript\n * import type { BaseRepositoryLike } from '@kysera/executor'\n *\n * // In a plugin's extendRepository method:\n * extendRepository<T extends object>(repo: T): T {\n * if (!isRepositoryLike(repo)) {\n * return repo // Not a repository, skip extension\n * }\n *\n * // Now we can safely access repo.tableName and repo.executor\n * const { tableName, executor } = repo\n * // ... extend the repository\n * }\n * ```\n */\nexport interface BaseRepositoryLike<DB = unknown> {\n /** The name of the database table this repository manages */\n readonly tableName: string\n /** The Kysely executor (database or transaction) */\n readonly executor: Kysely<DB>\n /** Find a record by its primary key */\n findById?: (id: unknown) => Promise<unknown>\n /** Find all records in the table */\n findAll?: () => Promise<unknown[]>\n /** Create a new record */\n create?: (data: unknown) => Promise<unknown>\n /** Update an existing record by primary key */\n update?: (id: unknown, data: unknown) => Promise<unknown>\n /** Delete a record by primary key (returns deleted record or boolean) */\n delete?: (id: unknown) => Promise<unknown>\n}\n\n/**\n * Type guard to check if an object is a repository-like object.\n *\n * This function checks for the minimum required properties of a repository:\n * - `tableName`: A string identifying the database table\n * - `executor`: A Kysely instance for database operations\n *\n * @param obj - The object to check\n * @returns True if the object is repository-like, false otherwise\n *\n * @example\n * ```typescript\n * import { isRepositoryLike } from '@kysera/executor'\n *\n * function processRepo(maybeRepo: unknown) {\n * if (isRepositoryLike(maybeRepo)) {\n * console.log(`Repository for table: ${maybeRepo.tableName}`)\n * }\n * }\n * ```\n */\nexport function isRepositoryLike<DB = unknown>(obj: unknown): obj is BaseRepositoryLike<DB> {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'tableName' in obj &&\n 'executor' in obj &&\n typeof (obj as Record<string, unknown>)['tableName'] === 'string'\n )\n}\n","/**\n * @kysera/executor - KyseraExecutor Implementation\n * @module @kysera/executor\n *\n * ## Architecture Notes\n *\n * ### Type System Constraints\n *\n * This implementation uses type assertions due to Kysely's complex type system.\n * All assertions are documented inline and verified safe through runtime behavior.\n *\n * **Type Assertion Categories:**\n *\n * 1. **Plugin interceptQuery** (Line 261)\n * - Issue: QB is constrained to `Compilable<unknown>` but query builders have\n * incompatible method signatures (where, and, etc.)\n * - Safety: Plugin authors must cast based on `context.operation` type\n * - Alternative: None - Kysely lacks a shared interface for query modification\n *\n * 2. **Transaction wrapping** (Lines 326, 332)\n * - Issue: Transaction<DB> extends Kysely<DB> but proxy requires Kysely type\n * - Safety: Structural compatibility verified - Transaction IS-A Kysely\n * - Alternative: None - TypeScript requires explicit cast despite structural typing\n *\n * 3. **Dynamic method access** (Line 245)\n * - Issue: Kysely<DB> lacks index signature for dynamic property access\n * - Safety: Method names validated against INTERCEPTED_METHODS constant\n * - Alternative: None - Cannot use mapped types with runtime method names\n *\n * 4. **Object.assign marker properties** (Lines 394, 427, 473, 488, 527)\n * - Issue: Object.assign returns intersection type (Kysely & Marker)\n * - Safety: Type assertion to union type (KyseraExecutor/KyseraTransaction)\n * - Alternative: Manual object spread (less performant, same type assertion needed)\n *\n * 5. **wrapTransaction cast chain** (Lines 539, 542)\n * - Issue: Transaction -> Kysely -> Proxy -> KyseraTransaction requires casts\n * - Safety: All types structurally compatible; verified in tests\n * - Alternative: None - TypeScript nominal types would solve this\n *\n * 6. **getRawDb executor check** (Line 588)\n * - Issue: Need to check if plain Kysely has __rawDb property\n * - Safety: Optional chaining handles both KyseraExecutor and plain Kysely\n * - Alternative: Type guard (more verbose, same runtime behavior)\n *\n * ### Transaction API Limitation\n *\n * The wrapped transaction only exposes `.execute()` method, not `.setIsolationLevel()`.\n * This is intentional: isolation level should be set before plugin interception.\n *\n * **Rationale:**\n * - Isolation level is a transaction-level concern, not a query-level concern\n * - Setting isolation level after plugin initialization could cause inconsistencies\n * - Keeps the wrapper API simple and focused on query interception\n *\n * **Escape Hatch:**\n * ```typescript\n * executor.__rawDb.transaction().setIsolationLevel('serializable').execute(...)\n * ```\n *\n * This design keeps the plugin system simple while allowing escape hatches.\n */\n\nimport type { Kysely, Transaction } from 'kysely'\nimport type {\n Plugin,\n KyseraExecutor,\n KyseraTransaction,\n QueryBuilderContext,\n ExecutorConfig,\n PluginValidationErrorType,\n PluginValidationDetails\n} from './types.js'\n\n/** Methods that accept table name and should be intercepted */\nexport const INTERCEPTED_METHODS = [\n 'selectFrom',\n 'insertInto',\n 'updateTable',\n 'deleteFrom',\n 'replaceInto', // MySQL REPLACE\n 'mergeInto' // SQL MERGE (Kysely 0.28.x)\n] as const\n\nexport type InterceptedMethod = (typeof INTERCEPTED_METHODS)[number]\n\n/** Pre-computed Set for O(1) lookup instead of Array.includes O(n) */\nconst INTERCEPTED_METHODS_SET = new Set<string>(INTERCEPTED_METHODS)\n\n/** Map method names to operation types */\nconst METHOD_TO_OPERATION: Record<InterceptedMethod, QueryBuilderContext['operation']> = {\n selectFrom: 'select',\n insertInto: 'insert',\n updateTable: 'update',\n deleteFrom: 'delete',\n replaceInto: 'replace',\n mergeInto: 'merge'\n}\n\n/**\n * Plugin validation error\n */\nexport class PluginValidationError extends Error {\n constructor(\n message: string,\n public readonly type: PluginValidationErrorType,\n public readonly details: PluginValidationDetails\n ) {\n super(message)\n this.name = 'PluginValidationError'\n }\n}\n\n/**\n * Validate plugins for conflicts, duplicates, and missing dependencies\n */\nexport function validatePlugins(plugins: readonly Plugin[]): void {\n const names = new Set<string>()\n\n for (const plugin of plugins) {\n if (names.has(plugin.name)) {\n throw new PluginValidationError(`Duplicate plugin: \"${plugin.name}\"`, 'DUPLICATE_NAME', {\n pluginName: plugin.name\n })\n }\n names.add(plugin.name)\n }\n\n for (const plugin of plugins) {\n if (plugin.dependencies) {\n for (const dep of plugin.dependencies) {\n if (!names.has(dep)) {\n throw new PluginValidationError(\n `Plugin \"${plugin.name}\" requires \"${dep}\" which is not registered`,\n 'MISSING_DEPENDENCY',\n { pluginName: plugin.name, missingDependency: dep }\n )\n }\n }\n }\n\n if (plugin.conflictsWith) {\n for (const conflict of plugin.conflictsWith) {\n if (names.has(conflict)) {\n throw new PluginValidationError(\n `Plugin \"${plugin.name}\" conflicts with \"${conflict}\"`,\n 'CONFLICT',\n { pluginName: plugin.name, conflictingPlugin: conflict }\n )\n }\n }\n }\n }\n\n detectCircularDependencies(plugins)\n}\n\n/**\n * Detect circular dependencies using iterative DFS\n * Prevents stack overflow with deep dependency chains\n */\nfunction detectCircularDependencies(plugins: readonly Plugin[]): void {\n const map = new Map(plugins.map(p => [p.name, p]))\n const visited = new Set<string>()\n\n for (const plugin of plugins) {\n if (visited.has(plugin.name)) continue\n\n // Iterative DFS using explicit stack\n const stack: { name: string; deps: readonly string[]; depIndex: number }[] = []\n const inStack = new Set<string>()\n const path: string[] = []\n\n stack.push({ name: plugin.name, deps: plugin.dependencies ?? [], depIndex: 0 })\n inStack.add(plugin.name)\n path.push(plugin.name)\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1]!\n\n if (frame.depIndex >= frame.deps.length) {\n // Done with this node, backtrack\n stack.pop()\n inStack.delete(frame.name)\n path.pop()\n visited.add(frame.name)\n continue\n }\n\n const dep = frame.deps[frame.depIndex]!\n frame.depIndex++\n\n if (inStack.has(dep)) {\n // Cycle detected\n const start = path.indexOf(dep)\n const cycle = [...path.slice(start), dep]\n throw new PluginValidationError(\n `Circular dependency: ${cycle.join(' -> ')}`,\n 'CIRCULAR_DEPENDENCY',\n { pluginName: frame.name, cycle }\n )\n }\n\n if (!visited.has(dep)) {\n const depPlugin = map.get(dep)\n if (depPlugin) {\n stack.push({ name: dep, deps: depPlugin.dependencies ?? [], depIndex: 0 })\n inStack.add(dep)\n path.push(dep)\n }\n }\n }\n }\n}\n\n/**\n * Resolve plugin execution order using topological sort with priority\n */\nexport function resolvePluginOrder(plugins: readonly Plugin[]): Plugin[] {\n if (plugins.length === 0) return []\n\n const map = new Map(plugins.map(p => [p.name, p]))\n const inDegree = new Map<string, number>()\n const dependents = new Map<string, Set<string>>()\n\n for (const plugin of plugins) {\n inDegree.set(plugin.name, 0)\n dependents.set(plugin.name, new Set())\n }\n\n for (const plugin of plugins) {\n if (plugin.dependencies) {\n for (const dep of plugin.dependencies) {\n inDegree.set(plugin.name, (inDegree.get(plugin.name) ?? 0) + 1)\n dependents.get(dep)?.add(plugin.name)\n }\n }\n }\n\n const result: Plugin[] = []\n const available = plugins.filter(p => (inDegree.get(p.name) ?? 0) === 0)\n\n // Helper to maintain sorted order efficiently (descending priority, then alphabetical)\n const insertSorted = (arr: Plugin[], plugin: Plugin): void => {\n const priority = plugin.priority ?? 0\n let left = 0\n let right = arr.length\n\n // Binary search for insertion point (O(log n))\n // We want descending priority (high to low), then alphabetical\n while (left < right) {\n const mid = (left + right) >>> 1\n const midPriority = arr[mid]!.priority ?? 0\n // If mid has higher priority, or same priority but earlier name, insert after mid\n if (midPriority > priority || (midPriority === priority && arr[mid]!.name < plugin.name)) {\n left = mid + 1\n } else {\n right = mid\n }\n }\n arr.splice(left, 0, plugin)\n }\n\n // Initial sort: descending priority (high to low), then alphabetical\n available.sort((a, b) => {\n const pA = a.priority ?? 0\n const pB = b.priority ?? 0\n return pA !== pB ? pB - pA : a.name.localeCompare(b.name)\n })\n\n while (available.length > 0) {\n // Take first element (highest priority): O(1) with shift\n const current = available.shift()\n // Safety: available.length > 0 check ensures current is defined\n if (!current) break\n result.push(current)\n\n const deps = dependents.get(current.name)\n if (deps) {\n for (const dep of deps) {\n const newDegree = (inDegree.get(dep) ?? 0) - 1\n inDegree.set(dep, newDegree)\n if (newDegree === 0) {\n const plugin = map.get(dep)\n // Insert maintaining sorted order: O(log n) search + O(n) splice\n // Overall complexity: O(n log n) instead of O(n²)\n if (plugin) insertSorted(available, plugin)\n }\n }\n }\n }\n\n return result\n}\n\n/**\n * Create intercepted method that applies plugins\n *\n * @param db - Kysely database instance\n * @param method - Method name being intercepted\n * @param interceptors - Plugins with interceptQuery methods\n * @param currentSchema - Optional schema context (from withSchema)\n */\nfunction createInterceptedMethod<DB>(\n db: Kysely<DB>,\n method: InterceptedMethod,\n interceptors: readonly Plugin[],\n currentSchema?: string\n): (table: string) => unknown {\n const operation = METHOD_TO_OPERATION[method]\n\n return (table: string) => {\n /**\n * TYPE ASSERTION #3: Dynamic method access\n *\n * Cast: Kysely<DB> -> Record<string, (t: string) => unknown>\n *\n * Why needed:\n * - Kysely<DB> interface doesn't have an index signature\n * - TypeScript doesn't allow db[method] for dynamic property access\n *\n * Why safe:\n * - Method name validated against INTERCEPTED_METHODS constant\n * - Runtime check throws if method doesn't exist\n * - All intercepted methods have signature: (table: string) => QueryBuilder\n */\n const originalMethod = (db as unknown as Record<string, (t: string) => unknown>)[method]\n if (!originalMethod) {\n throw new Error(`Method ${method} not found on Kysely instance`)\n }\n // Call with correct 'this' context\n let qb = originalMethod.call(db, table)\n\n // Apply interceptors with schema context\n // Use spread to conditionally include schema only when defined\n const context: QueryBuilderContext = currentSchema !== undefined\n ? { operation, table, schema: currentSchema, metadata: {} }\n : { operation, table, metadata: {} }\n\n for (const plugin of interceptors) {\n if (plugin.interceptQuery) {\n qb = plugin.interceptQuery(qb, context)\n }\n }\n\n return qb\n }\n}\n\n/** Marker properties Set for fast O(1) lookup */\nconst MARKER_PROPS = new Set<string | symbol>(['__kysera', '__plugins', '__rawDb'])\n\n/** Maximum size for LRU caches to prevent unbounded growth */\nconst MAX_CACHE_SIZE = 100\n\n/**\n * Sentinel value to distinguish \"cached undefined\" from \"not in cache\"\n * @internal\n */\nconst UNDEFINED_SENTINEL = Symbol('UNDEFINED_SENTINEL')\n\n/**\n * Wrapper type for cache values to handle undefined correctly\n * @internal\n */\ntype CacheValue<V> = V | typeof UNDEFINED_SENTINEL\n\n/**\n * Simple LRU cache implementation to prevent unbounded cache growth.\n *\n * Correctly handles undefined values using a sentinel pattern:\n * - get() returns undefined for both \"cached undefined\" and \"not in cache\"\n * - has() returns true only if key is actually in cache (even if value is undefined)\n *\n * @internal\n */\nclass LRUCache<K, V> {\n private cache: Map<K, CacheValue<V>>\n private readonly maxSize: number\n\n constructor(maxSize: number) {\n this.cache = new Map()\n this.maxSize = maxSize\n }\n\n get(key: K): V | undefined {\n const value = this.cache.get(key)\n if (value !== undefined) {\n // Move to end (most recently used)\n this.cache.delete(key)\n this.cache.set(key, value)\n // Unwrap sentinel value\n return value === UNDEFINED_SENTINEL ? undefined : value\n }\n return undefined\n }\n\n set(key: K, value: V): void {\n // Wrap undefined values with sentinel\n const wrappedValue: CacheValue<V> = value === undefined ? UNDEFINED_SENTINEL : value\n\n // Delete if exists to move to end\n if (this.cache.has(key)) {\n this.cache.delete(key)\n }\n this.cache.set(key, wrappedValue)\n\n // Evict oldest (first) entry if size exceeded\n if (this.cache.size > this.maxSize) {\n const firstKey = this.cache.keys().next().value\n if (firstKey !== undefined) {\n this.cache.delete(firstKey)\n }\n }\n }\n\n has(key: K): boolean {\n return this.cache.has(key)\n }\n}\n\n/**\n * Create plugin-aware executor using Proxy\n * Optimized with LRU caching and Set-based lookups\n *\n * @param db - Kysely database instance\n * @param interceptors - Plugins with interceptQuery methods\n * @param allPlugins - All registered plugins\n * @param currentSchema - Optional schema context (from withSchema)\n */\nfunction createProxy<DB>(\n db: Kysely<DB>,\n interceptors: readonly Plugin[],\n allPlugins: readonly Plugin[],\n currentSchema?: string\n): KyseraExecutor<DB> {\n // Cache for bound methods to avoid repeated .bind() allocations\n const methodCache = new Map<string | symbol, unknown>()\n\n // Cache intercepted methods to avoid repeated creation\n const interceptedCache = new Map<string, (table: string) => unknown>()\n\n // Cached transaction wrapper (created once, reused)\n let cachedTransactionWrapper:\n | (() => { execute: <T>(fn: (trx: Transaction<DB>) => Promise<T>) => Promise<T> })\n | null = null\n\n // LRU cache for withSchema to prevent unbounded growth (max 100 schemas)\n const schemaProxyCache = new LRUCache<string, KyseraExecutor<DB>>(MAX_CACHE_SIZE)\n\n const handler: ProxyHandler<Kysely<DB>> = {\n // Handle 'in' operator for type guards\n has(target, prop) {\n if (MARKER_PROPS.has(prop)) return true\n if (prop === '__schema') return true\n return Reflect.has(target, prop)\n },\n\n get(target, prop, receiver) {\n // Fast path: marker properties (O(1) Set lookup)\n if (prop === '__kysera') return true\n if (prop === '__plugins') return allPlugins\n if (prop === '__rawDb') return target\n if (prop === '__schema') return currentSchema\n\n // Fast path: check intercepted methods first (most common hot path)\n if (typeof prop === 'string' && INTERCEPTED_METHODS_SET.has(prop)) {\n let intercepted = interceptedCache.get(prop)\n if (!intercepted) {\n intercepted = createInterceptedMethod(target, prop as InterceptedMethod, interceptors, currentSchema)\n interceptedCache.set(prop, intercepted)\n }\n return intercepted\n }\n\n // Intercept withSchema to maintain plugin proxy and track schema\n if (prop === 'withSchema') {\n return (schema: string) => {\n const cachedSchemaProxy = schemaProxyCache.get(schema)\n if (cachedSchemaProxy) {\n return cachedSchemaProxy\n }\n const schemaDb = target.withSchema(schema)\n // Pass schema to new proxy so it's available in QueryBuilderContext\n const newProxy = createProxy(schemaDb, interceptors, allPlugins, schema)\n schemaProxyCache.set(schema, newProxy)\n return newProxy\n }\n }\n\n // Intercept with() for CTEs - cache the wrapper and also wrap the result\n if (prop === 'with') {\n if (!methodCache.has('with')) {\n const withWrapper = (name: string, fn: (db: Kysely<DB>) => unknown): unknown => {\n const wrappedFn = (innerDb: Kysely<DB>): unknown =>\n fn(createProxy(innerDb, interceptors, allPlugins))\n const originalMethod = Reflect.get(target, 'with') as (\n n: string,\n f: (db: Kysely<DB>) => unknown\n ) => Kysely<DB>\n const result = originalMethod.call(target, name, wrappedFn)\n return createProxy(result, interceptors, allPlugins)\n }\n methodCache.set('with', withWrapper)\n }\n return methodCache.get('with')\n }\n\n // Intercept withRecursive() for recursive CTEs - cache the wrapper and wrap result\n if (prop === 'withRecursive') {\n if (!methodCache.has('withRecursive')) {\n const withRecursiveWrapper = (name: string, fn: (db: Kysely<DB>) => unknown): unknown => {\n const wrappedFn = (innerDb: Kysely<DB>): unknown =>\n fn(createProxy(innerDb, interceptors, allPlugins))\n const originalMethod = Reflect.get(target, 'withRecursive') as (\n n: string,\n f: (db: Kysely<DB>) => unknown\n ) => Kysely<DB>\n const result = originalMethod.call(target, name, wrappedFn)\n return createProxy(result, interceptors, allPlugins)\n }\n methodCache.set('withRecursive', withRecursiveWrapper)\n }\n return methodCache.get('withRecursive')\n }\n\n // Cached transaction wrapper\n // NOTE: Transaction API limitation - only execute() method is wrapped\n // Methods like setIsolationLevel() are not available on the wrapper\n // This is intentional: isolation level should be set before plugin interception\n // For advanced use cases, use: executor.__rawDb.transaction().setIsolationLevel(...).execute(...)\n if (prop === 'transaction') {\n if (!cachedTransactionWrapper) {\n cachedTransactionWrapper = () => ({\n execute: async <T>(fn: (trx: Transaction<DB>) => Promise<T>): Promise<T> => {\n return await target.transaction().execute(async trx => {\n /**\n * TYPE ASSERTION #2a: Transaction to Kysely for proxy creation\n *\n * Cast: Transaction<DB> -> Kysely<DB>\n *\n * Why needed:\n * - createProxy expects Kysely<DB>, not Transaction<DB>\n * - TypeScript doesn't recognize structural compatibility automatically\n *\n * Why safe:\n * - Transaction<DB> extends Kysely<DB> (verified in Kysely types)\n * - All Kysely methods are available on Transaction\n * - createProxy only accesses Kysely methods\n */\n const wrappedTrx = createProxy(\n trx as unknown as Kysely<DB>,\n interceptors,\n allPlugins\n )\n /**\n * TYPE ASSERTION #2b: Wrapped proxy back to Transaction\n *\n * Cast: KyseraExecutor<DB> -> Transaction<DB>\n *\n * Why needed:\n * - User callback expects Transaction<DB>, not KyseraExecutor<DB>\n * - Proxy wraps a Transaction but returns KyseraExecutor type\n *\n * Why safe:\n * - Original trx is Transaction<DB>\n * - Proxy preserves all Transaction methods\n * - Only adds marker properties (__kysera, __plugins, __rawDb)\n */\n return await fn(wrappedTrx as unknown as Transaction<DB>)\n })\n }\n })\n }\n return cachedTransactionWrapper\n }\n\n // Check method cache for bound functions\n if (methodCache.has(prop)) {\n return methodCache.get(prop)\n }\n\n const value = Reflect.get(target, prop, receiver)\n\n // Cache bound methods to avoid repeated .bind() allocations\n if (typeof value === 'function') {\n const bound = value.bind(target)\n methodCache.set(prop, bound)\n return bound\n }\n\n return value\n }\n }\n\n return new Proxy(db, handler) as KyseraExecutor<DB>\n}\n\n/**\n * Create a plugin-aware executor\n *\n * Zero overhead if no plugins have interceptQuery\n *\n * @param db - Kysely database instance\n * @param plugins - Array of plugins to apply\n * @param config - Optional configuration\n * @returns Plugin-aware executor\n *\n * @example\n * ```typescript\n * import { createExecutor } from '@kysera/executor';\n * import { softDeletePlugin } from '@kysera/soft-delete';\n *\n * const executor = await createExecutor(db, [softDeletePlugin()]);\n *\n * // All queries now have soft-delete filter applied\n * const users = await executor.selectFrom('users').selectAll().execute();\n * ```\n */\nexport async function createExecutor<DB>(\n db: Kysely<DB>,\n plugins: readonly Plugin[] = [],\n config: ExecutorConfig = {}\n): Promise<KyseraExecutor<DB>> {\n const { enabled = true } = config\n\n // Fast path: no plugins or disabled\n if (plugins.length === 0 || !enabled) {\n /**\n * TYPE ASSERTION #4a: Object.assign result to KyseraExecutor\n *\n * Cast: Kysely<DB> & KyseraExecutorMarker<DB> -> KyseraExecutor<DB>\n *\n * Why needed:\n * - Object.assign returns intersection type (Kysely & Marker)\n * - KyseraExecutor is defined as: type KyseraExecutor<DB> = Kysely<DB> & KyseraExecutorMarker<DB>\n * - TypeScript treats intersection types different from type aliases\n *\n * Why safe:\n * - We're adding exactly the marker properties defined in KyseraExecutorMarker\n * - Runtime type is identical to KyseraExecutor type definition\n * - No structural difference between intersection and type alias at runtime\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: plugins,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n // Validate and sort plugins\n validatePlugins(plugins)\n const sorted = resolvePluginOrder(plugins)\n\n // Initialize plugins with error handling\n for (const plugin of sorted) {\n try {\n await plugin.onInit?.(db)\n } catch (error) {\n throw new PluginValidationError(\n `Plugin \"${plugin.name}\" failed to initialize: ${error instanceof Error ? error.message : String(error)}`,\n 'INITIALIZATION_FAILED',\n { pluginName: plugin.name }\n )\n }\n }\n\n // Filter plugins with interceptQuery for performance\n const interceptors = sorted.filter(p => p.interceptQuery)\n\n // Fast path: no interceptors\n if (interceptors.length === 0) {\n /**\n * TYPE ASSERTION #4b: Object.assign result to KyseraExecutor (no interceptors)\n *\n * Same as #4a but with sorted plugins instead of input plugins.\n * This path is for plugins that have onInit but no interceptQuery.\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: sorted,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n // Create proxy with interception\n return createProxy(db, interceptors, sorted)\n}\n\n/**\n * Creates executor synchronously WITHOUT calling plugin onInit hooks.\n *\n * @warning This function skips plugin initialization. Use createExecutor()\n * instead unless you are certain plugins don't need async initialization.\n *\n * Use cases where this is safe:\n * - Plugins without onInit hooks\n * - Plugins with synchronous-only initialization\n * - Testing scenarios where initialization is handled separately\n *\n * @param db - Kysely database instance\n * @param plugins - Array of plugins to apply\n * @param config - Optional configuration\n * @returns Plugin-aware executor (without onInit called)\n *\n * @example\n * ```typescript\n * // Use for simple plugins without async init:\n * const executor = createExecutorSync(db, [simplePlugin]);\n *\n * // WARNING: Plugin onInit hooks are NOT called!\n * // If your plugin requires initialization, use createExecutor() instead.\n * ```\n */\nexport function createExecutorSync<DB>(\n db: Kysely<DB>,\n plugins: readonly Plugin[] = [],\n config: ExecutorConfig = {}\n): KyseraExecutor<DB> {\n const { enabled = true } = config\n\n if (plugins.length === 0 || !enabled) {\n /**\n * TYPE ASSERTION #4c: Object.assign in createExecutorSync (no plugins/disabled)\n *\n * Same as #4a - see explanation there.\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: plugins,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n validatePlugins(plugins)\n const sorted = resolvePluginOrder(plugins)\n const interceptors = sorted.filter(p => p.interceptQuery)\n\n if (interceptors.length === 0) {\n /**\n * TYPE ASSERTION #4d: Object.assign in createExecutorSync (no interceptors)\n *\n * Same as #4b - see explanation there.\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: sorted,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n return createProxy(db, interceptors, sorted)\n}\n\n/**\n * Check if value is a KyseraExecutor\n */\nexport function isKyseraExecutor<DB>(\n value: Kysely<DB> | KyseraExecutor<DB>\n): value is KyseraExecutor<DB> {\n return '__kysera' in value && value.__kysera\n}\n\n/**\n * Get plugins from executor\n */\nexport function getPlugins<DB>(executor: KyseraExecutor<DB>): readonly Plugin[] {\n return executor.__plugins\n}\n\n/**\n * Wrap transaction with plugins\n */\nexport function wrapTransaction<DB>(\n trx: Transaction<DB>,\n plugins: readonly Plugin[]\n): KyseraTransaction<DB> {\n const interceptors = plugins.filter(p => p.interceptQuery)\n\n if (interceptors.length === 0) {\n /**\n * TYPE ASSERTION #4e: Object.assign for transaction wrapping (no interceptors)\n *\n * Cast: Transaction<DB> & KyseraExecutorMarker<DB> -> KyseraTransaction<DB>\n *\n * Similar to #4a but for Transaction type instead of Kysely type.\n * KyseraTransaction is defined as: type KyseraTransaction<DB> = Transaction<DB> & KyseraExecutorMarker<DB>\n */\n return Object.assign(trx, {\n __kysera: true as const,\n __plugins: plugins,\n __rawDb: trx\n }) as KyseraTransaction<DB>\n }\n\n /**\n * TYPE ASSERTION #5: wrapTransaction cast chain\n *\n * Double cast: Transaction<DB> -> Kysely<DB> -> KyseraExecutor<DB> -> KyseraTransaction<DB>\n *\n * Why needed:\n * - createProxy expects Kysely<DB> and returns KyseraExecutor<DB>\n * - We need to return KyseraTransaction<DB>\n * - TypeScript doesn't recognize that KyseraExecutor wrapping a Transaction is compatible with KyseraTransaction\n *\n * Why safe:\n * - Transaction<DB> extends Kysely<DB> (first cast is upcast)\n * - createProxy preserves all methods (adds marker properties only)\n * - KyseraTransaction<DB> = Transaction<DB> & Marker, KyseraExecutor<DB> = Kysely<DB> & Marker\n * - Since original is Transaction, wrapped result is structurally KyseraTransaction\n * - Verified in integration tests (packages/executor/test/executor.test.ts)\n */\n return createProxy(\n trx as unknown as Kysely<DB>,\n interceptors,\n plugins\n ) as unknown as KyseraTransaction<DB>\n}\n\n/**\n * Apply plugins to a query builder manually\n * Useful for complex queries that bypass normal interception\n */\nexport function applyPlugins<QB>(\n qb: QB,\n plugins: readonly Plugin[],\n context: QueryBuilderContext\n): QB {\n let result = qb\n for (const plugin of plugins) {\n if (plugin.interceptQuery) {\n result = plugin.interceptQuery(result, context)\n }\n }\n return result\n}\n\n/**\n * Get raw Kysely instance from executor, bypassing plugin interceptors.\n * Returns the executor itself if it's not a KyseraExecutor.\n *\n * Useful for plugins that need to:\n * - Perform internal queries without triggering interceptors\n * - Avoid double-filtering (e.g., soft-delete checking its own records)\n * - Access the underlying Kysely instance for advanced operations\n *\n * @param executor - Kysely or KyseraExecutor instance\n * @returns Raw Kysely instance without plugin interception\n *\n * @example\n * ```typescript\n * // Inside a plugin's extendRepository:\n * const rawDb = getRawDb(baseRepo.executor);\n * // This query bypasses all plugin interceptors\n * const result = await rawDb.selectFrom('users').selectAll().execute();\n * ```\n */\nexport function getRawDb<DB>(executor: Kysely<DB>): Kysely<DB> {\n /**\n * TYPE ASSERTION #6: getRawDb executor check\n *\n * Cast: Kysely<DB> -> KyseraExecutor<DB>\n *\n * Why needed:\n * - Need to check if executor has __rawDb property\n * - Plain Kysely<DB> doesn't have __rawDb, only KyseraExecutor<DB> does\n * - TypeScript doesn't allow property access without type assertion\n *\n * Why safe:\n * - Optional chaining (??) handles both cases gracefully:\n * - If KyseraExecutor: __rawDb exists and is returned\n * - If plain Kysely: __rawDb is undefined, executor is returned\n * - No runtime error possible - undefined ?? executor always succeeds\n * - Type guard alternative would be more verbose with same behavior\n */\n const kyseraExecutor = executor as unknown as KyseraExecutor<DB>\n return kyseraExecutor.__rawDb ?? executor\n}\n\n/**\n * Destroy executor and call onDestroy for all plugins\n *\n * @param executor - KyseraExecutor instance to destroy\n *\n * @example\n * ```typescript\n * const executor = await createExecutor(db, [myPlugin]);\n * // ... use executor ...\n * await destroyExecutor(executor); // Calls onDestroy on all plugins\n * await db.destroy(); // Then destroy underlying Kysely instance\n * ```\n */\nexport async function destroyExecutor<DB>(executor: KyseraExecutor<DB>): Promise<void> {\n const plugins = executor.__plugins\n\n // Call onDestroy in reverse order (cleanup in reverse of initialization)\n for (let i = plugins.length - 1; i >= 0; i--) {\n const plugin = plugins[i]\n if (plugin?.onDestroy) {\n await plugin.onDestroy()\n }\n }\n}\n","/**\n * Schema Plugin - Unified schema management for Kysera\n *\n * This plugin provides centralized schema configuration and validation\n * for multi-tenant and modular database architectures.\n *\n * @example\n * // Basic usage with default schema\n * const executor = await createExecutor(db, [\n * schemaPlugin({ defaultSchema: 'auth' })\n * ])\n *\n * @example\n * // Multi-tenant with withSchema()\n * const executor = await createExecutor(db, [\n * schemaPlugin({\n * defaultSchema: 'public',\n * allowedSchemas: ['public', 'tenant_a', 'tenant_b', 'tenant_c']\n * })\n * ])\n * // Use withSchema() to set tenant schema per-request\n * const tenantDb = executor.withSchema(`tenant_${tenantId}`)\n * const users = await tenantDb.selectFrom('users').selectAll().execute()\n */\n\nimport type { Plugin, QueryBuilderContext } from '../types.js'\n\n/**\n * Configuration options for the Schema Plugin\n */\nexport interface SchemaPluginOptions {\n /**\n * Default schema for all queries.\n * Used when no schema is resolved dynamically.\n * @default 'public'\n */\n defaultSchema?: string\n\n /**\n * Dynamic schema resolver function.\n * Called for each query to determine the schema.\n * If returns undefined, defaultSchema is used.\n *\n * The resolver receives `context.schema` which is set when `withSchema()` is called.\n * This allows you to add validation or transformation logic on top of withSchema().\n *\n * @param context - Query builder context with operation, table, schema, and metadata\n * @returns Schema name or undefined to use default\n *\n * @example\n * // Use schema set by withSchema() with fallback\n * resolveSchema: (ctx) => ctx.schema ?? 'public'\n *\n * @example\n * // Schema based on table name (auto-routing)\n * resolveSchema: (ctx) => {\n * if (ctx.table.startsWith('auth_')) return 'auth'\n * if (ctx.table.startsWith('admin_')) return 'admin'\n * return ctx.schema // use withSchema() value or default\n * }\n */\n resolveSchema?: (context: QueryBuilderContext) => string | undefined\n\n /**\n * Async schema validator.\n * Called during plugin initialization to validate the default schema.\n *\n * @param schema - Schema name to validate\n * @returns true if valid, false otherwise\n *\n * @example\n * validateSchema: async (schema) => {\n * return await adapter.schemaExists(db, schema)\n * }\n */\n validateSchema?: (schema: string) => boolean | Promise<boolean>\n\n /**\n * Whitelist of allowed schemas.\n * If set, only these schemas can be used.\n * Queries with other schemas will throw an error.\n *\n * @example\n * allowedSchemas: ['public', 'auth', 'admin']\n */\n allowedSchemas?: string[]\n\n /**\n * Whether to throw an error when schema validation fails.\n * If false, falls back to defaultSchema.\n * @default true\n */\n strictValidation?: boolean\n}\n\n/**\n * Error thrown when schema validation fails\n */\nexport class SchemaValidationError extends Error {\n constructor(\n message: string,\n public readonly schema: string,\n public readonly allowedSchemas?: string[]\n ) {\n super(message)\n this.name = 'SchemaValidationError'\n }\n}\n\n/**\n * Create a Schema Plugin for unified schema management.\n *\n * The plugin provides:\n * - Default schema configuration\n * - Dynamic schema resolution per query\n * - Schema whitelist validation\n * - Schema metadata in QueryBuilderContext\n *\n * @param options - Plugin configuration options\n * @returns Plugin instance\n *\n * @example\n * // Simple default schema\n * const executor = await createExecutor(db, [\n * schemaPlugin({ defaultSchema: 'app' })\n * ])\n *\n * @example\n * // Multi-tenant application with schema validation\n * const executor = await createExecutor(db, [\n * schemaPlugin({\n * defaultSchema: 'public',\n * allowedSchemas: ['public', 'tenant_a', 'tenant_b', 'tenant_c'],\n * strictValidation: true // throws if schema not in whitelist\n * })\n * ])\n *\n * // Per-request: use withSchema() to set tenant context\n * app.use((req, res, next) => {\n * req.db = executor.withSchema(`tenant_${req.tenantId}`)\n * next()\n * })\n */\nexport function schemaPlugin(options: SchemaPluginOptions = {}): Plugin {\n const {\n defaultSchema = 'public',\n resolveSchema,\n validateSchema,\n allowedSchemas,\n strictValidation = true\n } = options\n\n // Pre-compute allowed schemas set for O(1) lookup\n const allowedSet = allowedSchemas ? new Set(allowedSchemas) : null\n\n return {\n name: '@kysera/schema',\n version: '1.0.0',\n priority: 1000, // Run early to set schema context for other plugins\n\n async onInit(_db) {\n // Validate default schema during initialization\n if (validateSchema) {\n const isValid = await validateSchema(defaultSchema)\n if (!isValid) {\n throw new SchemaValidationError(\n `Invalid default schema: ${defaultSchema}`,\n defaultSchema\n )\n }\n }\n\n // Validate allowed schemas if whitelist is provided\n if (allowedSet && !allowedSet.has(defaultSchema)) {\n const allowedList = allowedSchemas ? allowedSchemas.join(', ') : ''\n throw new SchemaValidationError(\n `Default schema \"${defaultSchema}\" is not in allowed list: [${allowedList}]`,\n defaultSchema,\n allowedSchemas\n )\n }\n },\n\n interceptQuery<QB>(qb: QB, context: QueryBuilderContext): QB {\n // Resolve schema for this query\n let schema = resolveSchema?.(context) ?? context.schema ?? defaultSchema\n\n // Validate against whitelist\n if (allowedSet && !allowedSet.has(schema)) {\n if (strictValidation) {\n const allowedList = allowedSchemas ? allowedSchemas.join(', ') : ''\n throw new SchemaValidationError(\n `Schema \"${schema}\" is not in allowed list: [${allowedList}]`,\n schema,\n allowedSchemas\n )\n }\n // Fall back to default if not strict\n schema = defaultSchema\n }\n\n // Store resolved schema in metadata for other plugins\n context.metadata['__resolvedSchema'] = schema\n\n return qb\n }\n }\n}\n\n/**\n * Get the resolved schema from query context metadata.\n * Useful for plugins that need to access the schema set by schemaPlugin.\n *\n * @param context - Query builder context\n * @returns Resolved schema or undefined\n *\n * @example\n * // In another plugin's interceptQuery\n * interceptQuery(qb, context) {\n * const schema = getResolvedSchema(context)\n * if (schema === 'admin') {\n * // Apply admin-specific logic\n * }\n * return qb\n * }\n */\nexport function getResolvedSchema(context: QueryBuilderContext): string | undefined {\n return context.metadata['__resolvedSchema'] as string | undefined\n}\n"]}
{"version":3,"sources":["../src/types.ts","../src/executor.ts","../src/plugins/schema-plugin.ts"],"names":["isRepositoryLike","obj","INTERCEPTED_METHODS","INTERCEPTED_METHODS_SET","METHOD_TO_OPERATION","PluginValidationError","message","type","details","validatePlugins","plugins","names","plugin","dep","conflict","detectCircularDependencies","map","p","visited","stack","inStack","path","frame","start","cycle","depPlugin","resolvePluginOrder","inDegree","dependents","result","available","insertSorted","arr","priority","left","right","mid","midPriority","a","b","pA","pB","current","deps","newDegree","createInterceptedMethod","db","method","interceptors","currentSchema","operation","table","originalMethod","qb","context","error","MARKER_PROPS","MAX_CACHE_SIZE","UNDEFINED_SENTINEL","LRUCache","maxSize","key","value","wrappedValue","firstKey","createProxy","allPlugins","methodCache","interceptedCache","cachedTransactionWrapper","schemaProxyCache","handler","target","prop","receiver","intercepted","schema","cachedSchemaProxy","schemaDb","newProxy","withWrapper","name","fn","wrappedFn","innerDb","withRecursiveWrapper","trx","wrappedTrx","bound","createExecutor","config","enabled","sorted","createExecutorSync","isKyseraExecutor","getPlugins","executor","wrapTransaction","applyPlugins","getRawDb","destroyExecutor","i","SchemaValidationError","allowedSchemas","schemaPlugin","options","defaultSchema","resolveSchema","validateSchema","strictValidation","allowedSet","_db","allowedList","getResolvedSchema"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @kysera/executor - Unified Execution Layer Types\n * @module @kysera/executor\n */\n\nimport type { Kysely, Transaction } from 'kysely'\n\n/**\n * Query builder context passed to plugin interceptors\n */\nexport interface QueryBuilderContext {\n /** Type of operation */\n readonly operation: 'select' | 'insert' | 'update' | 'delete' | 'replace' | 'merge'\n /** Table name */\n readonly table: string\n /**\n * Current schema context (if withSchema was called).\n * undefined means default schema is being used.\n */\n readonly schema?: string\n /** Additional metadata */\n readonly metadata: Record<string, unknown>\n}\n\n/**\n * Plugin interface - unified for both Repository and DAL patterns\n *\n * Plugins can:\n * - Intercept queries before execution (interceptQuery)\n * - Extend repositories with additional methods (extendRepository)\n * - Initialize resources on startup (onInit)\n * - Cleanup resources on shutdown (onDestroy)\n */\nexport interface Plugin {\n /** Unique plugin name */\n readonly name: string\n /** Plugin version */\n readonly version: string\n /** Plugin dependencies (must be loaded first) */\n readonly dependencies?: readonly string[]\n /** Higher priority = runs first (default: 0) */\n readonly priority?: number\n /** Plugins that conflict with this one */\n readonly conflictsWith?: readonly string[]\n\n /**\n * Lifecycle: Called once when plugin is initialized\n * @param db - Kysely database instance (not the executor wrapper)\n */\n onInit?<DB>(db: Kysely<DB>): Promise<void> | void\n\n /**\n * Lifecycle: Called when executor is being destroyed\n * Use for cleanup, closing connections, clearing timers, etc.\n */\n onDestroy?(): Promise<void> | void\n\n /**\n * Query interception: Modify query builder before execution\n * Works in both Repository and DAL patterns via @kysera/executor\n *\n * @typeParam QB - Query builder type (intentionally unconstrained)\n *\n * **Type Safety Note:**\n * QB is intentionally unconstrained (not constrained to any base type) because:\n *\n * 1. **Kysely's query builders lack a shared interface**: SelectQueryBuilder,\n * InsertQueryBuilder, UpdateQueryBuilder, DeleteQueryBuilder, and MergeQueryBuilder\n * don't share a common base interface that includes query modification methods\n * like where(), and(), etc.\n *\n * 2. **Each builder has unique generic parameters**: Even if they implemented\n * Compilable<unknown>, their type parameters differ (DB, TB, O, UT, etc.),\n * making it impossible to express a shared constraint.\n *\n * 3. **Type inference requirement**: The QB type must be preserved exactly as-is\n * through the plugin chain. Any constraint would break this preservation.\n *\n * **Plugin Implementation Pattern:**\n * Plugins must handle type safety internally by:\n * 1. Checking the operation type from context.operation\n * 2. Casting to the appropriate specific builder type\n * 3. Using type assertions (documented in plugin code)\n *\n * @example\n * ```typescript\n * interceptQuery<QB>(qb: QB, context: QueryBuilderContext): QB {\n * if (context.operation === 'select') {\n * // Cast to SelectQueryBuilder for type-safe WHERE clause\n * type GenericSelect = SelectQueryBuilder<Record<string, unknown>, string, Record<string, unknown>>;\n * return (qb as unknown as GenericSelect)\n * .where('deleted_at', 'is', null) as QB;\n * }\n * return qb;\n * }\n * ```\n */\n interceptQuery?<QB>(qb: QB, context: QueryBuilderContext): QB\n\n /**\n * Repository extensions: Add methods to repositories (Repository pattern only)\n */\n extendRepository?<T extends object>(repo: T): T\n}\n\n/**\n * Marker interface for KyseraExecutor\n */\nexport interface KyseraExecutorMarker<DB = unknown> {\n readonly __kysera: true\n readonly __plugins: readonly Plugin[]\n /** Raw Kysely instance bypassing plugin interceptors (for internal plugin use) */\n readonly __rawDb: Kysely<DB>\n /**\n * Current schema context (if withSchema was called).\n * undefined means default schema is being used.\n */\n readonly __schema?: string\n}\n\n/**\n * Plugin-aware Kysely wrapper type\n * Extends Kysely with plugin interception capabilities\n */\nexport type KyseraExecutor<DB> = Kysely<DB> & KyseraExecutorMarker<DB>\n\n/**\n * Plugin-aware Transaction wrapper type\n */\nexport type KyseraTransaction<DB> = Transaction<DB> & KyseraExecutorMarker<DB>\n\n/**\n * Union type for any Kysera executor (database or transaction)\n */\nexport type AnyKyseraExecutor<DB> = KyseraExecutor<DB> | KyseraTransaction<DB>\n\n/**\n * Configuration for executor creation\n */\nexport interface ExecutorConfig {\n /** Enable/disable plugin interception at runtime */\n readonly enabled?: boolean\n}\n\n/**\n * Plugin validation error details\n */\nexport interface PluginValidationDetails {\n readonly pluginName: string\n readonly missingDependency?: string\n readonly conflictingPlugin?: string\n readonly cycle?: readonly string[]\n}\n\n/**\n * Plugin validation error types\n */\nexport type PluginValidationErrorType =\n | 'DUPLICATE_NAME'\n | 'MISSING_DEPENDENCY'\n | 'CONFLICT'\n | 'CIRCULAR_DEPENDENCY'\n | 'INITIALIZATION_FAILED'\n\n/**\n * Base repository interface for plugin extensions.\n * Plugins should use this interface to type-check repository objects.\n *\n * This interface represents the minimum contract that a repository-like object\n * must fulfill to be extended by plugins. It's designed to work with both\n * the @kysera/repository pattern and custom repository implementations.\n *\n * @template DB - The database schema type (defaults to unknown for flexibility)\n *\n * @example\n * ```typescript\n * import type { BaseRepositoryLike } from '@kysera/executor'\n *\n * // In a plugin's extendRepository method:\n * extendRepository<T extends object>(repo: T): T {\n * if (!isRepositoryLike(repo)) {\n * return repo // Not a repository, skip extension\n * }\n *\n * // Now we can safely access repo.tableName and repo.executor\n * const { tableName, executor } = repo\n * // ... extend the repository\n * }\n * ```\n */\nexport interface BaseRepositoryLike<DB = unknown> {\n /** The name of the database table this repository manages */\n readonly tableName: string\n /** The Kysely executor (database or transaction) */\n readonly executor: Kysely<DB>\n /** Find a record by its primary key */\n findById?: (id: unknown) => Promise<unknown>\n /** Find all records in the table */\n findAll?: () => Promise<unknown[]>\n /** Create a new record */\n create?: (data: unknown) => Promise<unknown>\n /** Update an existing record by primary key */\n update?: (id: unknown, data: unknown) => Promise<unknown>\n /** Delete a record by primary key (returns deleted record or boolean) */\n delete?: (id: unknown) => Promise<unknown>\n}\n\n/**\n * Type guard to check if an object is a repository-like object.\n *\n * This function checks for the minimum required properties of a repository:\n * - `tableName`: A string identifying the database table\n * - `executor`: A Kysely instance for database operations\n *\n * @param obj - The object to check\n * @returns True if the object is repository-like, false otherwise\n *\n * @example\n * ```typescript\n * import { isRepositoryLike } from '@kysera/executor'\n *\n * function processRepo(maybeRepo: unknown) {\n * if (isRepositoryLike(maybeRepo)) {\n * console.log(`Repository for table: ${maybeRepo.tableName}`)\n * }\n * }\n * ```\n */\nexport function isRepositoryLike<DB = unknown>(obj: unknown): obj is BaseRepositoryLike<DB> {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'tableName' in obj &&\n 'executor' in obj &&\n typeof (obj as Record<string, unknown>)['tableName'] === 'string'\n )\n}\n","/**\n * @kysera/executor - KyseraExecutor Implementation\n * @module @kysera/executor\n *\n * ## Architecture Notes\n *\n * ### Type System Constraints\n *\n * This implementation uses type assertions due to Kysely's complex type system.\n * All assertions are documented inline and verified safe through runtime behavior.\n *\n * **Type Assertion Categories:**\n *\n * 1. **Plugin interceptQuery** (Line 261)\n * - Issue: QB is constrained to `Compilable<unknown>` but query builders have\n * incompatible method signatures (where, and, etc.)\n * - Safety: Plugin authors must cast based on `context.operation` type\n * - Alternative: None - Kysely lacks a shared interface for query modification\n *\n * 2. **Transaction wrapping** (Lines 326, 332)\n * - Issue: Transaction<DB> extends Kysely<DB> but proxy requires Kysely type\n * - Safety: Structural compatibility verified - Transaction IS-A Kysely\n * - Alternative: None - TypeScript requires explicit cast despite structural typing\n *\n * 3. **Dynamic method access** (Line 245)\n * - Issue: Kysely<DB> lacks index signature for dynamic property access\n * - Safety: Method names validated against INTERCEPTED_METHODS constant\n * - Alternative: None - Cannot use mapped types with runtime method names\n *\n * 4. **Object.assign marker properties** (Lines 394, 427, 473, 488, 527)\n * - Issue: Object.assign returns intersection type (Kysely & Marker)\n * - Safety: Type assertion to union type (KyseraExecutor/KyseraTransaction)\n * - Alternative: Manual object spread (less performant, same type assertion needed)\n *\n * 5. **wrapTransaction cast chain** (Lines 539, 542)\n * - Issue: Transaction -> Kysely -> Proxy -> KyseraTransaction requires casts\n * - Safety: All types structurally compatible; verified in tests\n * - Alternative: None - TypeScript nominal types would solve this\n *\n * 6. **getRawDb executor check** (Line 588)\n * - Issue: Need to check if plain Kysely has __rawDb property\n * - Safety: Optional chaining handles both KyseraExecutor and plain Kysely\n * - Alternative: Type guard (more verbose, same runtime behavior)\n *\n * ### Transaction API Limitation\n *\n * The wrapped transaction only exposes `.execute()` method, not `.setIsolationLevel()`.\n * This is intentional: isolation level should be set before plugin interception.\n *\n * **Rationale:**\n * - Isolation level is a transaction-level concern, not a query-level concern\n * - Setting isolation level after plugin initialization could cause inconsistencies\n * - Keeps the wrapper API simple and focused on query interception\n *\n * **Escape Hatch:**\n * ```typescript\n * executor.__rawDb.transaction().setIsolationLevel('serializable').execute(...)\n * ```\n *\n * This design keeps the plugin system simple while allowing escape hatches.\n */\n\nimport type { Kysely, Transaction } from 'kysely'\nimport type {\n Plugin,\n KyseraExecutor,\n KyseraTransaction,\n QueryBuilderContext,\n ExecutorConfig,\n PluginValidationErrorType,\n PluginValidationDetails\n} from './types.js'\n\n/** Methods that accept table name and should be intercepted */\nexport const INTERCEPTED_METHODS = [\n 'selectFrom',\n 'insertInto',\n 'updateTable',\n 'deleteFrom',\n 'replaceInto', // MySQL REPLACE\n 'mergeInto' // SQL MERGE (Kysely 0.28.x)\n] as const\n\nexport type InterceptedMethod = (typeof INTERCEPTED_METHODS)[number]\n\n/** Pre-computed Set for O(1) lookup instead of Array.includes O(n) */\nconst INTERCEPTED_METHODS_SET = new Set<string>(INTERCEPTED_METHODS)\n\n/** Map method names to operation types */\nconst METHOD_TO_OPERATION: Record<InterceptedMethod, QueryBuilderContext['operation']> = {\n selectFrom: 'select',\n insertInto: 'insert',\n updateTable: 'update',\n deleteFrom: 'delete',\n replaceInto: 'replace',\n mergeInto: 'merge'\n}\n\n/**\n * Plugin validation error\n */\nexport class PluginValidationError extends Error {\n constructor(\n message: string,\n public readonly type: PluginValidationErrorType,\n public readonly details: PluginValidationDetails\n ) {\n super(message)\n this.name = 'PluginValidationError'\n }\n}\n\n/**\n * Validate plugins for conflicts, duplicates, and missing dependencies\n */\nexport function validatePlugins(plugins: readonly Plugin[]): void {\n const names = new Set<string>()\n\n for (const plugin of plugins) {\n if (names.has(plugin.name)) {\n throw new PluginValidationError(`Duplicate plugin: \"${plugin.name}\"`, 'DUPLICATE_NAME', {\n pluginName: plugin.name\n })\n }\n names.add(plugin.name)\n }\n\n for (const plugin of plugins) {\n if (plugin.dependencies) {\n for (const dep of plugin.dependencies) {\n if (!names.has(dep)) {\n throw new PluginValidationError(\n `Plugin \"${plugin.name}\" requires \"${dep}\" which is not registered`,\n 'MISSING_DEPENDENCY',\n { pluginName: plugin.name, missingDependency: dep }\n )\n }\n }\n }\n\n if (plugin.conflictsWith) {\n for (const conflict of plugin.conflictsWith) {\n if (names.has(conflict)) {\n throw new PluginValidationError(\n `Plugin \"${plugin.name}\" conflicts with \"${conflict}\"`,\n 'CONFLICT',\n { pluginName: plugin.name, conflictingPlugin: conflict }\n )\n }\n }\n }\n }\n\n detectCircularDependencies(plugins)\n}\n\n/**\n * Detect circular dependencies using iterative DFS\n * Prevents stack overflow with deep dependency chains\n */\nfunction detectCircularDependencies(plugins: readonly Plugin[]): void {\n const map = new Map(plugins.map(p => [p.name, p]))\n const visited = new Set<string>()\n\n for (const plugin of plugins) {\n if (visited.has(plugin.name)) continue\n\n // Iterative DFS using explicit stack\n const stack: { name: string; deps: readonly string[]; depIndex: number }[] = []\n const inStack = new Set<string>()\n const path: string[] = []\n\n stack.push({ name: plugin.name, deps: plugin.dependencies ?? [], depIndex: 0 })\n inStack.add(plugin.name)\n path.push(plugin.name)\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1]!\n\n if (frame.depIndex >= frame.deps.length) {\n // Done with this node, backtrack\n stack.pop()\n inStack.delete(frame.name)\n path.pop()\n visited.add(frame.name)\n continue\n }\n\n const dep = frame.deps[frame.depIndex]!\n frame.depIndex++\n\n if (inStack.has(dep)) {\n // Cycle detected\n const start = path.indexOf(dep)\n const cycle = [...path.slice(start), dep]\n throw new PluginValidationError(\n `Circular dependency: ${cycle.join(' -> ')}`,\n 'CIRCULAR_DEPENDENCY',\n { pluginName: frame.name, cycle }\n )\n }\n\n if (!visited.has(dep)) {\n const depPlugin = map.get(dep)\n if (depPlugin) {\n stack.push({ name: dep, deps: depPlugin.dependencies ?? [], depIndex: 0 })\n inStack.add(dep)\n path.push(dep)\n }\n }\n }\n }\n}\n\n/**\n * Resolve plugin execution order using topological sort with priority\n */\nexport function resolvePluginOrder(plugins: readonly Plugin[]): Plugin[] {\n if (plugins.length === 0) return []\n\n const map = new Map(plugins.map(p => [p.name, p]))\n const inDegree = new Map<string, number>()\n const dependents = new Map<string, Set<string>>()\n\n for (const plugin of plugins) {\n inDegree.set(plugin.name, 0)\n dependents.set(plugin.name, new Set())\n }\n\n for (const plugin of plugins) {\n if (plugin.dependencies) {\n for (const dep of plugin.dependencies) {\n inDegree.set(plugin.name, (inDegree.get(plugin.name) ?? 0) + 1)\n dependents.get(dep)?.add(plugin.name)\n }\n }\n }\n\n const result: Plugin[] = []\n const available = plugins.filter(p => (inDegree.get(p.name) ?? 0) === 0)\n\n // Helper to maintain sorted order efficiently (descending priority, then alphabetical)\n const insertSorted = (arr: Plugin[], plugin: Plugin): void => {\n const priority = plugin.priority ?? 0\n let left = 0\n let right = arr.length\n\n // Binary search for insertion point (O(log n))\n // We want descending priority (high to low), then alphabetical\n while (left < right) {\n const mid = (left + right) >>> 1\n const midPriority = arr[mid]!.priority ?? 0\n // If mid has higher priority, or same priority but earlier name, insert after mid\n if (midPriority > priority || (midPriority === priority && arr[mid]!.name < plugin.name)) {\n left = mid + 1\n } else {\n right = mid\n }\n }\n arr.splice(left, 0, plugin)\n }\n\n // Initial sort: descending priority (high to low), then alphabetical\n available.sort((a, b) => {\n const pA = a.priority ?? 0\n const pB = b.priority ?? 0\n return pA !== pB ? pB - pA : a.name.localeCompare(b.name)\n })\n\n while (available.length > 0) {\n // Take first element (highest priority): O(1) with shift\n const current = available.shift()\n // Safety: available.length > 0 check ensures current is defined\n if (!current) break\n result.push(current)\n\n const deps = dependents.get(current.name)\n if (deps) {\n for (const dep of deps) {\n const newDegree = (inDegree.get(dep) ?? 0) - 1\n inDegree.set(dep, newDegree)\n if (newDegree === 0) {\n const plugin = map.get(dep)\n // Insert maintaining sorted order: O(log n) search + O(n) splice\n // Overall complexity: O(n log n) instead of O(n²)\n if (plugin) insertSorted(available, plugin)\n }\n }\n }\n }\n\n return result\n}\n\n/**\n * Create intercepted method that applies plugins\n *\n * @param db - Kysely database instance\n * @param method - Method name being intercepted\n * @param interceptors - Plugins with interceptQuery methods\n * @param currentSchema - Optional schema context (from withSchema)\n */\nfunction createInterceptedMethod<DB>(\n db: Kysely<DB>,\n method: InterceptedMethod,\n interceptors: readonly Plugin[],\n currentSchema?: string\n): (table: string) => unknown {\n const operation = METHOD_TO_OPERATION[method]\n\n return (table: string) => {\n /**\n * TYPE ASSERTION #3: Dynamic method access\n *\n * Cast: Kysely<DB> -> Record<string, (t: string) => unknown>\n *\n * Why needed:\n * - Kysely<DB> interface doesn't have an index signature\n * - TypeScript doesn't allow db[method] for dynamic property access\n *\n * Why safe:\n * - Method name validated against INTERCEPTED_METHODS constant\n * - Runtime check throws if method doesn't exist\n * - All intercepted methods have signature: (table: string) => QueryBuilder\n */\n const originalMethod = (db as unknown as Record<string, (t: string) => unknown>)[method]\n if (!originalMethod) {\n throw new Error(`Method ${method} not found on Kysely instance`)\n }\n // Call with correct 'this' context\n let qb = originalMethod.call(db, table)\n\n // Apply interceptors with schema context\n // Use spread to conditionally include schema only when defined\n const context: QueryBuilderContext = currentSchema !== undefined\n ? { operation, table, schema: currentSchema, metadata: {} }\n : { operation, table, metadata: {} }\n\n for (const plugin of interceptors) {\n if (plugin.interceptQuery) {\n try {\n qb = plugin.interceptQuery(qb, context)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(\n `Plugin \"${plugin.name}\" threw during interceptQuery for ${context.operation} on \"${context.table}\": ${message}`\n )\n }\n }\n }\n\n return qb\n }\n}\n\n/** Marker properties Set for fast O(1) lookup */\nconst MARKER_PROPS = new Set<string | symbol>(['__kysera', '__plugins', '__rawDb'])\n\n/** Maximum size for LRU caches to prevent unbounded growth */\nconst MAX_CACHE_SIZE = 100\n\n/**\n * Sentinel value to distinguish \"cached undefined\" from \"not in cache\"\n * @internal\n */\nconst UNDEFINED_SENTINEL = Symbol('UNDEFINED_SENTINEL')\n\n/**\n * Wrapper type for cache values to handle undefined correctly\n * @internal\n */\ntype CacheValue<V> = V | typeof UNDEFINED_SENTINEL\n\n/**\n * Simple LRU cache implementation to prevent unbounded cache growth.\n *\n * Correctly handles undefined values using a sentinel pattern:\n * - get() returns undefined for both \"cached undefined\" and \"not in cache\"\n * - has() returns true only if key is actually in cache (even if value is undefined)\n *\n * @internal\n */\nclass LRUCache<K, V> {\n private cache: Map<K, CacheValue<V>>\n private readonly maxSize: number\n\n constructor(maxSize: number) {\n this.cache = new Map()\n this.maxSize = maxSize\n }\n\n get(key: K): V | undefined {\n const value = this.cache.get(key)\n if (value !== undefined) {\n // Move to end (most recently used)\n this.cache.delete(key)\n this.cache.set(key, value)\n // Unwrap sentinel value\n return value === UNDEFINED_SENTINEL ? undefined : value\n }\n return undefined\n }\n\n set(key: K, value: V): void {\n // Wrap undefined values with sentinel\n const wrappedValue: CacheValue<V> = value === undefined ? UNDEFINED_SENTINEL : value\n\n // Delete if exists to move to end\n if (this.cache.has(key)) {\n this.cache.delete(key)\n }\n this.cache.set(key, wrappedValue)\n\n // Evict oldest (first) entry if size exceeded\n if (this.cache.size > this.maxSize) {\n const firstKey = this.cache.keys().next().value\n if (firstKey !== undefined) {\n this.cache.delete(firstKey)\n }\n }\n }\n\n has(key: K): boolean {\n return this.cache.has(key)\n }\n}\n\n/**\n * Create plugin-aware executor using Proxy\n * Optimized with LRU caching and Set-based lookups\n *\n * @param db - Kysely database instance\n * @param interceptors - Plugins with interceptQuery methods\n * @param allPlugins - All registered plugins\n * @param currentSchema - Optional schema context (from withSchema)\n */\nfunction createProxy<DB>(\n db: Kysely<DB>,\n interceptors: readonly Plugin[],\n allPlugins: readonly Plugin[],\n currentSchema?: string\n): KyseraExecutor<DB> {\n // Cache for bound methods to avoid repeated .bind() allocations\n const methodCache = new Map<string | symbol, unknown>()\n\n // Cache intercepted methods to avoid repeated creation\n const interceptedCache = new Map<string, (table: string) => unknown>()\n\n // Cached transaction wrapper (created once, reused)\n let cachedTransactionWrapper:\n | (() => { execute: <T>(fn: (trx: Transaction<DB>) => Promise<T>) => Promise<T> })\n | null = null\n\n // LRU cache for withSchema to prevent unbounded growth (max 100 schemas)\n const schemaProxyCache = new LRUCache<string, KyseraExecutor<DB>>(MAX_CACHE_SIZE)\n\n const handler: ProxyHandler<Kysely<DB>> = {\n // Handle 'in' operator for type guards\n has(target, prop) {\n if (MARKER_PROPS.has(prop)) return true\n if (prop === '__schema') return true\n return Reflect.has(target, prop)\n },\n\n get(target, prop, receiver) {\n // Fast path: marker properties (O(1) Set lookup)\n if (prop === '__kysera') return true\n if (prop === '__plugins') return allPlugins\n if (prop === '__rawDb') return target\n if (prop === '__schema') return currentSchema\n\n // Fast path: check intercepted methods first (most common hot path)\n if (typeof prop === 'string' && INTERCEPTED_METHODS_SET.has(prop)) {\n let intercepted = interceptedCache.get(prop)\n if (!intercepted) {\n intercepted = createInterceptedMethod(target, prop as InterceptedMethod, interceptors, currentSchema)\n interceptedCache.set(prop, intercepted)\n }\n return intercepted\n }\n\n // Intercept withSchema to maintain plugin proxy and track schema\n if (prop === 'withSchema') {\n return (schema: string) => {\n const cachedSchemaProxy = schemaProxyCache.get(schema)\n if (cachedSchemaProxy) {\n return cachedSchemaProxy\n }\n const schemaDb = target.withSchema(schema)\n // Pass schema to new proxy so it's available in QueryBuilderContext\n const newProxy = createProxy(schemaDb, interceptors, allPlugins, schema)\n schemaProxyCache.set(schema, newProxy)\n return newProxy\n }\n }\n\n // Intercept with() for CTEs - cache the wrapper and also wrap the result\n if (prop === 'with') {\n if (!methodCache.has('with')) {\n const withWrapper = (name: string, fn: (db: Kysely<DB>) => unknown): unknown => {\n const wrappedFn = (innerDb: Kysely<DB>): unknown =>\n fn(createProxy(innerDb, interceptors, allPlugins))\n const originalMethod = Reflect.get(target, 'with') as (\n n: string,\n f: (db: Kysely<DB>) => unknown\n ) => Kysely<DB>\n const result = originalMethod.call(target, name, wrappedFn)\n return createProxy(result, interceptors, allPlugins)\n }\n methodCache.set('with', withWrapper)\n }\n return methodCache.get('with')\n }\n\n // Intercept withRecursive() for recursive CTEs - cache the wrapper and wrap result\n if (prop === 'withRecursive') {\n if (!methodCache.has('withRecursive')) {\n const withRecursiveWrapper = (name: string, fn: (db: Kysely<DB>) => unknown): unknown => {\n const wrappedFn = (innerDb: Kysely<DB>): unknown =>\n fn(createProxy(innerDb, interceptors, allPlugins))\n const originalMethod = Reflect.get(target, 'withRecursive') as (\n n: string,\n f: (db: Kysely<DB>) => unknown\n ) => Kysely<DB>\n const result = originalMethod.call(target, name, wrappedFn)\n return createProxy(result, interceptors, allPlugins)\n }\n methodCache.set('withRecursive', withRecursiveWrapper)\n }\n return methodCache.get('withRecursive')\n }\n\n // Cached transaction wrapper\n // NOTE: Transaction API limitation - only execute() method is wrapped\n // Methods like setIsolationLevel() are not available on the wrapper\n // This is intentional: isolation level should be set before plugin interception\n // For advanced use cases, use: executor.__rawDb.transaction().setIsolationLevel(...).execute(...)\n if (prop === 'transaction') {\n if (!cachedTransactionWrapper) {\n cachedTransactionWrapper = () => ({\n execute: async <T>(fn: (trx: Transaction<DB>) => Promise<T>): Promise<T> => {\n return await target.transaction().execute(async trx => {\n /**\n * TYPE ASSERTION #2a: Transaction to Kysely for proxy creation\n *\n * Cast: Transaction<DB> -> Kysely<DB>\n *\n * Why needed:\n * - createProxy expects Kysely<DB>, not Transaction<DB>\n * - TypeScript doesn't recognize structural compatibility automatically\n *\n * Why safe:\n * - Transaction<DB> extends Kysely<DB> (verified in Kysely types)\n * - All Kysely methods are available on Transaction\n * - createProxy only accesses Kysely methods\n */\n const wrappedTrx = createProxy(\n trx as unknown as Kysely<DB>,\n interceptors,\n allPlugins\n )\n /**\n * TYPE ASSERTION #2b: Wrapped proxy back to Transaction\n *\n * Cast: KyseraExecutor<DB> -> Transaction<DB>\n *\n * Why needed:\n * - User callback expects Transaction<DB>, not KyseraExecutor<DB>\n * - Proxy wraps a Transaction but returns KyseraExecutor type\n *\n * Why safe:\n * - Original trx is Transaction<DB>\n * - Proxy preserves all Transaction methods\n * - Only adds marker properties (__kysera, __plugins, __rawDb)\n */\n return await fn(wrappedTrx as unknown as Transaction<DB>)\n })\n }\n })\n }\n return cachedTransactionWrapper\n }\n\n // Check method cache for bound functions\n if (methodCache.has(prop)) {\n return methodCache.get(prop)\n }\n\n const value = Reflect.get(target, prop, receiver)\n\n // Cache bound methods to avoid repeated .bind() allocations\n if (typeof value === 'function') {\n const bound = value.bind(target)\n methodCache.set(prop, bound)\n return bound\n }\n\n return value\n }\n }\n\n return new Proxy(db, handler) as KyseraExecutor<DB>\n}\n\n/**\n * Create a plugin-aware executor\n *\n * Zero overhead if no plugins have interceptQuery\n *\n * @param db - Kysely database instance\n * @param plugins - Array of plugins to apply\n * @param config - Optional configuration\n * @returns Plugin-aware executor\n *\n * @example\n * ```typescript\n * import { createExecutor } from '@kysera/executor';\n * import { softDeletePlugin } from '@kysera/soft-delete';\n *\n * const executor = await createExecutor(db, [softDeletePlugin()]);\n *\n * // All queries now have soft-delete filter applied\n * const users = await executor.selectFrom('users').selectAll().execute();\n * ```\n */\nexport async function createExecutor<DB>(\n db: Kysely<DB>,\n plugins: readonly Plugin[] = [],\n config: ExecutorConfig = {}\n): Promise<KyseraExecutor<DB>> {\n const { enabled = true } = config\n\n // Fast path: no plugins or disabled\n if (plugins.length === 0 || !enabled) {\n /**\n * TYPE ASSERTION #4a: Object.assign result to KyseraExecutor\n *\n * Cast: Kysely<DB> & KyseraExecutorMarker<DB> -> KyseraExecutor<DB>\n *\n * Why needed:\n * - Object.assign returns intersection type (Kysely & Marker)\n * - KyseraExecutor is defined as: type KyseraExecutor<DB> = Kysely<DB> & KyseraExecutorMarker<DB>\n * - TypeScript treats intersection types different from type aliases\n *\n * Why safe:\n * - We're adding exactly the marker properties defined in KyseraExecutorMarker\n * - Runtime type is identical to KyseraExecutor type definition\n * - No structural difference between intersection and type alias at runtime\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: plugins,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n // Validate and sort plugins\n validatePlugins(plugins)\n const sorted = resolvePluginOrder(plugins)\n\n // Initialize plugins with error handling\n for (const plugin of sorted) {\n try {\n await plugin.onInit?.(db)\n } catch (error) {\n throw new PluginValidationError(\n `Plugin \"${plugin.name}\" failed to initialize: ${error instanceof Error ? error.message : String(error)}`,\n 'INITIALIZATION_FAILED',\n { pluginName: plugin.name }\n )\n }\n }\n\n // Filter plugins with interceptQuery for performance\n const interceptors = sorted.filter(p => p.interceptQuery)\n\n // Fast path: no interceptors\n if (interceptors.length === 0) {\n /**\n * TYPE ASSERTION #4b: Object.assign result to KyseraExecutor (no interceptors)\n *\n * Same as #4a but with sorted plugins instead of input plugins.\n * This path is for plugins that have onInit but no interceptQuery.\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: sorted,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n // Create proxy with interception\n return createProxy(db, interceptors, sorted)\n}\n\n/**\n * Creates executor synchronously WITHOUT calling plugin onInit hooks.\n *\n * @warning This function skips plugin initialization. Use createExecutor()\n * instead unless you are certain plugins don't need async initialization.\n *\n * Use cases where this is safe:\n * - Plugins without onInit hooks\n * - Plugins with synchronous-only initialization\n * - Testing scenarios where initialization is handled separately\n *\n * @param db - Kysely database instance\n * @param plugins - Array of plugins to apply\n * @param config - Optional configuration\n * @returns Plugin-aware executor (without onInit called)\n *\n * @example\n * ```typescript\n * // Use for simple plugins without async init:\n * const executor = createExecutorSync(db, [simplePlugin]);\n *\n * // WARNING: Plugin onInit hooks are NOT called!\n * // If your plugin requires initialization, use createExecutor() instead.\n * ```\n */\nexport function createExecutorSync<DB>(\n db: Kysely<DB>,\n plugins: readonly Plugin[] = [],\n config: ExecutorConfig = {}\n): KyseraExecutor<DB> {\n const { enabled = true } = config\n\n if (plugins.length === 0 || !enabled) {\n /**\n * TYPE ASSERTION #4c: Object.assign in createExecutorSync (no plugins/disabled)\n *\n * Same as #4a - see explanation there.\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: plugins,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n validatePlugins(plugins)\n const sorted = resolvePluginOrder(plugins)\n const interceptors = sorted.filter(p => p.interceptQuery)\n\n if (interceptors.length === 0) {\n /**\n * TYPE ASSERTION #4d: Object.assign in createExecutorSync (no interceptors)\n *\n * Same as #4b - see explanation there.\n */\n return Object.assign(db, {\n __kysera: true as const,\n __plugins: sorted,\n __rawDb: db\n }) as KyseraExecutor<DB>\n }\n\n return createProxy(db, interceptors, sorted)\n}\n\n/**\n * Check if value is a KyseraExecutor\n */\nexport function isKyseraExecutor<DB>(\n value: Kysely<DB> | KyseraExecutor<DB>\n): value is KyseraExecutor<DB> {\n return '__kysera' in value && value.__kysera\n}\n\n/**\n * Get plugins from executor\n */\nexport function getPlugins<DB>(executor: KyseraExecutor<DB>): readonly Plugin[] {\n return executor.__plugins\n}\n\n/**\n * Wrap transaction with plugins\n */\nexport function wrapTransaction<DB>(\n trx: Transaction<DB>,\n plugins: readonly Plugin[]\n): KyseraTransaction<DB> {\n const interceptors = plugins.filter(p => p.interceptQuery)\n\n if (interceptors.length === 0) {\n /**\n * TYPE ASSERTION #4e: Object.assign for transaction wrapping (no interceptors)\n *\n * Cast: Transaction<DB> & KyseraExecutorMarker<DB> -> KyseraTransaction<DB>\n *\n * Similar to #4a but for Transaction type instead of Kysely type.\n * KyseraTransaction is defined as: type KyseraTransaction<DB> = Transaction<DB> & KyseraExecutorMarker<DB>\n */\n return Object.assign(trx, {\n __kysera: true as const,\n __plugins: plugins,\n __rawDb: trx\n }) as KyseraTransaction<DB>\n }\n\n /**\n * TYPE ASSERTION #5: wrapTransaction cast chain\n *\n * Double cast: Transaction<DB> -> Kysely<DB> -> KyseraExecutor<DB> -> KyseraTransaction<DB>\n *\n * Why needed:\n * - createProxy expects Kysely<DB> and returns KyseraExecutor<DB>\n * - We need to return KyseraTransaction<DB>\n * - TypeScript doesn't recognize that KyseraExecutor wrapping a Transaction is compatible with KyseraTransaction\n *\n * Why safe:\n * - Transaction<DB> extends Kysely<DB> (first cast is upcast)\n * - createProxy preserves all methods (adds marker properties only)\n * - KyseraTransaction<DB> = Transaction<DB> & Marker, KyseraExecutor<DB> = Kysely<DB> & Marker\n * - Since original is Transaction, wrapped result is structurally KyseraTransaction\n * - Verified in integration tests (packages/executor/test/executor.test.ts)\n */\n return createProxy(\n trx as unknown as Kysely<DB>,\n interceptors,\n plugins\n ) as unknown as KyseraTransaction<DB>\n}\n\n/**\n * Apply plugins to a query builder manually\n * Useful for complex queries that bypass normal interception\n */\nexport function applyPlugins<QB>(\n qb: QB,\n plugins: readonly Plugin[],\n context: QueryBuilderContext\n): QB {\n let result = qb\n for (const plugin of plugins) {\n if (plugin.interceptQuery) {\n try {\n result = plugin.interceptQuery(result, context)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(\n `Plugin \"${plugin.name}\" threw during interceptQuery for ${context.operation} on \"${context.table}\": ${message}`\n )\n }\n }\n }\n return result\n}\n\n/**\n * Get raw Kysely instance from executor, bypassing plugin interceptors.\n * Returns the executor itself if it's not a KyseraExecutor.\n *\n * Useful for plugins that need to:\n * - Perform internal queries without triggering interceptors\n * - Avoid double-filtering (e.g., soft-delete checking its own records)\n * - Access the underlying Kysely instance for advanced operations\n *\n * @param executor - Kysely or KyseraExecutor instance\n * @returns Raw Kysely instance without plugin interception\n *\n * @example\n * ```typescript\n * // Inside a plugin's extendRepository:\n * const rawDb = getRawDb(baseRepo.executor);\n * // This query bypasses all plugin interceptors\n * const result = await rawDb.selectFrom('users').selectAll().execute();\n * ```\n */\nexport function getRawDb<DB>(executor: Kysely<DB>): Kysely<DB> {\n /**\n * TYPE ASSERTION #6: getRawDb executor check\n *\n * Cast: Kysely<DB> -> KyseraExecutor<DB>\n *\n * Why needed:\n * - Need to check if executor has __rawDb property\n * - Plain Kysely<DB> doesn't have __rawDb, only KyseraExecutor<DB> does\n * - TypeScript doesn't allow property access without type assertion\n *\n * Why safe:\n * - Optional chaining (??) handles both cases gracefully:\n * - If KyseraExecutor: __rawDb exists and is returned\n * - If plain Kysely: __rawDb is undefined, executor is returned\n * - No runtime error possible - undefined ?? executor always succeeds\n * - Type guard alternative would be more verbose with same behavior\n */\n const kyseraExecutor = executor as unknown as KyseraExecutor<DB>\n return kyseraExecutor.__rawDb ?? executor\n}\n\n/**\n * Destroy executor and call onDestroy for all plugins\n *\n * @param executor - KyseraExecutor instance to destroy\n *\n * @example\n * ```typescript\n * const executor = await createExecutor(db, [myPlugin]);\n * // ... use executor ...\n * await destroyExecutor(executor); // Calls onDestroy on all plugins\n * await db.destroy(); // Then destroy underlying Kysely instance\n * ```\n */\nexport async function destroyExecutor<DB>(executor: KyseraExecutor<DB>): Promise<void> {\n const plugins = executor.__plugins\n\n // Call onDestroy in reverse order (cleanup in reverse of initialization)\n for (let i = plugins.length - 1; i >= 0; i--) {\n const plugin = plugins[i]\n if (plugin?.onDestroy) {\n await plugin.onDestroy()\n }\n }\n}\n","/**\n * Schema Plugin - Unified schema management for Kysera\n *\n * This plugin provides centralized schema configuration and validation\n * for multi-tenant and modular database architectures.\n *\n * @example\n * // Basic usage with default schema\n * const executor = await createExecutor(db, [\n * schemaPlugin({ defaultSchema: 'auth' })\n * ])\n *\n * @example\n * // Multi-tenant with withSchema()\n * const executor = await createExecutor(db, [\n * schemaPlugin({\n * defaultSchema: 'public',\n * allowedSchemas: ['public', 'tenant_a', 'tenant_b', 'tenant_c']\n * })\n * ])\n * // Use withSchema() to set tenant schema per-request\n * const tenantDb = executor.withSchema(`tenant_${tenantId}`)\n * const users = await tenantDb.selectFrom('users').selectAll().execute()\n */\n\nimport type { Plugin, QueryBuilderContext } from '../types.js'\n\n/**\n * Configuration options for the Schema Plugin\n */\nexport interface SchemaPluginOptions {\n /**\n * Default schema for all queries.\n * Used when no schema is resolved dynamically.\n * @default 'public'\n */\n defaultSchema?: string\n\n /**\n * Dynamic schema resolver function.\n * Called for each query to determine the schema.\n * If returns undefined, defaultSchema is used.\n *\n * The resolver receives `context.schema` which is set when `withSchema()` is called.\n * This allows you to add validation or transformation logic on top of withSchema().\n *\n * @param context - Query builder context with operation, table, schema, and metadata\n * @returns Schema name or undefined to use default\n *\n * @example\n * // Use schema set by withSchema() with fallback\n * resolveSchema: (ctx) => ctx.schema ?? 'public'\n *\n * @example\n * // Schema based on table name (auto-routing)\n * resolveSchema: (ctx) => {\n * if (ctx.table.startsWith('auth_')) return 'auth'\n * if (ctx.table.startsWith('admin_')) return 'admin'\n * return ctx.schema // use withSchema() value or default\n * }\n */\n resolveSchema?: (context: QueryBuilderContext) => string | undefined\n\n /**\n * Async schema validator.\n * Called during plugin initialization to validate the default schema.\n *\n * @param schema - Schema name to validate\n * @returns true if valid, false otherwise\n *\n * @example\n * validateSchema: async (schema) => {\n * return await adapter.schemaExists(db, schema)\n * }\n */\n validateSchema?: (schema: string) => boolean | Promise<boolean>\n\n /**\n * Whitelist of allowed schemas.\n * If set, only these schemas can be used.\n * Queries with other schemas will throw an error.\n *\n * @example\n * allowedSchemas: ['public', 'auth', 'admin']\n */\n allowedSchemas?: string[]\n\n /**\n * Whether to throw an error when schema validation fails.\n * If false, falls back to defaultSchema.\n * @default true\n */\n strictValidation?: boolean\n}\n\n/**\n * Error thrown when schema validation fails\n */\nexport class SchemaValidationError extends Error {\n constructor(\n message: string,\n public readonly schema: string,\n public readonly allowedSchemas?: string[]\n ) {\n super(message)\n this.name = 'SchemaValidationError'\n }\n}\n\n/**\n * Create a Schema Plugin for unified schema management.\n *\n * The plugin provides:\n * - Default schema configuration\n * - Dynamic schema resolution per query\n * - Schema whitelist validation\n * - Schema metadata in QueryBuilderContext\n *\n * @param options - Plugin configuration options\n * @returns Plugin instance\n *\n * @example\n * // Simple default schema\n * const executor = await createExecutor(db, [\n * schemaPlugin({ defaultSchema: 'app' })\n * ])\n *\n * @example\n * // Multi-tenant application with schema validation\n * const executor = await createExecutor(db, [\n * schemaPlugin({\n * defaultSchema: 'public',\n * allowedSchemas: ['public', 'tenant_a', 'tenant_b', 'tenant_c'],\n * strictValidation: true // throws if schema not in whitelist\n * })\n * ])\n *\n * // Per-request: use withSchema() to set tenant context\n * app.use((req, res, next) => {\n * req.db = executor.withSchema(`tenant_${req.tenantId}`)\n * next()\n * })\n */\nexport function schemaPlugin(options: SchemaPluginOptions = {}): Plugin {\n const {\n defaultSchema = 'public',\n resolveSchema,\n validateSchema,\n allowedSchemas,\n strictValidation = true\n } = options\n\n // Pre-compute allowed schemas set for O(1) lookup\n const allowedSet = allowedSchemas ? new Set(allowedSchemas) : null\n\n return {\n name: '@kysera/schema',\n version: '1.0.0',\n priority: 1000, // Run early to set schema context for other plugins\n\n async onInit(_db) {\n // Validate default schema during initialization\n if (validateSchema) {\n const isValid = await validateSchema(defaultSchema)\n if (!isValid) {\n throw new SchemaValidationError(\n `Invalid default schema: ${defaultSchema}`,\n defaultSchema\n )\n }\n }\n\n // Validate allowed schemas if whitelist is provided\n if (allowedSet && !allowedSet.has(defaultSchema)) {\n const allowedList = allowedSchemas!.join(', ')\n throw new SchemaValidationError(\n `Default schema \"${defaultSchema}\" is not in allowed list: [${allowedList}]`,\n defaultSchema,\n allowedSchemas\n )\n }\n },\n\n interceptQuery<QB>(qb: QB, context: QueryBuilderContext): QB {\n // Resolve schema for this query\n let schema = resolveSchema?.(context) ?? context.schema ?? defaultSchema\n\n // Validate against whitelist\n if (allowedSet && !allowedSet.has(schema)) {\n if (strictValidation) {\n const allowedList = allowedSchemas!.join(', ')\n throw new SchemaValidationError(\n `Schema \"${schema}\" is not in allowed list: [${allowedList}]`,\n schema,\n allowedSchemas\n )\n }\n // Fall back to default if not strict\n schema = defaultSchema\n }\n\n // Store resolved schema in metadata for other plugins\n context.metadata['__resolvedSchema'] = schema\n\n return qb\n }\n }\n}\n\n/**\n * Get the resolved schema from query context metadata.\n * Useful for plugins that need to access the schema set by schemaPlugin.\n *\n * @param context - Query builder context\n * @returns Resolved schema or undefined\n *\n * @example\n * // In another plugin's interceptQuery\n * interceptQuery(qb, context) {\n * const schema = getResolvedSchema(context)\n * if (schema === 'admin') {\n * // Apply admin-specific logic\n * }\n * return qb\n * }\n */\nexport function getResolvedSchema(context: QueryBuilderContext): string | undefined {\n return context.metadata['__resolvedSchema'] as string | undefined\n}\n"]}
{
"name": "@kysera/executor",
"version": "0.8.5",
"version": "0.8.6",
"description": "Unified Execution Layer for Kysera - Plugin-aware Kysely wrapper",

@@ -5,0 +5,0 @@ "type": "module",

@@ -341,3 +341,10 @@ /**

if (plugin.interceptQuery) {
qb = plugin.interceptQuery(qb, context)
try {
qb = plugin.interceptQuery(qb, context)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
throw new Error(
`Plugin "${plugin.name}" threw during interceptQuery for ${context.operation} on "${context.table}": ${message}`
)
}
}

@@ -832,3 +839,10 @@ }

if (plugin.interceptQuery) {
result = plugin.interceptQuery(result, context)
try {
result = plugin.interceptQuery(result, context)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
throw new Error(
`Plugin "${plugin.name}" threw during interceptQuery for ${context.operation} on "${context.table}": ${message}`
)
}
}

@@ -835,0 +849,0 @@ }

@@ -175,3 +175,3 @@ /**

if (allowedSet && !allowedSet.has(defaultSchema)) {
const allowedList = allowedSchemas ? allowedSchemas.join(', ') : ''
const allowedList = allowedSchemas!.join(', ')
throw new SchemaValidationError(

@@ -192,3 +192,3 @@ `Default schema "${defaultSchema}" is not in allowed list: [${allowedList}]`,

if (strictValidation) {
const allowedList = allowedSchemas ? allowedSchemas.join(', ') : ''
const allowedList = allowedSchemas!.join(', ')
throw new SchemaValidationError(

@@ -195,0 +195,0 @@ `Schema "${schema}" is not in allowed list: [${allowedList}]`,