@ditojs/server 1.12.0 → 1.13.1

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