@jsreport/jsreport-core 4.1.0 → 4.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.
@@ -4,7 +4,6 @@
4
4
  * Reporter main class including all methods jsreport-core exposes.
5
5
  */
6
6
  const path = require('path')
7
- const { Readable } = require('stream')
8
7
  const Reaper = require('@jsreport/reap')
9
8
  const pkg = require('../../package.json')
10
9
  const optionsLoad = require('./optionsLoad')
@@ -27,7 +26,7 @@ const documentStoreActions = require('./store/mainActions')
27
26
  const blobStorageActions = require('./blobStorage/mainActions')
28
27
  const Reporter = require('../shared/reporter')
29
28
  const Request = require('./request')
30
- const generateRequestId = require('../shared/generateRequestId')
29
+ const Response = require('../shared/response')
31
30
  const Profiler = require('./profiler')
32
31
  const semver = require('semver')
33
32
  let reportCounter = 0
@@ -370,7 +369,7 @@ class MainReporter extends Reporter {
370
369
 
371
370
  req = Object.assign({}, req)
372
371
  req.context = Object.assign({}, req.context)
373
- req.context.rootId = req.context.rootId || generateRequestId()
372
+ req.context.rootId = req.context.rootId || this.generateRequestId()
374
373
  req.context.id = req.context.rootId
375
374
  req.context.reportCounter = ++reportCounter
376
375
  req.context.startTimestamp = new Date().getTime()
@@ -379,7 +378,9 @@ class MainReporter extends Reporter {
379
378
  let worker
380
379
  let workerAborted
381
380
  let dontCloseProcessing
382
- const res = { meta: {} }
381
+
382
+ const res = Response(this, req.context.id)
383
+
383
384
  try {
384
385
  await this.beforeRenderWorkerAllocatedListeners.fire(req)
385
386
 
@@ -442,7 +443,8 @@ class MainReporter extends Reporter {
442
443
  worker
443
444
  }, req)
444
445
 
445
- Object.assign(res, responseResult)
446
+ await res.parse(responseResult)
447
+
446
448
  await this.afterRenderListeners.fire(req, res)
447
449
  } catch (err) {
448
450
  await this._handleRenderError(req, res, err).catch((e) => {})
@@ -455,11 +457,11 @@ class MainReporter extends Reporter {
455
457
  })
456
458
 
457
459
  dontCloseProcessing = true
458
- const r = {
459
- ...req.context.clientNotification,
460
- stream: Readable.from(req.context.clientNotification.content)
461
- }
460
+
461
+ const r = req.context.clientNotification
462
+
462
463
  delete req.context.clientNotification
464
+
463
465
  return r
464
466
  }
465
467
 
@@ -472,16 +474,10 @@ class MainReporter extends Reporter {
472
474
  worker
473
475
  }, req)
474
476
 
475
- Object.assign(res, responseResult)
477
+ await res.parse(responseResult)
476
478
 
477
479
  await this.afterRenderListeners.fire(req, res)
478
480
 
479
- if (!res.content) {
480
- this.logger.error('Worker didnt return render res.content, returned:' + JSON.stringify(responseResult), req)
481
- }
482
-
483
- res.stream = Readable.from(res.content)
484
-
485
481
  this._cleanProfileInRequest(req)
486
482
 
487
483
  return res
@@ -551,7 +547,7 @@ class MainReporter extends Reporter {
551
547
  }
552
548
 
553
549
  async executeWorkerAction (actionName, data, options = {}, req) {
554
- req.context.rootId = req.context.rootId || generateRequestId()
550
+ req.context.rootId = req.context.rootId || this.generateRequestId()
555
551
  const timeout = options.timeout || 60000
556
552
 
557
553
  const worker = options.worker
@@ -1,21 +1,30 @@
1
- const extend = require('node.extend.without.arrays')
1
+ const Request = require('../shared/request')
2
2
 
3
3
  module.exports = (obj) => {
4
- const request = Object.create({}, {
5
- __isJsreportRequest__: {
6
- value: true,
7
- writable: false,
8
- configurable: false,
9
- enumerable: false
10
- }
11
- })
12
-
13
- request.template = extend(true, {}, obj.template)
14
-
15
- request.options = extend(true, {}, request.options, obj.options)
16
- request.context = extend(true, {}, request.context, obj.context)
17
- request.context.shared = extend(true, {}, request.context.shared)
18
- request.data = obj.data
4
+ const targetObj = { ...obj }
5
+
6
+ const originalData = targetObj.data
7
+
8
+ delete targetObj.data
9
+
10
+ const customOriginalInputDataIsEmptyResult = {
11
+ defined: false,
12
+ value: null
13
+ }
14
+
15
+ if (targetObj?.context != null && Object.hasOwn(targetObj.context, 'originalInputDataIsEmpty')) {
16
+ customOriginalInputDataIsEmptyResult.defined = true
17
+ customOriginalInputDataIsEmptyResult.value = targetObj.context.originalInputDataIsEmpty
18
+ }
19
+
20
+ const request = Request(targetObj)
21
+ request.data = originalData
22
+
23
+ if (customOriginalInputDataIsEmptyResult.defined) {
24
+ request.context.originalInputDataIsEmpty = customOriginalInputDataIsEmptyResult.value
25
+ } else {
26
+ delete request.context.originalInputDataIsEmpty
27
+ }
19
28
 
20
29
  return request
21
30
  }
@@ -1,6 +1,7 @@
1
1
  const EventEmitter = require('events')
2
2
  const createListenerCollection = require('./listenerCollection')
3
3
  const Request = require('./request')
4
+ const Response = require('./response')
4
5
  const Templates = require('./templates')
5
6
  const Folders = require('./folders')
6
7
  const createOrExtendError = require('./createError')
@@ -8,6 +9,7 @@ const tempFilesHandler = require('./tempFilesHandler')
8
9
  const encryption = require('./encryption')
9
10
  const generateRequestId = require('./generateRequestId')
10
11
  const adminRequest = require('./adminRequest')
12
+ const ReqStorage = require('./reqStorage')
11
13
 
12
14
  class Reporter extends EventEmitter {
13
15
  constructor (options) {
@@ -15,7 +17,9 @@ class Reporter extends EventEmitter {
15
17
 
16
18
  this.options = options || {}
17
19
  this.Request = Request
20
+ this.Response = (...args) => Response(this, ...args)
18
21
  this.adminRequest = adminRequest
22
+ this.reqStorage = ReqStorage(this)
19
23
 
20
24
  // since `reporter` instance will be used for other extensions,
21
25
  // it will quickly reach the limit of `10` listeners,
@@ -82,6 +86,27 @@ class Reporter extends EventEmitter {
82
86
  return tempFilesHandler.ensureTempDirectoryExists(this.options.tempAutoCleanupDirectory)
83
87
  }
84
88
 
89
+ getTempFilePath (filename) {
90
+ if (this.options.tempAutoCleanupDirectory == null) {
91
+ throw new Error('Can not use getTempFilename when tempAutoCleanupDirectory option is not initialized, make sure to initialize jsreport first using jsreport.init()')
92
+ }
93
+
94
+ return tempFilesHandler.getTempFilePath(this.options.tempAutoCleanupDirectory, filename)
95
+ }
96
+
97
+ /**
98
+ * Synchronously reads a file from the jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory)
99
+ *
100
+ * @public
101
+ */
102
+ readTempFileSync (filename, opts) {
103
+ if (this.options.tempAutoCleanupDirectory == null) {
104
+ throw new Error('Can not use readTempFileSync when tempAutoCleanupDirectory option is not initialized, make sure to initialize jsreport first using jsreport.init()')
105
+ }
106
+
107
+ return tempFilesHandler.readTempFileSync(this.options.tempAutoCleanupDirectory, filename, opts)
108
+ }
109
+
85
110
  /**
86
111
  * Reads a file from the jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory)
87
112
  *
@@ -95,6 +120,33 @@ class Reporter extends EventEmitter {
95
120
  return tempFilesHandler.readTempFile(this.options.tempAutoCleanupDirectory, filename, opts)
96
121
  }
97
122
 
123
+ /**
124
+ * Open temp file in jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory)
125
+ *
126
+ * @public
127
+ */
128
+ async openTempFile (filename, flags) {
129
+ if (this.options.tempAutoCleanupDirectory == null) {
130
+ throw new Error('Can not use openTempFile when tempAutoCleanupDirectory option is not initialized, make sure to initialize jsreport first using jsreport.init()')
131
+ }
132
+
133
+ return tempFilesHandler.openTempFile(this.options.tempAutoCleanupDirectory, filename, flags)
134
+ }
135
+
136
+ /**
137
+ * Synchronously creates a file into the jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory)
138
+ * ensuring that the directory always exists
139
+ *
140
+ * @public
141
+ */
142
+ writeTempFileSync (filenameFn, content, opts) {
143
+ if (this.options.tempAutoCleanupDirectory == null) {
144
+ throw new Error('Can not use writeTempFileSync when tempAutoCleanupDirectory option is not initialized, make sure to initialize jsreport first using jsreport.init()')
145
+ }
146
+
147
+ return tempFilesHandler.writeTempFileSync(this.options.tempAutoCleanupDirectory, filenameFn, content, opts)
148
+ }
149
+
98
150
  /**
99
151
  * Creates a file into the jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory)
100
152
  * ensuring that the directory always exists
@@ -114,7 +166,7 @@ class Reporter extends EventEmitter {
114
166
  *
115
167
  * @public
116
168
  */
117
- async readTempFileStream (filename, opts) {
169
+ readTempFileStream (filename, opts) {
118
170
  if (this.options.tempAutoCleanupDirectory == null) {
119
171
  throw new Error('Can not use readTempFileStream when tempAutoCleanupDirectory option is not initialized, make sure to initialize jsreport first using jsreport.init()')
120
172
  }
@@ -136,6 +188,20 @@ class Reporter extends EventEmitter {
136
188
  return tempFilesHandler.writeTempFileStream(this.options.tempAutoCleanupDirectory, filenameFn, opts)
137
189
  }
138
190
 
191
+ /**
192
+ * Copies a file into the jsreport auto-cleanup temp directory (options.tempAutoCleanupDirectory)
193
+ * ensuring that the directory always exists
194
+ *
195
+ * @public
196
+ */
197
+ async copyFileToTempFile (srcFilePath, destFilenameFn, mode) {
198
+ if (this.options.tempAutoCleanupDirectory == null) {
199
+ throw new Error('Can not use copyToTempFile when tempAutoCleanupDirectory option is not initialized, make sure to initialize jsreport first using jsreport.init()')
200
+ }
201
+
202
+ return tempFilesHandler.copyFileToTempFile(this.options.tempAutoCleanupDirectory, srcFilePath, destFilenameFn, mode)
203
+ }
204
+
139
205
  async init () {
140
206
  this.templates = Templates(this)
141
207
  this.folders = Folders(this)
@@ -0,0 +1,20 @@
1
+ module.exports = (reporter) => {
2
+ const runningReqMap = new Map()
3
+
4
+ return {
5
+ set: (key, val, req) => {
6
+ const keyValueMap = runningReqMap.get(req.context.rootId)
7
+ keyValueMap.set(key, val)
8
+ },
9
+ get: (key, req) => {
10
+ const keyValueMap = runningReqMap.get(req.context.rootId)
11
+ return keyValueMap.get(key)
12
+ },
13
+ registerReq: (req) => {
14
+ runningReqMap.set(req.context.rootId, new Map())
15
+ },
16
+ unregisterReq: (req) => {
17
+ runningReqMap.delete(req.context.rootId)
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,231 @@
1
+ const extend = require('node.extend.without.arrays')
2
+ const fs = require('fs/promises')
3
+ const { Readable } = require('stream')
4
+ const { pipeline } = require('stream/promises')
5
+ const path = require('path')
6
+ const isArrayBufferView = require('util').types.isArrayBufferView
7
+
8
+ module.exports = (reporter, requestId, obj) => {
9
+ let outputImpl = new BufferOutput(reporter)
10
+ let cachedStream
11
+
12
+ const response = {
13
+ meta: extend(true, {}, (obj || {}).meta),
14
+
15
+ /** back compatibility methods **/
16
+ get content () {
17
+ return outputImpl.getBufferSync()
18
+ },
19
+
20
+ set content (v) {
21
+ outputImpl.setBufferSync(Buffer.from(v))
22
+ },
23
+
24
+ get stream () {
25
+ if (cachedStream == null) {
26
+ cachedStream = outputImpl.getStream()
27
+ }
28
+
29
+ return cachedStream
30
+ },
31
+ /** //// back compatibility methods **/
32
+
33
+ get isInStreamingMode () {
34
+ return outputImpl instanceof StreamOutput
35
+ },
36
+
37
+ get __isJsreportResponse__ () {
38
+ return true
39
+ },
40
+
41
+ output: {
42
+ async getBuffer () { return outputImpl.getBuffer() },
43
+ async getStream () { return outputImpl.getStream() },
44
+ async getSize () { return outputImpl.getSize() },
45
+ async writeToTempFile (...args) { return outputImpl.writeToTempFile(...args) },
46
+ async update (bufOrStreamOrPath) {
47
+ if (Buffer.isBuffer(bufOrStreamOrPath) || isArrayBufferView(bufOrStreamOrPath)) {
48
+ return outputImpl.setBuffer(bufOrStreamOrPath)
49
+ }
50
+
51
+ if (typeof bufOrStreamOrPath === 'string') {
52
+ if (!path.isAbsolute(bufOrStreamOrPath)) {
53
+ throw new Error('Invalid content passed to res.output.update, when content is string it must be an absolute path')
54
+ }
55
+
56
+ if (outputImpl instanceof BufferOutput) {
57
+ outputImpl = new StreamOutput(reporter, requestId)
58
+ }
59
+
60
+ await reporter.copyFileToTempFile(bufOrStreamOrPath, outputImpl.filePath)
61
+ return
62
+ }
63
+
64
+ if (isReadableStream(bufOrStreamOrPath)) {
65
+ if (outputImpl instanceof BufferOutput) {
66
+ outputImpl = new StreamOutput(reporter, requestId)
67
+ }
68
+
69
+ return outputImpl.setStream(bufOrStreamOrPath)
70
+ }
71
+
72
+ throw new Error('Invalid content passed to res.output.update')
73
+ }
74
+ },
75
+
76
+ serialize () {
77
+ return {
78
+ meta: extend(true, {}, this.meta),
79
+ output: outputImpl.serialize()
80
+ }
81
+ },
82
+
83
+ async parse (res) {
84
+ Object.assign(this.meta, res.meta)
85
+
86
+ if (res.output.type === 'buffer') {
87
+ outputImpl = await BufferOutput.parse(reporter, res.output)
88
+ } else {
89
+ outputImpl = await StreamOutput.parse(reporter, requestId, res.output)
90
+ }
91
+ }
92
+ }
93
+
94
+ return response
95
+ }
96
+
97
+ class BufferOutput {
98
+ constructor (reporter) {
99
+ this.reporter = reporter
100
+ this.buffer = Buffer.from([])
101
+
102
+ this.getBufferSync = this.getBuffer
103
+ this.setBufferSync = this.setBuffer
104
+ }
105
+
106
+ getBuffer () {
107
+ return this.buffer
108
+ }
109
+
110
+ setBuffer (buf) {
111
+ // we need to ensure that the buffer is an actually buffer instance,
112
+ // so when receiving Uint8Array we convert it to a buffer
113
+ this.buffer = Buffer.isBuffer(buf) ? buf : Buffer.from(buf)
114
+ }
115
+
116
+ writeToTempFile (tmpNameFn) {
117
+ return this.reporter.writeTempFile(tmpNameFn, this.buffer)
118
+ }
119
+
120
+ getSize () {
121
+ return this.buffer.length
122
+ }
123
+
124
+ getStream () {
125
+ return Readable.from(this.buffer)
126
+ }
127
+
128
+ serialize () {
129
+ const sharedBuf = new SharedArrayBuffer(this.buffer.byteLength)
130
+ const buf = Buffer.from(sharedBuf)
131
+
132
+ this.buffer.copy(buf)
133
+
134
+ return {
135
+ type: 'buffer',
136
+ content: buf
137
+ }
138
+ }
139
+
140
+ static parse (reporter, output) {
141
+ const instance = new BufferOutput(reporter)
142
+ if (output?.content?.length) {
143
+ instance.setBufferSync(Buffer.from(output?.content))
144
+ }
145
+ return instance
146
+ }
147
+ }
148
+
149
+ class StreamOutput {
150
+ constructor (reporter, requestId) {
151
+ this.reporter = reporter
152
+ this.filename = `response-${requestId}.raw-content`
153
+ const { pathToFile } = this.reporter.getTempFilePath(this.filename)
154
+ this.filePath = pathToFile
155
+ }
156
+
157
+ async getBuffer () {
158
+ const { content } = await this.reporter.readTempFile(this.filename)
159
+ return content
160
+ }
161
+
162
+ setBuffer (buf) {
163
+ return this.reporter.writeTempFile(this.filename, buf)
164
+ }
165
+
166
+ getBufferSync () {
167
+ const { content } = this.reporter.readTempFileSync(this.filename)
168
+ return content
169
+ }
170
+
171
+ setBufferSync (buf) {
172
+ this.reporter.writeTempFileSync(this.filename, buf)
173
+ }
174
+
175
+ writeToTempFile (tmpNameFn) {
176
+ return this.reporter.copyFileToTempFile(this.filePath, tmpNameFn)
177
+ }
178
+
179
+ async getSize () {
180
+ const stat = await fs.stat(this.filePath)
181
+ return stat.size
182
+ }
183
+
184
+ getStream () {
185
+ const reporter = this.reporter
186
+ const filename = this.filename
187
+
188
+ async function * generateResponseContent () {
189
+ const responseFileStream = reporter.readTempFileStream(filename).stream
190
+
191
+ for await (const chunk of responseFileStream) {
192
+ yield chunk
193
+ }
194
+ }
195
+
196
+ // we produce a new Readable stream to avoid exposing the file stream directly
197
+ return Readable.from(generateResponseContent())
198
+ }
199
+
200
+ async setStream (stream) {
201
+ const { stream: responseFileStream } = await this.reporter.writeTempFileStream(this.filename)
202
+ await pipeline(stream, responseFileStream)
203
+ }
204
+
205
+ serialize () {
206
+ return {
207
+ type: 'stream',
208
+ filePath: this.filePath
209
+ }
210
+ }
211
+
212
+ static async parse (reporter, requestId, output) {
213
+ const instance = new StreamOutput(reporter, requestId)
214
+
215
+ if (output.filePath !== instance.filePath) {
216
+ await reporter.copyFileToTempFile(output.filePath, instance.filePath)
217
+ }
218
+ return instance
219
+ }
220
+ }
221
+
222
+ // from https://github.com/sindresorhus/is-stream/blob/main/index.js
223
+ function isReadableStream (stream) {
224
+ return (
225
+ stream !== null &&
226
+ typeof stream === 'object' &&
227
+ typeof stream.pipe === 'function' &&
228
+ stream.readable !== false && typeof stream._read === 'function' &&
229
+ typeof stream._readableState === 'object'
230
+ )
231
+ }
@@ -13,8 +13,34 @@ module.exports.ensureTempDirectoryExists = async function (tempDirectory) {
13
13
  }
14
14
  }
15
15
 
16
+ module.exports.getTempFilePath = getTempFilePath
17
+
18
+ module.exports.readTempFileSync = function readTempFileSync (tempDirectory, filename, opts = {}) {
19
+ const { pathToFile } = getTempFilePath(tempDirectory, filename)
20
+
21
+ const content = fs.readFileSync(pathToFile, opts)
22
+
23
+ return {
24
+ pathToFile,
25
+ filename,
26
+ content
27
+ }
28
+ }
29
+
30
+ module.exports.openTempFile = async function (tempDirectory, filenameFn, flags) {
31
+ const { pathToFile, filename } = getTempFilePath(tempDirectory, filenameFn)
32
+
33
+ const fileHandle = await fsAsync.open(pathToFile, flags)
34
+
35
+ return {
36
+ pathToFile,
37
+ filename,
38
+ fileHandle
39
+ }
40
+ }
41
+
16
42
  module.exports.readTempFile = async function readTempFile (tempDirectory, filename, opts = {}) {
17
- const pathToFile = path.join(tempDirectory, filename)
43
+ const { pathToFile } = getTempFilePath(tempDirectory, filename)
18
44
 
19
45
  const content = await fsAsync.readFile(pathToFile, opts)
20
46
 
@@ -25,51 +51,91 @@ module.exports.readTempFile = async function readTempFile (tempDirectory, filena
25
51
  }
26
52
  }
27
53
 
28
- module.exports.readTempFileStream = async function readTempFileStream (tempDirectory, filename, opts = {}) {
29
- const pathToFile = path.join(tempDirectory, filename)
54
+ module.exports.readTempFileStream = function readTempFileStream (tempDirectory, filename, opts = {}) {
55
+ const { pathToFile } = getTempFilePath(tempDirectory, filename)
30
56
 
31
- return new Promise((resolve) => {
32
- const stream = fs.createReadStream(pathToFile, opts)
57
+ const stream = fs.createReadStream(pathToFile, opts)
33
58
 
34
- resolve({
35
- pathToFile,
36
- filename,
37
- stream
38
- })
39
- })
59
+ return {
60
+ pathToFile,
61
+ filename,
62
+ stream
63
+ }
64
+ }
65
+
66
+ module.exports.writeTempFileSync = function writeTempFileSync (tempDirectory, filenameOrFn, content, opts = {}) {
67
+ return writeFileSync(tempDirectory, filenameOrFn, content, opts)
40
68
  }
41
69
 
42
- module.exports.writeTempFile = async function writeTempFile (tempDirectory, filenameFn, content, opts = {}) {
43
- return writeFile(tempDirectory, filenameFn, content, opts)
70
+ module.exports.writeTempFile = async function writeTempFile (tempDirectory, filenameOrFn, content, opts = {}) {
71
+ return writeFile(tempDirectory, filenameOrFn, content, opts)
44
72
  }
45
73
 
46
- module.exports.writeTempFileStream = async function writeTempFileStream (tempDirectory, filenameFn, opts = {}) {
47
- return writeFile(tempDirectory, filenameFn, undefined, opts, true)
74
+ module.exports.writeTempFileStream = async function writeTempFileStream (tempDirectory, filenameOrFn, opts = {}) {
75
+ return writeFile(tempDirectory, filenameOrFn, undefined, opts, true)
48
76
  }
49
77
 
50
- async function writeFile (tempDirectory, filenameFn, content, opts, asStream = false) {
51
- const filename = filenameFn(uuidv4())
78
+ module.exports.copyFileToTempFile = async function copyFileToTempFile (tempDirectory, srcFilePath, destFilenameOrFn, mode) {
79
+ const { pathToFile, filename } = getTempFilePath(tempDirectory, destFilenameOrFn)
52
80
 
53
- if (filename == null || filename === '') {
54
- throw new Error('No valid filename was returned from filenameFn')
81
+ await fsAsync.mkdir(tempDirectory, {
82
+ recursive: true
83
+ })
84
+
85
+ await fsAsync.copyFile(srcFilePath, pathToFile, mode)
86
+
87
+ return {
88
+ pathToFile,
89
+ filename
90
+ }
91
+ }
92
+
93
+ function getTempFilePath (tempDirectory, filenameOrFn) {
94
+ const filenameResult = typeof filenameOrFn === 'function' ? filenameOrFn(uuidv4()) : filenameOrFn
95
+
96
+ if (filenameResult == null || filenameResult === '') {
97
+ throw new Error('No valid filename')
98
+ }
99
+
100
+ const pathToFile = path.isAbsolute(filenameResult) ? filenameResult : path.join(tempDirectory, filenameResult)
101
+ const filename = path.basename(pathToFile)
102
+
103
+ return {
104
+ pathToFile,
105
+ filename
106
+ }
107
+ }
108
+
109
+ function writeFileSync (tempDirectory, filenameOrFn, content, opts) {
110
+ const { pathToFile, filename } = getTempFilePath(tempDirectory, filenameOrFn)
111
+
112
+ fs.mkdirSync(tempDirectory, {
113
+ recursive: true
114
+ })
115
+
116
+ fs.writeFileSync(pathToFile, content, opts)
117
+
118
+ return {
119
+ pathToFile,
120
+ filename
55
121
  }
122
+ }
56
123
 
57
- const pathToFile = path.join(tempDirectory, filename)
124
+ async function writeFile (tempDirectory, filenameOrFn, content, opts, asStream = false) {
125
+ const { pathToFile, filename } = getTempFilePath(tempDirectory, filenameOrFn)
58
126
 
59
127
  await fsAsync.mkdir(tempDirectory, {
60
128
  recursive: true
61
129
  })
62
130
 
63
131
  if (asStream === true) {
64
- return new Promise((resolve) => {
65
- const stream = fs.createWriteStream(pathToFile, opts)
66
-
67
- resolve({
68
- pathToFile,
69
- filename,
70
- stream
71
- })
72
- })
132
+ const stream = fs.createWriteStream(pathToFile, opts)
133
+
134
+ return {
135
+ pathToFile,
136
+ filename,
137
+ stream
138
+ }
73
139
  } else {
74
140
  await fsAsync.writeFile(pathToFile, content, opts)
75
141
 
@@ -12,10 +12,8 @@ module.exports = (reporter) => (proxy, req) => {
12
12
  context: {}
13
13
  }, req)
14
14
 
15
- return {
16
- content: res.content,
17
- meta: res.meta
18
- }
15
+ // expose the response api
16
+ return res
19
17
  }
20
18
 
21
19
  proxy.documentStore = {