@mhmdhammoud/meritt-utils 1.5.1 → 1.5.3

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.
@@ -15,63 +15,208 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
28
35
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.isValidLogLevel = void 0;
36
+ exports.isValidLogLevel = isValidLogLevel;
30
37
  const pino_1 = require("pino");
31
38
  const dotenv = __importStar(require("dotenv"));
32
- const pino_elasticsearch_1 = __importDefault(require("pino-elasticsearch"));
39
+ const elastic_transport_1 = require("./elastic-transport");
33
40
  dotenv.config();
34
41
  /**
35
42
  * Pino logger backend - singleton
36
43
  */
37
44
  let pinoLogger;
45
+ /**
46
+ * Elasticsearch transport instance - kept for cleanup
47
+ */
48
+ let esTransport = null;
49
+ /**
50
+ * Flag to track if shutdown handlers are registered
51
+ */
52
+ let shutdownHandlersRegistered = false;
53
+ /**
54
+ * Validates required Elasticsearch environment variables.
55
+ * @throws Error if required environment variables are missing.
56
+ */
57
+ function validateElasticsearchEnv() {
58
+ const required = [
59
+ 'ELASTICSEARCH_NODE',
60
+ 'ELASTICSEARCH_USERNAME',
61
+ 'ELASTICSEARCH_PASSWORD',
62
+ 'SERVER_NICKNAME',
63
+ ];
64
+ const missing = required.filter((key) => !process.env[key]);
65
+ if (missing.length > 0) {
66
+ throw new Error(`Missing required Elasticsearch environment variables: ${missing.join(', ')}`);
67
+ }
68
+ }
69
+ /**
70
+ * Safely parses an integer from an environment variable.
71
+ * @param envValue - The environment variable value to parse.
72
+ * @param defaultValue - The default value to use if parsing fails.
73
+ * @param varName - The name of the environment variable (for error messages).
74
+ * @returns The parsed integer or the default value.
75
+ * @throws Error if the value is not a valid number.
76
+ */
77
+ function parseIntEnv(envValue, defaultValue, varName) {
78
+ if (!envValue) {
79
+ return defaultValue;
80
+ }
81
+ const parsed = parseInt(envValue, 10);
82
+ if (isNaN(parsed)) {
83
+ throw new Error(`Invalid value for ${varName}: "${envValue}" is not a valid number. Using default: ${defaultValue}`);
84
+ }
85
+ if (parsed < 0) {
86
+ throw new Error(`Invalid value for ${varName}: "${envValue}" must be a positive number. Using default: ${defaultValue}`);
87
+ }
88
+ return parsed;
89
+ }
90
+ /**
91
+ * Registers shutdown handlers to flush logs before process exits.
92
+ */
93
+ function registerShutdownHandlers() {
94
+ if (shutdownHandlersRegistered) {
95
+ return;
96
+ }
97
+ let isShuttingDown = false;
98
+ const flushAndExit = async (signal, exitCode = 0) => {
99
+ // Prevent multiple shutdown attempts
100
+ if (isShuttingDown) {
101
+ return;
102
+ }
103
+ isShuttingDown = true;
104
+ if (esTransport) {
105
+ try {
106
+ // Flush any pending logs
107
+ await new Promise((resolve) => {
108
+ const timeout = setTimeout(() => {
109
+ console.error(`[Logger] Flush timeout after 5s on ${signal}`);
110
+ resolve();
111
+ }, 5000); // 5 second timeout
112
+ // Register listener BEFORE calling end() to avoid race condition
113
+ esTransport === null || esTransport === void 0 ? void 0 : esTransport.once('finish', () => {
114
+ clearTimeout(timeout);
115
+ resolve();
116
+ });
117
+ // Now trigger the flush
118
+ esTransport === null || esTransport === void 0 ? void 0 : esTransport.end();
119
+ });
120
+ }
121
+ catch (error) {
122
+ console.error(`[Logger] Error flushing logs on ${signal}:`, error);
123
+ }
124
+ }
125
+ process.exit(exitCode);
126
+ };
127
+ process.on('SIGTERM', () => {
128
+ flushAndExit('SIGTERM', 0).catch((err) => {
129
+ console.error('[Logger] Flush and exit failed:', err);
130
+ process.exit(1);
131
+ });
132
+ });
133
+ process.on('SIGINT', () => {
134
+ flushAndExit('SIGINT', 130).catch((err) => {
135
+ console.error('[Logger] Flush and exit failed:', err);
136
+ process.exit(1);
137
+ });
138
+ });
139
+ process.on('beforeExit', () => {
140
+ if (esTransport && !isShuttingDown) {
141
+ esTransport.end();
142
+ }
143
+ });
144
+ shutdownHandlersRegistered = true;
145
+ }
38
146
  /**
39
147
  * Creates a Pino logger instance with specified Elasticsearch configuration.
40
148
  * @param elasticConfig - Optional Elasticsearch configuration.
41
149
  * @returns The Pino logger instance.
150
+ * @throws Error if LOG_LEVEL is invalid or required environment variables are missing.
42
151
  */
43
152
  function getLogger(elasticConfig) {
44
153
  if (!pinoLogger) {
45
- if (isValidLogLevel(process.env.LOG_LEVEL)) {
46
- const transports = [];
47
- if (process.env.NODE_ENV !== 'local' && process.env.NODE_ENV !== 'test') {
48
- const esConfig = {
49
- index: process.env.SERVER_NICKNAME,
50
- node: process.env.ELASTICSEARCH_NODE,
51
- auth: {
52
- username: process.env.ELASTICSEARCH_USERNAME,
53
- password: process.env.ELASTICSEARCH_PASSWORD,
54
- },
55
- flushInterval: 1000,
56
- 'flush-bytes': 1000,
57
- };
58
- if (elasticConfig) {
59
- Object.assign(esConfig, elasticConfig);
60
- }
61
- const esTransport = (0, pino_elasticsearch_1.default)(esConfig);
62
- transports.push(esTransport);
63
- }
64
- else {
65
- transports.push(pino_1.pino.destination({
66
- minLength: 128,
67
- sync: false,
68
- }));
154
+ // Validate log level - will throw if invalid
155
+ const logLevel = process.env.LOG_LEVEL || 'info';
156
+ isValidLogLevel(logLevel);
157
+ const transports = [];
158
+ if (process.env.NODE_ENV !== 'local' && process.env.NODE_ENV !== 'test') {
159
+ // Validate Elasticsearch environment variables
160
+ validateElasticsearchEnv();
161
+ // Parse flush settings from environment with validation
162
+ const flushIntervalMs = parseIntEnv(process.env.ES_FLUSH_INTERVAL_MS, 2000, // Default: 2 seconds
163
+ 'ES_FLUSH_INTERVAL_MS');
164
+ const flushBytes = parseIntEnv(process.env.ES_FLUSH_BYTES, 100 * 1024, // Default: 100KB
165
+ 'ES_FLUSH_BYTES');
166
+ const maxRetries = parseIntEnv(process.env.ES_MAX_RETRIES, 3, // Default: 3 retries
167
+ 'ES_MAX_RETRIES');
168
+ const requestTimeout = parseIntEnv(process.env.ES_REQUEST_TIMEOUT_MS, 30000, // Default: 30 seconds
169
+ 'ES_REQUEST_TIMEOUT_MS');
170
+ // Safe to access after validation
171
+ const esConfig = {
172
+ index: process.env.SERVER_NICKNAME,
173
+ node: process.env.ELASTICSEARCH_NODE,
174
+ auth: {
175
+ username: process.env.ELASTICSEARCH_USERNAME,
176
+ password: process.env.ELASTICSEARCH_PASSWORD,
177
+ },
178
+ // Configurable flush settings
179
+ flushInterval: flushIntervalMs,
180
+ 'flush-bytes': flushBytes,
181
+ // Retry configuration for connection resilience
182
+ maxRetries: maxRetries,
183
+ requestTimeout: requestTimeout,
184
+ // Automatically reconnect on connection faults
185
+ sniffOnConnectionFault: true,
186
+ };
187
+ if (elasticConfig) {
188
+ Object.assign(esConfig, elasticConfig);
69
189
  }
70
- pinoLogger = (0, pino_1.pino)({
71
- level: process.env.LOG_LEVEL,
72
- timestamp: pino_1.stdTimeFunctions.isoTime.bind(pino_1.stdTimeFunctions),
73
- }, ...transports);
190
+ // Create transport with connection lifecycle fix (pino-elasticsearch #140)
191
+ esTransport = (0, elastic_transport_1.createElasticTransport)(esConfig);
192
+ // Handle Elasticsearch connection errors
193
+ esTransport.on('error', (err) => {
194
+ console.error('[Logger] Elasticsearch transport error:', err.message);
195
+ console.error('[Logger] Logs may not be reaching Kibana. Check Elasticsearch connection.');
196
+ });
197
+ // Handle insert errors (document indexing failures)
198
+ esTransport.on('insertError', (err) => {
199
+ console.error('[Logger] Elasticsearch insert error:', err.message);
200
+ console.error('[Logger] Some logs failed to index to Elasticsearch.');
201
+ });
202
+ // Log successful connection (for debugging)
203
+ esTransport.on('insert', () => {
204
+ // Uncomment for debugging: console.log(`[Logger] Successfully inserted log entries`)
205
+ });
206
+ // Register shutdown handlers to flush logs on process exit
207
+ registerShutdownHandlers();
208
+ transports.push(esTransport);
74
209
  }
210
+ else {
211
+ transports.push(pino_1.pino.destination({
212
+ minLength: 1024,
213
+ sync: true,
214
+ }));
215
+ }
216
+ pinoLogger = (0, pino_1.pino)({
217
+ level: logLevel,
218
+ timestamp: pino_1.stdTimeFunctions.isoTime.bind(pino_1.stdTimeFunctions),
219
+ }, ...transports);
75
220
  }
76
221
  return pinoLogger;
77
222
  }
@@ -86,7 +231,6 @@ function isValidLogLevel(level) {
86
231
  }
87
232
  return true;
88
233
  }
89
- exports.isValidLogLevel = isValidLogLevel;
90
234
  /**
91
235
  * Logger Wrapper.
92
236
  * Wraps a Pino logger instance and provides logging methods.
@@ -102,8 +246,7 @@ class Logger {
102
246
  detail = args;
103
247
  }
104
248
  else {
105
- //@ts-ignore
106
- detail = JSON.stringify(...args);
249
+ detail = JSON.stringify(args);
107
250
  }
108
251
  this._logger[logLevel]({
109
252
  component: this._name,
package/dist/lib/pdf.d.ts CHANGED
@@ -9,7 +9,7 @@ declare class Pdf {
9
9
  * // => 'Hello-World'
10
10
  * ```
11
11
  * */
12
- getBase64Images: (url: string) => string;
12
+ getBase64Images: (_url: string) => string;
13
13
  }
14
14
  declare const pdf: Pdf;
15
15
  export default pdf;
package/dist/lib/pdf.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ // import { AxiosInstance } from '../utilities'
3
4
  /*
4
5
  Author : Mustafa Halabi https://github.com/mustafahalabi
5
6
  Date : 2023-06-24
@@ -18,7 +19,7 @@ class Pdf {
18
19
  * // => 'Hello-World'
19
20
  * ```
20
21
  * */
21
- this.getBase64Images = (url) => {
22
+ this.getBase64Images = (_url) => {
22
23
  return '';
23
24
  };
24
25
  }
@@ -44,4 +44,21 @@ export interface ElasticConfig {
44
44
  * The interval (in milliseconds) at which logs are flushed to Elasticsearch.
45
45
  */
46
46
  flushInterval?: number;
47
+ /**
48
+ * The number of bytes to buffer before flushing to Elasticsearch.
49
+ */
50
+ 'flush-bytes'?: number;
51
+ /**
52
+ * Maximum number of retries for failed Elasticsearch requests.
53
+ */
54
+ maxRetries?: number;
55
+ /**
56
+ * Request timeout in milliseconds before considering a request failed.
57
+ */
58
+ requestTimeout?: number;
59
+ /**
60
+ * Whether to sniff for additional Elasticsearch nodes on connection fault.
61
+ * Enables automatic reconnection when a node fails.
62
+ */
63
+ sniffOnConnectionFault?: boolean;
47
64
  }
@@ -0,0 +1,64 @@
1
+ // @ts-check
2
+ import eslint from '@eslint/js'
3
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
4
+ import globals from 'globals'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ export default tseslint.config(
8
+ {
9
+ ignores: [
10
+ 'eslint.config.mjs',
11
+ 'dist/**',
12
+ 'node_modules/**',
13
+ 'coverage/**',
14
+ '*.d.ts',
15
+ ],
16
+ },
17
+ eslint.configs.recommended,
18
+ ...tseslint.configs.recommendedTypeChecked,
19
+ eslintPluginPrettierRecommended,
20
+ {
21
+ languageOptions: {
22
+ globals: {
23
+ ...globals.node,
24
+ },
25
+ sourceType: 'module',
26
+ parserOptions: {
27
+ projectService: true,
28
+ tsconfigRootDir: import.meta.dirname,
29
+ },
30
+ },
31
+ },
32
+ {
33
+ rules: {
34
+ '@typescript-eslint/no-explicit-any': 'off',
35
+ '@typescript-eslint/no-floating-promises': 'warn',
36
+ '@typescript-eslint/no-unsafe-argument': 'off',
37
+ '@typescript-eslint/no-unsafe-assignment': 'off',
38
+ '@typescript-eslint/no-unsafe-return': 'off',
39
+ '@typescript-eslint/no-unsafe-member-access': 'off',
40
+ '@typescript-eslint/no-unsafe-call': 'off',
41
+ '@typescript-eslint/ban-ts-comment': 'off',
42
+ '@typescript-eslint/require-await': 'off',
43
+ '@typescript-eslint/await-thenable': 'off',
44
+ '@typescript-eslint/no-unsafe-enum-comparison': 'off',
45
+ '@typescript-eslint/no-base-to-string': 'off',
46
+ '@typescript-eslint/no-redundant-type-constituents': 'off',
47
+ '@typescript-eslint/no-unused-vars': [
48
+ 'error',
49
+ {
50
+ argsIgnorePattern: '^_',
51
+ varsIgnorePattern: '^_',
52
+ },
53
+ ],
54
+ 'no-console': 'error',
55
+ 'prettier/prettier': ['error', { semi: false }],
56
+ },
57
+ },
58
+ {
59
+ files: ['src/lib/logger.ts'],
60
+ rules: {
61
+ 'no-console': 'off',
62
+ },
63
+ }
64
+ )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhmdhammoud/meritt-utils",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
@@ -25,25 +25,31 @@
25
25
  "test:coverage": "jest --coverage"
26
26
  },
27
27
  "devDependencies": {
28
- "@types/jest": "^27.5.2",
29
- "@typescript-eslint/eslint-plugin": "^5.17.0",
30
- "@typescript-eslint/parser": "^5.17.0",
31
- "eslint": "^8.12.0",
32
- "eslint-plugin-tsdoc": "^0.2.14",
33
- "husky": "^8.0.0",
28
+ "@eslint/js": "^9.31.0",
29
+ "@types/jest": "^29.5.12",
30
+ "eslint": "^9.31.0",
31
+ "eslint-config-prettier": "^10.1.8",
32
+ "eslint-plugin-prettier": "^5.5.3",
33
+ "eslint-plugin-tsdoc": "^0.5.2",
34
+ "globals": "^16.0.0",
35
+ "husky": "^9.1.6",
34
36
  "jest": "^29.7.0",
35
37
  "pino-pretty": "^10.3.1",
38
+ "prettier": "^3.3.3",
36
39
  "ts-jest": "^29.1.1",
37
- "ts-node-dev": "^1.1.8",
38
- "typescript": "^4.6.3"
40
+ "ts-node-dev": "^2.0.0",
41
+ "typescript": "^5.3.3",
42
+ "typescript-eslint": "^8.20.0"
39
43
  },
40
44
  "author": "Mhmdhammoud",
41
45
  "license": "ISC",
42
46
  "dependencies": {
47
+ "@elastic/elasticsearch": "^9.3.2",
43
48
  "axios": "^1.4.0",
44
49
  "dotenv": "^16.4.1",
45
50
  "imagesloaded": "^5.0.0",
46
51
  "pino": "^8.19.0",
47
- "pino-elasticsearch": "^8.0.0"
52
+ "pino-elasticsearch": "^8.0.0",
53
+ "split2": "^4.2.0"
48
54
  }
49
55
  }
@@ -1,4 +1,4 @@
1
- import {Colorful} from '../lib'
1
+ import { Colorful } from '../lib'
2
2
 
3
3
  describe('Colorful Class', () => {
4
4
  describe('rgbToHex method', () => {
@@ -1,4 +1,4 @@
1
- import {Formatter} from '../lib'
1
+ import { Formatter } from '../lib'
2
2
 
3
3
  describe('Formatter', () => {
4
4
  describe('toUpperFirst method', () => {
@@ -1,5 +1,5 @@
1
- import Logger, {isValidLogLevel} from '../lib/logger'
2
- import {pino} from 'pino'
1
+ import Logger, { isValidLogLevel } from '../lib/logger'
2
+ import { pino } from 'pino'
3
3
 
4
4
  jest.mock('pino')
5
5
 
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Elasticsearch transport for Pino with connection lifecycle resilience.
3
+ *
4
+ * Based on pino-elasticsearch with a fix for GitHub issue #140:
5
+ * When maxRetries are exceeded and Elasticsearch nodes are DEAD, the bulk helper
6
+ * destroys the splitter stream, causing logs to stop permanently until restart.
7
+ *
8
+ * This implementation overrides splitter.destroy to BOTH resurrect the connection
9
+ * pool AND reinitialize the bulk handler, so logging continues after ES recovers.
10
+ *
11
+ * @see https://github.com/pinojs/pino-elasticsearch/issues/140
12
+ * @see https://github.com/pinojs/pino-elasticsearch/issues/72
13
+ */
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
16
+ const split = require('split2') as (
17
+ fn: (line: string) => unknown,
18
+ opts?: { autoDestroy?: boolean }
19
+ ) => NodeJS.ReadWriteStream
20
+ import { Readable } from 'stream'
21
+ import { Client } from '@elastic/elasticsearch'
22
+ import type { ClientOptions } from '@elastic/elasticsearch'
23
+
24
+ export interface ElasticTransportOptions extends Pick<
25
+ ClientOptions,
26
+ | 'node'
27
+ | 'auth'
28
+ | 'cloud'
29
+ | 'caFingerprint'
30
+ | 'Connection'
31
+ | 'ConnectionPool'
32
+ | 'maxRetries'
33
+ | 'requestTimeout'
34
+ > {
35
+ sniffOnConnectionFault?: boolean
36
+ index?: string | ((logTime: string) => string)
37
+ flushBytes?: number
38
+ 'flush-bytes'?: number
39
+ flushInterval?: number
40
+ 'flush-interval'?: number
41
+ esVersion?: number
42
+ 'es-version'?: number
43
+ rejectUnauthorized?: boolean
44
+ tls?: ClientOptions['tls']
45
+ }
46
+
47
+ interface LogDocument {
48
+ time?: string
49
+ '@timestamp'?: string
50
+ [k: string]: unknown
51
+ }
52
+
53
+ function setDateTimeString(value: unknown): string {
54
+ if (value !== null && typeof value === 'object' && 'time' in value) {
55
+ const t = (value as { time: unknown }).time
56
+ if (
57
+ (typeof t === 'string' && t.length > 0) ||
58
+ (typeof t === 'number' && t >= 0)
59
+ ) {
60
+ return new Date(t).toISOString()
61
+ }
62
+ }
63
+ return new Date().toISOString()
64
+ }
65
+
66
+ function getIndexName(
67
+ index: string | ((logTime: string) => string),
68
+ time: string
69
+ ): string {
70
+ if (typeof index === 'function') {
71
+ return index(time)
72
+ }
73
+ return index.replace('%{DATE}', time.substring(0, 10))
74
+ }
75
+
76
+ function initializeBulkHandler(
77
+ opts: ElasticTransportOptions,
78
+ client: Client,
79
+ splitter: NodeJS.ReadWriteStream
80
+ ): void {
81
+ const esVersion = Number(opts.esVersion ?? opts['es-version'] ?? 7)
82
+ const index = opts.index ?? 'pino'
83
+ const buildIndexName = typeof index === 'function' ? index : null
84
+ const opType = esVersion >= 7 ? undefined : undefined
85
+
86
+ // CRITICAL FIX (issue #140): When bulk helper destroys stream after retries exhausted,
87
+ // we must BOTH resurrect the pool AND reinitialize the bulk handler so logging continues.
88
+ // connectionPool.resurrect exists at runtime (elastic-transport) but may not be in types
89
+ const pool = client.connectionPool as {
90
+ resurrect?: (opts: { name: string }) => void
91
+ }
92
+ const splitterWithDestroy = splitter as NodeJS.ReadWriteStream & {
93
+ destroy: (err?: Error) => void
94
+ }
95
+ splitterWithDestroy.destroy = function () {
96
+ if (typeof pool.resurrect === 'function') {
97
+ pool.resurrect({ name: 'elasticsearch-js' })
98
+ }
99
+ // Reinitialize bulk handler - without this, logging stops permanently until restart
100
+ initializeBulkHandler(opts, client, splitter)
101
+ }
102
+
103
+ const indexName = (time = new Date().toISOString()) =>
104
+ buildIndexName ? buildIndexName(time) : getIndexName(index as string, time)
105
+
106
+ const bulkInsert = client.helpers.bulk({
107
+ datasource: splitter as unknown as Readable,
108
+ flushBytes: opts.flushBytes ?? opts['flush-bytes'] ?? 1000,
109
+ flushInterval: opts.flushInterval ?? opts['flush-interval'] ?? 3000,
110
+ refreshOnCompletion: indexName(),
111
+ onDocument(doc: unknown) {
112
+ const d = doc as LogDocument
113
+ const date = d.time ?? d['@timestamp'] ?? new Date().toISOString()
114
+ if (opType === 'create') {
115
+ d['@timestamp'] = date
116
+ }
117
+ return {
118
+ index: {
119
+ _index: indexName(date),
120
+ op_type: opType,
121
+ },
122
+ }
123
+ },
124
+ onDrop(doc: unknown) {
125
+ const error = new Error('Dropped document') as Error & {
126
+ document: unknown
127
+ }
128
+ error.document = doc
129
+ splitter.emit('insertError', error)
130
+ },
131
+ })
132
+
133
+ bulkInsert.then(
134
+ (stats) => splitter.emit('insert', stats),
135
+ (err) => splitter.emit('error', err)
136
+ )
137
+ }
138
+
139
+ export const createElasticTransport = (
140
+ opts: ElasticTransportOptions = {}
141
+ ): NodeJS.ReadWriteStream => {
142
+ const splitter = split(
143
+ function (this: NodeJS.ReadWriteStream, line: string) {
144
+ let value: unknown
145
+
146
+ try {
147
+ value = JSON.parse(line) as unknown
148
+ } catch (error) {
149
+ this.emit('unknown', line, error)
150
+ return
151
+ }
152
+
153
+ if (typeof value === 'boolean') {
154
+ this.emit('unknown', line, 'Boolean value ignored')
155
+ return
156
+ }
157
+ if (value === null) {
158
+ this.emit('unknown', line, 'Null value ignored')
159
+ return
160
+ }
161
+ if (typeof value !== 'object') {
162
+ value = { data: value, time: setDateTimeString(value) }
163
+ } else {
164
+ const obj = value as Record<string, unknown>
165
+ if (obj['@timestamp'] === undefined) {
166
+ ;(obj as LogDocument).time = setDateTimeString(obj)
167
+ }
168
+ }
169
+ return value
170
+ },
171
+ { autoDestroy: true }
172
+ )
173
+
174
+ const clientOpts: ClientOptions = {
175
+ node: opts.node,
176
+ auth: opts.auth,
177
+ cloud: opts.cloud,
178
+ tls: { rejectUnauthorized: opts.rejectUnauthorized, ...opts.tls },
179
+ maxRetries: opts.maxRetries,
180
+ requestTimeout: opts.requestTimeout,
181
+ sniffOnConnectionFault: opts.sniffOnConnectionFault,
182
+ }
183
+
184
+ if (opts.caFingerprint) {
185
+ clientOpts.caFingerprint = opts.caFingerprint
186
+ }
187
+ if (opts.Connection) {
188
+ clientOpts.Connection = opts.Connection
189
+ }
190
+ if (opts.ConnectionPool) {
191
+ clientOpts.ConnectionPool = opts.ConnectionPool
192
+ }
193
+
194
+ const client = new Client(clientOpts)
195
+
196
+ client.diagnostic.on('resurrect', () => {
197
+ initializeBulkHandler(opts, client, splitter)
198
+ })
199
+
200
+ initializeBulkHandler(opts, client, splitter)
201
+
202
+ return splitter
203
+ }
@@ -28,7 +28,7 @@ class ImageFull {
28
28
  return new Promise((resolve) => {
29
29
  imagesLoaded(
30
30
  document.querySelectorAll(selector),
31
- {background: true},
31
+ { background: true },
32
32
  (event) => {
33
33
  resolve(event)
34
34
  }
package/src/lib/index.ts CHANGED
@@ -1,6 +1,6 @@
1
- export {default as Crypto} from './cypto'
2
- export {default as Formatter} from './formatter'
3
- export {default as Pdf} from './formatter'
4
- export {default as Colorful} from './colorful'
5
- export {default as ImageFull} from './imagefull'
6
- export {default as Logger} from './logger'
1
+ export { default as Crypto } from './cypto'
2
+ export { default as Formatter } from './formatter'
3
+ export { default as Pdf } from './formatter'
4
+ export { default as Colorful } from './colorful'
5
+ export { default as ImageFull } from './imagefull'
6
+ export { default as Logger } from './logger'