@ditojs/server 2.48.0 → 2.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "2.48.0",
3
+ "version": "2.49.1",
4
4
  "type": "module",
5
5
  "description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
6
6
  "repository": "https://github.com/ditojs/dito/tree/master/packages/server",
@@ -25,10 +25,10 @@
25
25
  "node >= 18"
26
26
  ],
27
27
  "dependencies": {
28
- "@ditojs/admin": "^2.48.0",
29
- "@ditojs/build": "^2.48.0",
30
- "@ditojs/router": "^2.48.0",
31
- "@ditojs/utils": "^2.48.0",
28
+ "@ditojs/admin": "^2.49.1",
29
+ "@ditojs/build": "^2.49.1",
30
+ "@ditojs/router": "^2.49.0",
31
+ "@ditojs/utils": "^2.49.0",
32
32
  "@koa/cors": "^5.0.0",
33
33
  "@koa/multer": "^3.1.0",
34
34
  "@originjs/vite-plugin-commonjs": "^1.0.3",
@@ -66,7 +66,7 @@
66
66
  "pino-pretty": "^13.0.0",
67
67
  "pluralize": "^8.0.0",
68
68
  "repl": "^0.1.3",
69
- "type-fest": "^4.40.1",
69
+ "type-fest": "^4.41.0",
70
70
  "uuid": "^11.1.0"
71
71
  },
72
72
  "peerDependencies": {
@@ -84,11 +84,11 @@
84
84
  "@types/koa-session": "^6.4.5",
85
85
  "@types/koa-static": "^4.0.4",
86
86
  "@types/koa__cors": "^5.0.0",
87
- "@types/node": "^22.15.3",
87
+ "@types/node": "^22.15.17",
88
88
  "knex": "^3.1.0",
89
89
  "objection": "^3.1.5",
90
90
  "typescript": "^5.8.3"
91
91
  },
92
92
  "types": "types",
93
- "gitHead": "bacff1f9a6b314fad4f01b4a83d5cf4778d344e1"
93
+ "gitHead": "45e0fcf6cc3616b095ad32928cba034198b01ba0"
94
94
  }
@@ -3,10 +3,15 @@ import Ajv from 'ajv/dist/2020.js'
3
3
  import addFormats from 'ajv-formats'
4
4
  import { isArray, isObject, clone, isAsync, isPromise } from '@ditojs/utils'
5
5
  import { formatJson } from '../utils/json.js'
6
- import * as schema from '../schema/index.js'
6
+ import {
7
+ keywords as defaultKeywords,
8
+ formats as defaultFormats,
9
+ types as defaultTypes,
10
+ convertSchema
11
+ } from '../schema/index.js'
7
12
 
8
- // Dito does not rely on objection.AjvValidator but instead implements its own
9
- // validator instance that is shared across the whole app and handles schema
13
+ // Dito.js does not rely on objection.AjvValidator but instead implements its
14
+ // own validator instance that is shared across the whole app and handles schema
10
15
  // compilation and caching differently:
11
16
  // It relies on Ajv's addSchema() / getSchema() pattern in conjunction with the
12
17
  // `schemaId: '$id'` option, and each schema is assigned an $id based on the
@@ -14,23 +19,25 @@ import * as schema from '../schema/index.js'
14
19
  // easily validate nested structures.
15
20
 
16
21
  export class Validator extends objection.Validator {
17
- constructor({ options, keywords, formats } = {}) {
22
+ constructor({ options, keywords, formats, types } = {}) {
18
23
  super()
19
24
 
20
25
  this.options = {
21
26
  ...defaultOptions,
22
27
  ...options
23
28
  }
24
-
25
29
  this.keywords = {
26
- ...schema.keywords,
30
+ ...defaultKeywords,
27
31
  ...keywords
28
32
  }
29
-
30
33
  this.formats = {
31
- ...schema.formats,
34
+ ...defaultFormats,
32
35
  ...formats
33
36
  }
37
+ this.types = {
38
+ ...defaultTypes,
39
+ ...types
40
+ }
34
41
 
35
42
  this.schemas = []
36
43
 
@@ -71,6 +78,13 @@ export class Validator extends objection.Validator {
71
78
  })
72
79
  )
73
80
 
81
+ addSchemas(this.types, (type, schema) => {
82
+ ajv.addSchema({
83
+ $id: type,
84
+ ...this.processSchema(convertSchema(schema), options)
85
+ })
86
+ })
87
+
74
88
  // Also add all model schemas that were already compiled so far.
75
89
  for (const schema of this.schemas) {
76
90
  ajv.addSchema(this.processSchema(schema, options))
@@ -32,7 +32,7 @@ export default async function startConsole(app, config) {
32
32
  server.eval = wrapEval(server)
33
33
 
34
34
  server.defineCommand('usage', {
35
- help: 'Detailed Dito Console usage information',
35
+ help: 'Detailed Dito.js Console usage information',
36
36
  action() {
37
37
  displayUsage(app, config, true)
38
38
  this.displayPrompt()
@@ -40,7 +40,7 @@ export default async function startConsole(app, config) {
40
40
  })
41
41
 
42
42
  server.defineCommand('models', {
43
- help: 'Display available Dito models',
43
+ help: 'Display available Dito.js models',
44
44
  action() {
45
45
  console.info(Object.keys(app.models).join(', '))
46
46
  this.displayPrompt()
@@ -99,13 +99,13 @@ function displayUsage(app, config, details) {
99
99
  console.info(deindent`
100
100
 
101
101
  ------------------------------------------------------------
102
- Dito Console
102
+ Dito.js Console
103
103
 
104
104
  Available references:
105
- - Dito app: ${pico.cyan('app')}
105
+ - Dito.js app: ${pico.cyan('app')}
106
106
  ${
107
107
  modelHandleNames.length > 0
108
- ? ` - Dito models: ${
108
+ ? ` - Dito.js models: ${
109
109
  modelHandleNames.map(m => pico.cyan(m)).join(', ')
110
110
  }`
111
111
  : ''
@@ -300,7 +300,7 @@ export default class ControllerAction {
300
300
  if (objectType) {
301
301
  if (value && isString(value)) {
302
302
  if (!/^\{.*\}$/.test(value)) {
303
- // Convert simplified Dito object notation to JSON, supporting:
303
+ // Convert simplified Dito.js object notation to JSON, supporting:
304
304
  // - `"key1":X, "key2":Y` (curly braces are added and parsed through
305
305
  // `JSON.parse()`)
306
306
  // - `key1:X,key2:Y` (a simple parser is applied, splitting into
@@ -329,7 +329,7 @@ export default class ControllerAction {
329
329
  }
330
330
  }
331
331
  if (objectType !== 'object' && isObject(value)) {
332
- // Convert the Pojo to the desired Dito model:
332
+ // Convert the Pojo to the desired Dito.js model:
333
333
  const modelClass = this.app.models[objectType]
334
334
  if (modelClass && !(value instanceof modelClass)) {
335
335
  value = modelClass.fromJson(value, modelOptions)
@@ -343,7 +343,7 @@ export class Model extends objection.Model {
343
343
  },
344
344
  {}
345
345
  ),
346
- additionalProperties: false
346
+ unevaluatedProperties: false
347
347
  },
348
348
  {
349
349
  type: 'object',
@@ -352,7 +352,7 @@ export class Model extends objection.Model {
352
352
  type: 'string'
353
353
  }
354
354
  },
355
- additionalProperties: false
355
+ unevaluatedProperties: false
356
356
  }
357
357
  ]
358
358
  },
@@ -387,13 +387,21 @@ export class Model extends objection.Model {
387
387
  return this._getCached(
388
388
  'jsonSchema',
389
389
  () => {
390
- const schema = convertSchema({
391
- type: 'object',
392
- properties: this.definition.properties
393
- })
390
+ const definitions = {}
391
+ const schema = convertSchema(
392
+ {
393
+ type: 'object',
394
+ properties: this.definition.properties
395
+ },
396
+ { definitions }
397
+ )
394
398
  addRelationSchemas(this, schema.properties)
395
399
  // Merge in root-level schema additions
396
400
  assignDeeply(schema, this.definition.schema)
401
+ // Merge in definitions
402
+ if (Object.keys(definitions).length > 0) {
403
+ schema.definitions = definitions
404
+ }
397
405
  return {
398
406
  $id: this.name,
399
407
  ...schema
@@ -467,7 +475,17 @@ export class Model extends objection.Model {
467
475
  static getAttributes(filter) {
468
476
  const attributes = []
469
477
  const { properties } = this.definition
470
- for (const [name, property] of Object.entries(properties)) {
478
+ const { definitions } = this.jsonSchema
479
+ for (let [name, property] of Object.entries(properties)) {
480
+ // Expand $refs so we can even find properties that uses definitions:
481
+ const { $ref, ...schema } = property
482
+ const definition = $ref && definitions?.[$ref]
483
+ if (definition) {
484
+ property = {
485
+ ...schema,
486
+ ...definition
487
+ }
488
+ }
471
489
  if (filter(property)) {
472
490
  attributes.push(name)
473
491
  }
@@ -814,8 +832,8 @@ export class Model extends objection.Model {
814
832
  // @override
815
833
  static createValidator() {
816
834
  // Use a shared validator per app, so model schema can reference each other.
817
- // NOTE: The Dito Validator class creates and manages this shared Objection
818
- // Validator instance for us, we just need to return it here:
835
+ // NOTE: The Dito.js Validator class creates and manages this shared
836
+ // Objection Validator instance for us, we just need to return it here:
819
837
  return this.app.validator
820
838
  }
821
839
 
@@ -1,4 +1,5 @@
1
1
  export * as keywords from './keywords/index.js'
2
2
  export * as formats from './formats/index.js'
3
+ export * as types from './types/index.js'
3
4
  export * from './properties.js'
4
5
  export * from './relations.js'
@@ -0,0 +1,31 @@
1
+ import { resolve } from 'url'
2
+ import { MissingRefError } from 'ajv'
3
+ import { clone, mergeDeeply } from '@ditojs/utils'
4
+
5
+ export const $extend = {
6
+ macro(schemas, parentSchema, ctx) {
7
+ const [source, ...patch] = schemas.map(schema => {
8
+ const { $ref } = schema
9
+ if ($ref) {
10
+ const { baseId, self } = ctx
11
+ const id =
12
+ baseId && baseId !== '#'
13
+ ? resolve(baseId, $ref)
14
+ : $ref
15
+ const validate = self.getSchema(id)
16
+ if (!validate) {
17
+ throw new MissingRefError(baseId, $ref)
18
+ }
19
+ schema = validate.schema
20
+ }
21
+ return schema
22
+ })
23
+ return mergeDeeply(clone(source), ...patch)
24
+ },
25
+
26
+ metaSchema: {
27
+ type: 'array',
28
+ items: { type: 'object' },
29
+ minItems: 2
30
+ }
31
+ }
@@ -14,8 +14,8 @@ export const _instanceof = {
14
14
  },
15
15
 
16
16
  validate(schema, data) {
17
- // Support instanceof for basic JS types and Dito models. If `this` is the
18
- // validator's ctx (see passContext), then we can access the models and
17
+ // Support instanceof for basic JS types and Dito.js models. If `this` is
18
+ // the validator's ctx (see passContext), then we can access the models and
19
19
  // check.
20
20
  const models = this?.app?.models
21
21
  for (const type of asArray(schema)) {
@@ -10,3 +10,4 @@ export * from './_instanceof.js'
10
10
  export * from './_validate.js'
11
11
  export * from './_relate.js'
12
12
  export * from './_range.js'
13
+ export * from './_extend.js'
@@ -1,13 +1,17 @@
1
- import { isObject, isArray, isString } from '@ditojs/utils'
1
+ import { isObject, isArray, isString, equals } from '@ditojs/utils'
2
2
 
3
3
  export function convertSchema(schema, options = {}) {
4
4
  if (isArray(schema)) {
5
- // Needed for allOf, anyOf, oneOf, not, items:
5
+ // Needed for allOf, anyOf, oneOf, not, items, see below:
6
6
  schema = schema.map(entry => convertSchema(entry, options))
7
7
  } else if (isObject(schema)) {
8
8
  // Create a shallow clone so we can modify and return:
9
- schema = { ...schema }
10
- const { type } = schema
9
+ // Also collect and propagate the definitions up to the root schema through
10
+ // `options.definitions`, as passed from `Model static get jsonSchema()`:
11
+ const { definitions, ...rest } = schema
12
+ mergeDefinitions(options.definitions, definitions, options)
13
+ schema = rest
14
+ const { $ref, type } = schema
11
15
  if (schema.required === true) {
12
16
  // Our 'required' is not the same as JSON Schema's: Use the 'required'
13
17
  // format instead that only validates if the required value is not empty,
@@ -44,22 +48,31 @@ export function convertSchema(schema, options = {}) {
44
48
  schema.prefixItems &&= convertSchema(schema.prefixItems, options)
45
49
  schema.items &&= convertSchema(schema.items, options)
46
50
 
47
- // Handle nested allOf, anyOf, oneOf, not fields
48
- for (const key of ['allOf', 'anyOf', 'oneOf', 'not']) {
51
+ // Handle nested allOf, anyOf, oneOf & co. fields
52
+ for (const key of ['allOf', 'anyOf', 'oneOf', 'not', '$extend']) {
49
53
  if (key in schema) {
50
54
  schema[key] = convertSchema(schema[key], options)
51
55
  }
52
56
  }
53
57
 
54
- if (isString(type)) {
58
+ if (isString($ref)) {
59
+ // If the $ref is a nested Dito.js definition, convert it to a JSON schema
60
+ // reference. If it is a full URL, use it as is.
61
+ schema.$ref = $ref.startsWith('#')
62
+ ? `#/definitions/${$ref}`
63
+ : $ref
64
+ } else if (isString(type)) {
55
65
  // Convert schema property notation to JSON schema
56
66
  const jsonType = jsonTypes[type]
57
67
  if (jsonType) {
58
68
  schema.type = jsonType
59
- if (hasConvertedProperties && !('additionalProperties' in schema)) {
60
- // Invert the logic of `additionalProperties` so that it needs to be
69
+ if (
70
+ (hasConvertedProperties || schema.discriminator) &&
71
+ !('unevaluatedProperties' in schema)
72
+ ) {
73
+ // Invert the logic of `unevaluatedProperties` so that it needs to be
61
74
  // explicitly set to `true`:
62
- schema.additionalProperties = false
75
+ schema.unevaluatedProperties = false
63
76
  }
64
77
  } else if (['date', 'datetime', 'timestamp'].includes(type)) {
65
78
  // Date properties can be submitted both as a string or a Date object.
@@ -105,7 +118,7 @@ export function convertSchema(schema, options = {}) {
105
118
  return schema
106
119
  }
107
120
 
108
- export function convertProperties(schemaProperties, options) {
121
+ function convertProperties(schemaProperties, options) {
109
122
  const properties = {}
110
123
  const required = []
111
124
  for (const [key, property] of Object.entries(schemaProperties)) {
@@ -117,6 +130,32 @@ export function convertProperties(schemaProperties, options) {
117
130
  return { properties, required }
118
131
  }
119
132
 
133
+ function mergeDefinitions(definitions, defs, options) {
134
+ if (definitions && defs) {
135
+ for (const [key, def] of Object.entries(defs)) {
136
+ if (!key.startsWith('#')) {
137
+ throw new Error(
138
+ `Invalid definition '${
139
+ key
140
+ }', the name of nested Dito.js definitions must start with '#': ${
141
+ JSON.stringify(def)
142
+ }`
143
+ )
144
+ }
145
+ const definition = definitions[key]
146
+ const converted = convertSchema(def, options)
147
+ if (definition && !equals(definition, converted)) {
148
+ throw new Error(
149
+ `Duplicate nested definition for '${key}' with different schema: ${
150
+ JSON.stringify(def)
151
+ }`
152
+ )
153
+ }
154
+ definitions[key] = converted
155
+ }
156
+ }
157
+ }
158
+
120
159
  function addFormat(schema, newFormat) {
121
160
  // Support multiple `format` keywords through `allOf`:
122
161
  const { allOf, format, ...rest } = schema
@@ -30,7 +30,7 @@ describe('convertSchema()', () => {
30
30
  ).toEqual({
31
31
  type: 'object',
32
32
  properties,
33
- additionalProperties: false
33
+ unevaluatedProperties: false
34
34
  })
35
35
  })
36
36
 
@@ -69,7 +69,7 @@ describe('convertSchema()', () => {
69
69
  ).toEqual({
70
70
  type: 'object',
71
71
  properties,
72
- additionalProperties: false
72
+ unevaluatedProperties: false
73
73
  })
74
74
  })
75
75
 
@@ -90,7 +90,7 @@ describe('convertSchema()', () => {
90
90
  type: 'string'
91
91
  }
92
92
  },
93
- additionalProperties: false
93
+ unevaluatedProperties: false
94
94
  })
95
95
  })
96
96
 
@@ -121,7 +121,7 @@ describe('convertSchema()', () => {
121
121
  format: 'required'
122
122
  }
123
123
  },
124
- additionalProperties: false,
124
+ unevaluatedProperties: false,
125
125
  required: ['myString', 'myNumber']
126
126
  })
127
127
  })
@@ -142,7 +142,7 @@ describe('convertSchema()', () => {
142
142
  myString: { type: 'string' },
143
143
  myNumber: { type: 'number' }
144
144
  },
145
- additionalProperties: false,
145
+ unevaluatedProperties: false,
146
146
  required: ['myString', 'myNumber']
147
147
  })
148
148
  })
@@ -163,11 +163,11 @@ describe('convertSchema()', () => {
163
163
  properties: {
164
164
  myText: {
165
165
  type: 'object',
166
- additionalProperties: false,
166
+ unevaluatedProperties: false,
167
167
  properties: {}
168
168
  }
169
169
  },
170
- additionalProperties: false
170
+ unevaluatedProperties: false
171
171
  })
172
172
  })
173
173
 
@@ -178,7 +178,7 @@ describe('convertSchema()', () => {
178
178
  properties: {
179
179
  myText: {
180
180
  type: 'object',
181
- additionalProperties: true,
181
+ unevaluatedProperties: true,
182
182
  properties: {}
183
183
  }
184
184
  }
@@ -188,11 +188,11 @@ describe('convertSchema()', () => {
188
188
  properties: {
189
189
  myText: {
190
190
  type: 'object',
191
- additionalProperties: true,
191
+ unevaluatedProperties: true,
192
192
  properties: {}
193
193
  }
194
194
  },
195
- additionalProperties: false
195
+ unevaluatedProperties: false
196
196
  })
197
197
  })
198
198
 
@@ -223,11 +223,11 @@ describe('convertSchema()', () => {
223
223
  format: 'required'
224
224
  }
225
225
  },
226
- additionalProperties: false,
226
+ unevaluatedProperties: false,
227
227
  required: ['myProperty']
228
228
  }
229
229
  },
230
- additionalProperties: false
230
+ unevaluatedProperties: false
231
231
  })
232
232
  })
233
233
 
@@ -256,10 +256,10 @@ describe('convertSchema()', () => {
256
256
  type: 'string'
257
257
  }
258
258
  },
259
- additionalProperties: false
259
+ unevaluatedProperties: false
260
260
  }
261
261
  },
262
- additionalProperties: false
262
+ unevaluatedProperties: false
263
263
  })
264
264
  })
265
265
 
@@ -295,7 +295,7 @@ describe('convertSchema()', () => {
295
295
  format: 'date-time'
296
296
  }
297
297
  },
298
- additionalProperties: false
298
+ unevaluatedProperties: false
299
299
  })
300
300
  })
301
301
 
@@ -316,7 +316,7 @@ describe('convertSchema()', () => {
316
316
  $ref: 'MyModel'
317
317
  }
318
318
  },
319
- additionalProperties: false
319
+ unevaluatedProperties: false
320
320
  })
321
321
  })
322
322
 
@@ -343,7 +343,7 @@ describe('convertSchema()', () => {
343
343
  instanceof: 'MyModel'
344
344
  }
345
345
  },
346
- additionalProperties: false
346
+ unevaluatedProperties: false
347
347
  })
348
348
  })
349
349
 
@@ -366,7 +366,7 @@ describe('convertSchema()', () => {
366
366
  nullable: true
367
367
  }
368
368
  },
369
- additionalProperties: false
369
+ unevaluatedProperties: false
370
370
  })
371
371
  })
372
372
 
@@ -391,7 +391,7 @@ describe('convertSchema()', () => {
391
391
  ]
392
392
  }
393
393
  },
394
- additionalProperties: false
394
+ unevaluatedProperties: false
395
395
  })
396
396
  })
397
397
 
@@ -415,7 +415,7 @@ describe('convertSchema()', () => {
415
415
  nullable: true
416
416
  }
417
417
  },
418
- additionalProperties: false
418
+ unevaluatedProperties: false
419
419
  })
420
420
  })
421
421
 
@@ -440,7 +440,7 @@ describe('convertSchema()', () => {
440
440
  nullable: true
441
441
  }
442
442
  },
443
- additionalProperties: false
443
+ unevaluatedProperties: false
444
444
  })
445
445
  })
446
446
 
@@ -504,7 +504,7 @@ describe('convertSchema()', () => {
504
504
  }
505
505
  },
506
506
  required: ['prop1', 'prop2'],
507
- additionalProperties: false
507
+ unevaluatedProperties: false
508
508
  },
509
509
  {
510
510
  type: 'object',
@@ -519,13 +519,13 @@ describe('convertSchema()', () => {
519
519
  }
520
520
  },
521
521
  required: ['prop3', 'prop4'],
522
- additionalProperties: false
522
+ unevaluatedProperties: false
523
523
  }
524
524
  ]
525
525
  }
526
526
  }
527
527
  },
528
- additionalProperties: false
528
+ unevaluatedProperties: false
529
529
  })
530
530
  })
531
531
 
@@ -566,11 +566,11 @@ describe('convertSchema()', () => {
566
566
  type: 'number'
567
567
  }
568
568
  },
569
- additionalProperties: false,
569
+ unevaluatedProperties: false,
570
570
  required: ['prop1', 'prop2']
571
571
  }
572
572
  },
573
- additionalProperties: false,
573
+ unevaluatedProperties: false,
574
574
  required: ['myObject']
575
575
  })
576
576
  })
@@ -605,6 +605,7 @@ describe('convertSchema()', () => {
605
605
  ).toEqual({
606
606
  type: 'object',
607
607
  discriminator: { propertyName: 'foo' },
608
+ unevaluatedProperties: false,
608
609
  required: ['foo'],
609
610
  oneOf: [
610
611
  {
@@ -630,4 +631,76 @@ describe('convertSchema()', () => {
630
631
  ]
631
632
  })
632
633
  })
634
+
635
+ it('supports nested Dito.js definitions', () => {
636
+ const definitions = {}
637
+ expect(
638
+ convertSchema(
639
+ {
640
+ type: 'object',
641
+ properties: {
642
+ prop1: {
643
+ $ref: '#type1'
644
+ },
645
+ prop2: {
646
+ type: 'object',
647
+ properties: {
648
+ prop3: {
649
+ $ref: '#type2'
650
+ }
651
+ },
652
+ definitions: {
653
+ '#type2': {
654
+ type: 'object',
655
+ properties: {
656
+ prop3: {
657
+ type: 'string'
658
+ }
659
+ }
660
+ }
661
+ }
662
+ }
663
+ },
664
+
665
+ definitions: {
666
+ '#type1': {
667
+ type: 'integer'
668
+ }
669
+ }
670
+ },
671
+ { definitions }
672
+ )
673
+ ).toEqual({
674
+ type: 'object',
675
+ unevaluatedProperties: false,
676
+ properties: {
677
+ prop1: {
678
+ $ref: '#/definitions/#type1'
679
+ },
680
+ prop2: {
681
+ type: 'object',
682
+ unevaluatedProperties: false,
683
+ properties: {
684
+ prop3: {
685
+ $ref: '#/definitions/#type2'
686
+ }
687
+ }
688
+ }
689
+ }
690
+ })
691
+ expect(definitions).toEqual({
692
+ '#type1': {
693
+ type: 'integer'
694
+ },
695
+ '#type2': {
696
+ type: 'object',
697
+ unevaluatedProperties: false,
698
+ properties: {
699
+ prop3: {
700
+ type: 'string'
701
+ }
702
+ }
703
+ }
704
+ })
705
+ })
633
706
  })
@@ -0,0 +1,31 @@
1
+ export const asset = {
2
+ type: 'object',
3
+ properties: {
4
+ key: {
5
+ type: 'string',
6
+ required: true
7
+ },
8
+ name: {
9
+ type: 'string',
10
+ required: true
11
+ },
12
+ type: {
13
+ type: 'string',
14
+ required: true
15
+ },
16
+ size: {
17
+ type: 'integer',
18
+ required: true
19
+ },
20
+ url: {
21
+ type: 'string',
22
+ format: 'uri'
23
+ },
24
+ width: {
25
+ type: 'integer'
26
+ },
27
+ height: {
28
+ type: 'integer'
29
+ }
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ export * from './_asset.js'
@@ -60,7 +60,6 @@ export class AssetFile {
60
60
  static convert(object, storage) {
61
61
  Object.setPrototypeOf(object, AssetFile.prototype)
62
62
  setHiddenProperty(object, SYMBOL_STORAGE, storage)
63
- return object
64
63
  }
65
64
 
66
65
  static create(options) {
@@ -77,7 +77,7 @@ export class Storage {
77
77
  }
78
78
 
79
79
  convertAssetFile(file) {
80
- return AssetFile.convert(file, this)
80
+ AssetFile.convert(file, this)
81
81
  }
82
82
 
83
83
  convertStorageFile(storageFile) {
@@ -104,7 +104,8 @@ export class Storage {
104
104
  file.url = this._getFileUrl(file)
105
105
  // TODO: Support `config.readDimensions`, but this can only be done once
106
106
  // there are separate storage instances per model assets config!
107
- return this.convertAssetFile(file)
107
+ this.convertAssetFile(file)
108
+ return file
108
109
  }
109
110
 
110
111
  async removeFile(file) {
package/types/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Type definitions for Dito.js server
4
4
  // Project: <https://github.com/ditojs/dito/>
5
5
 
6
- // Export the entire Dito namespace.
6
+ // Export the entire Dito.js namespace.
7
7
 
8
8
  import { ObjectCannedACL, S3ClientConfig } from '@aws-sdk/client-s3'
9
9
  import { DateFormat } from '@ditojs/utils'
@@ -645,14 +645,15 @@ export class Model extends objection.Model {
645
645
  }
646
646
 
647
647
  /**
648
- * Dito automatically adds an `id` property if a model property with the
648
+ * Dito.js automatically adds an `id` property if a model property with the
649
649
  * `primary: true` setting is not already explicitly defined.
650
650
  */
651
651
  readonly id: Id
652
652
 
653
653
  /**
654
- * Dito automatically adds a `foreignKeyId` property if foreign keys occurring
655
- * in relations definitions are not explicitly defined in the properties.
654
+ * Dito.js automatically adds a `foreignKeyId` property if foreign keys
655
+ * occurring in relations definitions are not explicitly defined in the
656
+ * properties.
656
657
  */
657
658
  readonly foreignKeyId: Id
658
659