@engine9-io/input-tools 1.9.11 → 2.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.
package/file/tools.js CHANGED
@@ -1,33 +1,33 @@
1
- const fs = require('node:fs');
2
-
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import debug$0 from "debug";
4
+ import os from "node:os";
5
+ import mkdirp$0 from "mkdirp";
6
+ import nodestream from "node:stream";
7
+ import JSON5 from "json5";
8
+ import unzipper from "unzipper";
9
+ import dayjs from "dayjs";
10
+ import clientS3 from "@aws-sdk/client-s3";
11
+ import uuid from "uuid";
3
12
  const fsp = fs.promises;
4
- const path = require('node:path');
5
- const debug = require('debug')('@engine9/input-tools');
6
- const os = require('node:os');
7
- const { mkdirp } = require('mkdirp');
8
- const { Transform } = require('node:stream');
9
-
10
- const JSON5 = require('json5');
11
- const { PassThrough } = require('node:stream');
12
- const progress = require('debug')('info:@engine9/input-tools');
13
- const unzipper = require('unzipper');
14
-
15
- const dayjs = require('dayjs');
16
-
17
- const { S3Client, HeadObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
18
-
19
- const { v7: uuidv7 } = require('uuid');
20
-
13
+ const debug = debug$0('@engine9/input-tools');
14
+ const { mkdirp } = mkdirp$0;
15
+ const { Transform } = nodestream;
16
+ const { PassThrough } = nodestream;
17
+ const progress = debug$0('info:@engine9/input-tools');
18
+ const { S3Client, HeadObjectCommand, GetObjectCommand } = clientS3;
19
+ const { v7: uuidv7 } = uuid;
21
20
  async function getTempDir({ accountId = 'engine9' }) {
22
- const dir = [os.tmpdir(), accountId, new Date().toISOString().substring(0, 10)].join(path.sep);
23
- try {
24
- await mkdirp(dir);
25
- } catch (err) {
26
- if (err.code !== 'EEXIST') throw err;
27
- }
28
- return dir;
21
+ const dir = [os.tmpdir(), accountId, new Date().toISOString().substring(0, 10)].join(path.sep);
22
+ try {
23
+ await mkdirp(dir);
24
+ }
25
+ catch (err) {
26
+ if (err.code !== 'EEXIST')
27
+ throw err;
28
+ }
29
+ return dir;
29
30
  }
30
-
31
31
  /*
32
32
  Get a new, timestamp based filename, creating any necessary directories
33
33
  options:
@@ -35,346 +35,354 @@ async function getTempDir({ accountId = 'engine9' }) {
35
35
  source:source file, used to generate friendly name
36
36
  */
37
37
  async function getTempFilename(options) {
38
- let dir = await getTempDir(options);
39
-
40
- const target = options.targetFilename;
41
- if (target) {
42
- if (target.indexOf('/') === 0 || target.indexOf('\\') === 0) {
43
- // assume a full directory path has been specified
44
- return target;
38
+ let dir = await getTempDir(options);
39
+ const target = options.targetFilename;
40
+ if (target) {
41
+ if (target.indexOf('/') === 0 || target.indexOf('\\') === 0) {
42
+ // assume a full directory path has been specified
43
+ return target;
44
+ }
45
+ // make a distinct directory, so we don't overwrite the file
46
+ dir = `${dir}/${new Date()
47
+ .toISOString()
48
+ .slice(0, -6)
49
+ .replace(/[^0-9]/g, '_')}`;
50
+ const newDir = await mkdirp(dir);
51
+ return `${newDir}/${target}`;
52
+ }
53
+ let { prefix } = options;
54
+ let { postfix } = options;
55
+ const { targetFormat } = options;
56
+ if (!postfix && targetFormat === 'csv')
57
+ postfix = '.csv';
58
+ if (options.source) {
59
+ postfix = `_${options.source.split('/').pop()}`;
60
+ postfix = postfix.replace(/['"\\]/g, '').replace(/[^a-zA-Z0-9_.-]/g, '_');
45
61
  }
46
-
47
- // make a distinct directory, so we don't overwrite the file
48
- dir = `${dir}/${new Date()
49
- .toISOString()
50
- .slice(0, -6)
51
- .replace(/[^0-9]/g, '_')}`;
52
-
53
- const newDir = await mkdirp(dir);
54
-
55
- return `${newDir}/${target}`;
56
- }
57
- let { prefix } = options;
58
- let { postfix } = options;
59
- const { targetFormat } = options;
60
- if (!postfix && targetFormat === 'csv') postfix = '.csv';
61
- if (options.source) {
62
- postfix = `_${options.source.split('/').pop()}`;
63
- postfix = postfix.replace(/['"\\]/g, '').replace(/[^a-zA-Z0-9_.-]/g, '_');
64
- }
65
-
66
- if (prefix) prefix += '_';
67
-
68
- const p = `${dir}/${prefix || ''}${uuidv7()}${postfix || '.txt'}`;
69
- return p;
62
+ if (prefix)
63
+ prefix += '_';
64
+ const p = `${dir}/${prefix || ''}${uuidv7()}${postfix || '.txt'}`;
65
+ return p;
70
66
  }
71
-
72
67
  async function writeTempFile(options) {
73
- const { content, postfix = '.txt' } = options;
74
- const filename = await getTempFilename({ ...options, postfix });
75
-
76
- await fsp.writeFile(filename, content);
77
- return { filename };
68
+ const { content, postfix = '.txt' } = options;
69
+ const filename = await getTempFilename({ ...options, postfix });
70
+ await fsp.writeFile(filename, content);
71
+ return { filename };
78
72
  }
79
-
80
73
  async function getPacketFiles({ packet }) {
81
- if (packet.indexOf('s3://') === 0) {
82
- const parts = packet.split('/');
83
- const Bucket = parts[2];
84
- const Key = parts.slice(3).join('/');
85
- const s3Client = new S3Client({});
86
-
87
- debug('Getting ', { Bucket, Key });
88
-
89
- // const directory = await unzipper.Open.s3(s3Client, { Bucket, Key });
90
- let size = null;
91
- const directory = await unzipper.Open.custom({
92
- async size() {
93
- const info = await s3Client.send(
94
- new HeadObjectCommand({
95
- Bucket,
96
- Key
97
- })
98
- );
99
- size = info.ContentLength;
100
- progress(`Retrieving file of size ${size / (1024 * 1024)} MB`);
101
- return info.ContentLength;
102
- },
103
-
104
- stream(offset, length) {
105
- const ptStream = new PassThrough();
106
- s3Client
107
- .send(
108
- new GetObjectCommand({
109
- Bucket,
110
- Key,
111
- Range: `bytes=${offset}-${length ?? ''}`
112
- })
113
- )
114
- .then((response) => {
115
- response.Body.pipe(ptStream);
116
- })
117
- .catch((error) => {
118
- ptStream.emit('error', error);
119
- });
120
-
121
- return ptStream;
122
- }
123
- });
124
-
74
+ if (packet.indexOf('s3://') === 0) {
75
+ const parts = packet.split('/');
76
+ const Bucket = parts[2];
77
+ const Key = parts.slice(3).join('/');
78
+ const s3Client = new S3Client({});
79
+ debug('Getting ', { Bucket, Key });
80
+ // const directory = await unzipper.Open.s3(s3Client, { Bucket, Key });
81
+ let size = null;
82
+ const directory = await unzipper.Open.custom({
83
+ async size() {
84
+ const info = await s3Client.send(new HeadObjectCommand({
85
+ Bucket,
86
+ Key
87
+ }));
88
+ size = info.ContentLength;
89
+ progress(`Retrieving file of size ${size / (1024 * 1024)} MB`);
90
+ return info.ContentLength;
91
+ },
92
+ stream(offset, length) {
93
+ const ptStream = new PassThrough();
94
+ s3Client
95
+ .send(new GetObjectCommand({
96
+ Bucket,
97
+ Key,
98
+ Range: `bytes=${offset}-${length ?? ''}`
99
+ }))
100
+ .then((response) => {
101
+ response.Body.pipe(ptStream);
102
+ })
103
+ .catch((error) => {
104
+ ptStream.emit('error', error);
105
+ });
106
+ return ptStream;
107
+ }
108
+ });
109
+ return directory;
110
+ }
111
+ const directory = await unzipper.Open.file(packet);
125
112
  return directory;
126
- }
127
- const directory = await unzipper.Open.file(packet);
128
- return directory;
129
113
  }
130
-
131
114
  async function getManifest({ packet }) {
132
- if (!packet) throw new Error('no packet option specififed');
133
- const { files } = await getPacketFiles({ packet });
134
- const file = files.find((d) => d.path === 'manifest.json');
135
- const content = await file.buffer();
136
- const manifest = JSON.parse(content.toString());
137
- return manifest;
115
+ if (!packet)
116
+ throw new Error('no packet option specififed');
117
+ const { files } = await getPacketFiles({ packet });
118
+ const file = files.find((d) => d.path === 'manifest.json');
119
+ const content = await file.buffer();
120
+ const manifest = JSON.parse(content.toString());
121
+ return manifest;
138
122
  }
139
-
140
123
  function getBatchTransform({ batchSize = 100 }) {
141
- return {
142
- transform: new Transform({
143
- objectMode: true,
144
- transform(chunk, encoding, cb) {
145
- this.buffer = (this.buffer || []).concat(chunk);
146
- if (this.buffer.length >= batchSize) {
147
- this.push(this.buffer);
148
- this.buffer = [];
149
- }
150
- cb();
151
- },
152
- flush(cb) {
153
- if (this.buffer?.length > 0) this.push(this.buffer);
154
- cb();
155
- }
156
- })
157
- };
124
+ return {
125
+ transform: new Transform({
126
+ objectMode: true,
127
+ transform(chunk, encoding, cb) {
128
+ this.buffer = (this.buffer || []).concat(chunk);
129
+ if (this.buffer.length >= batchSize) {
130
+ this.push(this.buffer);
131
+ this.buffer = [];
132
+ }
133
+ cb();
134
+ },
135
+ flush(cb) {
136
+ if (this.buffer?.length > 0)
137
+ this.push(this.buffer);
138
+ cb();
139
+ }
140
+ })
141
+ };
158
142
  }
159
143
  function getDebatchTransform() {
160
- return {
161
- transform: new Transform({
162
- objectMode: true,
163
- transform(chunk, encoding, cb) {
164
- chunk.forEach((c) => this.push(c));
165
- cb();
166
- }
167
- })
168
- };
144
+ return {
145
+ transform: new Transform({
146
+ objectMode: true,
147
+ transform(chunk, encoding, cb) {
148
+ chunk.forEach((c) => this.push(c));
149
+ cb();
150
+ }
151
+ })
152
+ };
169
153
  }
170
-
171
154
  async function getFile({ filename, packet, type }) {
172
- if (!packet && !filename) throw new Error('no packet option specififed');
173
- let content = null;
174
- let filePath = null;
175
- if (packet) {
155
+ if (!packet && !filename)
156
+ throw new Error('no packet option specififed');
157
+ let content = null;
158
+ let filePath = null;
159
+ if (packet) {
160
+ const manifest = await getManifest({ packet });
161
+ const manifestFiles = manifest.files?.filter((d) => d.type === type);
162
+ if (!manifestFiles?.length)
163
+ throw new Error(`No files of type ${type} found in packet`);
164
+ if (manifestFiles?.length > 1)
165
+ throw new Error(`Multiple files of type ${type} found in packet`);
166
+ filePath = manifestFiles[0].path;
167
+ const { files } = await getPacketFiles({ packet });
168
+ const handle = files.find((d) => d.path === filePath);
169
+ const buffer = await handle.buffer();
170
+ content = await buffer.toString();
171
+ }
172
+ else {
173
+ content = await fsp.readFile(filename);
174
+ filePath = filename.split('/').pop();
175
+ }
176
+ if (filePath.slice(-5) === '.json' || filePath.slice(-6) === '.json5') {
177
+ try {
178
+ return JSON5.parse(content);
179
+ }
180
+ catch (e) {
181
+ debug(`Erroring parsing json content from ${path}`, content);
182
+ throw e;
183
+ }
184
+ }
185
+ return content;
186
+ }
187
+ async function streamPacket({ packet, type }) {
188
+ if (!packet)
189
+ throw new Error('no packet option specififed');
176
190
  const manifest = await getManifest({ packet });
177
191
  const manifestFiles = manifest.files?.filter((d) => d.type === type);
178
- if (!manifestFiles?.length) throw new Error(`No files of type ${type} found in packet`);
179
- if (manifestFiles?.length > 1) throw new Error(`Multiple files of type ${type} found in packet`);
180
- filePath = manifestFiles[0].path;
192
+ if (!manifestFiles?.length)
193
+ throw new Error(`No files of type ${type} found in packet`);
194
+ if (manifestFiles?.length > 1)
195
+ throw new Error(`Multiple files of type ${type} found in packet`);
196
+ const filePath = manifestFiles[0].path;
181
197
  const { files } = await getPacketFiles({ packet });
182
198
  const handle = files.find((d) => d.path === filePath);
183
- const buffer = await handle.buffer();
184
- content = await buffer.toString();
185
- } else {
186
- content = await fsp.readFile(filename);
187
- filePath = filename.split('/').pop();
188
- }
189
- if (filePath.slice(-5) === '.json' || filePath.slice(-6) === '.json5') {
190
- try {
191
- return JSON5.parse(content);
192
- } catch (e) {
193
- debug(`Erroring parsing json content from ${path}`, content);
194
- throw e;
195
- }
196
- }
197
- return content;
198
- }
199
-
200
- async function streamPacket({ packet, type }) {
201
- if (!packet) throw new Error('no packet option specififed');
202
- const manifest = await getManifest({ packet });
203
- const manifestFiles = manifest.files?.filter((d) => d.type === type);
204
- if (!manifestFiles?.length) throw new Error(`No files of type ${type} found in packet`);
205
- if (manifestFiles?.length > 1) throw new Error(`Multiple files of type ${type} found in packet`);
206
- const filePath = manifestFiles[0].path;
207
- const { files } = await getPacketFiles({ packet });
208
- const handle = files.find((d) => d.path === filePath);
209
- return { stream: handle.stream(), path: filePath };
199
+ return { stream: handle.stream(), path: filePath };
210
200
  }
211
-
212
201
  async function downloadFile({ packet, type = 'person' }) {
213
- const { stream: fileStream, path: filePath } = await streamPacket({ packet, type });
214
- const filename = await getTempFilename({ targetFilename: filePath.split('/').pop() });
215
-
216
- return new Promise((resolve, reject) => {
217
- fileStream
218
- .pipe(fs.createWriteStream(filename))
219
- .on('error', reject)
220
- .on('finish', () => {
221
- resolve({ filename });
222
- });
223
- });
202
+ const { stream: fileStream, path: filePath } = await streamPacket({ packet, type });
203
+ const filename = await getTempFilename({ targetFilename: filePath.split('/').pop() });
204
+ return new Promise((resolve, reject) => {
205
+ fileStream
206
+ .pipe(fs.createWriteStream(filename))
207
+ .on('error', reject)
208
+ .on('finish', () => {
209
+ resolve({ filename });
210
+ });
211
+ });
224
212
  }
225
-
226
213
  function isValidDate(d) {
227
- // we WANT to use isNaN, not the Number.isNaN -- we're checking the date type
228
-
229
- return d instanceof Date && !isNaN(d);
214
+ // we WANT to use isNaN, not the Number.isNaN -- we're checking the date type
215
+ return d instanceof Date && !isNaN(d);
230
216
  }
231
-
232
217
  function bool(x, _defaultVal) {
233
- const defaultVal = _defaultVal === undefined ? false : _defaultVal;
234
- if (x === undefined || x === null || x === '') return defaultVal;
235
- if (typeof x !== 'string') return !!x;
236
- if (x === '1') return true; // 0 will return false, but '1' is true
237
- const y = x.toLowerCase();
238
- return !!(y.indexOf('y') + 1) || !!(y.indexOf('t') + 1);
218
+ const defaultVal = _defaultVal === undefined ? false : _defaultVal;
219
+ if (x === undefined || x === null || x === '')
220
+ return defaultVal;
221
+ if (typeof x !== 'string')
222
+ return !!x;
223
+ if (x === '1')
224
+ return true; // 0 will return false, but '1' is true
225
+ const y = x.toLowerCase();
226
+ return !!(y.indexOf('y') + 1) || !!(y.indexOf('t') + 1);
239
227
  }
240
228
  function getStringArray(s, nonZeroLength) {
241
- let a = s || [];
242
- if (typeof a === 'number') a = String(a);
243
- if (typeof a === 'string') a = [a];
244
-
245
- if (typeof s === 'string') a = s.split(',');
246
- a = a.map((x) => x.toString().trim()).filter(Boolean);
247
- if (nonZeroLength && a.length === 0) a = [0];
248
- return a;
229
+ let a = s || [];
230
+ if (typeof a === 'number')
231
+ a = String(a);
232
+ if (typeof a === 'string')
233
+ a = [a];
234
+ if (typeof s === 'string')
235
+ a = s.split(',');
236
+ a = a.map((x) => x.toString().trim()).filter(Boolean);
237
+ if (nonZeroLength && a.length === 0)
238
+ a = [0];
239
+ return a;
249
240
  }
250
241
  function relativeDate(s, _initialDate) {
251
- let initialDate = _initialDate;
252
- if (!s || s === 'none') return null;
253
- if (typeof s.getMonth === 'function') return s;
254
- // We actually want a double equals here to test strings as well
255
-
256
- if (parseInt(s, 10) == s) {
257
- const r = new Date(parseInt(s, 10));
258
- if (!isValidDate(r)) throw new Error(`Invalid integer date:${s}`);
259
- return r;
260
- }
261
-
262
- if (initialDate) {
263
- initialDate = new Date(initialDate);
264
- } else {
265
- initialDate = new Date();
266
- }
267
-
268
- let r = s.match(/^([+-]{1})([0-9]+)([YyMwdhms]{1})([.a-z]*)$/);
269
-
270
- if (r) {
271
- let period = null;
272
- switch (r[3]) {
273
- case 'Y':
274
- case 'y':
275
- period = 'years';
276
- break;
277
-
278
- case 'M':
279
- period = 'months';
280
- break;
281
- case 'w':
282
- period = 'weeks';
283
- break;
284
- case 'd':
285
- period = 'days';
286
- break;
287
- case 'h':
288
- period = 'hours';
289
- break;
290
- case 'm':
291
- period = 'minutes';
292
- break;
293
- case 's':
294
- period = 'seconds';
295
- break;
296
- default:
297
- period = 'minutes';
298
- break;
242
+ let initialDate = _initialDate;
243
+ if (!s || s === 'none')
244
+ return null;
245
+ if (typeof s.getMonth === 'function')
246
+ return s;
247
+ // We actually want a double equals here to test strings as well
248
+ if (parseInt(s, 10) == s) {
249
+ const r = new Date(parseInt(s, 10));
250
+ if (!isValidDate(r))
251
+ throw new Error(`Invalid integer date:${s}`);
252
+ return r;
253
+ }
254
+ if (initialDate) {
255
+ initialDate = new Date(initialDate);
256
+ }
257
+ else {
258
+ initialDate = new Date();
299
259
  }
300
-
301
- let d = dayjs(initialDate);
302
-
303
- if (r[1] === '+') {
304
- d = d.add(parseInt(r[2], 10), period);
305
- } else {
306
- d = d.subtract(parseInt(r[2], 10), period);
260
+ let r = s.match(/^([+-]{1})([0-9]+)([YyMwdhms]{1})([.a-z]*)$/);
261
+ if (r) {
262
+ let period = null;
263
+ switch (r[3]) {
264
+ case 'Y':
265
+ case 'y':
266
+ period = 'years';
267
+ break;
268
+ case 'M':
269
+ period = 'months';
270
+ break;
271
+ case 'w':
272
+ period = 'weeks';
273
+ break;
274
+ case 'd':
275
+ period = 'days';
276
+ break;
277
+ case 'h':
278
+ period = 'hours';
279
+ break;
280
+ case 'm':
281
+ period = 'minutes';
282
+ break;
283
+ case 's':
284
+ period = 'seconds';
285
+ break;
286
+ default:
287
+ period = 'minutes';
288
+ break;
289
+ }
290
+ let d = dayjs(initialDate);
291
+ if (r[1] === '+') {
292
+ d = d.add(parseInt(r[2], 10), period);
293
+ }
294
+ else {
295
+ d = d.subtract(parseInt(r[2], 10), period);
296
+ }
297
+ if (!isValidDate(d.toDate()))
298
+ throw new Error(`Invalid date configuration:${r}`);
299
+ if (r[4]) {
300
+ const opts = r[4].split('.').filter(Boolean);
301
+ if (opts[0] === 'start')
302
+ d = d.startOf(opts[1] || 'day');
303
+ else if (opts[0] === 'end')
304
+ d = d.endOf(opts[1] || 'day');
305
+ else
306
+ throw new Error(`Invalid relative date,unknown options:${r[4]}`);
307
+ }
308
+ return d.toDate();
307
309
  }
308
- if (!isValidDate(d.toDate())) throw new Error(`Invalid date configuration:${r}`);
309
- if (r[4]) {
310
- const opts = r[4].split('.').filter(Boolean);
311
- if (opts[0] === 'start') d = d.startOf(opts[1] || 'day');
312
- else if (opts[0] === 'end') d = d.endOf(opts[1] || 'day');
313
- else throw new Error(`Invalid relative date,unknown options:${r[4]}`);
310
+ if (s === 'now') {
311
+ r = dayjs(new Date()).toDate();
312
+ return r;
314
313
  }
315
-
316
- return d.toDate();
317
- }
318
- if (s === 'now') {
319
- r = dayjs(new Date()).toDate();
314
+ r = dayjs(new Date(s)).toDate();
315
+ if (!isValidDate(r))
316
+ throw new Error(`Invalid Date: ${s}`);
320
317
  return r;
321
- }
322
- r = dayjs(new Date(s)).toDate();
323
- if (!isValidDate(r)) throw new Error(`Invalid Date: ${s}`);
324
- return r;
325
318
  }
326
-
327
319
  /*
328
320
  When comparing two objects, some may come from a file (thus strings), and some from
329
321
  a database or elsewhere (not strings), so for deduping make sure to make them all strings
330
322
  */
331
323
  function makeStrings(o) {
332
- return Object.entries(o).reduce((a, [k, v]) => {
333
- a[k] = typeof v === 'object' ? JSON.stringify(v) : String(v);
334
- return a;
335
- }, {});
324
+ return Object.entries(o).reduce((a, [k, v]) => {
325
+ a[k] = typeof v === 'object' ? JSON.stringify(v) : String(v);
326
+ return a;
327
+ }, {});
336
328
  }
337
329
  function appendPostfix(filename, postfix) {
338
- const filenameParts = filename.split('/');
339
- const fileParts = filenameParts
340
- .slice(-1)[0]
341
- .split('.')
342
- .filter(Boolean)
343
- .filter((d) => d !== postfix);
344
-
345
- let targetFile = null;
346
- if (fileParts.slice(-1)[0] === 'gz') {
347
- targetFile = fileParts.slice(0, -2).concat(postfix).concat(fileParts.slice(-2)).join('.');
348
- } else {
349
- targetFile = fileParts.slice(0, -1).concat(postfix).concat(fileParts.slice(-1)).join('.');
350
- }
351
- return filenameParts.slice(0, -1).concat(targetFile).join('/');
330
+ const filenameParts = filename.split('/');
331
+ const fileParts = filenameParts
332
+ .slice(-1)[0]
333
+ .split('.')
334
+ .filter(Boolean)
335
+ .filter((d) => d !== postfix);
336
+ let targetFile = null;
337
+ if (fileParts.slice(-1)[0] === 'gz') {
338
+ targetFile = fileParts.slice(0, -2).concat(postfix).concat(fileParts.slice(-2)).join('.');
339
+ }
340
+ else {
341
+ targetFile = fileParts.slice(0, -1).concat(postfix).concat(fileParts.slice(-1)).join('.');
342
+ }
343
+ return filenameParts.slice(0, -1).concat(targetFile).join('/');
352
344
  }
353
-
354
345
  function parseJSON5(o, defaultVal) {
355
- if (o) {
356
- if (typeof o === 'object') return o;
357
- if (typeof o === 'string') return JSON5.parse(o);
358
- throw new Error(`Could not parse object:${o}`);
359
- }
360
- return defaultVal || o;
346
+ if (o) {
347
+ if (typeof o === 'object')
348
+ return o;
349
+ if (typeof o === 'string')
350
+ return JSON5.parse(o);
351
+ throw new Error(`Could not parse object:${o}`);
352
+ }
353
+ return defaultVal || o;
361
354
  }
362
-
363
- module.exports = {
364
- appendPostfix,
365
- bool,
366
- downloadFile,
367
- getTempFilename,
368
- getTempDir,
369
- getBatchTransform,
370
- getDebatchTransform,
371
- getFile,
372
- getManifest,
373
- getPacketFiles,
374
- getStringArray,
375
- makeStrings,
376
- parseJSON5,
377
- relativeDate,
378
- streamPacket,
379
- writeTempFile
355
+ export { appendPostfix };
356
+ export { bool };
357
+ export { downloadFile };
358
+ export { getTempFilename };
359
+ export { getTempDir };
360
+ export { getBatchTransform };
361
+ export { getDebatchTransform };
362
+ export { getFile };
363
+ export { getManifest };
364
+ export { getPacketFiles };
365
+ export { getStringArray };
366
+ export { makeStrings };
367
+ export { parseJSON5 };
368
+ export { relativeDate };
369
+ export { streamPacket };
370
+ export { writeTempFile };
371
+ export default {
372
+ appendPostfix,
373
+ bool,
374
+ downloadFile,
375
+ getTempFilename,
376
+ getTempDir,
377
+ getBatchTransform,
378
+ getDebatchTransform,
379
+ getFile,
380
+ getManifest,
381
+ getPacketFiles,
382
+ getStringArray,
383
+ makeStrings,
384
+ parseJSON5,
385
+ relativeDate,
386
+ streamPacket,
387
+ writeTempFile
380
388
  };