@jsreport/jsreport-core 3.12.0 → 4.0.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.
@@ -47,40 +47,46 @@ Settings.prototype.addOrSet = async function (key, avalue, req) {
47
47
  }
48
48
  }
49
49
 
50
- Settings.prototype.init = async function (documentStore, authorization) {
50
+ Settings.prototype.init = async function (documentStore, { authentication, authorization }) {
51
51
  this.documentStore = documentStore
52
52
 
53
- if (authorization != null) {
53
+ if (authentication != null && authorization != null) {
54
54
  const col = documentStore.collection('settings')
55
55
 
56
56
  // settings can be read by anyone so we don't add find listeners,
57
57
  // we only care about modification listeners
58
- col.beforeInsertListeners.add('settings', (doc, req) => {
58
+ col.beforeInsertListeners.add('settings', async (doc, req) => {
59
59
  if (req && req.context && req.context.skipAuthorization) {
60
60
  return
61
61
  }
62
62
 
63
- if (req && req.context && req.context.user && !req.context.user.isAdmin) {
63
+ const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
64
+
65
+ if (req && req.context && req.context.user && !isAdmin) {
64
66
  throw authorization.createAuthorizationError(col.name)
65
67
  }
66
68
  })
67
69
 
68
- col.beforeUpdateListeners.add('settings', (q, u, options, req) => {
70
+ col.beforeUpdateListeners.add('settings', async (q, u, options, req) => {
69
71
  if (req && req.context && req.context.skipAuthorization) {
70
72
  return
71
73
  }
72
74
 
73
- if (req && req.context && req.context.user && !req.context.user.isAdmin) {
75
+ const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
76
+
77
+ if (req && req.context && req.context.user && !isAdmin) {
74
78
  throw authorization.createAuthorizationError(col.name)
75
79
  }
76
80
  })
77
81
 
78
- col.beforeRemoveListeners.add('settings', (q, req) => {
82
+ col.beforeRemoveListeners.add('settings', async (q, req) => {
79
83
  if (req && req.context && req.context.skipAuthorization) {
80
84
  return
81
85
  }
82
86
 
83
- if (req && req.context && req.context.user && !req.context.user.isAdmin) {
87
+ const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
88
+
89
+ if (req && req.context && req.context.user && !isAdmin) {
84
90
  throw authorization.createAuthorizationError(col.name)
85
91
  }
86
92
  })
@@ -140,6 +140,10 @@ module.exports = (reporter) => {
140
140
  const executionFnParsedParamsKey = `entity:${entity.shortid || 'anonymous'}:helpers:${normalizedHelpers}`
141
141
 
142
142
  const initFn = async (getTopLevelFunctions, compileScript) => {
143
+ if (reporter.options.trustUserCode === false) {
144
+ return null
145
+ }
146
+
143
147
  if (systemHelpersCache != null) {
144
148
  return systemHelpersCache
145
149
  }
@@ -259,12 +263,30 @@ module.exports = (reporter) => {
259
263
  templatesCache.reset()
260
264
  }
261
265
 
266
+ let helpersStr = normalizedHelpers
267
+ if (reporter.options.trustUserCode === false) {
268
+ const registerResults = await reporter.registerHelpersListeners.fire()
269
+ const systemHelpers = []
270
+
271
+ for (const result of registerResults) {
272
+ if (result == null) {
273
+ continue
274
+ }
275
+
276
+ if (typeof result === 'string') {
277
+ systemHelpers.push(result)
278
+ }
279
+ }
280
+ const systemHelpersStr = systemHelpers.join('\n')
281
+ helpersStr = normalizedHelpers + '\n' + systemHelpersStr
282
+ }
283
+
262
284
  try {
263
285
  return await reporter.runInSandbox({
264
286
  context: {
265
287
  ...(engine.createContext ? engine.createContext(req) : {})
266
288
  },
267
- userCode: normalizedHelpers,
289
+ userCode: helpersStr,
268
290
  initFn,
269
291
  executionFn,
270
292
  currentPath: entityPath,
@@ -19,6 +19,7 @@ class WorkerReporter extends Reporter {
19
19
 
20
20
  this._executeMain = executeMain
21
21
  this._initialized = false
22
+ this._lockedDown = false
22
23
  this._documentStoreData = documentStore
23
24
  this._requestContextMetaConfigCollection = new Map()
24
25
  this._proxyRegistrationFns = []
@@ -80,6 +81,50 @@ class WorkerReporter extends Reporter {
80
81
 
81
82
  await this.initializeListeners.fire()
82
83
 
84
+ if (!this._lockedDown && this.options.trustUserCode === false) {
85
+ require('@jsreport/ses')
86
+
87
+ // eslint-disable-next-line
88
+ lockdown({
89
+ // don't change locale based methods which users may be using in their templates
90
+ localeTaming: 'unsafe',
91
+ errorTaming: 'unsafe',
92
+ stackFiltering: 'verbose',
93
+ /*
94
+ FROM SES DOCS
95
+ The 'severe' setting enables all the properties on at least Object.prototype, which is sometimes needed for compatibility with code generated by rollup or webpack.
96
+ However, this extra compatibility comes at the price of a miserable debugging experience.
97
+
98
+ We need this to make jsrender working, which overrides constructor.
99
+ In case we need to put back default, we will need to fork jsrender and change the following line
100
+ (Tag.prototype = compiledDef).constructor = compiledDef._ctr = Tag;
101
+ x
102
+ Tag.prototype = compiledDef
103
+ compiledDef._ctr = Tag
104
+ */
105
+ overrideTaming: 'severe'
106
+ })
107
+
108
+ // in this mode we alias the unsafe methods to safe ones
109
+ Buffer.allocUnsafe = function allocUnsafe (size) {
110
+ return Buffer.alloc(size)
111
+ }
112
+
113
+ Buffer.allocUnsafeSlow = function allocUnsafeSlow (size) {
114
+ return Buffer.alloc(size)
115
+ }
116
+
117
+ // we also harden Buffer because we expose it to sandbox
118
+ // eslint-disable-next-line
119
+ harden(Buffer)
120
+
121
+ // we need to expose Intl to sandbox
122
+ // eslint-disable-next-line
123
+ harden(Intl)
124
+
125
+ this._lockedDown = true
126
+ }
127
+
83
128
  this._initialized = true
84
129
  }
85
130
 
@@ -1,12 +1,11 @@
1
1
  const util = require('util')
2
- const { VM, VMScript } = require('vm2')
3
- const originalVM = require('vm')
2
+ const vm = require('vm')
4
3
  const stackTrace = require('stack-trace')
5
4
  const { codeFrameColumns } = require('@babel/code-frame')
6
5
  const createPropertiesManager = require('./propertiesSandbox')
7
6
  const createSandboxRequire = require('./requireSandbox')
8
7
 
9
- module.exports = function createSandbox (_sandbox, options = {}) {
8
+ module.exports = async function createSandbox (_sandbox, options = {}) {
10
9
  const {
11
10
  rootDirectory,
12
11
  onLog,
@@ -47,11 +46,35 @@ module.exports = function createSandbox (_sandbox, options = {}) {
47
46
 
48
47
  propsManager.applyPropertiesConfigTo(sandbox)
49
48
 
50
- let safeVM
51
- // with standard vm this variable is the same as context, with vm2 it is a proxy of context
52
- // (which is not the real internal context)
49
+ const sourceFilesInfo = new Map()
50
+ // eslint-disable-next-line
51
+ let compartment
52
+
53
+ if (safeExecution) {
54
+ // eslint-disable-next-line
55
+ compartment = new Compartment()
56
+ }
57
+
53
58
  let vmSandbox
54
59
 
60
+ if (safeExecution) {
61
+ vmSandbox = compartment.globalThis
62
+
63
+ vmSandbox = Object.assign(vmSandbox, {
64
+ // SES does not expose the Buffer, Intl by default, we expose it because it is handy for users,
65
+ // it is exposed as it is, because we already harden() it on reporter init
66
+ Buffer,
67
+ Intl,
68
+ // we need to expose Date, and Math to allow Date.now(), Math.random()
69
+ // these objects are already hardened by lockdown()
70
+ Date,
71
+ Math
72
+ })
73
+ } else {
74
+ vmSandbox = vm.createContext(undefined)
75
+ vmSandbox.Buffer = Buffer
76
+ }
77
+
55
78
  const doSandboxRequire = createSandboxRequire(safeExecution, isolateModules, modulesCache, {
56
79
  rootDirectory,
57
80
  requirePaths,
@@ -63,43 +86,17 @@ module.exports = function createSandbox (_sandbox, options = {}) {
63
86
 
64
87
  Object.assign(sandbox, {
65
88
  console: _console,
66
- require (m) { return doSandboxRequire(m, { context: vmSandbox }) }
67
- })
68
-
69
- if (safeExecution) {
70
- safeVM = new VM()
71
-
72
- // delete the vm.sandbox.global because it introduces json stringify issues
73
- // and we don't need such global in context
74
- delete safeVM.sandbox.global
75
-
76
- for (const name in sandbox) {
77
- safeVM.setGlobal(name, sandbox[name])
89
+ require: (m) => { return doSandboxRequire(m, { context: vmSandbox }) },
90
+ setTimeout: (...args) => {
91
+ return setTimeout(...args)
92
+ },
93
+ clearTimeout: (...args) => {
94
+ return clearTimeout(...args)
78
95
  }
96
+ })
79
97
 
80
- // so far we don't have the need to have access to real vm context inside vm2,
81
- // but if we need it, we should use the code bellow to get it.
82
- // NOTE: if we need to upgrade vm2 we will need to check the source of this function
83
- // in vm2 repo and see if we need to change this,
84
- // we just execute this to get access to the internal context, so we can use it later
85
- // with the our require function, in newer versions of vm2 we may need to change how to
86
- // get access to it
87
- // https://github.com/patriksimek/vm2/blob/3.9.17/lib/vm.js#L281
88
- // safeVM._runScript({
89
- // runInContext: (_context) => {
90
- // vmContext = _context
91
- // return ''
92
- // }
93
- // })
94
-
95
- vmSandbox = safeVM.sandbox
96
- } else {
97
- vmSandbox = originalVM.createContext(undefined)
98
- vmSandbox.Buffer = Buffer
99
-
100
- for (const name in sandbox) {
101
- vmSandbox[name] = sandbox[name]
102
- }
98
+ for (const name in sandbox) {
99
+ vmSandbox[name] = sandbox[name]
103
100
  }
104
101
 
105
102
  // processing top level props here because getter/setter descriptors
@@ -112,8 +109,6 @@ module.exports = function createSandbox (_sandbox, options = {}) {
112
109
  vmSandbox[info.globalVariableName] = doSandboxRequire(info.module, { context: vmSandbox, useMap: false, allowAllModules: true })
113
110
  }
114
111
 
115
- const sourceFilesInfo = new Map()
116
-
117
112
  return {
118
113
  sandbox: vmSandbox,
119
114
  console: _console,
@@ -128,29 +123,20 @@ module.exports = function createSandbox (_sandbox, options = {}) {
128
123
  return doSandboxRequire(modulePath, { context: vmSandbox, allowAllModules: true })
129
124
  },
130
125
  async run (codeOrScript, { filename, errorLineNumberOffset = 0, source, entity, entitySet } = {}) {
131
- let runScript
132
-
133
126
  if (filename != null && source != null) {
134
127
  sourceFilesInfo.set(filename, { filename, source, entity, entitySet, errorLineNumberOffset })
135
128
  }
136
129
 
137
- const script = typeof codeOrScript !== 'string' ? codeOrScript : doCompileScript(codeOrScript, filename, safeExecution)
138
-
139
- if (safeExecution) {
140
- runScript = async function runScript () {
141
- return safeVM.run(script)
142
- }
143
- } else {
144
- runScript = async function runScript () {
145
- return script.runInContext(vmSandbox, {
146
- displayErrors: true
147
- })
130
+ try {
131
+ if (safeExecution) {
132
+ return await compartment.evaluate(codeOrScript + `\n//# sourceURL=${filename}`)
148
133
  }
149
- }
150
134
 
151
- try {
152
- const result = await runScript()
153
- return result
135
+ const script = typeof codeOrScript !== 'string' ? codeOrScript : doCompileScript(codeOrScript, filename, safeExecution)
136
+
137
+ return await script.runInContext(vmSandbox, {
138
+ displayErrors: true
139
+ })
154
140
  } catch (e) {
155
141
  decorateErrorMessage(e, sourceFilesInfo)
156
142
 
@@ -161,47 +147,19 @@ module.exports = function createSandbox (_sandbox, options = {}) {
161
147
  }
162
148
 
163
149
  function doCompileScript (code, filename, safeExecution) {
164
- let script
165
-
166
150
  if (safeExecution) {
167
- script = new VMScript(code, filename)
168
-
169
- // NOTE: if we need to upgrade vm2 we will need to check the source of this function
170
- // in vm2 repo and see if we need to change this,
171
- // we needed to override this method because we want "displayErrors" to be true in order
172
- // to show nice error when the compile of a script fails
173
- // https://github.com/patriksimek/vm2/blob/3.9.17/lib/script.js#L329
174
- script._compile = function (prefix, suffix) {
175
- return new originalVM.Script(prefix + this.getCompiledCode() + suffix, {
176
- __proto__: null,
177
- filename: this.filename,
178
- displayErrors: true,
179
- lineOffset: this.lineOffset,
180
- columnOffset: this.columnOffset,
181
- // THIS FN WAS TAKEN FROM vm2 source, nothing special here
182
- importModuleDynamically: () => {
183
- // We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
184
- // eslint-disable-next-line no-throw-literal
185
- throw 'Dynamic imports are not allowed.'
186
- }
187
- })
188
- }
189
-
190
- // do the compilation
191
- script._compileVM()
192
- } else {
193
- script = new originalVM.Script(code, {
194
- filename,
195
- displayErrors: true,
196
- importModuleDynamically: () => {
197
- // We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
198
- // eslint-disable-next-line no-throw-literal
199
- throw 'Dynamic imports are not allowed.'
200
- }
201
- })
151
+ return code
202
152
  }
203
153
 
204
- return script
154
+ return new vm.Script(code, {
155
+ filename,
156
+ displayErrors: true,
157
+ importModuleDynamically: () => {
158
+ // We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
159
+ // eslint-disable-next-line no-throw-literal
160
+ throw 'Dynamic imports are not allowed.'
161
+ }
162
+ })
205
163
  }
206
164
 
207
165
  function decorateErrorMessage (e, sourceFilesInfo) {
@@ -6,8 +6,6 @@ const REQUIRE_RESOLVE_CACHE = new Map()
6
6
  const REQUIRE_SCRIPT_CACHE = new Map()
7
7
  const PACKAGE_JSON_CACHE = new Map()
8
8
 
9
- let ALL_BUILTIN_MODULES
10
-
11
9
  // The isolated require is a function that replicates the node.js require but that does not
12
10
  // cache the modules with the standard node.js cache, instead its uses its own cache in order
13
11
  // to bring isolated modules across renders and without memory leaks.
@@ -24,7 +22,7 @@ function isolatedRequire (_moduleId, requireFromRootDirectory, isolatedModulesMe
24
22
  throw createInvalidArgValueError('id', moduleId, 'must be a non-empty string')
25
23
  }
26
24
 
27
- if (isBuiltinModule(moduleId)) {
25
+ if (Module.isBuiltin(moduleId)) {
28
26
  // built-in modules can not be require from other part than the node.js require
29
27
  // perhaps in the future it can be possible:
30
28
  // https://github.com/nodejs/node/issues/31852
@@ -174,24 +172,6 @@ function IsolatedModule (id = '', parent) {
174
172
  })
175
173
  }
176
174
 
177
- // NOTE: we can not use Module.isBuiltin because it is not available on node 16
178
- // we can upgrade our implementation to just use Module.isBuiltin when we drop support for node 16
179
- // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
180
- function isBuiltinModule (moduleId) {
181
- // use the standard function when available
182
- if (Module.isBuiltin) {
183
- return Module.isBuiltin(moduleId)
184
- }
185
-
186
- // the only version in which this code would run is node 16 and early versions of node 18
187
- // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
188
- if (!ALL_BUILTIN_MODULES) {
189
- ALL_BUILTIN_MODULES = new Set(Module.builtinModules.flatMap((bm) => [bm, `node:${bm}`]))
190
- }
191
-
192
- return ALL_BUILTIN_MODULES.has(moduleId)
193
- }
194
-
195
175
  // https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/helpers.js#L65
196
176
  function makeRequireFunction (mod, requireFromRootDirectory, currentExtensions) {
197
177
  const requireFn = function require (path) {
@@ -21,7 +21,7 @@ module.exports = function createRunInSandbox (reporter) {
21
21
  }, req) {
22
22
  let jsreportProxy = null
23
23
 
24
- // we use dynamic name because of the potential nested vm2 execution in the jsreportProxy.assets.require
24
+ // we use dynamic name because of the potential nested vm execution in the jsreportProxy.assets.require
25
25
  // it may turn out it is a bad approach in assets so we gonna delete it here
26
26
  const executionFnName = `${nanoid()}_executionFn`
27
27
 
@@ -29,11 +29,10 @@ module.exports = function createRunInSandbox (reporter) {
29
29
  context.__appDirectory = reporter.options.appDirectory
30
30
  context.__rootDirectory = reporter.options.rootDirectory
31
31
  context.__parentModuleDirectory = reporter.options.parentModuleDirectory
32
- context.setTimeout = setTimeout
33
32
  context.__topLevelFunctions = {}
34
33
  context.__handleError = (err) => handleError(reporter, err)
35
34
 
36
- const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = createSandbox(context, {
35
+ const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = await createSandbox(context, {
37
36
  rootDirectory: reporter.options.rootDirectory,
38
37
  onLog: (log) => {
39
38
  // we mark any log done in sandbox as userLevel: true, this allows us to detect which logs belongs to user
@@ -159,9 +158,23 @@ module.exports = function createRunInSandbox (reporter) {
159
158
  }
160
159
 
161
160
  const functionNames = getTopLevelFunctions(functionsCache, userCode)
162
- const functionsCode = `return {${functionNames.map(h => `"${h}": ${h}`).join(',')}}`
163
- const executionCode = `;(async () => { ${userCode} \n\n;${functionsCode} })()
164
- .then((topLevelFunctions) => {
161
+
162
+ // it is better we remove our internal functions so we avoid user having the chance
163
+ // to call them, as long as we force the execution to be truly async (with the await 1)
164
+ // then it is safe to delete __handleError from context, when the execution is truly
165
+ // async then it means the __handleError was already passed to catch handler,
166
+ // therefore safe to delete
167
+ const contextNormalizeCode = [
168
+ 'await 1;',
169
+ `const ${executionFnName}_expose = ${executionFnName};`,
170
+ 'delete this.__handleError;',
171
+ `delete this['${executionFnName}'];`
172
+ ].join('')
173
+
174
+ const functionsCode = `return {topLevelFunctions: {${functionNames.map(h => `"${h}": ${h}`).join(',')}}, fnToExecute: ${executionFnName}_expose}`
175
+
176
+ const executionCode = `;(async () => { ${contextNormalizeCode}${userCode} \n\n;${functionsCode} })()
177
+ .then(({ topLevelFunctions, fnToExecute }) => {
165
178
  const mergedTopLevelFunctions = { ...topLevelFunctions, ...__topLevelFunctions }
166
179
 
167
180
  // expose top level functions to the sandbox context
@@ -170,7 +183,7 @@ module.exports = function createRunInSandbox (reporter) {
170
183
  this[topLevelFnName] = topLevelFn
171
184
  }
172
185
 
173
- return ${executionFnName}({
186
+ return fnToExecute({
174
187
  topLevelFunctions: mergedTopLevelFunctions,
175
188
  require,
176
189
  console,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsreport/jsreport-core",
3
- "version": "3.12.0",
3
+ "version": "4.0.0",
4
4
  "description": "javascript based business reporting",
5
5
  "keywords": [
6
6
  "report",
@@ -40,9 +40,11 @@
40
40
  "@babel/parser": "7.14.4",
41
41
  "@babel/traverse": "7.12.9",
42
42
  "@colors/colors": "1.5.0",
43
- "@jsreport/advanced-workers": "1.3.0",
43
+ "@jsreport/advanced-workers": "2.0.0",
44
44
  "@jsreport/mingo": "2.4.1",
45
45
  "@jsreport/reap": "0.1.0",
46
+ "@jsreport/serializator": "1.0.0",
47
+ "@jsreport/ses": "1.0.0",
46
48
  "ajv": "6.12.6",
47
49
  "app-root-path": "3.0.0",
48
50
  "bytes": "3.1.2",
@@ -65,13 +67,11 @@
65
67
  "nanoid": "3.2.0",
66
68
  "nconf": "0.12.0",
67
69
  "node.extend.without.arrays": "1.1.6",
68
- "semver": "7.3.5",
69
- "serializator": "1.0.2",
70
+ "semver": "7.5.4",
70
71
  "stack-trace": "0.0.10",
71
72
  "triple-beam": "1.3.0",
72
73
  "unset-value": "2.0.1",
73
74
  "uuid": "8.3.2",
74
- "vm2": "3.9.19",
75
75
  "winston": "3.8.1",
76
76
  "winston-transport": "4.5.0",
77
77
  "yieldable-json": "2.0.1"
@@ -85,7 +85,7 @@
85
85
  "winston-loggly-bulk": "3.2.1"
86
86
  },
87
87
  "engines": {
88
- "node": ">=16.11"
88
+ "node": ">=18.15"
89
89
  },
90
90
  "standard": {
91
91
  "env": {
@@ -1,51 +1,62 @@
1
-
2
- module.exports = (reporter, definition) => {
3
- reporter.tests = reporter.tests || {}
4
- reporter.tests.beforeRenderListeners = reporter.createListenerCollection()
5
- reporter.tests.afterRenderListeners = reporter.createListenerCollection()
6
- reporter.tests.validateRenderListeners = reporter.createListenerCollection()
7
- reporter.tests.afterTemplatingEnginesExecutedListeners = reporter.createListenerCollection()
8
-
9
- reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
10
- data.req = reporter.Request(data.req)
11
- await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
12
- return { req: data.req, res: data.res }
13
- })
14
- reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
15
- data.req = reporter.Request(data.req)
16
- await reporter.tests.afterRenderListeners.fire(data.req, data.res)
17
- return { req: data.req, res: data.res }
18
- })
19
- reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
20
- data.req = reporter.Request(data.req)
21
- await reporter.tests.validateRenderListeners.fire(data.req, data.res)
22
- return { req: data.req, res: data.res }
23
- })
24
- reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
25
- data.req = reporter.Request(data.req)
26
- await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
27
- return { req: data.req, res: data.res }
28
- })
29
-
30
- let beforeRenderEval
31
- reporter.tests.beforeRenderEval = (fn) => {
32
- beforeRenderEval = fn
33
- }
34
- reporter.registerMainAction('test-beforeRenderEval', async (data, req) => {
35
- if (beforeRenderEval == null) {
36
- return
37
- }
38
- return beforeRenderEval.toString()
39
- })
40
-
41
- let afterRenderEval
42
- reporter.tests.afterRenderEval = (fn) => {
43
- afterRenderEval = fn
44
- }
45
- reporter.registerMainAction('test-afterRenderEval', async (data, req) => {
46
- if (afterRenderEval == null) {
47
- return
48
- }
49
- return afterRenderEval.toString()
50
- })
51
- }
1
+
2
+ module.exports = (reporter, definition) => {
3
+ reporter.tests = reporter.tests || {}
4
+ reporter.tests.beforeRenderListeners = reporter.createListenerCollection()
5
+ reporter.tests.afterRenderListeners = reporter.createListenerCollection()
6
+ reporter.tests.validateRenderListeners = reporter.createListenerCollection()
7
+ reporter.tests.afterTemplatingEnginesExecutedListeners = reporter.createListenerCollection()
8
+
9
+ reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
10
+ data.req = reporter.Request(data.req)
11
+ await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
12
+ return { req: data.req, res: data.res }
13
+ })
14
+ reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
15
+ data.req = reporter.Request(data.req)
16
+ await reporter.tests.afterRenderListeners.fire(data.req, data.res)
17
+ return { req: data.req, res: data.res }
18
+ })
19
+ reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
20
+ data.req = reporter.Request(data.req)
21
+ await reporter.tests.validateRenderListeners.fire(data.req, data.res)
22
+ return { req: data.req, res: data.res }
23
+ })
24
+ reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
25
+ data.req = reporter.Request(data.req)
26
+ await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
27
+ return { req: data.req, res: data.res }
28
+ })
29
+
30
+ let beforeRenderEval
31
+ reporter.tests.beforeRenderEval = (fn) => {
32
+ beforeRenderEval = fn
33
+ }
34
+ reporter.registerMainAction('test-beforeRenderEval', async (data, req) => {
35
+ if (beforeRenderEval == null) {
36
+ return
37
+ }
38
+ return beforeRenderEval.toString()
39
+ })
40
+
41
+ let afterRenderEval
42
+ reporter.tests.afterRenderEval = (fn) => {
43
+ afterRenderEval = fn
44
+ }
45
+ reporter.registerMainAction('test-afterRenderEval', async (data, req) => {
46
+ if (afterRenderEval == null) {
47
+ return
48
+ }
49
+ return afterRenderEval.toString()
50
+ })
51
+
52
+ let afterTemplatingEnginesExecutedEval
53
+ reporter.tests.afterTemplatingEnginesExecutedEval = (fn) => {
54
+ afterTemplatingEnginesExecutedEval = fn
55
+ }
56
+ reporter.registerMainAction('test-afterTemplatingEnginesExecutedEval', async (data, req) => {
57
+ if (afterTemplatingEnginesExecutedEval == null) {
58
+ return
59
+ }
60
+ return afterTemplatingEnginesExecutedEval.toString()
61
+ })
62
+ }