@jsreport/jsreport-core 3.1.2-test.2 → 3.4.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/LICENSE +166 -166
- package/README.md +310 -298
- package/index.js +29 -29
- package/lib/main/blobStorage/blobStorage.js +53 -52
- package/lib/main/blobStorage/inMemoryProvider.js +27 -27
- package/lib/main/blobStorage/mainActions.js +24 -24
- package/lib/main/createDefaultLoggerFormat.js +17 -17
- package/lib/main/defaults.js +14 -14
- package/lib/main/extensions/discover.js +20 -20
- package/lib/main/extensions/extensionsManager.js +264 -264
- package/lib/main/extensions/fileUtils.js +56 -56
- package/lib/main/extensions/findVersion.js +49 -49
- package/lib/main/extensions/locationCache.js +103 -103
- package/lib/main/extensions/sorter.js +10 -10
- package/lib/main/extensions/validateMinimalVersion.js +50 -50
- package/lib/main/folders/cascadeFolderRemove.js +25 -25
- package/lib/main/folders/getEntitiesInFolder.js +53 -53
- package/lib/main/folders/index.js +42 -42
- package/lib/main/folders/moveBetweenFolders.js +354 -354
- package/lib/main/folders/validateDuplicatedName.js +107 -107
- package/lib/main/folders/validateReservedName.js +53 -53
- package/lib/main/logger.js +254 -244
- package/lib/main/migration/resourcesToAssets.js +230 -230
- package/lib/main/migration/xlsxTemplatesToAssets.js +128 -128
- package/lib/main/monitoring.js +92 -91
- package/lib/main/optionsLoad.js +231 -237
- package/lib/main/optionsSchema.js +237 -237
- package/lib/main/profiler.js +13 -1
- package/lib/main/reporter.js +589 -579
- package/lib/main/request.js +21 -0
- package/lib/main/schemaValidator.js +252 -252
- package/lib/main/settings.js +154 -154
- package/lib/main/store/checkDuplicatedId.js +27 -27
- package/lib/main/store/collection.js +329 -329
- package/lib/main/store/documentStore.js +469 -469
- package/lib/main/store/mainActions.js +28 -28
- package/lib/main/store/memoryStoreProvider.js +99 -99
- package/lib/main/store/queue.js +48 -48
- package/lib/main/store/referenceUtils.js +251 -251
- package/lib/main/store/setupValidateId.js +43 -43
- package/lib/main/store/setupValidateShortid.js +71 -71
- package/lib/main/store/transaction.js +69 -69
- package/lib/main/store/typeUtils.js +180 -180
- package/lib/main/templates.js +34 -34
- package/lib/main/validateEntityName.js +62 -62
- package/lib/shared/createError.js +36 -36
- package/lib/shared/encryption.js +114 -114
- package/lib/shared/folders/index.js +11 -11
- package/lib/shared/folders/normalizeEntityPath.js +15 -15
- package/lib/shared/folders/resolveEntityFromPath.js +88 -88
- package/lib/shared/folders/resolveEntityPath.js +46 -46
- package/lib/shared/folders/resolveFolderFromPath.js +38 -38
- package/lib/shared/generateRequestId.js +4 -4
- package/lib/shared/listenerCollection.js +169 -169
- package/lib/shared/normalizeMetaFromLogs.js +30 -30
- package/lib/shared/reporter.js +128 -123
- package/lib/shared/request.js +64 -64
- package/lib/shared/tempFilesHandler.js +81 -81
- package/lib/shared/templates.js +82 -82
- package/lib/static/helpers.js +33 -33
- package/lib/worker/blobStorage.js +34 -34
- package/lib/worker/defaultProxyExtend.js +46 -46
- package/lib/worker/documentStore.js +49 -49
- package/lib/worker/extensionsManager.js +17 -17
- package/lib/worker/logger.js +48 -48
- package/lib/worker/render/diff.js +138 -138
- package/lib/worker/render/executeEngine.js +232 -207
- package/lib/worker/render/htmlRecipe.js +10 -10
- package/lib/worker/render/moduleHelper.js +45 -43
- package/lib/worker/render/noneEngine.js +12 -12
- package/lib/worker/render/profiler.js +162 -158
- package/lib/worker/render/render.js +202 -205
- package/lib/worker/render/resolveReferences.js +60 -60
- package/lib/worker/reporter.js +197 -191
- package/lib/worker/sandbox/runInSandbox.js +64 -12
- package/lib/worker/sandbox/safeSandbox.js +829 -828
- package/lib/worker/templates.js +80 -78
- package/lib/worker/workerHandler.js +55 -54
- package/package.json +91 -92
- package/test/blobStorage/common.js +25 -21
- package/test/store/common.js +1449 -1449
|
@@ -1,205 +1,202 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright(c) 2018 Jan Blaha
|
|
3
|
-
*
|
|
4
|
-
* Orchestration of the rendering process
|
|
5
|
-
*/
|
|
6
|
-
const { Readable } = require('stream')
|
|
7
|
-
const extend = require('node.extend.without.arrays')
|
|
8
|
-
const ExecuteEngine = require('./executeEngine')
|
|
9
|
-
const Request = require('../../shared/request')
|
|
10
|
-
const generateRequestId = require('../../shared/generateRequestId')
|
|
11
|
-
const resolveReferences = require('./resolveReferences.js')
|
|
12
|
-
const moduleHelper = require('./moduleHelper')
|
|
13
|
-
let reportCounter = 0
|
|
14
|
-
|
|
15
|
-
module.exports = (reporter) => {
|
|
16
|
-
reporter
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
request.data
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
request.context
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright(c) 2018 Jan Blaha
|
|
3
|
+
*
|
|
4
|
+
* Orchestration of the rendering process
|
|
5
|
+
*/
|
|
6
|
+
const { Readable } = require('stream')
|
|
7
|
+
const extend = require('node.extend.without.arrays')
|
|
8
|
+
const ExecuteEngine = require('./executeEngine')
|
|
9
|
+
const Request = require('../../shared/request')
|
|
10
|
+
const generateRequestId = require('../../shared/generateRequestId')
|
|
11
|
+
const resolveReferences = require('./resolveReferences.js')
|
|
12
|
+
const moduleHelper = require('./moduleHelper')
|
|
13
|
+
let reportCounter = 0
|
|
14
|
+
|
|
15
|
+
module.exports = (reporter) => {
|
|
16
|
+
moduleHelper(reporter)
|
|
17
|
+
|
|
18
|
+
const executeEngine = ExecuteEngine(reporter)
|
|
19
|
+
async function beforeRender (reporter, request, response) {
|
|
20
|
+
if (!request.template) {
|
|
21
|
+
throw reporter.createError('template property must be defined', {
|
|
22
|
+
statusCode: 400
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await reporter.beforeRenderListeners.fire(request, response)
|
|
27
|
+
await reporter.validateRenderListeners.fire(request, response)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function invokeRender (reporter, request, response) {
|
|
31
|
+
if (!request.template.engine) {
|
|
32
|
+
throw reporter.createError('Engine must be specified', {
|
|
33
|
+
statusCode: 400
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const engine = reporter.extensionsManager.engines.find((e) => e.name === request.template.engine)
|
|
38
|
+
|
|
39
|
+
if (!engine) {
|
|
40
|
+
throw reporter.createError(`Engine '${request.template.engine}' not found. If this is a custom engine make sure it's properly installed from npm`, {
|
|
41
|
+
statusCode: 400
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
request.data != null &&
|
|
47
|
+
typeof request.data === 'object' &&
|
|
48
|
+
Array.isArray(request.data)
|
|
49
|
+
) {
|
|
50
|
+
throw reporter.createError('Request data can not be an array. you should pass an object in request.data input', {
|
|
51
|
+
statusCode: 400
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const engineProfilerEvent = reporter.profiler.emit({
|
|
56
|
+
type: 'operationStart',
|
|
57
|
+
subtype: 'engine',
|
|
58
|
+
name: request.template.engine
|
|
59
|
+
}, request, response)
|
|
60
|
+
|
|
61
|
+
reporter.logger.debug(`Rendering engine ${engine.name}`, request)
|
|
62
|
+
|
|
63
|
+
const engineRes = await executeEngine(engine, request)
|
|
64
|
+
|
|
65
|
+
response.content = Buffer.from(engineRes.content != null ? engineRes.content : '')
|
|
66
|
+
|
|
67
|
+
reporter.profiler.emit({
|
|
68
|
+
type: 'operationEnd',
|
|
69
|
+
operationId: engineProfilerEvent.operationId
|
|
70
|
+
}, request, response)
|
|
71
|
+
|
|
72
|
+
await reporter.afterTemplatingEnginesExecutedListeners.fire(request, response)
|
|
73
|
+
|
|
74
|
+
if (!request.template.recipe) {
|
|
75
|
+
throw reporter.createError('Recipe must be specified', {
|
|
76
|
+
statusCode: 400
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const recipe = reporter.extensionsManager.recipes.find((r) => r.name === request.template.recipe)
|
|
81
|
+
|
|
82
|
+
if (!recipe) {
|
|
83
|
+
throw reporter.createError(`Recipe '${request.template.recipe}' not found. If this is a custom recipe make sure it's properly installed from npm.`, {
|
|
84
|
+
statusCode: 400
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const recipeProfilerEvent = reporter.profiler.emit({
|
|
89
|
+
type: 'operationStart',
|
|
90
|
+
subtype: 'recipe',
|
|
91
|
+
name: request.template.recipe
|
|
92
|
+
}, request, response)
|
|
93
|
+
|
|
94
|
+
reporter.logger.debug('Executing recipe ' + request.template.recipe, request)
|
|
95
|
+
|
|
96
|
+
await recipe.execute(request, response)
|
|
97
|
+
reporter.profiler.emit({
|
|
98
|
+
type: 'operationEnd',
|
|
99
|
+
operationId: recipeProfilerEvent.operationId
|
|
100
|
+
}, request, response)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function afterRender (reporter, request, response) {
|
|
104
|
+
await reporter.afterRenderListeners.fire(request, response)
|
|
105
|
+
|
|
106
|
+
response.stream = Readable.from(response.content)
|
|
107
|
+
response.result = response.stream
|
|
108
|
+
|
|
109
|
+
return response
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return async (req, parentReq) => {
|
|
113
|
+
const request = Request(req, parentReq)
|
|
114
|
+
const response = { meta: {} }
|
|
115
|
+
let renderStartProfilerEvent
|
|
116
|
+
try {
|
|
117
|
+
if (request.context.id == null) {
|
|
118
|
+
request.context.id = generateRequestId()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
renderStartProfilerEvent = await reporter.profiler.renderStart(request, parentReq, response)
|
|
122
|
+
request.data = resolveReferences(request.data) || {}
|
|
123
|
+
|
|
124
|
+
if (request.options.reportName) {
|
|
125
|
+
response.meta.reportName = String(request.options.reportName)
|
|
126
|
+
} else {
|
|
127
|
+
response.meta.reportName = 'report'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
request.context.reportCounter = ++reportCounter
|
|
131
|
+
request.context.startTimestamp = new Date().getTime()
|
|
132
|
+
|
|
133
|
+
if (parentReq == null) {
|
|
134
|
+
reporter.requestModulesCache.set(request.context.rootId, Object.create(null))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
reporter.logger.info(`Starting rendering request ${request.context.reportCounter} (user: ${(request.context.user ? request.context.user.username : 'null')})`, request)
|
|
138
|
+
|
|
139
|
+
// TODO
|
|
140
|
+
/* if (reporter.entityTypeValidator.getSchema('TemplateType') != null) {
|
|
141
|
+
const templateValidationResult = reporter.entityTypeValidator.validate('TemplateType', request.template, { rootPrefix: 'template' })
|
|
142
|
+
|
|
143
|
+
if (!templateValidationResult.valid) {
|
|
144
|
+
throw reporter.createError(`template input in request contain values that does not match the defined schema. ${templateValidationResult.fullErrorMessage}`, {
|
|
145
|
+
statusCode: 400
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
} */
|
|
149
|
+
|
|
150
|
+
await beforeRender(reporter, request, response)
|
|
151
|
+
await invokeRender(reporter, request, response)
|
|
152
|
+
await afterRender(reporter, request, response)
|
|
153
|
+
|
|
154
|
+
reporter.logger.info(`Rendering request ${request.context.reportCounter} finished in ${(new Date().getTime() - request.context.startTimestamp)} ms`, request)
|
|
155
|
+
|
|
156
|
+
response.meta.logs = request.context.logs
|
|
157
|
+
|
|
158
|
+
if (parentReq) {
|
|
159
|
+
parentReq.context.logs = parentReq.context.logs.concat(request.context.logs)
|
|
160
|
+
parentReq.context.shared = extend(true, parentReq.context.shared, request.context.shared)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await reporter.profiler.renderEnd(renderStartProfilerEvent.operationId, request, response)
|
|
164
|
+
|
|
165
|
+
return response
|
|
166
|
+
} catch (e) {
|
|
167
|
+
await reporter.renderErrorListeners.fire(request, response, e)
|
|
168
|
+
|
|
169
|
+
const logFn = e.weak ? reporter.logger.warn : reporter.logger.error
|
|
170
|
+
|
|
171
|
+
logFn(`Error when processing render request ${request.context.reportCounter} ${e.message}${e.stack != null ? ' ' + e.stack : ''}`, request)
|
|
172
|
+
|
|
173
|
+
logFn(`Rendering request ${request.context.reportCounter} finished with error in ${(new Date().getTime() - request.context.startTimestamp)} ms`, request)
|
|
174
|
+
|
|
175
|
+
if (
|
|
176
|
+
parentReq &&
|
|
177
|
+
parentReq.context &&
|
|
178
|
+
parentReq.context.logs &&
|
|
179
|
+
request.context &&
|
|
180
|
+
request.context.logs
|
|
181
|
+
) {
|
|
182
|
+
parentReq.context.logs = parentReq.context.logs.concat(request.context.logs)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (parentReq) {
|
|
186
|
+
parentReq.context.shared = extend(true, parentReq.context.shared, request.context.shared)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
e.logged = true
|
|
190
|
+
|
|
191
|
+
if (renderStartProfilerEvent) {
|
|
192
|
+
await reporter.profiler.renderEnd(renderStartProfilerEvent.operationId, request, response, e)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
throw e
|
|
196
|
+
} finally {
|
|
197
|
+
if (parentReq == null) {
|
|
198
|
+
reporter.requestModulesCache.delete(request.context.rootId)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
// resolve references in json specified by $ref and $id attribute, this is handy when user send cycles in json
|
|
2
|
-
module.exports = function (json) {
|
|
3
|
-
if (typeof json === 'string') {
|
|
4
|
-
json = JSON.parse(json)
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const byid = {} // all objects by id
|
|
8
|
-
const refs = [] // references to objects that could not be resolved
|
|
9
|
-
json = (function recurse (obj, prop, parent) {
|
|
10
|
-
if (typeof obj !== 'object' || !obj) { // a primitive value
|
|
11
|
-
return obj
|
|
12
|
-
}
|
|
13
|
-
if (Object.prototype.toString.call(obj) === '[object Array]') {
|
|
14
|
-
for (let i = 0; i < obj.length; i++) {
|
|
15
|
-
if (obj[i] === null) {
|
|
16
|
-
continue
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (Object.prototype.hasOwnProperty.call(obj[i], '$ref')) {
|
|
20
|
-
obj[i] = recurse(obj[i], i, obj)
|
|
21
|
-
} else {
|
|
22
|
-
obj[i] = recurse(obj[i], prop, obj)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return obj
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if ('$ref' in obj) { // a reference
|
|
29
|
-
const ref = obj.$ref
|
|
30
|
-
if (ref in byid) {
|
|
31
|
-
return byid[ref]
|
|
32
|
-
}
|
|
33
|
-
// else we have to make it lazy:
|
|
34
|
-
refs.push([parent, prop, ref])
|
|
35
|
-
return
|
|
36
|
-
} else if ('$id' in obj) {
|
|
37
|
-
const id = obj.$id
|
|
38
|
-
delete obj.$id
|
|
39
|
-
if ('$values' in obj) { // an array
|
|
40
|
-
obj = obj.$values.map(recurse)
|
|
41
|
-
} else { // a plain object
|
|
42
|
-
for (const p in obj) {
|
|
43
|
-
if (Object.prototype.hasOwnProperty.call(obj, p)) {
|
|
44
|
-
obj[p] = recurse(obj[p], p, obj)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
byid[id] = obj
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return obj
|
|
52
|
-
})(json) // run it!
|
|
53
|
-
|
|
54
|
-
for (let i = 0; i < refs.length; i++) { // resolve previously unknown references
|
|
55
|
-
const ref = refs[i]
|
|
56
|
-
ref[0][ref[1]] = byid[ref[2]]
|
|
57
|
-
// Notice that this throws if you put in a reference at top-level
|
|
58
|
-
}
|
|
59
|
-
return json
|
|
60
|
-
}
|
|
1
|
+
// resolve references in json specified by $ref and $id attribute, this is handy when user send cycles in json
|
|
2
|
+
module.exports = function (json) {
|
|
3
|
+
if (typeof json === 'string') {
|
|
4
|
+
json = JSON.parse(json)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const byid = {} // all objects by id
|
|
8
|
+
const refs = [] // references to objects that could not be resolved
|
|
9
|
+
json = (function recurse (obj, prop, parent) {
|
|
10
|
+
if (typeof obj !== 'object' || !obj) { // a primitive value
|
|
11
|
+
return obj
|
|
12
|
+
}
|
|
13
|
+
if (Object.prototype.toString.call(obj) === '[object Array]') {
|
|
14
|
+
for (let i = 0; i < obj.length; i++) {
|
|
15
|
+
if (obj[i] === null) {
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (Object.prototype.hasOwnProperty.call(obj[i], '$ref')) {
|
|
20
|
+
obj[i] = recurse(obj[i], i, obj)
|
|
21
|
+
} else {
|
|
22
|
+
obj[i] = recurse(obj[i], prop, obj)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return obj
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if ('$ref' in obj) { // a reference
|
|
29
|
+
const ref = obj.$ref
|
|
30
|
+
if (ref in byid) {
|
|
31
|
+
return byid[ref]
|
|
32
|
+
}
|
|
33
|
+
// else we have to make it lazy:
|
|
34
|
+
refs.push([parent, prop, ref])
|
|
35
|
+
return
|
|
36
|
+
} else if ('$id' in obj) {
|
|
37
|
+
const id = obj.$id
|
|
38
|
+
delete obj.$id
|
|
39
|
+
if ('$values' in obj) { // an array
|
|
40
|
+
obj = obj.$values.map(recurse)
|
|
41
|
+
} else { // a plain object
|
|
42
|
+
for (const p in obj) {
|
|
43
|
+
if (Object.prototype.hasOwnProperty.call(obj, p)) {
|
|
44
|
+
obj[p] = recurse(obj[p], p, obj)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
byid[id] = obj
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return obj
|
|
52
|
+
})(json) // run it!
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < refs.length; i++) { // resolve previously unknown references
|
|
55
|
+
const ref = refs[i]
|
|
56
|
+
ref[0][ref[1]] = byid[ref[2]]
|
|
57
|
+
// Notice that this throws if you put in a reference at top-level
|
|
58
|
+
}
|
|
59
|
+
return json
|
|
60
|
+
}
|