@jsreport/jsreport-core 4.0.1 → 4.2.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/README.md +7 -0
- package/lib/main/profiler.js +2 -2
- package/lib/main/reporter.js +14 -18
- package/lib/main/request.js +25 -16
- package/lib/shared/reporter.js +67 -1
- package/lib/shared/reqStorage.js +20 -0
- package/lib/shared/response.js +231 -0
- package/lib/shared/tempFilesHandler.js +95 -29
- package/lib/worker/defaultProxyExtend.js +2 -4
- package/lib/worker/render/engineStream.js +97 -0
- package/lib/worker/render/executeEngine.js +35 -7
- package/lib/worker/render/profiler.js +12 -4
- package/lib/worker/render/render.js +15 -13
- package/lib/worker/reporter.js +7 -13
- package/lib/worker/sandbox/isolatedRequire.js +25 -46
- package/lib/worker/sandbox/requireSandbox.js +69 -24
- package/lib/worker/sandbox/resolveFilename.js +30 -0
- package/lib/worker/sandbox/runInSandbox.js +16 -0
- package/package.json +7 -7
- package/test/extensions/validExtensions/listeners/main.js +16 -8
- package/test/extensions/validExtensions/listeners/worker.js +81 -81
|
@@ -13,8 +13,34 @@ module.exports.ensureTempDirectoryExists = async function (tempDirectory) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
module.exports.getTempFilePath = getTempFilePath
|
|
17
|
+
|
|
18
|
+
module.exports.readTempFileSync = function readTempFileSync (tempDirectory, filename, opts = {}) {
|
|
19
|
+
const { pathToFile } = getTempFilePath(tempDirectory, filename)
|
|
20
|
+
|
|
21
|
+
const content = fs.readFileSync(pathToFile, opts)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
pathToFile,
|
|
25
|
+
filename,
|
|
26
|
+
content
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports.openTempFile = async function (tempDirectory, filenameFn, flags) {
|
|
31
|
+
const { pathToFile, filename } = getTempFilePath(tempDirectory, filenameFn)
|
|
32
|
+
|
|
33
|
+
const fileHandle = await fsAsync.open(pathToFile, flags)
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
pathToFile,
|
|
37
|
+
filename,
|
|
38
|
+
fileHandle
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
16
42
|
module.exports.readTempFile = async function readTempFile (tempDirectory, filename, opts = {}) {
|
|
17
|
-
const pathToFile =
|
|
43
|
+
const { pathToFile } = getTempFilePath(tempDirectory, filename)
|
|
18
44
|
|
|
19
45
|
const content = await fsAsync.readFile(pathToFile, opts)
|
|
20
46
|
|
|
@@ -25,51 +51,91 @@ module.exports.readTempFile = async function readTempFile (tempDirectory, filena
|
|
|
25
51
|
}
|
|
26
52
|
}
|
|
27
53
|
|
|
28
|
-
module.exports.readTempFileStream =
|
|
29
|
-
const pathToFile =
|
|
54
|
+
module.exports.readTempFileStream = function readTempFileStream (tempDirectory, filename, opts = {}) {
|
|
55
|
+
const { pathToFile } = getTempFilePath(tempDirectory, filename)
|
|
30
56
|
|
|
31
|
-
|
|
32
|
-
const stream = fs.createReadStream(pathToFile, opts)
|
|
57
|
+
const stream = fs.createReadStream(pathToFile, opts)
|
|
33
58
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
return {
|
|
60
|
+
pathToFile,
|
|
61
|
+
filename,
|
|
62
|
+
stream
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports.writeTempFileSync = function writeTempFileSync (tempDirectory, filenameOrFn, content, opts = {}) {
|
|
67
|
+
return writeFileSync(tempDirectory, filenameOrFn, content, opts)
|
|
40
68
|
}
|
|
41
69
|
|
|
42
|
-
module.exports.writeTempFile = async function writeTempFile (tempDirectory,
|
|
43
|
-
return writeFile(tempDirectory,
|
|
70
|
+
module.exports.writeTempFile = async function writeTempFile (tempDirectory, filenameOrFn, content, opts = {}) {
|
|
71
|
+
return writeFile(tempDirectory, filenameOrFn, content, opts)
|
|
44
72
|
}
|
|
45
73
|
|
|
46
|
-
module.exports.writeTempFileStream = async function writeTempFileStream (tempDirectory,
|
|
47
|
-
return writeFile(tempDirectory,
|
|
74
|
+
module.exports.writeTempFileStream = async function writeTempFileStream (tempDirectory, filenameOrFn, opts = {}) {
|
|
75
|
+
return writeFile(tempDirectory, filenameOrFn, undefined, opts, true)
|
|
48
76
|
}
|
|
49
77
|
|
|
50
|
-
async function
|
|
51
|
-
const filename =
|
|
78
|
+
module.exports.copyFileToTempFile = async function copyFileToTempFile (tempDirectory, srcFilePath, destFilenameOrFn, mode) {
|
|
79
|
+
const { pathToFile, filename } = getTempFilePath(tempDirectory, destFilenameOrFn)
|
|
52
80
|
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
await fsAsync.mkdir(tempDirectory, {
|
|
82
|
+
recursive: true
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await fsAsync.copyFile(srcFilePath, pathToFile, mode)
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
pathToFile,
|
|
89
|
+
filename
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getTempFilePath (tempDirectory, filenameOrFn) {
|
|
94
|
+
const filenameResult = typeof filenameOrFn === 'function' ? filenameOrFn(uuidv4()) : filenameOrFn
|
|
95
|
+
|
|
96
|
+
if (filenameResult == null || filenameResult === '') {
|
|
97
|
+
throw new Error('No valid filename')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const pathToFile = path.isAbsolute(filenameResult) ? filenameResult : path.join(tempDirectory, filenameResult)
|
|
101
|
+
const filename = path.basename(pathToFile)
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
pathToFile,
|
|
105
|
+
filename
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function writeFileSync (tempDirectory, filenameOrFn, content, opts) {
|
|
110
|
+
const { pathToFile, filename } = getTempFilePath(tempDirectory, filenameOrFn)
|
|
111
|
+
|
|
112
|
+
fs.mkdirSync(tempDirectory, {
|
|
113
|
+
recursive: true
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
fs.writeFileSync(pathToFile, content, opts)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
pathToFile,
|
|
120
|
+
filename
|
|
55
121
|
}
|
|
122
|
+
}
|
|
56
123
|
|
|
57
|
-
|
|
124
|
+
async function writeFile (tempDirectory, filenameOrFn, content, opts, asStream = false) {
|
|
125
|
+
const { pathToFile, filename } = getTempFilePath(tempDirectory, filenameOrFn)
|
|
58
126
|
|
|
59
127
|
await fsAsync.mkdir(tempDirectory, {
|
|
60
128
|
recursive: true
|
|
61
129
|
})
|
|
62
130
|
|
|
63
131
|
if (asStream === true) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
})
|
|
72
|
-
})
|
|
132
|
+
const stream = fs.createWriteStream(pathToFile, opts)
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
pathToFile,
|
|
136
|
+
filename,
|
|
137
|
+
stream
|
|
138
|
+
}
|
|
73
139
|
} else {
|
|
74
140
|
await fsAsync.writeFile(pathToFile, content, opts)
|
|
75
141
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const bytes = require('bytes')
|
|
2
|
+
const { Readable } = require('stream')
|
|
3
|
+
/*
|
|
4
|
+
This adds jsreport.templatingEngines.createStream to the helpers proxy allowing to write giant texts to output
|
|
5
|
+
which would otherwise hit nodejs max string size limit.
|
|
6
|
+
|
|
7
|
+
Example usage
|
|
8
|
+
===================
|
|
9
|
+
async function myEach(items, options) {
|
|
10
|
+
const stream = await jsreport.templatingEngines.createStream()
|
|
11
|
+
for (let i = 0; i < items.length; i++) {
|
|
12
|
+
await stream.write(options.fn())
|
|
13
|
+
}
|
|
14
|
+
return await stream.toResult()
|
|
15
|
+
}
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
module.exports = (reporter) => {
|
|
19
|
+
reporter.afterTemplatingEnginesExecutedListeners.add('streamedEach', async (req, res) => {
|
|
20
|
+
if (req.context.engineStreamEnabled !== true) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const content = (await res.output.getBuffer()).toString()
|
|
25
|
+
|
|
26
|
+
const matches = [...content.matchAll(/{#stream ([^{}]{0,500})}/g)]
|
|
27
|
+
|
|
28
|
+
async function * transform () {
|
|
29
|
+
if (matches.length) {
|
|
30
|
+
yield content.substring(0, matches[0].index)
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < matches.length; i++) {
|
|
33
|
+
const { stream } = reporter.readTempFileStream(matches[i][1])
|
|
34
|
+
|
|
35
|
+
for await (const content of stream) {
|
|
36
|
+
yield content
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (i < matches.length - 1) {
|
|
40
|
+
yield content.substring(matches[i].index + matches[i][0].length, matches[i + 1].index)
|
|
41
|
+
} else {
|
|
42
|
+
yield content.substring(matches[i].index + matches[i][0].length)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
yield content
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await res.output.update(Readable.from(transform()))
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
reporter.extendProxy((proxy, req, {
|
|
54
|
+
runInSandbox,
|
|
55
|
+
context,
|
|
56
|
+
getTopLevelFunctions
|
|
57
|
+
}) => {
|
|
58
|
+
if (proxy.templatingEngines) {
|
|
59
|
+
proxy.templatingEngines.createStream = async (opts = {}) => {
|
|
60
|
+
// limiting the number of temp files to avoid breaking server, otherwise I see no reason why having more than 1000 calls per req should be valid usecase
|
|
61
|
+
const counter = reporter.reqStorage.get('engine-stream-counter', req) || 0
|
|
62
|
+
if (counter > 1000) {
|
|
63
|
+
throw reporter.createError('Reached maximum limit of templatingEngine.createStream calls', {
|
|
64
|
+
weak: true,
|
|
65
|
+
statusCode: 400
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
reporter.reqStorage.set('engine-stream-counter', counter + 1, req)
|
|
69
|
+
|
|
70
|
+
req.context.engineStreamEnabled = true
|
|
71
|
+
|
|
72
|
+
const bufferSize = bytes(opts.bufferSize || '10mb')
|
|
73
|
+
let buf = ''
|
|
74
|
+
|
|
75
|
+
const { fileHandle, filename } = await reporter.openTempFile((uuid) => `${uuid}.stream`, 'a')
|
|
76
|
+
proxy.templatingEngines.addFinishListener(() => fileHandle.close().catch((e) => reporter.logger.error('Failed to close temp file handle', e, req)))
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
write: async (text) => {
|
|
80
|
+
const realText = await proxy.templatingEngines.waitForAsyncHelper(text)
|
|
81
|
+
|
|
82
|
+
buf += realText
|
|
83
|
+
|
|
84
|
+
if (buf.length > bufferSize) {
|
|
85
|
+
await fileHandle.appendFile(buf)
|
|
86
|
+
buf = ''
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
toResult: async () => {
|
|
90
|
+
await fileHandle.appendFile(buf)
|
|
91
|
+
return `{#stream ${filename}}`
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
@@ -16,6 +16,7 @@ module.exports = (reporter) => {
|
|
|
16
16
|
|
|
17
17
|
const executionFnParsedParamsMap = new Map()
|
|
18
18
|
const executionAsyncResultsMap = new Map()
|
|
19
|
+
const executionFinishListenersMap = new Map()
|
|
19
20
|
|
|
20
21
|
const templatingEnginesEvaluate = async (mainCall, { engine, content, helpers, data }, { entity, entitySet }, req) => {
|
|
21
22
|
const engineImpl = reporter.extensionsManager.engines.find((e) => e.name === engine)
|
|
@@ -47,6 +48,7 @@ module.exports = (reporter) => {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
executionAsyncResultsMap.delete(executionId)
|
|
51
|
+
executionFinishListenersMap.delete(executionId)
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -94,6 +96,11 @@ module.exports = (reporter) => {
|
|
|
94
96
|
return Promise.all([...asyncResultMap.keys()].map((k) => asyncResultMap.get(k)))
|
|
95
97
|
}
|
|
96
98
|
},
|
|
99
|
+
addFinishListener: (fn) => {
|
|
100
|
+
if (executionFinishListenersMap.has(context.__executionId)) {
|
|
101
|
+
executionFinishListenersMap.get(context.__executionId).add('finish', fn)
|
|
102
|
+
}
|
|
103
|
+
},
|
|
97
104
|
createAsyncHelperResult: (v) => {
|
|
98
105
|
const asyncResultMap = executionAsyncResultsMap.get(context.__executionId)
|
|
99
106
|
const asyncResultId = nanoid(7)
|
|
@@ -190,9 +197,12 @@ module.exports = (reporter) => {
|
|
|
190
197
|
context.__executionId = executionId
|
|
191
198
|
|
|
192
199
|
executionAsyncResultsMap.set(executionId, asyncResultMap)
|
|
200
|
+
executionFinishListenersMap.set(executionId, reporter.createListenerCollection())
|
|
193
201
|
executionFnParsedParamsMap.get(req.context.id).get(executionFnParsedParamsKey).resolve({ require, console, topLevelFunctions, context })
|
|
194
202
|
|
|
195
|
-
const key =
|
|
203
|
+
const key = engine.buildTemplateCacheKey
|
|
204
|
+
? engine.buildTemplateCacheKey({ content }, req)
|
|
205
|
+
: `template:${content}:${engine.name}`
|
|
196
206
|
|
|
197
207
|
if (!templatesCache.has(key)) {
|
|
198
208
|
try {
|
|
@@ -208,7 +218,7 @@ module.exports = (reporter) => {
|
|
|
208
218
|
|
|
209
219
|
for (const h of Object.keys(topLevelFunctions)) {
|
|
210
220
|
// extra wrapping for enhance the error with the helper name
|
|
211
|
-
wrappedTopLevelFunctions[h] = wrapHelperForHelperNameWhenError(topLevelFunctions[h], h)
|
|
221
|
+
wrappedTopLevelFunctions[h] = wrapHelperForHelperNameWhenError(topLevelFunctions[h], h, () => executionFnParsedParamsMap.has(req.context.id))
|
|
212
222
|
|
|
213
223
|
if (engine.getWrappingHelpersEnabled && engine.getWrappingHelpersEnabled(req) === false) {
|
|
214
224
|
wrappedTopLevelFunctions[h] = engine.wrapHelper(wrappedTopLevelFunctions[h], { context })
|
|
@@ -221,19 +231,33 @@ module.exports = (reporter) => {
|
|
|
221
231
|
|
|
222
232
|
const resolvedResultsMap = new Map()
|
|
223
233
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
234
|
+
// we need to use the cloned map, becuase there can be a waitForAsyncHelper pending that needs the asyncResultMap values
|
|
235
|
+
const clonedMap = new Map(asyncResultMap)
|
|
236
|
+
while (clonedMap.size > 0) {
|
|
237
|
+
await Promise.all([...clonedMap.keys()].map(async (k) => {
|
|
238
|
+
resolvedResultsMap.set(k, `${await clonedMap.get(k)}`)
|
|
239
|
+
clonedMap.delete(k)
|
|
228
240
|
}))
|
|
229
241
|
}
|
|
242
|
+
asyncResultMap.clear()
|
|
230
243
|
|
|
231
244
|
while (contentResult.includes('{#asyncHelperResult')) {
|
|
232
245
|
contentResult = contentResult.replace(/{#asyncHelperResult ([^{}]+)}/g, (str, p1) => {
|
|
233
246
|
const asyncResultId = p1
|
|
247
|
+
// this can happen if a child jsreport.templatingEngines.evaluate receives an async value from outer scope
|
|
248
|
+
// because every evaluate uses a unique map of async resuts
|
|
249
|
+
// example is the case when component receives as a value async thing
|
|
250
|
+
// instead of returning "undefined" we let the outer eval to do the replace
|
|
251
|
+
if (!resolvedResultsMap.has(asyncResultId)) {
|
|
252
|
+
// returning asyncUnresolvedHelperResult just to avoid endless loop, after replace we put it back to asyncHelperResult
|
|
253
|
+
return `{#asyncUnresolvedHelperResult ${asyncResultId}}`
|
|
254
|
+
}
|
|
234
255
|
return `${resolvedResultsMap.get(asyncResultId)}`
|
|
235
256
|
})
|
|
236
257
|
}
|
|
258
|
+
contentResult = contentResult.replace(/asyncUnresolvedHelperResult/g, 'asyncHelperResult')
|
|
259
|
+
|
|
260
|
+
await executionFinishListenersMap.get(context.__executionId).fire()
|
|
237
261
|
|
|
238
262
|
return {
|
|
239
263
|
// handlebars escapes single brackets before execution to prevent errors on {#asset}
|
|
@@ -355,7 +379,7 @@ module.exports = (reporter) => {
|
|
|
355
379
|
}
|
|
356
380
|
}
|
|
357
381
|
|
|
358
|
-
function wrapHelperForHelperNameWhenError (fn, helperName) {
|
|
382
|
+
function wrapHelperForHelperNameWhenError (fn, helperName, isMainEvalStillRunningFn) {
|
|
359
383
|
return function (...args) {
|
|
360
384
|
let fnResult
|
|
361
385
|
|
|
@@ -373,6 +397,10 @@ module.exports = (reporter) => {
|
|
|
373
397
|
}
|
|
374
398
|
|
|
375
399
|
return fnResult.catch((asyncError) => {
|
|
400
|
+
if (!isMainEvalStillRunningFn()) {
|
|
401
|
+
// main exec already finished on some error, we just ignore errors of the hanging async calls
|
|
402
|
+
return
|
|
403
|
+
}
|
|
376
404
|
throw getEnhancedHelperError(asyncError)
|
|
377
405
|
})
|
|
378
406
|
}
|
|
@@ -72,7 +72,15 @@ class Profiler {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (m.doDiffs !== false && req.context.profiling.mode === 'full' && (m.type === 'operationStart' || m.type === 'operationEnd')) {
|
|
75
|
-
let
|
|
75
|
+
let originalResContent = res.content
|
|
76
|
+
|
|
77
|
+
// if content is empty assume null to keep old logic working without major changes
|
|
78
|
+
// (here and in studio)
|
|
79
|
+
if (originalResContent != null && originalResContent.length === 0) {
|
|
80
|
+
originalResContent = null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let content = originalResContent
|
|
76
84
|
|
|
77
85
|
if (content != null) {
|
|
78
86
|
if (content.length > this.reporter.options.profiler.maxDiffSize) {
|
|
@@ -82,12 +90,12 @@ class Profiler {
|
|
|
82
90
|
} else {
|
|
83
91
|
if (isbinaryfile(content)) {
|
|
84
92
|
content = {
|
|
85
|
-
content:
|
|
93
|
+
content: originalResContent.toString('base64'),
|
|
86
94
|
encoding: 'base64'
|
|
87
95
|
}
|
|
88
96
|
} else {
|
|
89
97
|
content = {
|
|
90
|
-
content: createPatch('res', req.context.profiling.resLastVal ? req.context.profiling.resLastVal.toString() : '',
|
|
98
|
+
content: createPatch('res', req.context.profiling.resLastVal ? req.context.profiling.resLastVal.toString() : '', originalResContent.toString(), 0),
|
|
91
99
|
encoding: 'diff'
|
|
92
100
|
}
|
|
93
101
|
}
|
|
@@ -107,7 +115,7 @@ class Profiler {
|
|
|
107
115
|
m.req.diff = createPatch('req', req.context.profiling.reqLastVal || '', stringifiedReq, 0)
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
req.context.profiling.resLastVal = (
|
|
118
|
+
req.context.profiling.resLastVal = (originalResContent == null || isbinaryfile(originalResContent) || content.tooLarge) ? null : originalResContent.toString()
|
|
111
119
|
req.context.profiling.resMetaLastVal = stringifiedResMeta
|
|
112
120
|
req.context.profiling.reqLastVal = stringifiedReq
|
|
113
121
|
}
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Orchestration of the rendering process
|
|
5
5
|
*/
|
|
6
|
-
const { Readable } = require('stream')
|
|
7
6
|
const extend = require('node.extend.without.arrays')
|
|
8
7
|
const ExecuteEngine = require('./executeEngine')
|
|
9
8
|
const Request = require('../../shared/request')
|
|
10
|
-
const
|
|
9
|
+
const Response = require('../../shared/response')
|
|
11
10
|
const resolveReferences = require('./resolveReferences.js')
|
|
12
11
|
const moduleHelper = require('./moduleHelper')
|
|
13
12
|
|
|
@@ -60,8 +59,7 @@ module.exports = (reporter) => {
|
|
|
60
59
|
reporter.logger.debug(`Rendering engine ${engine.name}`, request)
|
|
61
60
|
|
|
62
61
|
const engineRes = await executeEngine(engine, request)
|
|
63
|
-
|
|
64
|
-
response.content = Buffer.from(engineRes.content != null ? engineRes.content : '')
|
|
62
|
+
await response.output.update(Buffer.from(engineRes.content != null ? engineRes.content : ''))
|
|
65
63
|
|
|
66
64
|
reporter.profiler.emit({
|
|
67
65
|
type: 'operationEnd',
|
|
@@ -93,6 +91,7 @@ module.exports = (reporter) => {
|
|
|
93
91
|
reporter.logger.debug('Executing recipe ' + request.template.recipe, request)
|
|
94
92
|
|
|
95
93
|
await recipe.execute(request, response)
|
|
94
|
+
|
|
96
95
|
reporter.profiler.emit({
|
|
97
96
|
type: 'operationEnd',
|
|
98
97
|
operationId: recipeProfilerEvent.operationId
|
|
@@ -101,22 +100,24 @@ module.exports = (reporter) => {
|
|
|
101
100
|
|
|
102
101
|
async function afterRender (reporter, request, response) {
|
|
103
102
|
await reporter.afterRenderListeners.fire(request, response)
|
|
104
|
-
|
|
105
|
-
response.stream = Readable.from(response.content)
|
|
106
|
-
response.result = response.stream
|
|
107
|
-
|
|
108
103
|
return response
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
return async (req, parentReq) => {
|
|
112
107
|
const request = Request(req, parentReq)
|
|
113
|
-
|
|
108
|
+
|
|
109
|
+
if (request.context.id == null) {
|
|
110
|
+
request.context.id = reporter.generateRequestId()
|
|
111
|
+
}
|
|
112
|
+
if (parentReq == null) {
|
|
113
|
+
reporter.reqStorage.registerReq(request)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const response = Response(reporter, request.context.id)
|
|
117
|
+
|
|
114
118
|
let renderStartProfilerEvent
|
|
115
|
-
try {
|
|
116
|
-
if (request.context.id == null) {
|
|
117
|
-
request.context.id = generateRequestId()
|
|
118
|
-
}
|
|
119
119
|
|
|
120
|
+
try {
|
|
120
121
|
renderStartProfilerEvent = await reporter.profiler.renderStart(request, parentReq, response)
|
|
121
122
|
request.data = resolveReferences(request.data) || {}
|
|
122
123
|
|
|
@@ -194,6 +195,7 @@ module.exports = (reporter) => {
|
|
|
194
195
|
} finally {
|
|
195
196
|
if (parentReq == null) {
|
|
196
197
|
reporter.requestModulesCache.delete(request.context.rootId)
|
|
198
|
+
reporter.reqStorage.unregisterReq(request)
|
|
197
199
|
}
|
|
198
200
|
}
|
|
199
201
|
}
|
package/lib/worker/reporter.js
CHANGED
|
@@ -10,6 +10,7 @@ const Reporter = require('../shared/reporter')
|
|
|
10
10
|
const BlobStorage = require('./blobStorage.js')
|
|
11
11
|
const Render = require('./render/render')
|
|
12
12
|
const Profiler = require('./render/profiler.js')
|
|
13
|
+
const engineStream = require('./render/engineStream.js')
|
|
13
14
|
|
|
14
15
|
class WorkerReporter extends Reporter {
|
|
15
16
|
constructor (workerData, executeMain) {
|
|
@@ -79,6 +80,8 @@ class WorkerReporter extends Reporter {
|
|
|
79
80
|
execute: htmlRecipe
|
|
80
81
|
})
|
|
81
82
|
|
|
83
|
+
engineStream(this)
|
|
84
|
+
|
|
82
85
|
await this.initializeListeners.fire()
|
|
83
86
|
|
|
84
87
|
if (!this._lockedDown && this.options.trustUserCode === false) {
|
|
@@ -169,8 +172,8 @@ class WorkerReporter extends Reporter {
|
|
|
169
172
|
return proxyInstance
|
|
170
173
|
}
|
|
171
174
|
|
|
172
|
-
render (req, parentReq) {
|
|
173
|
-
return this._render(req, parentReq)
|
|
175
|
+
async render (req, parentReq) {
|
|
176
|
+
return await this._render(req, parentReq)
|
|
174
177
|
}
|
|
175
178
|
|
|
176
179
|
async executeMainAction (actionName, data, req) {
|
|
@@ -221,17 +224,8 @@ class WorkerReporter extends Reporter {
|
|
|
221
224
|
|
|
222
225
|
_registerRenderAction () {
|
|
223
226
|
this.registerWorkerAction('render', async (data, req) => {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
const sharedBuf = new SharedArrayBuffer(res.content.byteLength)
|
|
227
|
-
const buf = Buffer.from(sharedBuf)
|
|
228
|
-
|
|
229
|
-
res.content.copy(buf)
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
meta: res.meta,
|
|
233
|
-
content: buf
|
|
234
|
-
}
|
|
227
|
+
const response = await this._render(req)
|
|
228
|
+
return response.serialize()
|
|
235
229
|
})
|
|
236
230
|
}
|
|
237
231
|
|