aiplang 2.0.0 → 2.1.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.
Files changed (53) hide show
  1. package/bin/aiplang.js +7 -7
  2. package/package.json +7 -5
  3. package/server/node_modules/.package-lock.json +9 -0
  4. package/server/node_modules/nodemailer/.gitattributes +6 -0
  5. package/server/node_modules/nodemailer/.ncurc.js +9 -0
  6. package/server/node_modules/nodemailer/.prettierignore +8 -0
  7. package/server/node_modules/nodemailer/.prettierrc +12 -0
  8. package/server/node_modules/nodemailer/.prettierrc.js +10 -0
  9. package/server/node_modules/nodemailer/.release-please-config.json +9 -0
  10. package/server/node_modules/nodemailer/CHANGELOG.md +976 -0
  11. package/server/node_modules/nodemailer/CODE_OF_CONDUCT.md +76 -0
  12. package/server/node_modules/nodemailer/LICENSE +16 -0
  13. package/server/node_modules/nodemailer/README.md +86 -0
  14. package/server/node_modules/nodemailer/SECURITY.txt +22 -0
  15. package/server/node_modules/nodemailer/eslint.config.js +88 -0
  16. package/server/node_modules/nodemailer/lib/addressparser/index.js +382 -0
  17. package/server/node_modules/nodemailer/lib/base64/index.js +140 -0
  18. package/server/node_modules/nodemailer/lib/dkim/index.js +245 -0
  19. package/server/node_modules/nodemailer/lib/dkim/message-parser.js +154 -0
  20. package/server/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
  21. package/server/node_modules/nodemailer/lib/dkim/sign.js +116 -0
  22. package/server/node_modules/nodemailer/lib/errors.js +58 -0
  23. package/server/node_modules/nodemailer/lib/fetch/cookies.js +276 -0
  24. package/server/node_modules/nodemailer/lib/fetch/index.js +278 -0
  25. package/server/node_modules/nodemailer/lib/json-transport/index.js +82 -0
  26. package/server/node_modules/nodemailer/lib/mail-composer/index.js +599 -0
  27. package/server/node_modules/nodemailer/lib/mailer/index.js +446 -0
  28. package/server/node_modules/nodemailer/lib/mailer/mail-message.js +312 -0
  29. package/server/node_modules/nodemailer/lib/mime-funcs/index.js +610 -0
  30. package/server/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2109 -0
  31. package/server/node_modules/nodemailer/lib/mime-node/index.js +1334 -0
  32. package/server/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
  33. package/server/node_modules/nodemailer/lib/mime-node/le-unix.js +40 -0
  34. package/server/node_modules/nodemailer/lib/mime-node/le-windows.js +49 -0
  35. package/server/node_modules/nodemailer/lib/nodemailer.js +151 -0
  36. package/server/node_modules/nodemailer/lib/punycode/index.js +460 -0
  37. package/server/node_modules/nodemailer/lib/qp/index.js +230 -0
  38. package/server/node_modules/nodemailer/lib/sendmail-transport/index.js +205 -0
  39. package/server/node_modules/nodemailer/lib/ses-transport/index.js +223 -0
  40. package/server/node_modules/nodemailer/lib/shared/index.js +698 -0
  41. package/server/node_modules/nodemailer/lib/smtp-connection/data-stream.js +105 -0
  42. package/server/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +144 -0
  43. package/server/node_modules/nodemailer/lib/smtp-connection/index.js +1903 -0
  44. package/server/node_modules/nodemailer/lib/smtp-pool/index.js +641 -0
  45. package/server/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +256 -0
  46. package/server/node_modules/nodemailer/lib/smtp-transport/index.js +402 -0
  47. package/server/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
  48. package/server/node_modules/nodemailer/lib/well-known/index.js +47 -0
  49. package/server/node_modules/nodemailer/lib/well-known/services.json +619 -0
  50. package/server/node_modules/nodemailer/lib/xoauth2/index.js +436 -0
  51. package/server/node_modules/nodemailer/package.json +48 -0
  52. package/server/server.js +686 -865
  53. /package/{FLUX-PROJECT-KNOWLEDGE.md → aiplang-knowledge.md} +0 -0
@@ -0,0 +1,698 @@
1
+ /* eslint no-console: 0 */
2
+
3
+ 'use strict';
4
+
5
+ const urllib = require('url');
6
+ const util = require('util');
7
+ const fs = require('fs');
8
+ const nmfetch = require('../fetch');
9
+ const dns = require('dns');
10
+ const net = require('net');
11
+ const os = require('os');
12
+
13
+ const DNS_TTL = 5 * 60 * 1000;
14
+ const CACHE_CLEANUP_INTERVAL = 30 * 1000; // Minimum 30 seconds between cleanups
15
+ const MAX_CACHE_SIZE = 1000; // Maximum number of entries in cache
16
+
17
+ let lastCacheCleanup = 0;
18
+ module.exports._lastCacheCleanup = () => lastCacheCleanup;
19
+ module.exports._resetCacheCleanup = () => {
20
+ lastCacheCleanup = 0;
21
+ };
22
+
23
+ let networkInterfaces;
24
+ try {
25
+ networkInterfaces = os.networkInterfaces();
26
+ } catch (_err) {
27
+ // fails on some systems
28
+ }
29
+
30
+ module.exports.networkInterfaces = networkInterfaces;
31
+
32
+ const isFamilySupported = (family, allowInternal) => {
33
+ const ifaces = module.exports.networkInterfaces;
34
+ if (!ifaces) {
35
+ // hope for the best
36
+ return true;
37
+ }
38
+
39
+ return Object.keys(ifaces)
40
+ .map(key => ifaces[key])
41
+ .reduce((acc, val) => acc.concat(val), [])
42
+ .filter(i => !i.internal || allowInternal)
43
+ .some(i => i.family === 'IPv' + family || i.family === family);
44
+ };
45
+
46
+ const resolve = (family, hostname, options, callback) => {
47
+ options = options || {};
48
+
49
+ if (!isFamilySupported(family, options.allowInternalNetworkInterfaces)) {
50
+ return callback(null, []);
51
+ }
52
+
53
+ const dnsResolver = dns.Resolver ? new dns.Resolver(options) : dns;
54
+ dnsResolver['resolve' + family](hostname, (err, addresses) => {
55
+ if (err) {
56
+ switch (err.code) {
57
+ case dns.NODATA:
58
+ case dns.NOTFOUND:
59
+ case dns.NOTIMP:
60
+ case dns.SERVFAIL:
61
+ case dns.CONNREFUSED:
62
+ case dns.REFUSED:
63
+ case 'EAI_AGAIN':
64
+ return callback(null, []);
65
+ }
66
+ return callback(err);
67
+ }
68
+ return callback(null, Array.isArray(addresses) ? addresses : [].concat(addresses || []));
69
+ });
70
+ };
71
+
72
+ const dnsCache = (module.exports.dnsCache = new Map());
73
+
74
+ const formatDNSValue = (value, extra) => {
75
+ if (!value) {
76
+ return Object.assign({}, extra || {});
77
+ }
78
+
79
+ const addresses = value.addresses || [];
80
+
81
+ // Select a random address from available addresses, or null if none
82
+ const host = addresses.length > 0 ? addresses[Math.floor(Math.random() * addresses.length)] : null;
83
+
84
+ return Object.assign(
85
+ {
86
+ servername: value.servername,
87
+ host,
88
+ // Include all addresses for connection fallback support
89
+ _addresses: addresses
90
+ },
91
+ extra || {}
92
+ );
93
+ };
94
+
95
+ module.exports.resolveHostname = (options, callback) => {
96
+ options = options || {};
97
+
98
+ if (!options.host && options.servername) {
99
+ options.host = options.servername;
100
+ }
101
+
102
+ if (!options.host || net.isIP(options.host)) {
103
+ // nothing to do here
104
+ const value = {
105
+ addresses: [options.host],
106
+ servername: options.servername || false
107
+ };
108
+ return callback(
109
+ null,
110
+ formatDNSValue(value, {
111
+ cached: false
112
+ })
113
+ );
114
+ }
115
+
116
+ let cached;
117
+ if (dnsCache.has(options.host)) {
118
+ cached = dnsCache.get(options.host);
119
+
120
+ // Lazy cleanup with time throttling
121
+ const now = Date.now();
122
+ if (now - lastCacheCleanup > CACHE_CLEANUP_INTERVAL) {
123
+ lastCacheCleanup = now;
124
+
125
+ // Clean up expired entries
126
+ for (const [host, entry] of dnsCache.entries()) {
127
+ if (entry.expires && entry.expires < now) {
128
+ dnsCache.delete(host);
129
+ }
130
+ }
131
+
132
+ // If cache is still too large, remove oldest entries
133
+ if (dnsCache.size > MAX_CACHE_SIZE) {
134
+ const toDelete = Math.floor(MAX_CACHE_SIZE * 0.1); // Remove 10% of entries
135
+ const keys = Array.from(dnsCache.keys()).slice(0, toDelete);
136
+ keys.forEach(key => dnsCache.delete(key));
137
+ }
138
+ }
139
+
140
+ if (!cached.expires || cached.expires >= now) {
141
+ return callback(
142
+ null,
143
+ formatDNSValue(cached.value, {
144
+ cached: true
145
+ })
146
+ );
147
+ }
148
+ }
149
+
150
+ // Resolve both IPv4 and IPv6 addresses for fallback support
151
+ let ipv4Addresses = [];
152
+ let ipv6Addresses = [];
153
+ let ipv4Error = null;
154
+ let ipv6Error = null;
155
+
156
+ resolve(4, options.host, options, (err, addresses) => {
157
+ if (err) {
158
+ ipv4Error = err;
159
+ } else {
160
+ ipv4Addresses = addresses || [];
161
+ }
162
+
163
+ resolve(6, options.host, options, (err, addresses) => {
164
+ if (err) {
165
+ ipv6Error = err;
166
+ } else {
167
+ ipv6Addresses = addresses || [];
168
+ }
169
+
170
+ // Combine addresses: IPv4 first, then IPv6
171
+ const allAddresses = ipv4Addresses.concat(ipv6Addresses);
172
+
173
+ if (allAddresses.length) {
174
+ const value = {
175
+ addresses: allAddresses,
176
+ servername: options.servername || options.host
177
+ };
178
+
179
+ dnsCache.set(options.host, {
180
+ value,
181
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
182
+ });
183
+
184
+ return callback(
185
+ null,
186
+ formatDNSValue(value, {
187
+ cached: false
188
+ })
189
+ );
190
+ }
191
+
192
+ // No addresses from resolve4/resolve6, try dns.lookup as fallback
193
+ if (ipv4Error && ipv6Error) {
194
+ // Both resolvers had errors
195
+ if (cached) {
196
+ dnsCache.set(options.host, {
197
+ value: cached.value,
198
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
199
+ });
200
+
201
+ return callback(
202
+ null,
203
+ formatDNSValue(cached.value, {
204
+ cached: true,
205
+ error: ipv4Error
206
+ })
207
+ );
208
+ }
209
+ }
210
+
211
+ try {
212
+ dns.lookup(options.host, { all: true }, (err, addresses) => {
213
+ if (err) {
214
+ if (cached) {
215
+ dnsCache.set(options.host, {
216
+ value: cached.value,
217
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
218
+ });
219
+
220
+ return callback(
221
+ null,
222
+ formatDNSValue(cached.value, {
223
+ cached: true,
224
+ error: err
225
+ })
226
+ );
227
+ }
228
+ return callback(err);
229
+ }
230
+
231
+ // Get all supported addresses from dns.lookup
232
+ const supportedAddresses = addresses
233
+ ? addresses.filter(addr => isFamilySupported(addr.family)).map(addr => addr.address)
234
+ : [];
235
+
236
+ if (addresses && addresses.length && !supportedAddresses.length) {
237
+ // there are addresses but none can be used
238
+ console.warn(`Failed to resolve IPv${addresses[0].family} addresses with current network`);
239
+ }
240
+
241
+ if (!supportedAddresses.length && cached) {
242
+ // nothing was found, fallback to cached value
243
+ return callback(
244
+ null,
245
+ formatDNSValue(cached.value, {
246
+ cached: true
247
+ })
248
+ );
249
+ }
250
+
251
+ const value = {
252
+ addresses: supportedAddresses.length ? supportedAddresses : [options.host],
253
+ servername: options.servername || options.host
254
+ };
255
+
256
+ dnsCache.set(options.host, {
257
+ value,
258
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
259
+ });
260
+
261
+ return callback(
262
+ null,
263
+ formatDNSValue(value, {
264
+ cached: false
265
+ })
266
+ );
267
+ });
268
+ } catch (lookupErr) {
269
+ if (cached) {
270
+ dnsCache.set(options.host, {
271
+ value: cached.value,
272
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
273
+ });
274
+
275
+ return callback(
276
+ null,
277
+ formatDNSValue(cached.value, {
278
+ cached: true,
279
+ error: lookupErr
280
+ })
281
+ );
282
+ }
283
+ return callback(ipv4Error || ipv6Error || lookupErr);
284
+ }
285
+ });
286
+ });
287
+ };
288
+ /**
289
+ * Parses connection url to a structured configuration object
290
+ *
291
+ * @param {String} str Connection url
292
+ * @return {Object} Configuration object
293
+ */
294
+ module.exports.parseConnectionUrl = str => {
295
+ str = str || '';
296
+ const options = {};
297
+ const url = urllib.parse(str, true);
298
+
299
+ switch (url.protocol) {
300
+ case 'smtp:':
301
+ options.secure = false;
302
+ break;
303
+ case 'smtps:':
304
+ options.secure = true;
305
+ break;
306
+ case 'direct:':
307
+ options.direct = true;
308
+ break;
309
+ }
310
+
311
+ if (!isNaN(url.port) && Number(url.port)) {
312
+ options.port = Number(url.port);
313
+ }
314
+
315
+ if (url.hostname) {
316
+ options.host = url.hostname;
317
+ }
318
+
319
+ if (url.auth) {
320
+ const auth = url.auth.split(':');
321
+ options.auth = {
322
+ user: auth.shift(),
323
+ pass: auth.join(':')
324
+ };
325
+ }
326
+
327
+ Object.keys(url.query || {}).forEach(key => {
328
+ let obj = options;
329
+ let lKey = key;
330
+ let value = url.query[key];
331
+
332
+ if (!isNaN(value)) {
333
+ value = Number(value);
334
+ }
335
+
336
+ switch (value) {
337
+ case 'true':
338
+ value = true;
339
+ break;
340
+ case 'false':
341
+ value = false;
342
+ break;
343
+ }
344
+
345
+ // tls is nested object
346
+ if (key.indexOf('tls.') === 0) {
347
+ lKey = key.substr(4);
348
+ if (!options.tls) {
349
+ options.tls = {};
350
+ }
351
+ obj = options.tls;
352
+ } else if (key.indexOf('.') >= 0) {
353
+ // ignore nested properties besides tls
354
+ return;
355
+ }
356
+
357
+ if (!(lKey in obj)) {
358
+ obj[lKey] = value;
359
+ }
360
+ });
361
+
362
+ return options;
363
+ };
364
+
365
+ module.exports._logFunc = (logger, level, defaults, data, message, ...args) => {
366
+ const entry = Object.assign({}, defaults || {}, data || {});
367
+ delete entry.level;
368
+
369
+ logger[level](entry, message, ...args);
370
+ };
371
+
372
+ /**
373
+ * Returns a bunyan-compatible logger interface. Uses either provided logger or
374
+ * creates a default console logger
375
+ *
376
+ * @param {Object} [options] Options object that might include 'logger' value
377
+ * @return {Object} bunyan compatible logger
378
+ */
379
+ module.exports.getLogger = (options, defaults) => {
380
+ options = options || {};
381
+
382
+ const response = {};
383
+ const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
384
+
385
+ if (!options.logger) {
386
+ // use vanity logger
387
+ levels.forEach(level => {
388
+ response[level] = () => false;
389
+ });
390
+ return response;
391
+ }
392
+
393
+ const logger = options.logger === true ? createDefaultLogger(levels) : options.logger;
394
+
395
+ levels.forEach(level => {
396
+ response[level] = (data, message, ...args) => {
397
+ module.exports._logFunc(logger, level, defaults, data, message, ...args);
398
+ };
399
+ });
400
+
401
+ return response;
402
+ };
403
+
404
+ /**
405
+ * Wrapper for creating a callback that either resolves or rejects a promise
406
+ * based on input
407
+ *
408
+ * @param {Function} resolve Function to run if callback is called
409
+ * @param {Function} reject Function to run if callback ends with an error
410
+ */
411
+ module.exports.callbackPromise = (resolve, reject) =>
412
+ function () {
413
+ const args = Array.from(arguments);
414
+ const err = args.shift();
415
+ if (err) {
416
+ reject(err);
417
+ } else {
418
+ resolve(...args);
419
+ }
420
+ };
421
+
422
+ module.exports.parseDataURI = uri => {
423
+ if (typeof uri !== 'string') {
424
+ return null;
425
+ }
426
+
427
+ // Early return for non-data URIs to avoid unnecessary processing
428
+ if (!uri.startsWith('data:')) {
429
+ return null;
430
+ }
431
+
432
+ // Find the first comma safely - this prevents ReDoS
433
+ const commaPos = uri.indexOf(',');
434
+ if (commaPos === -1) {
435
+ return null;
436
+ }
437
+
438
+ const data = uri.substring(commaPos + 1);
439
+ const metaStr = uri.substring('data:'.length, commaPos);
440
+
441
+ let encoding;
442
+ const metaEntries = metaStr.split(';');
443
+
444
+ if (metaEntries.length > 0) {
445
+ const lastEntry = metaEntries[metaEntries.length - 1].toLowerCase().trim();
446
+ // Only recognize valid encoding types to prevent manipulation
447
+ if (['base64', 'utf8', 'utf-8'].includes(lastEntry) && lastEntry.indexOf('=') === -1) {
448
+ encoding = lastEntry;
449
+ metaEntries.pop();
450
+ }
451
+ }
452
+
453
+ const contentType = metaEntries.length > 0 ? metaEntries.shift() : 'application/octet-stream';
454
+ const params = {};
455
+
456
+ for (let i = 0; i < metaEntries.length; i++) {
457
+ const entry = metaEntries[i];
458
+ const sepPos = entry.indexOf('=');
459
+ if (sepPos > 0) {
460
+ // Ensure there's a key before the '='
461
+ const key = entry.substring(0, sepPos).trim();
462
+ const value = entry.substring(sepPos + 1).trim();
463
+ if (key) {
464
+ params[key] = value;
465
+ }
466
+ }
467
+ }
468
+
469
+ // Decode data based on encoding with proper error handling
470
+ let bufferData;
471
+ try {
472
+ if (encoding === 'base64') {
473
+ bufferData = Buffer.from(data, 'base64');
474
+ } else {
475
+ try {
476
+ bufferData = Buffer.from(decodeURIComponent(data));
477
+ } catch (_decodeError) {
478
+ bufferData = Buffer.from(data);
479
+ }
480
+ }
481
+ } catch (_bufferError) {
482
+ bufferData = Buffer.alloc(0);
483
+ }
484
+
485
+ return {
486
+ data: bufferData,
487
+ encoding: encoding || null,
488
+ contentType: contentType || 'application/octet-stream',
489
+ params
490
+ };
491
+ };
492
+
493
+ /**
494
+ * Resolves a String or a Buffer value for content value. Useful if the value
495
+ * is a Stream or a file or an URL. If the value is a Stream, overwrites
496
+ * the stream object with the resolved value (you can't stream a value twice).
497
+ *
498
+ * This is useful when you want to create a plugin that needs a content value,
499
+ * for example the `html` or `text` value as a String or a Buffer but not as
500
+ * a file path or an URL.
501
+ *
502
+ * @param {Object} data An object or an Array you want to resolve an element for
503
+ * @param {String|Number} key Property name or an Array index
504
+ * @param {Function} callback Callback function with (err, value)
505
+ */
506
+ module.exports.resolveContent = (data, key, callback) => {
507
+ let promise;
508
+
509
+ if (!callback) {
510
+ promise = new Promise((resolve, reject) => {
511
+ callback = module.exports.callbackPromise(resolve, reject);
512
+ });
513
+ }
514
+
515
+ let content = (data && data[key] && data[key].content) || data[key];
516
+ const encoding = ((typeof data[key] === 'object' && data[key].encoding) || 'utf8')
517
+ .toString()
518
+ .toLowerCase()
519
+ .replace(/[-_\s]/g, '');
520
+
521
+ if (!content) {
522
+ return callback(null, content);
523
+ }
524
+
525
+ if (typeof content === 'object') {
526
+ if (typeof content.pipe === 'function') {
527
+ return resolveStream(content, (err, value) => {
528
+ if (err) {
529
+ return callback(err);
530
+ }
531
+ // we can't stream twice the same content, so we need
532
+ // to replace the stream object with the streaming result
533
+ if (data[key].content) {
534
+ data[key].content = value;
535
+ } else {
536
+ data[key] = value;
537
+ }
538
+ callback(null, value);
539
+ });
540
+ } else if (/^https?:\/\//i.test(content.path || content.href)) {
541
+ return resolveStream(nmfetch(content.path || content.href), callback);
542
+ } else if (/^data:/i.test(content.path || content.href)) {
543
+ const parsedDataUri = module.exports.parseDataURI(content.path || content.href);
544
+
545
+ if (!parsedDataUri || !parsedDataUri.data) {
546
+ return callback(null, Buffer.from(0));
547
+ }
548
+ return callback(null, parsedDataUri.data);
549
+ } else if (content.path) {
550
+ return resolveStream(fs.createReadStream(content.path), callback);
551
+ }
552
+ }
553
+
554
+ if (typeof data[key].content === 'string' && !['utf8', 'usascii', 'ascii'].includes(encoding)) {
555
+ content = Buffer.from(data[key].content, encoding);
556
+ }
557
+
558
+ // default action, return as is
559
+ setImmediate(() => callback(null, content));
560
+
561
+ return promise;
562
+ };
563
+
564
+ /**
565
+ * Copies properties from source objects to target objects
566
+ */
567
+ module.exports.assign = function (/* target, ... sources */) {
568
+ const args = Array.from(arguments);
569
+ const target = args.shift() || {};
570
+
571
+ args.forEach(source => {
572
+ Object.keys(source || {}).forEach(key => {
573
+ if (['tls', 'auth'].includes(key) && source[key] && typeof source[key] === 'object') {
574
+ // tls and auth are special keys that need to be enumerated separately
575
+ // other objects are passed as is
576
+ target[key] = Object.assign(target[key] || {}, source[key]);
577
+ } else {
578
+ target[key] = source[key];
579
+ }
580
+ });
581
+ });
582
+ return target;
583
+ };
584
+
585
+ module.exports.encodeXText = str => {
586
+ // ! 0x21
587
+ // + 0x2B
588
+ // = 0x3D
589
+ // ~ 0x7E
590
+ if (!/[^\x21-\x2A\x2C-\x3C\x3E-\x7E]/.test(str)) {
591
+ return str;
592
+ }
593
+ const buf = Buffer.from(str);
594
+ let result = '';
595
+ for (let i = 0, len = buf.length; i < len; i++) {
596
+ const c = buf[i];
597
+ if (c < 0x21 || c > 0x7e || c === 0x2b || c === 0x3d) {
598
+ result += '+' + (c < 0x10 ? '0' : '') + c.toString(16).toUpperCase();
599
+ } else {
600
+ result += String.fromCharCode(c);
601
+ }
602
+ }
603
+ return result;
604
+ };
605
+
606
+ /**
607
+ * Streams a stream value into a Buffer
608
+ *
609
+ * @param {Object} stream Readable stream
610
+ * @param {Function} callback Callback function with (err, value)
611
+ */
612
+ function resolveStream(stream, callback) {
613
+ let responded = false;
614
+ const chunks = [];
615
+ let chunklen = 0;
616
+
617
+ stream.on('error', err => {
618
+ if (responded) {
619
+ return;
620
+ }
621
+
622
+ responded = true;
623
+ callback(err);
624
+ });
625
+
626
+ stream.on('readable', () => {
627
+ let chunk;
628
+ while ((chunk = stream.read()) !== null) {
629
+ chunks.push(chunk);
630
+ chunklen += chunk.length;
631
+ }
632
+ });
633
+
634
+ stream.on('end', () => {
635
+ if (responded) {
636
+ return;
637
+ }
638
+ responded = true;
639
+
640
+ let value;
641
+
642
+ try {
643
+ value = Buffer.concat(chunks, chunklen);
644
+ } catch (E) {
645
+ return callback(E);
646
+ }
647
+ callback(null, value);
648
+ });
649
+ }
650
+
651
+ /**
652
+ * Generates a bunyan-like logger that prints to console
653
+ *
654
+ * @returns {Object} Bunyan logger instance
655
+ */
656
+ function createDefaultLogger(levels) {
657
+ const levelMaxLen = levels.reduce((max, level) => Math.max(max, level.length), 0);
658
+ const levelNames = new Map();
659
+
660
+ levels.forEach(level => {
661
+ let levelName = level.toUpperCase();
662
+ if (levelName.length < levelMaxLen) {
663
+ levelName += ' '.repeat(levelMaxLen - levelName.length);
664
+ }
665
+ levelNames.set(level, levelName);
666
+ });
667
+
668
+ const print = (level, entry, message, ...args) => {
669
+ let prefix = '';
670
+ if (entry) {
671
+ if (entry.tnx === 'server') {
672
+ prefix = 'S: ';
673
+ } else if (entry.tnx === 'client') {
674
+ prefix = 'C: ';
675
+ }
676
+
677
+ if (entry.sid) {
678
+ prefix = '[' + entry.sid + '] ' + prefix;
679
+ }
680
+
681
+ if (entry.cid) {
682
+ prefix = '[#' + entry.cid + '] ' + prefix;
683
+ }
684
+ }
685
+
686
+ message = util.format(message, ...args);
687
+ message.split(/\r?\n/).forEach(line => {
688
+ console.log('[%s] %s %s', new Date().toISOString().substr(0, 19).replace(/T/, ' '), levelNames.get(level), prefix + line);
689
+ });
690
+ };
691
+
692
+ const logger = {};
693
+ levels.forEach(level => {
694
+ logger[level] = print.bind(null, level);
695
+ });
696
+
697
+ return logger;
698
+ }