@ditojs/server 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2125 @@
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 namespace.
7
+
8
+ import { DateFormat } from '@ditojs/utils'
9
+ import koaCors from '@koa/cors'
10
+ import * as Ajv from 'ajv/dist/2020.js'
11
+ import * as aws from 'aws-sdk'
12
+ import * as dbErrors from 'db-errors'
13
+ import * as EventEmitter2 from 'eventemitter2'
14
+ import helmet from 'helmet'
15
+ import * as Knex from 'knex'
16
+ import * as Koa from 'koa'
17
+ import koaBodyParser from 'koa-bodyparser'
18
+ import koaCompress from 'koa-compress'
19
+ import koaLogger from 'koa-logger'
20
+ import mount from 'koa-mount'
21
+ import koaPinoLogger from 'koa-pino-logger'
22
+ import koaResponseTime from 'koa-response-time'
23
+ import koaSession from 'koa-session'
24
+ import * as objection from 'objection'
25
+ import { KnexSnakeCaseMappersFactory } from 'objection'
26
+ import { Class, ConditionalExcept, ConditionalKeys, Constructor, SetReturnType } from 'type-fest'
27
+ import { UserConfig } from 'vite'
28
+
29
+ export type Page<$Model extends Model> = {
30
+ total: number
31
+ results: $Model[]
32
+ }
33
+
34
+ export type ApplicationConfig = {
35
+ /**
36
+ * @defaultValue `production`
37
+ */
38
+ env?: 'production' | 'development'
39
+ /**
40
+ * The server configuration
41
+ */
42
+ server?: {
43
+ /**
44
+ * The ip address or hostname used to serve requests
45
+ */
46
+ host?: string
47
+ /**
48
+ * The port to listen on for connections
49
+ */
50
+ port?: string
51
+ }
52
+ /**
53
+ * Logging options
54
+ */
55
+ log?: {
56
+ /**
57
+ * Enable logging requests to console by passing `true` or pick between
58
+ * 'console' for logging to console and 'file' for logging to file
59
+ * @defaultValue `false`
60
+ */
61
+ requests?: boolean | 'console' | 'file'
62
+ /**
63
+ * Whether to output route (Controller) logs
64
+ * @defaultValue `false`
65
+ */
66
+ routes?: boolean
67
+ /**
68
+ * Whether to log relation mappings
69
+ * @defaultValue `false`
70
+ */
71
+ relations?: boolean
72
+ /**
73
+ * Whether to log the json schema generated out of the model property
74
+ * definitions
75
+ * @defaultValue `false`
76
+ */
77
+ schema?: boolean
78
+ /**
79
+ * Whether to log sql queries
80
+ * @defaultValue `false`
81
+ */
82
+ sql?: boolean
83
+ /**
84
+ * Whether to turn off all logging
85
+ */
86
+ silent?: boolean
87
+ errors?: boolean | {
88
+ /**
89
+ * Whether to log sql errors
90
+ */
91
+ sql?: boolean;
92
+ /**
93
+ * Whether to log the error stack
94
+ */
95
+ stack?: boolean;
96
+ json?: boolean
97
+ }
98
+ }
99
+ api?: ApiConfig
100
+ app?: {
101
+ /**
102
+ * Whether to normalize paths from camel case to kebab case.
103
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/controllers.md#path-normalization|Path Normalization}
104
+ *
105
+ * @defaultValue `false`
106
+ */
107
+ normalizePaths?: boolean
108
+ /**
109
+ * Whether proxy header fields will be trusted.
110
+ * @defaultValue `false`
111
+ */
112
+ proxy?: Koa['proxy']
113
+ /**
114
+ * Whether to include X-Response-Time header in responses
115
+ * @defaultValue `true`
116
+ */
117
+ responseTime?: boolean | Parameters<typeof koaResponseTime>[0]
118
+ /**
119
+ * Whether to use koa-helmet middleware which provides important security
120
+ * headers to make your app more secure by default.
121
+ * @defaultValue `true`
122
+ * @see https://github.com/venables/koa-helmet
123
+ * @see https://github.com/helmetjs/helmet
124
+ */
125
+ helmet?: boolean | Parameters<typeof helmet>[0]
126
+ logger?:
127
+ | Parameters<typeof koaLogger>[0]
128
+ | Parameters<typeof koaPinoLogger>[0]
129
+ /**
130
+ * Configure body parser.
131
+ * @see https://github.com/koajs/bodyparser#options
132
+ */
133
+ bodyParser?: koaBodyParser.Options
134
+ /**
135
+ * Enable or configure Cross-Origin Resource Sharing (CORS)
136
+ * @defaultValue `true`
137
+ * @see https://github.com/koajs/cors#corsoptions
138
+ */
139
+ cors?: boolean | koaCors.Options
140
+ /**
141
+ * Enable or configure server response compression
142
+ * @defaultValue `true`
143
+ * @see https://github.com/koajs/compress#options
144
+ */
145
+ compress?: boolean | koaCompress.CompressOptions
146
+ /**
147
+ * Enable ETag headers in server responses
148
+ * @defaultValue `true`
149
+ */
150
+ etag?: boolean
151
+ /**
152
+ * @defaultValue `false`
153
+ * @see https://github.com/koajs/session
154
+ */
155
+ session?: boolean | (koaSession.opts & { modelClass: string })
156
+ /**
157
+ * Enable passport authentication middleware
158
+ * @defaultValue `false`
159
+ */
160
+ passport?: boolean
161
+ /**
162
+ * Set signed cookie keys.
163
+ * @see https://github.com/koajs/koa/blob/master/docs/api/index.md#appkeys
164
+ */
165
+ keys?: Koa['keys']
166
+ }
167
+ admin?: AdminConfig
168
+ knex?: Knex.Config<any> & {
169
+ /**
170
+ * @defaultValue `false`
171
+ */
172
+ normalizeDbNames?: boolean | Parameters<KnexSnakeCaseMappersFactory>
173
+ // See https://github.com/brianc/node-pg-types/blob/master/index.d.ts#L67
174
+ typeParsers?: Record<number, <I extends (string | Buffer)>(value: I) => any>
175
+ }
176
+ /**
177
+ * Service configurations. Pass `false` as a value to disable a service.
178
+ */
179
+ services?: Services
180
+ storages?: StorageConfigs
181
+ assets?: {
182
+ /**
183
+ * Threshold after which unused assets that haven't seen changes for given
184
+ * timeframe are removed.
185
+ *
186
+ * @example '1 hr 20 mins'
187
+ * @default `0`
188
+ * @see https://www.npmjs.com/package/parse-duration
189
+ */
190
+ cleanupTimeThreshold?: string | number
191
+ }
192
+ }
193
+
194
+ export type MulterS3File = {
195
+ bucket: string
196
+ key: string
197
+ acl: string
198
+ contentType: string
199
+ contentDisposition: null
200
+ storageClass: string
201
+ serverSideEncryption: null
202
+ metadata: any
203
+ location: string
204
+ etag: string
205
+ }
206
+
207
+ export type StorageConfigs = {[key: string]: StorageConfig}
208
+
209
+ export type StorageConfig =
210
+ | {
211
+ type: 's3'
212
+ /**
213
+ * The name of the destination bucket.
214
+ */
215
+ bucket: aws.S3.BucketName
216
+ /**
217
+ *
218
+ * @default 'private'
219
+ */
220
+ acl: LiteralUnion<
221
+ | 'private'
222
+ | 'public-read'
223
+ | 'public-read-write'
224
+ | 'authenticated-read'
225
+ | 'aws-exec-read'
226
+ | 'bucket-owner-read'
227
+ | 'bucket-owner-full-control'
228
+ >
229
+ /**
230
+ * Can be used to specify caching behavior along the request/reply
231
+ * chain.
232
+ *
233
+ * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.
234
+ */
235
+ cacheControl?: aws.S3.CacheControl
236
+ /**
237
+ * The type of storage to use for the object.
238
+ *
239
+ * @default 'STANDARD'.
240
+ */
241
+ storageClass?: LiteralUnion<
242
+ | 'STANDARD'
243
+ | 'REDUCED_REDUNDANCY'
244
+ | 'STANDARD_IA'
245
+ | 'ONEZONE_IA'
246
+ | 'INTELLIGENT_TIERING'
247
+ | 'GLACIER'
248
+ | 'DEEP_ARCHIVE'
249
+ >
250
+ /**
251
+ * The server-side encryption algorithm used when storing this object in
252
+ * Amazon S3 (for example, AES256, aws:kms).
253
+ */
254
+ serverSideEncryption?: aws.S3.ServerSideEncryption
255
+ /**
256
+ * If present, specifies the ID of the AWS Key Management Service
257
+ * (AWS KMS) symmetric customer managed customer master key (CMK)
258
+ */
259
+ sseKmsKeyId?: aws.S3.SSEKMSKeyId
260
+ s3: aws.S3.ClientConfiguration
261
+ url?: string
262
+ }
263
+ | {
264
+ type: 'disk'
265
+ path: string
266
+ url?: string
267
+ mount?: string
268
+ allowedImports?: string[]
269
+ }
270
+
271
+ export interface AdminConfig {
272
+ api?: ApiConfig
273
+ /**
274
+ * Path to the admin's src directory. Mandatory when in development
275
+ * mode.
276
+ */
277
+ root?: string;
278
+ /**
279
+ * Path to the dist/src/admin directory. Mandatory when in production
280
+ * mode.
281
+ */
282
+ dist?: string;
283
+ /**
284
+ * @default Application.config.env or `'production'` when missing
285
+ */
286
+ mode?: 'production' | 'development'
287
+ /**
288
+ * Settings accessible on the browser side as `global.dito.settings`.
289
+ */
290
+ settings?: Record<string, any>
291
+ }
292
+
293
+ export interface ApiResource {
294
+ type: string
295
+ path?: string
296
+ parent?: ApiResource
297
+ }
298
+
299
+ export interface ApiConfig {
300
+ /**
301
+ * The base url to use for api requests.
302
+ */
303
+ url?: string
304
+ /**
305
+ * @defaultValue 'en-US'
306
+ */
307
+ locale?: string
308
+ dateFormat?: DateFormat
309
+ /**
310
+ * Whether to display admin notifications.
311
+ *
312
+ * @default `true`
313
+ */
314
+ notifications?:
315
+ | boolean
316
+ | {
317
+ /**
318
+ * The amount of milliseconds multiplied with the amount of characters
319
+ * displayed in the notification, plus 40 (40 + title + message).
320
+ * @defaultValue `20`
321
+ **/
322
+ durationFactor: number
323
+ }
324
+ cors?: {
325
+ /**
326
+ * Whether cross-site `Access-Control` requests are made using credentials.
327
+ */
328
+ credentials: boolean
329
+ }
330
+ /**
331
+ * Setting normalizePaths to `true` sets `api.normalizePath` to hyphenate
332
+ * camelized strings and `api.denormalizePath` to do the opposite. If you
333
+ * prefer to use another path normalization algorithm, they can be defined
334
+ * the api settings passed to the DitoAdmin constructor.
335
+ *
336
+ * @default Defaults to Application.config.app.normalizePaths and then
337
+ * `false` when missing.
338
+ */
339
+ normalizePaths?: boolean
340
+ /**
341
+ * Auth resources
342
+ */
343
+ users?: {
344
+ path?: string
345
+ login?: {
346
+ /**
347
+ * @defaultValue `'login'`
348
+ */
349
+ path?: string
350
+ /**
351
+ * @defaultValue `'post'`
352
+ */
353
+ method?: HTTPMethod
354
+ }
355
+ logout?: {
356
+ /**
357
+ * @defaultValue `'logout'`
358
+ */
359
+ path?: string
360
+ /**
361
+ * @defaultValue `'post'`
362
+ */
363
+ method?: HTTPMethod
364
+ }
365
+ session?: {
366
+ /**
367
+ * @defaultValue `'session'`
368
+ */
369
+ path?: string
370
+ /**
371
+ * @defaultValue `'get'`
372
+ */
373
+ method?: HTTPMethod
374
+ }
375
+ }
376
+ /**
377
+ * Optionally override resource path handlers.
378
+ */
379
+ resources?: Record<string, (resource: ApiResource | string) => string>
380
+
381
+ /**
382
+ * Optionally override / extend headers
383
+ * @defaultValue `{
384
+ * 'Content-Type': 'application/json'
385
+ * }`
386
+ */
387
+ headers?: Record<string, string>
388
+ }
389
+
390
+ export interface ApplicationControllers {
391
+ [k: string]: Class<ModelController<Model>> | Class<Controller> | ApplicationControllers
392
+ }
393
+
394
+ export type Models = Record<string, Class<Model>>
395
+
396
+ export class Application<$Models extends Models> {
397
+ constructor(options: {
398
+ config?: ApplicationConfig
399
+ validator?: Validator
400
+ // TODO: router types
401
+ router?: any
402
+ /**
403
+ * Subscribe to application events. Event names: `'before:start'`,
404
+ * `'after:start'`, `'before:stop'`, `'after:stop'`, `'error'`
405
+ */
406
+ events?: Record<string, (this: Application<$Models>, ...args: []) => void>
407
+ models: $Models
408
+ controllers?: ApplicationControllers
409
+ // TODO: services docs
410
+ services?: Services
411
+ middleware?: Koa.Middleware
412
+ })
413
+
414
+ models: $Models
415
+ start(): Promise<void>
416
+ stop(timeout?: number): Promise<void>
417
+ startOrExit(): Promise<void>
418
+ addServices(services: Services): void
419
+ addService(service: Service): void
420
+ addController(controllers: Controller, namespace?: string): void
421
+ addControllers(controllers: ApplicationControllers, namespace?: string): void
422
+ addStorages(storages: StorageConfigs): void
423
+ addStorage(storage: StorageConfig): void
424
+ addModels(models: Models): void
425
+ addModel(model: Class<Model>): void
426
+ getAdminViteConfig(config?: UserConfig): UserConfig
427
+ }
428
+ export interface Application<$Models extends Models>
429
+ extends Omit<
430
+ Koa,
431
+ | 'setMaxListeners'
432
+ | 'removeListener'
433
+ | 'removeAllListeners'
434
+ | 'prependOnceListener'
435
+ | 'prependListener'
436
+ | 'once'
437
+ | 'on'
438
+ | 'off'
439
+ | 'listeners'
440
+ | 'addListener'
441
+ | 'listenerCount'
442
+ | 'emit'
443
+ | 'eventNames'
444
+ >,
445
+ EventEmitter {}
446
+
447
+ export type SchemaType = LiteralUnion<
448
+ | 'string'
449
+ | 'number'
450
+ | 'integer'
451
+ | 'boolean'
452
+ | 'object'
453
+ | 'array'
454
+ | 'null'
455
+ | 'date'
456
+ | 'datetime'
457
+ | 'timestamp'
458
+ >
459
+
460
+ export interface ModelRelation {
461
+ /**
462
+ * The type of relation
463
+ *
464
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-relations.md#relation-types|Relation Types}
465
+ */
466
+ relation: LiteralUnion<
467
+ 'belongsTo' | 'hasMany' | 'hasOne' | 'manyToMany' | 'hasOneThrough'
468
+ >
469
+ /**
470
+ * The model and property name from which the relation is to be built, as a
471
+ * string with both identifiers separated by '.',
472
+ * e.g.: 'FromModelClass.fromPropertyName'
473
+ */
474
+ from: string
475
+ /**
476
+ * The model and property name to which the relation is to be built, as a
477
+ * string with both identifiers separated by '.',
478
+ * e.g.: 'ToModelClass.toPropertyName'
479
+ */
480
+ to: string
481
+ /**
482
+ * When set to true the join model class and table is to be built automatically,
483
+ * or allows to specify an existing one manually.
484
+ *
485
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-relations.md#join-models-and-tables|Join Models and Tables}
486
+ */
487
+ through?:
488
+ | boolean
489
+ | {
490
+ /**
491
+ * The model and property name or table and column name of an existing
492
+ * join model class or join table from which the through relation is to
493
+ * be built, as a string with both identifiers separated by '.',
494
+ * e.g.: 'FromModelClass.fromPropertyName'
495
+ */
496
+ from: string
497
+ /**
498
+ * The model and property name or table and column name of an existing join
499
+ * model class or join table to which the through relation is to be built,
500
+ * as a string with both identifiers separated by '.',
501
+ * e.g.: 'toModelClass.toPropertyName'
502
+ */
503
+ to: string
504
+ /**
505
+ * List additional columns to be added to the related model.
506
+ *
507
+ * When working with a join model class or table, extra columns from it can
508
+ * be added to the related model, as if it was define on its own table. They
509
+ * then appear as additional properties on the related model.
510
+ */
511
+ extra?: string[]
512
+ }
513
+ /**
514
+ * Controls whether the relation is the inverse of another relation.
515
+ *
516
+ * This information is only required when working with through relations.
517
+ * Without it, Dito.js wouldn't be able to tell which side of the relation is
518
+ * on the left-hand side, and which is on the right-hand side when automatically
519
+ * creating the join model class and table.
520
+ */
521
+ inverse?: boolean
522
+ /**
523
+ * Optionally, a scope can be defined to be applied when loading the
524
+ * relation's models. The scope needs to be defined in the related model
525
+ * class' scopes definitions.
526
+ */
527
+ scope?: string
528
+ /**
529
+ * Controls whether the auto-inserted foreign key property should be marked as
530
+ * nullable. This only makes sense on a 'belongsTo' relation, where the model
531
+ * class holds the foreign key, and only when the foreign key isn't already
532
+ * explicitly defined in the Model Properties.
533
+ */
534
+ nullable?: boolean
535
+ /**
536
+ * Controls whether the relation owns the models that it holds, or whether it
537
+ * is simply relating to them, and a relation elsewhere is considered to be
538
+ * their owner.
539
+ */
540
+ owner?: boolean
541
+ }
542
+
543
+ export type ModelProperty<T = any> = Schema<T> & {
544
+ /**
545
+ * Marks the column as the primary key in the database.
546
+ */
547
+ primary?: boolean
548
+ /**
549
+ * Defines if the property is a foreign key.
550
+ *
551
+ * Finds the information about the related model in the relations
552
+ * definition and adds a reference to the related model table in
553
+ * migrations, by calling the .references(columnName).inTable(tableName)
554
+ * method.
555
+ */
556
+ foreign?: boolean
557
+ /**
558
+ * Adds an index to the database column in the migrations, by calling the
559
+ * .index() method.
560
+ */
561
+ index?: boolean
562
+ /**
563
+ * Marks the column as nullable in the migrations, by calling the
564
+ * .nullable() method.
565
+ */
566
+ nullable?: boolean
567
+ /**
568
+ * Adds a unique constraint to the table for the given column in the
569
+ * migrations, by calling the .unique() method. If a string is provided,
570
+ * all columns with the same string value for unique are grouped together
571
+ * in one unique constraint, by calling .unique([column1, column2, …]).
572
+ */
573
+ unique?: boolean | string
574
+ /**
575
+ * Marks the column for a property of type 'integer' to be unsigned in
576
+ * the migrations, by calling the .index() method.calling the .unsigned()
577
+ * method.
578
+ */
579
+ unsigned?: boolean
580
+ /**
581
+ * Marks the property as computed.
582
+ *
583
+ * Computed properties are not present as columns in the database itself.
584
+ * They can be created either by an SQL statement (SELECT … AS), or by a
585
+ * getter accessor defined on the model. Computed properties are set when
586
+ * converting to JSON if not present already, and removed again before
587
+ * data is sent to the database.
588
+ */
589
+ computed?: boolean
590
+ /**
591
+ * Marks the property has hidden, so that it does not show up in data
592
+ * converted to JSON.
593
+ *
594
+ * This can be used for sensitive data.
595
+ */
596
+ hidden?: boolean
597
+ }
598
+
599
+ export type ModelScope<$Model extends Model> = (
600
+ this: $Model,
601
+ query: QueryBuilder<$Model>,
602
+ applyParentScope: (query: QueryBuilder<$Model>) => QueryBuilder<$Model>
603
+ ) => QueryBuilder<$Model, any> | void
604
+
605
+ export type ModelScopes<$Model extends Model> = Record<
606
+ string,
607
+ ModelScope<$Model>
608
+ >
609
+
610
+ export type ModelFilterFunction<$Model extends Model> = (
611
+ queryBuilder: QueryBuilder<$Model>,
612
+ ...args: any[]
613
+ ) => void
614
+
615
+ export type ModelFilter<$Model extends Model> =
616
+ | {
617
+ filter: 'text' | 'date-range'
618
+ properties?: string[]
619
+ }
620
+ | {
621
+ handler: ModelFilterFunction<$Model>
622
+ parameters?: { [key: string]: Schema }
623
+ // TODO: validate type
624
+ validate?: any
625
+ }
626
+ | ModelFilterFunction<$Model>;
627
+
628
+ export type ModelFilters<$Model extends Model> = Record<
629
+ string,
630
+ ModelFilter<$Model>
631
+ >
632
+
633
+ export interface ModelAsset {
634
+ storage: string
635
+ readImageSize?: boolean
636
+ }
637
+
638
+ export type ModelAssets = Record<string, ModelAsset>
639
+
640
+ export interface ModelOptions extends objection.ModelOptions {
641
+ graph?: boolean
642
+ async?: boolean
643
+ mutable?: boolean
644
+ }
645
+
646
+ type ModelHookFunction<$Model extends Model> = (
647
+ args: objection.StaticHookArguments<$Model>
648
+ ) => void
649
+ export type ModelHooks<$Model extends Model> = {
650
+ [key in `${'before' | 'after'}:${'find' | 'insert' | 'update' | 'delete'}`]?: ModelHookFunction<$Model>
651
+ }
652
+
653
+ export class Model extends objection.Model {
654
+ /**
655
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md|Model Properties}
656
+ */
657
+ static properties: ModelProperties
658
+
659
+ /**
660
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-relations.md|Model Relations}
661
+ */
662
+ static relations: ModelRelations
663
+
664
+ /**
665
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md|Model Scopes}
666
+ */
667
+ static scopes: ModelScopes<Model>
668
+
669
+ /**
670
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-filters.md|Model Filters}
671
+ */
672
+ static filters: ModelFilters<Model>
673
+
674
+ static hooks: ModelHooks<Model>
675
+
676
+ static assets: ModelAssets
677
+
678
+ static getPropertyOrRelationAtDataPath: (dataPath: OrArrayOf<string>) => any
679
+
680
+ static count: {
681
+ (column?: objection.ColumnRef, options?: { as: string }): number
682
+ (aliasToColumnDict: Record<string, string | string[]>): number
683
+ (...columns: objection.ColumnRef[]): number
684
+ }
685
+
686
+ /**
687
+ * Dito automatically adds an `id` property if a model property with the
688
+ * `primary: true` setting is not already explicitly defined.
689
+ */
690
+ readonly id: Id
691
+
692
+ /**
693
+ * Dito automatically adds a `foreignKeyId` property if foreign keys
694
+ * occurring in relations definitions are not explicitly defined in the
695
+ * properties.
696
+ */
697
+ readonly foreignKeyId: Id
698
+
699
+ QueryBuilderType: QueryBuilder<this, this[]>
700
+
701
+ // Todo: include application settings
702
+ $app: Application<Models>
703
+ $is(model: Model): boolean
704
+ $update(
705
+ attributes: Partial<ExtractModelProperties<this>>,
706
+ trx?: objection.Transaction
707
+ ): objection.SingleQueryBuilder<objection.QueryBuilderType<this>>
708
+
709
+ $patch(
710
+ attributes: Partial<ExtractModelProperties<this>>,
711
+ trx?: objection.Transaction
712
+ ): objection.SingleQueryBuilder<objection.QueryBuilderType<this>>
713
+
714
+ $validate<$JSON extends null | {}>(
715
+ json?: $JSON,
716
+ options?: ModelOptions & Record<string, any>
717
+ ): Promise<$JSON | this>
718
+
719
+ $validateGraph(options: ModelOptions & Record<string, any>): Promise<this>
720
+
721
+ // /*-------------------- Start QueryBuilder.mixin(Model) -------------------*/
722
+ static first: StaticQueryBuilderMethod<'first'>
723
+ static find: StaticQueryBuilderMethod<'find'>
724
+ static findOne: StaticQueryBuilderMethod<'findOne'>
725
+ static findById: StaticQueryBuilderMethod<'findById'>
726
+
727
+ static withGraph: StaticQueryBuilderMethod<'withGraph'>
728
+ static withGraphFetched: StaticQueryBuilderMethod<'withGraphFetched'>
729
+ static withGraphJoined: StaticQueryBuilderMethod<'withGraphJoined'>
730
+ static clearWithGraph: StaticQueryBuilderMethod<'clearWithGraph'>
731
+
732
+ static withScope: StaticQueryBuilderMethod<'withScope'>
733
+ static applyScope: StaticQueryBuilderMethod<'applyScope'>
734
+ static clearWithScope: StaticQueryBuilderMethod<'clearWithScope'>
735
+
736
+ static clear: StaticQueryBuilderMethod<'clear'>
737
+ static pick: StaticQueryBuilderMethod<'pick'>
738
+ static omit: StaticQueryBuilderMethod<'omit'>
739
+ static select: StaticQueryBuilderMethod<'select'>
740
+
741
+ static insert: StaticQueryBuilderMethod<'insert'>
742
+ static upsert: StaticQueryBuilderMethod<'upsert'>
743
+ static update: StaticQueryBuilderMethod<'update'>
744
+ static relate: StaticQueryBuilderMethod<'relate'>
745
+ static patch: StaticQueryBuilderMethod<'patch'>
746
+
747
+ static truncate: StaticQueryBuilderMethod<'truncate'>
748
+ static delete: StaticQueryBuilderMethod<'delete'>
749
+ static deleteById: StaticQueryBuilderMethod<'deleteById'>
750
+
751
+ static insertAndFetch: StaticQueryBuilderMethod<'insertAndFetch'>
752
+ static upsertAndFetch: StaticQueryBuilderMethod<'upsertAndFetch'>
753
+ static updateAndFetch: StaticQueryBuilderMethod<'updateAndFetch'>
754
+ static patchAndFetch: StaticQueryBuilderMethod<'patchAndFetch'>
755
+ static patchAndFetchById: StaticQueryBuilderMethod<'patchAndFetchById'>
756
+ static updateAndFetchById: StaticQueryBuilderMethod<'updateAndFetchById'>
757
+
758
+ static insertGraph: StaticQueryBuilderMethod<'insertGraph'>
759
+ static upsertGraph: StaticQueryBuilderMethod<'upsertGraph'>
760
+ static insertGraphAndFetch: StaticQueryBuilderMethod<'insertGraphAndFetch'>
761
+ static upsertGraphAndFetch: StaticQueryBuilderMethod<'upsertGraphAndFetch'>
762
+
763
+ static insertDitoGraph: StaticQueryBuilderMethod<'insertDitoGraph'>
764
+ static upsertDitoGraph: StaticQueryBuilderMethod<'upsertDitoGraph'>
765
+ static updateDitoGraph: StaticQueryBuilderMethod<'updateDitoGraph'>
766
+ static patchDitoGraph: StaticQueryBuilderMethod<'patchDitoGraph'>
767
+ static insertDitoGraphAndFetch: StaticQueryBuilderMethod<'insertDitoGraphAndFetch'>
768
+ static upsertDitoGraphAndFetch: StaticQueryBuilderMethod<'upsertDitoGraphAndFetch'>
769
+ static updateDitoGraphAndFetch: StaticQueryBuilderMethod<'updateDitoGraphAndFetch'>
770
+ static patchDitoGraphAndFetch: StaticQueryBuilderMethod<'patchDitoGraphAndFetch'>
771
+ static upsertDitoGraphAndFetchById: StaticQueryBuilderMethod<'upsertDitoGraphAndFetchById'>
772
+ static updateDitoGraphAndFetchById: StaticQueryBuilderMethod<'updateDitoGraphAndFetchById'>
773
+ static patchDitoGraphAndFetchById: StaticQueryBuilderMethod<'patchDitoGraphAndFetchById'>
774
+
775
+ static where: StaticQueryBuilderMethod<'where'>
776
+ static whereNot: StaticQueryBuilderMethod<'whereNot'>
777
+ static whereRaw: StaticQueryBuilderMethod<'whereRaw'>
778
+ static whereWrapped: StaticQueryBuilderMethod<'whereWrapped'>
779
+ static whereExists: StaticQueryBuilderMethod<'whereExists'>
780
+ static whereNotExists: StaticQueryBuilderMethod<'whereNotExists'>
781
+ static whereIn: StaticQueryBuilderMethod<'whereIn'>
782
+ static whereNotIn: StaticQueryBuilderMethod<'whereNotIn'>
783
+ static whereNull: StaticQueryBuilderMethod<'whereNull'>
784
+ static whereNotNull: StaticQueryBuilderMethod<'whereNotNull'>
785
+ static whereBetween: StaticQueryBuilderMethod<'whereBetween'>
786
+ static whereNotBetween: StaticQueryBuilderMethod<'whereNotBetween'>
787
+ static whereColumn: StaticQueryBuilderMethod<'whereColumn'>
788
+ static whereNotColumn: StaticQueryBuilderMethod<'whereNotColumn'>
789
+ static whereComposite: StaticQueryBuilderMethod<'whereComposite'>
790
+ static whereInComposite: StaticQueryBuilderMethod<'whereInComposite'>
791
+ // whereNotInComposite: QueryBuilder<Model>['whereNotInComposite']
792
+ static whereJsonHasAny: StaticQueryBuilderMethod<'whereJsonHasAny'>
793
+ static whereJsonHasAll: StaticQueryBuilderMethod<'whereJsonHasAll'>
794
+ static whereJsonIsArray: StaticQueryBuilderMethod<'whereJsonIsArray'>
795
+ static whereJsonNotArray: StaticQueryBuilderMethod<'whereJsonNotArray'>
796
+ static whereJsonIsObject: StaticQueryBuilderMethod<'whereJsonIsObject'>
797
+ static whereJsonNotObject: StaticQueryBuilderMethod<'whereJsonNotObject'>
798
+ static whereJsonSubsetOf: StaticQueryBuilderMethod<'whereJsonSubsetOf'>
799
+ static whereJsonNotSubsetOf: StaticQueryBuilderMethod<'whereJsonNotSubsetOf'>
800
+ static whereJsonSupersetOf: StaticQueryBuilderMethod<'whereJsonSupersetOf'>
801
+ static whereJsonNotSupersetOf: StaticQueryBuilderMethod<'whereJsonNotSupersetOf'>
802
+
803
+ static having: StaticQueryBuilderMethod<'having'>
804
+ static havingIn: StaticQueryBuilderMethod<'havingIn'>
805
+ static havingNotIn: StaticQueryBuilderMethod<'havingNotIn'>
806
+ static havingNull: StaticQueryBuilderMethod<'havingNull'>
807
+ static havingNotNull: StaticQueryBuilderMethod<'havingNotNull'>
808
+ static havingExists: StaticQueryBuilderMethod<'havingExists'>
809
+ static havingNotExists: StaticQueryBuilderMethod<'havingNotExists'>
810
+ static havingBetween: StaticQueryBuilderMethod<'havingBetween'>
811
+ static havingNotBetween: StaticQueryBuilderMethod<'havingNotBetween'>
812
+ static havingRaw: StaticQueryBuilderMethod<'havingRaw'>
813
+ static havingWrapped: StaticQueryBuilderMethod<'havingWrapped'>
814
+
815
+ // deprecated methods that are still supported at the moment.
816
+ // TODO: Remove once we move to Objection 3.0
817
+
818
+ static eager: StaticQueryBuilderMethod<'eager'>
819
+ static joinEager: StaticQueryBuilderMethod<'joinEager'>
820
+ static naiveEager: StaticQueryBuilderMethod<'naiveEager'>
821
+ static mergeEager: StaticQueryBuilderMethod<'mergeEager'>
822
+ static mergeJoinEager: StaticQueryBuilderMethod<'mergeJoinEager'>
823
+ static mergeNaiveEager: StaticQueryBuilderMethod<'mergeNaiveEager'>
824
+ static clearEager: StaticQueryBuilderMethod<'clearEager'>
825
+
826
+ // static scope: QueryBuilder<Model>['scope']
827
+ // static mergeScope: QueryBuilder<Model>['mergeScope']
828
+ // static clearScope: QueryBuilder<Model>['clearScope']
829
+
830
+ /* --------------------- End QueryBuilder.mixin(Model) -------------------- */
831
+ }
832
+
833
+ type StaticQueryBuilderMethod<
834
+ K extends ConditionalKeys<QueryBuilder<Model>, (...a: any[]) => any>
835
+ > = <$Model extends Class<Model>>(
836
+ ...args: Parameters<QueryBuilder<InstanceType<$Model>>[K]>
837
+ ) => ReturnType<QueryBuilder<InstanceType<$Model>>[K]>
838
+
839
+ export interface Model extends EventEmitter {}
840
+ export interface Model extends KnexHelper {}
841
+
842
+ export type ModelClass = Class<Model>
843
+
844
+ export type ModelRelations = Record<string, ModelRelation>
845
+
846
+ export type ModelProperties = Record<string, ModelProperty>
847
+
848
+ export type ControllerAction<$Controller extends Controller> =
849
+ | ControllerActionOptions<$Controller>
850
+ | ControllerActionHandler<$Controller>
851
+
852
+ export class Controller {
853
+ app: Application
854
+ /**
855
+ * Optionally provide the controller path. A default is deducted from
856
+ * the normalized class name otherwise.
857
+ */
858
+ path?: string
859
+ /**
860
+ * The controller's name. If not provided, it is automatically deducted
861
+ * from the controller class name. If this name ends in 'Controller', that is
862
+ * stripped off the name, so 'GreetingsController' turns into 'Greetings'.
863
+ */
864
+ name?: string
865
+ /**
866
+ * The controller's namespace, which is prepended to path to generate the
867
+ * absolute controller route. Note that it is rare to provide this manually.
868
+ * Usually Dito.js determines the namespace automatically from the controller
869
+ * object passed to the Dito.js application's constructor and its sub-objects.
870
+ *
871
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/controllers.md#namespaces Namespaces}
872
+ */
873
+ namespace?: string
874
+ /**
875
+ * A list of allowed actions. If provided, only the action names listed here
876
+ * as strings will be mapped to routes, everything else will be omitted.
877
+ */
878
+ allow?: OrReadOnly<ControllerActionName[]>
879
+
880
+ /**
881
+ * Authorization
882
+ */
883
+ authorize?: Authorize
884
+ actions?: ControllerActions<this>
885
+
886
+ initialize(): void
887
+ setup(isRoot: boolean, setupActionsObject: boolean): void
888
+ // TODO: type reflectActionsObject
889
+ reflectActionsObject(): any
890
+ setupRoute<$ControllerAction extends ControllerAction<any>>(
891
+ method: HTTPMethod,
892
+ url: string,
893
+ transacted: boolean,
894
+ authorize: Authorize,
895
+ action: $ControllerAction,
896
+ handlers: ((ctx: KoaContext, next: Function) => void)[]
897
+ ): void
898
+
899
+ setupActions(type: string): string[]
900
+ setupActionRoute(type: any, action: any): void
901
+ setupAssets(): any
902
+ setupAssetRoute(
903
+ dataPath: OrArrayOf<string>,
904
+ config: any,
905
+ authorize: Authorize
906
+ ): void
907
+
908
+ compose(): Parameters<typeof mount>[1]
909
+ /**
910
+ * To be overridden by sub-classes.
911
+ */
912
+ getPath(type: string, path: string): string
913
+ getUrl(type: string, path: string): string
914
+ inheritValues(type: string): any
915
+ processValues(values: any): {
916
+ // Create a filtered `values` object that only contains the allowed fields
917
+ values: any
918
+ allow: string[]
919
+ authorize: Authorize
920
+ }
921
+
922
+ emitHook(
923
+ type: string,
924
+ handleResult: any,
925
+ ctx: any,
926
+ ...args: any[]
927
+ ): Promise<any>
928
+
929
+ processAuthorize(authorize: any): any
930
+ describeAuthorize(authorize: any): string
931
+ handleAuthorization(): Promise<void>
932
+ /**
933
+ *
934
+ * @param str The string to log.
935
+ * @param [indent=0] The amount of levels to indent (in pairs of two spaces).
936
+ */
937
+ log(str: string, indent?: number): void
938
+ }
939
+
940
+ export type ActionParameter = Schema & { name: string }
941
+
942
+ export type ModelControllerActionHandler<
943
+ $ModelController extends ModelController<Model>
944
+ > = (this: $ModelController, ctx: KoaContext, ...args: any[]) => any
945
+
946
+ export type ControllerActionHandler<$Controller extends Controller> = (
947
+ this: $Controller,
948
+ ctx: KoaContext,
949
+ ...args: any[]
950
+ ) => any
951
+
952
+ export type ExtractModelProperties<$Model> = {
953
+ [$Key in SelectModelPropertyKeys<$Model>]: $Model[$Key] extends Model
954
+ ? ExtractModelProperties<$Model[$Key]>
955
+ : $Model[$Key]
956
+ }
957
+
958
+ export type Extends<$A extends any, $B extends any> = $A extends $B ? 1 : 0
959
+
960
+ export type SelectModelPropertyKeys<$Model extends Model> = {
961
+ [K in keyof $Model]-?: K extends 'QueryBuilderType' | 'foreignKeyId' | `$${string}`
962
+ ? never
963
+ : $Model[K] extends Function
964
+ ? never
965
+ : K
966
+ }[keyof $Model]
967
+
968
+ export type Authorize =
969
+ | boolean
970
+ | OrArrayOf<LiteralUnion<'$self' | '$owner'>>
971
+ | ((ctx: KoaContext) => OrPromiseOf<Authorize>)
972
+ | Record<HTTPMethod, string | string[]>
973
+
974
+ export type BaseControllerActionOptions = {
975
+ /**
976
+ * The HTTP method (`'get'`, `'post'`, `'put'`, `'delete'` or `'patch'`) to
977
+ * which the action should listen. By default, the `'get'` method is assigned
978
+ * if none is provided.
979
+ */
980
+ method?: HTTPMethod
981
+ /**
982
+ * The path to which the action is mapped, defined in relation to the route
983
+ * path of its controller. By default, the normalized method name is used as
984
+ * the action's path.
985
+ */
986
+ path?: string
987
+ /**
988
+ * Determines whether or how the request is authorized. This value can
989
+ * either be one of the values as described below, an array of them or
990
+ * a function which returns one or more of them.
991
+ *
992
+ * - boolean: `true` if the action should be authorized, `false` otherwise.
993
+ * - '$self': The requested member is checked against `ctx.state.user`
994
+ * and the action is only authorized if it matches the member.
995
+ * - '$owner': The member is asked if it is owned by `ctx.state.user`
996
+ * through the optional `Model.$hasOwner()` method.
997
+ * - any string: `ctx.state.user` is checked for this role through
998
+ * the overridable `UserModel.hasRole()` method.
999
+ */
1000
+ authorize?: Authorize
1001
+ /**
1002
+ * Validates action parameters and maps them to Koa's `ctx.query` object passed
1003
+ * to the action handler.
1004
+ *
1005
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md Model Properties}
1006
+ */
1007
+ parameters?: { [key: string]: Schema }
1008
+ /**
1009
+ * Provides a schema for the value returned from the action handler and
1010
+ * optionally maps the value to a key inside a returned object when it
1011
+ * contains a `name` property.
1012
+ *
1013
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md Model Properties}
1014
+ */
1015
+ returns?: Schema & { name?: string }
1016
+ /**
1017
+ * The scope(s) to be applied to every query executed through the action.
1018
+ *
1019
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md Model Scopes}
1020
+ */
1021
+ scope?: string[]
1022
+ /**
1023
+ * Determines whether queries in the action should be executed within
1024
+ * a transaction. Any failure will mean the database will rollback any
1025
+ * queries executed to the pre-transaction state.
1026
+ */
1027
+ transacted?: boolean
1028
+ }
1029
+
1030
+ export type ControllerActionOptions<$Controller extends Controller> =
1031
+ BaseControllerActionOptions & {
1032
+ handler: ControllerActionHandler<$Controller>
1033
+ }
1034
+
1035
+ export type ModelControllerActionOptions<
1036
+ $ModelController extends ModelController<Model>
1037
+ > = BaseControllerActionOptions & {
1038
+ /**
1039
+ * The function to be called when the action route is requested.
1040
+ */
1041
+ handler: ModelControllerActionHandler<$ModelController>
1042
+ }
1043
+
1044
+ export type MemberActionParameter<M extends Model> =
1045
+ | Schema
1046
+ | {
1047
+ member: true
1048
+
1049
+ /**
1050
+ * Sets ctx.query.
1051
+ */
1052
+ query?: Record<string, any>
1053
+ /**
1054
+ * Adds a FOR UPDATE in PostgreSQL and MySQL during a select statement.
1055
+ * FOR UPDATE causes the rows retrieved by the SELECT statement to be locked
1056
+ * as though for update. This prevents them from being locked, modified or
1057
+ * deleted by other transactions until the current transaction ends.
1058
+ *
1059
+ * @default `false`
1060
+ * @see {@link http://knexjs.org/#Builder-forUpdate}
1061
+ * @see {@link https://www.postgresql.org/docs/12/explicit-locking.html#LOCKING-ROWS}
1062
+ */
1063
+ forUpdate?: boolean
1064
+ /**
1065
+ * Modify the member query.
1066
+ */
1067
+ modify?: (query: QueryBuilder<M>) => QueryBuilder<M>
1068
+ }
1069
+
1070
+ export type ModelControllerAction<
1071
+ $ModelController extends ModelController<Model>
1072
+ > =
1073
+ | ModelControllerActionOptions<$ModelController>
1074
+ | ModelControllerActionHandler<$ModelController>;
1075
+
1076
+ export type ModelControllerActions<
1077
+ $ModelController extends ModelController<Model> = ModelController<Model>
1078
+ > = {
1079
+ [name: ControllerActionName]: ModelControllerAction<$ModelController>,
1080
+ allow?: OrReadOnly<ControllerActionName[]>,
1081
+ authorize?: Authorize
1082
+ }
1083
+
1084
+ type ModelControllerMemberAction<
1085
+ $ModelController extends ModelController<Model>
1086
+ > =
1087
+ | (
1088
+ | (Omit<ModelControllerActionOptions<$ModelController>, 'parameters'> & {
1089
+ parameters?: {
1090
+ [key: string]: MemberActionParameter<
1091
+ modelFromModelController<$ModelController>
1092
+ >
1093
+ }
1094
+ })
1095
+ | ModelControllerActionHandler<$ModelController>
1096
+ )
1097
+
1098
+ export type ModelControllerMemberActions<
1099
+ $ModelController extends ModelController<Model>
1100
+ > = {
1101
+ [name: ControllerActionName]: ModelControllerMemberAction<$ModelController>;
1102
+ allow?: OrReadOnly<ControllerActionName[]>;
1103
+ authorize?: Authorize
1104
+ }
1105
+
1106
+ export type ControllerActionName = `${HTTPMethod}${string}`;
1107
+
1108
+ export type ControllerActions<$Controller extends Controller> = {
1109
+ [name: ControllerActionName]: ControllerAction<$Controller>
1110
+ allow?: OrReadOnly<ControllerActionName[]>;
1111
+ authorize?: Authorize
1112
+ }
1113
+
1114
+ export class UsersController<M extends Model> extends ModelController<M> {}
1115
+
1116
+ export class AdminController extends Controller {
1117
+ config: AdminConfig
1118
+ mode: 'production' | 'development'
1119
+ closed: boolean
1120
+ koa: Koa
1121
+ getPath(name: string): string
1122
+ getDitoObject(): {
1123
+ base: string
1124
+ api: AdminConfig['api']
1125
+ settings: AdminConfig['settings']
1126
+ }
1127
+
1128
+ sendDitoObject(ctx: Koa.Context): void
1129
+ middleware(): Koa.Middleware
1130
+ setupViteServer(): void
1131
+ getViteConfig(config: UserConfig): UserConfig
1132
+ }
1133
+ type ModelControllerHookType = 'collection' | 'member'
1134
+ type ModelControllerHookKeys<
1135
+ $Keys extends string,
1136
+ $ModelControllerHookType extends string
1137
+ > = `${'before' | 'after' | '*'}:${$ModelControllerHookType | '*'}:${
1138
+ | Exclude<$Keys, 'allow'>
1139
+ | ControllerActionName
1140
+ | '*'}`
1141
+ type ModelControllerHook<
1142
+ $ModelController extends ModelController<Model> = ModelController<Model>
1143
+ > = (
1144
+ ctx: KoaContext,
1145
+ result: objection.Page<modelFromModelController<$ModelController>>
1146
+ ) => any
1147
+
1148
+ type HookHandler = () => void
1149
+
1150
+ type HookKeysFromController<$ModelController extends ModelController<Model>> =
1151
+ | ModelControllerHookKeys<
1152
+ Exclude<
1153
+ keyof Exclude<$ModelController['collection'], undefined>,
1154
+ symbol | number
1155
+ >,
1156
+ 'collection'
1157
+ >
1158
+ | ModelControllerHookKeys<
1159
+ Exclude<
1160
+ keyof Exclude<$ModelController['member'], undefined>,
1161
+ symbol | number
1162
+ >,
1163
+ 'member'
1164
+ >
1165
+
1166
+ type HandlerFromHookKey<
1167
+ $ModelController extends ModelController<Model>,
1168
+ $Key extends HookKeysFromController<$ModelController>
1169
+ > = $Key extends `${'before' | 'after' | '*'}:${
1170
+ | 'collection'
1171
+ | 'member'
1172
+ | '*'}:${string}`
1173
+ ? (this: $ModelController, ctx: KoaContext, ...args: any[]) => any
1174
+ : never
1175
+
1176
+ type ModelControllerHooks<
1177
+ $ModelController extends ModelController<Model> = ModelController<Model>
1178
+ > = {
1179
+ [$Key in HookKeysFromController<$ModelController>]?: HandlerFromHookKey<
1180
+ $ModelController,
1181
+ HookKeysFromController<$ModelController>
1182
+ >
1183
+ }
1184
+
1185
+ export type ModelControllerScope = OrArrayOf<string>
1186
+
1187
+ export class ModelController<$Model extends Model = Model> extends Controller {
1188
+ /**
1189
+ * The model class that this controller represents. If none is provided,
1190
+ * the singularized controller name is used to look up the model class in
1191
+ * models registered with the application. As a convention, model controller
1192
+ * names should always be provided in pluralized form.
1193
+ */
1194
+ modelClass?: Class<$Model>
1195
+ /**
1196
+ * The controller's collection actions. Instead of being provided on the
1197
+ * instance level as in the controller base class, they are to be wrapped
1198
+ * in a designated object in order to be assigned to the collection.
1199
+ *
1200
+ * To limit which collection actions will be mapped to routes, supply an
1201
+ * array of action names under the `allow` key. Only the action names listed
1202
+ * there will be mapped to routes, everything else will be omitted.
1203
+ */
1204
+ collection?: ModelControllerActions<ModelController<$Model>>
1205
+ /**
1206
+ * The controller's member actions. Instead of being provided on the instance
1207
+ * level as in the controller base class, they are to be wrapped in a
1208
+ * designated object in order to be assigned to the member.
1209
+ *
1210
+ * To limit which member actions will be mapped to routes, supply an array
1211
+ * of action names under the `allow` key. Only the action names listed there
1212
+ * will be mapped to routes, everything else will be omitted.
1213
+ */
1214
+ member?: ModelControllerMemberActions<ModelController<$Model>>
1215
+ assets?:
1216
+ | boolean
1217
+ | {
1218
+ allow?: OrArrayOf<string>
1219
+ authorize: Record<string, OrArrayOf<string>>
1220
+ }
1221
+
1222
+ /**
1223
+ * When nothing is returned from a hook, the standard action result is used.
1224
+ */
1225
+ hooks?: ModelControllerHooks<ModelController<$Model>>
1226
+ /**
1227
+ * Controls whether normal database methods should be used, or their …Graph…
1228
+ * counterparts.
1229
+ *
1230
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-queries.md#graph-methods Model Queries – Graph Methods}
1231
+ */
1232
+ graph?: boolean
1233
+ /**
1234
+ * The query parameter(s) allowed to be passed to the default model actions,
1235
+ * both on `collection` and `member` level. If none is provided, every
1236
+ * supported parameter is allowed.
1237
+ *
1238
+ * @See {@link https://github.com/ditojs/dito/blob/master/docs/model-queries.md#find-methods) Model Queries – Find Methods}
1239
+ */
1240
+ allowParam?: OrArrayOf<LiteralUnion<keyof QueryParameterOptions>>
1241
+ /**
1242
+ * The scope(s) allowed to be requested when passing the 'scope' query
1243
+ * parameter to the default model actions. If none is provided, every
1244
+ * supported scope is allowed.
1245
+ *
1246
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md Model Scopes}
1247
+ */
1248
+ allowScope?: boolean | OrArrayOf<string>
1249
+ /**
1250
+ * The filter(s) allowed to be requested when passing the 'filter' query
1251
+ * parameter to the default model actions. If none is provided, every
1252
+ * supported filter is allowed.
1253
+ *
1254
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-filters.md Model Filters}
1255
+ */
1256
+ allowFilter?: boolean | OrArrayOf<string>
1257
+ /**
1258
+ * The scope(s) to be applied to every query executed through this controller.
1259
+ *
1260
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-scopes.md Model Scopes}
1261
+ */
1262
+ scope?: ModelControllerScope
1263
+ query(): QueryBuilder<$Model>
1264
+ }
1265
+
1266
+ export class Validator extends objection.Validator {
1267
+ constructor(schema?: {
1268
+ options?: {
1269
+ /**
1270
+ * @defaultValue `false`
1271
+ */
1272
+ async?: boolean
1273
+ /**
1274
+ * @defaultValue `false`
1275
+ */
1276
+ patch?: boolean
1277
+ /**
1278
+ * @defaultValue `false`
1279
+ */
1280
+ $data?: boolean
1281
+ /**
1282
+ * @defaultValue `false`
1283
+ */
1284
+ $comment?: boolean
1285
+ /**
1286
+ * @defaultValue `false`
1287
+ */
1288
+ coerceTypes?: boolean
1289
+ /**
1290
+ * @defaultValue `false`
1291
+ */
1292
+ multipleOfPrecision?: boolean
1293
+ /**
1294
+ * @defaultValue `true`
1295
+ */
1296
+ ownProperties?: boolean
1297
+ /**
1298
+ * @defaultValue `false`
1299
+ */
1300
+ removeAdditional?: boolean
1301
+ /**
1302
+ * @defaultValue `true`
1303
+ */
1304
+ uniqueItems?: boolean
1305
+ /**
1306
+ * @defaultValue `true`
1307
+ */
1308
+ useDefaults?: boolean
1309
+ /**
1310
+ * @defaultValue `false`
1311
+ */
1312
+ verbose?: boolean
1313
+ }
1314
+ keywords?: Record<string, Keyword>
1315
+ formats?: Record<string, Format>
1316
+ })
1317
+ }
1318
+
1319
+ // NOTE: Because EventEmitter overrides a number of EventEmitter2 methods with
1320
+ // changed signatures, we are unable to extend it.
1321
+ export class EventEmitter {
1322
+ static mixin: (target: any) => {}
1323
+ constructor(options?: EventEmitter2.ConstructorOptions)
1324
+ responds(event: EventEmitter2.event): boolean
1325
+ emit(
1326
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1327
+ ...values: any[]
1328
+ ): Promise<any[]>
1329
+
1330
+ on(
1331
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1332
+ listener: EventEmitter2.ListenerFn
1333
+ ): this
1334
+
1335
+ off(
1336
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1337
+ listener: EventEmitter2.ListenerFn
1338
+ ): this
1339
+
1340
+ once(
1341
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1342
+ listener: EventEmitter2.ListenerFn
1343
+ ): this
1344
+
1345
+ setupEmitter(
1346
+ events: Record<string, EventEmitter2.ListenerFn>,
1347
+ options: EventEmitter2.ConstructorOptions
1348
+ ): void
1349
+
1350
+ // From EventEmitter2:
1351
+ emitAsync(
1352
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1353
+ ...values: any[]
1354
+ ): Promise<any[]>
1355
+
1356
+ addListener(
1357
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1358
+ listener: EventEmitter2.ListenerFn
1359
+ ): this | EventEmitter2.Listener
1360
+
1361
+ prependListener(
1362
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1363
+ listener: EventEmitter2.ListenerFn,
1364
+ options?: boolean | EventEmitter2.OnOptions
1365
+ ): this | EventEmitter2.Listener
1366
+
1367
+ prependOnceListener(
1368
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1369
+ listener: EventEmitter2.ListenerFn,
1370
+ options?: boolean | EventEmitter2.OnOptions
1371
+ ): this | EventEmitter2.Listener
1372
+
1373
+ many(
1374
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1375
+ timesToListen: number,
1376
+ listener: EventEmitter2.ListenerFn,
1377
+ options?: boolean | EventEmitter2.OnOptions
1378
+ ): this | EventEmitter2.Listener
1379
+
1380
+ prependMany(
1381
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1382
+ timesToListen: number,
1383
+ listener: EventEmitter2.ListenerFn,
1384
+ options?: boolean | EventEmitter2.OnOptions
1385
+ ): this | EventEmitter2.Listener
1386
+
1387
+ onAny(listener: EventEmitter2.EventAndListener): this
1388
+ prependAny(listener: EventEmitter2.EventAndListener): this
1389
+ offAny(listener: EventEmitter2.ListenerFn): this
1390
+ removeListener(
1391
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1392
+ listener: EventEmitter2.ListenerFn
1393
+ ): this
1394
+
1395
+ removeAllListeners(event?: EventEmitter2.event | EventEmitter2.eventNS): this
1396
+ setMaxListeners(n: number): void
1397
+ getMaxListeners(): number
1398
+ eventNames(
1399
+ nsAsArray?: boolean
1400
+ ): (EventEmitter2.event | EventEmitter2.eventNS)[]
1401
+
1402
+ listenerCount(event?: EventEmitter2.event | EventEmitter2.eventNS): number
1403
+ listeners(
1404
+ event?: EventEmitter2.event | EventEmitter2.eventNS
1405
+ ): EventEmitter2.ListenerFn[]
1406
+
1407
+ listenersAny(): EventEmitter2.ListenerFn[]
1408
+ waitFor(
1409
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1410
+ timeout?: number
1411
+ ): EventEmitter2.CancelablePromise<any[]>
1412
+
1413
+ waitFor(
1414
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1415
+ filter?: EventEmitter2.WaitForFilter
1416
+ ): EventEmitter2.CancelablePromise<any[]>
1417
+
1418
+ waitFor(
1419
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1420
+ options?: EventEmitter2.WaitForOptions
1421
+ ): EventEmitter2.CancelablePromise<any[]>
1422
+
1423
+ listenTo(
1424
+ target: EventEmitter2.GeneralEventEmitter,
1425
+ events: EventEmitter2.event | EventEmitter2.eventNS,
1426
+ options?: EventEmitter2.ListenToOptions
1427
+ ): this
1428
+
1429
+ listenTo(
1430
+ target: EventEmitter2.GeneralEventEmitter,
1431
+ events: EventEmitter2.event[],
1432
+ options?: EventEmitter2.ListenToOptions
1433
+ ): this
1434
+
1435
+ listenTo(
1436
+ target: EventEmitter2.GeneralEventEmitter,
1437
+ events: Object,
1438
+ options?: EventEmitter2.ListenToOptions
1439
+ ): this
1440
+
1441
+ stopListeningTo(
1442
+ target?: EventEmitter2.GeneralEventEmitter,
1443
+ event?: EventEmitter2.event | EventEmitter2.eventNS
1444
+ ): Boolean
1445
+
1446
+ hasListeners(event?: String): Boolean
1447
+ static once(
1448
+ emitter: EventEmitter2.EventEmitter2,
1449
+ event: EventEmitter2.event | EventEmitter2.eventNS,
1450
+ options?: EventEmitter2.OnceOptions
1451
+ ): EventEmitter2.CancelablePromise<any[]>
1452
+
1453
+ static defaultMaxListeners: number
1454
+ }
1455
+
1456
+ export interface DitoGraphOptions {
1457
+ fetchStrategy?: 'OnlyNeeded' | 'OnlyIdentifiers' | 'Everything'
1458
+ relate?: boolean
1459
+ allowRefs?: boolean
1460
+ insertMissing?: boolean
1461
+ unrelate?: boolean
1462
+ update?: boolean
1463
+ }
1464
+
1465
+ export type QueryParameterOptions = {
1466
+ scope?: OrArrayOf<string>
1467
+ filter?: OrArrayOf<string>
1468
+ /**
1469
+ * A range between two numbers. When expressed as a string, the value
1470
+ * is split at the ',' character ignoring any spaces on either side.
1471
+ * i.e. `'1,2'` and `'1 , 2'`
1472
+ */
1473
+ range?: [number, number] | string
1474
+ limit?: number
1475
+ offset?: number
1476
+ order?: 'asc' | 'desc'
1477
+ }
1478
+ export type QueryParameterOptionKey = keyof QueryParameterOptions
1479
+
1480
+ export class Service {
1481
+ constructor(app: Application<Models>, name?: string)
1482
+
1483
+ setup(config: any): void
1484
+
1485
+ initialize(): void
1486
+
1487
+ start(): Promise<void>
1488
+
1489
+ stop(): Promise<void>
1490
+ }
1491
+ export type Services = Record<string, Class<Service> | Service>
1492
+
1493
+ export class QueryBuilder<
1494
+ M extends Model,
1495
+ R = M[]
1496
+ > extends objection.QueryBuilder<M, R> {
1497
+ /**
1498
+ * Returns true if the query defines normal selects:
1499
+ * select(), column(), columns()
1500
+ */
1501
+ hasNormalSelects: () => boolean
1502
+ /**
1503
+ * Returns true if the query defines special selects:
1504
+ * distinct(), count(), countDistinct(), min(), max(),
1505
+ * sum(), sumDistinct(), avg(), avgDistinct()
1506
+ */
1507
+ hasSpecialSelects: () => boolean
1508
+ withScope: (...scopes: string[]) => this
1509
+ /**
1510
+ * Clear all scopes defined with `withScope()` statements, preserving the
1511
+ * default scope.
1512
+ */
1513
+ clearWithScope: () => this
1514
+ ignoreScope: (...scopes: string[]) => this
1515
+ applyScope: (...scopes: string[]) => this
1516
+ allowScope: (...scopes: string[]) => void
1517
+ clearAllowScope: () => void
1518
+ applyFilter: (name: string, ...args: any[]) => this
1519
+ allowFilter: (...filters: string[]) => void
1520
+ withGraph: (
1521
+ expr: objection.RelationExpression<M>,
1522
+ options?: objection.GraphOptions & { algorithm: 'fetch' | 'join' }
1523
+ ) => this
1524
+
1525
+ toSQL: () => string
1526
+ raw: Knex.RawBuilder
1527
+ selectRaw: SetReturnType<Knex.RawBuilder, this>
1528
+ // TODO: add type for Dito's pluck method, which has a different method
1529
+ // signature than the objection one:
1530
+ // pluck: <K extends objection.ModelProps<M>>(
1531
+ // key: K
1532
+ // ) => QueryBuilder<M, ReflectArrayType<R, M[K]>>
1533
+ loadDataPath: (
1534
+ dataPath: string[] | string,
1535
+ options: objection.GraphOptions & { algorithm: 'fetch' | 'join' }
1536
+ ) => this
1537
+
1538
+ upsert: (
1539
+ data: PartialModelObject<M>,
1540
+ options?: {
1541
+ update: boolean
1542
+ fetch: boolean
1543
+ }
1544
+ ) => this
1545
+
1546
+ find: (
1547
+ query: QueryParameterOptions,
1548
+ allowParam?:
1549
+ | QueryParameterOptionKey[]
1550
+ | { [key in keyof QueryParameterOptionKey]: boolean }
1551
+ ) => this
1552
+
1553
+ patchById: (id: Id, data: PartialModelObject<M>) => this
1554
+ updateById: (id: Id, data: PartialModelObject<M>) => this
1555
+ upsertAndFetch: (data: PartialModelObject<M>) => this
1556
+ insertDitoGraph: (
1557
+ data: PartialDitoModelGraph<M>,
1558
+ options?: DitoGraphOptions
1559
+ ) => this
1560
+
1561
+ insertDitoGraphAndFetch: (
1562
+ data: PartialDitoModelGraph<M>,
1563
+ options?: DitoGraphOptions
1564
+ ) => this
1565
+
1566
+ upsertDitoGraph: (
1567
+ data: PartialDitoModelGraph<M>,
1568
+ options?: DitoGraphOptions
1569
+ ) => this
1570
+
1571
+ upsertDitoGraphAndFetch: (data: any, options?: DitoGraphOptions) => this
1572
+ upsertDitoGraphAndFetchById: (
1573
+ id: Id,
1574
+ data: any,
1575
+ options?: DitoGraphOptions
1576
+ ) => QueryBuilder<M, M>
1577
+
1578
+ updateDitoGraph: (
1579
+ data: PartialDitoModelGraph<M>,
1580
+ options?: DitoGraphOptions
1581
+ ) => Promise<any>
1582
+
1583
+ updateDitoGraphAndFetch: (
1584
+ data: PartialDitoModelGraph<M>,
1585
+ options?: DitoGraphOptions
1586
+ ) => this
1587
+
1588
+ updateDitoGraphAndFetchById: (
1589
+ id: Id,
1590
+ data: any,
1591
+ options?: DitoGraphOptions
1592
+ ) => QueryBuilder<M, M>
1593
+
1594
+ patchDitoGraph: (
1595
+ data: PartialDitoModelGraph<M>,
1596
+ options?: DitoGraphOptions
1597
+ ) => this
1598
+
1599
+ patchDitoGraphAndFetch: (
1600
+ data: PartialDitoModelGraph<M>,
1601
+ options?: DitoGraphOptions
1602
+ ) => this
1603
+
1604
+ patchDitoGraphAndFetchById: (
1605
+ id: Id,
1606
+ data: PartialDitoModelGraph<M>,
1607
+ options?: DitoGraphOptions
1608
+ ) => QueryBuilder<M, M>
1609
+ // TODO: static mixin(target)
1610
+
1611
+ ArrayQueryBuilderType: QueryBuilder<M, M[]>
1612
+ SingleQueryBuilderType: QueryBuilder<M, M>
1613
+ NumberQueryBuilderType: QueryBuilder<M, number>
1614
+ PageQueryBuilderType: QueryBuilder<M, objection.Page<M>>
1615
+ }
1616
+ export interface QueryBuilder<M extends Model, R = M[]> extends KnexHelper {}
1617
+
1618
+ export type PartialModelObject<T extends Model> = {
1619
+ [K in objection.NonFunctionPropertyNames<T>]?: objection.Defined<
1620
+ T[K]
1621
+ > extends Model
1622
+ ? T[K]
1623
+ : objection.Defined<T[K]> extends Array<infer I>
1624
+ ? I extends Model
1625
+ ? I[]
1626
+ : objection.Expression<T[K]>
1627
+ : objection.Expression<T[K]>
1628
+ }
1629
+
1630
+ export type PartialDitoModelGraph<M extends Partial<Model>> = {
1631
+ [K in objection.NonFunctionPropertyNames<M>]?: objection.Defined<
1632
+ M[K]
1633
+ > extends Model
1634
+ ? PartialDitoModelGraph<M[K]>
1635
+ : objection.Defined<M[K]> extends Array<infer I>
1636
+ ? I extends Partial<Model>
1637
+ ? PartialDitoModelGraph<I>[]
1638
+ : M[K]
1639
+ : M[K]
1640
+ }
1641
+
1642
+ /* ------------------------------ Start Errors ----------------------------- */
1643
+ export class ResponseError extends Error {
1644
+ constructor()
1645
+ constructor(
1646
+ error:
1647
+ | {
1648
+ /**
1649
+ * The http status code.
1650
+ */
1651
+ status: number
1652
+ /**
1653
+ * The error message.
1654
+ */
1655
+ message?: string
1656
+ /**
1657
+ * An optional code to be used to distinguish different error instances.
1658
+ */
1659
+ code?: string | number
1660
+ }
1661
+ | string,
1662
+ defaults?: { message?: string; status?: number }
1663
+ )
1664
+
1665
+ status: number
1666
+ code?: string | number
1667
+ }
1668
+ export class AssetError extends ResponseError {}
1669
+ export class AuthenticationError extends ResponseError {}
1670
+ export class AuthorizationError extends ResponseError {}
1671
+ export class WrappedError extends ResponseError {}
1672
+ export class DatabaseError extends WrappedError {
1673
+ constructor(
1674
+ error:
1675
+ | dbErrors.CheckViolationError
1676
+ | dbErrors.NotNullViolationError
1677
+ | dbErrors.ConstraintViolationError
1678
+ | dbErrors.DataError
1679
+ | dbErrors.DBError
1680
+ )
1681
+ }
1682
+ export class GraphError extends ResponseError {}
1683
+ export class ModelError extends ResponseError {
1684
+ constructor(model: Class<Model> | Model)
1685
+ }
1686
+ export class NotFoundError extends ResponseError {}
1687
+ export class NotImplementedError extends ResponseError {}
1688
+ export class QueryBuilderError extends ResponseError {}
1689
+ export class RelationError extends ResponseError {}
1690
+ export class ValidationError extends ResponseError {}
1691
+ export class ControllerError extends ResponseError {
1692
+ constructor(controller: { name: string } | { constructor: { name: string } })
1693
+ }
1694
+ /* ------------------------------- End Errors ------------------------------ */
1695
+
1696
+ /* ------------------------------ Start Mixins ----------------------------- */
1697
+ export type Mixin = (
1698
+ target: Object,
1699
+ propertyName: string,
1700
+ propertyDescriptor: PropertyDescriptor
1701
+ ) => void
1702
+
1703
+ type AssetFileObject = {
1704
+ // The unique key within the storage (uuid/v4 + file extension)
1705
+ key: string;
1706
+ // The original filename
1707
+ name: string;
1708
+ // The file's mime-type
1709
+ type: string;
1710
+ // The amount of bytes consumed by the file
1711
+ size: number;
1712
+ // The public url of the file
1713
+ url: string;
1714
+ // The width of the image if the storage defines `config.readImageSize`
1715
+ width: number;
1716
+ // The height of the image if the storage defines `config.readImageSize`
1717
+ height: number;
1718
+ }
1719
+
1720
+ export class AssetModel extends TimeStampedModel {
1721
+ key: string
1722
+ file: AssetFileObject
1723
+ storage: string
1724
+ count: number
1725
+ }
1726
+
1727
+ export const AssetMixin: <T extends Constructor>(target: T) =>
1728
+ Constructor<InstanceType<T> & {
1729
+ key: string;
1730
+ file: AssetFileObject;
1731
+ storage: string;
1732
+ count: number;
1733
+ }>
1734
+
1735
+ type TimeStampedMixinProperties = {
1736
+ createdAt: Date;
1737
+ updatedAt: Date;
1738
+ };
1739
+
1740
+ export class TimeStampedModel extends Model {
1741
+ createdAt: Date
1742
+ updatedAt: Date
1743
+ }
1744
+
1745
+ export const TimeStampedMixin: <T extends Constructor>(target: T) =>
1746
+ Constructor<InstanceType<T> & AssetMixinModelProperties>
1747
+
1748
+ export class UserModel extends Model {
1749
+ static options?: {
1750
+ usernameProperty?: string
1751
+ passwordProperty?: string
1752
+ /**
1753
+ * This option can be used to specify (eager) scopes to be applied when
1754
+ * the user is deserialized from the session.
1755
+ */
1756
+ sessionScope?: OrArrayOf<string>
1757
+ }
1758
+
1759
+ username: string
1760
+ password: string
1761
+ hash: string
1762
+ lastLogin?: Date
1763
+
1764
+ $verifyPassword(password: string): Promise<boolean>
1765
+
1766
+ $hasRole(...roles: string[]): boolean
1767
+
1768
+ $hasOwner(owner: UserModel): boolean
1769
+
1770
+ $isLoggedIn(ctx: KoaContext): boolean
1771
+
1772
+ // TODO: type options
1773
+ static login(ctx: KoaContext, options: any): Promise<void>
1774
+
1775
+ static sessionQuery(trx: Knex.Transaction): QueryBuilder<UserModel>
1776
+ }
1777
+
1778
+ export class SessionModel extends Model {
1779
+ id: string
1780
+ value: {[key: string]: any }
1781
+ }
1782
+
1783
+ export const SessionMixin: <T extends Constructor>(target: T) =>
1784
+ Constructor<InstanceType<T> & AssetMixinModelProperties>
1785
+
1786
+ export const UserMixin: <T extends Constructor>(target: T) =>
1787
+ Constructor<InstanceType<T> & {
1788
+ id: string;
1789
+ value: {[key: string]: any }
1790
+ }>
1791
+
1792
+ /**
1793
+ * Apply the action mixin to a controller action, in order to
1794
+ * determine which HTTP method (`'get'`, `'post'`, `'put'`, `'delete'` or
1795
+ * `'patch'`) the action should listen to and optionally the path to which it
1796
+ * is mapped, defined in relation to the route path of its controller. By
1797
+ * default, the normalized method name is used as the action's path, and
1798
+ * the `'get'` method is assigned if none is provided.
1799
+ */
1800
+ export const action: (method: string, path: string) => Mixin
1801
+
1802
+ /**
1803
+ * Apply the authorize mixin to a controller action, in order to
1804
+ * determines whether or how the request is authorized. This value can
1805
+ * either be one of the values as described below, an array of them or
1806
+ * a function which returns one or more of them.
1807
+ *
1808
+ * - boolean: `true` if the action should be authorized, `false` otherwise.
1809
+ * - '$self': The requested member is checked against `ctx.state.user`
1810
+ * and the action is only authorized if it matches the member.
1811
+ * - '$owner': The member is asked if it is owned by `ctx.state.user`
1812
+ * through the optional `Model.$hasOwner()` method.
1813
+ * - any string: `ctx.state.user` is checked for this role through
1814
+ * the overridable `UserModel.hasRole()` method.
1815
+ */
1816
+ export const authorize: (
1817
+ authorize: (ctx: KoaContext) => void | boolean | OrArrayOf<string>
1818
+ ) => Mixin
1819
+
1820
+ /**
1821
+ * Apply the parameters mixin to a controller action, in order to
1822
+ * apply automatic mapping of Koa.js' `ctx.query` object to method parameters
1823
+ * along with their automatic validation.
1824
+ *
1825
+ * @see {@link https://github.com/ditojs/dito/blob/master/docs/model-properties.md Model Properties}
1826
+ */
1827
+ export const parameters: (params: { [key: string]: Schema }) => Mixin
1828
+
1829
+ /**
1830
+ * Apply the returns mixin to a controller action, in order to
1831
+ * provide a schema for the value returned from the action handler and
1832
+ * optionally map the value to a key inside a returned object when it
1833
+ * contains a `name` property.
1834
+ */
1835
+ export const returns: (
1836
+ returns: Schema & { name?: string },
1837
+ options: any
1838
+ ) => Mixin
1839
+
1840
+ /**
1841
+ * Apply the scope mixin to a controller action, in order to
1842
+ * determine the scope(s) to be applied when loading the relation's models.
1843
+ * The scope needs to be defined in the related model class' scopes
1844
+ * definitions.
1845
+ */
1846
+ export const scope: (...scopes: string[]) => Mixin
1847
+
1848
+ /**
1849
+ * Apply the transacted mixin to a controller action in order to
1850
+ * determine whether queries in the action should be executed within a
1851
+ * transaction. Any failure will mean the database will rollback any queries
1852
+ * executed to the pre-transaction state.
1853
+ */
1854
+ export const transacted: () => Mixin
1855
+
1856
+ /* ------------------------------ End Mixins ----------------------------- */
1857
+
1858
+ export type HTTPMethod =
1859
+ | 'get'
1860
+ | 'post'
1861
+ | 'put'
1862
+ | 'delete'
1863
+ | 'patch'
1864
+ | 'options'
1865
+ | 'trace'
1866
+ | 'connect'
1867
+
1868
+ export interface KnexHelper {
1869
+ getDialect(): string
1870
+
1871
+ isPostgreSQL(): boolean
1872
+
1873
+ isMySQL(): boolean
1874
+
1875
+ isSQLite(): boolean
1876
+
1877
+ isMsSQL(): boolean
1878
+ }
1879
+
1880
+ export type Keyword =
1881
+ | SetOptional<Ajv.MacroKeywordDefinition, 'keyword'>
1882
+ | SetOptional<Ajv.CodeKeywordDefinition, 'keyword'>
1883
+ | SetOptional<Ajv.FuncKeywordDefinition, 'keyword'>;
1884
+ export type Format = Ajv.ValidateFunction | Ajv.FormatDefinition<string>
1885
+ export type Id = string | number
1886
+ export type KoaContext<$State = any> = Koa.ParameterizedContext<
1887
+ $State,
1888
+ {
1889
+ transaction: objection.Transaction
1890
+ session: koaSession.ContextSession & { state: { user: any } }
1891
+ }
1892
+ >
1893
+
1894
+ type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>);
1895
+
1896
+ type ReflectArrayType<Source, Target> = Source extends any[] ? Target[] : Target
1897
+
1898
+ type OrArrayOf<T> = T[] | T
1899
+
1900
+ type OrReadOnly<T> = Readonly<T> | T
1901
+
1902
+ type OrPromiseOf<T> = Promise<T> | T
1903
+
1904
+ type modelFromModelController<$ModelController extends ModelController<Model>> =
1905
+ InstanceType<Exclude<$ModelController['modelClass'], undefined>>
1906
+
1907
+ export type SelectModelProperties<T> = {
1908
+ [$Key in SelectModelKeys<T>]: T[$Key] extends Model
1909
+ ? SelectModelProperties<T[$Key]>
1910
+ : T[$Key]
1911
+ }
1912
+
1913
+ // https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
1914
+ type AnyGate<$CheckType, $TypeWhenNotAny, $TypeWhenAny = $CheckType> =
1915
+ 0 extends 1 & $CheckType ? $TypeWhenAny : $TypeWhenNotAny
1916
+
1917
+ export type SelectModelKeys<T> = AnyGate<
1918
+ T,
1919
+ Exclude<
1920
+ keyof ConditionalExcept<T, Function>,
1921
+ `$${string}` | 'QueryBuilderType' | 'foreignKeyId'
1922
+ >,
1923
+ string
1924
+ >
1925
+
1926
+ /* ----------------------- Extended from Ajv JSON Schema ---------------------- */
1927
+
1928
+ export type Schema<T = any> = JSONSchemaType<T> & {
1929
+ // keywords/_validate.js
1930
+ validate?: (params: {
1931
+ data: any
1932
+ parentData: object | any[]
1933
+ rootData: object | any[]
1934
+ dataPath: string
1935
+ parentIndex?: number
1936
+ parentKey?: string
1937
+ app: Application<Models>
1938
+ validator: Validator
1939
+ options: any
1940
+ }) => boolean | void
1941
+
1942
+ // keywords/_validate.js
1943
+ validateAsync?: (params: {
1944
+ data: any
1945
+ parentData: object | any[]
1946
+ rootData: object | any[]
1947
+ dataPath: string
1948
+ parentIndex?: number
1949
+ parentKey?: string
1950
+ app: Application<Models>
1951
+ validator: Validator
1952
+ options: any
1953
+ }) => Promise<boolean | void>
1954
+
1955
+ // keywords/_instanceof.js
1956
+ /**
1957
+ * Validates whether the value is an instance of at least one of the
1958
+ * passed types.
1959
+ */
1960
+ instanceof?: OrArrayOf<
1961
+ | LiteralUnion<
1962
+ | 'Object'
1963
+ | 'Array'
1964
+ | 'Function'
1965
+ | 'String'
1966
+ | 'Number'
1967
+ | 'Boolean'
1968
+ | 'Date'
1969
+ | 'RegExp'
1970
+ | 'Buffer'
1971
+ >
1972
+ | Function
1973
+ | typeof Object
1974
+ | typeof Array
1975
+ | typeof Function
1976
+ | typeof String
1977
+ | typeof Number
1978
+ | typeof Boolean
1979
+ | typeof Date
1980
+ | typeof RegExp
1981
+ | typeof Buffer
1982
+ >
1983
+ }
1984
+
1985
+ declare type StrictNullChecksWrapper<Name extends string, Type> = undefined extends null ? `strictNullChecks must be true in tsconfig to use ${Name}` : Type;
1986
+ declare type UnionToIntersection<U> = (U extends any ? (_: U) => void : never) extends (_: infer I) => void ? I : never;
1987
+ declare type SomeJSONSchema = UncheckedJSONSchemaType<Known, true>;
1988
+ declare type UncheckedPartialSchema<T> = Partial<UncheckedJSONSchemaType<T, true>>;
1989
+ declare type PartialSchema<T> = StrictNullChecksWrapper<'PartialSchema', UncheckedPartialSchema<T>>;
1990
+ declare type JSONType<T extends string, IsPartial extends boolean> = IsPartial extends true ? T | undefined : T;
1991
+ interface NumberKeywords {
1992
+ minimum?: number;
1993
+ maximum?: number;
1994
+ exclusiveMinimum?: number;
1995
+ exclusiveMaximum?: number;
1996
+ multipleOf?: number;
1997
+ format?: string;
1998
+ range?: [number, number]
1999
+ }
2000
+ interface StringKeywords {
2001
+ minLength?: number;
2002
+ maxLength?: number;
2003
+ pattern?: string;
2004
+ format?: LiteralUnion<
2005
+ | 'date'
2006
+ | 'time'
2007
+ | 'uri'
2008
+ | 'uri-reference'
2009
+ | 'uri-template'
2010
+ | 'email'
2011
+ | 'hostname'
2012
+ | 'ipv4'
2013
+ | 'ipv6'
2014
+ | 'uuid'
2015
+ | 'json-pointer'
2016
+ | 'relative-json-pointer'
2017
+ | 'datetime'
2018
+ | 'timestamp'
2019
+ >;
2020
+ }
2021
+ declare type UncheckedJSONSchemaType<T, IsPartial extends boolean> = (// these two unions allow arbitrary unions of types
2022
+ {
2023
+ anyOf: readonly UncheckedJSONSchemaType<T, IsPartial>[];
2024
+ } | {
2025
+ oneOf: readonly UncheckedJSONSchemaType<T, IsPartial>[];
2026
+ } | ({
2027
+ type: readonly (T extends number ? JSONType<'number' | 'integer', IsPartial> : T extends string ? JSONType<'string', IsPartial> : T extends boolean ? JSONType<'boolean', IsPartial> : never)[];
2028
+ } & UnionToIntersection<T extends number ? NumberKeywords : T extends string ? StringKeywords : T extends boolean ? {} : never>) | ((T extends number ? {
2029
+ type: JSONType<'number' | 'integer', IsPartial>;
2030
+ } & NumberKeywords : T extends string ? ({
2031
+ type: JSONType<'string' | 'text' | 'date' | 'datetime' | 'timestamp', IsPartial>;
2032
+ } & StringKeywords) : T extends Date ? ({
2033
+ type: JSONType<'date' | 'datetime' | 'timestamp', IsPartial>;
2034
+ }) : T extends boolean ? {
2035
+ type: JSONType<'boolean', IsPartial>;
2036
+ } : T extends readonly [any, ...any[]] ? {
2037
+ type: JSONType<'array', IsPartial>;
2038
+ items: {
2039
+ readonly [K in keyof T]-?: UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>;
2040
+ } & {
2041
+ length: T['length'];
2042
+ };
2043
+ minItems: T['length'];
2044
+ } & ({
2045
+ maxItems: T['length'];
2046
+ } | {
2047
+ additionalItems: false;
2048
+ }) : T extends readonly any[] ? {
2049
+ type: JSONType<'array', IsPartial>;
2050
+ items: UncheckedJSONSchemaType<T[0], false>;
2051
+ contains?: UncheckedPartialSchema<T[0]>;
2052
+ minItems?: number;
2053
+ maxItems?: number;
2054
+ minContains?: number;
2055
+ maxContains?: number;
2056
+ uniqueItems?: true;
2057
+ additionalItems?: never;
2058
+ } : T extends Record<string, any> ? {
2059
+ type: JSONType<'object', IsPartial>;
2060
+ additionalProperties?: boolean | UncheckedJSONSchemaType<T[string], false>;
2061
+ unevaluatedProperties?: boolean | UncheckedJSONSchemaType<T[string], false>;
2062
+ properties?: IsPartial extends true ? Partial<UncheckedPropertiesSchema<T>> : UncheckedPropertiesSchema<T>;
2063
+ patternProperties?: Record<string, UncheckedJSONSchemaType<T[string], false>>;
2064
+ propertyNames?: Omit<UncheckedJSONSchemaType<string, false>, 'type'> & {
2065
+ type?: 'string';
2066
+ };
2067
+ dependencies?: {
2068
+ [K in keyof T]?: Readonly<(keyof T)[]> | UncheckedPartialSchema<T>;
2069
+ };
2070
+ dependentRequired?: {
2071
+ [K in keyof T]?: Readonly<(keyof T)[]>;
2072
+ };
2073
+ dependentSchemas?: {
2074
+ [K in keyof T]?: UncheckedPartialSchema<T>;
2075
+ };
2076
+ minProperties?: number;
2077
+ maxProperties?: number;
2078
+ } & (IsPartial extends true ? {
2079
+ required: Readonly<(keyof T)[] | boolean>;
2080
+ } : [UncheckedRequiredMembers<T>] extends [never] ? {
2081
+ required?: Readonly<UncheckedRequiredMembers<T>[]> | boolean;
2082
+ } : {
2083
+ required: Readonly<UncheckedRequiredMembers<T>[]> | boolean;
2084
+ }) : T extends null ? {
2085
+ type: JSONType<'null', IsPartial>;
2086
+ nullable: true;
2087
+ } : never) & {
2088
+ allOf?: Readonly<UncheckedPartialSchema<T>[]>;
2089
+ anyOf?: Readonly<UncheckedPartialSchema<T>[]>;
2090
+ oneOf?: Readonly<UncheckedPartialSchema<T>[]>;
2091
+ if?: UncheckedPartialSchema<T>;
2092
+ then?: UncheckedPartialSchema<T>;
2093
+ else?: UncheckedPartialSchema<T>;
2094
+ not?: UncheckedPartialSchema<T>;
2095
+ })) & {
2096
+ [keyword: string]: any;
2097
+ $id?: string;
2098
+ $ref?: string;
2099
+ $defs?: Record<string, UncheckedJSONSchemaType<Known, true>>;
2100
+ definitions?: Record<string, UncheckedJSONSchemaType<Known, true>>;
2101
+ };
2102
+ declare type JSONSchemaType<T> = StrictNullChecksWrapper<'JSONSchemaType', UncheckedJSONSchemaType<T, false>>;
2103
+ declare type Known = {
2104
+ [key: string]: Known;
2105
+ } | [Known, ...Known[]] | Known[] | number | string | boolean | null;
2106
+ declare type UncheckedPropertiesSchema<T> = {
2107
+ [K in keyof T]-?: (UncheckedJSONSchemaType<T[K], false> & Nullable<T[K]>) | {
2108
+ $ref: string;
2109
+ };
2110
+ };
2111
+ declare type PropertiesSchema<T> = StrictNullChecksWrapper<'PropertiesSchema', UncheckedPropertiesSchema<T>>;
2112
+ declare type UncheckedRequiredMembers<T> = {
2113
+ [K in keyof T]-?: undefined extends T[K] ? never : K;
2114
+ }[keyof T];
2115
+ declare type RequiredMembers<T> = StrictNullChecksWrapper<'RequiredMembers', UncheckedRequiredMembers<T>>;
2116
+ declare type Nullable<T> = undefined extends T ? {
2117
+ nullable: true;
2118
+ const?: null;
2119
+ enum?: Readonly<(T | null)[]>;
2120
+ default?: T | null;
2121
+ } : {
2122
+ const?: T;
2123
+ enum?: Readonly<T[]>;
2124
+ default?: T;
2125
+ };