@jsreport/jsreport-core 4.3.1 → 4.4.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 +6 -0
- package/lib/main/profiler.js +4 -0
- package/lib/main/reporter.js +6 -1
- package/lib/worker/render/executeEngine.js +123 -118
- package/lib/worker/reporter.js +6 -1
- package/lib/worker/sandbox/isolatedRequire.js +25 -17
- package/lib/worker/sandbox/requireSandbox.js +16 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -282,6 +282,12 @@ jsreport.documentStore.collection('templates')
|
|
|
282
282
|
|
|
283
283
|
## Changelog
|
|
284
284
|
|
|
285
|
+
### 4.4.0
|
|
286
|
+
|
|
287
|
+
- trigger async report using header to avoid allocating worker
|
|
288
|
+
- fix require('..') and require('.') in sandbox
|
|
289
|
+
- fix component rendering in loop with async work on helpers
|
|
290
|
+
|
|
285
291
|
### 4.3.1
|
|
286
292
|
|
|
287
293
|
- fix `waitForAsyncHelper`, `waitForAsyncHelpers` not working with trustUserCode: true
|
package/lib/main/profiler.js
CHANGED
|
@@ -159,6 +159,10 @@ module.exports = (reporter) => {
|
|
|
159
159
|
reporter.beforeRenderWorkerAllocatedListeners.add('profiler', async (req) => {
|
|
160
160
|
req.context.profiling = req.context.profiling || {}
|
|
161
161
|
|
|
162
|
+
if (req.context.profiling.enabled === false) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
162
166
|
if (req.context.profiling.mode == null) {
|
|
163
167
|
const profilerSettings = await reporter.settings.findValue('profiler', req)
|
|
164
168
|
const defaultMode = reporter.options.profiler.defaultMode || 'standard'
|
package/lib/main/reporter.js
CHANGED
|
@@ -382,7 +382,12 @@ class MainReporter extends Reporter {
|
|
|
382
382
|
const res = Response(this, req.context.id)
|
|
383
383
|
|
|
384
384
|
try {
|
|
385
|
-
await this.beforeRenderWorkerAllocatedListeners.fire(req)
|
|
385
|
+
await this.beforeRenderWorkerAllocatedListeners.fire(req, res)
|
|
386
|
+
if (req.context.clientNotification) {
|
|
387
|
+
const r = req.context.clientNotification
|
|
388
|
+
delete req.context.clientNotification
|
|
389
|
+
return r
|
|
390
|
+
}
|
|
386
391
|
|
|
387
392
|
worker = await this._workersManager.allocate(req, {
|
|
388
393
|
timeout: this.getReportTimeout(req)
|
|
@@ -229,98 +229,103 @@ module.exports = (reporter) => {
|
|
|
229
229
|
executionFinishListenersMap.set(executionId, reporter.createListenerCollection())
|
|
230
230
|
executionFnParsedParamsMap.get(req.context.id).get(executionFnParsedParamsKey).resolve({ require, console, topLevelFunctions, context })
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
e
|
|
241
|
-
|
|
232
|
+
try {
|
|
233
|
+
const key = engine.buildTemplateCacheKey
|
|
234
|
+
? engine.buildTemplateCacheKey({ content }, req)
|
|
235
|
+
: `template:${content}:${engine.name}`
|
|
236
|
+
|
|
237
|
+
if (!templatesCache.has(key)) {
|
|
238
|
+
try {
|
|
239
|
+
templatesCache.set(key, engine.compile(content, { require }))
|
|
240
|
+
} catch (e) {
|
|
241
|
+
e.property = 'content'
|
|
242
|
+
throw e
|
|
243
|
+
}
|
|
242
244
|
}
|
|
243
|
-
}
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
const compiledTemplate = templatesCache.get(key)
|
|
247
|
+
const wrappedTopLevelFunctions = {}
|
|
247
248
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
for (const h of Object.keys(topLevelFunctions)) {
|
|
250
|
+
// extra wrapping for enhance the error with the helper name
|
|
251
|
+
wrappedTopLevelFunctions[h] = wrapHelperForHelperNameWhenError(topLevelFunctions[h], h, () => executionFnParsedParamsMap.has(req.context.id))
|
|
251
252
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
253
|
+
if (engine.getWrappingHelpersEnabled && engine.getWrappingHelpersEnabled(req) === false) {
|
|
254
|
+
wrappedTopLevelFunctions[h] = engine.wrapHelper(wrappedTopLevelFunctions[h], { context })
|
|
255
|
+
} else {
|
|
256
|
+
wrappedTopLevelFunctions[h] = wrapHelperForAsyncSupport(wrappedTopLevelFunctions[h], asyncResultMap, asyncCallChainSet)
|
|
257
|
+
}
|
|
256
258
|
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
let contentResult = await engine.execute(compiledTemplate, wrappedTopLevelFunctions, data, { require })
|
|
260
259
|
|
|
261
|
-
|
|
260
|
+
let contentResult = await engine.execute(compiledTemplate, wrappedTopLevelFunctions, data, { require })
|
|
262
261
|
|
|
263
|
-
|
|
264
|
-
let clonedMap = new Map(asyncResultMap)
|
|
262
|
+
const resolvedResultsMap = new Map()
|
|
265
263
|
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
// we need to use the cloned map, because there can be a waitForAsyncHelper pending that needs the asyncResultMap values
|
|
265
|
+
let clonedMap = new Map(asyncResultMap)
|
|
268
266
|
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
asyncCallChainSet.delete(k)
|
|
272
|
-
resolvedResultsMap.set(k, `${result}`)
|
|
273
|
-
clonedMap.delete(k)
|
|
274
|
-
}))
|
|
267
|
+
while (clonedMap.size > 0) {
|
|
268
|
+
const keysEvaluated = [...clonedMap.keys()]
|
|
275
269
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
270
|
+
await Promise.all(keysEvaluated.map(async (k) => {
|
|
271
|
+
const result = await clonedMap.get(k)
|
|
272
|
+
asyncCallChainSet.delete(k)
|
|
273
|
+
resolvedResultsMap.set(k, `${result}`)
|
|
274
|
+
clonedMap.delete(k)
|
|
275
|
+
}))
|
|
281
276
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
277
|
+
// we need to remove the keys processed from the original map at this point
|
|
278
|
+
// (after the await) because during the async work the asyncResultMap will be read
|
|
279
|
+
for (const k of keysEvaluated) {
|
|
280
|
+
asyncResultMap.delete(k)
|
|
281
|
+
}
|
|
287
282
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
// this can happen if a child jsreport.templatingEngines.evaluate receives an async value from outer scope
|
|
292
|
-
// because every evaluate uses a unique map of async results
|
|
293
|
-
// example is the case when component receives as a value async thing
|
|
294
|
-
// instead of returning "undefined" we let the outer eval to do the replace
|
|
295
|
-
if (!resolvedResultsMap.has(asyncResultId)) {
|
|
296
|
-
// returning asyncUnresolvedHelperResult just to avoid endless loop, after replace we put it back to asyncHelperResult
|
|
297
|
-
return `{#asyncUnresolvedHelperResult ${asyncResultId}}`
|
|
283
|
+
// we want to process the new generated pending async results
|
|
284
|
+
if (asyncResultMap.size > 0) {
|
|
285
|
+
clonedMap = new Map(asyncResultMap)
|
|
298
286
|
}
|
|
299
|
-
|
|
300
|
-
})
|
|
301
|
-
}
|
|
287
|
+
}
|
|
302
288
|
|
|
303
|
-
|
|
289
|
+
while (contentResult.includes('{#asyncHelperResult')) {
|
|
290
|
+
contentResult = contentResult.replace(/{#asyncHelperResult ([^{}]+)}/g, (str, p1) => {
|
|
291
|
+
const asyncResultId = p1
|
|
292
|
+
// this can happen if a child jsreport.templatingEngines.evaluate receives an async value from outer scope
|
|
293
|
+
// because every evaluate uses a unique map of async results
|
|
294
|
+
// example is the case when component receives as a value async thing
|
|
295
|
+
// instead of returning "undefined" we let the outer eval to do the replace
|
|
296
|
+
if (!resolvedResultsMap.has(asyncResultId)) {
|
|
297
|
+
// returning asyncUnresolvedHelperResult just to avoid endless loop, after replace we put it back to asyncHelperResult
|
|
298
|
+
return `{#asyncUnresolvedHelperResult ${asyncResultId}}`
|
|
299
|
+
}
|
|
300
|
+
return `${resolvedResultsMap.get(asyncResultId)}`
|
|
301
|
+
})
|
|
302
|
+
}
|
|
304
303
|
|
|
305
|
-
|
|
304
|
+
contentResult = contentResult.replace(/asyncUnresolvedHelperResult/g, 'asyncHelperResult')
|
|
306
305
|
|
|
307
|
-
|
|
306
|
+
await executionFinishListenersMap.get(executionId).fire()
|
|
308
307
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
308
|
+
return {
|
|
309
|
+
// handlebars escapes single brackets before execution to prevent errors on {#asset}
|
|
310
|
+
// we need to unescape them later here, because at the moment the engine.execute finishes
|
|
311
|
+
// the async helpers aren't executed yet
|
|
312
|
+
content: engine.unescape ? engine.unescape(contentResult) : contentResult
|
|
313
|
+
}
|
|
314
|
+
} finally {
|
|
315
|
+
// ensure we clean the execution from the chain always, even on errors
|
|
316
|
+
contextExecutionChainMap.set(sandboxId, contextExecutionChainMap.get(sandboxId).filter((id) => id !== executionId))
|
|
314
317
|
}
|
|
315
318
|
}
|
|
316
319
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
320
|
+
try {
|
|
321
|
+
// executionFnParsedParamsMap is there to cache parsed components helpers to speed up longer loops
|
|
322
|
+
// we store there for the particular request and component a promise and only the first component gets compiled
|
|
323
|
+
if (executionFnParsedParamsMap.get(req.context.id).has(executionFnParsedParamsKey)) {
|
|
324
|
+
const { require, console, topLevelFunctions, context } = await (executionFnParsedParamsMap.get(req.context.id).get(executionFnParsedParamsKey).promise)
|
|
325
|
+
|
|
326
|
+
return await executionFn({ require, console, topLevelFunctions, context })
|
|
327
|
+
}
|
|
321
328
|
|
|
322
|
-
return executionFn({ require, console, topLevelFunctions, context })
|
|
323
|
-
} else {
|
|
324
329
|
const awaiter = {}
|
|
325
330
|
|
|
326
331
|
awaiter.promise = new Promise((resolve) => {
|
|
@@ -328,69 +333,69 @@ module.exports = (reporter) => {
|
|
|
328
333
|
})
|
|
329
334
|
|
|
330
335
|
executionFnParsedParamsMap.get(req.context.id).set(executionFnParsedParamsKey, awaiter)
|
|
331
|
-
}
|
|
332
336
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
337
|
+
if (reporter.options.sandbox.cache && reporter.options.sandbox.cache.enabled === false) {
|
|
338
|
+
templatesCache.reset()
|
|
339
|
+
}
|
|
336
340
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
341
|
+
try {
|
|
342
|
+
return await reporter.runInSandbox({
|
|
343
|
+
context: {
|
|
344
|
+
...(engine.createContext ? engine.createContext(req) : {})
|
|
345
|
+
},
|
|
346
|
+
userCode: normalizedHelpers,
|
|
347
|
+
initFn,
|
|
348
|
+
executionFn,
|
|
349
|
+
currentPath: entityPath,
|
|
350
|
+
onRequire: (moduleName, { context }) => {
|
|
351
|
+
if (engine.onRequire) {
|
|
352
|
+
return engine.onRequire(moduleName, { context })
|
|
353
|
+
}
|
|
349
354
|
}
|
|
355
|
+
}, req)
|
|
356
|
+
} catch (e) {
|
|
357
|
+
if (!handleErrors) {
|
|
358
|
+
throw e
|
|
350
359
|
}
|
|
351
|
-
}, req)
|
|
352
|
-
} catch (e) {
|
|
353
|
-
if (!handleErrors) {
|
|
354
|
-
throw e
|
|
355
|
-
}
|
|
356
360
|
|
|
357
|
-
|
|
361
|
+
const nestedErrorWithEntity = e.entity != null
|
|
358
362
|
|
|
359
|
-
|
|
363
|
+
const templatePath = req.template._id ? await reporter.folders.resolveEntityPath(req.template, 'templates', req) : 'anonymous'
|
|
360
364
|
|
|
361
|
-
|
|
365
|
+
const newError = reporter.createError(`Error when evaluating engine ${engine.name} for template ${templatePath}`, { original: e })
|
|
362
366
|
|
|
363
|
-
|
|
364
|
-
|
|
367
|
+
if (templatePath !== 'anonymous' && !nestedErrorWithEntity) {
|
|
368
|
+
const templateFound = await reporter.folders.resolveEntityFromPath(templatePath, 'templates', req)
|
|
365
369
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
370
|
+
if (templateFound != null) {
|
|
371
|
+
newError.entity = {
|
|
372
|
+
shortid: templateFound.entity.shortid,
|
|
373
|
+
name: templateFound.entity.name,
|
|
374
|
+
content
|
|
375
|
+
}
|
|
371
376
|
}
|
|
372
377
|
}
|
|
373
|
-
}
|
|
374
378
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
379
|
+
if (!nestedErrorWithEntity && e.property !== 'content') {
|
|
380
|
+
newError.property = 'helpers'
|
|
381
|
+
}
|
|
378
382
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
+
if (nestedErrorWithEntity) {
|
|
384
|
+
// errors from nested assets evals needs an unwrap for some reason
|
|
385
|
+
newError.entity = { ...e.entity }
|
|
386
|
+
}
|
|
383
387
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
// we remove the decoratedSuffix (created from sandbox) from the stack trace (if it is there) because it
|
|
389
|
+
// just creates noise and duplication when printing the error,
|
|
390
|
+
// we just want the decoration on the message not in the stack trace
|
|
391
|
+
if (e.decoratedSuffix != null && newError.stack.includes(e.decoratedSuffix)) {
|
|
392
|
+
newError.stack = newError.stack.replace(e.decoratedSuffix, '')
|
|
393
|
+
}
|
|
390
394
|
|
|
391
|
-
|
|
395
|
+
throw newError
|
|
396
|
+
}
|
|
392
397
|
} finally {
|
|
393
|
-
if (sandboxId != null) {
|
|
398
|
+
if (sandboxId != null && contextExecutionChainMap.get(sandboxId)?.length === 0) {
|
|
394
399
|
contextExecutionChainMap.delete(sandboxId)
|
|
395
400
|
}
|
|
396
401
|
}
|
package/lib/worker/reporter.js
CHANGED
|
@@ -88,7 +88,7 @@ class WorkerReporter extends Reporter {
|
|
|
88
88
|
require('@jsreport/ses')
|
|
89
89
|
|
|
90
90
|
// eslint-disable-next-line
|
|
91
|
-
|
|
91
|
+
repairIntrinsics({
|
|
92
92
|
// don't change locale based methods which users may be using in their templates
|
|
93
93
|
localeTaming: 'unsafe',
|
|
94
94
|
errorTaming: 'unsafe',
|
|
@@ -108,6 +108,8 @@ class WorkerReporter extends Reporter {
|
|
|
108
108
|
overrideTaming: 'severe'
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
+
// NOTE: we need to add these overrides between repairIntrinsics and hardenIntrinsics
|
|
112
|
+
// for them to be valid.
|
|
111
113
|
// in this mode we alias the unsafe methods to safe ones
|
|
112
114
|
Buffer.allocUnsafe = function allocUnsafe (size) {
|
|
113
115
|
return Buffer.alloc(size)
|
|
@@ -117,6 +119,9 @@ class WorkerReporter extends Reporter {
|
|
|
117
119
|
return Buffer.alloc(size)
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
// eslint-disable-next-line
|
|
123
|
+
hardenIntrinsics()
|
|
124
|
+
|
|
120
125
|
// we also harden Buffer because we expose it to sandbox
|
|
121
126
|
// eslint-disable-next-line
|
|
122
127
|
harden(Buffer)
|
|
@@ -12,7 +12,7 @@ const ISOLATED_PACKAGE_JSON_CACHE = new Map()
|
|
|
12
12
|
// to bring isolated modules across renders and without memory leaks.
|
|
13
13
|
// most of the code is copied from node.js source code and adapted a bit
|
|
14
14
|
// (you will see in some parts specific links to node.js source code counterpart for reference)
|
|
15
|
-
function isolatedRequire (_moduleId, modulesMeta,
|
|
15
|
+
function isolatedRequire (_moduleId, modulesMeta, resolveModule) {
|
|
16
16
|
const parentModule = typeof _moduleId !== 'string' ? _moduleId.parent : null
|
|
17
17
|
const moduleId = parentModule ? _moduleId.moduleId : _moduleId
|
|
18
18
|
|
|
@@ -32,10 +32,14 @@ function isolatedRequire (_moduleId, modulesMeta, requireFromRootDirectory) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const { rootModule, modulesCache, requireExtensions } = modulesMeta
|
|
35
|
-
|
|
36
|
-
const fullModulePath = resolveFilename(ISOLATED_REQUIRE_RESOLVE_CACHE, requireFromRootDirectory.resolve, moduleId, { parentModulePath: parentModule?.path })
|
|
35
|
+
const fullModulePath = resolveFilename(ISOLATED_REQUIRE_RESOLVE_CACHE, resolveModule, moduleId, { parentModulePath: parentModule?.path })
|
|
37
36
|
|
|
38
37
|
if (modulesCache[fullModulePath]) {
|
|
38
|
+
// if module was already tried to be loaded and ended with error we rethrow the error
|
|
39
|
+
if (modulesCache[fullModulePath].loadingError != null) {
|
|
40
|
+
throw modulesCache[fullModulePath].loadingError
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
return modulesCache[fullModulePath].exports
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -50,29 +54,33 @@ function isolatedRequire (_moduleId, modulesMeta, requireFromRootDirectory) {
|
|
|
50
54
|
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1133
|
|
51
55
|
// we can not add this to the IsolatedModule.prototype because we need access to other variables
|
|
52
56
|
mod.require = function (id) {
|
|
53
|
-
return isolatedRequire({ parent: this, moduleId: id }, modulesMeta,
|
|
57
|
+
return isolatedRequire({ parent: this, moduleId: id }, modulesMeta, resolveModule)
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
modulesCache[fullModulePath] = mod
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
try {
|
|
63
|
+
mod.filename = fullModulePath
|
|
64
|
+
mod.paths = Module._nodeModulePaths(path.dirname(fullModulePath))
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
const extension = findLongestRegisteredExtension(fullModulePath, requireExtensions)
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const moduleResolver = requireExtensions[extension]
|
|
68
|
+
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L1113
|
|
69
|
+
// allow .mjs to be overridden
|
|
70
|
+
if (fullModulePath.endsWith('.mjs') && !requireExtensions['.mjs']) {
|
|
71
|
+
throw createRequireESMError(fullModulePath)
|
|
72
|
+
}
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
const moduleResolver = requireExtensions[extension]
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
moduleResolver(mod, fullModulePath)
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
mod.loaded = true
|
|
79
|
+
return mod.exports
|
|
80
|
+
} catch (error) {
|
|
81
|
+
mod.loadingError = error
|
|
82
|
+
throw error
|
|
83
|
+
}
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
function setDefaultRequireExtensions (currentExtensions, requireFromRootDirectory, compileScript) {
|
|
@@ -80,11 +80,24 @@ function doRequire (moduleId, requireFromRootDirectory, _requirePaths, modulesMe
|
|
|
80
80
|
const _require = isolateModules ? isolatedRequire : requireFromRootDirectory
|
|
81
81
|
const extraRequireParams = []
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
const resolveModule = (mId, ...args) => {
|
|
84
|
+
let normalizedModuleId = mId
|
|
85
|
+
|
|
86
|
+
if (normalizedModuleId === '..' || normalizedModuleId === '.') {
|
|
87
|
+
// NOTE: we need to manually normalize because node has a bug
|
|
88
|
+
// https://github.com/nodejs/node/issues/47000
|
|
89
|
+
// when using require.resolve and using options.paths, it does not recognize for "..", "."
|
|
90
|
+
// to be relative, just other cases work like "../", "..\\",
|
|
91
|
+
// so when we detect this case we normalize it in order for node to resolve correctly
|
|
92
|
+
normalizedModuleId = normalizedModuleId + path.sep
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return requireFromRootDirectory.resolve(normalizedModuleId, ...args)
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
|
|
98
|
+
if (isolateModules) {
|
|
99
|
+
extraRequireParams.push(modulesMeta, resolveModule)
|
|
100
|
+
}
|
|
88
101
|
|
|
89
102
|
let result = executeRequire(_require, resolveModule, moduleId, searchedPaths, ...extraRequireParams)
|
|
90
103
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@jsreport/mingo": "2.4.1",
|
|
45
45
|
"@jsreport/reap": "0.1.0",
|
|
46
46
|
"@jsreport/serializator": "1.0.0",
|
|
47
|
-
"@jsreport/ses": "1.0
|
|
47
|
+
"@jsreport/ses": "1.1.0",
|
|
48
48
|
"ajv": "6.12.6",
|
|
49
49
|
"app-root-path": "3.0.0",
|
|
50
50
|
"bytes": "3.1.2",
|