@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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { QueryBuilder } from '../query/index.js'
|
|
2
|
+
|
|
3
|
+
export default class RelationAccessor {
|
|
4
|
+
constructor(relation, modelClass, model) {
|
|
5
|
+
this.relation = relation
|
|
6
|
+
this.name = relation.name
|
|
7
|
+
this.modelClass = modelClass
|
|
8
|
+
this.model = model
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
query(trx) {
|
|
12
|
+
return this.modelClass
|
|
13
|
+
? this.modelClass.relatedQuery(this.name, trx)
|
|
14
|
+
: this.model.$relatedQuery(this.name, trx)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
load(arg0, ...args) {
|
|
18
|
+
return this.modelClass
|
|
19
|
+
? this.modelClass.loadRelated(arg0, this.name, ...args)
|
|
20
|
+
: this.model.$loadRelated(this.name, arg0, ...args)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Accessor to provide simplified access to the implicitly generated join
|
|
25
|
+
* model class, handling the passing on of the knex instance, as well as
|
|
26
|
+
* the application of `QueryBuilder.mixin()`.
|
|
27
|
+
*/
|
|
28
|
+
get joinModelClass() {
|
|
29
|
+
const { joinModelClass } = this.relation
|
|
30
|
+
// Result is already cached per knex by `this.relation.joinModelClass()`,
|
|
31
|
+
// so all that's left to do is apply `QueryBuilder.mixin()`,
|
|
32
|
+
// if there is no `joinModelClass.where()` yet:
|
|
33
|
+
if (joinModelClass && !('where' in joinModelClass)) {
|
|
34
|
+
QueryBuilder.mixin(joinModelClass)
|
|
35
|
+
}
|
|
36
|
+
return joinModelClass
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Expose a selection of QueryBuilder methods as instance methods on relations.
|
|
41
|
+
QueryBuilder.mixin(RelationAccessor.prototype)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { isObject, isFunction, deprecate } from '@ditojs/utils'
|
|
2
|
+
import { processHandlerParameters } from '../../utils/handler.js'
|
|
3
|
+
import { mergeReversed } from '../../utils/object.js'
|
|
4
|
+
import { QueryFilters } from '../../query/index.js'
|
|
5
|
+
|
|
6
|
+
export default function filters(values) {
|
|
7
|
+
const filters = {}
|
|
8
|
+
for (const [name, definition] of Object.entries(mergeReversed(values))) {
|
|
9
|
+
const filter = isFunction(definition)
|
|
10
|
+
? definition
|
|
11
|
+
: isObject(definition)
|
|
12
|
+
? convertFilterObject(name, definition)
|
|
13
|
+
: null
|
|
14
|
+
if (!filter) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Invalid filter '${name}': Unrecognized definition: ${definition}.`
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
filters[name] = wrapWithValidation(filter, name, this.app)
|
|
20
|
+
}
|
|
21
|
+
return filters
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function convertFilterObject(name, object) {
|
|
25
|
+
const addHandlerSettings = (handler, definition) => {
|
|
26
|
+
// Copy over parameters, returns and their validation options settings.
|
|
27
|
+
const {
|
|
28
|
+
parameters,
|
|
29
|
+
// TODO: `returns` was deprecated in May 2025 in favour of `response`.
|
|
30
|
+
// Remove this in 2026.
|
|
31
|
+
returns,
|
|
32
|
+
response = returns,
|
|
33
|
+
...rest
|
|
34
|
+
} = definition
|
|
35
|
+
if (returns) {
|
|
36
|
+
deprecate(
|
|
37
|
+
'The `returns` property is deprecated in favour of `response`. ' +
|
|
38
|
+
'Update your handler definition to use `response` instead.'
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
processHandlerParameters(handler, 'parameters', parameters)
|
|
42
|
+
processHandlerParameters(handler, 'response', response)
|
|
43
|
+
return Object.assign(handler, rest)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { handler, filter, properties } = object
|
|
47
|
+
if (handler) {
|
|
48
|
+
return addHandlerSettings(handler, object)
|
|
49
|
+
} else if (filter) {
|
|
50
|
+
// Convert QueryFilter to normal filter function.
|
|
51
|
+
const queryFilter = QueryFilters.get(filter)
|
|
52
|
+
if (!queryFilter) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Invalid filter '${name}': Unknown filter type '${filter}'.`
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
// Support both object and function definitions.
|
|
58
|
+
const queryHandler = isObject(queryFilter)
|
|
59
|
+
? queryFilter.handler
|
|
60
|
+
: queryFilter
|
|
61
|
+
const func = properties
|
|
62
|
+
? (query, ...args) => {
|
|
63
|
+
// When the filter provides multiple properties, match them
|
|
64
|
+
// all, but combine the expressions with OR.
|
|
65
|
+
for (const property of properties) {
|
|
66
|
+
query.orWhere(query => queryHandler(query, property, ...args))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
: (query, ...args) => {
|
|
70
|
+
queryHandler(query, name, ...args)
|
|
71
|
+
}
|
|
72
|
+
return addHandlerSettings(func, queryFilter)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function wrapWithValidation(filter, name, app) {
|
|
77
|
+
if (filter) {
|
|
78
|
+
// TODO: Implement `response` validation for filters too.
|
|
79
|
+
// TODO: Share additional coercion handling with
|
|
80
|
+
// `ControllerAction#coerceValue()`
|
|
81
|
+
const { parameters, options = {} } = filter
|
|
82
|
+
// If parameters are defined, wrap the function in a closure that
|
|
83
|
+
// performs parameter validation...
|
|
84
|
+
const dataName = 'query'
|
|
85
|
+
const validator = app.compileParametersValidator(parameters, {
|
|
86
|
+
...options.parameters,
|
|
87
|
+
dataName
|
|
88
|
+
})
|
|
89
|
+
if (validator?.validate) {
|
|
90
|
+
return (query, ...args) => {
|
|
91
|
+
// Convert args to object for validation:
|
|
92
|
+
const object = {}
|
|
93
|
+
let index = 0
|
|
94
|
+
for (const { name } of validator.list) {
|
|
95
|
+
// Use dataName if no name is given, see:
|
|
96
|
+
// Application.compileParametersValidator()
|
|
97
|
+
object[name || dataName] = args[index++]
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
validator.validate(object)
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw app.createValidationError({
|
|
103
|
+
type: 'FilterValidation',
|
|
104
|
+
message: `The provided data for query filter '${
|
|
105
|
+
name
|
|
106
|
+
}' is not valid`,
|
|
107
|
+
errors: app.validator.prefixInstancePaths(
|
|
108
|
+
error.errors,
|
|
109
|
+
`.${name}`
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
return validator.asObject
|
|
114
|
+
? filter(query, object)
|
|
115
|
+
: filter(query, ...args)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ...otherwise use the defined filter function unmodified.
|
|
120
|
+
return filter
|
|
121
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { mergeAsReversedArrays } from '../../utils/object.js'
|
|
2
|
+
|
|
3
|
+
export default function hooks(values) {
|
|
4
|
+
// Use `mergeAsReversedArrays()` so that for each event there is an array
|
|
5
|
+
// of hooks in sequence from base class to sub class, so that the hooks
|
|
6
|
+
// from the base class are run first by `_emitStaticHook()`.
|
|
7
|
+
return mergeAsReversedArrays(values)
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import options from './options.js'
|
|
2
|
+
import properties from './properties.js'
|
|
3
|
+
import relations from './relations.js'
|
|
4
|
+
import schema from './schema.js'
|
|
5
|
+
import scopes from './scopes.js'
|
|
6
|
+
import filters from './filters.js'
|
|
7
|
+
import modifiers from './modifiers.js'
|
|
8
|
+
import assets from './assets.js'
|
|
9
|
+
import hooks from './hooks.js'
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
// Export options first, as other definitions may rely on them, e.g. UserMixin
|
|
13
|
+
options,
|
|
14
|
+
properties,
|
|
15
|
+
relations,
|
|
16
|
+
schema,
|
|
17
|
+
scopes,
|
|
18
|
+
filters,
|
|
19
|
+
modifiers,
|
|
20
|
+
assets,
|
|
21
|
+
hooks
|
|
22
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { mergeReversed } from '../../utils/object.js'
|
|
2
|
+
|
|
3
|
+
export default function properties(values) {
|
|
4
|
+
const properties = mergeReversed(values)
|
|
5
|
+
// Include auto-generated 'id' properties for models and relations.
|
|
6
|
+
const addIdProperty = (name, schema) => {
|
|
7
|
+
if (!(name in properties)) {
|
|
8
|
+
properties[name] = {
|
|
9
|
+
type: 'integer',
|
|
10
|
+
...schema
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
for (const name of this.getIdPropertyArray()) {
|
|
16
|
+
addIdProperty(name, {
|
|
17
|
+
primary: true
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const addRelationProperties = (relation, propName) => {
|
|
22
|
+
const modelClass = relation.ownerModelClass
|
|
23
|
+
const { nullable } = modelClass.definition.relations[relation.name]
|
|
24
|
+
for (const property of relation[propName].props) {
|
|
25
|
+
addIdProperty(property, {
|
|
26
|
+
unsigned: true,
|
|
27
|
+
foreign: true,
|
|
28
|
+
index: true,
|
|
29
|
+
...(nullable && { nullable })
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const relation of Object.values(this.getRelations())) {
|
|
35
|
+
addRelationProperties(relation, 'ownerProp')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const relation of this.getRelatedRelations()) {
|
|
39
|
+
addRelationProperties(relation, 'relatedProp')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Support Objection's #id and #ref references on all models:
|
|
43
|
+
properties[this.uidProp] = {
|
|
44
|
+
type: 'string'
|
|
45
|
+
}
|
|
46
|
+
properties[this.uidRefProp] = {
|
|
47
|
+
type: 'string'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Convert root-level short-forms, for easier properties handling in
|
|
51
|
+
// attributes and idColumn() & co:
|
|
52
|
+
// - `name: type` to `name: { type }`
|
|
53
|
+
// - `name: [...items]` to `name: { type: 'array', items }
|
|
54
|
+
// NOTE: Substitutions on all other levels happen in convertSchema()
|
|
55
|
+
const ids = []
|
|
56
|
+
const rest = []
|
|
57
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
58
|
+
// Also sort properties by kind: primary id > foreign id > rest:
|
|
59
|
+
const entry = [name, property]
|
|
60
|
+
if (property.primary) {
|
|
61
|
+
ids.unshift(entry)
|
|
62
|
+
} else if (property.foreign) {
|
|
63
|
+
ids.push(entry)
|
|
64
|
+
} else {
|
|
65
|
+
rest.push(entry)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Finally recompile a new properties object out of the sorted properties.
|
|
69
|
+
return [...ids, ...rest].reduce((merged, [name, property]) => {
|
|
70
|
+
merged[name] = property
|
|
71
|
+
return merged
|
|
72
|
+
}, {})
|
|
73
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isObject, isFunction } from '@ditojs/utils'
|
|
2
|
+
import { ModelError } from '../../errors/index.js'
|
|
3
|
+
import { mergeReversed } from '../../utils/object.js'
|
|
4
|
+
|
|
5
|
+
export default function scopes(values) {
|
|
6
|
+
const scopes = {}
|
|
7
|
+
// Use mergeAsReversedArrays() to keep lists of filters to be inherited per
|
|
8
|
+
// scope, so they can be called in sequence.
|
|
9
|
+
for (const [name, scope] of Object.entries(mergeReversed(values))) {
|
|
10
|
+
const func = isFunction(scope)
|
|
11
|
+
? scope
|
|
12
|
+
: isObject(scope)
|
|
13
|
+
? query => query.find(scope)
|
|
14
|
+
: null
|
|
15
|
+
if (!func) {
|
|
16
|
+
throw new Error(`Invalid scope '${name}': ${scope}.`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parentModelClass = Object.getPrototypeOf(this)
|
|
20
|
+
|
|
21
|
+
const applyParentScope = query => {
|
|
22
|
+
const parentScope = parentModelClass.getScope(name)
|
|
23
|
+
if (!parentScope) {
|
|
24
|
+
throw new ModelError(this, `Undefined parent scope: '${name}'`)
|
|
25
|
+
}
|
|
26
|
+
parentScope(query)
|
|
27
|
+
return query
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
scopes[name] = query => {
|
|
31
|
+
func(query, applyParentScope)
|
|
32
|
+
return query
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return scopes
|
|
36
|
+
}
|