@mhmdhammoud/meritt-utils 1.6.2 → 1.6.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.
- package/ReleaseNotes.md +17 -1
- package/dist/__tests__/elastic-transport.test.js +13 -0
- package/dist/__tests__/logger.test.js +21 -0
- package/dist/lib/elastic-transport.js +3 -1
- package/dist/lib/logger.js +4 -3
- package/dist/types/logger.d.ts +2 -1
- package/package.json +9 -3
- package/.github/workflows/npm-publish.yml +0 -84
- package/.github/workflows/push.yml +0 -83
- package/.husky/pre-commit +0 -62
- package/.prettierrc +0 -8
- package/.prettierrc.json +0 -8
- package/ISSUES.md +0 -0
- package/eslint.config.mjs +0 -64
- package/example.env +0 -6
- package/jest.config.js +0 -13
- package/src/__tests__/colorful.test.ts +0 -77
- package/src/__tests__/elastic-transport.test.ts +0 -94
- package/src/__tests__/formatter.test.ts +0 -96
- package/src/__tests__/logger.test.ts +0 -122
- package/src/env.d.ts +0 -9
- package/src/index.ts +0 -1
- package/src/lib/colorful.ts +0 -167
- package/src/lib/cypto.ts +0 -296
- package/src/lib/elastic-transport.ts +0 -356
- package/src/lib/formatter.ts +0 -125
- package/src/lib/imagefull.ts +0 -41
- package/src/lib/index.ts +0 -12
- package/src/lib/logger.ts +0 -600
- package/src/lib/pdf.ts +0 -25
- package/src/lib/trace-store.ts +0 -46
- package/src/types/index.ts +0 -1
- package/src/types/logger.ts +0 -66
- package/src/utilities/axios.ts +0 -4
- package/src/utilities/index.ts +0 -1
- package/tsconfig.json +0 -16
package/ReleaseNotes.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## Version 1.6.3
|
|
4
|
+
|
|
5
|
+
### Elasticsearch Proxy Logging Fix
|
|
6
|
+
|
|
7
|
+
- Disabled Elasticsearch node sniffing by default. Sniffing is unsafe when the
|
|
8
|
+
configured endpoint is an HTTPS proxy because discovered nodes may advertise
|
|
9
|
+
internal addresses or a different protocol.
|
|
10
|
+
- Preserved explicit opt-in through `sniffOnConnectionFault: true` for direct
|
|
11
|
+
multi-node Elasticsearch deployments.
|
|
12
|
+
- Sanitized dots and whitespace from recursive context field names.
|
|
13
|
+
- Replaced empty context field names with `unnamed_field`.
|
|
14
|
+
- Added regression coverage for proxy-safe transport configuration and context
|
|
15
|
+
field sanitization.
|
|
16
|
+
|
|
3
17
|
## Version 1.5.3
|
|
4
18
|
|
|
5
19
|
### Logger Improvements - Critical Bug Fixes & Production Reliability
|
|
@@ -34,7 +48,9 @@
|
|
|
34
48
|
- Handles `SIGTERM` and `SIGINT` signals
|
|
35
49
|
- 5-second timeout prevents hanging forever
|
|
36
50
|
- Proper async/await handling to ensure flush completes
|
|
37
|
-
- **
|
|
51
|
+
- **Node discovery**: `sniffOnConnectionFault` is available for direct
|
|
52
|
+
multi-node Elasticsearch deployments. It should remain disabled behind a
|
|
53
|
+
proxy because discovered node addresses may not be externally reachable.
|
|
38
54
|
- **Retry logic**: Configurable retry attempts with timeout for failed requests
|
|
39
55
|
|
|
40
56
|
#### Type Safety
|
|
@@ -15,6 +15,19 @@ describe('elastic transport resilience', () => {
|
|
|
15
15
|
jest.useRealTimers();
|
|
16
16
|
MockedClient.mockReset();
|
|
17
17
|
});
|
|
18
|
+
test('disables node sniffing by default', () => {
|
|
19
|
+
MockedClient.mockImplementation(() => ({ bulk: jest.fn() }));
|
|
20
|
+
(0, elastic_transport_1.createElasticTransport)({ index: 'logs' });
|
|
21
|
+
expect(MockedClient).toHaveBeenCalledWith(expect.objectContaining({ sniffOnConnectionFault: false }));
|
|
22
|
+
});
|
|
23
|
+
test('allows node sniffing only when explicitly enabled', () => {
|
|
24
|
+
MockedClient.mockImplementation(() => ({ bulk: jest.fn() }));
|
|
25
|
+
(0, elastic_transport_1.createElasticTransport)({
|
|
26
|
+
index: 'logs',
|
|
27
|
+
sniffOnConnectionFault: true,
|
|
28
|
+
});
|
|
29
|
+
expect(MockedClient).toHaveBeenCalledWith(expect.objectContaining({ sniffOnConnectionFault: true }));
|
|
30
|
+
});
|
|
18
31
|
test('reports bulk failures without emitting terminal stream errors', async () => {
|
|
19
32
|
const bulk = jest
|
|
20
33
|
.fn()
|
|
@@ -132,4 +132,25 @@ describe('route and format logs', () => {
|
|
|
132
132
|
_id: expect.anything(),
|
|
133
133
|
}));
|
|
134
134
|
});
|
|
135
|
+
test('sanitize empty, dotted, and whitespace context field names', () => {
|
|
136
|
+
//@ts-ignore
|
|
137
|
+
jest.spyOn(pino_1.pino, 'destination').mockReturnValue(PINO_DESTINATION);
|
|
138
|
+
//@ts-ignore
|
|
139
|
+
pino_1.pino.mockReturnValue(PINO);
|
|
140
|
+
const logger = new logger_1.default(LOGGER_NAME);
|
|
141
|
+
logger.info(LOG_EVENT, {
|
|
142
|
+
'': 'empty',
|
|
143
|
+
'field.with.dots': 'dotted',
|
|
144
|
+
' field name ': 'spaced',
|
|
145
|
+
nested: { ' ': 'nested-empty' },
|
|
146
|
+
});
|
|
147
|
+
expect(PINO.info).toHaveBeenCalledWith(expect.objectContaining({
|
|
148
|
+
context: {
|
|
149
|
+
unnamed_field: 'empty',
|
|
150
|
+
fieldwithdots: 'dotted',
|
|
151
|
+
fieldname: 'spaced',
|
|
152
|
+
nested: { unnamed_field: 'nested-empty' },
|
|
153
|
+
},
|
|
154
|
+
}));
|
|
155
|
+
});
|
|
135
156
|
});
|
|
@@ -204,6 +204,7 @@ function createBulkSender(opts, client, splitter) {
|
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
206
|
const createElasticTransport = (opts = {}) => {
|
|
207
|
+
var _a;
|
|
207
208
|
const splitter = split(function (line) {
|
|
208
209
|
let value;
|
|
209
210
|
try {
|
|
@@ -240,7 +241,8 @@ const createElasticTransport = (opts = {}) => {
|
|
|
240
241
|
tls: { rejectUnauthorized: opts.rejectUnauthorized, ...opts.tls },
|
|
241
242
|
maxRetries: opts.maxRetries,
|
|
242
243
|
requestTimeout: opts.requestTimeout,
|
|
243
|
-
|
|
244
|
+
// Keep the configured proxy endpoint unless callers explicitly opt in.
|
|
245
|
+
sniffOnConnectionFault: (_a = opts.sniffOnConnectionFault) !== null && _a !== void 0 ? _a : false,
|
|
244
246
|
};
|
|
245
247
|
if (opts.caFingerprint) {
|
|
246
248
|
clientOpts.caFingerprint = opts.caFingerprint;
|
package/dist/lib/logger.js
CHANGED
|
@@ -59,7 +59,8 @@ const RESERVED_ES_FIELD_ALIASES = {
|
|
|
59
59
|
};
|
|
60
60
|
/** Convert arbitrary field names into ES-safe flattened keys */
|
|
61
61
|
const toSafeElasticFieldName = (key) => {
|
|
62
|
-
const
|
|
62
|
+
const cleaned = key.replace(/[.\s]+/g, '');
|
|
63
|
+
const normalized = toSnakeCase(cleaned) || 'unnamed_field';
|
|
63
64
|
const mapped = RESERVED_ES_FIELD_ALIASES[normalized];
|
|
64
65
|
if (mapped) {
|
|
65
66
|
return mapped;
|
|
@@ -290,8 +291,8 @@ function getLogger(elasticConfig) {
|
|
|
290
291
|
// Retry configuration for connection resilience
|
|
291
292
|
maxRetries: maxRetries,
|
|
292
293
|
requestTimeout: requestTimeout,
|
|
293
|
-
//
|
|
294
|
-
sniffOnConnectionFault:
|
|
294
|
+
// Sniffing can bypass proxies and discover unreachable internal nodes.
|
|
295
|
+
sniffOnConnectionFault: false,
|
|
295
296
|
};
|
|
296
297
|
if (elasticConfig) {
|
|
297
298
|
Object.assign(esConfig, elasticConfig);
|
package/dist/types/logger.d.ts
CHANGED
|
@@ -58,7 +58,8 @@ export interface ElasticConfig {
|
|
|
58
58
|
requestTimeout?: number;
|
|
59
59
|
/**
|
|
60
60
|
* Whether to sniff for additional Elasticsearch nodes on connection fault.
|
|
61
|
-
*
|
|
61
|
+
* Defaults to false because sniffing can bypass a proxy and discover
|
|
62
|
+
* internal node addresses that are unreachable or use a different protocol.
|
|
62
63
|
*/
|
|
63
64
|
sniffOnConnectionFault?: boolean;
|
|
64
65
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhmdhammoud/meritt-utils",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"private": false,
|
|
7
7
|
"typings": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"imagesloaded.d.ts",
|
|
11
|
+
"README.md",
|
|
12
|
+
"ReleaseNotes.md"
|
|
13
|
+
],
|
|
8
14
|
"repository": {
|
|
9
15
|
"type": "git",
|
|
10
|
-
"url": "git@github.com
|
|
16
|
+
"url": "git+ssh://git@github.com/Mhmdhammoud/meritt-utils.git"
|
|
11
17
|
},
|
|
12
18
|
"publishConfig": {
|
|
13
19
|
"access": "public"
|
|
@@ -18,7 +24,7 @@
|
|
|
18
24
|
"watch": "tsc --watch",
|
|
19
25
|
"build": "tsc",
|
|
20
26
|
"test:types": "tsc --noemit",
|
|
21
|
-
"prepare": "husky
|
|
27
|
+
"prepare": "husky",
|
|
22
28
|
"lint": "eslint src --fix",
|
|
23
29
|
"prepublish": "npm run build",
|
|
24
30
|
"test": "jest",
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
name: NPM Publish on Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [master]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
test:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
steps:
|
|
11
|
-
- name: Checkout code
|
|
12
|
-
uses: actions/checkout@v4
|
|
13
|
-
|
|
14
|
-
- name: Setup Node.js
|
|
15
|
-
uses: actions/setup-node@v4
|
|
16
|
-
with:
|
|
17
|
-
node-version: '20'
|
|
18
|
-
cache: 'npm'
|
|
19
|
-
|
|
20
|
-
- name: Install dependencies
|
|
21
|
-
run: npm ci
|
|
22
|
-
|
|
23
|
-
- name: Run type check
|
|
24
|
-
run: npm run test:types
|
|
25
|
-
|
|
26
|
-
- name: Run linting
|
|
27
|
-
run: npm run lint
|
|
28
|
-
|
|
29
|
-
- name: Run tests
|
|
30
|
-
run: npm test
|
|
31
|
-
|
|
32
|
-
build:
|
|
33
|
-
needs: test
|
|
34
|
-
runs-on: ubuntu-latest
|
|
35
|
-
steps:
|
|
36
|
-
- name: Checkout code
|
|
37
|
-
uses: actions/checkout@v4
|
|
38
|
-
|
|
39
|
-
- name: Setup Node.js
|
|
40
|
-
uses: actions/setup-node@v4
|
|
41
|
-
with:
|
|
42
|
-
node-version: '20'
|
|
43
|
-
cache: 'npm'
|
|
44
|
-
|
|
45
|
-
- name: Install dependencies
|
|
46
|
-
run: npm ci
|
|
47
|
-
|
|
48
|
-
- name: Build project
|
|
49
|
-
run: npm run build
|
|
50
|
-
|
|
51
|
-
- name: Upload build artifacts
|
|
52
|
-
uses: actions/upload-artifact@v4
|
|
53
|
-
with:
|
|
54
|
-
name: dist
|
|
55
|
-
path: dist/
|
|
56
|
-
|
|
57
|
-
publish-npm:
|
|
58
|
-
needs: [test, build]
|
|
59
|
-
runs-on: ubuntu-latest
|
|
60
|
-
if: success()
|
|
61
|
-
steps:
|
|
62
|
-
- name: Checkout code
|
|
63
|
-
uses: actions/checkout@v4
|
|
64
|
-
|
|
65
|
-
- name: Setup Node.js
|
|
66
|
-
uses: actions/setup-node@v4
|
|
67
|
-
with:
|
|
68
|
-
node-version: '20'
|
|
69
|
-
registry-url: https://registry.npmjs.org/
|
|
70
|
-
cache: 'npm'
|
|
71
|
-
|
|
72
|
-
- name: Install dependencies
|
|
73
|
-
run: npm ci
|
|
74
|
-
|
|
75
|
-
- name: Download build artifacts
|
|
76
|
-
uses: actions/download-artifact@v4
|
|
77
|
-
with:
|
|
78
|
-
name: dist
|
|
79
|
-
path: dist/
|
|
80
|
-
|
|
81
|
-
- name: Publish to NPM
|
|
82
|
-
run: npm publish
|
|
83
|
-
env:
|
|
84
|
-
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
name: CI Pipeline
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [dev]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [dev, master]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
lint-and-test:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- name: Checkout code
|
|
14
|
-
uses: actions/checkout@v4
|
|
15
|
-
|
|
16
|
-
- name: Setup Node.js
|
|
17
|
-
uses: actions/setup-node@v4
|
|
18
|
-
with:
|
|
19
|
-
node-version: '20'
|
|
20
|
-
cache: 'npm'
|
|
21
|
-
|
|
22
|
-
- name: Install dependencies
|
|
23
|
-
run: npm ci
|
|
24
|
-
|
|
25
|
-
- name: Run type check
|
|
26
|
-
run: npm run test:types
|
|
27
|
-
|
|
28
|
-
- name: Run linting
|
|
29
|
-
run: npm run lint
|
|
30
|
-
|
|
31
|
-
- name: Run tests with coverage
|
|
32
|
-
run: npm run test:coverage
|
|
33
|
-
|
|
34
|
-
- name: Upload coverage reports
|
|
35
|
-
uses: actions/upload-artifact@v4
|
|
36
|
-
if: always()
|
|
37
|
-
with:
|
|
38
|
-
name: coverage-report
|
|
39
|
-
path: coverage/
|
|
40
|
-
|
|
41
|
-
build:
|
|
42
|
-
needs: lint-and-test
|
|
43
|
-
runs-on: ubuntu-latest
|
|
44
|
-
steps:
|
|
45
|
-
- name: Checkout code
|
|
46
|
-
uses: actions/checkout@v4
|
|
47
|
-
|
|
48
|
-
- name: Setup Node.js
|
|
49
|
-
uses: actions/setup-node@v4
|
|
50
|
-
with:
|
|
51
|
-
node-version: '20'
|
|
52
|
-
cache: 'npm'
|
|
53
|
-
|
|
54
|
-
- name: Install dependencies
|
|
55
|
-
run: npm ci
|
|
56
|
-
|
|
57
|
-
- name: Build project
|
|
58
|
-
run: npm run build
|
|
59
|
-
|
|
60
|
-
- name: Upload build artifacts
|
|
61
|
-
uses: actions/upload-artifact@v4
|
|
62
|
-
with:
|
|
63
|
-
name: dist
|
|
64
|
-
path: dist/
|
|
65
|
-
|
|
66
|
-
security-audit:
|
|
67
|
-
runs-on: ubuntu-latest
|
|
68
|
-
steps:
|
|
69
|
-
- name: Checkout code
|
|
70
|
-
uses: actions/checkout@v4
|
|
71
|
-
|
|
72
|
-
- name: Setup Node.js
|
|
73
|
-
uses: actions/setup-node@v4
|
|
74
|
-
with:
|
|
75
|
-
node-version: '20'
|
|
76
|
-
cache: 'npm'
|
|
77
|
-
|
|
78
|
-
- name: Install dependencies
|
|
79
|
-
run: npm ci
|
|
80
|
-
|
|
81
|
-
- name: Run security audit
|
|
82
|
-
run: npm audit --audit-level=moderate
|
|
83
|
-
|
package/.husky/pre-commit
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
. "$(dirname -- "$0")/_/husky.sh"
|
|
3
|
-
|
|
4
|
-
red='\033[0;31m'
|
|
5
|
-
green='\033[0;32m'
|
|
6
|
-
yellow='\033[0;33m'
|
|
7
|
-
blue='\033[0;34m'
|
|
8
|
-
magenta='\033[0;35m'
|
|
9
|
-
cyan='\033[0;36m'
|
|
10
|
-
clear='\033[0m'
|
|
11
|
-
echo "
|
|
12
|
-
|
|
13
|
-
__ __ ______ _____ _____ _______ _______ _____ ________ __
|
|
14
|
-
| \/ | ____| __ \|_ _|__ __|__ __| | __ \| ____\ \ / /
|
|
15
|
-
| \ / | |__ | |__) | | | | | | | | | | | |__ \ \ / /
|
|
16
|
-
| |\/| | __| | _ / | | | | | | | | | | __| \ \/ /
|
|
17
|
-
| | | | |____| | \ \ _| |_ | | | | | |__| | |____ \ /
|
|
18
|
-
|_| |_|______|_| \_\_____| |_| |_| |_____/|______| \/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
echo -e "Let's hope you are not ${red}DUMB${clear}!"
|
|
23
|
-
|
|
24
|
-
echo -ne '\n'
|
|
25
|
-
|
|
26
|
-
echo -e "${yellow}Or Are You ??${clear}!"
|
|
27
|
-
echo -ne '\n'
|
|
28
|
-
echo -e "${yellow}Running pre-commit hook...${clear}"
|
|
29
|
-
echo -ne '\n'
|
|
30
|
-
echo -ne '\n'
|
|
31
|
-
|
|
32
|
-
echo -ne '##################### (25%)\r'
|
|
33
|
-
sleep 0.3
|
|
34
|
-
echo -ne '############################## (35%)\r'
|
|
35
|
-
sleep 0.3
|
|
36
|
-
echo -ne '###################################### (45%)\r'
|
|
37
|
-
sleep 0.3
|
|
38
|
-
echo -ne '############################################################ (77%)\r'
|
|
39
|
-
sleep 0.3
|
|
40
|
-
echo -ne '################################################################## (100%)\r'
|
|
41
|
-
|
|
42
|
-
echo -ne '\n'
|
|
43
|
-
echo -ne '\n'
|
|
44
|
-
echo -ne '\n'
|
|
45
|
-
|
|
46
|
-
echo -e "${yellow}Running types...${clear}"
|
|
47
|
-
echo -ne '\n'
|
|
48
|
-
npm run test:types
|
|
49
|
-
echo -ne '\n'
|
|
50
|
-
|
|
51
|
-
echo -e "${green}types passed...${clear} ✅"
|
|
52
|
-
echo -ne '\n'
|
|
53
|
-
echo -e "${yellow}Running linting...${clear}"
|
|
54
|
-
|
|
55
|
-
npm run lint
|
|
56
|
-
echo -e "${green}linting passed...${clear} ✅"
|
|
57
|
-
|
|
58
|
-
echo -ne '\n'
|
|
59
|
-
|
|
60
|
-
echo -e "${yellow}Seems like you are a${clear} ${green}CLEVER DEVELOPER${clear} 👌"
|
|
61
|
-
echo -ne '\n'
|
|
62
|
-
echo -e "${green}GOOD TO GO${clear} 🚀🚀🚀"
|
package/.prettierrc
DELETED
package/.prettierrc.json
DELETED
package/ISSUES.md
DELETED
|
File without changes
|
package/eslint.config.mjs
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
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/example.env
DELETED
package/jest.config.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
-
module.exports = {
|
|
3
|
-
preset: 'ts-jest',
|
|
4
|
-
testEnvironment: 'node',
|
|
5
|
-
coverageDirectory: 'coverage',
|
|
6
|
-
coverageProvider: 'v8',
|
|
7
|
-
collectCoverageFrom: ['src/**/*.ts', '!test/**/*.ts'],
|
|
8
|
-
transform: {
|
|
9
|
-
'^.+\\.(ts)$': 'ts-jest',
|
|
10
|
-
},
|
|
11
|
-
transformIgnorePatterns: [],
|
|
12
|
-
testMatch: ['<rootDir>/src/**/__tests__/**/*.test.ts'],
|
|
13
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { Colorful } from '../lib'
|
|
2
|
-
|
|
3
|
-
describe('Colorful Class', () => {
|
|
4
|
-
describe('rgbToHex method', () => {
|
|
5
|
-
it('should convert RGB to HEX', () => {
|
|
6
|
-
const rgbColor = 'rgb(255, 255, 255)'
|
|
7
|
-
const hexColor = Colorful.rgbToHex(rgbColor)
|
|
8
|
-
expect(hexColor).toBe('#ffffff')
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('should convert RGBA to HEX', () => {
|
|
12
|
-
const rgbaColor = 'rgba(255, 0, 0, 0.5)'
|
|
13
|
-
const hexColor = Colorful.rgbToHex(rgbaColor)
|
|
14
|
-
expect(hexColor).toBe('#ff0000')
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('should throw an error for invalid color format', () => {
|
|
18
|
-
const invalidColor = 'invalidColor'
|
|
19
|
-
expect(() => Colorful.rgbToHex(invalidColor)).toThrowError(
|
|
20
|
-
`Invalid color format '${invalidColor}'. Please provide a valid RGB or RGBA color.`
|
|
21
|
-
)
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
describe('hexToRgb method', () => {
|
|
26
|
-
it('should convert HEX to RGB', () => {
|
|
27
|
-
const hexColor = '#ffffff'
|
|
28
|
-
const rgbColor = Colorful.hexToRgb(hexColor)
|
|
29
|
-
expect(rgbColor).toBe('rgb(255, 255, 255)')
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
describe('isLightColor method', () => {
|
|
34
|
-
it('should return true for a light color (HEX format)', () => {
|
|
35
|
-
const lightHexColor = '#f0f0f0'
|
|
36
|
-
const isLight = Colorful.isLightColor(lightHexColor)
|
|
37
|
-
expect(isLight).toBe(true)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should return false for a dark color (HEX format)', () => {
|
|
41
|
-
const darkHexColor = '#333333'
|
|
42
|
-
const isLight = Colorful.isLightColor(darkHexColor)
|
|
43
|
-
expect(isLight).toBe(false)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should return true for a light color (RGBA format)', () => {
|
|
47
|
-
const lightRgbaColor = 'rgba(255, 255, 255, 0.5)'
|
|
48
|
-
const isLight = Colorful.isLightColor(lightRgbaColor)
|
|
49
|
-
expect(isLight).toBe(true)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('should return false for a dark color (RGBA format)', () => {
|
|
53
|
-
const darkRgbaColor = 'rgba(0, 0, 0, 0.8)'
|
|
54
|
-
const isLight = Colorful.isLightColor(darkRgbaColor)
|
|
55
|
-
expect(isLight).toBe(false)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('should return true for a light color (RGB format)', () => {
|
|
59
|
-
const lightRgbColor = 'rgb(200, 220, 255)'
|
|
60
|
-
const isLight = Colorful.isLightColor(lightRgbColor)
|
|
61
|
-
expect(isLight).toBe(true)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('should return false for a dark color (RGB format)', () => {
|
|
65
|
-
const darkRgbColor = 'rgb(10, 20, 30)'
|
|
66
|
-
const isLight = Colorful.isLightColor(darkRgbColor)
|
|
67
|
-
expect(isLight).toBe(false)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('should throw an error for an invalid color format', () => {
|
|
71
|
-
const invalidColor = 'invalid-color'
|
|
72
|
-
expect(() => Colorful.isLightColor(invalidColor)).toThrowError(
|
|
73
|
-
"Invalid color format 'invalid-color'. Please provide a valid HEX, RGB, or RGBA color."
|
|
74
|
-
)
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
})
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { createElasticTransport } from '../lib/elastic-transport'
|
|
2
|
-
import { Client } from '@elastic/elasticsearch'
|
|
3
|
-
import { EventEmitter } from 'events'
|
|
4
|
-
|
|
5
|
-
jest.mock('@elastic/elasticsearch', () => ({
|
|
6
|
-
Client: jest.fn(),
|
|
7
|
-
}))
|
|
8
|
-
|
|
9
|
-
const MockedClient = Client as jest.Mock
|
|
10
|
-
|
|
11
|
-
const writeLog = (
|
|
12
|
-
transport: NodeJS.WritableStream,
|
|
13
|
-
doc: Record<string, unknown>
|
|
14
|
-
) => transport.write(`${JSON.stringify(doc)}\n`)
|
|
15
|
-
|
|
16
|
-
const waitForEvent = <T = unknown>(
|
|
17
|
-
emitter: EventEmitter,
|
|
18
|
-
eventName: string
|
|
19
|
-
): Promise<T> =>
|
|
20
|
-
new Promise((resolve) => {
|
|
21
|
-
emitter.once(eventName, (value) => resolve(value as T))
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
describe('elastic transport resilience', () => {
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
jest.useRealTimers()
|
|
27
|
-
MockedClient.mockReset()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('reports bulk failures without emitting terminal stream errors', async () => {
|
|
31
|
-
const bulk = jest
|
|
32
|
-
.fn()
|
|
33
|
-
.mockRejectedValueOnce(new Error('ES unavailable'))
|
|
34
|
-
.mockResolvedValueOnce({ errors: false, items: [] })
|
|
35
|
-
MockedClient.mockImplementation(() => ({ bulk }))
|
|
36
|
-
|
|
37
|
-
const transport = createElasticTransport({
|
|
38
|
-
index: 'logs',
|
|
39
|
-
flushBytes: 1,
|
|
40
|
-
flushInterval: 10000,
|
|
41
|
-
requestTimeout: 10,
|
|
42
|
-
})
|
|
43
|
-
const streamError = jest.fn()
|
|
44
|
-
transport.on('error', streamError)
|
|
45
|
-
|
|
46
|
-
const failed = waitForEvent<Error>(transport, 'bulkError')
|
|
47
|
-
writeLog(transport, { message: 'first' })
|
|
48
|
-
await failed
|
|
49
|
-
|
|
50
|
-
const inserted = waitForEvent<{ successful: number; failed: number }>(
|
|
51
|
-
transport,
|
|
52
|
-
'insert'
|
|
53
|
-
)
|
|
54
|
-
writeLog(transport, { message: 'second' })
|
|
55
|
-
|
|
56
|
-
await expect(inserted).resolves.toEqual({ successful: 1, failed: 0 })
|
|
57
|
-
expect(streamError).not.toHaveBeenCalled()
|
|
58
|
-
expect(bulk).toHaveBeenCalledTimes(2)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('watchdog releases a stuck bulk request so later logs can flush', async () => {
|
|
62
|
-
jest.useFakeTimers()
|
|
63
|
-
|
|
64
|
-
const bulk = jest
|
|
65
|
-
.fn()
|
|
66
|
-
.mockReturnValueOnce(new Promise(() => undefined))
|
|
67
|
-
.mockResolvedValueOnce({ errors: false, items: [] })
|
|
68
|
-
MockedClient.mockImplementation(() => ({ bulk }))
|
|
69
|
-
|
|
70
|
-
const transport = createElasticTransport({
|
|
71
|
-
index: 'logs',
|
|
72
|
-
flushBytes: 1,
|
|
73
|
-
flushInterval: 10000,
|
|
74
|
-
requestTimeout: 1,
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const timedOut = waitForEvent<Error>(transport, 'bulkError')
|
|
78
|
-
writeLog(transport, { message: 'stuck' })
|
|
79
|
-
|
|
80
|
-
await jest.advanceTimersByTimeAsync(5001)
|
|
81
|
-
await expect(timedOut).resolves.toMatchObject({
|
|
82
|
-
message: 'Elasticsearch bulk request timed out after 5001ms',
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
const inserted = waitForEvent<{ successful: number; failed: number }>(
|
|
86
|
-
transport,
|
|
87
|
-
'insert'
|
|
88
|
-
)
|
|
89
|
-
writeLog(transport, { message: 'recovered' })
|
|
90
|
-
|
|
91
|
-
await expect(inserted).resolves.toEqual({ successful: 1, failed: 0 })
|
|
92
|
-
expect(bulk).toHaveBeenCalledTimes(2)
|
|
93
|
-
})
|
|
94
|
-
})
|