@ditojs/server 2.96.0 → 2.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "2.96.0",
3
+ "version": "2.97.0",
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/main/packages/server",
@@ -25,10 +25,10 @@
25
25
  "node >= 18"
26
26
  ],
27
27
  "dependencies": {
28
- "@ditojs/admin": "^2.96.0",
29
- "@ditojs/build": "^2.96.0",
30
- "@ditojs/router": "^2.96.0",
31
- "@ditojs/utils": "^2.96.0",
28
+ "@ditojs/admin": "^2.97.0",
29
+ "@ditojs/build": "^2.97.0",
30
+ "@ditojs/router": "^2.97.0",
31
+ "@ditojs/utils": "^2.97.0",
32
32
  "@koa/cors": "^5.0.0",
33
33
  "@koa/etag": "^5.0.2",
34
34
  "@koa/multer": "^4.0.0",
@@ -90,5 +90,5 @@
90
90
  "objection": "^3.1.5",
91
91
  "typescript": "^6.0.3"
92
92
  },
93
- "gitHead": "689f4d061088500884114c5ced79d68209717bc5"
93
+ "gitHead": "a15b3aa1474b88b4dbefd114491605b5715a6526"
94
94
  }
@@ -924,7 +924,7 @@ export class Application extends Koa {
924
924
  changedFiles,
925
925
  transaction = null
926
926
  ) {
927
- let importedFiles = []
927
+ let importedFiles
928
928
  const AssetModel = this.getModel('Asset')
929
929
  if (AssetModel) {
930
930
  importedFiles = await this.addForeignAssets(
@@ -12,6 +12,7 @@ import {
12
12
  parseDataPath,
13
13
  normalizeDataPath,
14
14
  getValueAtDataPath,
15
+ getEntriesAtDataPath,
15
16
  setValueAtDataPath,
16
17
  mapConcurrently,
17
18
  assignDeeply,
@@ -1018,14 +1019,20 @@ export class Model extends objection.Model {
1018
1019
  const { assets } = this.definition
1019
1020
  if (assets) {
1020
1021
  for (const dataPath in assets) {
1021
- const data = getValueAtDataPath(json, dataPath, noop)
1022
- if (!data) continue
1023
1022
  const storage = this.app.getStorage(assets[dataPath].storage)
1024
- setValueAtDataPath(
1025
- json,
1026
- dataPath,
1027
- mapAssetFiles(data, storage, callback)
1028
- )
1023
+ // Wildcard data paths (e.g. `**.file`) expand to multiple concrete
1024
+ // paths; iterate them so each match is written back to its own
1025
+ // location instead of the flattened wildcard result.
1026
+ const entries = getEntriesAtDataPath(json, dataPath, noop)
1027
+ for (const [path, data] of Object.entries(entries)) {
1028
+ if (data) {
1029
+ setValueAtDataPath(
1030
+ json,
1031
+ path,
1032
+ mapAssetFiles(data, storage, callback)
1033
+ )
1034
+ }
1035
+ }
1029
1036
  }
1030
1037
  }
1031
1038
  }
@@ -0,0 +1,68 @@
1
+ import { Model } from './Model.js'
2
+
3
+ function makeFakeModel(assets) {
4
+ const storage = {
5
+ signAssetFile(file) {
6
+ file.signature = `sig-of-${file.key}`
7
+ }
8
+ }
9
+ return {
10
+ app: {
11
+ getStorage(name) {
12
+ return name === 'test' ? storage : null
13
+ }
14
+ },
15
+ definition: { assets }
16
+ }
17
+ }
18
+
19
+ function signCallback(file, storage) {
20
+ const signed = { ...file }
21
+ storage.signAssetFile(signed)
22
+ return signed
23
+ }
24
+
25
+ describe('Model._mapAssetFiles: asset signing across data paths', () => {
26
+ it('writes the signature back to a literal data path', () => {
27
+ const model = makeFakeModel({ 'image.file': { storage: 'test' } })
28
+ const json = { image: { file: { key: 'foo.png' } } }
29
+ Model._mapAssetFiles.call(model, json, signCallback)
30
+ expect(json.image.file.signature).toBe('sig-of-foo.png')
31
+ })
32
+
33
+ it('writes the signature back to every match of a deep wildcard', () => {
34
+ const model = makeFakeModel({ '**.file': { storage: 'test' } })
35
+ const json = {
36
+ content: {
37
+ file: { key: 'a.png' },
38
+ sections: [
39
+ { file: { key: 'b.png' } },
40
+ { file: { key: 'c.png' } }
41
+ ]
42
+ }
43
+ }
44
+ Model._mapAssetFiles.call(model, json, signCallback)
45
+ expect(json.content.file.signature).toBe('sig-of-a.png')
46
+ expect(json.content.sections[0].file.signature).toBe('sig-of-b.png')
47
+ expect(json.content.sections[1].file.signature).toBe('sig-of-c.png')
48
+ })
49
+
50
+ it('writes the signature back into wildcard array values', () => {
51
+ const model = makeFakeModel({ '**.files': { storage: 'test' } })
52
+ const json = {
53
+ content: {
54
+ files: [{ key: 'x.png' }, { key: 'y.png' }]
55
+ }
56
+ }
57
+ Model._mapAssetFiles.call(model, json, signCallback)
58
+ expect(json.content.files[0].signature).toBe('sig-of-x.png')
59
+ expect(json.content.files[1].signature).toBe('sig-of-y.png')
60
+ })
61
+
62
+ it('is a no-op when the wildcard matches nothing', () => {
63
+ const model = makeFakeModel({ '**.file': { storage: 'test' } })
64
+ const json = { name: 'no-files-here' }
65
+ Model._mapAssetFiles.call(model, json, signCallback)
66
+ expect(json).toEqual({ name: 'no-files-here' })
67
+ })
68
+ })