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