@edium/halifax 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 (135) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +148 -0
  4. package/README_AUTH.md +172 -0
  5. package/README_AUTOCRUD.md +253 -0
  6. package/README_CACHE.md +164 -0
  7. package/README_HTTP_ADAPTERS.md +309 -0
  8. package/README_MULTITENANCY.md +162 -0
  9. package/README_QUERYBUILDER.md +219 -0
  10. package/README_REPO_ADAPTERS.md +266 -0
  11. package/dist/adapters/http/ExpressAdapter.d.ts +40 -0
  12. package/dist/adapters/http/ExpressAdapter.d.ts.map +1 -0
  13. package/dist/adapters/http/ExpressAdapter.js +109 -0
  14. package/dist/adapters/http/ExpressAdapter.js.map +1 -0
  15. package/dist/adapters/orm/prisma/PrismaAdapter.d.ts +143 -0
  16. package/dist/adapters/orm/prisma/PrismaAdapter.d.ts.map +1 -0
  17. package/dist/adapters/orm/prisma/PrismaAdapter.js +277 -0
  18. package/dist/adapters/orm/prisma/PrismaAdapter.js.map +1 -0
  19. package/dist/adapters/orm/prisma/createPrismaResources.d.ts +15 -0
  20. package/dist/adapters/orm/prisma/createPrismaResources.d.ts.map +1 -0
  21. package/dist/adapters/orm/prisma/createPrismaResources.js +51 -0
  22. package/dist/adapters/orm/prisma/createPrismaResources.js.map +1 -0
  23. package/dist/adapters/orm/prisma/helpers.d.ts +27 -0
  24. package/dist/adapters/orm/prisma/helpers.d.ts.map +1 -0
  25. package/dist/adapters/orm/prisma/helpers.js +45 -0
  26. package/dist/adapters/orm/prisma/helpers.js.map +1 -0
  27. package/dist/adapters/orm/prisma/index.d.ts +4 -0
  28. package/dist/adapters/orm/prisma/index.d.ts.map +1 -0
  29. package/dist/adapters/orm/prisma/index.js +3 -0
  30. package/dist/adapters/orm/prisma/index.js.map +1 -0
  31. package/dist/adapters/orm/prisma/types.d.ts +49 -0
  32. package/dist/adapters/orm/prisma/types.d.ts.map +1 -0
  33. package/dist/adapters/orm/prisma/types.js +2 -0
  34. package/dist/adapters/orm/prisma/types.js.map +1 -0
  35. package/dist/auth/AuthStrategy.d.ts +198 -0
  36. package/dist/auth/AuthStrategy.d.ts.map +1 -0
  37. package/dist/auth/AuthStrategy.js +227 -0
  38. package/dist/auth/AuthStrategy.js.map +1 -0
  39. package/dist/classes/QueryBuilder.d.ts +33 -0
  40. package/dist/classes/QueryBuilder.d.ts.map +1 -0
  41. package/dist/classes/QueryBuilder.js +262 -0
  42. package/dist/classes/QueryBuilder.js.map +1 -0
  43. package/dist/core/crudRouter.d.ts +36 -0
  44. package/dist/core/crudRouter.d.ts.map +1 -0
  45. package/dist/core/crudRouter.js +391 -0
  46. package/dist/core/crudRouter.js.map +1 -0
  47. package/dist/core/queryString.d.ts +13 -0
  48. package/dist/core/queryString.d.ts.map +1 -0
  49. package/dist/core/queryString.js +89 -0
  50. package/dist/core/queryString.js.map +1 -0
  51. package/dist/core/types.d.ts +293 -0
  52. package/dist/core/types.d.ts.map +1 -0
  53. package/dist/core/types.js +13 -0
  54. package/dist/core/types.js.map +1 -0
  55. package/dist/core/validation.d.ts +75 -0
  56. package/dist/core/validation.d.ts.map +1 -0
  57. package/dist/core/validation.js +206 -0
  58. package/dist/core/validation.js.map +1 -0
  59. package/dist/enums/SqlComparison.d.ts +18 -0
  60. package/dist/enums/SqlComparison.d.ts.map +1 -0
  61. package/dist/enums/SqlComparison.js +19 -0
  62. package/dist/enums/SqlComparison.js.map +1 -0
  63. package/dist/enums/SqlOperator.d.ts +6 -0
  64. package/dist/enums/SqlOperator.d.ts.map +1 -0
  65. package/dist/enums/SqlOperator.js +7 -0
  66. package/dist/enums/SqlOperator.js.map +1 -0
  67. package/dist/enums/SqlOrder.d.ts +6 -0
  68. package/dist/enums/SqlOrder.d.ts.map +1 -0
  69. package/dist/enums/SqlOrder.js +7 -0
  70. package/dist/enums/SqlOrder.js.map +1 -0
  71. package/dist/errors/AuthenticationError.d.ts +10 -0
  72. package/dist/errors/AuthenticationError.d.ts.map +1 -0
  73. package/dist/errors/AuthenticationError.js +13 -0
  74. package/dist/errors/AuthenticationError.js.map +1 -0
  75. package/dist/errors/AuthorizationError.d.ts +10 -0
  76. package/dist/errors/AuthorizationError.d.ts.map +1 -0
  77. package/dist/errors/AuthorizationError.js +13 -0
  78. package/dist/errors/AuthorizationError.js.map +1 -0
  79. package/dist/errors/BadRequestError.d.ts +10 -0
  80. package/dist/errors/BadRequestError.d.ts.map +1 -0
  81. package/dist/errors/BadRequestError.js +13 -0
  82. package/dist/errors/BadRequestError.js.map +1 -0
  83. package/dist/errors/HttpError.d.ts +12 -0
  84. package/dist/errors/HttpError.d.ts.map +1 -0
  85. package/dist/errors/HttpError.js +17 -0
  86. package/dist/errors/HttpError.js.map +1 -0
  87. package/dist/errors/MethodNotAllowedError.d.ts +10 -0
  88. package/dist/errors/MethodNotAllowedError.d.ts.map +1 -0
  89. package/dist/errors/MethodNotAllowedError.js +13 -0
  90. package/dist/errors/MethodNotAllowedError.js.map +1 -0
  91. package/dist/errors/NotAcceptableError.d.ts +10 -0
  92. package/dist/errors/NotAcceptableError.d.ts.map +1 -0
  93. package/dist/errors/NotAcceptableError.js +13 -0
  94. package/dist/errors/NotAcceptableError.js.map +1 -0
  95. package/dist/errors/NotFoundError.d.ts +10 -0
  96. package/dist/errors/NotFoundError.d.ts.map +1 -0
  97. package/dist/errors/NotFoundError.js +13 -0
  98. package/dist/errors/NotFoundError.js.map +1 -0
  99. package/dist/errors/NotImplementedError.d.ts +10 -0
  100. package/dist/errors/NotImplementedError.d.ts.map +1 -0
  101. package/dist/errors/NotImplementedError.js +13 -0
  102. package/dist/errors/NotImplementedError.js.map +1 -0
  103. package/dist/errors/ServerError.d.ts +10 -0
  104. package/dist/errors/ServerError.d.ts.map +1 -0
  105. package/dist/errors/ServerError.js +13 -0
  106. package/dist/errors/ServerError.js.map +1 -0
  107. package/dist/errors/UnprocessableEntityError.d.ts +10 -0
  108. package/dist/errors/UnprocessableEntityError.d.ts.map +1 -0
  109. package/dist/errors/UnprocessableEntityError.js +13 -0
  110. package/dist/errors/UnprocessableEntityError.js.map +1 -0
  111. package/dist/errors/UnsupportedMediaTypeError.d.ts +10 -0
  112. package/dist/errors/UnsupportedMediaTypeError.d.ts.map +1 -0
  113. package/dist/errors/UnsupportedMediaTypeError.js +13 -0
  114. package/dist/errors/UnsupportedMediaTypeError.js.map +1 -0
  115. package/dist/index.d.ts +27 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +27 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/interfaces/IParamQuery.d.ts +8 -0
  120. package/dist/interfaces/IParamQuery.d.ts.map +1 -0
  121. package/dist/interfaces/IParamQuery.js +2 -0
  122. package/dist/interfaces/IParamQuery.js.map +1 -0
  123. package/dist/interfaces/IQueryFilter.d.ts +18 -0
  124. package/dist/interfaces/IQueryFilter.d.ts.map +1 -0
  125. package/dist/interfaces/IQueryFilter.js +2 -0
  126. package/dist/interfaces/IQueryFilter.js.map +1 -0
  127. package/dist/interfaces/IQueryOptions.d.ts +20 -0
  128. package/dist/interfaces/IQueryOptions.d.ts.map +1 -0
  129. package/dist/interfaces/IQueryOptions.js +2 -0
  130. package/dist/interfaces/IQueryOptions.js.map +1 -0
  131. package/dist/interfaces/ISort.d.ts +9 -0
  132. package/dist/interfaces/ISort.d.ts.map +1 -0
  133. package/dist/interfaces/ISort.js +2 -0
  134. package/dist/interfaces/ISort.js.map +1 -0
  135. package/package.json +169 -0
@@ -0,0 +1,293 @@
1
+ import type { IQueryOptions } from '../interfaces/IQueryOptions.js';
2
+ /** HTTP methods supported by Halifax routes. `'*'` matches any method (used for 405 fallbacks). */
3
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | '*';
4
+ /** Framework-agnostic representation of an incoming HTTP request. */
5
+ export interface HttpRequest<TRaw = unknown> {
6
+ method: string;
7
+ params: Record<string, string>;
8
+ query: Record<string, unknown>;
9
+ body: unknown;
10
+ headers: Record<string, string | string[] | undefined>;
11
+ /** The underlying raw request object from the HTTP framework (e.g. Express `Request`). */
12
+ raw: TRaw;
13
+ }
14
+ /** Framework-agnostic representation of an outgoing HTTP response. */
15
+ export interface HttpResponse<TRaw = unknown> {
16
+ /**
17
+ * Set the HTTP status code. Returns `this` for chaining.
18
+ * @param code - HTTP status code to send (e.g. `200`, `404`).
19
+ * @returns This response object for method chaining.
20
+ */
21
+ status(code: number): HttpResponse<TRaw>;
22
+ /**
23
+ * Serialize `payload` as JSON and send it as the response body.
24
+ * @param payload - Value to serialize and send.
25
+ */
26
+ json(payload: unknown): void | Promise<void>;
27
+ /**
28
+ * Send a raw response body.
29
+ * @param payload - Raw body to send.
30
+ */
31
+ send?(payload?: unknown): void | Promise<void>;
32
+ /**
33
+ * Set a response header.
34
+ * @param name - Header name (e.g. `'Content-Type'`).
35
+ * @param value - Header value.
36
+ */
37
+ setHeader?(name: string, value: string): void;
38
+ /** The underlying raw response object from the HTTP framework (e.g. Express `Response`). */
39
+ raw: TRaw;
40
+ }
41
+ /** A route handler function compatible with Halifax's framework-agnostic request/response types. */
42
+ export type HttpRouteHandler = (req: HttpRequest, res: HttpResponse) => Promise<void> | void;
43
+ /** Minimal interface an HTTP server adapter must implement for Halifax to register routes. */
44
+ export interface HttpServer {
45
+ /**
46
+ * Register a route handler for the given method and path.
47
+ * @param method - HTTP method (or `'*'` for a catch-all fallback).
48
+ * @param path - Route path pattern (e.g. `'/users/:id'`).
49
+ * @param handler - Async handler function to invoke on matching requests.
50
+ */
51
+ registerRoute(method: HttpMethod, path: string, handler: HttpRouteHandler): void;
52
+ /**
53
+ * Start listening on the given port and optional host.
54
+ * @param port - TCP port number to bind to.
55
+ * @param host - Hostname or IP address to bind to (defaults to all interfaces).
56
+ */
57
+ start(port: number, host?: string): Promise<void> | void;
58
+ }
59
+ /** Flags indicating which optional repository operations the adapter supports. */
60
+ export interface RepositoryCapabilities {
61
+ /** True when the adapter can execute raw SQL via the query builder. */
62
+ supportsNativeSql: boolean;
63
+ /** True when the adapter supports `?include=` for eager-loading relations. */
64
+ supportsIncludes: boolean;
65
+ /** True when the adapter can participate in database transactions. */
66
+ supportsTransactions: boolean;
67
+ /** True when `createMany` returns the created records rather than an empty array. */
68
+ supportsCreateManyReturn: boolean;
69
+ /** True when the adapter accepts a NoSQL-style query AST (non-SQL ORMs). */
70
+ supportsNoSqlQueryAst: boolean;
71
+ }
72
+ /** Options for paginated, filtered, and sorted list queries. */
73
+ export interface ListOptions {
74
+ /** Columns to return. Returns all columns when omitted. */
75
+ fields?: string[] | undefined;
76
+ /** Key–value pairs applied as equality filters (or `{ in: [...] }` for multi-value). */
77
+ where?: Record<string, unknown>;
78
+ /** Maximum number of records to return. */
79
+ limit?: number | undefined;
80
+ /** Number of records to skip for pagination. */
81
+ offset?: number | undefined;
82
+ /** Sort expressions. Each entry specifies a field name and direction. */
83
+ orderBy?: Array<{
84
+ field: string;
85
+ direction: 'asc' | 'desc';
86
+ }> | undefined;
87
+ /** Relation names to eager-load. */
88
+ include?: string[] | undefined;
89
+ }
90
+ /** Paginated result envelope returned by `getMany`. */
91
+ export interface ListResult<TRecord> {
92
+ /** Total number of matching records (before pagination). */
93
+ count: number;
94
+ /** Records for the current page. */
95
+ results: TRecord[];
96
+ }
97
+ /** Result envelope returned by `deleteMany`. */
98
+ export interface DeleteManyResult {
99
+ /** IDs (or records) of the deleted rows. */
100
+ deleted: unknown[];
101
+ }
102
+ /** Result envelope returned by `updateMany`. */
103
+ export interface UpdateManyResult<TRecord> {
104
+ /** IDs of the updated rows. */
105
+ updated: unknown[];
106
+ /** Updated records, when the adapter supports returning them. */
107
+ results?: TRecord[];
108
+ }
109
+ /** Result envelope returned by `executeQueryBuilder`. */
110
+ export interface NativeQueryResult<TRecord> {
111
+ /** Total number of matching records (before pagination). */
112
+ count?: number;
113
+ /** Records for the current page. */
114
+ results: TRecord[];
115
+ }
116
+ /** Options passed to `createOne` / `createMany` for idempotent writes. */
117
+ export interface CreateOptions {
118
+ /** An idempotency key — the adapter may de-duplicate requests with the same key. */
119
+ idempotencyKey?: string;
120
+ }
121
+ /** Core data-access contract that every Halifax repository adapter must satisfy. */
122
+ export interface Repository<TRecord = unknown, TCreate = Partial<TRecord>, TUpdate = Partial<TRecord>> {
123
+ /** Optional capability flags used by Halifax to decide which routes to activate. */
124
+ readonly capabilities?: Partial<RepositoryCapabilities>;
125
+ /**
126
+ * Fetch a single record by its primary key.
127
+ * @param id - Primary key value (integer or UUID string).
128
+ * @param options - Optional field projection and relation includes.
129
+ * @returns The matching record, or `null` when not found.
130
+ */
131
+ getOne(id: string | number, options?: Pick<ListOptions, 'fields' | 'include'>): Promise<TRecord | null>;
132
+ /**
133
+ * Fetch a paginated, filtered list of records.
134
+ * @param options - Pagination, filtering, sorting, and projection options.
135
+ * @returns A count-and-results envelope for the current page.
136
+ */
137
+ getMany(options?: ListOptions): Promise<ListResult<TRecord>>;
138
+ /**
139
+ * Insert a single record and return it.
140
+ * @param data - Record fields to insert.
141
+ * @param options - Optional idempotency key.
142
+ * @returns The newly created record.
143
+ */
144
+ createOne(data: TCreate, options?: CreateOptions): Promise<TRecord>;
145
+ /**
146
+ * Insert multiple records. Returns created records when the adapter supports it.
147
+ * @param data - Array of record field objects to insert.
148
+ * @param options - Optional idempotency key.
149
+ * @returns The created records, or an empty array when the adapter uses bulk insert.
150
+ */
151
+ createMany(data: TCreate[], options?: CreateOptions): Promise<TRecord[]>;
152
+ /**
153
+ * Update a single record by its primary key.
154
+ * @param id - Primary key of the record to update.
155
+ * @param data - Fields to update.
156
+ * @returns The updated record, or `null` when not found.
157
+ */
158
+ updateOne(id: string | number, data: TUpdate): Promise<TRecord | null>;
159
+ /**
160
+ * Update multiple records matching the query.
161
+ * @param query - Query AST describing which rows to update.
162
+ * @param data - Fields to apply to all matching rows.
163
+ * @returns IDs of the updated rows (and optionally the updated records).
164
+ */
165
+ updateMany?(query: IQueryOptions, data: TUpdate): Promise<UpdateManyResult<TRecord>>;
166
+ /**
167
+ * Insert or update a single record by its primary key.
168
+ * @param id - Primary key to match for the update, or use for the insert.
169
+ * @param data - Fields to create or update.
170
+ * @returns The created or updated record.
171
+ */
172
+ upsertOne?(id: string | number, data: TCreate & TUpdate): Promise<TRecord>;
173
+ /**
174
+ * Delete a single record by its primary key.
175
+ * @param id - Primary key of the record to delete.
176
+ * @returns `true` when deleted, `false` when not found.
177
+ */
178
+ deleteOne(id: string | number): Promise<boolean>;
179
+ /**
180
+ * Delete multiple records matching the query.
181
+ * @param query - Query AST describing which rows to delete.
182
+ * @returns IDs of the deleted rows.
183
+ */
184
+ deleteMany?(query: IQueryOptions): Promise<DeleteManyResult>;
185
+ /**
186
+ * Execute a raw query-builder AST against the underlying data source.
187
+ * @param query - Full query AST including table name, filters, pagination, and sort.
188
+ * @returns A count-and-results envelope for the matching rows.
189
+ */
190
+ executeQueryBuilder?(query: IQueryOptions): Promise<NativeQueryResult<TRecord>>;
191
+ }
192
+ /** All CRUD action identifiers used for permissions and audit. */
193
+ export type CrudAction = 'create' | 'readOne' | 'readMany' | 'readManyWithQueryBuilder' | 'updateOne' | 'updateMany' | 'upsertOne' | 'deleteOne' | 'deleteMany';
194
+ /** Per-action toggles controlling which CRUD endpoints are registered for a resource. */
195
+ export interface CrudPermissions {
196
+ allowCreate?: boolean;
197
+ allowReadOne?: boolean;
198
+ allowReadMany?: boolean;
199
+ allowReadManyWithQueryBuilder?: boolean;
200
+ allowUpdateOne?: boolean;
201
+ allowUpdateMany?: boolean;
202
+ allowUpsertOne?: boolean;
203
+ allowDeleteOne?: boolean;
204
+ allowDeleteMany?: boolean;
205
+ }
206
+ /** Minimal shape of a Prisma DMMF field — structurally compatible with `Prisma.DMMF.Field`. */
207
+ export interface ModelField {
208
+ /** Column / property name. */
209
+ name: string;
210
+ /** Prisma field kind: `'scalar'`, `'object'` (relation), `'enum'`, or `'unsupported'`. */
211
+ kind: string;
212
+ /** True when this field is the model's primary key. */
213
+ isId: boolean;
214
+ /** True for fields that are auto-managed by Prisma (e.g. relation FKs, read-only scalars). */
215
+ isReadOnly: boolean;
216
+ /** True when Prisma provides a default value (e.g. `@default(autoincrement())`). */
217
+ hasDefault: boolean;
218
+ }
219
+ /** Minimal shape of a Prisma DMMF model — structurally compatible with `Prisma.DMMF.Model`. */
220
+ export interface ModelSchema {
221
+ /** Prisma model name (PascalCase). */
222
+ name?: string;
223
+ /** Underlying database table name, or `null` to use the model name. */
224
+ dbName?: string | null;
225
+ fields: ModelField[];
226
+ }
227
+ /** Per-model overrides for {@link createPrismaResources}. */
228
+ export interface ModelResourceOptions {
229
+ /** When true, this model is skipped entirely. */
230
+ exclude?: boolean;
231
+ /** Override the URL prefix (default: auto-derived kebab-plural of the model name). */
232
+ routePrefix?: string;
233
+ /** Override the database table name. */
234
+ tableName?: string;
235
+ /** Override the default CRUD permissions for this model. */
236
+ permissions?: CrudPermissions;
237
+ /** Required permission strings per action for fine-grained access control. */
238
+ requiredPermissions?: Partial<Record<CrudAction, string[]>>;
239
+ /** Default page size when the caller omits `?limit=`. */
240
+ defaultLimit?: number;
241
+ /** Hard cap on page size. Requests above this are silently capped. */
242
+ maxLimit?: number;
243
+ /** Maximum nesting depth for WHERE clause children (default: 3). */
244
+ maxFilterDepth?: number;
245
+ }
246
+ /** Describes a single column exposed through the Halifax API. */
247
+ export interface FieldDefinition {
248
+ /** Column / property name. */
249
+ name: string;
250
+ /** When `false`, the field cannot be used in `?field=` filters. */
251
+ filterable?: boolean;
252
+ /** When `false`, the field cannot be used in `?order=` sorts. */
253
+ sortable?: boolean;
254
+ /** When `false`, the field is excluded from `?fields=` projections. */
255
+ selectable?: boolean;
256
+ /** When `false`, the field is stripped from POST/PATCH request bodies. */
257
+ writable?: boolean;
258
+ }
259
+ /** Describes a relation that callers may eagerly load via `?include=`. */
260
+ export interface RelationDefinition {
261
+ /** Relation name as defined on the Prisma model. */
262
+ name: string;
263
+ /** When `false`, this relation cannot be requested via `?include=`. */
264
+ includable?: boolean;
265
+ }
266
+ /** Full definition of a Halifax resource: its repository, field schema, routing, and permissions. */
267
+ export interface ResourceDefinition<TRecord = unknown, TCreate = Partial<TRecord>, TUpdate = Partial<TRecord>> {
268
+ /** Human-readable resource name (usually the Prisma model name). */
269
+ name: string;
270
+ /** URL path segment (e.g. `'users'`, `'blog-posts'`). */
271
+ routePrefix: string;
272
+ /** Database table name used by the query builder. */
273
+ tableName?: string;
274
+ /** Scalar field definitions — controls filtering, sorting, selection, and write access. */
275
+ fields: FieldDefinition[];
276
+ /** Relation definitions — controls `?include=` access. */
277
+ relations?: RelationDefinition[];
278
+ /** CRUD operation toggles. Defaults to {@link defaultCrudPermissions}. */
279
+ permissions?: CrudPermissions;
280
+ /** The data adapter that handles reads and writes for this resource. */
281
+ repository: Repository<TRecord, TCreate, TUpdate>;
282
+ /** Required permission strings per action (checked by the auth strategy). */
283
+ requiredPermissions?: Partial<Record<CrudAction, string[]>>;
284
+ /** Default page size when the caller omits `?limit=`. No limit applied when `undefined`. */
285
+ defaultLimit?: number;
286
+ /** Hard cap on page size. Requests over this are silently capped. No cap when `undefined`. */
287
+ maxLimit?: number;
288
+ /** Maximum nesting depth for WHERE clause children. Defaults to 3. */
289
+ maxFilterDepth?: number;
290
+ }
291
+ /** Default permissions applied to every resource — all CRUD operations enabled. */
292
+ export declare const defaultCrudPermissions: Required<CrudPermissions>;
293
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** Default permissions applied to every resource — all CRUD operations enabled. */
2
+ export const defaultCrudPermissions = {
3
+ allowCreate: true,
4
+ allowReadOne: true,
5
+ allowReadMany: true,
6
+ allowReadManyWithQueryBuilder: true,
7
+ allowUpdateOne: true,
8
+ allowUpdateMany: true,
9
+ allowUpsertOne: true,
10
+ allowDeleteOne: true,
11
+ allowDeleteMany: true
12
+ };
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AA+UA,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAA8B;IAC/D,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,IAAI;IAClB,aAAa,EAAE,IAAI;IACnB,6BAA6B,EAAE,IAAI;IACnC,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,IAAI;IACpB,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,IAAI;CACtB,CAAA"}
@@ -0,0 +1,75 @@
1
+ import type { IQueryFilter } from '../interfaces/IQueryFilter.js';
2
+ import type { IQueryOptions } from '../interfaces/IQueryOptions.js';
3
+ import type { ResourceDefinition } from '../core/types.js';
4
+ /**
5
+ * Returns `true` when `value` is a safe 32-bit integer >= `min`.
6
+ * @param value - Raw value to test (string or number).
7
+ * @param min - Inclusive lower bound (default: `1`).
8
+ * @returns `true` when value is a valid integer within the 32-bit range and >= min.
9
+ */
10
+ export declare function isValidInt32(value: string | number | null, min?: number): boolean;
11
+ /**
12
+ * Returns `true` when `value` is a valid RFC 4122 UUID.
13
+ * @param value - String to test.
14
+ * @returns `true` when `value` matches the UUID format.
15
+ */
16
+ export declare function isValidUuid(value: string): boolean;
17
+ /**
18
+ * Asserts that `value` is a valid integer ID or UUID string.
19
+ * Throws {@link BadRequestError} when validation fails.
20
+ * @param value - Raw `:id` route parameter to validate.
21
+ */
22
+ export declare function validateId(value: string | number | undefined): asserts value is string | number;
23
+ /**
24
+ * Returns the list of field names defined on a resource.
25
+ * @param resource - The resource definition to extract field names from.
26
+ * @returns Array of field name strings.
27
+ */
28
+ export declare function getFieldNames(resource: ResourceDefinition): string[];
29
+ /**
30
+ * Throws {@link UnprocessableEntityError} when any of `fields` are not defined on the resource.
31
+ * @param resource - The resource definition to validate against.
32
+ * @param fields - Field names to validate (empty array is a no-op).
33
+ */
34
+ export declare function validateFields(resource: ResourceDefinition, fields?: string[]): void;
35
+ /**
36
+ * Throws {@link UnprocessableEntityError} when any of `fields` have `selectable: false`.
37
+ * @param resource - The resource definition to validate against.
38
+ * @param fields - Field names to check for selectability.
39
+ */
40
+ export declare function validateSelectableFields(resource: ResourceDefinition, fields: string[]): void;
41
+ /**
42
+ * Throws {@link UnprocessableEntityError} when any of `fields` have `sortable: false`.
43
+ * @param resource - The resource definition to validate against.
44
+ * @param fields - Field names to check for sortability.
45
+ */
46
+ export declare function validateSortableFields(resource: ResourceDefinition, fields: string[]): void;
47
+ /**
48
+ * Throws {@link UnprocessableEntityError} when any of `includes` reference non-includable relations.
49
+ * @param resource - The resource definition whose relations are checked.
50
+ * @param includes - Relation names to validate.
51
+ */
52
+ export declare function validateIncludes(resource: ResourceDefinition, includes?: string[]): void;
53
+ /**
54
+ * Validates that every query-string key is either a reserved parameter or a filterable field.
55
+ * Throws {@link UnprocessableEntityError} for non-filterable fields or unknown properties.
56
+ * @param resource - The resource definition whose fields are checked.
57
+ * @param query - The raw query-string object from the HTTP request.
58
+ */
59
+ export declare function validateQueryString(resource: ResourceDefinition, query: Record<string, unknown>): void;
60
+ /**
61
+ * Validates a WHERE clause filter tree against the resource's field definitions.
62
+ * Throws {@link UnprocessableEntityError} on invalid fields, comparisons, operators, or exceeded nesting depth.
63
+ * @param resource - The resource definition whose fields and depth limit are used.
64
+ * @param where - Array of filter conditions to validate.
65
+ * @param depth - Current recursion depth (used for nesting-limit enforcement; start at `0`).
66
+ */
67
+ export declare function validateWhere(resource: ResourceDefinition, where?: IQueryFilter[], depth?: number): void;
68
+ /**
69
+ * Validates a full query-builder AST (fields, orderBy, where) against the resource's schema.
70
+ * Throws {@link UnprocessableEntityError} on any invalid input.
71
+ * @param resource - The resource definition used for field and relation validation.
72
+ * @param query - The query AST to validate.
73
+ */
74
+ export declare function validateAdvancedQuery(resource: ResourceDefinition, query: IQueryOptions): void;
75
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAIzD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,GAAG,SAAI,GAAG,OAAO,CAM5E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,GAAG,MAAM,CAS/F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAIpE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,GAAE,MAAM,EAAO,GAAG,IAAI,CASxF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ7F;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ3F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,IAAI,CAkB5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CA0BN;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,GAAE,YAAY,EAAO,EAC1B,KAAK,SAAI,GACR,IAAI,CAsCN;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAmB9F"}
@@ -0,0 +1,206 @@
1
+ import { validate as uuidValidate } from 'uuid';
2
+ import { SqlComparison } from '../enums/SqlComparison.js';
3
+ import { SqlOperator } from '../enums/SqlOperator.js';
4
+ import { SqlOrder } from '../enums/SqlOrder.js';
5
+ import { BadRequestError } from '../errors/BadRequestError.js';
6
+ import { UnprocessableEntityError } from '../errors/UnprocessableEntityError.js';
7
+ const reservedQueryStringProperties = ['fields', 'limit', 'offset', 'order', 'include'];
8
+ /**
9
+ * Returns `true` when `value` is a safe 32-bit integer >= `min`.
10
+ * @param value - Raw value to test (string or number).
11
+ * @param min - Inclusive lower bound (default: `1`).
12
+ * @returns `true` when value is a valid integer within the 32-bit range and >= min.
13
+ */
14
+ export function isValidInt32(value, min = 1) {
15
+ if (value === null || value === undefined) {
16
+ return false;
17
+ }
18
+ const normalized = typeof value === 'string' ? Number(value.trim()) : value;
19
+ return Number.isSafeInteger(normalized) && normalized >= min && normalized <= 2147483647;
20
+ }
21
+ /**
22
+ * Returns `true` when `value` is a valid RFC 4122 UUID.
23
+ * @param value - String to test.
24
+ * @returns `true` when `value` matches the UUID format.
25
+ */
26
+ export function isValidUuid(value) {
27
+ return uuidValidate(value);
28
+ }
29
+ /**
30
+ * Asserts that `value` is a valid integer ID or UUID string.
31
+ * Throws {@link BadRequestError} when validation fails.
32
+ * @param value - Raw `:id` route parameter to validate.
33
+ */
34
+ export function validateId(value) {
35
+ if (value === undefined) {
36
+ throw new BadRequestError('Id parameter must be an integer (1–2147483647) or a valid UUID.');
37
+ }
38
+ const isInt = isValidInt32(value);
39
+ const isUuid = typeof value === 'string' && isValidUuid(value);
40
+ if (!isInt && !isUuid) {
41
+ throw new BadRequestError('Id parameter must be an integer (1–2147483647) or a valid UUID.');
42
+ }
43
+ }
44
+ /**
45
+ * Returns the list of field names defined on a resource.
46
+ * @param resource - The resource definition to extract field names from.
47
+ * @returns Array of field name strings.
48
+ */
49
+ export function getFieldNames(resource) {
50
+ return resource.fields.map((field) => {
51
+ return field.name;
52
+ });
53
+ }
54
+ /**
55
+ * Throws {@link UnprocessableEntityError} when any of `fields` are not defined on the resource.
56
+ * @param resource - The resource definition to validate against.
57
+ * @param fields - Field names to validate (empty array is a no-op).
58
+ */
59
+ export function validateFields(resource, fields = []) {
60
+ const validFields = new Set(getFieldNames(resource));
61
+ const invalidFields = fields.filter((field) => {
62
+ return !validFields.has(field);
63
+ });
64
+ if (invalidFields.length) {
65
+ throw new UnprocessableEntityError(`Invalid field(s): ${invalidFields.join(', ')}.`);
66
+ }
67
+ }
68
+ /**
69
+ * Throws {@link UnprocessableEntityError} when any of `fields` have `selectable: false`.
70
+ * @param resource - The resource definition to validate against.
71
+ * @param fields - Field names to check for selectability.
72
+ */
73
+ export function validateSelectableFields(resource, fields) {
74
+ const nonSelectable = fields.filter((name) => {
75
+ const field = resource.fields.find((f) => f.name === name);
76
+ return field?.selectable === false;
77
+ });
78
+ if (nonSelectable.length) {
79
+ throw new UnprocessableEntityError(`Field(s) not selectable: ${nonSelectable.join(', ')}.`);
80
+ }
81
+ }
82
+ /**
83
+ * Throws {@link UnprocessableEntityError} when any of `fields` have `sortable: false`.
84
+ * @param resource - The resource definition to validate against.
85
+ * @param fields - Field names to check for sortability.
86
+ */
87
+ export function validateSortableFields(resource, fields) {
88
+ const nonSortable = fields.filter((name) => {
89
+ const field = resource.fields.find((f) => f.name === name);
90
+ return field?.sortable === false;
91
+ });
92
+ if (nonSortable.length) {
93
+ throw new UnprocessableEntityError(`Field(s) not sortable: ${nonSortable.join(', ')}.`);
94
+ }
95
+ }
96
+ /**
97
+ * Throws {@link UnprocessableEntityError} when any of `includes` reference non-includable relations.
98
+ * @param resource - The resource definition whose relations are checked.
99
+ * @param includes - Relation names to validate.
100
+ */
101
+ export function validateIncludes(resource, includes = []) {
102
+ const validIncludes = new Set((resource.relations ?? [])
103
+ .filter((relation) => {
104
+ return relation.includable !== false;
105
+ })
106
+ .map((relation) => {
107
+ return relation.name;
108
+ }));
109
+ const invalidIncludes = includes.filter((include) => {
110
+ return !validIncludes.has(include);
111
+ });
112
+ if (invalidIncludes.length) {
113
+ throw new UnprocessableEntityError(`Invalid include(s): ${invalidIncludes.join(', ')}.`);
114
+ }
115
+ }
116
+ /**
117
+ * Validates that every query-string key is either a reserved parameter or a filterable field.
118
+ * Throws {@link UnprocessableEntityError} for non-filterable fields or unknown properties.
119
+ * @param resource - The resource definition whose fields are checked.
120
+ * @param query - The raw query-string object from the HTTP request.
121
+ */
122
+ export function validateQueryString(resource, query) {
123
+ const filterableFieldNames = resource.fields
124
+ .filter((f) => f.filterable !== false)
125
+ .map((f) => f.name);
126
+ const allFieldNames = new Set(getFieldNames(resource));
127
+ const validProps = new Set([...filterableFieldNames, ...reservedQueryStringProperties]);
128
+ const nonFilterable = [];
129
+ const unknown = [];
130
+ for (const prop of Object.keys(query)) {
131
+ if (!validProps.has(prop)) {
132
+ if (allFieldNames.has(prop)) {
133
+ nonFilterable.push(prop);
134
+ }
135
+ else {
136
+ unknown.push(prop);
137
+ }
138
+ }
139
+ }
140
+ if (nonFilterable.length) {
141
+ throw new UnprocessableEntityError(`Field(s) not filterable: ${nonFilterable.join(', ')}.`);
142
+ }
143
+ if (unknown.length) {
144
+ throw new UnprocessableEntityError(`Invalid query-string properties: ${unknown.join(', ')}.`);
145
+ }
146
+ }
147
+ /**
148
+ * Validates a WHERE clause filter tree against the resource's field definitions.
149
+ * Throws {@link UnprocessableEntityError} on invalid fields, comparisons, operators, or exceeded nesting depth.
150
+ * @param resource - The resource definition whose fields and depth limit are used.
151
+ * @param where - Array of filter conditions to validate.
152
+ * @param depth - Current recursion depth (used for nesting-limit enforcement; start at `0`).
153
+ */
154
+ export function validateWhere(resource, where = [], depth = 0) {
155
+ const maxDepth = resource.maxFilterDepth ?? 3;
156
+ if (depth > maxDepth) {
157
+ throw new UnprocessableEntityError(`Filter nesting exceeds the maximum allowed depth of ${maxDepth}.`);
158
+ }
159
+ const validComparisons = new Set(Object.values(SqlComparison));
160
+ const validOperators = new Set(Object.values(SqlOperator));
161
+ where.forEach((filter, index) => {
162
+ if (!filter.field) {
163
+ throw new UnprocessableEntityError('WHERE clause must have a field.');
164
+ }
165
+ validateFields(resource, [filter.field]);
166
+ const comparison = filter.comparison?.toUpperCase();
167
+ if (!comparison || !validComparisons.has(comparison)) {
168
+ throw new UnprocessableEntityError(`Invalid comparison: '${filter.comparison}'.`);
169
+ }
170
+ const operator = filter.operator?.toUpperCase();
171
+ if (operator && !validOperators.has(operator)) {
172
+ throw new UnprocessableEntityError(`Invalid operator: '${filter.operator}'.`);
173
+ }
174
+ if (index + 1 < where.length && !operator) {
175
+ throw new UnprocessableEntityError('Operator is required for all but the last item in the WHERE clause.');
176
+ }
177
+ if (filter.children?.length) {
178
+ validateWhere(resource, filter.children, depth + 1);
179
+ }
180
+ });
181
+ }
182
+ /**
183
+ * Validates a full query-builder AST (fields, orderBy, where) against the resource's schema.
184
+ * Throws {@link UnprocessableEntityError} on any invalid input.
185
+ * @param resource - The resource definition used for field and relation validation.
186
+ * @param query - The query AST to validate.
187
+ */
188
+ export function validateAdvancedQuery(resource, query) {
189
+ if (query.fields) {
190
+ validateFields(resource, query.fields);
191
+ validateSelectableFields(resource, query.fields);
192
+ }
193
+ if (query.orderBy) {
194
+ const validOrders = new Set(Object.values(SqlOrder));
195
+ const sortFields = query.orderBy.map((s) => s.field);
196
+ validateFields(resource, sortFields);
197
+ validateSortableFields(resource, sortFields);
198
+ query.orderBy.forEach((sort) => {
199
+ if (!validOrders.has(sort.order.toUpperCase())) {
200
+ throw new UnprocessableEntityError(`Invalid sort order: '${sort.order}'.`);
201
+ }
202
+ });
203
+ }
204
+ validateWhere(resource, query.where);
205
+ }
206
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /** SQL comparison operators used in query-builder WHERE clauses. */
2
+ export declare enum SqlComparison {
3
+ Between = "BETWEEN",
4
+ Equal = "=",
5
+ GreaterThan = ">",
6
+ GreaterThanOrEqual = ">=",
7
+ In = "IN",
8
+ IsNotNull = "IS NOT NULL",
9
+ IsNull = "IS NULL",
10
+ LessThan = "<",
11
+ LessThanOrEqual = "<=",
12
+ Like = "LIKE",
13
+ NotBetween = "NOT BETWEEN",
14
+ NotEqual = "<>",
15
+ NotIn = "NOT IN",
16
+ NotLike = "NOT LIKE"
17
+ }
18
+ //# sourceMappingURL=SqlComparison.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlComparison.d.ts","sourceRoot":"","sources":["../../src/enums/SqlComparison.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,oBAAY,aAAa;IACvB,OAAO,YAAY;IACnB,KAAK,MAAM;IACX,WAAW,MAAM;IACjB,kBAAkB,OAAO;IACzB,EAAE,OAAO;IACT,SAAS,gBAAgB;IACzB,MAAM,YAAY;IAClB,QAAQ,MAAM;IACd,eAAe,OAAO;IACtB,IAAI,SAAS;IACb,UAAU,gBAAgB;IAC1B,QAAQ,OAAO;IACf,KAAK,WAAW;IAChB,OAAO,aAAa;CACrB"}