@jsreport/jsreport-core 3.1.0 → 3.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/lib/main/defaults.js +1 -1
- package/lib/main/reporter.js +4 -9
- package/lib/shared/reporter.js +5 -0
- package/lib/shared/request.js +1 -1
- package/lib/worker/render/executeEngine.js +54 -22
- package/lib/worker/render/moduleHelper.js +7 -5
- package/lib/worker/render/render.js +6 -13
- package/lib/worker/reporter.js +1 -0
- package/lib/worker/sandbox/runInSandbox.js +17 -10
- package/lib/worker/templates.js +4 -2
- package/package.json +3 -3
package/lib/main/defaults.js
CHANGED
package/lib/main/reporter.js
CHANGED
|
@@ -385,8 +385,8 @@ class MainReporter extends Reporter {
|
|
|
385
385
|
|
|
386
386
|
if (
|
|
387
387
|
this.options.enableRequestReportTimeout &&
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
req.options &&
|
|
389
|
+
req.options.timeout != null
|
|
390
390
|
) {
|
|
391
391
|
reportTimeout = req
|
|
392
392
|
}
|
|
@@ -435,10 +435,6 @@ class MainReporter extends Reporter {
|
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
-
generateRequestId () {
|
|
439
|
-
return generateRequestId()
|
|
440
|
-
}
|
|
441
|
-
|
|
442
438
|
registerWorkersManagerFactory (workersManagerFactory) {
|
|
443
439
|
this._workersManagerFactory = workersManagerFactory
|
|
444
440
|
}
|
|
@@ -459,12 +455,12 @@ class MainReporter extends Reporter {
|
|
|
459
455
|
clearInterval(this._reaperTimerRef)
|
|
460
456
|
}
|
|
461
457
|
|
|
458
|
+
await this.closeListeners.fire()
|
|
459
|
+
|
|
462
460
|
if (this._workersManager) {
|
|
463
461
|
await this._workersManager.close()
|
|
464
462
|
}
|
|
465
463
|
|
|
466
|
-
await this.closeListeners.fire()
|
|
467
|
-
|
|
468
464
|
if (this.documentStore) {
|
|
469
465
|
await this.documentStore.close()
|
|
470
466
|
}
|
|
@@ -515,7 +511,6 @@ class MainReporter extends Reporter {
|
|
|
515
511
|
options: req.options
|
|
516
512
|
}
|
|
517
513
|
}, {
|
|
518
|
-
// TODO add worker timeout
|
|
519
514
|
timeout,
|
|
520
515
|
timeoutErrorMessage: options.timeoutErrorMessage || ('Timeout during worker action ' + actionName),
|
|
521
516
|
executeMain: async (data) => {
|
package/lib/shared/reporter.js
CHANGED
|
@@ -6,6 +6,7 @@ const Folders = require('./folders')
|
|
|
6
6
|
const createOrExtendError = require('./createError')
|
|
7
7
|
const tempFilesHandler = require('./tempFilesHandler')
|
|
8
8
|
const encryption = require('./encryption')
|
|
9
|
+
const generateRequestId = require('../shared/generateRequestId')
|
|
9
10
|
|
|
10
11
|
class Reporter extends EventEmitter {
|
|
11
12
|
constructor (options) {
|
|
@@ -46,6 +47,10 @@ class Reporter extends EventEmitter {
|
|
|
46
47
|
return createOrExtendError(message, options)
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
generateRequestId () {
|
|
51
|
+
return generateRequestId()
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
/**
|
|
50
55
|
* Ensures that the jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory) exists by doing a mkdir call
|
|
51
56
|
*
|
package/lib/shared/request.js
CHANGED
|
@@ -18,7 +18,7 @@ module.exports = (obj, parent) => {
|
|
|
18
18
|
request.template = extend(true, {}, obj.template)
|
|
19
19
|
|
|
20
20
|
if (parent) {
|
|
21
|
-
request.context = Object.assign({}, request.context, omit(parent.context, ['id', 'logs'
|
|
21
|
+
request.context = Object.assign({}, request.context, omit(parent.context, ['id', 'logs']))
|
|
22
22
|
request.context.isChildRequest = true
|
|
23
23
|
request.options = Object.assign({}, request.options, parent.options)
|
|
24
24
|
|
|
@@ -10,32 +10,50 @@ const { nanoid } = require('nanoid')
|
|
|
10
10
|
|
|
11
11
|
module.exports = (reporter) => {
|
|
12
12
|
const cache = LRU(reporter.options.sandbox.cache || { max: 100 })
|
|
13
|
+
|
|
13
14
|
reporter.templatingEngines = { cache }
|
|
15
|
+
|
|
14
16
|
const executionFnParsedParamsMap = new Map()
|
|
15
17
|
|
|
18
|
+
const templatingEnginesEvaluate = async (mainCall, { engine, content, helpers, data }, { entity, entitySet }, req) => {
|
|
19
|
+
const engineImpl = reporter.extensionsManager.engines.find((e) => e.name === engine)
|
|
20
|
+
|
|
21
|
+
if (!engine) {
|
|
22
|
+
throw reporter.createError(`Engine '${engine}' not found. If this is a custom engine make sure it's properly installed from npm`, {
|
|
23
|
+
statusCode: 400
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (mainCall) {
|
|
28
|
+
executionFnParsedParamsMap.set(req.context.id, new Map())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const res = await executeEngine({
|
|
33
|
+
engine: engineImpl,
|
|
34
|
+
content,
|
|
35
|
+
helpers,
|
|
36
|
+
data
|
|
37
|
+
}, { handleErrors: false, entity, entitySet }, req)
|
|
38
|
+
|
|
39
|
+
return res.content
|
|
40
|
+
} finally {
|
|
41
|
+
if (mainCall) {
|
|
42
|
+
executionFnParsedParamsMap.delete(req.context.id)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
reporter.templatingEngines.evaluate = (executionInfo, entityInfo, req) => templatingEnginesEvaluate(true, executionInfo, entityInfo, req)
|
|
48
|
+
|
|
16
49
|
reporter.extendProxy((proxy, req, {
|
|
17
50
|
runInSandbox,
|
|
18
51
|
context,
|
|
19
52
|
getTopLevelFunctions
|
|
20
53
|
}) => {
|
|
21
54
|
proxy.templatingEngines = {
|
|
22
|
-
evaluate: async (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!engine) {
|
|
26
|
-
throw reporter.createError(`Engine '${engine}' not found. If this is a custom engine make sure it's properly installed from npm`, {
|
|
27
|
-
statusCode: 400
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const res = await executeEngine({
|
|
32
|
-
engine: engineImpl,
|
|
33
|
-
content,
|
|
34
|
-
helpers,
|
|
35
|
-
systemHelpers: req.context.systemHelpers,
|
|
36
|
-
data
|
|
37
|
-
}, { handleErrors: false, entity, entitySet }, req)
|
|
38
|
-
return res.content
|
|
55
|
+
evaluate: async (executionInfo, entityInfo) => {
|
|
56
|
+
return templatingEnginesEvaluate(false, executionInfo, entityInfo, req)
|
|
39
57
|
}
|
|
40
58
|
}
|
|
41
59
|
})
|
|
@@ -51,7 +69,6 @@ module.exports = (reporter) => {
|
|
|
51
69
|
engine,
|
|
52
70
|
content: req.template.content,
|
|
53
71
|
helpers: req.template.helpers,
|
|
54
|
-
systemHelpers: req.context.systemHelpers,
|
|
55
72
|
data: req.data
|
|
56
73
|
}, {
|
|
57
74
|
handleErrors: true,
|
|
@@ -63,14 +80,29 @@ module.exports = (reporter) => {
|
|
|
63
80
|
}
|
|
64
81
|
}
|
|
65
82
|
|
|
66
|
-
async function executeEngine ({ engine, content, helpers,
|
|
83
|
+
async function executeEngine ({ engine, content, helpers, data }, { handleErrors, entity, entitySet }, req) {
|
|
67
84
|
let entityPath
|
|
85
|
+
|
|
68
86
|
if (entity._id) {
|
|
69
87
|
entityPath = await reporter.folders.resolveEntityPath(entity, entitySet, req)
|
|
70
88
|
entityPath = entityPath.substring(0, entityPath.lastIndexOf('/'))
|
|
71
89
|
}
|
|
72
90
|
|
|
73
|
-
const
|
|
91
|
+
const registerResults = await reporter.registerHelpersListeners.fire(req)
|
|
92
|
+
const systemHelpers = []
|
|
93
|
+
|
|
94
|
+
for (const result of registerResults) {
|
|
95
|
+
if (result == null) {
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (typeof result === 'string') {
|
|
100
|
+
systemHelpers.push(result)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const systemHelpersStr = systemHelpers.join('\n')
|
|
105
|
+
const joinedHelpers = systemHelpersStr + '\n' + (helpers || '')
|
|
74
106
|
const executionFnParsedParamsKey = `entity:${entity.shortid || 'anonymous'}:helpers:${joinedHelpers}`
|
|
75
107
|
|
|
76
108
|
const executionFn = async ({ require, console, topLevelFunctions }) => {
|
|
@@ -148,7 +180,7 @@ module.exports = (reporter) => {
|
|
|
148
180
|
userCode: joinedHelpers,
|
|
149
181
|
executionFn,
|
|
150
182
|
currentPath: entityPath,
|
|
151
|
-
errorLineNumberOffset:
|
|
183
|
+
errorLineNumberOffset: systemHelpersStr.split('\n').length,
|
|
152
184
|
onRequire: (moduleName, { context }) => {
|
|
153
185
|
if (engine.onRequire) {
|
|
154
186
|
return engine.onRequire(moduleName, { context })
|
|
@@ -191,7 +223,7 @@ module.exports = (reporter) => {
|
|
|
191
223
|
|
|
192
224
|
function wrapHelperForAsyncSupport (fn, asyncResultMap) {
|
|
193
225
|
return function (...args) {
|
|
194
|
-
// important to call the helper with the current this to preserve the same
|
|
226
|
+
// important to call the helper with the current this to preserve the same behavior
|
|
195
227
|
const fnResult = fn.call(this, ...args)
|
|
196
228
|
|
|
197
229
|
if (fnResult == null || typeof fnResult.then !== 'function') {
|
|
@@ -3,11 +3,13 @@ const path = require('path')
|
|
|
3
3
|
|
|
4
4
|
module.exports = (reporter) => {
|
|
5
5
|
let helpersScript
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
|
|
7
|
+
reporter.registerHelpersListeners.add('core-helpers', (req) => {
|
|
8
|
+
return helpersScript
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
reporter.initializeListeners.add('core-helpers', async () => {
|
|
12
|
+
helpersScript = await fs.readFile(path.join(__dirname, '../../static/helpers.js'), 'utf8')
|
|
11
13
|
})
|
|
12
14
|
|
|
13
15
|
reporter.extendProxy((proxy, req, { safeRequire }) => {
|
|
@@ -11,19 +11,9 @@ const generateRequestId = require('../../shared/generateRequestId')
|
|
|
11
11
|
const resolveReferences = require('./resolveReferences.js')
|
|
12
12
|
const moduleHelper = require('./moduleHelper')
|
|
13
13
|
let reportCounter = 0
|
|
14
|
-
const fs = require('fs').promises
|
|
15
|
-
const path = require('path')
|
|
16
14
|
|
|
17
15
|
module.exports = (reporter) => {
|
|
18
|
-
let helpersScript
|
|
19
|
-
reporter.beforeRenderListeners.add('core-helpers', async (req) => {
|
|
20
|
-
if (!helpersScript) {
|
|
21
|
-
helpersScript = await fs.readFile(path.join(__dirname, '../../static/helpers.js'), 'utf8')
|
|
22
|
-
}
|
|
23
|
-
req.context.systemHelpers += helpersScript + '\n'
|
|
24
|
-
})
|
|
25
16
|
moduleHelper(reporter)
|
|
26
|
-
reporter.addRequestContextMetaConfig('systemHelpers', { sandboxHidden: true })
|
|
27
17
|
|
|
28
18
|
const executeEngine = ExecuteEngine(reporter)
|
|
29
19
|
async function beforeRender (reporter, request, response) {
|
|
@@ -121,7 +111,6 @@ module.exports = (reporter) => {
|
|
|
121
111
|
|
|
122
112
|
return async (req, parentReq) => {
|
|
123
113
|
const request = Request(req, parentReq)
|
|
124
|
-
request.context.systemHelpers = ''
|
|
125
114
|
const response = { meta: {} }
|
|
126
115
|
let renderStartProfilerEvent
|
|
127
116
|
try {
|
|
@@ -141,7 +130,9 @@ module.exports = (reporter) => {
|
|
|
141
130
|
request.context.reportCounter = ++reportCounter
|
|
142
131
|
request.context.startTimestamp = new Date().getTime()
|
|
143
132
|
|
|
144
|
-
|
|
133
|
+
if (parentReq == null) {
|
|
134
|
+
reporter.requestModulesCache.set(request.context.rootId, Object.create(null))
|
|
135
|
+
}
|
|
145
136
|
|
|
146
137
|
reporter.logger.info(`Starting rendering request ${request.context.reportCounter} (user: ${(request.context.user ? request.context.user.username : 'null')})`, request)
|
|
147
138
|
|
|
@@ -203,7 +194,9 @@ module.exports = (reporter) => {
|
|
|
203
194
|
|
|
204
195
|
throw e
|
|
205
196
|
} finally {
|
|
206
|
-
|
|
197
|
+
if (parentReq == null) {
|
|
198
|
+
reporter.requestModulesCache.delete(request.context.rootId)
|
|
199
|
+
}
|
|
207
200
|
}
|
|
208
201
|
}
|
|
209
202
|
}
|
package/lib/worker/reporter.js
CHANGED
|
@@ -26,6 +26,7 @@ class WorkerReporter extends Reporter {
|
|
|
26
26
|
this._workerActions = new Map()
|
|
27
27
|
this._registerRenderAction()
|
|
28
28
|
|
|
29
|
+
this.registerHelpersListeners = this.createListenerCollection('registerHelpers')
|
|
29
30
|
this.afterTemplatingEnginesExecutedListeners = this.createListenerCollection('afterTemplatingEnginesExecuted')
|
|
30
31
|
this.validateRenderListeners = this.createListenerCollection('validateRender')
|
|
31
32
|
|
|
@@ -34,7 +34,7 @@ module.exports = (reporter) => {
|
|
|
34
34
|
formatError: (error, moduleName) => {
|
|
35
35
|
error.message += ` To be able to require custom modules you need to add to configuration { "allowLocalFilesAccess": true } or enable just specific module using { sandbox: { allowedModules": ["${moduleName}"] }`
|
|
36
36
|
},
|
|
37
|
-
modulesCache: reporter.requestModulesCache.get(req.context.
|
|
37
|
+
modulesCache: reporter.requestModulesCache.get(req.context.rootId),
|
|
38
38
|
globalModules: reporter.options.sandbox.nativeModules || [],
|
|
39
39
|
allowedModules: reporter.options.sandbox.allowedModules,
|
|
40
40
|
propertiesConfig,
|
|
@@ -75,15 +75,22 @@ module.exports = (reporter) => {
|
|
|
75
75
|
const functionNames = getTopLevelFunctions(userCode)
|
|
76
76
|
const functionsCode = `return {${functionNames.map(h => `"${h}": ${h}`).join(',')}}`
|
|
77
77
|
const executionCode = `;(async () => { ${userCode}; ${functionsCode} })()
|
|
78
|
-
.then((topLevelFunctions) =>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
.then((topLevelFunctions) => {
|
|
79
|
+
const mergedTopLevelFunctions = { ...topLevelFunctions, ...__topLevelFunctions }
|
|
80
|
+
|
|
81
|
+
// expose top level functions to the sandbox context
|
|
82
|
+
// so helpers can call other helpers (from shared asset helpers, or .registerHelpers call from proxy)
|
|
83
|
+
for (const [topLevelFnName, topLevelFn] of Object.entries(mergedTopLevelFunctions)) {
|
|
84
|
+
this[topLevelFnName] = topLevelFn
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return ${executionFnName}({
|
|
88
|
+
topLevelFunctions: mergedTopLevelFunctions,
|
|
89
|
+
require,
|
|
90
|
+
console,
|
|
91
|
+
context: this
|
|
92
|
+
})
|
|
93
|
+
}).catch(__handleError);`
|
|
87
94
|
|
|
88
95
|
return run(executionCode, {
|
|
89
96
|
filename: 'sandbox.js',
|
package/lib/worker/templates.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const extend = require('node.extend.without.arrays')
|
|
2
|
+
const omit = require('lodash.omit')
|
|
2
3
|
|
|
3
4
|
module.exports = (reporter) => {
|
|
4
5
|
reporter.addRequestContextMetaConfig('currentFolderPath', { sandboxReadOnly: true })
|
|
@@ -35,8 +36,9 @@ module.exports = (reporter) => {
|
|
|
35
36
|
})
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
// store a copy to prevent side-effects
|
|
39
|
-
|
|
39
|
+
// store a copy to prevent side-effects, we ignore name from the req.template because it can be path "/path/to/template"
|
|
40
|
+
// and we want that req.template.name be always the real template name
|
|
41
|
+
req.template = template ? extend(true, {}, template, omit(req.template, ['name'])) : req.template
|
|
40
42
|
req.template.content = req.template.content || ''
|
|
41
43
|
|
|
42
44
|
reporter.logger.info(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@babel/code-frame": "7.12.13",
|
|
33
33
|
"@babel/parser": "7.14.4",
|
|
34
34
|
"@babel/traverse": "7.12.9",
|
|
35
|
-
"@jsreport/advanced-workers": "1.
|
|
35
|
+
"@jsreport/advanced-workers": "1.2.0",
|
|
36
36
|
"@jsreport/mingo": "2.4.1",
|
|
37
37
|
"ajv": "6.12.6",
|
|
38
38
|
"app-root-path": "2.0.1",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"mocha": "8.2.1",
|
|
73
73
|
"should": "13.2.3",
|
|
74
|
-
"standard": "16.0.
|
|
74
|
+
"standard": "16.0.4",
|
|
75
75
|
"std-mocks": "1.0.1",
|
|
76
76
|
"winston-loggly-bulk": "3.2.1"
|
|
77
77
|
},
|