@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.
Files changed (81) hide show
  1. package/LICENSE +166 -166
  2. package/README.md +310 -298
  3. package/index.js +29 -29
  4. package/lib/main/blobStorage/blobStorage.js +53 -52
  5. package/lib/main/blobStorage/inMemoryProvider.js +27 -27
  6. package/lib/main/blobStorage/mainActions.js +24 -24
  7. package/lib/main/createDefaultLoggerFormat.js +17 -17
  8. package/lib/main/defaults.js +14 -14
  9. package/lib/main/extensions/discover.js +20 -20
  10. package/lib/main/extensions/extensionsManager.js +264 -264
  11. package/lib/main/extensions/fileUtils.js +56 -56
  12. package/lib/main/extensions/findVersion.js +49 -49
  13. package/lib/main/extensions/locationCache.js +103 -103
  14. package/lib/main/extensions/sorter.js +10 -10
  15. package/lib/main/extensions/validateMinimalVersion.js +50 -50
  16. package/lib/main/folders/cascadeFolderRemove.js +25 -25
  17. package/lib/main/folders/getEntitiesInFolder.js +53 -53
  18. package/lib/main/folders/index.js +42 -42
  19. package/lib/main/folders/moveBetweenFolders.js +354 -354
  20. package/lib/main/folders/validateDuplicatedName.js +107 -107
  21. package/lib/main/folders/validateReservedName.js +53 -53
  22. package/lib/main/logger.js +254 -244
  23. package/lib/main/migration/resourcesToAssets.js +230 -230
  24. package/lib/main/migration/xlsxTemplatesToAssets.js +128 -128
  25. package/lib/main/monitoring.js +92 -91
  26. package/lib/main/optionsLoad.js +231 -237
  27. package/lib/main/optionsSchema.js +237 -237
  28. package/lib/main/profiler.js +13 -1
  29. package/lib/main/reporter.js +589 -579
  30. package/lib/main/request.js +21 -0
  31. package/lib/main/schemaValidator.js +252 -252
  32. package/lib/main/settings.js +154 -154
  33. package/lib/main/store/checkDuplicatedId.js +27 -27
  34. package/lib/main/store/collection.js +329 -329
  35. package/lib/main/store/documentStore.js +469 -469
  36. package/lib/main/store/mainActions.js +28 -28
  37. package/lib/main/store/memoryStoreProvider.js +99 -99
  38. package/lib/main/store/queue.js +48 -48
  39. package/lib/main/store/referenceUtils.js +251 -251
  40. package/lib/main/store/setupValidateId.js +43 -43
  41. package/lib/main/store/setupValidateShortid.js +71 -71
  42. package/lib/main/store/transaction.js +69 -69
  43. package/lib/main/store/typeUtils.js +180 -180
  44. package/lib/main/templates.js +34 -34
  45. package/lib/main/validateEntityName.js +62 -62
  46. package/lib/shared/createError.js +36 -36
  47. package/lib/shared/encryption.js +114 -114
  48. package/lib/shared/folders/index.js +11 -11
  49. package/lib/shared/folders/normalizeEntityPath.js +15 -15
  50. package/lib/shared/folders/resolveEntityFromPath.js +88 -88
  51. package/lib/shared/folders/resolveEntityPath.js +46 -46
  52. package/lib/shared/folders/resolveFolderFromPath.js +38 -38
  53. package/lib/shared/generateRequestId.js +4 -4
  54. package/lib/shared/listenerCollection.js +169 -169
  55. package/lib/shared/normalizeMetaFromLogs.js +30 -30
  56. package/lib/shared/reporter.js +128 -123
  57. package/lib/shared/request.js +64 -64
  58. package/lib/shared/tempFilesHandler.js +81 -81
  59. package/lib/shared/templates.js +82 -82
  60. package/lib/static/helpers.js +33 -33
  61. package/lib/worker/blobStorage.js +34 -34
  62. package/lib/worker/defaultProxyExtend.js +46 -46
  63. package/lib/worker/documentStore.js +49 -49
  64. package/lib/worker/extensionsManager.js +17 -17
  65. package/lib/worker/logger.js +48 -48
  66. package/lib/worker/render/diff.js +138 -138
  67. package/lib/worker/render/executeEngine.js +232 -207
  68. package/lib/worker/render/htmlRecipe.js +10 -10
  69. package/lib/worker/render/moduleHelper.js +45 -43
  70. package/lib/worker/render/noneEngine.js +12 -12
  71. package/lib/worker/render/profiler.js +162 -158
  72. package/lib/worker/render/render.js +202 -205
  73. package/lib/worker/render/resolveReferences.js +60 -60
  74. package/lib/worker/reporter.js +197 -191
  75. package/lib/worker/sandbox/runInSandbox.js +64 -12
  76. package/lib/worker/sandbox/safeSandbox.js +829 -828
  77. package/lib/worker/templates.js +80 -78
  78. package/lib/worker/workerHandler.js +55 -54
  79. package/package.json +91 -92
  80. package/test/blobStorage/common.js +25 -21
  81. package/test/store/common.js +1449 -1449
@@ -1,191 +1,197 @@
1
- const ExtensionsManager = require('./extensionsManager')
2
- const DocumentStore = require('./documentStore')
3
- const Templates = require('./templates')
4
- const createLogger = require('./logger')
5
- const runInSandbox = require('./sandbox/runInSandbox')
6
- const createNoneEngine = require('./render/noneEngine')
7
- const htmlRecipe = require('./render/htmlRecipe')
8
- const defaultProxyExtend = require('./defaultProxyExtend')
9
- const Reporter = require('../shared/reporter')
10
- const BlobStorage = require('./blobStorage.js')
11
- const Render = require('./render/render')
12
- const Profiler = require('./render/profiler.js')
13
-
14
- class WorkerReporter extends Reporter {
15
- constructor (workerData, executeMain) {
16
- const { options, documentStore, extensionsDefs } = workerData
17
-
18
- super(options)
19
-
20
- this._executeMain = executeMain
21
- this._initialized = false
22
- this._documentStoreData = documentStore
23
- this._requestContextMetaConfigCollection = new Map()
24
- this._proxyRegistrationFns = []
25
- this.requestModulesCache = new Map()
26
- this._workerActions = new Map()
27
- this._registerRenderAction()
28
-
29
- this.afterTemplatingEnginesExecutedListeners = this.createListenerCollection('afterTemplatingEnginesExecuted')
30
- this.validateRenderListeners = this.createListenerCollection('validateRender')
31
-
32
- this.extensionsManager = ExtensionsManager(this, extensionsDefs)
33
-
34
- this.extendProxy((proxy, req) => defaultProxyExtend(this)(proxy, req))
35
- this.beforeMainActionListeners = this.createListenerCollection('beforeMainAction')
36
- }
37
-
38
- async init () {
39
- if (this._initialized === true) {
40
- throw new Error('jsreport already initialized. Make sure init is called only once')
41
- }
42
-
43
- super.init()
44
-
45
- Templates(this)
46
-
47
- this.profiler = Profiler(this)
48
- this.logger = createLogger(this.profiler)
49
-
50
- this._render = Render(this)
51
- await this.extensionsManager.init()
52
-
53
- this.documentStore = DocumentStore(this._documentStoreData, this.executeMainAction.bind(this))
54
- this.blobStorage = BlobStorage(this.executeMainAction.bind(this))
55
-
56
- this.addRequestContextMetaConfig('rootId', { sandboxReadOnly: true })
57
- this.addRequestContextMetaConfig('id', { sandboxReadOnly: true })
58
- this.addRequestContextMetaConfig('reportCounter', { sandboxReadOnly: true })
59
- this.addRequestContextMetaConfig('startTimestamp', { sandboxReadOnly: true })
60
- this.addRequestContextMetaConfig('logs', { sandboxReadOnly: true })
61
- this.addRequestContextMetaConfig('isChildRequest', { sandboxReadOnly: true })
62
- this.addRequestContextMetaConfig('originalInputDataIsEmpty', { sandboxReadOnly: true })
63
- this.addRequestContextMetaConfig('skipModificationDateUpdate', { sandboxHidden: true })
64
-
65
- this._runInSandbox = runInSandbox(this)
66
-
67
- const { compile: compileNone, execute: executeNone } = createNoneEngine()
68
-
69
- this.extensionsManager.engines.push({
70
- name: 'none',
71
- compile: compileNone,
72
- execute: executeNone
73
- })
74
-
75
- this.extensionsManager.recipes.push({
76
- name: 'html',
77
- execute: htmlRecipe
78
- })
79
-
80
- await this.initializeListeners.fire()
81
-
82
- this._initialized = true
83
- }
84
-
85
- /**
86
- * @public
87
- */
88
- addRequestContextMetaConfig (property, options) {
89
- this._requestContextMetaConfigCollection.set(property, options)
90
- }
91
-
92
- /**
93
- * @public
94
- */
95
- getRequestContextMetaConfig (property) {
96
- if (property === undefined) {
97
- const all = {}
98
-
99
- for (const [key, value] of this._requestContextMetaConfigCollection.entries()) {
100
- all[key] = value
101
- }
102
-
103
- return all
104
- }
105
-
106
- return this._requestContextMetaConfigCollection.get(property)
107
- }
108
-
109
- extendProxy (registrationFn) {
110
- this._proxyRegistrationFns.push(registrationFn)
111
- }
112
-
113
- createProxy ({ req, runInSandbox, context, getTopLevelFunctions, safeRequire }) {
114
- const proxyInstance = {}
115
- for (const fn of this._proxyRegistrationFns) {
116
- fn(proxyInstance, req, {
117
- runInSandbox,
118
- context,
119
- getTopLevelFunctions,
120
- safeRequire
121
- })
122
- }
123
- return proxyInstance
124
- }
125
-
126
- render (req, parentReq) {
127
- return this._render(req, parentReq)
128
- }
129
-
130
- async executeMainAction (actionName, data, req) {
131
- await this.beforeMainActionListeners.fire(actionName, data, req)
132
- return this._executeMain(actionName, data, req)
133
- }
134
-
135
- async runInSandbox ({
136
- manager,
137
- context,
138
- userCode,
139
- executionFn,
140
- onRequire,
141
- propertiesConfig,
142
- currentPath,
143
- errorLineNumberOffset
144
- }, req) {
145
- return this._runInSandbox({
146
- manager,
147
- context,
148
- userCode,
149
- executionFn,
150
- onRequire,
151
- propertiesConfig,
152
- currentPath,
153
- errorLineNumberOffset
154
- }, req)
155
- }
156
-
157
- registerWorkerAction (actionName, fn) {
158
- this._workerActions.set(actionName, fn)
159
- }
160
-
161
- async executeWorkerAction (actionName, data, req) {
162
- const action = this._workerActions.get(actionName)
163
- if (!action) {
164
- throw new Error(`Worker action ${actionName} not registered`)
165
- }
166
- return action(data, req)
167
- }
168
-
169
- _registerRenderAction () {
170
- this.registerWorkerAction('render', async (data, req) => {
171
- const res = await this.render(req)
172
-
173
- const sharedBuf = new SharedArrayBuffer(res.content.byteLength)
174
- const buf = Buffer.from(sharedBuf)
175
-
176
- res.content.copy(buf)
177
-
178
- return {
179
- meta: res.meta,
180
- content: buf
181
- }
182
- })
183
- }
184
-
185
- async close () {
186
- this.logger.debug('Closing jsreport worker')
187
- return this.closeListeners.fire()
188
- }
189
- }
190
-
191
- module.exports = WorkerReporter
1
+ const ExtensionsManager = require('./extensionsManager')
2
+ const DocumentStore = require('./documentStore')
3
+ const Templates = require('./templates')
4
+ const createLogger = require('./logger')
5
+ const runInSandbox = require('./sandbox/runInSandbox')
6
+ const createNoneEngine = require('./render/noneEngine')
7
+ const htmlRecipe = require('./render/htmlRecipe')
8
+ const defaultProxyExtend = require('./defaultProxyExtend')
9
+ const Reporter = require('../shared/reporter')
10
+ const BlobStorage = require('./blobStorage.js')
11
+ const Render = require('./render/render')
12
+ const Profiler = require('./render/profiler.js')
13
+
14
+ class WorkerReporter extends Reporter {
15
+ constructor (workerData, executeMain) {
16
+ const { options, documentStore, extensionsDefs } = workerData
17
+
18
+ super(options)
19
+
20
+ this._executeMain = executeMain
21
+ this._initialized = false
22
+ this._documentStoreData = documentStore
23
+ this._requestContextMetaConfigCollection = new Map()
24
+ this._proxyRegistrationFns = []
25
+ this.requestModulesCache = new Map()
26
+ this._workerActions = new Map()
27
+ this._registerRenderAction()
28
+
29
+ this.registerHelpersListeners = this.createListenerCollection('registerHelpers')
30
+ this.afterTemplatingEnginesExecutedListeners = this.createListenerCollection('afterTemplatingEnginesExecuted')
31
+ this.validateRenderListeners = this.createListenerCollection('validateRender')
32
+
33
+ this.extensionsManager = ExtensionsManager(this, extensionsDefs)
34
+
35
+ this.extendProxy((proxy, req) => defaultProxyExtend(this)(proxy, req))
36
+ this.beforeMainActionListeners = this.createListenerCollection('beforeMainAction')
37
+ }
38
+
39
+ async init () {
40
+ if (this._initialized === true) {
41
+ throw new Error('jsreport already initialized. Make sure init is called only once')
42
+ }
43
+
44
+ super.init()
45
+
46
+ Templates(this)
47
+
48
+ this.profiler = Profiler(this)
49
+ this.logger = createLogger(this.profiler)
50
+
51
+ this._render = Render(this)
52
+ await this.extensionsManager.init()
53
+
54
+ this.documentStore = DocumentStore(this._documentStoreData, this.executeMainAction.bind(this))
55
+ this.blobStorage = BlobStorage(this.executeMainAction.bind(this))
56
+
57
+ this.addRequestContextMetaConfig('rootId', { sandboxReadOnly: true })
58
+ this.addRequestContextMetaConfig('id', { sandboxReadOnly: true })
59
+ this.addRequestContextMetaConfig('reportCounter', { sandboxReadOnly: true })
60
+ this.addRequestContextMetaConfig('startTimestamp', { sandboxReadOnly: true })
61
+ this.addRequestContextMetaConfig('logs', { sandboxReadOnly: true })
62
+ this.addRequestContextMetaConfig('isChildRequest', { sandboxReadOnly: true })
63
+ this.addRequestContextMetaConfig('originalInputDataIsEmpty', { sandboxReadOnly: true })
64
+ this.addRequestContextMetaConfig('skipModificationDateUpdate', { sandboxHidden: true })
65
+
66
+ this._runInSandbox = runInSandbox(this)
67
+
68
+ const { compile: compileNone, execute: executeNone } = createNoneEngine()
69
+
70
+ this.extensionsManager.engines.push({
71
+ name: 'none',
72
+ compile: compileNone,
73
+ execute: executeNone
74
+ })
75
+
76
+ this.extensionsManager.recipes.push({
77
+ name: 'html',
78
+ execute: htmlRecipe
79
+ })
80
+
81
+ await this.initializeListeners.fire()
82
+
83
+ this._initialized = true
84
+ }
85
+
86
+ /**
87
+ * @public
88
+ */
89
+ addRequestContextMetaConfig (property, options) {
90
+ this._requestContextMetaConfigCollection.set(property, options)
91
+ }
92
+
93
+ /**
94
+ * @public
95
+ */
96
+ getRequestContextMetaConfig (property) {
97
+ if (property === undefined) {
98
+ const all = {}
99
+
100
+ for (const [key, value] of this._requestContextMetaConfigCollection.entries()) {
101
+ all[key] = value
102
+ }
103
+
104
+ return all
105
+ }
106
+
107
+ return this._requestContextMetaConfigCollection.get(property)
108
+ }
109
+
110
+ extendProxy (registrationFn) {
111
+ this._proxyRegistrationFns.push(registrationFn)
112
+ }
113
+
114
+ createProxy ({ req, runInSandbox, context, getTopLevelFunctions, safeRequire }) {
115
+ const proxyInstance = {}
116
+ for (const fn of this._proxyRegistrationFns) {
117
+ fn(proxyInstance, req, {
118
+ runInSandbox,
119
+ context,
120
+ getTopLevelFunctions,
121
+ safeRequire
122
+ })
123
+ }
124
+ return proxyInstance
125
+ }
126
+
127
+ render (req, parentReq) {
128
+ return this._render(req, parentReq)
129
+ }
130
+
131
+ async executeMainAction (actionName, data, req) {
132
+ await this.beforeMainActionListeners.fire(actionName, data, req)
133
+ return this._executeMain(actionName, data, req)
134
+ }
135
+
136
+ async runInSandbox ({
137
+ manager,
138
+ context,
139
+ userCode,
140
+ executionFn,
141
+ onRequire,
142
+ propertiesConfig,
143
+ currentPath,
144
+ errorLineNumberOffset
145
+ }, req) {
146
+ // we flush before running code in sandbox because it can potentially
147
+ // include code that blocks the whole process (like `while (true) {}`) and we
148
+ // want to ensure that the batched messages are flushed before trying to execute the code
149
+ await this.profiler.flush(req.context.rootId)
150
+
151
+ return this._runInSandbox({
152
+ manager,
153
+ context,
154
+ userCode,
155
+ executionFn,
156
+ onRequire,
157
+ propertiesConfig,
158
+ currentPath,
159
+ errorLineNumberOffset
160
+ }, req)
161
+ }
162
+
163
+ registerWorkerAction (actionName, fn) {
164
+ this._workerActions.set(actionName, fn)
165
+ }
166
+
167
+ async executeWorkerAction (actionName, data, req) {
168
+ const action = this._workerActions.get(actionName)
169
+ if (!action) {
170
+ throw new Error(`Worker action ${actionName} not registered`)
171
+ }
172
+ return action(data, req)
173
+ }
174
+
175
+ _registerRenderAction () {
176
+ this.registerWorkerAction('render', async (data, req) => {
177
+ const res = await this.render(req)
178
+
179
+ const sharedBuf = new SharedArrayBuffer(res.content.byteLength)
180
+ const buf = Buffer.from(sharedBuf)
181
+
182
+ res.content.copy(buf)
183
+
184
+ return {
185
+ meta: res.meta,
186
+ content: buf
187
+ }
188
+ })
189
+ }
190
+
191
+ async close () {
192
+ this.logger.debug('Closing jsreport worker')
193
+ return this.closeListeners.fire()
194
+ }
195
+ }
196
+
197
+ module.exports = WorkerReporter
@@ -1,6 +1,7 @@
1
1
  const LRU = require('lru-cache')
2
- const safeSandbox = require('./safeSandbox')
2
+ const stackTrace = require('stack-trace')
3
3
  const { customAlphabet } = require('nanoid')
4
+ const safeSandbox = require('./safeSandbox')
4
5
  const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
5
6
 
6
7
  module.exports = (reporter) => {
@@ -27,7 +28,7 @@ module.exports = (reporter) => {
27
28
  context.__topLevelFunctions = {}
28
29
  context.__handleError = (err) => handleError(reporter, err)
29
30
 
30
- const { run, restore, contextifyValue, decontextifyValue, unproxyValue, sandbox, safeRequire } = safeSandbox(context, {
31
+ const { sourceFilesInfo, run, restore, contextifyValue, decontextifyValue, unproxyValue, sandbox, safeRequire } = safeSandbox(context, {
31
32
  onLog: (log) => {
32
33
  reporter.logger[log.level](log.message, { ...req, timestamp: log.timestamp })
33
34
  },
@@ -61,7 +62,51 @@ module.exports = (reporter) => {
61
62
  })
62
63
 
63
64
  jsreportProxy = reporter.createProxy({ req, runInSandbox: run, context: sandbox, getTopLevelFunctions, safeRequire })
64
- jsreportProxy.currentPath = currentPath
65
+
66
+ jsreportProxy.currentPath = async () => {
67
+ // we get the current path by throwing an error, which give us a stack trace
68
+ // which we analyze and see if some source file is associated to an entity
69
+ // if it is then we can properly get the path associated to it, if not we
70
+ // fallback to the current path passed as options
71
+ const filesCount = sourceFilesInfo.size
72
+ let resolvedPath = currentPath
73
+
74
+ if (filesCount > 0) {
75
+ const err = new Error('get me stack trace please')
76
+ const trace = stackTrace.parse(err)
77
+
78
+ for (let i = 0; i < trace.length; i++) {
79
+ const current = trace[i]
80
+
81
+ if (sourceFilesInfo.has(current.getFileName())) {
82
+ const { entity, entitySet } = sourceFilesInfo.get(current.getFileName())
83
+
84
+ if (entity != null && entitySet != null) {
85
+ resolvedPath = await reporter.folders.resolveEntityPath(entity, entitySet, req)
86
+ break
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ return resolvedPath
93
+ }
94
+
95
+ jsreportProxy.currentDirectoryPath = async () => {
96
+ const currentPath = await jsreportProxy.currentPath()
97
+
98
+ if (currentPath != null) {
99
+ const localPath = currentPath.substring(0, currentPath.lastIndexOf('/'))
100
+
101
+ if (localPath === '') {
102
+ return '/'
103
+ }
104
+
105
+ return localPath
106
+ }
107
+
108
+ return currentPath
109
+ }
65
110
 
66
111
  // NOTE: it is important that cleanup, restore methods are not called from a function attached to the
67
112
  // sandbox, because the arguments and return value of such function call will be sandboxed again, to solve this
@@ -75,15 +120,22 @@ module.exports = (reporter) => {
75
120
  const functionNames = getTopLevelFunctions(userCode)
76
121
  const functionsCode = `return {${functionNames.map(h => `"${h}": ${h}`).join(',')}}`
77
122
  const executionCode = `;(async () => { ${userCode}; ${functionsCode} })()
78
- .then((topLevelFunctions) => ${executionFnName}({
79
- topLevelFunctions: {
80
- ...topLevelFunctions,
81
- ...__topLevelFunctions
82
- },
83
- require,
84
- console,
85
- context: this
86
- })).catch(__handleError);`
123
+ .then((topLevelFunctions) => {
124
+ const mergedTopLevelFunctions = { ...topLevelFunctions, ...__topLevelFunctions }
125
+
126
+ // expose top level functions to the sandbox context
127
+ // so helpers can call other helpers (from shared asset helpers, or .registerHelpers call from proxy)
128
+ for (const [topLevelFnName, topLevelFn] of Object.entries(mergedTopLevelFunctions)) {
129
+ this[topLevelFnName] = topLevelFn
130
+ }
131
+
132
+ return ${executionFnName}({
133
+ topLevelFunctions: mergedTopLevelFunctions,
134
+ require,
135
+ console,
136
+ context: this
137
+ })
138
+ }).catch(__handleError);`
87
139
 
88
140
  return run(executionCode, {
89
141
  filename: 'sandbox.js',