express-model-binding 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/dist/BaseAdapter-BjvLQijd.d.mts +214 -0
  4. package/dist/BaseAdapter-BjvLQijd.d.ts +214 -0
  5. package/dist/adapters/KnexAdapter.d.mts +44 -0
  6. package/dist/adapters/KnexAdapter.d.ts +44 -0
  7. package/dist/adapters/KnexAdapter.js +257 -0
  8. package/dist/adapters/KnexAdapter.js.map +1 -0
  9. package/dist/adapters/KnexAdapter.mjs +229 -0
  10. package/dist/adapters/KnexAdapter.mjs.map +1 -0
  11. package/dist/adapters/MongooseAdapter.d.mts +30 -0
  12. package/dist/adapters/MongooseAdapter.d.ts +30 -0
  13. package/dist/adapters/MongooseAdapter.js +245 -0
  14. package/dist/adapters/MongooseAdapter.js.map +1 -0
  15. package/dist/adapters/MongooseAdapter.mjs +225 -0
  16. package/dist/adapters/MongooseAdapter.mjs.map +1 -0
  17. package/dist/adapters/PrismaAdapter.d.mts +42 -0
  18. package/dist/adapters/PrismaAdapter.d.ts +42 -0
  19. package/dist/adapters/PrismaAdapter.js +247 -0
  20. package/dist/adapters/PrismaAdapter.js.map +1 -0
  21. package/dist/adapters/PrismaAdapter.mjs +220 -0
  22. package/dist/adapters/PrismaAdapter.mjs.map +1 -0
  23. package/dist/adapters/SequelizeAdapter.d.mts +27 -0
  24. package/dist/adapters/SequelizeAdapter.d.ts +27 -0
  25. package/dist/adapters/SequelizeAdapter.js +280 -0
  26. package/dist/adapters/SequelizeAdapter.js.map +1 -0
  27. package/dist/adapters/SequelizeAdapter.mjs +260 -0
  28. package/dist/adapters/SequelizeAdapter.mjs.map +1 -0
  29. package/dist/adapters/TypeORMAdapter.d.mts +26 -0
  30. package/dist/adapters/TypeORMAdapter.d.ts +26 -0
  31. package/dist/adapters/TypeORMAdapter.js +294 -0
  32. package/dist/adapters/TypeORMAdapter.js.map +1 -0
  33. package/dist/adapters/TypeORMAdapter.mjs +267 -0
  34. package/dist/adapters/TypeORMAdapter.mjs.map +1 -0
  35. package/dist/index.d.mts +411 -0
  36. package/dist/index.d.ts +411 -0
  37. package/dist/index.js +1514 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/index.mjs +1450 -0
  40. package/dist/index.mjs.map +1 -0
  41. package/package.json +148 -0
@@ -0,0 +1,44 @@
1
+ import { Knex } from 'knex';
2
+ import { b as BaseAdapter, Q as QueryOptions, d as ModelMetadata } from '../BaseAdapter-BjvLQijd.js';
3
+ import 'express';
4
+
5
+ /**
6
+ * Model definition for Knex tables with soft delete support
7
+ */
8
+ interface KnexModel {
9
+ tableName: string;
10
+ primaryKey?: string;
11
+ softDeleteColumn?: string;
12
+ }
13
+ /**
14
+ * Union type for Knex model inputs
15
+ */
16
+ type KnexModelInput = string | KnexModel;
17
+ /**
18
+ * Adapter for Knex query builder supporting PostgreSQL, MySQL, SQLite, MSSQL, Oracle
19
+ */
20
+ declare class KnexAdapter extends BaseAdapter<KnexModelInput, Record<string, unknown>, Knex.QueryBuilder> {
21
+ private knex;
22
+ readonly name = "knex";
23
+ constructor(knex: Knex);
24
+ getKnex(): Knex;
25
+ findByKey(model: KnexModelInput, key: string, value: unknown, options?: QueryOptions): Promise<Record<string, unknown> | null>;
26
+ getPrimaryKeyName(model: KnexModelInput): string;
27
+ isValidModel(model: unknown): model is KnexModelInput;
28
+ transformValue(model: KnexModelInput, key: string, value: string): unknown;
29
+ supportsSoftDeletes(model: KnexModelInput): boolean;
30
+ getModelMetadata(model: KnexModelInput): ModelMetadata;
31
+ private getTableName;
32
+ private getSoftDeleteColumn;
33
+ protected applyWhereConditions(query: Knex.QueryBuilder, where: Record<string, unknown>): Knex.QueryBuilder;
34
+ }
35
+ /**
36
+ * Create a Knex model definition
37
+ */
38
+ declare function defineKnexModel(config: {
39
+ tableName: string;
40
+ primaryKey?: string;
41
+ softDeleteColumn?: string;
42
+ }): KnexModel;
43
+
44
+ export { KnexAdapter, type KnexModel, type KnexModelInput, defineKnexModel };
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/KnexAdapter.ts
21
+ var KnexAdapter_exports = {};
22
+ __export(KnexAdapter_exports, {
23
+ KnexAdapter: () => KnexAdapter,
24
+ defineKnexModel: () => defineKnexModel
25
+ });
26
+ module.exports = __toCommonJS(KnexAdapter_exports);
27
+
28
+ // src/errors/index.ts
29
+ var BindingError = class extends Error {
30
+ constructor(message, originalError) {
31
+ super(message);
32
+ this.name = "BindingError";
33
+ this.originalError = originalError;
34
+ Error.captureStackTrace(this, this.constructor);
35
+ }
36
+ toJSON() {
37
+ return {
38
+ name: this.name,
39
+ message: this.message,
40
+ originalError: this.originalError?.message
41
+ };
42
+ }
43
+ };
44
+ var InvalidModelError = class extends Error {
45
+ constructor(message, model) {
46
+ super(message);
47
+ this.name = "InvalidModelError";
48
+ this.model = model;
49
+ Error.captureStackTrace(this, this.constructor);
50
+ }
51
+ toJSON() {
52
+ return {
53
+ name: this.name,
54
+ message: this.message,
55
+ model: typeof this.model === "string" ? this.model : String(this.model)
56
+ };
57
+ }
58
+ };
59
+
60
+ // src/core/BaseAdapter.ts
61
+ var BaseAdapter = class {
62
+ transformValue(_model, _key, value) {
63
+ if (/^\d+$/.test(value)) {
64
+ const num = parseInt(value, 10);
65
+ if (!isNaN(num) && Number.isSafeInteger(num)) {
66
+ return num;
67
+ }
68
+ }
69
+ return value;
70
+ }
71
+ supportsSoftDeletes(_model) {
72
+ return false;
73
+ }
74
+ getModelMetadata(model) {
75
+ return {
76
+ name: this.getModelName(model),
77
+ primaryKey: this.getPrimaryKeyName(model),
78
+ softDeletes: this.supportsSoftDeletes(model),
79
+ adapter: this.name
80
+ };
81
+ }
82
+ validateModel(model) {
83
+ if (!this.isValidModel(model)) {
84
+ throw new InvalidModelError(`Invalid model for ${this.name} adapter`, model);
85
+ }
86
+ }
87
+ getModelName(model) {
88
+ if (typeof model === "string") {
89
+ return model;
90
+ }
91
+ if (model && typeof model === "object") {
92
+ const obj = model;
93
+ if (typeof obj.name === "string") return obj.name;
94
+ if (typeof obj.modelName === "string") return obj.modelName;
95
+ if (typeof obj.tableName === "string") return obj.tableName;
96
+ }
97
+ if (model && typeof model === "function") {
98
+ return model.name || "Unknown";
99
+ }
100
+ return "Unknown";
101
+ }
102
+ applySoftDeleteFilter(queryBuilder, _options) {
103
+ return queryBuilder;
104
+ }
105
+ applyIncludes(queryBuilder, _includes) {
106
+ return queryBuilder;
107
+ }
108
+ applySelect(queryBuilder, _select) {
109
+ return queryBuilder;
110
+ }
111
+ applyWhereConditions(queryBuilder, _where) {
112
+ return queryBuilder;
113
+ }
114
+ applyCustomQuery(queryBuilder, queryFn) {
115
+ if (queryFn) {
116
+ return queryFn(queryBuilder);
117
+ }
118
+ return queryBuilder;
119
+ }
120
+ };
121
+
122
+ // src/core/types.ts
123
+ function isOperatorCondition(value) {
124
+ return typeof value === "object" && value !== null && "operator" in value && "value" in value;
125
+ }
126
+
127
+ // src/utils/validators.ts
128
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
129
+ function isUUID(value) {
130
+ return UUID_REGEX.test(value);
131
+ }
132
+
133
+ // src/adapters/KnexAdapter.ts
134
+ function isKnexModel(value) {
135
+ return typeof value === "object" && value !== null && typeof value.tableName === "string";
136
+ }
137
+ var KnexAdapter = class extends BaseAdapter {
138
+ constructor(knex) {
139
+ super();
140
+ this.knex = knex;
141
+ this.name = "knex";
142
+ }
143
+ getKnex() {
144
+ return this.knex;
145
+ }
146
+ async findByKey(model, key, value, options = {}) {
147
+ this.validateModel(model);
148
+ const tableName = this.getTableName(model);
149
+ try {
150
+ let query = this.knex(tableName);
151
+ query = query.where(key, value);
152
+ if (this.supportsSoftDeletes(model) && !options.withTrashed && !options.onlyTrashed) {
153
+ const softDeleteColumn = this.getSoftDeleteColumn(model);
154
+ query = query.whereNull(softDeleteColumn);
155
+ } else if (options.onlyTrashed && this.supportsSoftDeletes(model)) {
156
+ const softDeleteColumn = this.getSoftDeleteColumn(model);
157
+ query = query.whereNotNull(softDeleteColumn);
158
+ }
159
+ if (options.where) {
160
+ query = this.applyWhereConditions(query, options.where);
161
+ }
162
+ if (options.select && options.select.length > 0) {
163
+ query = query.select(options.select);
164
+ } else {
165
+ query = query.select("*");
166
+ }
167
+ if (options.query) {
168
+ query = this.applyCustomQuery(
169
+ query,
170
+ options.query
171
+ );
172
+ }
173
+ if (options.lock === "forUpdate") {
174
+ query = query.forUpdate();
175
+ } else if (options.lock === "forShare") {
176
+ query = query.forShare();
177
+ }
178
+ const result = await query.first();
179
+ return result || null;
180
+ } catch (error) {
181
+ throw new BindingError(
182
+ `Failed to fetch ${this.getModelName(model)}: ${error.message}`,
183
+ error
184
+ );
185
+ }
186
+ }
187
+ getPrimaryKeyName(model) {
188
+ if (isKnexModel(model) && model.primaryKey) {
189
+ return model.primaryKey;
190
+ }
191
+ return "id";
192
+ }
193
+ isValidModel(model) {
194
+ return typeof model === "string" || isKnexModel(model);
195
+ }
196
+ transformValue(model, key, value) {
197
+ const primaryKey = this.getPrimaryKeyName(model);
198
+ if (key === primaryKey || key === "id") {
199
+ const num = parseInt(value, 10);
200
+ if (!isNaN(num) && num.toString() === value && Number.isSafeInteger(num)) {
201
+ return num;
202
+ }
203
+ }
204
+ if (isUUID(value)) {
205
+ return value;
206
+ }
207
+ return value;
208
+ }
209
+ supportsSoftDeletes(model) {
210
+ return isKnexModel(model) && !!model.softDeleteColumn;
211
+ }
212
+ getModelMetadata(model) {
213
+ return {
214
+ name: this.getModelName(model),
215
+ primaryKey: this.getPrimaryKeyName(model),
216
+ tableName: this.getTableName(model),
217
+ softDeletes: this.supportsSoftDeletes(model),
218
+ adapter: this.name
219
+ };
220
+ }
221
+ getTableName(model) {
222
+ return typeof model === "string" ? model : model.tableName;
223
+ }
224
+ getSoftDeleteColumn(model) {
225
+ if (isKnexModel(model) && model.softDeleteColumn) {
226
+ return model.softDeleteColumn;
227
+ }
228
+ return "deleted_at";
229
+ }
230
+ applyWhereConditions(query, where) {
231
+ Object.entries(where).forEach(([column, value]) => {
232
+ if (value === null) {
233
+ query = query.whereNull(column);
234
+ } else if (Array.isArray(value)) {
235
+ query = query.whereIn(column, value);
236
+ } else if (isOperatorCondition(value)) {
237
+ query = query.where(column, value.operator, value.value);
238
+ } else {
239
+ query = query.where(column, value);
240
+ }
241
+ });
242
+ return query;
243
+ }
244
+ };
245
+ function defineKnexModel(config) {
246
+ return {
247
+ tableName: config.tableName,
248
+ primaryKey: config.primaryKey || "id",
249
+ softDeleteColumn: config.softDeleteColumn
250
+ };
251
+ }
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ KnexAdapter,
255
+ defineKnexModel
256
+ });
257
+ //# sourceMappingURL=KnexAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/KnexAdapter.ts","../../src/errors/index.ts","../../src/core/BaseAdapter.ts","../../src/core/types.ts","../../src/utils/validators.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport { BaseAdapter } from '../core/BaseAdapter';\nimport { QueryOptions, ModelMetadata, isOperatorCondition } from '../core/types';\nimport { BindingError } from '../errors';\nimport { isUUID } from '../utils/validators';\n\n/**\n * Model definition for Knex tables with soft delete support\n */\nexport interface KnexModel {\n tableName: string;\n primaryKey?: string;\n softDeleteColumn?: string;\n}\n\n/**\n * Union type for Knex model inputs\n */\nexport type KnexModelInput = string | KnexModel;\n\n/**\n * Type guard for KnexModel\n */\nfunction isKnexModel(value: unknown): value is KnexModel {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as KnexModel).tableName === 'string'\n );\n}\n\n/**\n * Adapter for Knex query builder supporting PostgreSQL, MySQL, SQLite, MSSQL, Oracle\n */\nexport class KnexAdapter extends BaseAdapter<\n KnexModelInput,\n Record<string, unknown>,\n Knex.QueryBuilder\n> {\n readonly name = 'knex';\n\n constructor(private knex: Knex) {\n super();\n }\n\n getKnex(): Knex {\n return this.knex;\n }\n\n async findByKey(\n model: KnexModelInput,\n key: string,\n value: unknown,\n options: QueryOptions = {}\n ): Promise<Record<string, unknown> | null> {\n this.validateModel(model);\n\n const tableName = this.getTableName(model);\n\n try {\n let query = this.knex(tableName);\n\n query = query.where(key, value as Knex.Value);\n\n if (this.supportsSoftDeletes(model) && !options.withTrashed && !options.onlyTrashed) {\n const softDeleteColumn = this.getSoftDeleteColumn(model);\n query = query.whereNull(softDeleteColumn);\n } else if (options.onlyTrashed && this.supportsSoftDeletes(model)) {\n const softDeleteColumn = this.getSoftDeleteColumn(model);\n query = query.whereNotNull(softDeleteColumn);\n }\n\n if (options.where) {\n query = this.applyWhereConditions(query, options.where);\n }\n\n if (options.select && options.select.length > 0) {\n query = query.select(options.select);\n } else {\n query = query.select('*');\n }\n\n if (options.query) {\n query = this.applyCustomQuery(\n query,\n options.query as (qb: Knex.QueryBuilder) => Knex.QueryBuilder\n );\n }\n\n if (options.lock === 'forUpdate') {\n query = query.forUpdate();\n } else if (options.lock === 'forShare') {\n query = query.forShare();\n }\n\n const result = await query.first();\n return (result as Record<string, unknown>) || null;\n } catch (error) {\n throw new BindingError(\n `Failed to fetch ${this.getModelName(model)}: ${(error as Error).message}`,\n error as Error\n );\n }\n }\n\n getPrimaryKeyName(model: KnexModelInput): string {\n if (isKnexModel(model) && model.primaryKey) {\n return model.primaryKey;\n }\n return 'id';\n }\n\n isValidModel(model: unknown): model is KnexModelInput {\n return typeof model === 'string' || isKnexModel(model);\n }\n\n transformValue(model: KnexModelInput, key: string, value: string): unknown {\n const primaryKey = this.getPrimaryKeyName(model);\n\n if (key === primaryKey || key === 'id') {\n const num = parseInt(value, 10);\n if (!isNaN(num) && num.toString() === value && Number.isSafeInteger(num)) {\n return num;\n }\n }\n\n if (isUUID(value)) {\n return value;\n }\n\n return value;\n }\n\n supportsSoftDeletes(model: KnexModelInput): boolean {\n return isKnexModel(model) && !!model.softDeleteColumn;\n }\n\n getModelMetadata(model: KnexModelInput): ModelMetadata {\n return {\n name: this.getModelName(model),\n primaryKey: this.getPrimaryKeyName(model),\n tableName: this.getTableName(model),\n softDeletes: this.supportsSoftDeletes(model),\n adapter: this.name,\n };\n }\n\n private getTableName(model: KnexModelInput): string {\n return typeof model === 'string' ? model : model.tableName;\n }\n\n private getSoftDeleteColumn(model: KnexModelInput): string {\n if (isKnexModel(model) && model.softDeleteColumn) {\n return model.softDeleteColumn;\n }\n return 'deleted_at';\n }\n\n protected applyWhereConditions(\n query: Knex.QueryBuilder,\n where: Record<string, unknown>\n ): Knex.QueryBuilder {\n Object.entries(where).forEach(([column, value]) => {\n if (value === null) {\n query = query.whereNull(column);\n } else if (Array.isArray(value)) {\n query = query.whereIn(column, value as Knex.Value[]);\n } else if (isOperatorCondition(value)) {\n query = query.where(column, value.operator, value.value as Knex.Value);\n } else {\n query = query.where(column, value as Knex.Value);\n }\n });\n return query;\n }\n}\n\n/**\n * Create a Knex model definition\n */\nexport function defineKnexModel(config: {\n tableName: string;\n primaryKey?: string;\n softDeleteColumn?: string;\n}): KnexModel {\n return {\n tableName: config.tableName,\n primaryKey: config.primaryKey || 'id',\n softDeleteColumn: config.softDeleteColumn,\n };\n}\n","/**\n * Base error class for model binding errors\n */\nexport class BindingError extends Error {\n public readonly originalError?: Error;\n\n constructor(message: string, originalError?: Error) {\n super(message);\n this.name = 'BindingError';\n this.originalError = originalError;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n originalError: this.originalError?.message,\n };\n }\n}\n\n/**\n * Error thrown when a model is not found (404)\n */\nexport class ModelNotFoundError extends Error {\n public readonly statusCode = 404;\n public readonly paramName: string;\n public readonly paramValue: string;\n public readonly modelName: string;\n\n constructor(paramName: string, paramValue: string, modelName: string, customMessage?: string) {\n super(customMessage || `${modelName} not found with ${paramName} = ${paramValue}`);\n this.name = 'ModelNotFoundError';\n this.paramName = paramName;\n this.paramValue = paramValue;\n this.modelName = modelName;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n error: 'Not Found',\n message: this.message,\n statusCode: this.statusCode,\n param: this.paramName,\n value: this.paramValue,\n model: this.modelName,\n };\n }\n}\n\n/**\n * Error thrown when no adapter is configured\n */\nexport class AdapterNotSetError extends Error {\n constructor(\n message: string = 'No adapter set. Call ModelBinder.setAdapter() before using model binding.'\n ) {\n super(message);\n this.name = 'AdapterNotSetError';\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n };\n }\n}\n\n/**\n * Error thrown when a model is invalid for an adapter\n */\nexport class InvalidModelError extends Error {\n public readonly model: unknown;\n\n constructor(message: string, model: unknown) {\n super(message);\n this.name = 'InvalidModelError';\n this.model = model;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n model: typeof this.model === 'string' ? this.model : String(this.model),\n };\n }\n}\n\n/**\n * Error thrown when validation fails\n */\nexport class ValidationError extends Error {\n public readonly statusCode: number;\n public readonly details?: unknown;\n\n constructor(message: string, statusCode: number = 400, details?: unknown) {\n super(message);\n this.name = 'ValidationError';\n this.statusCode = statusCode;\n this.details = details;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n statusCode: this.statusCode,\n details: this.details,\n };\n }\n}\n","import { IORMAdapter, QueryOptions, ModelMetadata, QueryModifier } from './types';\nimport { InvalidModelError } from '../errors';\n\n/**\n * Abstract base class providing common adapter functionality.\n * Extend this class to implement ORM-specific adapters.\n *\n * @typeParam TModel - Model type accepted by this adapter\n * @typeParam TResult - Result type returned by queries\n * @typeParam TQueryBuilder - ORM-specific query builder type\n */\nexport abstract class BaseAdapter<\n TModel = unknown,\n TResult = unknown,\n TQueryBuilder = unknown,\n> implements IORMAdapter<TModel, TResult> {\n abstract readonly name: string;\n\n abstract findByKey(\n model: TModel,\n key: string,\n value: unknown,\n options?: QueryOptions\n ): Promise<TResult | null>;\n\n abstract getPrimaryKeyName(model: TModel): string;\n\n abstract isValidModel(model: unknown): model is TModel;\n\n transformValue(_model: TModel, _key: string, value: string): unknown {\n if (/^\\d+$/.test(value)) {\n const num = parseInt(value, 10);\n if (!isNaN(num) && Number.isSafeInteger(num)) {\n return num;\n }\n }\n return value;\n }\n\n supportsSoftDeletes(_model: TModel): boolean {\n return false;\n }\n\n getModelMetadata(model: TModel): ModelMetadata {\n return {\n name: this.getModelName(model),\n primaryKey: this.getPrimaryKeyName(model),\n softDeletes: this.supportsSoftDeletes(model),\n adapter: this.name,\n };\n }\n\n protected validateModel(model: unknown): asserts model is TModel {\n if (!this.isValidModel(model)) {\n throw new InvalidModelError(`Invalid model for ${this.name} adapter`, model);\n }\n }\n\n protected getModelName(model: TModel): string {\n if (typeof model === 'string') {\n return model;\n }\n if (model && typeof model === 'object') {\n const obj = model as Record<string, unknown>;\n if (typeof obj.name === 'string') return obj.name;\n if (typeof obj.modelName === 'string') return obj.modelName;\n if (typeof obj.tableName === 'string') return obj.tableName;\n }\n if (model && typeof model === 'function') {\n return (model as { name: string }).name || 'Unknown';\n }\n return 'Unknown';\n }\n\n protected applySoftDeleteFilter(\n queryBuilder: TQueryBuilder,\n _options?: QueryOptions\n ): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applyIncludes(\n queryBuilder: TQueryBuilder,\n _includes?: string[] | Record<string, unknown>\n ): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applySelect(queryBuilder: TQueryBuilder, _select?: string[]): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applyWhereConditions(\n queryBuilder: TQueryBuilder,\n _where?: Record<string, unknown>\n ): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applyCustomQuery(\n queryBuilder: TQueryBuilder,\n queryFn?: QueryModifier<TQueryBuilder>\n ): TQueryBuilder {\n if (queryFn) {\n return queryFn(queryBuilder) as TQueryBuilder;\n }\n return queryBuilder;\n }\n}\n","import { Request, Response, NextFunction, RequestHandler } from 'express';\n\n/**\n * Generic query builder type - adapters can narrow this to their specific type\n */\nexport type QueryBuilder<T = unknown> = T;\n\n/**\n * Query modifier function type\n */\nexport type QueryModifier<T = unknown> = (queryBuilder: QueryBuilder<T>) => QueryBuilder<T>;\n\n/**\n * Base adapter interface that all ORM adapters must implement\n */\nexport interface IORMAdapter<TModel = unknown, TResult = unknown> {\n readonly name: string;\n\n findByKey(\n model: TModel,\n key: string,\n value: unknown,\n options?: QueryOptions\n ): Promise<TResult | null>;\n\n getPrimaryKeyName(model: TModel): string;\n\n isValidModel(model: unknown): model is TModel;\n\n transformValue(model: TModel, key: string, value: string): unknown;\n\n supportsSoftDeletes(model: TModel): boolean;\n\n getModelMetadata?(model: TModel): ModelMetadata;\n}\n\n/**\n * Query options for model lookups\n */\nexport interface QueryOptions {\n /**\n * Relations to eager load\n */\n include?: string[] | Record<string, unknown>;\n\n /**\n * Additional WHERE conditions\n */\n where?: Record<string, unknown>;\n\n /**\n * Custom query modifier - receives ORM-specific query builder\n */\n query?: QueryModifier;\n\n /**\n * Fields to select\n */\n select?: string[];\n\n /**\n * Include soft-deleted records\n */\n withTrashed?: boolean;\n\n /**\n * Only return soft-deleted records\n */\n onlyTrashed?: boolean;\n\n /**\n * Enable result caching\n */\n cache?: boolean | number;\n\n /**\n * Row locking for transactions\n */\n lock?: 'forUpdate' | 'forShare';\n}\n\n/**\n * Binding middleware options\n */\nexport interface BindOptions extends QueryOptions {\n /**\n * Field to search by (defaults to primary key)\n */\n key?: string;\n\n /**\n * Custom error when model not found\n */\n onNotFound?: Error | ((paramName: string, paramValue: string) => Error);\n\n /**\n * Transform parameter value before querying\n */\n transformValue?: (value: string) => unknown;\n\n /**\n * Property name for attaching model to request\n */\n as?: string;\n\n /**\n * Don't throw if model not found\n */\n optional?: boolean;\n\n /**\n * Custom 404 message\n */\n errorMessage?: string;\n\n /**\n * Validate loaded model\n */\n validate?: (model: unknown, req: Request) => void | Promise<void>;\n\n /**\n * Enable caching\n */\n cache?: boolean;\n\n /**\n * Cache TTL in milliseconds\n */\n cacheTTL?: number;\n}\n\n/**\n * Multi-model binding configuration\n */\nexport interface ModelBindingsConfig {\n [paramName: string]: {\n model: unknown;\n options?: BindOptions;\n };\n}\n\n/**\n * Binding operation context\n */\nexport interface BindingContext {\n req: Request;\n res: Response;\n paramName: string;\n paramValue: string;\n model: unknown;\n options: BindOptions;\n adapter: IORMAdapter;\n startTime: number;\n}\n\n/**\n * Binding operation result\n */\nexport interface BindingResult {\n success: boolean;\n model?: unknown;\n error?: Error;\n duration: number;\n fromCache?: boolean;\n}\n\n/**\n * Model metadata for debugging\n */\nexport interface ModelMetadata {\n name: string;\n primaryKey: string;\n tableName?: string;\n relations?: string[];\n softDeletes: boolean;\n adapter?: string;\n [key: string]: unknown;\n}\n\n/**\n * Cache entry structure\n */\nexport interface CacheEntry<T = unknown> {\n value: T;\n timestamp: number;\n ttl: number;\n}\n\n/**\n * Global configuration options\n */\nexport interface ModelBindingGlobalConfig {\n adapter?: IORMAdapter;\n cache?: {\n enabled: boolean;\n ttl: number;\n maxSize?: number;\n };\n debug?: boolean;\n logger?: (message: string, context?: unknown) => void;\n onError?: (error: Error, context: BindingContext) => void;\n}\n\n/**\n * Express request with bound models\n */\nexport interface TypedRequest<\n P = Record<string, string>,\n ResBody = unknown,\n ReqBody = unknown,\n ReqQuery = unknown,\n Models extends Record<string, unknown> = Record<string, unknown>,\n> extends Request<P, ResBody, ReqBody, ReqQuery> {\n [K: string]: unknown;\n models?: Models;\n}\n\n/**\n * Extract model type from binding config\n */\nexport type ExtractModelType<T> = T extends { model: infer M } ? M : never;\n\n/**\n * Typed request handler\n */\nexport type TypedRequestHandler<\n Models extends Record<string, unknown> = Record<string, unknown>,\n P = Record<string, string>,\n ResBody = unknown,\n ReqBody = unknown,\n ReqQuery = unknown,\n> = (\n req: TypedRequest<P, ResBody, ReqBody, ReqQuery, Models>,\n res: Response<ResBody>,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Middleware function type\n */\nexport type MiddlewareFunction = RequestHandler;\n\n/**\n * Operator condition for advanced WHERE clauses\n */\nexport interface OperatorCondition {\n operator: string;\n value: unknown;\n}\n\n/**\n * Check if value is an operator condition\n */\nexport function isOperatorCondition(value: unknown): value is OperatorCondition {\n return typeof value === 'object' && value !== null && 'operator' in value && 'value' in value;\n}\n","/**\n * UUID regex pattern\n */\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * MongoDB ObjectId regex pattern (24 hex characters)\n */\nconst OBJECT_ID_REGEX = /^[0-9a-f]{24}$/i;\n\n/**\n * Check if a value is a valid UUID\n */\nexport function isUUID(value: string): boolean {\n return UUID_REGEX.test(value);\n}\n\n/**\n * Check if a value is a valid MongoDB ObjectId\n */\nexport function isObjectId(value: string): boolean {\n return OBJECT_ID_REGEX.test(value);\n}\n\n/**\n * Check if a value is a numeric string\n */\nexport function isNumeric(value: string): boolean {\n return /^-?\\d+$/.test(value);\n}\n\n/**\n * Check if a value is a valid positive integer\n */\nexport function isPositiveInteger(value: string): boolean {\n return /^\\d+$/.test(value) && parseInt(value, 10) > 0;\n}\n\n/**\n * Check if a value is a valid slug (lowercase alphanumeric with hyphens)\n */\nexport function isSlug(value: string): boolean {\n return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);\n}\n\n/**\n * Check if a value is a valid email\n */\nexport function isEmail(value: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value);\n}\n\n/**\n * Validate that a value is not empty\n */\nexport function isNotEmpty(value: unknown): boolean {\n if (value === null || value === undefined) {\n return false;\n }\n if (typeof value === 'string') {\n return value.trim().length > 0;\n }\n return true;\n}\n\n/**\n * Validate that a value is a non-empty string\n */\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/**\n * Validate that a value is a plain object\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Validate that a value is a function\n */\n// eslint-disable-next-line @typescript-eslint/ban-types\nexport function isFunction(value: unknown): value is Function {\n return typeof value === 'function';\n}\n\n/**\n * Validate route parameter name\n */\nexport function isValidParamName(value: string): boolean {\n return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,229 @@
1
+ // src/errors/index.ts
2
+ var BindingError = class extends Error {
3
+ constructor(message, originalError) {
4
+ super(message);
5
+ this.name = "BindingError";
6
+ this.originalError = originalError;
7
+ Error.captureStackTrace(this, this.constructor);
8
+ }
9
+ toJSON() {
10
+ return {
11
+ name: this.name,
12
+ message: this.message,
13
+ originalError: this.originalError?.message
14
+ };
15
+ }
16
+ };
17
+ var InvalidModelError = class extends Error {
18
+ constructor(message, model) {
19
+ super(message);
20
+ this.name = "InvalidModelError";
21
+ this.model = model;
22
+ Error.captureStackTrace(this, this.constructor);
23
+ }
24
+ toJSON() {
25
+ return {
26
+ name: this.name,
27
+ message: this.message,
28
+ model: typeof this.model === "string" ? this.model : String(this.model)
29
+ };
30
+ }
31
+ };
32
+
33
+ // src/core/BaseAdapter.ts
34
+ var BaseAdapter = class {
35
+ transformValue(_model, _key, value) {
36
+ if (/^\d+$/.test(value)) {
37
+ const num = parseInt(value, 10);
38
+ if (!isNaN(num) && Number.isSafeInteger(num)) {
39
+ return num;
40
+ }
41
+ }
42
+ return value;
43
+ }
44
+ supportsSoftDeletes(_model) {
45
+ return false;
46
+ }
47
+ getModelMetadata(model) {
48
+ return {
49
+ name: this.getModelName(model),
50
+ primaryKey: this.getPrimaryKeyName(model),
51
+ softDeletes: this.supportsSoftDeletes(model),
52
+ adapter: this.name
53
+ };
54
+ }
55
+ validateModel(model) {
56
+ if (!this.isValidModel(model)) {
57
+ throw new InvalidModelError(`Invalid model for ${this.name} adapter`, model);
58
+ }
59
+ }
60
+ getModelName(model) {
61
+ if (typeof model === "string") {
62
+ return model;
63
+ }
64
+ if (model && typeof model === "object") {
65
+ const obj = model;
66
+ if (typeof obj.name === "string") return obj.name;
67
+ if (typeof obj.modelName === "string") return obj.modelName;
68
+ if (typeof obj.tableName === "string") return obj.tableName;
69
+ }
70
+ if (model && typeof model === "function") {
71
+ return model.name || "Unknown";
72
+ }
73
+ return "Unknown";
74
+ }
75
+ applySoftDeleteFilter(queryBuilder, _options) {
76
+ return queryBuilder;
77
+ }
78
+ applyIncludes(queryBuilder, _includes) {
79
+ return queryBuilder;
80
+ }
81
+ applySelect(queryBuilder, _select) {
82
+ return queryBuilder;
83
+ }
84
+ applyWhereConditions(queryBuilder, _where) {
85
+ return queryBuilder;
86
+ }
87
+ applyCustomQuery(queryBuilder, queryFn) {
88
+ if (queryFn) {
89
+ return queryFn(queryBuilder);
90
+ }
91
+ return queryBuilder;
92
+ }
93
+ };
94
+
95
+ // src/core/types.ts
96
+ function isOperatorCondition(value) {
97
+ return typeof value === "object" && value !== null && "operator" in value && "value" in value;
98
+ }
99
+
100
+ // src/utils/validators.ts
101
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
102
+ function isUUID(value) {
103
+ return UUID_REGEX.test(value);
104
+ }
105
+
106
+ // src/adapters/KnexAdapter.ts
107
+ function isKnexModel(value) {
108
+ return typeof value === "object" && value !== null && typeof value.tableName === "string";
109
+ }
110
+ var KnexAdapter = class extends BaseAdapter {
111
+ constructor(knex) {
112
+ super();
113
+ this.knex = knex;
114
+ this.name = "knex";
115
+ }
116
+ getKnex() {
117
+ return this.knex;
118
+ }
119
+ async findByKey(model, key, value, options = {}) {
120
+ this.validateModel(model);
121
+ const tableName = this.getTableName(model);
122
+ try {
123
+ let query = this.knex(tableName);
124
+ query = query.where(key, value);
125
+ if (this.supportsSoftDeletes(model) && !options.withTrashed && !options.onlyTrashed) {
126
+ const softDeleteColumn = this.getSoftDeleteColumn(model);
127
+ query = query.whereNull(softDeleteColumn);
128
+ } else if (options.onlyTrashed && this.supportsSoftDeletes(model)) {
129
+ const softDeleteColumn = this.getSoftDeleteColumn(model);
130
+ query = query.whereNotNull(softDeleteColumn);
131
+ }
132
+ if (options.where) {
133
+ query = this.applyWhereConditions(query, options.where);
134
+ }
135
+ if (options.select && options.select.length > 0) {
136
+ query = query.select(options.select);
137
+ } else {
138
+ query = query.select("*");
139
+ }
140
+ if (options.query) {
141
+ query = this.applyCustomQuery(
142
+ query,
143
+ options.query
144
+ );
145
+ }
146
+ if (options.lock === "forUpdate") {
147
+ query = query.forUpdate();
148
+ } else if (options.lock === "forShare") {
149
+ query = query.forShare();
150
+ }
151
+ const result = await query.first();
152
+ return result || null;
153
+ } catch (error) {
154
+ throw new BindingError(
155
+ `Failed to fetch ${this.getModelName(model)}: ${error.message}`,
156
+ error
157
+ );
158
+ }
159
+ }
160
+ getPrimaryKeyName(model) {
161
+ if (isKnexModel(model) && model.primaryKey) {
162
+ return model.primaryKey;
163
+ }
164
+ return "id";
165
+ }
166
+ isValidModel(model) {
167
+ return typeof model === "string" || isKnexModel(model);
168
+ }
169
+ transformValue(model, key, value) {
170
+ const primaryKey = this.getPrimaryKeyName(model);
171
+ if (key === primaryKey || key === "id") {
172
+ const num = parseInt(value, 10);
173
+ if (!isNaN(num) && num.toString() === value && Number.isSafeInteger(num)) {
174
+ return num;
175
+ }
176
+ }
177
+ if (isUUID(value)) {
178
+ return value;
179
+ }
180
+ return value;
181
+ }
182
+ supportsSoftDeletes(model) {
183
+ return isKnexModel(model) && !!model.softDeleteColumn;
184
+ }
185
+ getModelMetadata(model) {
186
+ return {
187
+ name: this.getModelName(model),
188
+ primaryKey: this.getPrimaryKeyName(model),
189
+ tableName: this.getTableName(model),
190
+ softDeletes: this.supportsSoftDeletes(model),
191
+ adapter: this.name
192
+ };
193
+ }
194
+ getTableName(model) {
195
+ return typeof model === "string" ? model : model.tableName;
196
+ }
197
+ getSoftDeleteColumn(model) {
198
+ if (isKnexModel(model) && model.softDeleteColumn) {
199
+ return model.softDeleteColumn;
200
+ }
201
+ return "deleted_at";
202
+ }
203
+ applyWhereConditions(query, where) {
204
+ Object.entries(where).forEach(([column, value]) => {
205
+ if (value === null) {
206
+ query = query.whereNull(column);
207
+ } else if (Array.isArray(value)) {
208
+ query = query.whereIn(column, value);
209
+ } else if (isOperatorCondition(value)) {
210
+ query = query.where(column, value.operator, value.value);
211
+ } else {
212
+ query = query.where(column, value);
213
+ }
214
+ });
215
+ return query;
216
+ }
217
+ };
218
+ function defineKnexModel(config) {
219
+ return {
220
+ tableName: config.tableName,
221
+ primaryKey: config.primaryKey || "id",
222
+ softDeleteColumn: config.softDeleteColumn
223
+ };
224
+ }
225
+ export {
226
+ KnexAdapter,
227
+ defineKnexModel
228
+ };
229
+ //# sourceMappingURL=KnexAdapter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/errors/index.ts","../../src/core/BaseAdapter.ts","../../src/core/types.ts","../../src/utils/validators.ts","../../src/adapters/KnexAdapter.ts"],"sourcesContent":["/**\n * Base error class for model binding errors\n */\nexport class BindingError extends Error {\n public readonly originalError?: Error;\n\n constructor(message: string, originalError?: Error) {\n super(message);\n this.name = 'BindingError';\n this.originalError = originalError;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n originalError: this.originalError?.message,\n };\n }\n}\n\n/**\n * Error thrown when a model is not found (404)\n */\nexport class ModelNotFoundError extends Error {\n public readonly statusCode = 404;\n public readonly paramName: string;\n public readonly paramValue: string;\n public readonly modelName: string;\n\n constructor(paramName: string, paramValue: string, modelName: string, customMessage?: string) {\n super(customMessage || `${modelName} not found with ${paramName} = ${paramValue}`);\n this.name = 'ModelNotFoundError';\n this.paramName = paramName;\n this.paramValue = paramValue;\n this.modelName = modelName;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n error: 'Not Found',\n message: this.message,\n statusCode: this.statusCode,\n param: this.paramName,\n value: this.paramValue,\n model: this.modelName,\n };\n }\n}\n\n/**\n * Error thrown when no adapter is configured\n */\nexport class AdapterNotSetError extends Error {\n constructor(\n message: string = 'No adapter set. Call ModelBinder.setAdapter() before using model binding.'\n ) {\n super(message);\n this.name = 'AdapterNotSetError';\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n };\n }\n}\n\n/**\n * Error thrown when a model is invalid for an adapter\n */\nexport class InvalidModelError extends Error {\n public readonly model: unknown;\n\n constructor(message: string, model: unknown) {\n super(message);\n this.name = 'InvalidModelError';\n this.model = model;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n model: typeof this.model === 'string' ? this.model : String(this.model),\n };\n }\n}\n\n/**\n * Error thrown when validation fails\n */\nexport class ValidationError extends Error {\n public readonly statusCode: number;\n public readonly details?: unknown;\n\n constructor(message: string, statusCode: number = 400, details?: unknown) {\n super(message);\n this.name = 'ValidationError';\n this.statusCode = statusCode;\n this.details = details;\n Error.captureStackTrace(this, this.constructor);\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n statusCode: this.statusCode,\n details: this.details,\n };\n }\n}\n","import { IORMAdapter, QueryOptions, ModelMetadata, QueryModifier } from './types';\nimport { InvalidModelError } from '../errors';\n\n/**\n * Abstract base class providing common adapter functionality.\n * Extend this class to implement ORM-specific adapters.\n *\n * @typeParam TModel - Model type accepted by this adapter\n * @typeParam TResult - Result type returned by queries\n * @typeParam TQueryBuilder - ORM-specific query builder type\n */\nexport abstract class BaseAdapter<\n TModel = unknown,\n TResult = unknown,\n TQueryBuilder = unknown,\n> implements IORMAdapter<TModel, TResult> {\n abstract readonly name: string;\n\n abstract findByKey(\n model: TModel,\n key: string,\n value: unknown,\n options?: QueryOptions\n ): Promise<TResult | null>;\n\n abstract getPrimaryKeyName(model: TModel): string;\n\n abstract isValidModel(model: unknown): model is TModel;\n\n transformValue(_model: TModel, _key: string, value: string): unknown {\n if (/^\\d+$/.test(value)) {\n const num = parseInt(value, 10);\n if (!isNaN(num) && Number.isSafeInteger(num)) {\n return num;\n }\n }\n return value;\n }\n\n supportsSoftDeletes(_model: TModel): boolean {\n return false;\n }\n\n getModelMetadata(model: TModel): ModelMetadata {\n return {\n name: this.getModelName(model),\n primaryKey: this.getPrimaryKeyName(model),\n softDeletes: this.supportsSoftDeletes(model),\n adapter: this.name,\n };\n }\n\n protected validateModel(model: unknown): asserts model is TModel {\n if (!this.isValidModel(model)) {\n throw new InvalidModelError(`Invalid model for ${this.name} adapter`, model);\n }\n }\n\n protected getModelName(model: TModel): string {\n if (typeof model === 'string') {\n return model;\n }\n if (model && typeof model === 'object') {\n const obj = model as Record<string, unknown>;\n if (typeof obj.name === 'string') return obj.name;\n if (typeof obj.modelName === 'string') return obj.modelName;\n if (typeof obj.tableName === 'string') return obj.tableName;\n }\n if (model && typeof model === 'function') {\n return (model as { name: string }).name || 'Unknown';\n }\n return 'Unknown';\n }\n\n protected applySoftDeleteFilter(\n queryBuilder: TQueryBuilder,\n _options?: QueryOptions\n ): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applyIncludes(\n queryBuilder: TQueryBuilder,\n _includes?: string[] | Record<string, unknown>\n ): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applySelect(queryBuilder: TQueryBuilder, _select?: string[]): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applyWhereConditions(\n queryBuilder: TQueryBuilder,\n _where?: Record<string, unknown>\n ): TQueryBuilder {\n return queryBuilder;\n }\n\n protected applyCustomQuery(\n queryBuilder: TQueryBuilder,\n queryFn?: QueryModifier<TQueryBuilder>\n ): TQueryBuilder {\n if (queryFn) {\n return queryFn(queryBuilder) as TQueryBuilder;\n }\n return queryBuilder;\n }\n}\n","import { Request, Response, NextFunction, RequestHandler } from 'express';\n\n/**\n * Generic query builder type - adapters can narrow this to their specific type\n */\nexport type QueryBuilder<T = unknown> = T;\n\n/**\n * Query modifier function type\n */\nexport type QueryModifier<T = unknown> = (queryBuilder: QueryBuilder<T>) => QueryBuilder<T>;\n\n/**\n * Base adapter interface that all ORM adapters must implement\n */\nexport interface IORMAdapter<TModel = unknown, TResult = unknown> {\n readonly name: string;\n\n findByKey(\n model: TModel,\n key: string,\n value: unknown,\n options?: QueryOptions\n ): Promise<TResult | null>;\n\n getPrimaryKeyName(model: TModel): string;\n\n isValidModel(model: unknown): model is TModel;\n\n transformValue(model: TModel, key: string, value: string): unknown;\n\n supportsSoftDeletes(model: TModel): boolean;\n\n getModelMetadata?(model: TModel): ModelMetadata;\n}\n\n/**\n * Query options for model lookups\n */\nexport interface QueryOptions {\n /**\n * Relations to eager load\n */\n include?: string[] | Record<string, unknown>;\n\n /**\n * Additional WHERE conditions\n */\n where?: Record<string, unknown>;\n\n /**\n * Custom query modifier - receives ORM-specific query builder\n */\n query?: QueryModifier;\n\n /**\n * Fields to select\n */\n select?: string[];\n\n /**\n * Include soft-deleted records\n */\n withTrashed?: boolean;\n\n /**\n * Only return soft-deleted records\n */\n onlyTrashed?: boolean;\n\n /**\n * Enable result caching\n */\n cache?: boolean | number;\n\n /**\n * Row locking for transactions\n */\n lock?: 'forUpdate' | 'forShare';\n}\n\n/**\n * Binding middleware options\n */\nexport interface BindOptions extends QueryOptions {\n /**\n * Field to search by (defaults to primary key)\n */\n key?: string;\n\n /**\n * Custom error when model not found\n */\n onNotFound?: Error | ((paramName: string, paramValue: string) => Error);\n\n /**\n * Transform parameter value before querying\n */\n transformValue?: (value: string) => unknown;\n\n /**\n * Property name for attaching model to request\n */\n as?: string;\n\n /**\n * Don't throw if model not found\n */\n optional?: boolean;\n\n /**\n * Custom 404 message\n */\n errorMessage?: string;\n\n /**\n * Validate loaded model\n */\n validate?: (model: unknown, req: Request) => void | Promise<void>;\n\n /**\n * Enable caching\n */\n cache?: boolean;\n\n /**\n * Cache TTL in milliseconds\n */\n cacheTTL?: number;\n}\n\n/**\n * Multi-model binding configuration\n */\nexport interface ModelBindingsConfig {\n [paramName: string]: {\n model: unknown;\n options?: BindOptions;\n };\n}\n\n/**\n * Binding operation context\n */\nexport interface BindingContext {\n req: Request;\n res: Response;\n paramName: string;\n paramValue: string;\n model: unknown;\n options: BindOptions;\n adapter: IORMAdapter;\n startTime: number;\n}\n\n/**\n * Binding operation result\n */\nexport interface BindingResult {\n success: boolean;\n model?: unknown;\n error?: Error;\n duration: number;\n fromCache?: boolean;\n}\n\n/**\n * Model metadata for debugging\n */\nexport interface ModelMetadata {\n name: string;\n primaryKey: string;\n tableName?: string;\n relations?: string[];\n softDeletes: boolean;\n adapter?: string;\n [key: string]: unknown;\n}\n\n/**\n * Cache entry structure\n */\nexport interface CacheEntry<T = unknown> {\n value: T;\n timestamp: number;\n ttl: number;\n}\n\n/**\n * Global configuration options\n */\nexport interface ModelBindingGlobalConfig {\n adapter?: IORMAdapter;\n cache?: {\n enabled: boolean;\n ttl: number;\n maxSize?: number;\n };\n debug?: boolean;\n logger?: (message: string, context?: unknown) => void;\n onError?: (error: Error, context: BindingContext) => void;\n}\n\n/**\n * Express request with bound models\n */\nexport interface TypedRequest<\n P = Record<string, string>,\n ResBody = unknown,\n ReqBody = unknown,\n ReqQuery = unknown,\n Models extends Record<string, unknown> = Record<string, unknown>,\n> extends Request<P, ResBody, ReqBody, ReqQuery> {\n [K: string]: unknown;\n models?: Models;\n}\n\n/**\n * Extract model type from binding config\n */\nexport type ExtractModelType<T> = T extends { model: infer M } ? M : never;\n\n/**\n * Typed request handler\n */\nexport type TypedRequestHandler<\n Models extends Record<string, unknown> = Record<string, unknown>,\n P = Record<string, string>,\n ResBody = unknown,\n ReqBody = unknown,\n ReqQuery = unknown,\n> = (\n req: TypedRequest<P, ResBody, ReqBody, ReqQuery, Models>,\n res: Response<ResBody>,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * Middleware function type\n */\nexport type MiddlewareFunction = RequestHandler;\n\n/**\n * Operator condition for advanced WHERE clauses\n */\nexport interface OperatorCondition {\n operator: string;\n value: unknown;\n}\n\n/**\n * Check if value is an operator condition\n */\nexport function isOperatorCondition(value: unknown): value is OperatorCondition {\n return typeof value === 'object' && value !== null && 'operator' in value && 'value' in value;\n}\n","/**\n * UUID regex pattern\n */\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * MongoDB ObjectId regex pattern (24 hex characters)\n */\nconst OBJECT_ID_REGEX = /^[0-9a-f]{24}$/i;\n\n/**\n * Check if a value is a valid UUID\n */\nexport function isUUID(value: string): boolean {\n return UUID_REGEX.test(value);\n}\n\n/**\n * Check if a value is a valid MongoDB ObjectId\n */\nexport function isObjectId(value: string): boolean {\n return OBJECT_ID_REGEX.test(value);\n}\n\n/**\n * Check if a value is a numeric string\n */\nexport function isNumeric(value: string): boolean {\n return /^-?\\d+$/.test(value);\n}\n\n/**\n * Check if a value is a valid positive integer\n */\nexport function isPositiveInteger(value: string): boolean {\n return /^\\d+$/.test(value) && parseInt(value, 10) > 0;\n}\n\n/**\n * Check if a value is a valid slug (lowercase alphanumeric with hyphens)\n */\nexport function isSlug(value: string): boolean {\n return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);\n}\n\n/**\n * Check if a value is a valid email\n */\nexport function isEmail(value: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value);\n}\n\n/**\n * Validate that a value is not empty\n */\nexport function isNotEmpty(value: unknown): boolean {\n if (value === null || value === undefined) {\n return false;\n }\n if (typeof value === 'string') {\n return value.trim().length > 0;\n }\n return true;\n}\n\n/**\n * Validate that a value is a non-empty string\n */\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/**\n * Validate that a value is a plain object\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Validate that a value is a function\n */\n// eslint-disable-next-line @typescript-eslint/ban-types\nexport function isFunction(value: unknown): value is Function {\n return typeof value === 'function';\n}\n\n/**\n * Validate route parameter name\n */\nexport function isValidParamName(value: string): boolean {\n return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value);\n}\n","import type { Knex } from 'knex';\nimport { BaseAdapter } from '../core/BaseAdapter';\nimport { QueryOptions, ModelMetadata, isOperatorCondition } from '../core/types';\nimport { BindingError } from '../errors';\nimport { isUUID } from '../utils/validators';\n\n/**\n * Model definition for Knex tables with soft delete support\n */\nexport interface KnexModel {\n tableName: string;\n primaryKey?: string;\n softDeleteColumn?: string;\n}\n\n/**\n * Union type for Knex model inputs\n */\nexport type KnexModelInput = string | KnexModel;\n\n/**\n * Type guard for KnexModel\n */\nfunction isKnexModel(value: unknown): value is KnexModel {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as KnexModel).tableName === 'string'\n );\n}\n\n/**\n * Adapter for Knex query builder supporting PostgreSQL, MySQL, SQLite, MSSQL, Oracle\n */\nexport class KnexAdapter extends BaseAdapter<\n KnexModelInput,\n Record<string, unknown>,\n Knex.QueryBuilder\n> {\n readonly name = 'knex';\n\n constructor(private knex: Knex) {\n super();\n }\n\n getKnex(): Knex {\n return this.knex;\n }\n\n async findByKey(\n model: KnexModelInput,\n key: string,\n value: unknown,\n options: QueryOptions = {}\n ): Promise<Record<string, unknown> | null> {\n this.validateModel(model);\n\n const tableName = this.getTableName(model);\n\n try {\n let query = this.knex(tableName);\n\n query = query.where(key, value as Knex.Value);\n\n if (this.supportsSoftDeletes(model) && !options.withTrashed && !options.onlyTrashed) {\n const softDeleteColumn = this.getSoftDeleteColumn(model);\n query = query.whereNull(softDeleteColumn);\n } else if (options.onlyTrashed && this.supportsSoftDeletes(model)) {\n const softDeleteColumn = this.getSoftDeleteColumn(model);\n query = query.whereNotNull(softDeleteColumn);\n }\n\n if (options.where) {\n query = this.applyWhereConditions(query, options.where);\n }\n\n if (options.select && options.select.length > 0) {\n query = query.select(options.select);\n } else {\n query = query.select('*');\n }\n\n if (options.query) {\n query = this.applyCustomQuery(\n query,\n options.query as (qb: Knex.QueryBuilder) => Knex.QueryBuilder\n );\n }\n\n if (options.lock === 'forUpdate') {\n query = query.forUpdate();\n } else if (options.lock === 'forShare') {\n query = query.forShare();\n }\n\n const result = await query.first();\n return (result as Record<string, unknown>) || null;\n } catch (error) {\n throw new BindingError(\n `Failed to fetch ${this.getModelName(model)}: ${(error as Error).message}`,\n error as Error\n );\n }\n }\n\n getPrimaryKeyName(model: KnexModelInput): string {\n if (isKnexModel(model) && model.primaryKey) {\n return model.primaryKey;\n }\n return 'id';\n }\n\n isValidModel(model: unknown): model is KnexModelInput {\n return typeof model === 'string' || isKnexModel(model);\n }\n\n transformValue(model: KnexModelInput, key: string, value: string): unknown {\n const primaryKey = this.getPrimaryKeyName(model);\n\n if (key === primaryKey || key === 'id') {\n const num = parseInt(value, 10);\n if (!isNaN(num) && num.toString() === value && Number.isSafeInteger(num)) {\n return num;\n }\n }\n\n if (isUUID(value)) {\n return value;\n }\n\n return value;\n }\n\n supportsSoftDeletes(model: KnexModelInput): boolean {\n return isKnexModel(model) && !!model.softDeleteColumn;\n }\n\n getModelMetadata(model: KnexModelInput): ModelMetadata {\n return {\n name: this.getModelName(model),\n primaryKey: this.getPrimaryKeyName(model),\n tableName: this.getTableName(model),\n softDeletes: this.supportsSoftDeletes(model),\n adapter: this.name,\n };\n }\n\n private getTableName(model: KnexModelInput): string {\n return typeof model === 'string' ? model : model.tableName;\n }\n\n private getSoftDeleteColumn(model: KnexModelInput): string {\n if (isKnexModel(model) && model.softDeleteColumn) {\n return model.softDeleteColumn;\n }\n return 'deleted_at';\n }\n\n protected applyWhereConditions(\n query: Knex.QueryBuilder,\n where: Record<string, unknown>\n ): Knex.QueryBuilder {\n Object.entries(where).forEach(([column, value]) => {\n if (value === null) {\n query = query.whereNull(column);\n } else if (Array.isArray(value)) {\n query = query.whereIn(column, value as Knex.Value[]);\n } else if (isOperatorCondition(value)) {\n query = query.where(column, value.operator, value.value as Knex.Value);\n } else {\n query = query.where(column, value as Knex.Value);\n }\n });\n return query;\n }\n}\n\n/**\n * Create a Knex model definition\n */\nexport function defineKnexModel(config: {\n tableName: string;\n primaryKey?: string;\n softDeleteColumn?: string;\n}): KnexModel {\n return {\n tableName: config.tableName,\n primaryKey: config.primaryKey || 'id',\n softDeleteColumn: config.softDeleteColumn,\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,30 @@
1
+ import { Model, Document, Schema, Query } from 'mongoose';
2
+ import { b as BaseAdapter, Q as QueryOptions, d as ModelMetadata } from '../BaseAdapter-BjvLQijd.mjs';
3
+ import 'express';
4
+
5
+ /**
6
+ * Mongoose model type with required schema property
7
+ */
8
+ interface MongooseModel extends Model<Document> {
9
+ schema: Schema;
10
+ modelName: string;
11
+ }
12
+ /**
13
+ * Mongoose query type
14
+ */
15
+ type MongooseQuery = Query<Document | null, Document>;
16
+ /**
17
+ * Adapter for MongoDB using Mongoose ODM
18
+ */
19
+ declare class MongooseAdapter extends BaseAdapter<MongooseModel, Document, MongooseQuery> {
20
+ readonly name = "mongoose";
21
+ findByKey(model: MongooseModel, key: string, value: unknown, options?: QueryOptions): Promise<Document | null>;
22
+ getPrimaryKeyName(_model: MongooseModel): string;
23
+ isValidModel(model: unknown): model is MongooseModel;
24
+ transformValue(model: MongooseModel, key: string, value: string): unknown;
25
+ supportsSoftDeletes(model: MongooseModel): boolean;
26
+ getModelMetadata(model: MongooseModel): ModelMetadata;
27
+ protected applyIncludes(query: MongooseQuery, includes: string[] | Record<string, unknown>): MongooseQuery;
28
+ }
29
+
30
+ export { MongooseAdapter };