opticedge-cloud-utils 1.1.22 → 1.1.24
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/dist/env.d.ts +1 -1
- package/dist/env.js +7 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +53 -0
- package/package.json +1 -1
- package/src/env.ts +9 -7
- package/src/index.ts +1 -0
- package/src/logger.ts +55 -0
- package/tests/env.test.ts +59 -12
- package/tests/logger.test.ts +116 -0
package/dist/env.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function requireEnv(name: string, defaultValue?: string): string;
|
package/dist/env.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.
|
|
5
|
-
function
|
|
3
|
+
exports.requireEnv = requireEnv;
|
|
4
|
+
function requireEnv(name, defaultValue) {
|
|
6
5
|
const value = process.env[name];
|
|
7
|
-
if (
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
10
|
-
|
|
6
|
+
if (value === undefined || value === '') {
|
|
7
|
+
if (defaultValue !== undefined) {
|
|
8
|
+
return defaultValue;
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`${name} env var must be set`);
|
|
11
11
|
}
|
|
12
12
|
return value;
|
|
13
13
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ __exportStar(require("./tw/wallet"), exports);
|
|
|
22
22
|
__exportStar(require("./auth"), exports);
|
|
23
23
|
__exportStar(require("./chunk"), exports);
|
|
24
24
|
__exportStar(require("./env"), exports);
|
|
25
|
+
__exportStar(require("./logger"), exports);
|
|
25
26
|
__exportStar(require("./number"), exports);
|
|
26
27
|
__exportStar(require("./parser"), exports);
|
|
27
28
|
__exportStar(require("./pub"), exports);
|
package/dist/logger.d.ts
ADDED
package/dist/logger.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/logger.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.LogLevel = void 0;
|
|
5
|
+
exports.log = log;
|
|
6
|
+
var LogLevel;
|
|
7
|
+
(function (LogLevel) {
|
|
8
|
+
LogLevel["DEBUG"] = "DEBUG";
|
|
9
|
+
LogLevel["INFO"] = "INFO";
|
|
10
|
+
LogLevel["WARNING"] = "WARNING";
|
|
11
|
+
LogLevel["ERROR"] = "ERROR";
|
|
12
|
+
LogLevel["CRITICAL"] = "CRITICAL";
|
|
13
|
+
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
14
|
+
function log(level, message, metadata = {}) {
|
|
15
|
+
const payload = {
|
|
16
|
+
severity: level,
|
|
17
|
+
message,
|
|
18
|
+
...sanitize(metadata)
|
|
19
|
+
};
|
|
20
|
+
// IMPORTANT: must be single-line JSON for Cloud Logging
|
|
21
|
+
console.log(JSON.stringify(payload));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Makes metadata JSON-safe:
|
|
25
|
+
* - ObjectId works automatically (mongodb@7)
|
|
26
|
+
* - Error objects become structured
|
|
27
|
+
* - BigInt becomes string
|
|
28
|
+
*/
|
|
29
|
+
function sanitize(obj) {
|
|
30
|
+
const safe = {};
|
|
31
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
32
|
+
if (value instanceof Error) {
|
|
33
|
+
safe[key] = {
|
|
34
|
+
name: value.name,
|
|
35
|
+
message: value.message,
|
|
36
|
+
stack: value.stack
|
|
37
|
+
};
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === 'bigint') {
|
|
41
|
+
safe[key] = value.toString();
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
JSON.stringify(value);
|
|
46
|
+
safe[key] = value;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
safe[key] = String(value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return safe;
|
|
53
|
+
}
|
package/package.json
CHANGED
package/src/env.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export function getEnv(name: string, fallback?: any): any {
|
|
1
|
+
export function requireEnv(name: string, defaultValue?: string): string {
|
|
4
2
|
const value = process.env[name]
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
|
|
4
|
+
if (value === undefined || value === '') {
|
|
5
|
+
if (defaultValue !== undefined) {
|
|
6
|
+
return defaultValue
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`${name} env var must be set`)
|
|
8
9
|
}
|
|
10
|
+
|
|
9
11
|
return value
|
|
10
|
-
}
|
|
12
|
+
}
|
package/src/index.ts
CHANGED
package/src/logger.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/logger.ts
|
|
2
|
+
|
|
3
|
+
export enum LogLevel {
|
|
4
|
+
DEBUG = 'DEBUG',
|
|
5
|
+
INFO = 'INFO',
|
|
6
|
+
WARNING = 'WARNING',
|
|
7
|
+
ERROR = 'ERROR',
|
|
8
|
+
CRITICAL = 'CRITICAL'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function log(level: LogLevel, message: string, metadata: Record<string, any> = {}): void {
|
|
12
|
+
const payload = {
|
|
13
|
+
severity: level,
|
|
14
|
+
message,
|
|
15
|
+
...sanitize(metadata)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// IMPORTANT: must be single-line JSON for Cloud Logging
|
|
19
|
+
console.log(JSON.stringify(payload))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Makes metadata JSON-safe:
|
|
24
|
+
* - ObjectId works automatically (mongodb@7)
|
|
25
|
+
* - Error objects become structured
|
|
26
|
+
* - BigInt becomes string
|
|
27
|
+
*/
|
|
28
|
+
function sanitize(obj: Record<string, any>): Record<string, any> {
|
|
29
|
+
const safe: Record<string, any> = {}
|
|
30
|
+
|
|
31
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
32
|
+
if (value instanceof Error) {
|
|
33
|
+
safe[key] = {
|
|
34
|
+
name: value.name,
|
|
35
|
+
message: value.message,
|
|
36
|
+
stack: value.stack
|
|
37
|
+
}
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof value === 'bigint') {
|
|
42
|
+
safe[key] = value.toString()
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
JSON.stringify(value)
|
|
48
|
+
safe[key] = value
|
|
49
|
+
} catch {
|
|
50
|
+
safe[key] = String(value)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return safe
|
|
55
|
+
}
|
package/tests/env.test.ts
CHANGED
|
@@ -1,18 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireEnv } from '../src/env'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
describe('requireEnv', () => {
|
|
4
|
+
const ORIGINAL_ENV = process.env
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
jest.resetModules()
|
|
8
|
+
process.env = { ...ORIGINAL_ENV }
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterAll(() => {
|
|
12
|
+
process.env = ORIGINAL_ENV
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// ============================================================
|
|
16
|
+
// Success cases
|
|
17
|
+
// ============================================================
|
|
18
|
+
|
|
19
|
+
it('returns env value when set', () => {
|
|
20
|
+
process.env.TEST_VAR = 'hello'
|
|
21
|
+
expect(requireEnv('TEST_VAR')).toBe('hello')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('returns "0" correctly (should not treat as missing)', () => {
|
|
25
|
+
process.env.TEST_VAR = '0'
|
|
26
|
+
expect(requireEnv('TEST_VAR')).toBe('0')
|
|
7
27
|
})
|
|
8
28
|
|
|
9
|
-
it('returns
|
|
10
|
-
|
|
11
|
-
expect(
|
|
29
|
+
it('returns "false" correctly (should not treat as missing)', () => {
|
|
30
|
+
process.env.TEST_VAR = 'false'
|
|
31
|
+
expect(requireEnv('TEST_VAR')).toBe('false')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// ============================================================
|
|
35
|
+
// Default handling
|
|
36
|
+
// ============================================================
|
|
37
|
+
|
|
38
|
+
it('returns default when env is undefined', () => {
|
|
39
|
+
delete process.env.TEST_VAR
|
|
40
|
+
expect(requireEnv('TEST_VAR', 'fallback')).toBe('fallback')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('returns default when env is empty string', () => {
|
|
44
|
+
process.env.TEST_VAR = ''
|
|
45
|
+
expect(requireEnv('TEST_VAR', 'fallback')).toBe('fallback')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// ============================================================
|
|
49
|
+
// Error cases
|
|
50
|
+
// ============================================================
|
|
51
|
+
|
|
52
|
+
it('throws when env is undefined and no default provided', () => {
|
|
53
|
+
delete process.env.TEST_VAR
|
|
54
|
+
expect(() => requireEnv('TEST_VAR')).toThrow(
|
|
55
|
+
'TEST_VAR env var must be set'
|
|
56
|
+
)
|
|
12
57
|
})
|
|
13
58
|
|
|
14
|
-
it('throws
|
|
15
|
-
|
|
16
|
-
expect(() =>
|
|
59
|
+
it('throws when env is empty string and no default provided', () => {
|
|
60
|
+
process.env.TEST_VAR = ''
|
|
61
|
+
expect(() => requireEnv('TEST_VAR')).toThrow(
|
|
62
|
+
'TEST_VAR env var must be set'
|
|
63
|
+
)
|
|
17
64
|
})
|
|
18
|
-
})
|
|
65
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { log, LogLevel } from '../src/logger'
|
|
2
|
+
import { ObjectId } from 'mongodb'
|
|
3
|
+
|
|
4
|
+
describe('logger.ts (100% coverage)', () => {
|
|
5
|
+
let consoleSpy: jest.SpyInstance
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
consoleSpy.mockRestore()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// ------------------------------------------------------------
|
|
16
|
+
// Basic log levels
|
|
17
|
+
// ------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
it('logs INFO correctly', () => {
|
|
20
|
+
log(LogLevel.INFO, 'Test message', { foo: 'bar' })
|
|
21
|
+
|
|
22
|
+
expect(consoleSpy).toHaveBeenCalledTimes(1)
|
|
23
|
+
|
|
24
|
+
const output = JSON.parse(consoleSpy.mock.calls[0][0])
|
|
25
|
+
expect(output).toEqual({
|
|
26
|
+
severity: 'INFO',
|
|
27
|
+
message: 'Test message',
|
|
28
|
+
foo: 'bar'
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('logs all LogLevel values', () => {
|
|
33
|
+
for (const level of Object.values(LogLevel)) {
|
|
34
|
+
log(level, 'Level test')
|
|
35
|
+
const output = JSON.parse(consoleSpy.mock.calls.pop()?.[0])
|
|
36
|
+
expect(output.severity).toBe(level)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// ------------------------------------------------------------
|
|
41
|
+
// ObjectId handling
|
|
42
|
+
// ------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
it('serializes ObjectId correctly', () => {
|
|
45
|
+
const id = new ObjectId()
|
|
46
|
+
|
|
47
|
+
log(LogLevel.INFO, 'ObjectId test', { id })
|
|
48
|
+
|
|
49
|
+
const output = JSON.parse(consoleSpy.mock.calls[0][0])
|
|
50
|
+
|
|
51
|
+
expect(output.id).toBe(id.toHexString())
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// ------------------------------------------------------------
|
|
55
|
+
// Error handling
|
|
56
|
+
// ------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
it('serializes Error objects correctly', () => {
|
|
59
|
+
const error = new Error('Boom')
|
|
60
|
+
|
|
61
|
+
log(LogLevel.ERROR, 'Error test', { error })
|
|
62
|
+
|
|
63
|
+
const output = JSON.parse(consoleSpy.mock.calls[0][0])
|
|
64
|
+
|
|
65
|
+
expect(output.error).toMatchObject({
|
|
66
|
+
name: 'Error',
|
|
67
|
+
message: 'Boom'
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
expect(output.error.stack).toBeDefined()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// ------------------------------------------------------------
|
|
74
|
+
// BigInt handling
|
|
75
|
+
// ------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
it('converts BigInt to string', () => {
|
|
78
|
+
const big = BigInt(123456789)
|
|
79
|
+
|
|
80
|
+
log(LogLevel.INFO, 'BigInt test', { big })
|
|
81
|
+
|
|
82
|
+
const output = JSON.parse(consoleSpy.mock.calls[0][0])
|
|
83
|
+
|
|
84
|
+
expect(output.big).toBe('123456789')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// ------------------------------------------------------------
|
|
88
|
+
// Fallback serialization branch
|
|
89
|
+
// ------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
it('falls back to string for unserializable values', () => {
|
|
92
|
+
const circular: any = {}
|
|
93
|
+
circular.self = circular
|
|
94
|
+
|
|
95
|
+
log(LogLevel.INFO, 'Circular test', { circular })
|
|
96
|
+
|
|
97
|
+
const output = JSON.parse(consoleSpy.mock.calls[0][0])
|
|
98
|
+
|
|
99
|
+
expect(output.circular).toBe('[object Object]')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// ------------------------------------------------------------
|
|
103
|
+
// No metadata case
|
|
104
|
+
// ------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
it('works without metadata', () => {
|
|
107
|
+
log(LogLevel.WARNING, 'No metadata')
|
|
108
|
+
|
|
109
|
+
const output = JSON.parse(consoleSpy.mock.calls[0][0])
|
|
110
|
+
|
|
111
|
+
expect(output).toEqual({
|
|
112
|
+
severity: 'WARNING',
|
|
113
|
+
message: 'No metadata'
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
})
|