@forinda/kickjs-drizzle 0.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Felix Orinda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,184 @@
1
+ import { AppAdapter, Container } from '@forinda/kickjs-core';
2
+ import { QueryBuilderAdapter, ParsedQuery } from '@forinda/kickjs-http';
3
+
4
+ /** DI token for resolving the Drizzle database instance from the container */
5
+ declare const DRIZZLE_DB: unique symbol;
6
+ interface DrizzleAdapterOptions<TDb = unknown> {
7
+ /**
8
+ * Drizzle database instance — the return value of `drizzle()`.
9
+ * Preserves the full type so services can inject it type-safely.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { drizzle } from 'drizzle-orm/better-sqlite3'
14
+ * import * as schema from './schema'
15
+ *
16
+ * const db = drizzle({ client: sqlite, schema })
17
+ * // db is BetterSQLite3Database<typeof schema>
18
+ *
19
+ * new DrizzleAdapter({ db })
20
+ * // TDb is inferred as BetterSQLite3Database<typeof schema>
21
+ * ```
22
+ */
23
+ db: TDb;
24
+ /** Enable query logging (default: false) */
25
+ logging?: boolean;
26
+ /**
27
+ * Optional shutdown function to close the underlying connection pool.
28
+ * Drizzle doesn't expose a universal disconnect — this lets you pass your
29
+ * driver's cleanup (e.g., `pool.end()` for postgres, `client.close()` for libsql).
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const pool = new Pool({ connectionString: '...' })
34
+ * const db = drizzle(pool)
35
+ *
36
+ * new DrizzleAdapter({
37
+ * db,
38
+ * onShutdown: () => pool.end(),
39
+ * })
40
+ * ```
41
+ */
42
+ onShutdown?: () => void | Promise<void>;
43
+ }
44
+
45
+ /**
46
+ * Drizzle ORM adapter — registers a Drizzle database instance in the DI
47
+ * container and manages its lifecycle.
48
+ *
49
+ * Works with any Drizzle driver: `drizzle-orm/postgres-js`, `drizzle-orm/node-postgres`,
50
+ * `drizzle-orm/mysql2`, `drizzle-orm/better-sqlite3`, `drizzle-orm/libsql`, etc.
51
+ *
52
+ * The adapter is generic — the db type is inferred from what you pass in,
53
+ * so services can inject the fully-typed database instance.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import { drizzle } from 'drizzle-orm/better-sqlite3'
58
+ * import * as schema from './schema'
59
+ * import { DrizzleAdapter } from '@forinda/kickjs-drizzle'
60
+ *
61
+ * const db = drizzle({ client: sqlite, schema })
62
+ *
63
+ * bootstrap({
64
+ * modules,
65
+ * adapters: [
66
+ * new DrizzleAdapter({ db, onShutdown: () => sqlite.close() }),
67
+ * ],
68
+ * })
69
+ * ```
70
+ *
71
+ * Inject the typed db instance in services:
72
+ * ```ts
73
+ * import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'
74
+ * import * as schema from './schema'
75
+ *
76
+ * @Service()
77
+ * class UserService {
78
+ * constructor(@Inject(DRIZZLE_DB) private db: BetterSQLite3Database<typeof schema>) {}
79
+ * }
80
+ * ```
81
+ */
82
+ declare class DrizzleAdapter<TDb = unknown> implements AppAdapter {
83
+ private options;
84
+ name: string;
85
+ private db;
86
+ private onShutdown?;
87
+ constructor(options: DrizzleAdapterOptions<TDb>);
88
+ /** Register the Drizzle db instance in the DI container */
89
+ beforeStart(_app: unknown, container: Container): void;
90
+ /** Close the underlying connection on shutdown */
91
+ shutdown(): Promise<void>;
92
+ }
93
+
94
+ /**
95
+ * Configuration for the Drizzle query builder adapter.
96
+ *
97
+ * Unlike Prisma which uses its own query builder API, Drizzle uses SQL-like
98
+ * operators (`eq`, `gt`, `like`, etc.) from `drizzle-orm`. This adapter
99
+ * produces a config object that can be spread into Drizzle's `select().from().where()`.
100
+ */
101
+ interface DrizzleQueryConfig {
102
+ /** The Drizzle table schema object (e.g., `users` from your schema) */
103
+ table: Record<string, any>;
104
+ /** Columns to search across when a search string is provided */
105
+ searchColumns?: string[];
106
+ }
107
+ /**
108
+ * Result shape compatible with Drizzle's query builder.
109
+ * Use with `db.select().from(table).where(result.where).orderBy(...result.orderBy).limit(result.limit).offset(result.offset)`
110
+ */
111
+ interface DrizzleQueryResult {
112
+ /** SQL condition — pass to `.where()` */
113
+ where?: any;
114
+ /** Array of order expressions — spread into `.orderBy()` */
115
+ orderBy: any[];
116
+ /** Row limit — pass to `.limit()` */
117
+ limit: number;
118
+ /** Row offset — pass to `.offset()` */
119
+ offset: number;
120
+ }
121
+ /**
122
+ * Drizzle operator functions required by the query adapter.
123
+ * Pass these from your `drizzle-orm` import to avoid version coupling.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc } from 'drizzle-orm'
128
+ *
129
+ * const adapter = new DrizzleQueryAdapter({
130
+ * eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc,
131
+ * })
132
+ * ```
133
+ */
134
+ interface DrizzleOps {
135
+ eq: (column: any, value: any) => any;
136
+ ne: (column: any, value: any) => any;
137
+ gt: (column: any, value: any) => any;
138
+ gte: (column: any, value: any) => any;
139
+ lt: (column: any, value: any) => any;
140
+ lte: (column: any, value: any) => any;
141
+ ilike: (column: any, value: string) => any;
142
+ inArray: (column: any, values: any[]) => any;
143
+ and: (...conditions: any[]) => any;
144
+ or: (...conditions: any[]) => any;
145
+ asc: (column: any) => any;
146
+ desc: (column: any) => any;
147
+ }
148
+ /**
149
+ * Translates a ParsedQuery into Drizzle-compatible query parts.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * import { eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc } from 'drizzle-orm'
154
+ * import { users } from './schema'
155
+ *
156
+ * const adapter = new DrizzleQueryAdapter({
157
+ * eq, ne, gt, gte, lt, lte, ilike, inArray, and, or, asc, desc,
158
+ * })
159
+ * const parsed = ctx.qs({ filters: ['name', 'email'], sort: ['name'] })
160
+ * const query = adapter.build(parsed, { table: users, searchColumns: ['name', 'email'] })
161
+ *
162
+ * const results = await db
163
+ * .select()
164
+ * .from(users)
165
+ * .where(query.where)
166
+ * .orderBy(...query.orderBy)
167
+ * .limit(query.limit)
168
+ * .offset(query.offset)
169
+ * ```
170
+ */
171
+ declare class DrizzleQueryAdapter implements QueryBuilderAdapter<DrizzleQueryResult, DrizzleQueryConfig> {
172
+ private ops;
173
+ readonly name = "DrizzleQueryAdapter";
174
+ constructor(ops: DrizzleOps);
175
+ build(parsed: ParsedQuery, config?: DrizzleQueryConfig): DrizzleQueryResult;
176
+ /** Map a single FilterItem to a Drizzle condition */
177
+ private buildFilter;
178
+ /** Build Drizzle orderBy from SortItem[] */
179
+ private buildSort;
180
+ /** Attempt to coerce a string value to a number or boolean if appropriate */
181
+ private coerce;
182
+ }
183
+
184
+ export { DRIZZLE_DB, DrizzleAdapter, type DrizzleAdapterOptions, type DrizzleOps, DrizzleQueryAdapter, type DrizzleQueryConfig, type DrizzleQueryResult };
package/dist/index.js ADDED
@@ -0,0 +1,131 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/drizzle.adapter.ts
5
+ import { Logger, Scope } from "@forinda/kickjs-core";
6
+
7
+ // src/types.ts
8
+ var DRIZZLE_DB = /* @__PURE__ */ Symbol("DrizzleDB");
9
+
10
+ // src/drizzle.adapter.ts
11
+ var log = Logger.for("DrizzleAdapter");
12
+ var DrizzleAdapter = class {
13
+ static {
14
+ __name(this, "DrizzleAdapter");
15
+ }
16
+ options;
17
+ name = "DrizzleAdapter";
18
+ db;
19
+ onShutdown;
20
+ constructor(options) {
21
+ this.options = options;
22
+ this.db = options.db;
23
+ this.onShutdown = options.onShutdown;
24
+ }
25
+ /** Register the Drizzle db instance in the DI container */
26
+ beforeStart(_app, container) {
27
+ if (this.options.logging) {
28
+ log.info("Query logging enabled");
29
+ }
30
+ container.registerFactory(DRIZZLE_DB, () => this.db, Scope.SINGLETON);
31
+ log.info("Drizzle database registered in DI container");
32
+ }
33
+ /** Close the underlying connection on shutdown */
34
+ async shutdown() {
35
+ if (this.onShutdown) {
36
+ await this.onShutdown();
37
+ log.info("Drizzle connection closed");
38
+ }
39
+ }
40
+ };
41
+
42
+ // src/query-adapter.ts
43
+ var DrizzleQueryAdapter = class {
44
+ static {
45
+ __name(this, "DrizzleQueryAdapter");
46
+ }
47
+ ops;
48
+ name = "DrizzleQueryAdapter";
49
+ constructor(ops) {
50
+ this.ops = ops;
51
+ }
52
+ build(parsed, config = {}) {
53
+ const result = {
54
+ orderBy: [],
55
+ limit: parsed.pagination.limit,
56
+ offset: parsed.pagination.offset
57
+ };
58
+ const conditions = [];
59
+ for (const filter of parsed.filters) {
60
+ const condition = this.buildFilter(config.table, filter);
61
+ if (condition) conditions.push(condition);
62
+ }
63
+ if (parsed.search && config.searchColumns && config.searchColumns.length > 0) {
64
+ const searchConditions = config.searchColumns.filter((col) => config.table[col]).map((col) => this.ops.ilike(config.table[col], `%${parsed.search}%`));
65
+ if (searchConditions.length > 0) {
66
+ conditions.push(this.ops.or(...searchConditions));
67
+ }
68
+ }
69
+ if (conditions.length === 1) {
70
+ result.where = conditions[0];
71
+ } else if (conditions.length > 1) {
72
+ result.where = this.ops.and(...conditions);
73
+ }
74
+ result.orderBy = this.buildSort(config.table, parsed.sort);
75
+ return result;
76
+ }
77
+ /** Map a single FilterItem to a Drizzle condition */
78
+ buildFilter(table, filter) {
79
+ const column = table[filter.field];
80
+ if (!column) return null;
81
+ const value = this.coerce(filter.value);
82
+ switch (filter.operator) {
83
+ case "eq":
84
+ return this.ops.eq(column, value);
85
+ case "neq":
86
+ return this.ops.ne(column, value);
87
+ case "gt":
88
+ return this.ops.gt(column, value);
89
+ case "gte":
90
+ return this.ops.gte(column, value);
91
+ case "lt":
92
+ return this.ops.lt(column, value);
93
+ case "lte":
94
+ return this.ops.lte(column, value);
95
+ case "contains":
96
+ return this.ops.ilike(column, `%${filter.value}%`);
97
+ case "starts":
98
+ return this.ops.ilike(column, `${filter.value}%`);
99
+ case "ends":
100
+ return this.ops.ilike(column, `%${filter.value}`);
101
+ case "in": {
102
+ const values = filter.value.split(",").map((v) => this.coerce(v.trim()));
103
+ return this.ops.inArray(column, values);
104
+ }
105
+ case "between": {
106
+ const [min, max] = filter.value.split(",").map((v) => this.coerce(v.trim()));
107
+ return this.ops.and(this.ops.gte(column, min), this.ops.lte(column, max));
108
+ }
109
+ default:
110
+ return this.ops.eq(column, value);
111
+ }
112
+ }
113
+ /** Build Drizzle orderBy from SortItem[] */
114
+ buildSort(table, sort) {
115
+ return sort.filter((item) => table[item.field]).map((item) => item.direction === "desc" ? this.ops.desc(table[item.field]) : this.ops.asc(table[item.field]));
116
+ }
117
+ /** Attempt to coerce a string value to a number or boolean if appropriate */
118
+ coerce(value) {
119
+ if (value === "true") return true;
120
+ if (value === "false") return false;
121
+ const num = Number(value);
122
+ if (!Number.isNaN(num) && value.trim() !== "") return num;
123
+ return value;
124
+ }
125
+ };
126
+ export {
127
+ DRIZZLE_DB,
128
+ DrizzleAdapter,
129
+ DrizzleQueryAdapter
130
+ };
131
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@forinda/kickjs-drizzle",
3
+ "version": "0.5.1",
4
+ "description": "Drizzle ORM adapter with DI integration, transaction support, and query building for KickJS",
5
+ "keywords": [
6
+ "kickjs",
7
+ "nodejs",
8
+ "typescript",
9
+ "express",
10
+ "decorators",
11
+ "dependency-injection",
12
+ "backend",
13
+ "api",
14
+ "framework",
15
+ "drizzle",
16
+ "drizzle-orm",
17
+ "orm",
18
+ "database",
19
+ "query-builder",
20
+ "adapter",
21
+ "sql",
22
+ "postgres",
23
+ "mysql",
24
+ "sqlite",
25
+ "@forinda/kickjs-core",
26
+ "@forinda/kickjs-http",
27
+ "@forinda/kickjs-config",
28
+ "@forinda/kickjs-cli",
29
+ "@forinda/kickjs-swagger",
30
+ "@forinda/kickjs-testing",
31
+ "@forinda/kickjs-drizzle"
32
+ ],
33
+ "type": "module",
34
+ "main": "dist/index.js",
35
+ "types": "dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "import": "./dist/index.js",
39
+ "types": "./dist/index.d.ts"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist"
44
+ ],
45
+ "dependencies": {
46
+ "reflect-metadata": "^0.2.2",
47
+ "@forinda/kickjs-core": "0.5.2",
48
+ "@forinda/kickjs-http": "0.5.2"
49
+ },
50
+ "peerDependencies": {
51
+ "drizzle-orm": ">=0.30.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^24.5.2",
55
+ "tsup": "^8.5.0",
56
+ "typescript": "^5.9.2"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "license": "MIT",
62
+ "author": "Felix Orinda",
63
+ "engines": {
64
+ "node": ">=20.0"
65
+ },
66
+ "homepage": "https://forinda.github.io/kick-js/",
67
+ "repository": {
68
+ "type": "git",
69
+ "url": "https://github.com/forinda/kick-js.git",
70
+ "directory": "packages/drizzle"
71
+ },
72
+ "bugs": {
73
+ "url": "https://github.com/forinda/kick-js/issues"
74
+ },
75
+ "scripts": {
76
+ "build": "tsup",
77
+ "dev": "tsup --watch",
78
+ "typecheck": "tsc --noEmit",
79
+ "clean": "rm -rf dist .turbo",
80
+ "lint": "tsc --noEmit"
81
+ }
82
+ }