@radio-garden/ditojs-server 2.85.2-0.5067ad799

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 (137) hide show
  1. package/README.md +6 -0
  2. package/package.json +95 -0
  3. package/src/app/Application.js +1186 -0
  4. package/src/app/Validator.js +405 -0
  5. package/src/app/index.js +2 -0
  6. package/src/cli/console.js +152 -0
  7. package/src/cli/db/createMigration.js +241 -0
  8. package/src/cli/db/index.js +7 -0
  9. package/src/cli/db/listAssetConfig.js +10 -0
  10. package/src/cli/db/migrate.js +12 -0
  11. package/src/cli/db/reset.js +23 -0
  12. package/src/cli/db/rollback.js +12 -0
  13. package/src/cli/db/seed.js +80 -0
  14. package/src/cli/db/unlock.js +9 -0
  15. package/src/cli/index.js +72 -0
  16. package/src/controllers/AdminController.js +322 -0
  17. package/src/controllers/CollectionController.js +274 -0
  18. package/src/controllers/Controller.js +657 -0
  19. package/src/controllers/ControllerAction.js +370 -0
  20. package/src/controllers/MemberAction.js +27 -0
  21. package/src/controllers/ModelController.js +63 -0
  22. package/src/controllers/RelationController.js +93 -0
  23. package/src/controllers/UsersController.js +64 -0
  24. package/src/controllers/index.js +5 -0
  25. package/src/errors/AssetError.js +7 -0
  26. package/src/errors/AuthenticationError.js +7 -0
  27. package/src/errors/AuthorizationError.js +7 -0
  28. package/src/errors/ControllerError.js +14 -0
  29. package/src/errors/DatabaseError.js +37 -0
  30. package/src/errors/GraphError.js +7 -0
  31. package/src/errors/ModelError.js +12 -0
  32. package/src/errors/NotFoundError.js +7 -0
  33. package/src/errors/NotImplementedError.js +7 -0
  34. package/src/errors/QueryBuilderError.js +7 -0
  35. package/src/errors/RelationError.js +21 -0
  36. package/src/errors/ResponseError.js +56 -0
  37. package/src/errors/ValidationError.js +7 -0
  38. package/src/errors/index.js +13 -0
  39. package/src/graph/DitoGraphProcessor.js +213 -0
  40. package/src/graph/expression.js +53 -0
  41. package/src/graph/graph.js +258 -0
  42. package/src/graph/index.js +3 -0
  43. package/src/index.js +9 -0
  44. package/src/lib/EventEmitter.js +66 -0
  45. package/src/lib/KnexHelper.js +30 -0
  46. package/src/lib/index.js +2 -0
  47. package/src/middleware/attachLogger.js +8 -0
  48. package/src/middleware/createTransaction.js +33 -0
  49. package/src/middleware/extendContext.js +10 -0
  50. package/src/middleware/findRoute.js +20 -0
  51. package/src/middleware/handleConnectMiddleware.js +99 -0
  52. package/src/middleware/handleError.js +29 -0
  53. package/src/middleware/handleRoute.js +23 -0
  54. package/src/middleware/handleSession.js +77 -0
  55. package/src/middleware/handleUser.js +31 -0
  56. package/src/middleware/index.js +11 -0
  57. package/src/middleware/logRequests.js +125 -0
  58. package/src/middleware/setupRequestStorage.js +14 -0
  59. package/src/mixins/AssetMixin.js +78 -0
  60. package/src/mixins/SessionMixin.js +17 -0
  61. package/src/mixins/TimeStampedMixin.js +41 -0
  62. package/src/mixins/UserMixin.js +171 -0
  63. package/src/mixins/index.js +4 -0
  64. package/src/models/AssetModel.js +4 -0
  65. package/src/models/Model.js +1205 -0
  66. package/src/models/RelationAccessor.js +41 -0
  67. package/src/models/SessionModel.js +4 -0
  68. package/src/models/TimeStampedModel.js +4 -0
  69. package/src/models/UserModel.js +4 -0
  70. package/src/models/definitions/assets.js +5 -0
  71. package/src/models/definitions/filters.js +121 -0
  72. package/src/models/definitions/hooks.js +8 -0
  73. package/src/models/definitions/index.js +22 -0
  74. package/src/models/definitions/modifiers.js +5 -0
  75. package/src/models/definitions/options.js +5 -0
  76. package/src/models/definitions/properties.js +73 -0
  77. package/src/models/definitions/relations.js +5 -0
  78. package/src/models/definitions/schema.js +5 -0
  79. package/src/models/definitions/scopes.js +36 -0
  80. package/src/models/index.js +5 -0
  81. package/src/query/QueryBuilder.js +1077 -0
  82. package/src/query/QueryFilters.js +66 -0
  83. package/src/query/QueryParameters.js +79 -0
  84. package/src/query/Registry.js +29 -0
  85. package/src/query/index.js +3 -0
  86. package/src/schema/formats/_empty.js +4 -0
  87. package/src/schema/formats/_required.js +4 -0
  88. package/src/schema/formats/index.js +2 -0
  89. package/src/schema/index.js +5 -0
  90. package/src/schema/keywords/_computed.js +7 -0
  91. package/src/schema/keywords/_foreign.js +7 -0
  92. package/src/schema/keywords/_hidden.js +7 -0
  93. package/src/schema/keywords/_index.js +7 -0
  94. package/src/schema/keywords/_instanceof.js +45 -0
  95. package/src/schema/keywords/_primary.js +7 -0
  96. package/src/schema/keywords/_range.js +18 -0
  97. package/src/schema/keywords/_relate.js +13 -0
  98. package/src/schema/keywords/_specificType.js +7 -0
  99. package/src/schema/keywords/_unique.js +7 -0
  100. package/src/schema/keywords/_unsigned.js +7 -0
  101. package/src/schema/keywords/_validate.js +73 -0
  102. package/src/schema/keywords/index.js +12 -0
  103. package/src/schema/relations.js +324 -0
  104. package/src/schema/relations.test.js +177 -0
  105. package/src/schema/schema.js +289 -0
  106. package/src/schema/schema.test.js +720 -0
  107. package/src/schema/types/_asset.js +31 -0
  108. package/src/schema/types/_color.js +4 -0
  109. package/src/schema/types/index.js +2 -0
  110. package/src/services/Service.js +35 -0
  111. package/src/services/index.js +1 -0
  112. package/src/storage/AssetFile.js +81 -0
  113. package/src/storage/DiskStorage.js +114 -0
  114. package/src/storage/S3Storage.js +169 -0
  115. package/src/storage/Storage.js +231 -0
  116. package/src/storage/index.js +9 -0
  117. package/src/utils/duration.js +15 -0
  118. package/src/utils/emitter.js +8 -0
  119. package/src/utils/fs.js +10 -0
  120. package/src/utils/function.js +17 -0
  121. package/src/utils/function.test.js +77 -0
  122. package/src/utils/handler.js +17 -0
  123. package/src/utils/json.js +3 -0
  124. package/src/utils/model.js +35 -0
  125. package/src/utils/net.js +17 -0
  126. package/src/utils/object.js +82 -0
  127. package/src/utils/object.test.js +86 -0
  128. package/src/utils/scope.js +7 -0
  129. package/types/index.d.ts +3547 -0
  130. package/types/tests/application.test-d.ts +26 -0
  131. package/types/tests/controller.test-d.ts +113 -0
  132. package/types/tests/errors.test-d.ts +53 -0
  133. package/types/tests/fixtures.ts +19 -0
  134. package/types/tests/model.test-d.ts +193 -0
  135. package/types/tests/query-builder.test-d.ts +106 -0
  136. package/types/tests/relation.test-d.ts +83 -0
  137. package/types/tests/storage.test-d.ts +113 -0
@@ -0,0 +1,3547 @@
1
+ /// <reference types="node" />
2
+
3
+ // Type definitions for Dito.js server
4
+ // Project: <https://github.com/ditojs/dito/>
5
+
6
+ // Export the entire Dito.js namespace.
7
+
8
+ import { ObjectCannedACL, S3ClientConfig } from '@aws-sdk/client-s3'
9
+ import { DateFormat } from '@ditojs/utils'
10
+ import { Options as KoaCorsOptions } from '@koa/cors'
11
+ import * as Ajv from 'ajv/dist/2020.js'
12
+ import { AsyncLocalStorage } from 'async_hooks'
13
+ import * as dbErrors from 'db-errors'
14
+ import * as EventEmitter2 from 'eventemitter2'
15
+ import helmet from 'helmet'
16
+ import { Knex } from 'knex'
17
+ import * as Koa from 'koa'
18
+ import { Options as KoaBodyParserOptions } from 'koa-bodyparser'
19
+ import { CompressOptions } from 'koa-compress'
20
+ import koaMount from 'koa-mount'
21
+ import koaResponseTime from 'koa-response-time'
22
+ import koaSession from 'koa-session'
23
+ import multer from '@koa/multer'
24
+ import multerS3 from 'multer-s3'
25
+ import * as objection from 'objection'
26
+ import { KnexSnakeCaseMappersFactory } from 'objection'
27
+ import { Logger as PinoLogger, LoggerOptions as PinoLoggerOptions } from 'pino'
28
+ import { PrettyOptions } from 'pino-pretty'
29
+ import {
30
+ Class,
31
+ ConditionalKeys,
32
+ Constructor,
33
+ SetOptional,
34
+ SetReturnType
35
+ } from 'type-fest'
36
+ import { UserConfig } from 'vite'
37
+
38
+ export type Page<$Model extends Model = Model> = {
39
+ total: number
40
+ results: $Model[]
41
+ }
42
+
43
+ /** Result of compiling action parameter definitions. */
44
+ export type CompiledParametersValidator = {
45
+ list: Array<{ name: string | null } & Schema>
46
+ schema: Schema | null
47
+ asObject: boolean
48
+ dataName: string
49
+ validate: ((data: unknown) => boolean | PromiseLike<boolean>) | null
50
+ hasModelRefs: boolean
51
+ }
52
+
53
+ export type ApplicationConfig = {
54
+ /** @defaultValue `production` */
55
+ env?: 'production' | 'development'
56
+ /** The server configuration */
57
+ server?: {
58
+ /** The ip address or hostname used to serve requests */
59
+ host?: string
60
+ /** The port to listen on for connections */
61
+ port?: string
62
+ }
63
+ /** Logging options */
64
+ log?: {
65
+ /**
66
+ * Enable logging requests to console by passing `true` or pick between
67
+ * 'console' for logging to console and 'file' for logging to file
68
+ *
69
+ * @defaultValue `false`
70
+ */
71
+ requests?: boolean | 'console' | 'file'
72
+ /**
73
+ * Whether to output route (Controller) logs
74
+ *
75
+ * @defaultValue `false`
76
+ */
77
+ routes?: boolean
78
+ /**
79
+ * Whether to log relation mappings
80
+ *
81
+ * @defaultValue `false`
82
+ */
83
+ relations?: boolean
84
+ /**
85
+ * Whether to log the json schema generated out of the model property
86
+ * definitions
87
+ *
88
+ * @defaultValue `false`
89
+ */
90
+ schema?: boolean
91
+ /**
92
+ * Whether to log sql queries
93
+ *
94
+ * @defaultValue `false`
95
+ */
96
+ sql?: boolean
97
+ /** Whether to turn off all logging */
98
+ silent?: boolean
99
+ errors?:
100
+ | boolean
101
+ | {
102
+ /** Whether to log sql errors */
103
+ sql?: boolean
104
+ /** Whether to log the error stack */
105
+ stack?: boolean
106
+ json?: boolean
107
+ }
108
+ }
109
+ api?: ApiConfig
110
+ app?: {
111
+ /**
112
+ * Whether to normalize paths from camel case to kebab case.
113
+ *
114
+ * @defaultValue `false`
115
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/controllers.md#path-normalization|Path Normalization}
116
+ */
117
+ normalizePaths?: boolean
118
+ /**
119
+ * Whether proxy header fields will be trusted.
120
+ *
121
+ * @defaultValue `false`
122
+ */
123
+ proxy?: Koa['proxy']
124
+ /**
125
+ * Whether to include X-Response-Time header in responses
126
+ *
127
+ * @defaultValue `true`
128
+ */
129
+ responseTime?: boolean | Parameters<typeof koaResponseTime>[0]
130
+ /**
131
+ * Whether to use koa-helmet middleware which provides important security
132
+ * headers to make your app more secure by default.
133
+ *
134
+ * @defaultValue `true`
135
+ * @see https://github.com/venables/koa-helmet
136
+ * @see https://github.com/helmetjs/helmet
137
+ */
138
+ helmet?: boolean | Parameters<typeof helmet>[0]
139
+ logger?: {
140
+ prettyPrint?: PrettyOptions
141
+ } & PinoLoggerOptions
142
+ /**
143
+ * Configure body parser.
144
+ *
145
+ * @see https://github.com/koajs/bodyparser#options
146
+ */
147
+ bodyParser?: KoaBodyParserOptions
148
+ /**
149
+ * Enable or configure Cross-Origin Resource Sharing (CORS)
150
+ *
151
+ * @defaultValue `true`
152
+ * @see https://github.com/koajs/cors#corsoptions
153
+ */
154
+ cors?: boolean | KoaCorsOptions
155
+ /**
156
+ * Enable or configure server response compression
157
+ *
158
+ * @defaultValue `true`
159
+ * @see https://github.com/koajs/compress#options
160
+ */
161
+ compress?: boolean | CompressOptions
162
+ /**
163
+ * Enable ETag headers in server responses
164
+ *
165
+ * @defaultValue `true`
166
+ */
167
+ etag?: boolean
168
+ /**
169
+ * @defaultValue `false`
170
+ * @see https://github.com/koajs/session
171
+ */
172
+ session?: boolean | (koaSession.opts & { modelClass: string })
173
+ /**
174
+ * Enable passport authentication middleware
175
+ *
176
+ * @defaultValue `false`
177
+ */
178
+ passport?: boolean
179
+ /**
180
+ * Set signed cookie keys.
181
+ *
182
+ * @see https://github.com/koajs/koa/blob/master/docs/api/index.md#appkeys
183
+ */
184
+ keys?: Koa['keys']
185
+ }
186
+ admin?: AdminConfig
187
+ knex?: Knex.Config<any> & {
188
+ /** @defaultValue `false` */
189
+ normalizeDbNames?: boolean | Parameters<KnexSnakeCaseMappersFactory>
190
+ // See https://github.com/brianc/node-pg-types/blob/master/index.d.ts#L67
191
+ typeParsers?: Record<number, <I extends string | Buffer>(value: I) => any>
192
+ }
193
+ /**
194
+ * Service configurations keyed by service name. Pass
195
+ * `false` as a value to disable a service.
196
+ */
197
+ services?: Record<string, Record<string, unknown> | false>
198
+ storages?: StorageConfigs
199
+ /** Logger configuration at the application level. */
200
+ logger?: {
201
+ prettyPrint?: PrettyOptions
202
+ } & PinoLoggerOptions
203
+ assets?: {
204
+ /**
205
+ * Threshold after which unused assets that haven't
206
+ * seen changes for given timeframe are removed.
207
+ *
208
+ * @example '1 hr 20 mins'
209
+ * @defaultValue `'24h'`
210
+ * @see https://www.npmjs.com/package/parse-duration
211
+ */
212
+ cleanupTimeThreshold?: string | number
213
+ /**
214
+ * Threshold after which dangling assets (uploaded
215
+ * but never persisted) are removed. Cannot be set to
216
+ * 0 as the file would be deleted immediately after
217
+ * upload.
218
+ *
219
+ * @defaultValue `'24h'`
220
+ * @see https://www.npmjs.com/package/parse-duration
221
+ */
222
+ danglingTimeThreshold?: string | number
223
+ }
224
+ }
225
+
226
+ export type MulterS3File = {
227
+ bucket: string
228
+ key: string
229
+ acl: string
230
+ contentType: string
231
+ contentDisposition: null
232
+ storageClass: string
233
+ serverSideEncryption: null
234
+ metadata: Record<string, string>
235
+ location: string
236
+ etag: string
237
+ }
238
+
239
+ export type StorageConfigs = { [key: string]: StorageConfig }
240
+
241
+ interface CommonStorageConfig {
242
+ /**
243
+ * The concurrency at which assets are added to storage.
244
+ *
245
+ * @default `8`
246
+ */
247
+ concurrency?: number
248
+ /**
249
+ * The base URL at which assets are accessible.
250
+ */
251
+ url?: string
252
+ allowedImports?: string[]
253
+ }
254
+
255
+ export type S3StorageConfig = CommonStorageConfig & {
256
+ type: 's3'
257
+ bucket: string
258
+ acl?: ObjectCannedACL | string
259
+ s3: S3ClientConfig
260
+ } & Omit<
261
+ Parameters<typeof multerS3>[0],
262
+ 's3' | 'key' | 'contentType' | 'metadata'
263
+ >
264
+
265
+ export interface DiskStorageConfig extends CommonStorageConfig {
266
+ type: 'disk'
267
+ /**
268
+ * The path to the directory where assets are stored on.
269
+ */
270
+ path: string
271
+ }
272
+
273
+ export type StorageConfig = S3StorageConfig | DiskStorageConfig
274
+
275
+ export interface AdminConfig {
276
+ api?: ApiConfig
277
+ /** Path to the admin's src directory. Mandatory when in development mode. */
278
+ root?: string
279
+ /**
280
+ * Path to the dist/src/admin directory. Mandatory when in production mode.
281
+ */
282
+ dist?: string
283
+ /** @default Application */
284
+ mode?: 'production' | 'development'
285
+ /** Settings accessible on the browser side as `global.dito.settings`. */
286
+ settings?: Record<string, any>
287
+ }
288
+
289
+ export interface ApiResource {
290
+ type: string
291
+ path?: string
292
+ parent?: ApiResource
293
+ }
294
+
295
+ export interface ApiConfig {
296
+ /** The base url to use for api requests. */
297
+ url?: string
298
+ /** @defaultValue 'en-US' */
299
+ locale?: string
300
+ dateFormat?: DateFormat
301
+ /**
302
+ * Whether to display admin notifications.
303
+ *
304
+ * @default `true`
305
+ */
306
+ notifications?:
307
+ | boolean
308
+ | {
309
+ /**
310
+ * The amount of milliseconds multiplied with the amount of characters
311
+ * displayed in the notification, plus 40 (40 + title + message).
312
+ *
313
+ * @defaultValue `20`
314
+ */
315
+ durationFactor: number
316
+ }
317
+ cors?: {
318
+ /**
319
+ * Whether cross-site `Access-Control` requests are made using credentials.
320
+ */
321
+ credentials: boolean
322
+ }
323
+ /**
324
+ * Setting normalizePaths to `true` sets `api.normalizePath` to hyphenate
325
+ * camelized strings and `api.denormalizePath` to do the opposite. If you
326
+ * prefer to use another path normalization algorithm, they can be defined the
327
+ * api settings passed to the DitoAdmin constructor.
328
+ *
329
+ * @defaultValue Application.config.app.normalizePaths
330
+ */
331
+ normalizePaths?: boolean
332
+ /** Auth resources */
333
+ users?: {
334
+ path?: string
335
+ login?: {
336
+ /** @defaultValue `'login'` */
337
+ path?: string
338
+ /** @defaultValue `'post'` */
339
+ method?: HTTPMethod
340
+ }
341
+ logout?: {
342
+ /** @defaultValue `'logout'` */
343
+ path?: string
344
+ /** @defaultValue `'post'` */
345
+ method?: HTTPMethod
346
+ }
347
+ session?: {
348
+ /** @defaultValue `'session'` */
349
+ path?: string
350
+ /** @defaultValue `'get'` */
351
+ method?: HTTPMethod
352
+ }
353
+ }
354
+ /** Optionally override resource path handlers. */
355
+ resources?: Record<string, (resource: ApiResource | string) => string>
356
+
357
+ /**
358
+ * Optionally override / extend headers sent with API
359
+ * requests.
360
+ */
361
+ headers?: Record<string, string>
362
+ }
363
+
364
+ export interface ApplicationControllers {
365
+ [k: string]:
366
+ | Class<ModelController>
367
+ | Class<Controller>
368
+ | ApplicationControllers
369
+ }
370
+
371
+ export type Models = Record<string, Class<Model>>
372
+
373
+ interface AsyncRequestLocals {
374
+ transaction: objection.Transaction
375
+ logger: PinoLogger
376
+ }
377
+
378
+ export class Application<$Models extends Models = Models> {
379
+ constructor(options: {
380
+ basePath?: string
381
+ config?: ApplicationConfig
382
+ validator?: Validator
383
+ router?: {
384
+ add(
385
+ method: string,
386
+ path: string,
387
+ handler: Function
388
+ ): this
389
+ find(
390
+ method: string,
391
+ path: string
392
+ ): {
393
+ status: number
394
+ handler?: Function
395
+ params?: Record<string, string>
396
+ allowed: string[]
397
+ }
398
+ getAllowedMethods(
399
+ path?: string | null,
400
+ exclude?: string | null
401
+ ): string[]
402
+ normalizePath(path: string): string
403
+ }
404
+ /**
405
+ * Subscribe to application events. Event names: `'before:start'`,
406
+ * `'after:start'`, `'before:stop'`, `'after:stop'`, `'error'`
407
+ */
408
+ events?: Record<
409
+ string,
410
+ (this: Application<$Models>, ...args: any[]) => void
411
+ >
412
+ models?: $Models
413
+ controllers?: ApplicationControllers
414
+ /** Service classes or instances to register. */
415
+ services?: Services
416
+ middleware?: Koa.Middleware
417
+ })
418
+
419
+ /** The base path for resolving relative paths. */
420
+ basePath: string
421
+ /** The merged application configuration. */
422
+ config: ApplicationConfig
423
+ /** The Knex instance for database access. */
424
+ knex: Knex
425
+ /** The HTTP server instance, or `null` if not started. */
426
+ server: import('http').Server | null
427
+ /** Whether the application is currently running. */
428
+ isRunning: boolean
429
+ /** The schema validator instance. */
430
+ validator: Validator
431
+ /** Registered storage instances by name. */
432
+ storages: Record<string, Storage>
433
+ /** Registered service instances by name. */
434
+ services: Record<string, Service>
435
+ /** Registered controller instances by name. */
436
+ controllers: Record<string, Controller>
437
+ models: $Models
438
+
439
+ setup(): Promise<void>
440
+ /** Calls `start()` and exits the process on failure. */
441
+ execute(): Promise<void>
442
+ start(): Promise<void>
443
+ stop(timeout?: number): Promise<void>
444
+
445
+ /** Configures the pino logger instance. */
446
+ setupLogger(): void
447
+ /** Configures the Knex database connection. */
448
+ setupKnex(): void
449
+ /**
450
+ * Configures all Koa middleware (logger, error
451
+ * handling, CORS, compression, session, passport,
452
+ * etc.).
453
+ */
454
+ setupMiddleware(middleware?: Koa.Middleware): void
455
+
456
+ addStorage(
457
+ config: StorageConfig | Storage,
458
+ name?: string
459
+ ): Storage
460
+
461
+ addStorages(storages: StorageConfigs): void
462
+ setupStorages(): Promise<void>
463
+ /** Returns a storage by name, or `null` if not found. */
464
+ getStorage(name: string): Storage | null
465
+
466
+ addService(
467
+ service: Service | Class<Service>,
468
+ name?: string
469
+ ): void
470
+
471
+ addServices(services: Services): void
472
+ setupServices(): Promise<void>
473
+ /** Returns a service by name. */
474
+ getService(name: string): Service | null
475
+ /** Finds a service matching the given predicate. */
476
+ findService(
477
+ callback: (service: Service) => boolean
478
+ ): Service | null
479
+
480
+ addModel(model: Class<Model>): void
481
+ addModels(models: Models): void
482
+ setupModels(): Promise<void>
483
+ /**
484
+ * Returns a model class by name. Also looks up
485
+ * `${name}Model` if the name doesn't already end in
486
+ * 'Model'.
487
+ */
488
+ getModel(name: string): Class<Model> | null
489
+ /** Finds a model class matching the given predicate. */
490
+ findModel(
491
+ callback: (model: Class<Model>) => boolean
492
+ ): Class<Model> | null
493
+
494
+ addController(
495
+ controller: Controller | Class<Controller>,
496
+ namespace?: string
497
+ ): void
498
+
499
+ addControllers(
500
+ controllers: ApplicationControllers,
501
+ namespace?: string
502
+ ): void
503
+
504
+ setupControllers(): Promise<void>
505
+ /** Returns a controller by its URL. */
506
+ getController(url: string): Controller | null
507
+ /**
508
+ * Finds a controller matching the given predicate.
509
+ */
510
+ findController(
511
+ callback: (controller: Controller) => boolean
512
+ ): Controller | null
513
+
514
+ /** Returns the admin controller, if registered. */
515
+ getAdminController(): AdminController | null
516
+
517
+ /** Compiles a JSON schema into a validation function. */
518
+ compileValidator(
519
+ jsonSchema: Schema,
520
+ options?: Record<string, any>
521
+ ): Ajv.ValidateFunction | null
522
+
523
+ /**
524
+ * Compiles action parameter definitions into a
525
+ * validation function and metadata.
526
+ */
527
+ compileParametersValidator(
528
+ parameters:
529
+ | Record<string, Schema>
530
+ | Array<{ name?: string } & Schema>,
531
+ options?: Record<string, any>
532
+ ): CompiledParametersValidator
533
+
534
+ /** Creates a ValidationError from raw error data. */
535
+ createValidationError(error: {
536
+ type: string
537
+ message?: string
538
+ errors: Ajv.ErrorObject[]
539
+ options?: Record<string, any>
540
+ json?: any
541
+ }): ValidationError
542
+
543
+ /** Creates a DatabaseError from a native DB error. */
544
+ createDatabaseError(error: dbErrors.DBError): DatabaseError
545
+
546
+ /**
547
+ * Wraps a database identifier through Knex's
548
+ * `wrapIdentifier` (e.g. camelCase to snake_case).
549
+ */
550
+ normalizeIdentifier(identifier: string): string
551
+ /**
552
+ * Reverses a database identifier through Knex's
553
+ * `postProcessResponse` (e.g. snake_case to camelCase).
554
+ */
555
+ denormalizeIdentifier(identifier: string): string
556
+ /** Normalizes a URL path. */
557
+ normalizePath(path: string): string
558
+
559
+ /** Formats an error for logging/response. */
560
+ formatError(error: Error | ResponseError): unknown
561
+
562
+ /** Logs an error to the application logger. */
563
+ logError(error: Error | ResponseError, ctx?: KoaContext): void
564
+ /** Releases unused assets past the cleanup threshold. */
565
+ releaseUnusedAssets(options?: {
566
+ timeThreshold?: string | number | null
567
+ transaction?: objection.Transaction | null
568
+ concurrency?: number
569
+ }): Promise<Model[] | undefined>
570
+
571
+ /**
572
+ * Creates Asset model records for the given files.
573
+ * Returns inserted assets, or `null` if no AssetModel
574
+ * is registered.
575
+ */
576
+ createAssets(
577
+ storage: Storage,
578
+ files: AssetFile[],
579
+ count?: number,
580
+ transaction?: objection.Transaction | null
581
+ ): Promise<Model[] | null>
582
+
583
+ /**
584
+ * Handles added, removed, and changed asset files.
585
+ * Imports foreign assets, updates counts, and schedules
586
+ * cleanup. Returns imported files when an AssetModel is
587
+ * registered.
588
+ */
589
+ handleAddedAndRemovedAssets(
590
+ storage: Storage,
591
+ addedFiles: AssetFile[],
592
+ removedFiles: AssetFile[],
593
+ changedFiles: AssetFile[],
594
+ transaction?: objection.Transaction | null
595
+ ): Promise<AssetFile[] | undefined>
596
+
597
+ /**
598
+ * Finds and imports missing assets from external
599
+ * sources (data URIs, file:// URLs, or HTTP URLs).
600
+ * Returns the list of imported files.
601
+ */
602
+ addForeignAssets(
603
+ storage: Storage,
604
+ files: AssetFile[],
605
+ transaction?: objection.Transaction | null
606
+ ): Promise<AssetFile[]>
607
+
608
+ /**
609
+ * Handles modifications to existing asset files by
610
+ * updating their stored data. Returns the list of
611
+ * modified files.
612
+ */
613
+ handleModifiedAssets(
614
+ storage: Storage,
615
+ files: AssetFile[],
616
+ transaction?: objection.Transaction | null
617
+ ): Promise<AssetFile[]>
618
+
619
+ addRoute(
620
+ method: HTTPMethod,
621
+ path: string,
622
+ transacted: boolean,
623
+ middlewares: OrArrayOf<(ctx: KoaContext, next: Function) => void>,
624
+ controller?: Controller | null,
625
+ action?: any
626
+ ): void
627
+
628
+ loadAdminViteConfig(): Promise<UserConfig | null>
629
+ getAssetConfig(options?: {
630
+ models?: string[]
631
+ normalizeDbNames?: boolean
632
+ }): Record<string, Record<string, Record<string, any>>>
633
+
634
+ defineAdminViteConfig(config?: UserConfig): UserConfig | null
635
+ logger: PinoLogger
636
+ requestStorage: AsyncLocalStorage<AsyncRequestLocals>
637
+ requestLocals: Partial<AsyncRequestLocals>
638
+ }
639
+
640
+ export interface Application
641
+ extends Omit<
642
+ Koa,
643
+ | 'setMaxListeners'
644
+ | 'removeListener'
645
+ | 'removeAllListeners'
646
+ | 'prependOnceListener'
647
+ | 'prependListener'
648
+ | 'once'
649
+ | 'on'
650
+ | 'off'
651
+ | 'listeners'
652
+ | 'addListener'
653
+ | 'listenerCount'
654
+ | 'emit'
655
+ | 'eventNames'
656
+ >,
657
+ EventEmitter {}
658
+
659
+ export type SchemaType = LiteralUnion<
660
+ | 'string'
661
+ | 'number'
662
+ | 'integer'
663
+ | 'boolean'
664
+ | 'object'
665
+ | 'array'
666
+ | 'null'
667
+ | 'date'
668
+ | 'datetime'
669
+ | 'timestamp'
670
+ >
671
+
672
+ export interface ModelRelation {
673
+ /**
674
+ * The type of relation
675
+ *
676
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-relations.md#relation-types|Relation Types}
677
+ */
678
+ relation: LiteralUnion<
679
+ 'belongsTo' | 'hasMany' | 'hasOne' | 'manyToMany' | 'hasOneThrough'
680
+ >
681
+ /**
682
+ * The model and property name from which the relation is to be built, as a
683
+ * string with both identifiers separated by '.', e.g.:
684
+ * 'FromModelClass.fromPropertyName'
685
+ */
686
+ from: string
687
+ /**
688
+ * The model and property name to which the relation is to be built, as a
689
+ * string with both identifiers separated by '.', e.g.:
690
+ * 'ToModelClass.toPropertyName'
691
+ */
692
+ to: string
693
+ /**
694
+ * When set to true the join model class and table is to be built
695
+ * automatically, or allows to specify an existing one manually.
696
+ *
697
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-relations.md#join-models-and-tables|Join Models and Tables}
698
+ */
699
+ through?:
700
+ | boolean
701
+ | {
702
+ /**
703
+ * The model and property name or table and column name of an existing
704
+ * join model class or join table from which the through relation is to
705
+ * be built, as a string with both identifiers separated by '.', e.g.:
706
+ * 'FromModelClass.fromPropertyName'
707
+ */
708
+ from: string
709
+ /**
710
+ * The model and property name or table and column name of an existing
711
+ * join model class or join table to which the through relation is to be
712
+ * built, as a string with both identifiers separated by '.', e.g.:
713
+ * 'toModelClass.toPropertyName'
714
+ */
715
+ to: string
716
+ /**
717
+ * List additional columns to be added to the related model.
718
+ *
719
+ * When working with a join model class or table, extra columns from it
720
+ * can be added to the related model, as if it was define on its own
721
+ * table. They then appear as additional properties on the related
722
+ * model.
723
+ */
724
+ extra?: string[]
725
+ }
726
+ /**
727
+ * Controls whether the relation is the inverse of another relation.
728
+ *
729
+ * This information is only required when working with through relations.
730
+ * Without it, Dito.js wouldn't be able to tell which side of the relation is
731
+ * on the left-hand side, and which is on the right-hand side when
732
+ * automatically creating the join model class and table.
733
+ */
734
+ inverse?: boolean
735
+ /**
736
+ * Optionally, a scope can be defined to be applied when loading the
737
+ * relation's models. The scope needs to be defined in the related model
738
+ * class' scopes definitions.
739
+ */
740
+ scope?: string
741
+ /**
742
+ * Optionally, a filter to apply when loading the relation's models.
743
+ * Accepts a Dito.js filter name/object (resolved via the related model's
744
+ * filters), or a callback/find-filter object as an alias for `modify`.
745
+ */
746
+ filter?:
747
+ | string
748
+ | { [name: string]: unknown[] }
749
+ | ((query: QueryBuilder) => void)
750
+ | Record<string, unknown>
751
+ /**
752
+ * Controls whether the auto-inserted foreign key property should be marked as
753
+ * nullable. This only makes sense on a 'belongsTo' relation, where the model
754
+ * class holds the foreign key, and only when the foreign key isn't already
755
+ * explicitly defined in the Model Properties.
756
+ */
757
+ nullable?: boolean
758
+ /**
759
+ * Controls whether the relation owns the models that it holds, or whether it
760
+ * is simply relating to them, and a relation elsewhere is considered to be
761
+ * their owner.
762
+ */
763
+ owner?: boolean
764
+ /**
765
+ * An optional modify callback or find-filter object to scope the relation
766
+ * query. This is the Objection.js-native way to modify relation queries.
767
+ *
768
+ * As a function: `modify: query => query.where('active', true)`
769
+ * As an object: `modify: { active: true }` (converted to a find-filter)
770
+ */
771
+ modify?:
772
+ | ((query: QueryBuilder) => void)
773
+ | Record<string, unknown>
774
+ }
775
+
776
+ export type ModelProperty<T = any> = Schema<T> & {
777
+ /** Marks the column as the primary key in the database. */
778
+ primary?: boolean
779
+ /**
780
+ * Defines if the property is a foreign key.
781
+ *
782
+ * Finds the information about the related model in the relations definition
783
+ * and adds a reference to the related model table in migrations, by calling
784
+ * the .references(columnName).inTable(tableName) method.
785
+ */
786
+ foreign?: boolean
787
+ /**
788
+ * Adds an index to the database column in the migrations, by calling the
789
+ * .index() method.
790
+ */
791
+ index?: boolean
792
+ /**
793
+ * Marks the column as nullable in the migrations, by calling the .nullable()
794
+ * method.
795
+ */
796
+ nullable?: boolean
797
+ /**
798
+ * Adds a unique constraint to the table for the given column in the
799
+ * migrations, by calling the .unique() method. If a string is provided, all
800
+ * columns with the same string value for unique are grouped together in one
801
+ * unique constraint, by calling .unique([column1, column2, …]).
802
+ */
803
+ unique?: boolean | string
804
+ /**
805
+ * Marks the column for a property of type 'integer' to be
806
+ * unsigned in the migrations, by calling the .unsigned()
807
+ * method.
808
+ */
809
+ unsigned?: boolean
810
+ /**
811
+ * Marks the property as computed.
812
+ *
813
+ * Computed properties are not present as columns in the database itself. They
814
+ * can be created either by an SQL statement (SELECT … AS), or by a getter
815
+ * accessor defined on the model. Computed properties are set when converting
816
+ * to JSON if not present already, and removed again before data is sent to
817
+ * the database.
818
+ */
819
+ computed?: boolean
820
+ /**
821
+ * Marks the property has hidden, so that it does not show up in data
822
+ * converted to JSON.
823
+ *
824
+ * This can be used for sensitive data.
825
+ */
826
+ hidden?: boolean
827
+ }
828
+
829
+ /**
830
+ * A scope function that modifies a query builder in-place
831
+ * to apply filtering or ordering logic. The return value
832
+ * is ignored at runtime.
833
+ *
834
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md|Model Scopes}
835
+ */
836
+ export type ModelScope<$Model extends Model = Model> = (
837
+ query: QueryBuilder<$Model>,
838
+ applyParentScope: (query: QueryBuilder<$Model>) => QueryBuilder<$Model>
839
+ ) => QueryBuilder<$Model, any> | void
840
+
841
+ /**
842
+ * Map of scope names to scope functions. Scopes can be
843
+ * applied via `withScope()` on queries or set as defaults
844
+ * on controllers.
845
+ *
846
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md|Model Scopes}
847
+ */
848
+ export type ModelScopes<$Model extends Model = Model> = Record<
849
+ string,
850
+ ModelScope<$Model>
851
+ >
852
+
853
+ /**
854
+ * A filter handler function that modifies a query builder
855
+ * based on external parameters (e.g. from URL query strings).
856
+ */
857
+ export type ModelFilterFunction<$Model extends Model = Model> = (
858
+ queryBuilder: QueryBuilder<$Model>,
859
+ ...args: any[]
860
+ ) => void
861
+
862
+ /**
863
+ * A model filter definition. Can be one of:
864
+ *
865
+ * - A **built-in filter** reference (`{ filter: 'text' }` or
866
+ * `{ filter: 'date-range' }`) with optional `properties`.
867
+ * - A **custom filter** with a `handler` function, optional
868
+ * `parameters` schema for validation, and optional
869
+ * `response` schema.
870
+ * - A bare **handler function** as shorthand.
871
+ *
872
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-filters.md|Model Filters}
873
+ */
874
+ export type ModelFilter<$Model extends Model = Model> =
875
+ | {
876
+ filter: LiteralUnion<'text' | 'date-range'>
877
+ properties?: string[]
878
+ }
879
+ | {
880
+ handler: ModelFilterFunction<$Model>
881
+ parameters?: { [key: string]: Schema }
882
+ response?: Schema
883
+ // TODO: validate type
884
+ validate?: any
885
+ }
886
+ | ModelFilterFunction<$Model>
887
+
888
+ /**
889
+ * Map of filter names to filter definitions.
890
+ *
891
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-filters.md|Model Filters}
892
+ */
893
+ export type ModelFilters<$Model extends Model = Model> = Record<
894
+ string,
895
+ ModelFilter<$Model>
896
+ >
897
+
898
+ /**
899
+ * Configuration for a model asset property, linking it to a
900
+ * named storage backend.
901
+ */
902
+ export interface ModelAsset {
903
+ /** The name of the storage backend to use. */
904
+ storage: string
905
+ /**
906
+ * Whether to read image dimensions (width/height) on
907
+ * upload.
908
+ */
909
+ readDimensions?: boolean
910
+ }
911
+
912
+ /** Map of property names to their asset configurations. */
913
+ export type ModelAssets = Record<string, ModelAsset>
914
+
915
+ export interface ModelOptions extends objection.ModelOptions {
916
+ graph?: boolean
917
+ async?: boolean
918
+ mutable?: boolean
919
+ }
920
+
921
+ type ModelHookFunction<$Model extends Model> = (
922
+ args: objection.StaticHookArguments<$Model>
923
+ ) => void
924
+
925
+ /**
926
+ * Map of lifecycle hook names to handler functions. Hook
927
+ * names follow the pattern `'before:operation'` or
928
+ * `'after:operation'` where operation is `find`, `insert`,
929
+ * `update`, or `delete`.
930
+ *
931
+ * @example
932
+ * ```ts
933
+ * static hooks: ModelHooks<MyModel> = {
934
+ * 'before:insert': ({ items }) => {
935
+ * for (const item of items) {
936
+ * item.createdAt = new Date()
937
+ * }
938
+ * }
939
+ * }
940
+ * ```
941
+ */
942
+ export type ModelHooks<$Model extends Model = Model> = {
943
+ [key in `${'before' | 'after'}:${
944
+ | 'find'
945
+ | 'insert'
946
+ | 'update'
947
+ | 'delete'
948
+ }`]?: ModelHookFunction<$Model>
949
+ }
950
+
951
+ export class Model extends objection.Model {
952
+ constructor(json?: Record<string, any>)
953
+
954
+ /** @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md|Model Properties} */
955
+ static properties: ModelProperties
956
+
957
+ /** @see {@link https://github.com/ditojs/dito/blob/master/docs/model-relations.md|Model Relations} */
958
+ static relations: ModelRelations
959
+
960
+ /** @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md|Model Scopes} */
961
+ static scopes: ModelScopes<Model>
962
+
963
+ /** @see {@link https://github.com/ditojs/dito/blob/master/docs/model-filters.md|Model Filters} */
964
+ static filters: ModelFilters<Model>
965
+
966
+ static hooks: ModelHooks<Model>
967
+
968
+ static assets: ModelAssets
969
+
970
+ /** The merged definition object, assembled from the class hierarchy. */
971
+ static get definition(): {
972
+ properties: ModelProperties
973
+ relations: ModelRelations
974
+ scopes: ModelScopes
975
+ filters: ModelFilters
976
+ hooks: ModelHooks
977
+ assets: ModelAssets
978
+ options: Record<string, any>
979
+ modifiers: Record<
980
+ string,
981
+ (
982
+ builder: objection.QueryBuilder<any, any>,
983
+ ...args: any[]
984
+ ) => void
985
+ >
986
+ schema: Record<string, any>
987
+ [key: string]: any
988
+ }
989
+
990
+ /** Derived from class name (removes 'Model' suffix). */
991
+ static get tableName(): string
992
+ /** Returns the primary key column(s). */
993
+ static get idColumn(): string | string[]
994
+ /**
995
+ * Returns the cached converted relation mappings for
996
+ * the model.
997
+ */
998
+ static get relationMappings(): Record<
999
+ string,
1000
+ objection.RelationMapping<Model>
1001
+ >
1002
+
1003
+ /** Returns the cached converted JSON schema. */
1004
+ static get jsonSchema(): Record<string, any>
1005
+ /** Aliases `computedAttributes`. */
1006
+ static get virtualAttributes(): string[]
1007
+
1008
+ static get jsonAttributes(): string[]
1009
+ static get booleanAttributes(): string[]
1010
+ static get dateAttributes(): string[]
1011
+ static get computedAttributes(): string[]
1012
+ static get hiddenAttributes(): string[]
1013
+
1014
+ /** The application instance this model is registered with. */
1015
+ static app: Application<Models>
1016
+ /** Whether the model has been initialized by the application. */
1017
+ static initialized: boolean
1018
+ /** The QueryBuilder class used by this model. */
1019
+ static QueryBuilder: typeof QueryBuilder
1020
+ /** Whether to deep-clone object attributes on read. */
1021
+ static cloneObjectAttributes: boolean
1022
+ /**
1023
+ * Only pick properties defined in jsonSchema
1024
+ * for database JSON.
1025
+ */
1026
+ static pickJsonSchemaProperties: boolean
1027
+ /** Whether to use LIMIT 1 in first() queries. */
1028
+ static useLimitInFirst: boolean
1029
+
1030
+ /** Called by the application during model registration. */
1031
+ static configure(app: Application<Models>): void
1032
+ /** Sets up model schema, relations, and scopes. */
1033
+ static setup(): void
1034
+ /**
1035
+ * Async initialization hook. Override in subclasses for
1036
+ * setup that requires async operations.
1037
+ * @overridable
1038
+ */
1039
+ static initialize(): void | Promise<void>
1040
+
1041
+ /**
1042
+ * Creates a model instance from JSON data. Dito's
1043
+ * override adds async validation support: returns a
1044
+ * Promise when `options.async` is true.
1045
+ * @override
1046
+ */
1047
+ static fromJson<M extends Model>(
1048
+ this: Constructor<M>,
1049
+ json: Record<string, any>,
1050
+ options?: ModelOptions
1051
+ ): M | Promise<M>
1052
+
1053
+ /** Returns the named scope function, if defined. */
1054
+ static getScope(name: string): ModelScope | undefined
1055
+ /** Returns whether the model has the named scope. */
1056
+ static hasScope(name: string): boolean
1057
+ /**
1058
+ * Creates a reference instance containing only the
1059
+ * identifier properties.
1060
+ */
1061
+ static getReference(
1062
+ modelOrId: Model | Id,
1063
+ includeProperties?: string[]
1064
+ ): Model
1065
+
1066
+ /** Returns whether the given value is a model reference. */
1067
+ static isReference(obj: unknown): boolean
1068
+ /** Returns the named property definition, if found. */
1069
+ static getProperty(name: string): ModelProperty | null
1070
+ /** Returns the model's query modifiers. */
1071
+ static getModifiers(): Record<
1072
+ string,
1073
+ (
1074
+ builder: objection.QueryBuilder<any, any>,
1075
+ ...args: any[]
1076
+ ) => void
1077
+ >
1078
+
1079
+ /**
1080
+ * Returns property names matching the given filter
1081
+ * function.
1082
+ */
1083
+ static getAttributes(
1084
+ filter: (property: ModelProperty) => boolean
1085
+ ): string[]
1086
+
1087
+ /** Returns relations where this model is the related side. */
1088
+ static getRelatedRelations(): objection.Relation[]
1089
+
1090
+ /**
1091
+ * Maps property names to column names (identity
1092
+ * function — naming is handled at Knex level).
1093
+ * @override
1094
+ */
1095
+ static propertyNameToColumnName(
1096
+ propertyName: string
1097
+ ): string
1098
+
1099
+ /**
1100
+ * Maps column names to property names (identity
1101
+ * function — naming is handled at Knex level).
1102
+ * @override
1103
+ */
1104
+ static columnNameToPropertyName(
1105
+ columnName: string
1106
+ ): string
1107
+
1108
+ /**
1109
+ * Handles modifiers not found directly on the model by
1110
+ * checking scopes and special prefixes.
1111
+ * @override
1112
+ */
1113
+ static modifierNotFound(
1114
+ query: QueryBuilder<Model>,
1115
+ modifier: string | Function
1116
+ ): void
1117
+
1118
+ /**
1119
+ * Creates a NotFoundError with model context.
1120
+ * @override
1121
+ */
1122
+ static createNotFoundError(
1123
+ ctx: Record<string, any>,
1124
+ error?: Error
1125
+ ): NotFoundError
1126
+
1127
+ /**
1128
+ * Returns the shared application validator.
1129
+ * @override
1130
+ */
1131
+ static createValidator(): Validator
1132
+
1133
+ /**
1134
+ * Creates a typed validation or relation error from
1135
+ * raw error data.
1136
+ * @override
1137
+ */
1138
+ static createValidationError(error: {
1139
+ type: string
1140
+ message?: string
1141
+ errors: any[]
1142
+ options?: Record<string, any>
1143
+ json?: any
1144
+ }): ResponseError
1145
+
1146
+ /** Starts a new transaction. */
1147
+ static transaction(): Promise<objection.Transaction>
1148
+ /** Runs a callback within a transaction. */
1149
+ static transaction(
1150
+ handler: (trx: objection.Transaction) => Promise<any>
1151
+ ): Promise<any>
1152
+
1153
+ static transaction(
1154
+ trx: objection.Transaction,
1155
+ handler: (trx: objection.Transaction) => Promise<any>
1156
+ ): Promise<any>
1157
+
1158
+ static getPropertyOrRelationAtDataPath(
1159
+ dataPath: OrArrayOf<string>
1160
+ ): {
1161
+ property?: ModelProperty | null
1162
+ relation?: objection.Relation | null
1163
+ wildcard?: string | null
1164
+ dataPath?: string | null
1165
+ nestedDataPath?: string | null
1166
+ name?: string
1167
+ expression?: string | null
1168
+ }
1169
+
1170
+ /**
1171
+ * Creates a query builder for a relation. Unlike
1172
+ * Objection.js, Dito automatically aliases the query
1173
+ * to the relation name.
1174
+ * @override
1175
+ */
1176
+ static relatedQuery<M extends Model>(
1177
+ this: Constructor<M>,
1178
+ relationName: string,
1179
+ trx?: objection.Transaction
1180
+ ): QueryBuilder<M>
1181
+
1182
+ /**
1183
+ * Hook called before find queries.
1184
+ * @overridable
1185
+ */
1186
+ static beforeFind(
1187
+ args: objection.StaticHookArguments<Model>
1188
+ ): Promise<void>
1189
+
1190
+ /**
1191
+ * Hook called after find queries.
1192
+ * @overridable
1193
+ */
1194
+ static afterFind(
1195
+ args: objection.StaticHookArguments<Model>
1196
+ ): Promise<void>
1197
+
1198
+ /**
1199
+ * Hook called before insert queries.
1200
+ * @overridable
1201
+ */
1202
+ static beforeInsert(
1203
+ args: objection.StaticHookArguments<Model>
1204
+ ): Promise<void>
1205
+
1206
+ /**
1207
+ * Hook called after insert queries.
1208
+ * @overridable
1209
+ */
1210
+ static afterInsert(
1211
+ args: objection.StaticHookArguments<Model>
1212
+ ): Promise<void>
1213
+
1214
+ /**
1215
+ * Hook called before update queries.
1216
+ * @overridable
1217
+ */
1218
+ static beforeUpdate(
1219
+ args: objection.StaticHookArguments<Model>
1220
+ ): Promise<void>
1221
+
1222
+ /**
1223
+ * Hook called after update queries.
1224
+ * @overridable
1225
+ */
1226
+ static afterUpdate(
1227
+ args: objection.StaticHookArguments<Model>
1228
+ ): Promise<void>
1229
+
1230
+ /**
1231
+ * Hook called before delete queries.
1232
+ * @overridable
1233
+ */
1234
+ static beforeDelete(
1235
+ args: objection.StaticHookArguments<Model>
1236
+ ): Promise<void>
1237
+
1238
+ /**
1239
+ * Hook called after delete queries.
1240
+ * @overridable
1241
+ */
1242
+ static afterDelete(
1243
+ args: objection.StaticHookArguments<Model>
1244
+ ): Promise<void>
1245
+
1246
+ static count: {
1247
+ (column?: objection.ColumnRef, options?: { as: string }): Promise<number>
1248
+ (aliasToColumnDict: Record<string, string | string[]>): Promise<number>
1249
+ (...columns: objection.ColumnRef[]): Promise<number>
1250
+ }
1251
+
1252
+ /**
1253
+ * Filters a model graph using a relation expression,
1254
+ * removing any data not matching the expression.
1255
+ */
1256
+ static filterGraph(
1257
+ modelGraph: Model | Model[],
1258
+ expr: string | objection.RelationExpression<Model>
1259
+ ): Model | Model[]
1260
+
1261
+ /**
1262
+ * Populates a model graph by loading relations defined
1263
+ * in the given expression.
1264
+ */
1265
+ static populateGraph(
1266
+ modelGraph: Model | Model[],
1267
+ expr: string | objection.RelationExpression<Model>,
1268
+ trx?: objection.Transaction
1269
+ ): Promise<Model | Model[]>
1270
+
1271
+ /**
1272
+ * Dito.js automatically adds an `id` property if a model
1273
+ * property with the `primary: true` setting is not
1274
+ * already explicitly defined.
1275
+ */
1276
+ readonly id: Id
1277
+
1278
+ /**
1279
+ * Dito.js automatically adds a `foreignKeyId` property
1280
+ * if foreign keys occurring in relations definitions are
1281
+ * not explicitly defined in the properties.
1282
+ */
1283
+ readonly foreignKeyId: Id
1284
+
1285
+ QueryBuilderType: QueryBuilder<this, this[]>
1286
+
1287
+ $app: Application<Models>
1288
+ /**
1289
+ * Called after model construction. Override in subclasses
1290
+ * for custom instance initialization.
1291
+ * @overridable
1292
+ */
1293
+ $initialize(): void
1294
+ $is(model: Model | null | undefined): boolean
1295
+ /** Returns `true` if all named properties are defined. */
1296
+ $has(...properties: string[]): boolean
1297
+ /** Runs a callback within a transaction. */
1298
+ $transaction(
1299
+ handler: (trx: objection.Transaction) => Promise<any>
1300
+ ): Promise<any>
1301
+
1302
+ $transaction(
1303
+ trx: objection.Transaction,
1304
+ handler: (trx: objection.Transaction) => Promise<any>
1305
+ ): Promise<any>
1306
+
1307
+ /** Emits an event on this model instance. */
1308
+ $emit(event: string, ...args: any[]): Promise<any[]>
1309
+ /**
1310
+ * Filters a model graph using a relation expression,
1311
+ * removing data not matching the expression.
1312
+ */
1313
+ $filterGraph(
1314
+ modelGraph: Model | Model[],
1315
+ expr: string | objection.RelationExpression<Model>
1316
+ ): Model | Model[]
1317
+
1318
+ /**
1319
+ * Populates a model graph by loading relations defined
1320
+ * in the given expression.
1321
+ */
1322
+ $populateGraph(
1323
+ modelGraph: Model | Model[],
1324
+ expr: string | objection.RelationExpression<Model>,
1325
+ trx?: objection.Transaction
1326
+ ): Promise<Model | Model[]>
1327
+
1328
+ $update(
1329
+ attributes: Partial<ModelDataProperties<this>>,
1330
+ trx?: objection.Transaction
1331
+ ): objection.SingleQueryBuilder<objection.QueryBuilderType<this>>
1332
+
1333
+ $patch(
1334
+ attributes: Partial<ModelDataProperties<this>>,
1335
+ trx?: objection.Transaction
1336
+ ): objection.SingleQueryBuilder<objection.QueryBuilderType<this>>
1337
+
1338
+ $validate<$JSON extends null | {}>(
1339
+ json?: $JSON,
1340
+ options?: ModelOptions & Record<string, any>
1341
+ ): ($JSON | this) | Promise<$JSON | this>
1342
+
1343
+ $validateGraph(
1344
+ options: ModelOptions & Record<string, any>
1345
+ ): Promise<this>
1346
+
1347
+ /**
1348
+ * Sets JSON data on the model instance. Triggers
1349
+ * `$initialize()` when appropriate.
1350
+ * @override
1351
+ */
1352
+ $setJson(
1353
+ json: Record<string, any>,
1354
+ options?: ModelOptions
1355
+ ): this
1356
+
1357
+ /**
1358
+ * Formats data for database storage. Converts dates to
1359
+ * ISO strings, handles boolean conversion for SQLite,
1360
+ * and removes computed properties.
1361
+ * @override @overridable
1362
+ */
1363
+ $formatDatabaseJson(
1364
+ json: Record<string, any>
1365
+ ): Record<string, any>
1366
+
1367
+ /**
1368
+ * Parses data from the database. Converts SQLite
1369
+ * booleans back and delegates to `$parseJson()` for
1370
+ * date and AssetFile handling.
1371
+ * @override @overridable
1372
+ */
1373
+ $parseDatabaseJson(
1374
+ json: Record<string, any>
1375
+ ): Record<string, any>
1376
+
1377
+ /**
1378
+ * Parses general JSON data. Converts date strings to
1379
+ * Date objects and handles asset file conversion.
1380
+ * @override @overridable
1381
+ */
1382
+ $parseJson(
1383
+ json: Record<string, any>
1384
+ ): Record<string, any>
1385
+
1386
+ /**
1387
+ * Formats data for API output. Removes hidden
1388
+ * attributes from the JSON representation.
1389
+ * @override @overridable
1390
+ */
1391
+ $formatJson(
1392
+ json: Record<string, any>
1393
+ ): Record<string, any>
1394
+
1395
+ /* -------------------- Start QueryBuilder.mixin(Model) ------------------- */
1396
+ static first: StaticQueryBuilderMethod<'first'>
1397
+ static find: StaticQueryBuilderMethod<'find'>
1398
+ static findOne: StaticQueryBuilderMethod<'findOne'>
1399
+ static findById: StaticQueryBuilderMethod<'findById'>
1400
+
1401
+ static withGraph: StaticQueryBuilderMethod<'withGraph'>
1402
+ static withGraphFetched: StaticQueryBuilderMethod<'withGraphFetched'>
1403
+ static withGraphJoined: StaticQueryBuilderMethod<'withGraphJoined'>
1404
+ static clearWithGraph: StaticQueryBuilderMethod<'clearWithGraph'>
1405
+
1406
+ static withScope: StaticQueryBuilderMethod<'withScope'>
1407
+ static applyScope: StaticQueryBuilderMethod<'applyScope'>
1408
+ static clearWithScope: StaticQueryBuilderMethod<'clearWithScope'>
1409
+
1410
+ static clear: StaticQueryBuilderMethod<'clear'>
1411
+ static select: StaticQueryBuilderMethod<'select'>
1412
+
1413
+ static insert: StaticQueryBuilderMethod<'insert'>
1414
+ static upsert: StaticQueryBuilderMethod<'upsert'>
1415
+ static update: StaticQueryBuilderMethod<'update'>
1416
+ static patch: StaticQueryBuilderMethod<'patch'>
1417
+ static delete: StaticQueryBuilderMethod<'delete'>
1418
+
1419
+ static updateById: StaticQueryBuilderMethod<'updateById'>
1420
+ static patchById: StaticQueryBuilderMethod<'patchById'>
1421
+ static deleteById: StaticQueryBuilderMethod<'deleteById'>
1422
+
1423
+ static truncate: StaticQueryBuilderMethod<'truncate'>
1424
+
1425
+ static insertAndFetch: StaticQueryBuilderMethod<'insertAndFetch'>
1426
+ static upsertAndFetch: StaticQueryBuilderMethod<'upsertAndFetch'>
1427
+ static updateAndFetch: StaticQueryBuilderMethod<'updateAndFetch'>
1428
+ static patchAndFetch: StaticQueryBuilderMethod<'patchAndFetch'>
1429
+ static patchAndFetchById: StaticQueryBuilderMethod<'patchAndFetchById'>
1430
+ static updateAndFetchById: StaticQueryBuilderMethod<'updateAndFetchById'>
1431
+
1432
+ static insertGraph: StaticQueryBuilderMethod<'insertGraph'>
1433
+ static upsertGraph: StaticQueryBuilderMethod<'upsertGraph'>
1434
+ static insertGraphAndFetch: StaticQueryBuilderMethod<'insertGraphAndFetch'>
1435
+ static upsertGraphAndFetch: StaticQueryBuilderMethod<'upsertGraphAndFetch'>
1436
+
1437
+ static insertDitoGraph: StaticQueryBuilderMethod<'insertDitoGraph'>
1438
+ static upsertDitoGraph: StaticQueryBuilderMethod<'upsertDitoGraph'>
1439
+ static updateDitoGraph: StaticQueryBuilderMethod<'updateDitoGraph'>
1440
+ static patchDitoGraph: StaticQueryBuilderMethod<'patchDitoGraph'>
1441
+
1442
+ static insertDitoGraphAndFetch: StaticQueryBuilderMethod<'insertDitoGraphAndFetch'>
1443
+ static upsertDitoGraphAndFetch: StaticQueryBuilderMethod<'upsertDitoGraphAndFetch'>
1444
+ static updateDitoGraphAndFetch: StaticQueryBuilderMethod<'updateDitoGraphAndFetch'>
1445
+ static patchDitoGraphAndFetch: StaticQueryBuilderMethod<'patchDitoGraphAndFetch'>
1446
+ static upsertDitoGraphAndFetchById: StaticQueryBuilderMethod<'upsertDitoGraphAndFetchById'>
1447
+ static updateDitoGraphAndFetchById: StaticQueryBuilderMethod<'updateDitoGraphAndFetchById'>
1448
+ static patchDitoGraphAndFetchById: StaticQueryBuilderMethod<'patchDitoGraphAndFetchById'>
1449
+
1450
+ static where: StaticQueryBuilderMethod<'where'>
1451
+ static whereNot: StaticQueryBuilderMethod<'whereNot'>
1452
+ static whereRaw: StaticQueryBuilderMethod<'whereRaw'>
1453
+ static whereWrapped: StaticQueryBuilderMethod<'whereWrapped'>
1454
+ static whereExists: StaticQueryBuilderMethod<'whereExists'>
1455
+ static whereNotExists: StaticQueryBuilderMethod<'whereNotExists'>
1456
+ static whereIn: StaticQueryBuilderMethod<'whereIn'>
1457
+ static whereNotIn: StaticQueryBuilderMethod<'whereNotIn'>
1458
+ static whereNull: StaticQueryBuilderMethod<'whereNull'>
1459
+ static whereNotNull: StaticQueryBuilderMethod<'whereNotNull'>
1460
+ static whereBetween: StaticQueryBuilderMethod<'whereBetween'>
1461
+ static whereNotBetween: StaticQueryBuilderMethod<'whereNotBetween'>
1462
+ static whereColumn: StaticQueryBuilderMethod<'whereColumn'>
1463
+ static whereNotColumn: StaticQueryBuilderMethod<'whereNotColumn'>
1464
+ static whereComposite: StaticQueryBuilderMethod<'whereComposite'>
1465
+ static whereInComposite: StaticQueryBuilderMethod<'whereInComposite'>
1466
+ static whereNotInComposite: StaticQueryBuilderMethod<'whereNotInComposite'>
1467
+ static whereJsonHasAny: StaticQueryBuilderMethod<'whereJsonHasAny'>
1468
+ static whereJsonHasAll: StaticQueryBuilderMethod<'whereJsonHasAll'>
1469
+ static whereJsonIsArray: StaticQueryBuilderMethod<'whereJsonIsArray'>
1470
+ static whereJsonNotArray: StaticQueryBuilderMethod<'whereJsonNotArray'>
1471
+ static whereJsonIsObject: StaticQueryBuilderMethod<'whereJsonIsObject'>
1472
+ static whereJsonNotObject: StaticQueryBuilderMethod<'whereJsonNotObject'>
1473
+ static whereJsonSubsetOf: StaticQueryBuilderMethod<'whereJsonSubsetOf'>
1474
+ static whereJsonNotSubsetOf: StaticQueryBuilderMethod<'whereJsonNotSubsetOf'>
1475
+ static whereJsonSupersetOf: StaticQueryBuilderMethod<'whereJsonSupersetOf'>
1476
+ static whereJsonNotSupersetOf: StaticQueryBuilderMethod<'whereJsonNotSupersetOf'>
1477
+
1478
+ static having: StaticQueryBuilderMethod<'having'>
1479
+ static havingIn: StaticQueryBuilderMethod<'havingIn'>
1480
+ static havingNotIn: StaticQueryBuilderMethod<'havingNotIn'>
1481
+ static havingNull: StaticQueryBuilderMethod<'havingNull'>
1482
+ static havingNotNull: StaticQueryBuilderMethod<'havingNotNull'>
1483
+ static havingExists: StaticQueryBuilderMethod<'havingExists'>
1484
+ static havingNotExists: StaticQueryBuilderMethod<'havingNotExists'>
1485
+ static havingBetween: StaticQueryBuilderMethod<'havingBetween'>
1486
+ static havingNotBetween: StaticQueryBuilderMethod<'havingNotBetween'>
1487
+ static havingRaw: StaticQueryBuilderMethod<'havingRaw'>
1488
+ static havingWrapped: StaticQueryBuilderMethod<'havingWrapped'>
1489
+
1490
+ // static scope: QueryBuilder<Model>['scope']
1491
+ // static mergeScope: QueryBuilder<Model>['mergeScope']
1492
+ // static clearScope: QueryBuilder<Model>['clearScope']
1493
+
1494
+ /* --------------------- End QueryBuilder.mixin(Model) -------------------- */
1495
+ }
1496
+
1497
+ type StaticQueryBuilderMethod<
1498
+ K extends ConditionalKeys<QueryBuilder<Model>, (...a: any[]) => any>
1499
+ > = <$Model extends Class<Model>>(
1500
+ ...args: Parameters<QueryBuilder<InstanceType<$Model>>[K]>
1501
+ ) => ReturnType<QueryBuilder<InstanceType<$Model>>[K]>
1502
+
1503
+ // @eslint-disable-next-line @typescript-eslint/no-empty-interface
1504
+ export interface Model extends EventEmitter {}
1505
+ export interface Model extends KnexHelper {}
1506
+
1507
+ export type ModelClass = Class<Model>
1508
+
1509
+ export type ModelRelations = Record<string, ModelRelation>
1510
+
1511
+ export type ModelProperties = Record<string, ModelProperty>
1512
+
1513
+ /**
1514
+ * A controller action definition. Either an options object
1515
+ * with `handler`, `method`, `path`, `authorize`, etc., or a
1516
+ * bare handler function. The HTTP method and path are
1517
+ * derived from the action name key (e.g. `'postImport'`
1518
+ * maps to `POST /import`).
1519
+ */
1520
+ export type ControllerAction<$Controller extends Controller = Controller> =
1521
+ | ControllerActionOptions<$Controller>
1522
+ | ControllerActionHandler<$Controller>
1523
+
1524
+ export class Controller {
1525
+ app: Application
1526
+ /**
1527
+ * Optionally provide the controller path. A default is
1528
+ * deducted from the normalized class name otherwise.
1529
+ */
1530
+ path?: string
1531
+ /**
1532
+ * The controller's name. If not provided, it is
1533
+ * automatically deducted from the controller class name.
1534
+ * If this name ends in 'Controller', that is stripped
1535
+ * off the name, so 'GreetingsController' turns into
1536
+ * 'Greetings'.
1537
+ */
1538
+ name?: string
1539
+ /**
1540
+ * The controller's namespace, which is prepended to path
1541
+ * to generate the absolute controller route. Note that
1542
+ * it is rare to provide this manually. Usually Dito.js
1543
+ * determines the namespace automatically from the
1544
+ * controller object passed to the Dito.js application's
1545
+ * constructor and its sub-objects.
1546
+ *
1547
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/controllers.md#namespaces Namespaces}
1548
+ */
1549
+ namespace?: string
1550
+ /** The fully resolved URL for this controller. */
1551
+ url?: string
1552
+ /**
1553
+ * Whether actions should be transacted by default.
1554
+ * Can be overridden per-action.
1555
+ */
1556
+ transacted?: boolean
1557
+ /** Whether this controller has been initialized. */
1558
+ initialized: boolean
1559
+ /** The nesting level of this controller. */
1560
+ level: number
1561
+ /** Whether to log routes during setup. */
1562
+ logRoutes: boolean
1563
+
1564
+ /**
1565
+ * A list of allowed actions. If provided, only the
1566
+ * action names listed here as strings will be mapped to
1567
+ * routes, everything else will be omitted.
1568
+ */
1569
+ allow?: OrReadOnly<ControllerActionName[]>
1570
+
1571
+ /** Authorization */
1572
+ authorize?: Authorize
1573
+ actions?: ControllerActions<this>
1574
+
1575
+ /** Returns the logger, optionally scoped to a context. */
1576
+ getLogger(ctx?: KoaContext): PinoLogger
1577
+ /** The controller's logger instance. */
1578
+ get logger(): PinoLogger
1579
+ /**
1580
+ * Returns a member model for the request context.
1581
+ * No-op in the base class; override in subclasses.
1582
+ */
1583
+ getMember(
1584
+ ctx?: KoaContext,
1585
+ base?: any,
1586
+ options?: {
1587
+ query?: Record<string, any>
1588
+ modify?: (query: QueryBuilder<Model>) => QueryBuilder<Model>
1589
+ forUpdate?: boolean
1590
+ }
1591
+ ): Promise<Model | null>
1592
+
1593
+ setProperty(key: string, value: unknown): void
1594
+
1595
+ /**
1596
+ * Called right after the constructor, before `setup()` and `initialize()`.
1597
+ * @overridable
1598
+ */
1599
+ configure(): void
1600
+ /* @overridable */
1601
+ setup(): void
1602
+ /**
1603
+ * To be overridden in sub-classes, if the controller needs to initialize.
1604
+ * @overridable
1605
+ */
1606
+ initialize(): Promise<void>
1607
+ /**
1608
+ * @overridable
1609
+ */
1610
+ logController(): void
1611
+ /**
1612
+ * @param str The string to log.
1613
+ * @param [indent=0] The amount of levels to indent (in pairs of two spaces).
1614
+ * Default is `0`
1615
+ */
1616
+ logRoute(str: string, indent?: number): void
1617
+ setupRoute<$ControllerAction extends ControllerAction = ControllerAction>(
1618
+ method: HTTPMethod,
1619
+ url: string,
1620
+ transacted: boolean,
1621
+ authorize: Authorize,
1622
+ action: $ControllerAction,
1623
+ handlers: ((ctx: KoaContext, next: Function) => void)[]
1624
+ ): void
1625
+
1626
+ setupActions(type: string): any
1627
+ setupActionRoute(type: string, action: ControllerAction): void
1628
+ setupAssets(): any
1629
+ setupAssetRoute(
1630
+ dataPath: OrArrayOf<string>,
1631
+ config: any,
1632
+ authorize: Authorize
1633
+ ): void
1634
+
1635
+ /**
1636
+ * To be overridden in sub-classes, if the controller needs to install
1637
+ * middleware.
1638
+ * @overridable
1639
+ */
1640
+ compose(): Parameters<typeof koaMount>[1]
1641
+ /** To be overridden by sub-classes. */
1642
+ getPath(type: string, path: string): string
1643
+ getUrl(type: string, path: string): string
1644
+ inheritValues(type: string): Record<string, unknown> | undefined
1645
+ processValues(values: Record<string, unknown>): {
1646
+ values: Record<string, unknown>
1647
+ allow: string[]
1648
+ authorize: Record<string, Authorize>
1649
+ }
1650
+
1651
+ emitHook(
1652
+ type: string,
1653
+ handleResult: boolean,
1654
+ ctx: KoaContext,
1655
+ ...args: any[]
1656
+ ): Promise<any>
1657
+
1658
+ processAuthorize(
1659
+ authorize: Authorize
1660
+ ): (ctx: KoaContext, member?: Model) => OrPromiseOf<boolean>
1661
+
1662
+ describeAuthorize(authorize: Authorize): string
1663
+ handleAuthorization(
1664
+ authorization: (
1665
+ ctx: KoaContext,
1666
+ member?: Model
1667
+ ) => OrPromiseOf<boolean>,
1668
+ ctx: KoaContext,
1669
+ member?: Model
1670
+ ): Promise<void>
1671
+ }
1672
+
1673
+ export interface Controller extends EventEmitter {}
1674
+
1675
+ /** A named action parameter with a JSON Schema definition. */
1676
+ export type ActionParameter = Schema & { name: string }
1677
+
1678
+ /**
1679
+ * Handler function for a model controller action. Receives
1680
+ * the Koa context and any resolved action parameters. `this`
1681
+ * is bound to the controller instance.
1682
+ */
1683
+ export type ModelControllerActionHandler<
1684
+ $ModelController extends ModelController = ModelController
1685
+ > = (this: $ModelController, ctx: KoaContext, ...args: any[]) => any
1686
+
1687
+ /**
1688
+ * Handler function for a controller action. Receives the Koa
1689
+ * context and any resolved action parameters. `this` is
1690
+ * bound to the controller instance.
1691
+ */
1692
+ export type ControllerActionHandler<
1693
+ $Controller extends Controller = Controller
1694
+ > = (this: $Controller, ctx: KoaContext, ...args: any[]) => any
1695
+
1696
+ type ModelDataKey<T, K extends keyof T> = K extends
1697
+ | 'QueryBuilderType'
1698
+ | 'foreignKeyId'
1699
+ | `$${string}`
1700
+ ? never
1701
+ : T[K] extends (...args: any[]) => any
1702
+ ? never
1703
+ : K
1704
+
1705
+ type ModelDataProperties<$Model extends Model = Model> = {
1706
+ [K in keyof $Model as ModelDataKey<$Model, K>]: $Model[K] extends Model
1707
+ ? ModelDataProperties<$Model[K]>
1708
+ : $Model[K]
1709
+ }
1710
+
1711
+ /**
1712
+ * Authorization configuration for controllers and actions.
1713
+ *
1714
+ * - `boolean`: `true` requires authentication, `false` is
1715
+ * public.
1716
+ * - `'$self'`: Authorized only if the member matches the
1717
+ * authenticated user.
1718
+ * - `'$owner'`: Authorized if the member is owned by the
1719
+ * authenticated user (via `Model.$hasOwner()`).
1720
+ * - Any other `string`: Checked as a role via
1721
+ * `UserModel.$hasRole()`.
1722
+ * - `function`: Dynamically resolves to any of the above.
1723
+ * - `Record<HTTPMethod, string | string[]>`: Per-method
1724
+ * role authorization.
1725
+ */
1726
+ export type Authorize =
1727
+ | boolean
1728
+ | OrArrayOf<LiteralUnion<'$self' | '$owner'>>
1729
+ | ((ctx: KoaContext) => OrPromiseOf<Authorize>)
1730
+ | Record<HTTPMethod, string | string[]>
1731
+
1732
+ export type BaseControllerActionOptions = {
1733
+ /**
1734
+ * The HTTP method (`'get'`, `'post'`, `'put'`, `'delete'` or `'patch'`) to
1735
+ * which the action should listen. By default, the `'get'` method is assigned
1736
+ * if none is provided.
1737
+ */
1738
+ method?: HTTPMethod
1739
+ /**
1740
+ * The path to which the action is mapped, defined in relation to the route
1741
+ * path of its controller. By default, the normalized method name is used as
1742
+ * the action's path.
1743
+ */
1744
+ path?: string
1745
+ /**
1746
+ * Determines whether or how the request is authorized. This value can either
1747
+ * be one of the values as described below, an array of them or a function
1748
+ * which returns one or more of them.
1749
+ *
1750
+ * - Boolean: `true` if the action should be authorized, `false` otherwise.
1751
+ * - '$self': The requested member is checked against `ctx.state.user` and the
1752
+ * action is only authorized if it matches the member.
1753
+ * - '$owner': The member is asked if it is owned by `ctx.state.user` through
1754
+ * the optional `Model.$hasOwner()` method.
1755
+ * - Any string: `ctx.state.user` is checked for this role through the
1756
+ * overridable `UserModel.hasRole()` method.
1757
+ */
1758
+ authorize?: Authorize
1759
+ /**
1760
+ * Validates action parameters and maps them to Koa's `ctx.query` object
1761
+ * passed to the action handler.
1762
+ *
1763
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md Model Properties}
1764
+ */
1765
+ parameters?: { [key: string]: Schema }
1766
+ /**
1767
+ * Provides a schema for the value returned from the action handler and
1768
+ * optionally maps the value to a key inside a returned object when it
1769
+ * contains a `name` property.
1770
+ *
1771
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md Model Properties}
1772
+ */
1773
+ response?: Schema & { name?: string }
1774
+ /**
1775
+ * The scope(s) to be applied to every query executed through the action.
1776
+ *
1777
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md Model Scopes}
1778
+ */
1779
+ scope?: string[]
1780
+ /**
1781
+ * Determines whether queries in the action should be executed within a
1782
+ * transaction. Any failure will mean the database will rollback any queries
1783
+ * executed to the pre-transaction state.
1784
+ */
1785
+ transacted?: boolean
1786
+ }
1787
+
1788
+ export type ControllerActionOptions<
1789
+ $Controller extends Controller = Controller
1790
+ > = BaseControllerActionOptions & {
1791
+ handler: ControllerActionHandler<$Controller>
1792
+ }
1793
+
1794
+ export type ModelControllerActionOptions<
1795
+ $ModelController extends ModelController = ModelController
1796
+ > = BaseControllerActionOptions & {
1797
+ /** The function to be called when the action route is requested. */
1798
+ handler: ModelControllerActionHandler<$ModelController>
1799
+ }
1800
+
1801
+ export type MemberActionParameter<$Model extends Model = Model> =
1802
+ | Schema
1803
+ | {
1804
+ from: 'member'
1805
+
1806
+ /** Sets ctx.query. */
1807
+ query?: Record<string, any>
1808
+ /**
1809
+ * Adds a FOR UPDATE in PostgreSQL and MySQL during a select statement.
1810
+ * FOR UPDATE causes the rows retrieved by the SELECT statement to be
1811
+ * locked as though for update. This prevents them from being locked,
1812
+ * modified or deleted by other transactions until the current transaction
1813
+ * ends.
1814
+ *
1815
+ * @default `false`
1816
+ * @see {@link http://knexjs.org/#Builder-forUpdate}
1817
+ * @see {@link https://www.postgresql.org/docs/12/explicit-locking.html#LOCKING-ROWS}
1818
+ */
1819
+ forUpdate?: boolean
1820
+ /** Modify the member query. */
1821
+ modify?: (query: QueryBuilder<$Model>) => QueryBuilder<$Model>
1822
+ }
1823
+
1824
+ /**
1825
+ * A model controller action: either an options object with
1826
+ * `handler` or a bare handler function.
1827
+ */
1828
+ export type ModelControllerAction<
1829
+ $ModelController extends ModelController = ModelController
1830
+ > =
1831
+ | ModelControllerActionOptions<$ModelController>
1832
+ | ModelControllerActionHandler<$ModelController>
1833
+
1834
+ /**
1835
+ * Map of action names to action definitions for a model
1836
+ * controller's collection-level actions (e.g. `getList`,
1837
+ * `postCreate`).
1838
+ */
1839
+ export type ModelControllerActions<
1840
+ $ModelController extends ModelController = ModelController
1841
+ > = {
1842
+ [name: ControllerActionName]: ModelControllerAction<$ModelController>
1843
+ allow?: OrReadOnly<ControllerActionName[]>
1844
+ authorize?: Authorize
1845
+ }
1846
+
1847
+ type ModelControllerMemberAction<
1848
+ $ModelController extends ModelController = ModelController
1849
+ > =
1850
+ | (Omit<ModelControllerActionOptions<$ModelController>, 'parameters'> & {
1851
+ parameters?: {
1852
+ [key: string]: MemberActionParameter<
1853
+ ModelFromModelController<$ModelController>
1854
+ >
1855
+ }
1856
+ })
1857
+ | ModelControllerActionHandler<$ModelController>
1858
+
1859
+ /**
1860
+ * Map of action names to action definitions for a model
1861
+ * controller's member-level actions (e.g. `get`, `patch`,
1862
+ * `delete`). Member actions can use `{ from: 'member' }`
1863
+ * parameters to receive the resolved member model.
1864
+ */
1865
+ export type ModelControllerMemberActions<
1866
+ $ModelController extends ModelController = ModelController
1867
+ > = {
1868
+ [name: ControllerActionName]: ModelControllerMemberAction<$ModelController>
1869
+ allow?: OrReadOnly<ControllerActionName[]>
1870
+ authorize?: Authorize
1871
+ }
1872
+
1873
+ /**
1874
+ * Action name pattern: an HTTP method followed by an
1875
+ * optional suffix, e.g. `'getStats'`, `'postImport'`,
1876
+ * `'deleteAll'`.
1877
+ */
1878
+ export type ControllerActionName = `${HTTPMethod}${string}`
1879
+
1880
+ /**
1881
+ * Map of action names to action definitions for a
1882
+ * controller. Supports `allow` to whitelist specific
1883
+ * actions and `authorize` for group-level authorization.
1884
+ */
1885
+ export type ControllerActions<$Controller extends Controller = Controller> = {
1886
+ [name: ControllerActionName]: ControllerAction<$Controller>
1887
+ allow?: OrReadOnly<ControllerActionName[]>
1888
+ authorize?: Authorize
1889
+ }
1890
+
1891
+ export class UsersController<
1892
+ M extends Model = Model
1893
+ > extends ModelController<M> {
1894
+ /**
1895
+ * Returns whether the current request is authenticated
1896
+ * and the user is an instance of the controller's model.
1897
+ */
1898
+ isAuthenticated(ctx: KoaContext): boolean
1899
+ }
1900
+
1901
+ export class AdminController extends Controller {
1902
+ config: AdminConfig
1903
+ mode: 'production' | 'development'
1904
+ closed: boolean
1905
+ koa: Koa
1906
+ getPath(name: string): string
1907
+ getDitoObject(): {
1908
+ base: string
1909
+ api: AdminConfig['api']
1910
+ settings: AdminConfig['settings']
1911
+ }
1912
+
1913
+ sendDitoObject(ctx: Koa.Context): void
1914
+ middleware(): Koa.Middleware
1915
+ setupViteServer(): Promise<void>
1916
+ defineViteConfig(config?: UserConfig): UserConfig
1917
+ }
1918
+ type ModelControllerHookType = 'collection' | 'member'
1919
+ type ModelControllerHookKeys<
1920
+ $Keys extends string,
1921
+ $ModelControllerHookType extends string
1922
+ > = `${
1923
+ | 'before'
1924
+ | 'after'
1925
+ | '*'
1926
+ }:${
1927
+ | $ModelControllerHookType
1928
+ | '*'
1929
+ }:${
1930
+ | Exclude<$Keys, 'allow'>
1931
+ | ControllerActionName
1932
+ | '*'
1933
+ }`
1934
+ type ModelControllerHook<
1935
+ $ModelController extends ModelController = ModelController
1936
+ > = (
1937
+ ctx: KoaContext,
1938
+ result: objection.Page<ModelFromModelController<$ModelController>>
1939
+ ) => any
1940
+
1941
+ type HookKeysFromController<$ModelController extends ModelController> =
1942
+ | ModelControllerHookKeys<
1943
+ Exclude<
1944
+ keyof Exclude<$ModelController['collection'], undefined>,
1945
+ symbol | number
1946
+ >,
1947
+ 'collection'
1948
+ >
1949
+ | ModelControllerHookKeys<
1950
+ Exclude<
1951
+ keyof Exclude<$ModelController['member'], undefined>,
1952
+ symbol | number
1953
+ >,
1954
+ 'member'
1955
+ >
1956
+
1957
+ type HandlerFromHookKey<
1958
+ $ModelController extends ModelController,
1959
+ K extends HookKeysFromController<$ModelController>
1960
+ > = K extends `${
1961
+ | 'before'
1962
+ | 'after'
1963
+ | '*'
1964
+ }:${
1965
+ | 'collection'
1966
+ | 'member'
1967
+ | '*'
1968
+ }:${string}`
1969
+ ? (this: $ModelController, ctx: KoaContext, ...args: any[]) => any
1970
+ : never
1971
+
1972
+ type ModelControllerHooks<
1973
+ $ModelController extends ModelController = ModelController
1974
+ > = {
1975
+ [$Key in HookKeysFromController<$ModelController>]?: HandlerFromHookKey<
1976
+ $ModelController,
1977
+ $Key
1978
+ >
1979
+ }
1980
+
1981
+ /**
1982
+ * Scope(s) to apply to all queries in a model controller.
1983
+ * A single scope name or an array of scope names.
1984
+ */
1985
+ export type ModelControllerScope = OrArrayOf<string>
1986
+
1987
+ /**
1988
+ * Abstract base class for controllers that operate on
1989
+ * model collections. Provides CRUD action infrastructure,
1990
+ * query building, and member resolution.
1991
+ */
1992
+ export class CollectionController<
1993
+ $Model extends Model = Model
1994
+ > extends Controller {
1995
+ /**
1996
+ * The model class this controller operates on. Set by
1997
+ * subclasses during configuration.
1998
+ */
1999
+ modelClass?: Class<$Model>
2000
+ /**
2001
+ * Whether to use graph methods for insert/update/patch.
2002
+ *
2003
+ * @defaultValue `false`
2004
+ */
2005
+ graph?: boolean
2006
+ /** Whether the controller handles relate operations. */
2007
+ relate?: boolean
2008
+ /**
2009
+ * Whether the controller handles unrelate operations.
2010
+ */
2011
+ unrelate?: boolean
2012
+ /** Whether this is a one-to-one relation controller. */
2013
+ isOneToOne: boolean
2014
+ /** The route parameter name for the member id. */
2015
+ idParam: string
2016
+ /**
2017
+ * The scope(s) to apply to every query executed through
2018
+ * this controller.
2019
+ */
2020
+ scope?: ModelControllerScope
2021
+ allowScope?: boolean | OrArrayOf<string>
2022
+ allowFilter?: boolean | OrArrayOf<string>
2023
+ allowParam?: OrArrayOf<LiteralUnion<keyof QueryParameterOptions>>
2024
+
2025
+ /**
2026
+ * The controller's collection actions with built-in CRUD
2027
+ * defaults.
2028
+ */
2029
+ collection?: ModelControllerActions<CollectionController<$Model>>
2030
+ /**
2031
+ * The controller's member actions with built-in CRUD
2032
+ * defaults.
2033
+ */
2034
+ member?: ModelControllerMemberActions<CollectionController<$Model>>
2035
+
2036
+ /** Creates a query builder for this controller's model. */
2037
+ query(trx?: objection.Transaction): QueryBuilder<$Model>
2038
+ /**
2039
+ * Applies controller-level scopes and configuration to
2040
+ * a query builder.
2041
+ */
2042
+ setupQuery(
2043
+ query: QueryBuilder<$Model>,
2044
+ base?: any
2045
+ ): QueryBuilder<$Model>
2046
+
2047
+ /**
2048
+ * Extracts the member id from the request context's
2049
+ * route parameters.
2050
+ */
2051
+ getMemberId(ctx: KoaContext): Id | Id[]
2052
+ /**
2053
+ * Retrieves the model id from a model instance.
2054
+ */
2055
+ getModelId(model: $Model): Id | Id[]
2056
+ /**
2057
+ * Retrieves a member model from the database for the
2058
+ * current request context.
2059
+ */
2060
+ getMember(
2061
+ ctx: KoaContext,
2062
+ base?: any,
2063
+ options?: {
2064
+ query?: Record<string, any>
2065
+ modify?: (query: QueryBuilder<$Model>) => QueryBuilder<$Model>
2066
+ forUpdate?: boolean
2067
+ }
2068
+ ): Promise<$Model | null>
2069
+
2070
+ /**
2071
+ * Executes a controller action within a transaction
2072
+ * context.
2073
+ */
2074
+ execute(
2075
+ ctx: KoaContext,
2076
+ execute: (
2077
+ query: QueryBuilder<$Model>,
2078
+ trx?: objection.Transaction
2079
+ ) => any
2080
+ ): Promise<any>
2081
+
2082
+ /**
2083
+ * Extracts model IDs from the request body collection,
2084
+ * validating each ID.
2085
+ */
2086
+ getCollectionIds(ctx: KoaContext): Array<Id | Id[]>
2087
+ /**
2088
+ * Returns relevant IDs for the current request: from
2089
+ * route params for member requests, from body for
2090
+ * collection requests.
2091
+ */
2092
+ getIds(ctx: KoaContext): Array<Id | Id[]>
2093
+ /**
2094
+ * Returns the request context extended with a memberId
2095
+ * property.
2096
+ */
2097
+ getContextWithMemberId(
2098
+ ctx: KoaContext,
2099
+ memberId?: Id | Id[]
2100
+ ): KoaContext
2101
+
2102
+ /**
2103
+ * Validates and coerces a model ID using the model
2104
+ * class's reference mechanism.
2105
+ */
2106
+ validateId(id: any): Id | Id[]
2107
+
2108
+ /**
2109
+ * Executes an insert/update/patch action and fetches
2110
+ * the result. Supports both normal and DitoGraph modes.
2111
+ */
2112
+ executeAndFetch(
2113
+ action: string,
2114
+ ctx: KoaContext,
2115
+ modify?: (
2116
+ query: QueryBuilder<$Model>,
2117
+ trx?: objection.Transaction
2118
+ ) => void,
2119
+ body?: Record<string, any>
2120
+ ): Promise<$Model>
2121
+
2122
+ /**
2123
+ * Executes a by-id mutation action and fetches the
2124
+ * result. Throws NotFoundError if not found.
2125
+ */
2126
+ executeAndFetchById(
2127
+ action: string,
2128
+ ctx: KoaContext,
2129
+ modify?: (
2130
+ query: QueryBuilder<$Model>,
2131
+ trx?: objection.Transaction
2132
+ ) => void,
2133
+ body?: Record<string, any>
2134
+ ): Promise<$Model>
2135
+ }
2136
+
2137
+ /**
2138
+ * Controller for a top-level model resource. Extends
2139
+ * CollectionController with relation and asset setup.
2140
+ */
2141
+ export class ModelController<
2142
+ $Model extends Model = Model
2143
+ > extends CollectionController<$Model> {
2144
+ /**
2145
+ * The model class this controller represents. If not
2146
+ * provided, the singularized controller name is used
2147
+ * to look up the model class in models registered with
2148
+ * the application.
2149
+ */
2150
+ modelClass?: Class<$Model>
2151
+ /**
2152
+ * The controller's collection actions. Wrap actions in
2153
+ * this object to assign them to the collection.
2154
+ */
2155
+ collection?: ModelControllerActions<ModelController<$Model>>
2156
+ /**
2157
+ * The controller's member actions. Wrap actions in this
2158
+ * object to assign them to the member.
2159
+ */
2160
+ member?: ModelControllerMemberActions<ModelController<$Model>>
2161
+ assets?:
2162
+ | boolean
2163
+ | {
2164
+ allow?: OrArrayOf<string>
2165
+ authorize: Record<string, OrArrayOf<string>>
2166
+ }
2167
+
2168
+ /**
2169
+ * When nothing is returned from a hook, the standard
2170
+ * action result is used.
2171
+ */
2172
+ hooks?: ModelControllerHooks<ModelController<$Model>>
2173
+ /** Map of relation name to RelationController instance. */
2174
+ relations?: Record<string, RelationController>
2175
+ }
2176
+
2177
+ /**
2178
+ * Controller for nested relation resources. Created
2179
+ * automatically by ModelController during relation setup.
2180
+ */
2181
+ export class RelationController<
2182
+ $Model extends Model = Model
2183
+ > extends CollectionController<$Model> {
2184
+ /** The parent controller that owns this relation. */
2185
+ parent: CollectionController
2186
+
2187
+ /** The raw relation definition object. */
2188
+ object: Record<string, unknown>
2189
+
2190
+ /** The Objection.js relation instance. */
2191
+ relationInstance: objection.Relation
2192
+
2193
+ /** The raw relation definition from the parent. */
2194
+ relationDefinition: Record<string, unknown>
2195
+
2196
+ /** Whether this is a one-to-one relation. */
2197
+ isOneToOne: boolean
2198
+ /** Whether relate operations are supported. */
2199
+ relate: boolean
2200
+ /** Whether unrelate operations are supported. */
2201
+ unrelate: boolean
2202
+ }
2203
+
2204
+ export class Validator extends objection.Validator {
2205
+ constructor(options?: {
2206
+ options?: {
2207
+ /** @defaultValue `false` */
2208
+ async?: boolean
2209
+ /** @defaultValue `false` */
2210
+ patch?: boolean
2211
+ /** @defaultValue `false` */
2212
+ $data?: boolean
2213
+ /** @defaultValue `false` */
2214
+ $comment?: boolean
2215
+ /** @defaultValue `false` */
2216
+ coerceTypes?: boolean
2217
+ /** @defaultValue `false` */
2218
+ multipleOfPrecision?: boolean
2219
+ /** @defaultValue `true` */
2220
+ ownProperties?: boolean
2221
+ /** @defaultValue `false` */
2222
+ removeAdditional?: boolean
2223
+ /** @defaultValue `true` */
2224
+ uniqueItems?: boolean
2225
+ /** @defaultValue `true` */
2226
+ useDefaults?: boolean
2227
+ /** @defaultValue `false` */
2228
+ verbose?: boolean
2229
+ }
2230
+ keywords?: Record<string, Keyword>
2231
+ formats?: Record<string, Format>
2232
+ types?: Record<string, any>
2233
+ })
2234
+
2235
+ /**
2236
+ * Compiles a JSON schema into a validation function.
2237
+ * Supports sync, async, and throwing modes via options.
2238
+ */
2239
+ compile(
2240
+ jsonSchema: Schema,
2241
+ options?: Record<string, any>
2242
+ ): Ajv.ValidateFunction
2243
+
2244
+ /** Adds a schema to all cached Ajv instances. */
2245
+ addSchema(jsonSchema: Schema): void
2246
+
2247
+ /**
2248
+ * Returns a cached Ajv instance for the given options,
2249
+ * creating one if needed.
2250
+ */
2251
+ getAjv(options?: Record<string, any>): Ajv.default
2252
+
2253
+ /** Creates a new Ajv instance with the given options. */
2254
+ createAjv(options?: Record<string, any>): Ajv.default
2255
+
2256
+ /**
2257
+ * Converts Ajv validation errors into an Objection.js
2258
+ * style error hash.
2259
+ */
2260
+ parseErrors(
2261
+ errors: Ajv.ErrorObject[],
2262
+ options?: Record<string, any>
2263
+ ): Record<string, any[]>
2264
+
2265
+ /**
2266
+ * Processes a JSON schema for patch or async validation
2267
+ * modes.
2268
+ */
2269
+ processSchema(
2270
+ jsonSchema: Schema,
2271
+ options?: Record<string, any>
2272
+ ): Schema
2273
+
2274
+ /** Returns a registered keyword definition by name. */
2275
+ getKeyword(name: string): Keyword | undefined
2276
+
2277
+ /** Returns a registered format definition by name. */
2278
+ getFormat(name: string): Format | undefined
2279
+
2280
+ /** Prefixes error instance paths with a given prefix. */
2281
+ prefixInstancePaths(
2282
+ errors: Ajv.ErrorObject[],
2283
+ prefix: string
2284
+ ): Ajv.ErrorObject[]
2285
+ }
2286
+
2287
+ // NOTE: Because EventEmitter overrides a number of EventEmitter2 methods with
2288
+ // changed signatures, we are unable to extend it.
2289
+ export class EventEmitter {
2290
+ static mixin: (target: any) => void
2291
+ constructor(options?: EventEmitter2.ConstructorOptions)
2292
+ emit(
2293
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2294
+ ...values: any[]
2295
+ ): Promise<any[]>
2296
+
2297
+ on(
2298
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2299
+ listener: EventEmitter2.ListenerFn
2300
+ ): this
2301
+
2302
+ on(
2303
+ event: string[],
2304
+ listener: EventEmitter2.ListenerFn
2305
+ ): this
2306
+
2307
+ on(
2308
+ event: Record<string, EventEmitter2.ListenerFn>
2309
+ ): this
2310
+
2311
+ off(
2312
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2313
+ listener: EventEmitter2.ListenerFn
2314
+ ): this
2315
+
2316
+ off(
2317
+ event: string[],
2318
+ listener: EventEmitter2.ListenerFn
2319
+ ): this
2320
+
2321
+ off(
2322
+ event: Record<string, EventEmitter2.ListenerFn>
2323
+ ): this
2324
+
2325
+ once(
2326
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2327
+ listener: EventEmitter2.ListenerFn
2328
+ ): this
2329
+
2330
+ once(
2331
+ event: string[],
2332
+ listener: EventEmitter2.ListenerFn
2333
+ ): this
2334
+
2335
+ once(
2336
+ event: Record<string, EventEmitter2.ListenerFn>
2337
+ ): this
2338
+
2339
+ // From EventEmitter2:
2340
+ emitAsync(
2341
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2342
+ ...values: any[]
2343
+ ): Promise<any[]>
2344
+
2345
+ addListener(
2346
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2347
+ listener: EventEmitter2.ListenerFn
2348
+ ): this | EventEmitter2.Listener
2349
+
2350
+ prependListener(
2351
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2352
+ listener: EventEmitter2.ListenerFn,
2353
+ options?: boolean | EventEmitter2.OnOptions
2354
+ ): this | EventEmitter2.Listener
2355
+
2356
+ prependOnceListener(
2357
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2358
+ listener: EventEmitter2.ListenerFn,
2359
+ options?: boolean | EventEmitter2.OnOptions
2360
+ ): this | EventEmitter2.Listener
2361
+
2362
+ many(
2363
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2364
+ timesToListen: number,
2365
+ listener: EventEmitter2.ListenerFn,
2366
+ options?: boolean | EventEmitter2.OnOptions
2367
+ ): this | EventEmitter2.Listener
2368
+
2369
+ prependMany(
2370
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2371
+ timesToListen: number,
2372
+ listener: EventEmitter2.ListenerFn,
2373
+ options?: boolean | EventEmitter2.OnOptions
2374
+ ): this | EventEmitter2.Listener
2375
+
2376
+ onAny(listener: EventEmitter2.EventAndListener): this
2377
+ prependAny(listener: EventEmitter2.EventAndListener): this
2378
+ offAny(listener: EventEmitter2.ListenerFn): this
2379
+ removeListener(
2380
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2381
+ listener: EventEmitter2.ListenerFn
2382
+ ): this
2383
+
2384
+ removeAllListeners(event?: EventEmitter2.event | EventEmitter2.eventNS): this
2385
+ setMaxListeners(n: number): void
2386
+ getMaxListeners(): number
2387
+ eventNames(
2388
+ nsAsArray?: boolean
2389
+ ): (EventEmitter2.event | EventEmitter2.eventNS)[]
2390
+
2391
+ listenerCount(event?: EventEmitter2.event | EventEmitter2.eventNS): number
2392
+ listeners(
2393
+ event?: EventEmitter2.event | EventEmitter2.eventNS
2394
+ ): EventEmitter2.ListenerFn[]
2395
+
2396
+ listenersAny(): EventEmitter2.ListenerFn[]
2397
+ waitFor(
2398
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2399
+ timeout?: number
2400
+ ): EventEmitter2.CancelablePromise<any[]>
2401
+
2402
+ waitFor(
2403
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2404
+ filter?: EventEmitter2.WaitForFilter
2405
+ ): EventEmitter2.CancelablePromise<any[]>
2406
+
2407
+ waitFor(
2408
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2409
+ options?: EventEmitter2.WaitForOptions
2410
+ ): EventEmitter2.CancelablePromise<any[]>
2411
+
2412
+ listenTo(
2413
+ target: EventEmitter2.GeneralEventEmitter,
2414
+ events: EventEmitter2.event | EventEmitter2.eventNS,
2415
+ options?: EventEmitter2.ListenToOptions
2416
+ ): this
2417
+
2418
+ listenTo(
2419
+ target: EventEmitter2.GeneralEventEmitter,
2420
+ events: EventEmitter2.event[],
2421
+ options?: EventEmitter2.ListenToOptions
2422
+ ): this
2423
+
2424
+ listenTo(
2425
+ target: EventEmitter2.GeneralEventEmitter,
2426
+ events: Object,
2427
+ options?: EventEmitter2.ListenToOptions
2428
+ ): this
2429
+
2430
+ stopListeningTo(
2431
+ target?: EventEmitter2.GeneralEventEmitter,
2432
+ event?: EventEmitter2.event | EventEmitter2.eventNS
2433
+ ): boolean
2434
+
2435
+ hasListeners(event?: string): boolean
2436
+ static once(
2437
+ emitter: EventEmitter2.EventEmitter2,
2438
+ event: EventEmitter2.event | EventEmitter2.eventNS,
2439
+ options?: EventEmitter2.OnceOptions
2440
+ ): EventEmitter2.CancelablePromise<any[]>
2441
+
2442
+ static defaultMaxListeners: number
2443
+ }
2444
+
2445
+ /**
2446
+ * Options for Dito.js graph operations (`insertDitoGraph`,
2447
+ * `upsertDitoGraph`, `updateDitoGraph`, `patchDitoGraph`).
2448
+ * Controls how nested relation graphs are persisted.
2449
+ */
2450
+ export interface DitoGraphOptions {
2451
+ /**
2452
+ * Strategy for fetching existing data before upserting.
2453
+ *
2454
+ * - `'OnlyNeeded'`: Fetch only data needed to determine
2455
+ * changes.
2456
+ * - `'OnlyIdentifiers'`: Fetch only IDs for comparison.
2457
+ * - `'Everything'`: Fetch all existing graph data.
2458
+ */
2459
+ fetchStrategy?: 'OnlyNeeded' | 'OnlyIdentifiers' | 'Everything'
2460
+ /**
2461
+ * Whether to relate existing models found in the graph
2462
+ * instead of inserting new ones.
2463
+ */
2464
+ relate?: boolean
2465
+ /** Whether to allow `#ref` references in the graph. */
2466
+ allowRefs?: boolean
2467
+ /**
2468
+ * Whether to insert models that don't exist in the
2469
+ * database yet during an upsert.
2470
+ */
2471
+ insertMissing?: boolean
2472
+ /**
2473
+ * Whether to unrelate models removed from the graph
2474
+ * (sets the foreign key to `null` instead of deleting).
2475
+ */
2476
+ unrelate?: boolean
2477
+ /**
2478
+ * Whether to update existing models found in the graph
2479
+ * (as opposed to only inserting new ones).
2480
+ */
2481
+ update?: boolean
2482
+ /**
2483
+ * Enables special handling for cyclic graph upserts,
2484
+ * where self-referential relations are broken into two
2485
+ * phases.
2486
+ */
2487
+ cyclic?: boolean
2488
+ }
2489
+
2490
+ export type QueryParameterOptions = {
2491
+ scope?: OrArrayOf<string>
2492
+ filter?: OrArrayOf<string>
2493
+ /**
2494
+ * A range between two numbers. When expressed as a string, the value is split
2495
+ * at the ',' character ignoring any spaces on either side. i.e. `'1,2'` and
2496
+ * `'1 , 2'`
2497
+ */
2498
+ range?: [number, number] | string
2499
+ limit?: number
2500
+ offset?: number
2501
+ order?: OrArrayOf<string>
2502
+ }
2503
+ export type QueryParameterOptionKey = keyof QueryParameterOptions
2504
+
2505
+ export class Service {
2506
+ constructor(app: Application<Models>, name?: string)
2507
+
2508
+ /** The application instance. */
2509
+ app: Application<Models>
2510
+ /** The camelized service name. */
2511
+ name: string
2512
+ /** The service configuration. */
2513
+ config: Record<string, unknown> | null
2514
+ /** Whether this service has been initialized. */
2515
+ initialized: boolean
2516
+
2517
+ setup(config: Record<string, unknown>): void
2518
+
2519
+ /**
2520
+ * Override in sub-classes if the service needs async
2521
+ * initialization.
2522
+ * @overridable
2523
+ */
2524
+ initialize(): Promise<void>
2525
+
2526
+ /** @overridable */
2527
+ start(): Promise<void>
2528
+
2529
+ /** @overridable */
2530
+ stop(): Promise<void>
2531
+
2532
+ get logger(): PinoLogger
2533
+ }
2534
+ export type Services = Record<string, Class<Service> | Service>
2535
+
2536
+ export class QueryBuilder<
2537
+ M extends Model,
2538
+ R = M[]
2539
+ > extends objection.QueryBuilder<M, R> {
2540
+ /** Clones the query with scope/filter state. */
2541
+ clone(): QueryBuilder<M, R>
2542
+ /**
2543
+ * Inherits scopes from a parent query.
2544
+ * @override
2545
+ */
2546
+ childQueryOf(
2547
+ query: QueryBuilder<Model>,
2548
+ options?: Record<string, any>
2549
+ ): this
2550
+
2551
+ /**
2552
+ * Creates a find-only copy of this query (clears
2553
+ * `runAfter` callbacks).
2554
+ * @override
2555
+ */
2556
+ toFindQuery(): QueryBuilder<M, M[]>
2557
+
2558
+ /**
2559
+ * Returns true if the query defines normal selects: select(), column(),
2560
+ * columns()
2561
+ */
2562
+ hasNormalSelects(): boolean
2563
+ /**
2564
+ * Returns true if the query defines special selects:
2565
+ * distinct(), count(), countDistinct(), min(), max(),
2566
+ * sum(), sumDistinct(), avg(), avgDistinct()
2567
+ */
2568
+ hasSpecialSelects(): boolean
2569
+ withScope(...scopes: string[]): this
2570
+ /**
2571
+ * Clear all scopes defined with `withScope()` statements,
2572
+ * preserving the default scope.
2573
+ */
2574
+ clearWithScope(): this
2575
+ ignoreScope(...scopes: string[]): this
2576
+ applyScope(...scopes: string[]): this
2577
+ allowScope(...scopes: string[]): void
2578
+ clearAllowScope(): void
2579
+ applyFilter(name: string, ...args: unknown[]): this
2580
+ applyFilter(filters: { [name: string]: unknown[] }): this
2581
+ allowFilter(...filters: string[]): void
2582
+ /** Omits properties from the query result. */
2583
+ omit(...properties: string[]): void
2584
+ withGraph(
2585
+ expr: objection.RelationExpression<M>,
2586
+ options?: objection.GraphOptions & {
2587
+ algorithm?: 'fetch' | 'join'
2588
+ }
2589
+ ): this
2590
+
2591
+ toSQL(): { sql: string; bindings: unknown[] }
2592
+ raw: Knex.RawBuilder
2593
+ selectRaw: SetReturnType<Knex.RawBuilder, this>
2594
+ pluck(key: string): this
2595
+ loadDataPath(
2596
+ dataPath: string[] | string,
2597
+ options?: objection.GraphOptions & {
2598
+ algorithm?: 'fetch' | 'join'
2599
+ }
2600
+ ): this
2601
+
2602
+ upsert(
2603
+ data: PartialModelObject<M>,
2604
+ options?: {
2605
+ update?: boolean
2606
+ fetch?: boolean
2607
+ }
2608
+ ): this
2609
+
2610
+ find(
2611
+ query: QueryParameterOptions,
2612
+ allowParam?:
2613
+ | QueryParameterOptionKey[]
2614
+ | {
2615
+ [key in QueryParameterOptionKey]?: boolean
2616
+ }
2617
+ ): this
2618
+
2619
+ patchById(id: Id, data: PartialModelObject<M>): this
2620
+ updateById(id: Id, data: PartialModelObject<M>): this
2621
+ upsertAndFetch(data: PartialModelObject<M>): this
2622
+ insertDitoGraph(
2623
+ data: PartialDitoModelGraph<M>,
2624
+ options?: DitoGraphOptions
2625
+ ): this
2626
+
2627
+ insertDitoGraphAndFetch(
2628
+ data: PartialDitoModelGraph<M>,
2629
+ options?: DitoGraphOptions
2630
+ ): this
2631
+
2632
+ upsertDitoGraph(
2633
+ data: PartialDitoModelGraph<M>,
2634
+ options?: DitoGraphOptions
2635
+ ): this
2636
+
2637
+ upsertDitoGraphAndFetch(
2638
+ data: PartialDitoModelGraph<M>,
2639
+ options?: DitoGraphOptions
2640
+ ): this
2641
+
2642
+ upsertDitoGraphAndFetchById(
2643
+ id: Id,
2644
+ data: PartialDitoModelGraph<M>,
2645
+ options?: DitoGraphOptions
2646
+ ): this
2647
+
2648
+ updateDitoGraph(
2649
+ data: PartialDitoModelGraph<M>,
2650
+ options?: DitoGraphOptions
2651
+ ): this
2652
+
2653
+ updateDitoGraphAndFetch(
2654
+ data: PartialDitoModelGraph<M>,
2655
+ options?: DitoGraphOptions
2656
+ ): this
2657
+
2658
+ updateDitoGraphAndFetchById(
2659
+ id: Id,
2660
+ data: PartialDitoModelGraph<M>,
2661
+ options?: DitoGraphOptions
2662
+ ): this
2663
+
2664
+ patchDitoGraph(
2665
+ data: PartialDitoModelGraph<M>,
2666
+ options?: DitoGraphOptions
2667
+ ): this
2668
+
2669
+ patchDitoGraphAndFetch(
2670
+ data: PartialDitoModelGraph<M>,
2671
+ options?: DitoGraphOptions
2672
+ ): this
2673
+
2674
+ patchDitoGraphAndFetchById(
2675
+ id: Id,
2676
+ data: PartialDitoModelGraph<M>,
2677
+ options?: DitoGraphOptions
2678
+ ): this
2679
+
2680
+ truncate(options?: { restart?: boolean; cascade?: boolean }): this
2681
+
2682
+ ArrayQueryBuilderType: QueryBuilder<M, M[]>
2683
+ SingleQueryBuilderType: QueryBuilder<M, M>
2684
+ NumberQueryBuilderType: QueryBuilder<M, number>
2685
+ PageQueryBuilderType: QueryBuilder<M, objection.Page<M>>
2686
+ MaybeSingleQueryBuilderType: QueryBuilder<M, M | undefined>
2687
+ }
2688
+ export interface QueryBuilder<M extends Model, R = M[]> extends KnexHelper {}
2689
+
2690
+ /**
2691
+ * Registry of built-in query parameter handlers (scope,
2692
+ * filter, range, limit, offset, order). Used by
2693
+ * `QueryBuilder.find()` to apply URL query parameters.
2694
+ */
2695
+ type QueryParameterHandler = (
2696
+ query: QueryBuilder<Model>,
2697
+ key: string,
2698
+ value: unknown
2699
+ ) => void
2700
+
2701
+ export const QueryParameters: {
2702
+ register(name: string, handler: QueryParameterHandler): void
2703
+ register(
2704
+ handlers: Record<string, QueryParameterHandler>
2705
+ ): void
2706
+ get(name: string): QueryParameterHandler | undefined
2707
+ has(name: string): boolean
2708
+ getAllowed(): Record<string, boolean>
2709
+ }
2710
+
2711
+ export type QueryFilterDefinition = {
2712
+ parameters?: Record<string, Schema>
2713
+ handler: (
2714
+ query: QueryBuilder<Model>,
2715
+ property: string,
2716
+ ...args: unknown[]
2717
+ ) => void
2718
+ }
2719
+
2720
+ /**
2721
+ * Registry of built-in query filters (text, date-range).
2722
+ * Provides reusable filter implementations that can be
2723
+ * referenced by name in model filter definitions.
2724
+ */
2725
+ export const QueryFilters: {
2726
+ register(name: string, definition: QueryFilterDefinition): void
2727
+ register(
2728
+ definitions: Record<string, QueryFilterDefinition>
2729
+ ): void
2730
+ get(name: string): QueryFilterDefinition | undefined
2731
+ has(name: string): boolean
2732
+ getAllowed(): Record<string, boolean>
2733
+ }
2734
+
2735
+ export type PartialModelObject<T extends Model> = {
2736
+ [K in objection.NonFunctionPropertyNames<T>]?: objection.Defined<
2737
+ T[K]
2738
+ > extends Model
2739
+ ? T[K]
2740
+ : objection.Defined<T[K]> extends Array<infer I>
2741
+ ? I extends Model
2742
+ ? I[]
2743
+ : objection.Expression<T[K]>
2744
+ : objection.Expression<T[K]>
2745
+ }
2746
+
2747
+ export type PartialDitoModelGraph<M extends Partial<Model>> = {
2748
+ [K in objection.NonFunctionPropertyNames<M>]?: objection.Defined<
2749
+ M[K]
2750
+ > extends Model
2751
+ ? PartialDitoModelGraph<M[K]>
2752
+ : objection.Defined<M[K]> extends Array<infer I>
2753
+ ? I extends Partial<Model>
2754
+ ? PartialDitoModelGraph<I>[]
2755
+ : M[K]
2756
+ : M[K]
2757
+ }
2758
+
2759
+ /* ------------------------------ Start Errors ----------------------------- */
2760
+ export class ResponseError extends Error {
2761
+ constructor()
2762
+ constructor(
2763
+ error:
2764
+ | {
2765
+ /** The http status code. */
2766
+ status: number
2767
+ /** The error message. */
2768
+ message?: string
2769
+ /**
2770
+ * An optional code to be used to distinguish
2771
+ * different error instances.
2772
+ */
2773
+ code?: string | number
2774
+ }
2775
+ | Error
2776
+ | string,
2777
+ defaults?: { message?: string; status?: number },
2778
+ overrides?: Record<string, unknown>
2779
+ )
2780
+
2781
+ status: number
2782
+ code?: string | number
2783
+ /** Additional error data. */
2784
+ data?: Record<string, unknown>
2785
+ toJSON(): Record<string, unknown>
2786
+ }
2787
+ export class AssetError extends ResponseError {}
2788
+ export class AuthenticationError extends ResponseError {}
2789
+ export class AuthorizationError extends ResponseError {}
2790
+ export class DatabaseError extends ResponseError {
2791
+ constructor(
2792
+ error:
2793
+ | dbErrors.CheckViolationError
2794
+ | dbErrors.NotNullViolationError
2795
+ | dbErrors.ConstraintViolationError
2796
+ | dbErrors.DataError
2797
+ | dbErrors.DBError,
2798
+ overrides?: Record<string, unknown>
2799
+ )
2800
+ }
2801
+ export class GraphError extends ResponseError {}
2802
+ export class ModelError extends ResponseError {
2803
+ constructor(model: Class<Model> | Model, error?: unknown)
2804
+ }
2805
+ export class NotFoundError extends ResponseError {}
2806
+ export class NotImplementedError extends ResponseError {}
2807
+ export class QueryBuilderError extends ResponseError {}
2808
+ export class RelationError extends ResponseError {}
2809
+ export class ValidationError extends ResponseError {}
2810
+ export class ControllerError extends ResponseError {
2811
+ constructor(
2812
+ controller:
2813
+ | Function
2814
+ | { constructor: { name: string } },
2815
+ error?: unknown
2816
+ )
2817
+ }
2818
+ /* ------------------------------- End Errors ------------------------------ */
2819
+
2820
+ /* ----------------------------- Start Storage ----------------------------- */
2821
+ /**
2822
+ * Base class for file storage backends. Subclasses handle
2823
+ * disk and S3 storage.
2824
+ */
2825
+ export class Storage {
2826
+ constructor(app: Application<Models>, config: StorageConfig)
2827
+ /** The application instance. */
2828
+ app: Application<Models>
2829
+ /** The storage configuration. */
2830
+ config: StorageConfig
2831
+ /** The storage name. */
2832
+ name: string
2833
+ /** The base URL for accessing stored files. */
2834
+ url?: string
2835
+ /** The file system path for disk storage. */
2836
+ path?: string
2837
+ /**
2838
+ * Upload concurrency limit.
2839
+ *
2840
+ * @defaultValue `8`
2841
+ */
2842
+ concurrency: number
2843
+ /** Whether this storage has been initialized. */
2844
+ initialized: boolean
2845
+
2846
+ /** Sets up the storage backend. */
2847
+ setup(): Promise<void>
2848
+ /**
2849
+ * Override in sub-classes for async initialization.
2850
+ * @overridable
2851
+ */
2852
+ initialize(): Promise<void>
2853
+ /**
2854
+ * Returns a multer-compatible storage object for handling
2855
+ * uploads, or null if no underlying storage is configured.
2856
+ */
2857
+ getUploadStorage(
2858
+ config: multer.Options
2859
+ ): multer.StorageEngine | null
2860
+
2861
+ /** Returns a multer upload handler for this storage. */
2862
+ getUploadHandler(
2863
+ config: multer.Options
2864
+ ): Koa.Middleware | null
2865
+
2866
+ /**
2867
+ * Generates a unique storage key from a filename,
2868
+ * combining a UUID with the file extension.
2869
+ */
2870
+ getUniqueKey(name: string): string
2871
+ /**
2872
+ * Checks whether the given URL is allowed as an import
2873
+ * source based on `config.allowedImports`.
2874
+ */
2875
+ isImportSourceAllowed(url: string): boolean
2876
+ /** Adds a file to storage. */
2877
+ addFile(file: AssetFile, data: Buffer): Promise<AssetFile>
2878
+ /** Removes a file from storage. */
2879
+ removeFile(file: AssetFile): Promise<void>
2880
+ /** Reads a file's contents from storage. */
2881
+ readFile(file: AssetFile): Promise<Buffer>
2882
+ /** Lists all keys in the storage. */
2883
+ listKeys(): Promise<string[]>
2884
+ /** Returns the file system path for a file, if any. */
2885
+ getFilePath(file: AssetFile): string | undefined
2886
+ /** Returns the public URL for a file, if any. */
2887
+ getFileUrl(file: AssetFile): string | undefined
2888
+
2889
+ /**
2890
+ * Converts a multer upload object to the internal file
2891
+ * format.
2892
+ */
2893
+ convertStorageFile(
2894
+ storageFile: StorageFile
2895
+ ): AssetFileObject
2896
+
2897
+ /**
2898
+ * Converts an array of multer upload objects to the
2899
+ * internal file format.
2900
+ */
2901
+ convertStorageFiles(
2902
+ storageFiles: StorageFile[]
2903
+ ): AssetFileObject[]
2904
+
2905
+ /**
2906
+ * Converts a plain file object into an AssetFile
2907
+ * instance in-place on this storage.
2908
+ */
2909
+ convertAssetFile(file: AssetFileObject): void
2910
+
2911
+ /** Registers a storage subclass by type name. */
2912
+ static register(storageClass: Class<Storage>): void
2913
+ /** Retrieves a registered storage class by type name. */
2914
+ static get(type: string): Class<Storage> | null
2915
+ }
2916
+
2917
+ /**
2918
+ * Represents a file asset with metadata. Created from
2919
+ * uploaded files or imported URLs.
2920
+ */
2921
+ export class AssetFile {
2922
+ constructor(options: {
2923
+ name: string
2924
+ data: string | Buffer
2925
+ type?: string
2926
+ width?: number
2927
+ height?: number
2928
+ })
2929
+
2930
+ /** Unique storage key (UUID + extension). */
2931
+ key: string
2932
+ /** The original filename. */
2933
+ name: string
2934
+ /** The file's MIME type, set from options or detected from data. */
2935
+ type: string | undefined
2936
+ /** File size in bytes. */
2937
+ size: number
2938
+ /** Image width, if dimensions were read. */
2939
+ width?: number
2940
+ /** Image height, if dimensions were read. */
2941
+ height?: number
2942
+ /** The public URL for this file, set after storage upload. */
2943
+ url?: string
2944
+ /** The file data buffer. */
2945
+ get data(): Buffer | null
2946
+ /** The storage instance this file belongs to. */
2947
+ get storage(): Storage | null
2948
+ /** The file system path, if stored on disk. */
2949
+ get path(): string | undefined
2950
+ /** Reads the file's contents from storage. */
2951
+ read(): Promise<Buffer | null>
2952
+
2953
+ /**
2954
+ * Converts a plain object into an AssetFile instance
2955
+ * in-place on the given storage.
2956
+ */
2957
+ static convert(
2958
+ object: Record<string, any>,
2959
+ storage: Storage
2960
+ ): void
2961
+
2962
+ /** Creates a new AssetFile from the given options. */
2963
+ static create(options: {
2964
+ name: string
2965
+ data: string | Buffer
2966
+ type?: string
2967
+ width?: number
2968
+ height?: number
2969
+ }): AssetFile
2970
+
2971
+ /**
2972
+ * Generates a unique storage key for a filename,
2973
+ * combining a UUID with the file extension.
2974
+ */
2975
+ static getUniqueKey(name: string): string
2976
+ }
2977
+
2978
+ export interface StorageFile extends multer.File {
2979
+ key: string
2980
+ width?: number
2981
+ height?: number
2982
+ }
2983
+
2984
+ export type AssetFileObject = {
2985
+ // The unique key within the storage (uuid/v4 + file extension)
2986
+ key: string
2987
+ // The original filename
2988
+ name: string
2989
+ // The file's mime-type
2990
+ type: string
2991
+ // The amount of bytes consumed by the file
2992
+ size: number
2993
+ // The public url of the file
2994
+ url: string
2995
+ // The width of the image if the storage defines `config.readDimensions`
2996
+ width?: number
2997
+ // The height of the image if the storage defines `config.readDimensions`
2998
+ height?: number
2999
+ }
3000
+ /* ------------------------------ End Storage ------------------------------ */
3001
+
3002
+ /* ------------------------------ Start Mixins ----------------------------- */
3003
+
3004
+ export const AssetMixin: <T extends Constructor<{}>>(
3005
+ target: T
3006
+ ) => T &
3007
+ Constructor<{
3008
+ key: string
3009
+ file: AssetFileObject
3010
+ storage: string
3011
+ count: number
3012
+ createdAt: Date
3013
+ updatedAt: Date
3014
+ $parseJson(
3015
+ json: object,
3016
+ opt?: ModelOptions
3017
+ ): object
3018
+ }>
3019
+
3020
+ export const AssetModel: ReturnType<typeof AssetMixin<typeof Model>>
3021
+
3022
+ export const TimeStampedMixin: <T extends Constructor<{}>>(
3023
+ target: T
3024
+ ) => T &
3025
+ Constructor<{
3026
+ createdAt: Date
3027
+ updatedAt: Date
3028
+ }>
3029
+
3030
+ export const TimeStampedModel: ReturnType<typeof TimeStampedMixin<typeof Model>>
3031
+
3032
+ export const SessionMixin: <T extends Constructor<{}>>(
3033
+ target: T
3034
+ ) => T &
3035
+ Constructor<{
3036
+ id: string
3037
+ value: Record<string, unknown>
3038
+ }>
3039
+
3040
+ export const SessionModel: ReturnType<typeof SessionMixin<typeof Model>>
3041
+
3042
+ export const UserMixin: <T extends Constructor<{}>>(
3043
+ target: T
3044
+ ) => T &
3045
+ Constructor<{
3046
+ username: string
3047
+ password: string
3048
+ hash: string
3049
+ lastLogin?: Date
3050
+
3051
+ $verifyPassword(password: string): Promise<boolean>
3052
+
3053
+ $hasRole(...roles: string[]): boolean
3054
+
3055
+ $hasOwner(owner: InstanceType<typeof UserModel>): boolean
3056
+
3057
+ $isLoggedIn(ctx: KoaContext): boolean
3058
+ }> & {
3059
+ options?: {
3060
+ usernameProperty?: string
3061
+ passwordProperty?: string
3062
+ /**
3063
+ * This option can be used to specify (eager) scopes to be applied when
3064
+ * the user is deserialized from the session.
3065
+ */
3066
+ sessionScope?: OrArrayOf<string>
3067
+ }
3068
+
3069
+ /** Registers the passport strategy for this user class. */
3070
+ setup(): void
3071
+
3072
+ /** Authenticates a user via Passport. */
3073
+ login(
3074
+ ctx: KoaContext,
3075
+ options?: Record<string, unknown>
3076
+ ): Promise<InstanceType<typeof UserModel>>
3077
+
3078
+ sessionQuery(
3079
+ trx: Knex.Transaction
3080
+ ): QueryBuilder<InstanceType<typeof UserModel>>
3081
+ }
3082
+
3083
+ export const UserModel: ReturnType<typeof UserMixin<typeof Model>>
3084
+
3085
+ /* ------------------------------ End Mixins ----------------------------- */
3086
+
3087
+ export type HTTPMethod =
3088
+ | 'get'
3089
+ | 'head'
3090
+ | 'post'
3091
+ | 'put'
3092
+ | 'delete'
3093
+ | 'patch'
3094
+ | 'options'
3095
+ | 'trace'
3096
+ | 'connect'
3097
+
3098
+ export interface KnexHelper {
3099
+ getDialect(): string | null
3100
+
3101
+ isPostgreSQL(): boolean
3102
+
3103
+ isMySQL(): boolean
3104
+
3105
+ isSQLite(): boolean
3106
+
3107
+ isMsSQL(): boolean
3108
+ }
3109
+
3110
+ export function convertSchema(
3111
+ schema: Schema,
3112
+ options?: Record<string, any>,
3113
+ parentEntry?: Record<string, any> | null
3114
+ ): Record<string, any>
3115
+
3116
+ export function convertRelations(
3117
+ ownerModelClass: Class<Model>,
3118
+ relations: ModelRelations,
3119
+ models: Models
3120
+ ): Record<string, any>
3121
+
3122
+ export function convertRelation(
3123
+ schema: ModelRelation,
3124
+ models: Models
3125
+ ): Record<string, any>
3126
+
3127
+ export function getRelationClass(
3128
+ relation: string | typeof objection.Relation
3129
+ ): typeof objection.Relation | null
3130
+
3131
+ export function isThroughRelationClass(
3132
+ relationClass: typeof objection.Relation
3133
+ ): boolean
3134
+
3135
+ export function addRelationSchemas(
3136
+ modelClass: Class<Model>,
3137
+ properties: Record<string, ModelProperty>
3138
+ ): void
3139
+
3140
+ export type Keyword =
3141
+ | SetOptional<Ajv.MacroKeywordDefinition, 'keyword'>
3142
+ | SetOptional<Ajv.CodeKeywordDefinition, 'keyword'>
3143
+ | SetOptional<Ajv.FuncKeywordDefinition, 'keyword'>
3144
+ export type Format = Ajv.ValidateFunction | Ajv.FormatDefinition<string>
3145
+
3146
+ /** Built-in AJV keyword definitions. */
3147
+ export const keywords: {
3148
+ specificType: Keyword
3149
+ primary: Keyword
3150
+ foreign: Keyword
3151
+ unique: Keyword
3152
+ index: Keyword
3153
+ computed: Keyword
3154
+ hidden: Keyword
3155
+ unsigned: Keyword
3156
+ _instanceof: Keyword
3157
+ validate: Keyword
3158
+ validateAsync: Keyword
3159
+ relate: Keyword
3160
+ range: Keyword
3161
+ }
3162
+
3163
+ /** Built-in AJV format definitions. */
3164
+ export const formats: {
3165
+ empty: Format
3166
+ required: Format
3167
+ }
3168
+
3169
+ /** Built-in schema type definitions. */
3170
+ export const types: {
3171
+ asset: Record<string, any>
3172
+ color: Record<string, any>
3173
+ }
3174
+ export type Id = string | number
3175
+ export type KoaContext<$State = any> = Koa.ParameterizedContext<
3176
+ $State,
3177
+ {
3178
+ transaction: objection.Transaction
3179
+ session: koaSession.ContextSession & { state: { user: any } }
3180
+ logger: PinoLogger
3181
+ }
3182
+ >
3183
+
3184
+ type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>)
3185
+
3186
+ type OrArrayOf<T> = T[] | T
3187
+
3188
+ type OrReadOnly<T> = Readonly<T> | T
3189
+
3190
+ type OrPromiseOf<T> = Promise<T> | T
3191
+
3192
+ type ModelFromModelController<$ModelController extends ModelController> =
3193
+ InstanceType<Exclude<$ModelController['modelClass'], undefined>>
3194
+
3195
+ type SerializeModelPropertyValue<T> = T extends (infer U)[]
3196
+ ? SerializeModelPropertyValue<U>[]
3197
+ : T extends Model
3198
+ ? SerializedModel<T>
3199
+ : T extends Date
3200
+ ? string
3201
+ : T
3202
+
3203
+ /**
3204
+ * Extracts the JSON-serialized data properties from a Dito
3205
+ * Model, stripping methods, `$`-prefixed, and internal keys.
3206
+ * Converts `Date` to `string` to reflect JSON serialization.
3207
+ */
3208
+ export type SerializedModel<T extends Model> = {
3209
+ -readonly [K in keyof T as ModelDataKey<T, K>]: SerializeModelPropertyValue<
3210
+ T[K]
3211
+ >
3212
+ }
3213
+
3214
+ /** @deprecated Use `SerializedModel` instead. */
3215
+ export type SelectModelProperties<T extends Model> = SerializedModel<T>
3216
+ /** @deprecated Use `keyof SerializedModel<T>` instead. */
3217
+ export type SelectModelKeys<T extends Model> = keyof SerializedModel<T>
3218
+ /** @deprecated Use `SerializedModel` instead. */
3219
+ export type ExtractModelProperties<T extends Model> = SerializedModel<T>
3220
+ /** @deprecated Use `keyof SerializedModel<T>` instead. */
3221
+ export type SelectModelPropertyKeys<T extends Model> = keyof SerializedModel<T>
3222
+
3223
+ /* ---------------------- Extended from Ajv JSON Schema --------------------- */
3224
+
3225
+ /**
3226
+ * Dito.js JSON Schema type, extending the AJV JSON Schema type with
3227
+ * Dito.js-specific validation keywords (`validate`, `validateAsync`,
3228
+ * `instanceof`).
3229
+ *
3230
+ * Used throughout the framework for model property definitions, action
3231
+ * parameters, and response schemas.
3232
+ *
3233
+ * @template T - The TypeScript type that this schema validates against.
3234
+ *
3235
+ * @example
3236
+ * ```ts
3237
+ * const schema: Schema<string> = {
3238
+ * type: 'string',
3239
+ * minLength: 1,
3240
+ * validate: ({ data, app }) => typeof data === 'string'
3241
+ * }
3242
+ * ```
3243
+ */
3244
+ export type Schema<T = any> = JSONSchemaType<T> & {
3245
+ // keywords/_validate.js
3246
+ validate?: (params: {
3247
+ data: unknown
3248
+ parentData: object | unknown[]
3249
+ rootData: object | unknown[]
3250
+ dataPath: string
3251
+ parentIndex?: number
3252
+ parentKey?: string
3253
+ app: Application<Models>
3254
+ validator: Validator
3255
+ options: unknown
3256
+ }) => boolean | void
3257
+
3258
+ // keywords/_validate.js
3259
+ validateAsync?: (params: {
3260
+ data: unknown
3261
+ parentData: object | unknown[]
3262
+ rootData: object | unknown[]
3263
+ dataPath: string
3264
+ parentIndex?: number
3265
+ parentKey?: string
3266
+ app: Application<Models>
3267
+ validator: Validator
3268
+ options: unknown
3269
+ }) => Promise<boolean | void>
3270
+
3271
+ // keywords/_instanceof.js
3272
+ /**
3273
+ * Validates whether the value is an instance of at least one of the passed
3274
+ * types.
3275
+ */
3276
+ instanceof?: OrArrayOf<
3277
+ | LiteralUnion<
3278
+ | 'Object'
3279
+ | 'Array'
3280
+ | 'Function'
3281
+ | 'String'
3282
+ | 'Number'
3283
+ | 'Boolean'
3284
+ | 'Date'
3285
+ | 'RegExp'
3286
+ | 'Buffer'
3287
+ >
3288
+ | Function
3289
+ | typeof Object
3290
+ | typeof Array
3291
+ | typeof Function
3292
+ | typeof String
3293
+ | typeof Number
3294
+ | typeof Boolean
3295
+ | typeof Date
3296
+ | typeof RegExp
3297
+ | typeof Buffer
3298
+ >
3299
+ }
3300
+
3301
+ declare type StrictNullChecksWrapper<
3302
+ Name extends string,
3303
+ Type
3304
+ > = undefined extends null
3305
+ ? `strictNullChecks must be true in tsconfig to use ${Name}`
3306
+ : Type
3307
+ declare type UnionToIntersection<U> = (
3308
+ U extends any ? (_: U) => void : never
3309
+ ) extends (_: infer I) => void
3310
+ ? I
3311
+ : never
3312
+ declare type SomeJSONSchema = UncheckedJSONSchemaType<Known, true>
3313
+ declare type UncheckedPartialSchema<T> = Partial<
3314
+ UncheckedJSONSchemaType<T, true>
3315
+ >
3316
+ declare type PartialSchema<T> = StrictNullChecksWrapper<
3317
+ 'PartialSchema',
3318
+ UncheckedPartialSchema<T>
3319
+ >
3320
+ declare type JSONType<
3321
+ T extends string,
3322
+ IsPartial extends boolean
3323
+ > = IsPartial extends true ? T | undefined : T
3324
+ interface NumberKeywords {
3325
+ minimum?: number
3326
+ maximum?: number
3327
+ exclusiveMinimum?: number
3328
+ exclusiveMaximum?: number
3329
+ multipleOf?: number
3330
+ format?: string
3331
+ range?: [number, number]
3332
+ }
3333
+ interface StringKeywords {
3334
+ minLength?: number
3335
+ maxLength?: number
3336
+ pattern?: string
3337
+ format?: LiteralUnion<
3338
+ | 'date'
3339
+ | 'time'
3340
+ | 'uri'
3341
+ | 'uri-reference'
3342
+ | 'uri-template'
3343
+ | 'email'
3344
+ | 'hostname'
3345
+ | 'ipv4'
3346
+ | 'ipv6'
3347
+ | 'uuid'
3348
+ | 'json-pointer'
3349
+ | 'relative-json-pointer'
3350
+ | 'datetime'
3351
+ | 'timestamp'
3352
+ >
3353
+ }
3354
+
3355
+ // The first two unions allow arbitrary unions of types
3356
+ declare type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (
3357
+ | {
3358
+ anyOf: readonly UncheckedJSONSchemaType<T, IsPartial>[]
3359
+ }
3360
+ | {
3361
+ oneOf: readonly UncheckedJSONSchemaType<T, IsPartial>[]
3362
+ }
3363
+ | ({
3364
+ type: readonly (T extends number
3365
+ ? JSONType<'number' | 'integer', IsPartial>
3366
+ : T extends string
3367
+ ? JSONType<'string', IsPartial>
3368
+ : T extends boolean
3369
+ ? JSONType<'boolean', IsPartial>
3370
+ : never)[]
3371
+ } & UnionToIntersection<
3372
+ T extends number
3373
+ ? NumberKeywords
3374
+ : T extends string
3375
+ ? StringKeywords
3376
+ : T extends boolean
3377
+ ? {}
3378
+ : never
3379
+ >)
3380
+ | ((T extends number
3381
+ ? {
3382
+ type: JSONType<'number' | 'integer', IsPartial>
3383
+ } & NumberKeywords
3384
+ : T extends string
3385
+ ? {
3386
+ type: JSONType<
3387
+ 'string' | 'text' | 'date' | 'datetime' | 'timestamp',
3388
+ IsPartial
3389
+ >
3390
+ } & StringKeywords
3391
+ : T extends Date
3392
+ ? {
3393
+ type: JSONType<'date' | 'datetime' | 'timestamp', IsPartial>
3394
+ }
3395
+ : T extends boolean
3396
+ ? {
3397
+ type: JSONType<'boolean', IsPartial>
3398
+ }
3399
+ : T extends readonly [any, ...any[]]
3400
+ ? {
3401
+ type: JSONType<'array', IsPartial>
3402
+ items: {
3403
+ readonly [K in keyof T]-?: UncheckedJSONSchemaType<
3404
+ T[K],
3405
+ false
3406
+ > &
3407
+ Nullable<T[K]>
3408
+ } & {
3409
+ length: T['length']
3410
+ }
3411
+ minItems: T['length']
3412
+ } & (
3413
+ | {
3414
+ maxItems: T['length']
3415
+ }
3416
+ | {
3417
+ additionalItems: false
3418
+ }
3419
+ )
3420
+ : T extends readonly any[]
3421
+ ? {
3422
+ type: JSONType<'array', IsPartial>
3423
+ items: UncheckedJSONSchemaType<T[0], false>
3424
+ contains?: UncheckedPartialSchema<T[0]>
3425
+ minItems?: number
3426
+ maxItems?: number
3427
+ minContains?: number
3428
+ maxContains?: number
3429
+ uniqueItems?: true
3430
+ additionalItems?: never
3431
+ }
3432
+ : T extends Record<string, any>
3433
+ ? {
3434
+ type: JSONType<'object', IsPartial>
3435
+ additionalProperties?:
3436
+ | boolean
3437
+ | UncheckedJSONSchemaType<T[string], false>
3438
+ unevaluatedProperties?:
3439
+ | boolean
3440
+ | UncheckedJSONSchemaType<T[string], false>
3441
+ properties?: IsPartial extends true
3442
+ ? Partial<UncheckedPropertiesSchema<T>>
3443
+ : UncheckedPropertiesSchema<T>
3444
+ patternProperties?: Record<
3445
+ string,
3446
+ UncheckedJSONSchemaType<T[string], false>
3447
+ >
3448
+ propertyNames?: Omit<
3449
+ UncheckedJSONSchemaType<string, false>,
3450
+ 'type'
3451
+ > & {
3452
+ type?: 'string'
3453
+ }
3454
+ dependencies?: {
3455
+ [K in keyof T]?:
3456
+ | Readonly<(keyof T)[]>
3457
+ | UncheckedPartialSchema<T>
3458
+ }
3459
+ dependentRequired?: {
3460
+ [K in keyof T]?: Readonly<(keyof T)[]>
3461
+ }
3462
+ dependentSchemas?: {
3463
+ [K in keyof T]?: UncheckedPartialSchema<T>
3464
+ }
3465
+ minProperties?: number
3466
+ maxProperties?: number
3467
+ } & (IsPartial extends true
3468
+ ? {
3469
+ required: Readonly<(keyof T)[] | boolean>
3470
+ }
3471
+ : [UncheckedRequiredMembers<T>] extends [never]
3472
+ ? {
3473
+ required?:
3474
+ | Readonly<UncheckedRequiredMembers<T>[]>
3475
+ | boolean
3476
+ }
3477
+ : {
3478
+ required:
3479
+ | Readonly<UncheckedRequiredMembers<T>[]>
3480
+ | boolean
3481
+ })
3482
+ : T extends null
3483
+ ? {
3484
+ type: JSONType<'null', IsPartial>
3485
+ nullable: true
3486
+ }
3487
+ : never) & {
3488
+ allOf?: Readonly<UncheckedPartialSchema<T>[]>
3489
+ anyOf?: Readonly<UncheckedPartialSchema<T>[]>
3490
+ oneOf?: Readonly<UncheckedPartialSchema<T>[]>
3491
+ if?: UncheckedPartialSchema<T>
3492
+ then?: UncheckedPartialSchema<T>
3493
+ else?: UncheckedPartialSchema<T>
3494
+ not?: UncheckedPartialSchema<T>
3495
+ })
3496
+ ) & {
3497
+ [keyword: string]: any
3498
+ $id?: string
3499
+ $ref?: string
3500
+ $defs?: Record<string, UncheckedJSONSchemaType<Known, true>>
3501
+ definitions?: Record<string, UncheckedJSONSchemaType<Known, true>>
3502
+ }
3503
+
3504
+ declare type JSONSchemaType<T> = StrictNullChecksWrapper<
3505
+ 'JSONSchemaType',
3506
+ UncheckedJSONSchemaType<T, false>
3507
+ >
3508
+ declare type Known =
3509
+ | {
3510
+ [key: string]: Known
3511
+ }
3512
+ | [Known, ...Known[]]
3513
+ | Known[]
3514
+ | number
3515
+ | string
3516
+ | boolean
3517
+ | null
3518
+ declare type UncheckedPropertiesSchema<T> = {
3519
+ [K in keyof T]-?:
3520
+ | (UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>)
3521
+ | {
3522
+ $ref: string
3523
+ }
3524
+ }
3525
+ declare type PropertiesSchema<T> = StrictNullChecksWrapper<
3526
+ 'PropertiesSchema',
3527
+ UncheckedPropertiesSchema<T>
3528
+ >
3529
+ declare type UncheckedRequiredMembers<T> = {
3530
+ [K in keyof T]-?: undefined extends T[K] ? never : K
3531
+ }[keyof T]
3532
+ declare type RequiredMembers<T> = StrictNullChecksWrapper<
3533
+ 'RequiredMembers',
3534
+ UncheckedRequiredMembers<T>
3535
+ >
3536
+ declare type Nullable<T> = undefined extends T
3537
+ ? {
3538
+ nullable: true
3539
+ const?: null
3540
+ enum?: Readonly<(T | null)[]>
3541
+ default?: T | null
3542
+ }
3543
+ : {
3544
+ const?: T
3545
+ enum?: Readonly<T[]>
3546
+ default?: T
3547
+ }