@jsreport/jsreport-core 4.2.2 → 4.3.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 CHANGED
@@ -256,7 +256,7 @@ jsreport currently support these main listeners
256
256
  - `closeListeners()` called when jsreport is about to be closed, you will usually put here some code that clean up some resource
257
257
 
258
258
  ## Studio
259
- jsreport includes also visual html studio and rest API. This is provided through two extensions, [@jsreport/jsreport-express](https://github.com/jsreport/jsreport/tree/master/packages/jsreport-express) extension to have a web server available and [@jsreport/jsreport-studio](https://github.com/jsreport/jsreport/tree/master/packages/jsreport-studio) for the web UI, both extensions should be installed in order to have the studio ready. See the documentation of each extension for the details.
259
+ jsreport includes also visual html studio and rest API. This is provided through two extensions, [@jsreport/jsreport-express](https://github.com/jsreport/jsreport/tree/master/packages/jsreport-express) extension to have a web server available and [@jsreport/jsreport-studio](https://github.com/jsreport/jsreport/tree/master/packages/jsreport-studio) for the web UI, both extensions should be installed in order to have the studio ready. See the documentation of each extension for the details
260
260
 
261
261
  ## Template store
262
262
  `jsreport-core` includes API for persisting and accessing report templates. This API is then used by extensions mainly in combination with jsreport [studio](#studio). `jsreport-core` implements just in-memory persistence, but you can add other persistence methods through extensions, see the [template stores](https://jsreport.net/learn/template-stores) docummentation
@@ -282,11 +282,17 @@ jsreport.documentStore.collection('templates')
282
282
 
283
283
  ## Changelog
284
284
 
285
+ ### 4.3.0
286
+
287
+ - expose safe properties of `req.context.user` in sandbox
288
+ - fix component execution when wrapped with async helper
289
+ - fix jsdom require in sandbox
290
+
285
291
  ### 4.2.2
286
292
 
287
293
  - fix recursive component rendering
288
294
 
289
- ### 4.2.1
295
+ ### 4.2.1
290
296
 
291
297
  - response.output.update now accepts Web ReadableStream (which is what new version of puppeteer returns)
292
298
 
@@ -259,16 +259,29 @@ module.exports = (reporter) => {
259
259
  const resolvedResultsMap = new Map()
260
260
 
261
261
  // we need to use the cloned map, because there can be a waitForAsyncHelper pending that needs the asyncResultMap values
262
- const clonedMap = new Map(asyncResultMap)
262
+ let clonedMap = new Map(asyncResultMap)
263
+
263
264
  while (clonedMap.size > 0) {
264
- await Promise.all([...clonedMap.keys()].map(async (k) => {
265
+ const keysEvaluated = [...clonedMap.keys()]
266
+
267
+ await Promise.all(keysEvaluated.map(async (k) => {
265
268
  const result = await clonedMap.get(k)
266
269
  asyncCallChainSet.delete(k)
267
270
  resolvedResultsMap.set(k, `${result}`)
268
271
  clonedMap.delete(k)
269
272
  }))
273
+
274
+ // we need to remove the keys processed from the original map at this point
275
+ // (after the await) because during the async work the asyncResultMap will be read
276
+ for (const k of keysEvaluated) {
277
+ asyncResultMap.delete(k)
278
+ }
279
+
280
+ // we want to process the new generated pending async results
281
+ if (asyncResultMap.size > 0) {
282
+ clonedMap = new Map(asyncResultMap)
283
+ }
270
284
  }
271
- asyncResultMap.clear()
272
285
 
273
286
  while (contentResult.includes('{#asyncHelperResult')) {
274
287
  contentResult = contentResult.replace(/{#asyncHelperResult ([^{}]+)}/g, (str, p1) => {
@@ -284,6 +297,7 @@ module.exports = (reporter) => {
284
297
  return `${resolvedResultsMap.get(asyncResultId)}`
285
298
  })
286
299
  }
300
+
287
301
  contentResult = contentResult.replace(/asyncUnresolvedHelperResult/g, 'asyncHelperResult')
288
302
 
289
303
  await executionFinishListenersMap.get(executionId).fire()
@@ -253,6 +253,23 @@ function applyPropertiesConfig (context, hierarchyPropertiesConfig, {
253
253
  shouldStoreOriginal = false
254
254
  }
255
255
 
256
+ // allow configuring parent as hidden, but child props as readonly so we expose
257
+ // just part of the parent
258
+ // const ignoreParentHidden = false
259
+ const ignoreParentHidden = (
260
+ isHidden &&
261
+ isGrouped &&
262
+ (hierarchyPropertiesConfig.inner != null || hierarchyPropertiesConfig.standalone != null)
263
+ )
264
+
265
+ if (parentOpts?.originalSandboxHidden !== true && ignoreParentHidden) {
266
+ // we store the original value for the top parent with hidden value
267
+ shouldStoreOriginal = true
268
+ } else if (parentOpts && parentOpts.originalSandboxHidden === true) {
269
+ // we don't store original value when parent was configured as hidden
270
+ shouldStoreOriginal = false
271
+ }
272
+
256
273
  // saving original value
257
274
  if (shouldStoreOriginal) {
258
275
  let exists = true
@@ -284,6 +301,19 @@ function applyPropertiesConfig (context, hierarchyPropertiesConfig, {
284
301
  Object.keys(c.standalone).forEach((standaloneKey) => {
285
302
  const standaloneConfig = c.standalone[standaloneKey]
286
303
 
304
+ const newParentOpts = {
305
+ sandboxHidden: ignoreParentHidden ? false : isHidden,
306
+ sandboxReadOnly: isReadOnly
307
+ }
308
+
309
+ // set and inherit originalSandboxHidden if needed
310
+ if (
311
+ parentOpts?.originalSandboxHidden === true ||
312
+ (ignoreParentHidden && isHidden)
313
+ ) {
314
+ newParentOpts.originalSandboxHidden = true
315
+ }
316
+
287
317
  applyPropertiesConfig(context, standaloneConfig, {
288
318
  original,
289
319
  customProxies,
@@ -291,7 +321,7 @@ function applyPropertiesConfig (context, hierarchyPropertiesConfig, {
291
321
  isRoot: false,
292
322
  isGrouped: false,
293
323
  onlyReadOnlyTopLevel,
294
- parentOpts: { sandboxHidden: isHidden, sandboxReadOnly: isReadOnly }
324
+ parentOpts: newParentOpts
295
325
  }, readOnlyConfigured)
296
326
  })
297
327
  }
@@ -300,19 +330,65 @@ function applyPropertiesConfig (context, hierarchyPropertiesConfig, {
300
330
  Object.keys(c.inner).forEach((innerKey) => {
301
331
  const innerConfig = c.inner[innerKey]
302
332
 
333
+ const newParentOpts = {
334
+ sandboxHidden: ignoreParentHidden ? false : isHidden,
335
+ sandboxReadOnly: isReadOnly
336
+ }
337
+
338
+ // set and inherit originalSandboxHidden if needed
339
+ if (
340
+ parentOpts?.originalSandboxHidden === true ||
341
+ (ignoreParentHidden && isHidden)
342
+ ) {
343
+ newParentOpts.originalSandboxHidden = true
344
+ }
345
+
303
346
  applyPropertiesConfig(context, innerConfig, {
304
347
  original,
305
348
  customProxies,
306
349
  prop: innerKey,
307
350
  isRoot: false,
308
351
  isGrouped: true,
309
- parentOpts: { sandboxHidden: isHidden, sandboxReadOnly: isReadOnly }
352
+ parentOpts: newParentOpts
310
353
  }, readOnlyConfigured)
311
354
  })
312
355
  }
313
356
 
314
357
  if (isHidden) {
315
- omitProp(context, prop)
358
+ if (ignoreParentHidden) {
359
+ // when parent is hidden but there are configuration for child properties we just copy
360
+ // the properties listed there, we do this because we want to work with just the configured
361
+ // properties, the other properties should not be exposed
362
+ const currentValue = get(context, prop)
363
+
364
+ if (currentValue != null && typeof currentValue === 'object') {
365
+ let newValue
366
+
367
+ if (Array.isArray(currentValue)) {
368
+ newValue = []
369
+ } else {
370
+ newValue = {}
371
+ }
372
+
373
+ for (const config of [hierarchyPropertiesConfig.standalone, hierarchyPropertiesConfig.inner]) {
374
+ if (config == null) {
375
+ continue
376
+ }
377
+
378
+ for (const childProp of Object.keys(config)) {
379
+ if (hasOwn(context, childProp)) {
380
+ const childValue = get(context, childProp)
381
+ const targetProp = childProp.replace(`${prop}.`, '')
382
+ set(newValue, targetProp, childValue)
383
+ }
384
+ }
385
+ }
386
+
387
+ set(context, prop, newValue)
388
+ }
389
+ } else {
390
+ omitProp(context, prop)
391
+ }
316
392
  } else if (isReadOnly) {
317
393
  readOnlyProp(context, prop, readOnlyConfigured, customProxies, {
318
394
  onlyTopLevel: false,
@@ -38,7 +38,7 @@ module.exports = function createSandboxRequire (safeExecution, isolateModules, m
38
38
 
39
39
  if (isolateModules) {
40
40
  const requireExtensions = Object.create(null)
41
- isolatedRequire.setDefaultRequireExtensions(requireExtensions, modulesCache, compileScript)
41
+ isolatedRequire.setDefaultRequireExtensions(requireExtensions, requireFromRootDirectory, compileScript)
42
42
  modulesMeta.requireExtensions = requireExtensions
43
43
  }
44
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsreport/jsreport-core",
3
- "version": "4.2.2",
3
+ "version": "4.3.0",
4
4
  "description": "javascript based business reporting",
5
5
  "keywords": [
6
6
  "report",
@@ -78,6 +78,7 @@
78
78
  },
79
79
  "devDependencies": {
80
80
  "@node-rs/jsonwebtoken": "0.2.0",
81
+ "jsdom": "17.0.0",
81
82
  "mocha": "10.1.0",
82
83
  "should": "13.2.3",
83
84
  "standard": "16.0.4",