@forinda/kickjs-drizzle 0.5.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppAdapter, Container } from '@forinda/kickjs-core';
|
|
1
|
+
import { MaybePromise, AppAdapter, Container } from '@forinda/kickjs-core';
|
|
2
2
|
import { QueryBuilderAdapter, ParsedQuery } from '@forinda/kickjs-http';
|
|
3
3
|
|
|
4
4
|
/** DI token for resolving the Drizzle database instance from the container */
|
|
@@ -39,7 +39,7 @@ interface DrizzleAdapterOptions<TDb = unknown> {
|
|
|
39
39
|
* })
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
onShutdown?: () =>
|
|
42
|
+
onShutdown?: () => MaybePromise<any>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/drizzle.adapter.ts","../src/types.ts","../src/query-adapter.ts"],"sourcesContent":["import { Logger, type AppAdapter, type Container, Scope } from '@forinda/kickjs-core'\nimport { DRIZZLE_DB, type DrizzleAdapterOptions } from './types'\n\nconst log = Logger.for('DrizzleAdapter')\n\n/**\n * Drizzle ORM adapter — registers a Drizzle database instance in the DI\n * container and manages its lifecycle.\n *\n * Works with any Drizzle driver: `drizzle-orm/postgres-js`, `drizzle-orm/node-postgres`,\n * `drizzle-orm/mysql2`, `drizzle-orm/better-sqlite3`, `drizzle-orm/libsql`, etc.\n *\n * The adapter is generic — the db type is inferred from what you pass in,\n * so services can inject the fully-typed database instance.\n *\n * @example\n * ```ts\n * import { drizzle } from 'drizzle-orm/better-sqlite3'\n * import * as schema from './schema'\n * import { DrizzleAdapter } from '@forinda/kickjs-drizzle'\n *\n * const db = drizzle({ client: sqlite, schema })\n *\n * bootstrap({\n * modules,\n * adapters: [\n * new DrizzleAdapter({ db, onShutdown: () => sqlite.close() }),\n * ],\n * })\n * ```\n *\n * Inject the typed db instance in services:\n * ```ts\n * import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\n * import * as schema from './schema'\n *\n * @Service()\n * class UserService {\n * constructor(@Inject(DRIZZLE_DB) private db: BetterSQLite3Database<typeof schema>) {}\n * }\n * ```\n */\nexport class DrizzleAdapter<TDb = unknown> implements AppAdapter {\n name = 'DrizzleAdapter'\n private db: TDb\n private onShutdown?: () => void | Promise<void>\n\n constructor(private options: DrizzleAdapterOptions<TDb>) {\n this.db = options.db\n this.onShutdown = options.onShutdown\n }\n\n /** Register the Drizzle db instance in the DI container */\n beforeStart(_app: unknown, container: Container): void {\n if (this.options.logging) {\n log.info('Query logging enabled')\n }\n\n container.registerFactory(DRIZZLE_DB, () => this.db, Scope.SINGLETON)\n\n log.info('Drizzle database registered in DI container')\n }\n\n /** Close the underlying connection on shutdown */\n async shutdown(): Promise<void> {\n if (this.onShutdown) {\n await this.onShutdown()\n log.info('Drizzle connection closed')\n }\n }\n}\n","/** DI token for resolving the Drizzle database instance from the container */\nexport const DRIZZLE_DB = Symbol('DrizzleDB')\n\nexport interface DrizzleAdapterOptions<TDb = unknown> {\n /**\n * Drizzle database instance — the return value of `drizzle()`.\n * Preserves the full type so services can inject it type-safely.\n *\n * @example\n * ```ts\n * import { drizzle } from 'drizzle-orm/better-sqlite3'\n * import * as schema from './schema'\n *\n * const db = drizzle({ client: sqlite, schema })\n * // db is BetterSQLite3Database<typeof schema>\n *\n * new DrizzleAdapter({ db })\n * // TDb is inferred as BetterSQLite3Database<typeof schema>\n * ```\n */\n db: TDb\n\n /** Enable query logging (default: false) */\n logging?: boolean\n\n /**\n * Optional shutdown function to close the underlying connection pool.\n * Drizzle doesn't expose a universal disconnect — this lets you pass your\n * driver's cleanup (e.g., `pool.end()` for postgres, `client.close()` for libsql).\n *\n * @example\n * ```ts\n * const pool = new Pool({ connectionString: '...' })\n * const db = drizzle(pool)\n *\n * new DrizzleAdapter({\n * db,\n * onShutdown: () => pool.end(),\n * })\n * ```\n */\n onShutdown?: () => void | Promise<void>\n}\n","import type { QueryBuilderAdapter, ParsedQuery, FilterItem, SortItem } from '@forinda/kickjs-http'\n\n/**\n * Configuration for the Drizzle query builder adapter.\n *\n * Unlike Prisma which uses its own query builder API, Drizzle uses SQL-like\n * operators (`eq`, `gt`, `like`, etc.) from `drizzle-orm`. This adapter\n * produces a config object that can be spread into Drizzle's `select().from().where()`.\n */\nexport interface DrizzleQueryConfig {\n /** The Drizzle table schema object (e.g., `users` from your schema) */\n table: Record<string, any>\n /** Columns to search across when a search string is provided */\n searchColumns?: string[]\n}\n\n/**\n * Result shape compatible with Drizzle's query builder.\n * Use with `db.select().from(table).where(result.where).orderBy(...result.orderBy).limit(result.limit).offset(result.offset)`\n */\nexport interface DrizzleQueryResult {\n /** SQL condition — pass to `.where()` */\n where?: any\n /** Array of order expressions — spread into `.orderBy()` */\n orderBy: any[]\n /** Row limit — pass to `.limit()` */\n limit: number\n /** Row offset — pass to `.offset()` */\n offset: number\n}\n\n/**\n * Drizzle operator functions required by the query adapter.\n * Pass these from your `drizzle-orm` import to avoid version coupling.\n *\n * @example\n * ```ts\n * import { eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc } from 'drizzle-orm'\n *\n * const adapter = new DrizzleQueryAdapter({\n * eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc,\n * })\n * ```\n */\nexport interface DrizzleOps {\n eq: (column: any, value: any) => any\n ne: (column: any, value: any) => any\n gt: (column: any, value: any) => any\n gte: (column: any, value: any) => any\n lt: (column: any, value: any) => any\n lte: (column: any, value: any) => any\n ilike: (column: any, value: string) => any\n inArray: (column: any, values: any[]) => any\n and: (...conditions: any[]) => any\n or: (...conditions: any[]) => any\n asc: (column: any) => any\n desc: (column: any) => any\n}\n\n/**\n * Translates a ParsedQuery into Drizzle-compatible query parts.\n *\n * @example\n * ```ts\n * import { eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc } from 'drizzle-orm'\n * import { users } from './schema'\n *\n * const adapter = new DrizzleQueryAdapter({\n * eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc,\n * })\n * const parsed = ctx.qs({ filters: ['name', 'email'], sort: ['name'] })\n * const query = adapter.build(parsed, { table: users, searchColumns: ['name', 'email'] })\n *\n * const results = await db\n * .select()\n * .from(users)\n * .where(query.where)\n * .orderBy(...query.orderBy)\n * .limit(query.limit)\n * .offset(query.offset)\n * ```\n */\nexport class DrizzleQueryAdapter implements QueryBuilderAdapter<\n DrizzleQueryResult,\n DrizzleQueryConfig\n> {\n readonly name = 'DrizzleQueryAdapter'\n\n constructor(private ops: DrizzleOps) {}\n\n build(\n parsed: ParsedQuery,\n config: DrizzleQueryConfig = {} as DrizzleQueryConfig,\n ): DrizzleQueryResult {\n const result: DrizzleQueryResult = {\n orderBy: [],\n limit: parsed.pagination.limit,\n offset: parsed.pagination.offset,\n }\n\n // Build where conditions\n const conditions: any[] = []\n\n // Filters\n for (const filter of parsed.filters) {\n const condition = this.buildFilter(config.table, filter)\n if (condition) conditions.push(condition)\n }\n\n // Search\n if (parsed.search && config.searchColumns && config.searchColumns.length > 0) {\n const searchConditions = config.searchColumns\n .filter((col) => config.table[col])\n .map((col) => this.ops.ilike(config.table[col], `%${parsed.search}%`))\n\n if (searchConditions.length > 0) {\n conditions.push(this.ops.or(...searchConditions))\n }\n }\n\n // Combine conditions\n if (conditions.length === 1) {\n result.where = conditions[0]\n } else if (conditions.length > 1) {\n result.where = this.ops.and(...conditions)\n }\n\n // Sort\n result.orderBy = this.buildSort(config.table, parsed.sort)\n\n return result\n }\n\n /** Map a single FilterItem to a Drizzle condition */\n private buildFilter(table: Record<string, any>, filter: FilterItem): any {\n const column = table[filter.field]\n if (!column) return null\n\n const value = this.coerce(filter.value)\n\n switch (filter.operator) {\n case 'eq':\n return this.ops.eq(column, value)\n case 'neq':\n return this.ops.ne(column, value)\n case 'gt':\n return this.ops.gt(column, value)\n case 'gte':\n return this.ops.gte(column, value)\n case 'lt':\n return this.ops.lt(column, value)\n case 'lte':\n return this.ops.lte(column, value)\n case 'contains':\n return this.ops.ilike(column, `%${filter.value}%`)\n case 'starts':\n return this.ops.ilike(column, `${filter.value}%`)\n case 'ends':\n return this.ops.ilike(column, `%${filter.value}`)\n case 'in': {\n const values = filter.value.split(',').map((v) => this.coerce(v.trim()))\n return this.ops.inArray(column, values)\n }\n case 'between': {\n const [min, max] = filter.value.split(',').map((v) => this.coerce(v.trim()))\n return this.ops.and(this.ops.gte(column, min), this.ops.lte(column, max))\n }\n default:\n return this.ops.eq(column, value)\n }\n }\n\n /** Build Drizzle orderBy from SortItem[] */\n private buildSort(table: Record<string, any>, sort: SortItem[]): any[] {\n return sort\n .filter((item) => table[item.field])\n .map((item) =>\n item.direction === 'desc'\n ? this.ops.desc(table[item.field])\n : this.ops.asc(table[item.field]),\n )\n }\n\n /** Attempt to coerce a string value to a number or boolean if appropriate */\n private coerce(value: string): string | number | boolean {\n if (value === 'true') return true\n if (value === 'false') return false\n const num = Number(value)\n if (!Number.isNaN(num) && value.trim() !== '') return num\n return value\n }\n}\n"],"mappings":"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","names":["Logger","Scope","DRIZZLE_DB","Symbol","log","Logger","for","DrizzleAdapter","name","db","onShutdown","options","beforeStart","_app","container","logging","info","registerFactory","DRIZZLE_DB","Scope","SINGLETON","shutdown","DrizzleQueryAdapter","name","ops","build","parsed","config","result","orderBy","limit","pagination","offset","conditions","filter","filters","condition","buildFilter","table","push","search","searchColumns","length","searchConditions","col","map","ilike","or","where","and","buildSort","sort","column","field","value","coerce","operator","eq","ne","gt","gte","lt","lte","values","split","v","trim","inArray","min","max","item","direction","desc","asc","num","Number","isNaN"]}
|
|
1
|
+
{"version":3,"sources":["../src/drizzle.adapter.ts","../src/types.ts","../src/query-adapter.ts"],"sourcesContent":["import { Logger, type AppAdapter, type Container, Scope } from '@forinda/kickjs-core'\nimport { DRIZZLE_DB, type DrizzleAdapterOptions } from './types'\n\nconst log = Logger.for('DrizzleAdapter')\n\n/**\n * Drizzle ORM adapter — registers a Drizzle database instance in the DI\n * container and manages its lifecycle.\n *\n * Works with any Drizzle driver: `drizzle-orm/postgres-js`, `drizzle-orm/node-postgres`,\n * `drizzle-orm/mysql2`, `drizzle-orm/better-sqlite3`, `drizzle-orm/libsql`, etc.\n *\n * The adapter is generic — the db type is inferred from what you pass in,\n * so services can inject the fully-typed database instance.\n *\n * @example\n * ```ts\n * import { drizzle } from 'drizzle-orm/better-sqlite3'\n * import * as schema from './schema'\n * import { DrizzleAdapter } from '@forinda/kickjs-drizzle'\n *\n * const db = drizzle({ client: sqlite, schema })\n *\n * bootstrap({\n * modules,\n * adapters: [\n * new DrizzleAdapter({ db, onShutdown: () => sqlite.close() }),\n * ],\n * })\n * ```\n *\n * Inject the typed db instance in services:\n * ```ts\n * import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'\n * import * as schema from './schema'\n *\n * @Service()\n * class UserService {\n * constructor(@Inject(DRIZZLE_DB) private db: BetterSQLite3Database<typeof schema>) {}\n * }\n * ```\n */\nexport class DrizzleAdapter<TDb = unknown> implements AppAdapter {\n name = 'DrizzleAdapter'\n private db: TDb\n private onShutdown?: () => void | Promise<void>\n\n constructor(private options: DrizzleAdapterOptions<TDb>) {\n this.db = options.db\n this.onShutdown = options.onShutdown\n }\n\n /** Register the Drizzle db instance in the DI container */\n beforeStart(_app: unknown, container: Container): void {\n if (this.options.logging) {\n log.info('Query logging enabled')\n }\n\n container.registerFactory(DRIZZLE_DB, () => this.db, Scope.SINGLETON)\n\n log.info('Drizzle database registered in DI container')\n }\n\n /** Close the underlying connection on shutdown */\n async shutdown(): Promise<void> {\n if (this.onShutdown) {\n await this.onShutdown()\n log.info('Drizzle connection closed')\n }\n }\n}\n","import type { MaybePromise } from '@forinda/kickjs-core'\n\n/** DI token for resolving the Drizzle database instance from the container */\nexport const DRIZZLE_DB = Symbol('DrizzleDB')\n\nexport interface DrizzleAdapterOptions<TDb = unknown> {\n /**\n * Drizzle database instance — the return value of `drizzle()`.\n * Preserves the full type so services can inject it type-safely.\n *\n * @example\n * ```ts\n * import { drizzle } from 'drizzle-orm/better-sqlite3'\n * import * as schema from './schema'\n *\n * const db = drizzle({ client: sqlite, schema })\n * // db is BetterSQLite3Database<typeof schema>\n *\n * new DrizzleAdapter({ db })\n * // TDb is inferred as BetterSQLite3Database<typeof schema>\n * ```\n */\n db: TDb\n\n /** Enable query logging (default: false) */\n logging?: boolean\n\n /**\n * Optional shutdown function to close the underlying connection pool.\n * Drizzle doesn't expose a universal disconnect — this lets you pass your\n * driver's cleanup (e.g., `pool.end()` for postgres, `client.close()` for libsql).\n *\n * @example\n * ```ts\n * const pool = new Pool({ connectionString: '...' })\n * const db = drizzle(pool)\n *\n * new DrizzleAdapter({\n * db,\n * onShutdown: () => pool.end(),\n * })\n * ```\n */\n onShutdown?: () => MaybePromise<any>\n}\n","import type { QueryBuilderAdapter, ParsedQuery, FilterItem, SortItem } from '@forinda/kickjs-http'\n\n/**\n * Configuration for the Drizzle query builder adapter.\n *\n * Unlike Prisma which uses its own query builder API, Drizzle uses SQL-like\n * operators (`eq`, `gt`, `like`, etc.) from `drizzle-orm`. This adapter\n * produces a config object that can be spread into Drizzle's `select().from().where()`.\n */\nexport interface DrizzleQueryConfig {\n /** The Drizzle table schema object (e.g., `users` from your schema) */\n table: Record<string, any>\n /** Columns to search across when a search string is provided */\n searchColumns?: string[]\n}\n\n/**\n * Result shape compatible with Drizzle's query builder.\n * Use with `db.select().from(table).where(result.where).orderBy(...result.orderBy).limit(result.limit).offset(result.offset)`\n */\nexport interface DrizzleQueryResult {\n /** SQL condition — pass to `.where()` */\n where?: any\n /** Array of order expressions — spread into `.orderBy()` */\n orderBy: any[]\n /** Row limit — pass to `.limit()` */\n limit: number\n /** Row offset — pass to `.offset()` */\n offset: number\n}\n\n/**\n * Drizzle operator functions required by the query adapter.\n * Pass these from your `drizzle-orm` import to avoid version coupling.\n *\n * @example\n * ```ts\n * import { eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc } from 'drizzle-orm'\n *\n * const adapter = new DrizzleQueryAdapter({\n * eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc,\n * })\n * ```\n */\nexport interface DrizzleOps {\n eq: (column: any, value: any) => any\n ne: (column: any, value: any) => any\n gt: (column: any, value: any) => any\n gte: (column: any, value: any) => any\n lt: (column: any, value: any) => any\n lte: (column: any, value: any) => any\n ilike: (column: any, value: string) => any\n inArray: (column: any, values: any[]) => any\n and: (...conditions: any[]) => any\n or: (...conditions: any[]) => any\n asc: (column: any) => any\n desc: (column: any) => any\n}\n\n/**\n * Translates a ParsedQuery into Drizzle-compatible query parts.\n *\n * @example\n * ```ts\n * import { eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc } from 'drizzle-orm'\n * import { users } from './schema'\n *\n * const adapter = new DrizzleQueryAdapter({\n * eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc,\n * })\n * const parsed = ctx.qs({ filters: ['name', 'email'], sort: ['name'] })\n * const query = adapter.build(parsed, { table: users, searchColumns: ['name', 'email'] })\n *\n * const results = await db\n * .select()\n * .from(users)\n * .where(query.where)\n * .orderBy(...query.orderBy)\n * .limit(query.limit)\n * .offset(query.offset)\n * ```\n */\nexport class DrizzleQueryAdapter implements QueryBuilderAdapter<\n DrizzleQueryResult,\n DrizzleQueryConfig\n> {\n readonly name = 'DrizzleQueryAdapter'\n\n constructor(private ops: DrizzleOps) {}\n\n build(\n parsed: ParsedQuery,\n config: DrizzleQueryConfig = {} as DrizzleQueryConfig,\n ): DrizzleQueryResult {\n const result: DrizzleQueryResult = {\n orderBy: [],\n limit: parsed.pagination.limit,\n offset: parsed.pagination.offset,\n }\n\n // Build where conditions\n const conditions: any[] = []\n\n // Filters\n for (const filter of parsed.filters) {\n const condition = this.buildFilter(config.table, filter)\n if (condition) conditions.push(condition)\n }\n\n // Search\n if (parsed.search && config.searchColumns && config.searchColumns.length > 0) {\n const searchConditions = config.searchColumns\n .filter((col) => config.table[col])\n .map((col) => this.ops.ilike(config.table[col], `%${parsed.search}%`))\n\n if (searchConditions.length > 0) {\n conditions.push(this.ops.or(...searchConditions))\n }\n }\n\n // Combine conditions\n if (conditions.length === 1) {\n result.where = conditions[0]\n } else if (conditions.length > 1) {\n result.where = this.ops.and(...conditions)\n }\n\n // Sort\n result.orderBy = this.buildSort(config.table, parsed.sort)\n\n return result\n }\n\n /** Map a single FilterItem to a Drizzle condition */\n private buildFilter(table: Record<string, any>, filter: FilterItem): any {\n const column = table[filter.field]\n if (!column) return null\n\n const value = this.coerce(filter.value)\n\n switch (filter.operator) {\n case 'eq':\n return this.ops.eq(column, value)\n case 'neq':\n return this.ops.ne(column, value)\n case 'gt':\n return this.ops.gt(column, value)\n case 'gte':\n return this.ops.gte(column, value)\n case 'lt':\n return this.ops.lt(column, value)\n case 'lte':\n return this.ops.lte(column, value)\n case 'contains':\n return this.ops.ilike(column, `%${filter.value}%`)\n case 'starts':\n return this.ops.ilike(column, `${filter.value}%`)\n case 'ends':\n return this.ops.ilike(column, `%${filter.value}`)\n case 'in': {\n const values = filter.value.split(',').map((v) => this.coerce(v.trim()))\n return this.ops.inArray(column, values)\n }\n case 'between': {\n const [min, max] = filter.value.split(',').map((v) => this.coerce(v.trim()))\n return this.ops.and(this.ops.gte(column, min), this.ops.lte(column, max))\n }\n default:\n return this.ops.eq(column, value)\n }\n }\n\n /** Build Drizzle orderBy from SortItem[] */\n private buildSort(table: Record<string, any>, sort: SortItem[]): any[] {\n return sort\n .filter((item) => table[item.field])\n .map((item) =>\n item.direction === 'desc'\n ? this.ops.desc(table[item.field])\n : this.ops.asc(table[item.field]),\n )\n }\n\n /** Attempt to coerce a string value to a number or boolean if appropriate */\n private coerce(value: string): string | number | boolean {\n if (value === 'true') return true\n if (value === 'false') return false\n const num = Number(value)\n if (!Number.isNaN(num) && value.trim() !== '') return num\n return value\n }\n}\n"],"mappings":"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","names":["Logger","Scope","DRIZZLE_DB","Symbol","log","Logger","for","DrizzleAdapter","name","db","onShutdown","options","beforeStart","_app","container","logging","info","registerFactory","DRIZZLE_DB","Scope","SINGLETON","shutdown","DrizzleQueryAdapter","name","ops","build","parsed","config","result","orderBy","limit","pagination","offset","conditions","filter","filters","condition","buildFilter","table","push","search","searchColumns","length","searchConditions","col","map","ilike","or","where","and","buildSort","sort","column","field","value","coerce","operator","eq","ne","gt","gte","lt","lte","values","split","v","trim","inArray","min","max","item","direction","desc","asc","num","Number","isNaN"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forinda/kickjs-drizzle",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Drizzle ORM adapter with DI integration, transaction support, and query building for KickJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kickjs",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"reflect-metadata": "^0.2.2",
|
|
47
|
-
"@forinda/kickjs-core": "
|
|
48
|
-
"@forinda/kickjs-http": "
|
|
47
|
+
"@forinda/kickjs-core": "1.1.1",
|
|
48
|
+
"@forinda/kickjs-http": "1.1.1"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"drizzle-orm": ">=0.30.0"
|