@privateaim/server-kit 0.8.16 → 0.8.18
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/CHANGELOG.md +39 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/index.cjs +165 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +156 -135
- package/dist/index.mjs.map +1 -1
- package/dist/services/entity-event/handlers/index.d.ts +3 -0
- package/dist/services/entity-event/handlers/index.d.ts.map +1 -0
- package/dist/services/entity-event/handlers/redis/helpers.d.ts +2 -0
- package/dist/services/entity-event/handlers/redis/helpers.d.ts.map +1 -0
- package/dist/services/entity-event/handlers/redis/index.d.ts.map +1 -0
- package/dist/services/entity-event/handlers/redis/module.d.ts +8 -0
- package/dist/services/entity-event/handlers/redis/module.d.ts.map +1 -0
- package/dist/services/entity-event/handlers/socket/index.d.ts +2 -0
- package/dist/services/entity-event/handlers/socket/index.d.ts.map +1 -0
- package/dist/services/entity-event/handlers/socket/module.d.ts +10 -0
- package/dist/services/entity-event/handlers/socket/module.d.ts.map +1 -0
- package/dist/services/entity-event/index.d.ts +5 -0
- package/dist/services/entity-event/index.d.ts.map +1 -0
- package/dist/services/entity-event/module.d.ts +10 -0
- package/dist/services/{domain-event → entity-event}/module.d.ts.map +1 -1
- package/dist/services/entity-event/singleton.d.ts +5 -0
- package/dist/services/entity-event/singleton.d.ts.map +1 -0
- package/dist/services/entity-event/types.d.ts +38 -0
- package/dist/services/entity-event/types.d.ts.map +1 -0
- package/dist/services/entity-event/utils.d.ts +2 -0
- package/dist/services/entity-event/utils.d.ts.map +1 -0
- package/dist/services/index.d.ts +1 -1
- package/dist/services/logger/index.d.ts +1 -0
- package/dist/services/logger/index.d.ts.map +1 -1
- package/dist/services/logger/module.d.ts +1 -2
- package/dist/services/logger/module.d.ts.map +1 -1
- package/dist/services/logger/singleton.d.ts +1 -1
- package/dist/services/logger/transports/console.d.ts +8 -0
- package/dist/services/logger/transports/console.d.ts.map +1 -0
- package/dist/services/logger/transports/index.d.ts +3 -0
- package/dist/services/logger/transports/index.d.ts.map +1 -0
- package/dist/services/logger/transports/memory.d.ts +6 -0
- package/dist/services/logger/transports/memory.d.ts.map +1 -0
- package/dist/services/logger/types.d.ts +27 -4
- package/dist/services/logger/types.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/constants.ts +12 -0
- package/src/index.ts +1 -0
- package/src/services/entity-event/handlers/index.ts +9 -0
- package/src/services/{domain-event → entity-event/handlers}/redis/helpers.ts +4 -4
- package/src/services/{domain-event → entity-event/handlers}/redis/module.ts +8 -8
- package/src/services/{domain-event → entity-event/handlers}/socket/index.ts +0 -1
- package/src/services/entity-event/handlers/socket/module.ts +71 -0
- package/src/services/{domain-event → entity-event}/index.ts +1 -2
- package/src/services/{domain-event → entity-event}/module.ts +24 -20
- package/src/services/entity-event/singleton.ts +22 -0
- package/src/services/{domain-event → entity-event}/types.ts +18 -16
- package/src/services/{domain-event → entity-event}/utils.ts +1 -1
- package/src/services/index.ts +1 -1
- package/src/services/logger/index.ts +1 -0
- package/src/services/logger/module.ts +12 -25
- package/src/services/logger/singleton.ts +1 -1
- package/src/services/logger/transports/console.ts +27 -0
- package/src/services/logger/transports/index.ts +9 -0
- package/src/services/logger/transports/memory.ts +18 -0
- package/src/services/logger/types.ts +27 -5
- package/test/data/error.ts +16 -0
- package/test/unit/logger.spec.ts +57 -0
- package/dist/services/domain-event/index.d.ts +0 -6
- package/dist/services/domain-event/index.d.ts.map +0 -1
- package/dist/services/domain-event/module.d.ts +0 -10
- package/dist/services/domain-event/redis/helpers.d.ts +0 -2
- package/dist/services/domain-event/redis/helpers.d.ts.map +0 -1
- package/dist/services/domain-event/redis/index.d.ts.map +0 -1
- package/dist/services/domain-event/redis/module.d.ts +0 -8
- package/dist/services/domain-event/redis/module.d.ts.map +0 -1
- package/dist/services/domain-event/singleton.d.ts +0 -4
- package/dist/services/domain-event/singleton.d.ts.map +0 -1
- package/dist/services/domain-event/socket/helpers.d.ts +0 -3
- package/dist/services/domain-event/socket/helpers.d.ts.map +0 -1
- package/dist/services/domain-event/socket/index.d.ts +0 -3
- package/dist/services/domain-event/socket/index.d.ts.map +0 -1
- package/dist/services/domain-event/socket/module.d.ts +0 -8
- package/dist/services/domain-event/socket/module.d.ts.map +0 -1
- package/dist/services/domain-event/types.d.ts +0 -37
- package/dist/services/domain-event/types.d.ts.map +0 -1
- package/dist/services/domain-event/utils.d.ts +0 -2
- package/dist/services/domain-event/utils.d.ts.map +0 -1
- package/src/services/domain-event/singleton.ts +0 -36
- package/src/services/domain-event/socket/helpers.ts +0 -26
- package/src/services/domain-event/socket/module.ts +0 -53
- /package/dist/services/{domain-event → entity-event/handlers}/redis/index.d.ts +0 -0
- /package/src/services/{domain-event → entity-event/handlers}/redis/index.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@privateaim/server-kit",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.18",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./package.json": "./package.json",
|
|
6
6
|
".": {
|
|
@@ -29,13 +29,14 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@ebec/http": "^2.3.0",
|
|
31
31
|
"@isaacs/ttlcache": "^1.4.1",
|
|
32
|
-
"@privateaim/kit": "^0.8.
|
|
32
|
+
"@privateaim/kit": "^0.8.18",
|
|
33
33
|
"@socket.io/redis-emitter": "^5.1.0",
|
|
34
34
|
"envix": "^1.5.0",
|
|
35
35
|
"hapic": "^2.7.0",
|
|
36
36
|
"singa": "^1.1.0",
|
|
37
37
|
"triple-beam": "^1.4.1",
|
|
38
|
-
"winston": "^3.17.0"
|
|
38
|
+
"winston": "^3.17.0",
|
|
39
|
+
"winston-transport": "^4.9.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@authup/core-http-kit": "^1.0.0-beta.27",
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export enum EnvironmentName {
|
|
9
|
+
TEST = 'test',
|
|
10
|
+
DEVELOPMENT = 'development',
|
|
11
|
+
PRODUCTION = 'production',
|
|
12
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Copyright (c) 2025.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
function stringify(input: string | string[]) {
|
|
9
9
|
return typeof input === 'string' ? input : input.join('/');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function
|
|
12
|
+
export function buildEntityEventRedisChannel(
|
|
13
13
|
channel: string | string[],
|
|
14
14
|
namespace?: string | string[],
|
|
15
15
|
) {
|
|
@@ -7,22 +7,22 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Client } from 'redis-extension';
|
|
9
9
|
import type { DomainEventRecord } from '@privateaim/kit';
|
|
10
|
-
import type {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import type { EntityEventHandleOptions, IEntityEventHandler } from '../../types';
|
|
11
|
+
import { transformEntityEventData } from '../../utils';
|
|
12
|
+
import { buildEntityEventRedisChannel } from './helpers';
|
|
13
13
|
|
|
14
|
-
export class
|
|
14
|
+
export class EntityEventRedisHandler implements IEntityEventHandler {
|
|
15
15
|
protected driver : Client;
|
|
16
16
|
|
|
17
17
|
constructor(client: Client) {
|
|
18
18
|
this.driver = client;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
async
|
|
21
|
+
async handle(ctx: EntityEventHandleOptions) : Promise<void> {
|
|
22
22
|
const payload : DomainEventRecord = {
|
|
23
|
-
type: ctx.metadata.
|
|
23
|
+
type: ctx.metadata.ref_type,
|
|
24
24
|
event: ctx.metadata.event,
|
|
25
|
-
data:
|
|
25
|
+
data: transformEntityEventData(ctx.data),
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const payloadSerialized = JSON.stringify(payload);
|
|
@@ -31,7 +31,7 @@ export class DomainEventRedisPublisher implements IDomainEventConsumer {
|
|
|
31
31
|
for (let i = 0; i < ctx.destinations.length; i++) {
|
|
32
32
|
const destination = ctx.destinations[i];
|
|
33
33
|
|
|
34
|
-
const key =
|
|
34
|
+
const key = buildEntityEventRedisChannel(
|
|
35
35
|
destination.channel,
|
|
36
36
|
destination.namespace,
|
|
37
37
|
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2022-2024.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { buildDomainEventFullName } from '@privateaim/kit';
|
|
9
|
+
import { Emitter } from '@socket.io/redis-emitter';
|
|
10
|
+
import type { Client } from 'redis-extension';
|
|
11
|
+
import type { EntityEventHandleOptions, IEntityEventHandler } from '../../types';
|
|
12
|
+
import { transformEntityEventData } from '../../utils';
|
|
13
|
+
|
|
14
|
+
export class EntityEventSocketHandler implements IEntityEventHandler {
|
|
15
|
+
protected client : Client;
|
|
16
|
+
|
|
17
|
+
constructor(client: Client) {
|
|
18
|
+
this.client = client;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async handle(ctx: EntityEventHandleOptions) : Promise<void> {
|
|
22
|
+
ctx.data = transformEntityEventData(ctx.data);
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < ctx.destinations.length; i++) {
|
|
25
|
+
const destination = ctx.destinations[i];
|
|
26
|
+
|
|
27
|
+
const namespace = this.buildNamespace(destination.namespace);
|
|
28
|
+
const roomName = this.buildChannel(destination.channel);
|
|
29
|
+
|
|
30
|
+
const fullEventName = buildDomainEventFullName(
|
|
31
|
+
ctx.metadata.ref_type,
|
|
32
|
+
ctx.metadata.event,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const emitter = new Emitter(this.client, {}, namespace);
|
|
36
|
+
|
|
37
|
+
emitter
|
|
38
|
+
.in(roomName)
|
|
39
|
+
.emit(fullEventName, {
|
|
40
|
+
data: ctx.data,
|
|
41
|
+
meta: {
|
|
42
|
+
refType: ctx.metadata.ref_type,
|
|
43
|
+
refId: ctx.metadata.ref_id,
|
|
44
|
+
event: ctx.metadata.event,
|
|
45
|
+
namespace,
|
|
46
|
+
roomName,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected buildNamespace(namespace?: string | string[]) {
|
|
53
|
+
if (typeof namespace === 'undefined') {
|
|
54
|
+
return '/';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof namespace === 'string') {
|
|
58
|
+
return namespace.startsWith('/') ? namespace : `/${namespace}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return `/${namespace.join('/')}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected buildChannel(channel: string | string[]) {
|
|
65
|
+
if (typeof channel === 'string') {
|
|
66
|
+
return channel;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return channel.join('/');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2024-
|
|
2
|
+
* Copyright (c) 2024-2025.
|
|
3
3
|
* Author Peter Placzek (tada5hi)
|
|
4
4
|
* For the full copyright and license information,
|
|
5
5
|
* view the LICENSE file that was distributed with this source code.
|
|
@@ -9,63 +9,67 @@ import type { ObjectLiteral } from '@privateaim/kit';
|
|
|
9
9
|
import { buildDomainEventFullName } from '@privateaim/kit';
|
|
10
10
|
import { isLoggerUsable, useLogger } from '../logger';
|
|
11
11
|
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
EntityEventDestination,
|
|
13
|
+
EntityEventHandleOptions,
|
|
14
|
+
EntityEventPublishOptions,
|
|
15
|
+
IEntityEventHandler,
|
|
16
|
+
IEntityEventPublisher,
|
|
16
17
|
} from './types';
|
|
17
18
|
|
|
18
|
-
export class
|
|
19
|
-
protected
|
|
19
|
+
export class EntityEventPublisher implements IEntityEventPublisher {
|
|
20
|
+
protected handlers : Set<IEntityEventHandler>;
|
|
20
21
|
|
|
21
22
|
constructor() {
|
|
22
|
-
this.
|
|
23
|
+
this.handlers = new Set<IEntityEventHandler>();
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
this.
|
|
26
|
+
register(consumer: IEntityEventHandler) {
|
|
27
|
+
this.handlers.add(consumer);
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
async safePublish<T extends ObjectLiteral = ObjectLiteral>(
|
|
30
|
-
ctx:
|
|
31
|
+
ctx: EntityEventPublishOptions<T>,
|
|
31
32
|
) : Promise<void> {
|
|
32
33
|
try {
|
|
33
34
|
await this.publish(ctx);
|
|
34
35
|
} catch (e) {
|
|
35
36
|
if (isLoggerUsable()) {
|
|
36
|
-
useLogger().error(`Publishing event ${buildDomainEventFullName(ctx.metadata.
|
|
37
|
+
useLogger().error(`Publishing event ${buildDomainEventFullName(ctx.metadata.ref_type, ctx.metadata.event)} failed`);
|
|
37
38
|
useLogger().error(e);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
async publish<T extends ObjectLiteral = ObjectLiteral>(
|
|
43
|
-
ctx:
|
|
44
|
+
ctx: EntityEventPublishOptions<T>,
|
|
44
45
|
) : Promise<void> {
|
|
45
46
|
if (isLoggerUsable()) {
|
|
46
|
-
useLogger()
|
|
47
|
+
useLogger()
|
|
48
|
+
.debug(
|
|
49
|
+
`Publishing event ${buildDomainEventFullName(ctx.metadata.ref_type, ctx.metadata.event)}`,
|
|
50
|
+
);
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
let destinations :
|
|
53
|
+
let destinations : EntityEventDestination[] = [];
|
|
50
54
|
if (typeof ctx.destinations === 'function') {
|
|
51
55
|
destinations = ctx.destinations(ctx.data);
|
|
52
56
|
} else {
|
|
53
57
|
destinations = ctx.destinations;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
const consumeContext :
|
|
60
|
+
const consumeContext : EntityEventHandleOptions = {
|
|
57
61
|
...ctx,
|
|
58
62
|
destinations,
|
|
59
63
|
};
|
|
60
64
|
|
|
61
|
-
const
|
|
65
|
+
const handlers = this.handlers.values();
|
|
62
66
|
while (true) {
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
67
|
+
const handler = handlers.next();
|
|
68
|
+
if (handler.done) {
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
await
|
|
72
|
+
await (handler.value as IEntityEventHandler).handle(consumeContext);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Factory } from 'singa';
|
|
9
|
+
import { singa } from 'singa';
|
|
10
|
+
import type { EntityEventPublisher } from './module';
|
|
11
|
+
|
|
12
|
+
const instance = singa<EntityEventPublisher>({
|
|
13
|
+
name: 'entityEventPublisher',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export function setEntityEventPublisherFactory(factory: Factory<EntityEventPublisher>) {
|
|
17
|
+
instance.setFactory(factory);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function useEntityEventPublisher() {
|
|
21
|
+
return instance.use();
|
|
22
|
+
}
|
|
@@ -7,19 +7,21 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ObjectLiteral } from '@privateaim/kit';
|
|
9
9
|
|
|
10
|
-
export type
|
|
10
|
+
export type EntityEventDestination = {
|
|
11
11
|
namespace?: string | string[],
|
|
12
12
|
channel: string | string[]
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export type
|
|
15
|
+
export type EntityEventDestinations = EntityEventDestination[];
|
|
16
16
|
|
|
17
|
-
export type
|
|
17
|
+
export type EntityEventDestinationsFn<
|
|
18
18
|
T extends ObjectLiteral =ObjectLiteral,
|
|
19
|
-
> = (data: T) =>
|
|
19
|
+
> = (data: T) => EntityEventDestination[];
|
|
20
|
+
|
|
21
|
+
export type EntityEventMetadata = {
|
|
22
|
+
ref_type: string,
|
|
23
|
+
ref_id?: string,
|
|
20
24
|
|
|
21
|
-
export type DomainEventMetadata = {
|
|
22
|
-
domain: string,
|
|
23
25
|
event: string,
|
|
24
26
|
|
|
25
27
|
request_path?: string | null,
|
|
@@ -32,28 +34,28 @@ export type DomainEventMetadata = {
|
|
|
32
34
|
actor_name?: string | null;
|
|
33
35
|
};
|
|
34
36
|
|
|
35
|
-
export type
|
|
37
|
+
export type EntityEventPublishOptions<
|
|
36
38
|
T extends ObjectLiteral = ObjectLiteral,
|
|
37
39
|
> = {
|
|
38
40
|
data: T,
|
|
39
41
|
dataPrevious?: T,
|
|
40
|
-
metadata:
|
|
41
|
-
destinations:
|
|
42
|
+
metadata: EntityEventMetadata,
|
|
43
|
+
destinations: EntityEventDestinations | EntityEventDestinationsFn<T>
|
|
42
44
|
};
|
|
43
45
|
|
|
44
|
-
export interface
|
|
45
|
-
publish(ctx:
|
|
46
|
+
export interface IEntityEventPublisher {
|
|
47
|
+
publish(ctx: EntityEventPublishOptions) : Promise<void>;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
export type
|
|
50
|
+
export type EntityEventHandleOptions<
|
|
49
51
|
T extends ObjectLiteral = ObjectLiteral,
|
|
50
52
|
> = {
|
|
51
53
|
data: T,
|
|
52
54
|
dataPrevious?: T,
|
|
53
|
-
metadata:
|
|
54
|
-
destinations:
|
|
55
|
+
metadata: EntityEventMetadata,
|
|
56
|
+
destinations: EntityEventDestinations
|
|
55
57
|
};
|
|
56
58
|
|
|
57
|
-
export interface
|
|
58
|
-
|
|
59
|
+
export interface IEntityEventHandler {
|
|
60
|
+
handle(ctx: EntityEventHandleOptions) : Promise<void>;
|
|
59
61
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { isObject } from '@privateaim/kit';
|
|
9
9
|
|
|
10
|
-
export function
|
|
10
|
+
export function transformEntityEventData<T>(input: T) : T {
|
|
11
11
|
if (isObject(input)) {
|
|
12
12
|
const keys = Object.keys(input);
|
|
13
13
|
for (let i = 0; i < keys.length; i++) {
|
package/src/services/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from './amqp';
|
|
|
9
9
|
export * from './authup';
|
|
10
10
|
export * from './authup-client-authentication-hook';
|
|
11
11
|
export * from './cache';
|
|
12
|
-
export * from './
|
|
12
|
+
export * from './entity-event';
|
|
13
13
|
export * from './logger';
|
|
14
14
|
export * from './redis';
|
|
15
15
|
export * from './vault';
|
|
@@ -5,32 +5,19 @@
|
|
|
5
5
|
* view the LICENSE file that was distributed with this source code.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function toTransports(input: LoggerTransports) {
|
|
15
|
-
return Array.isArray(input) ? input : [input];
|
|
16
|
-
}
|
|
8
|
+
import { createLogger as create, format } from 'winston';
|
|
9
|
+
import { createLoggerConsoleTransport } from './transports';
|
|
10
|
+
import type {
|
|
11
|
+
Logger, LoggerCreateContext, LoggerTransport,
|
|
12
|
+
} from './types';
|
|
17
13
|
|
|
18
14
|
export function createLogger(ctx: LoggerCreateContext = {}) : Logger {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
...(ctx.transports ? toTransports(ctx.transports) : []),
|
|
26
|
-
];
|
|
27
|
-
} else {
|
|
28
|
-
loggerTransports = [
|
|
29
|
-
new transports.Console({
|
|
30
|
-
level: 'debug',
|
|
31
|
-
}),
|
|
32
|
-
...(ctx.transports ? toTransports(ctx.transports) : []),
|
|
33
|
-
];
|
|
15
|
+
const transports : LoggerTransport[] = [
|
|
16
|
+
...(ctx.transports || []),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
if (transports.length === 0) {
|
|
20
|
+
transports.push(createLoggerConsoleTransport());
|
|
34
21
|
}
|
|
35
22
|
|
|
36
23
|
return create({
|
|
@@ -40,7 +27,7 @@ export function createLogger(ctx: LoggerCreateContext = {}) : Logger {
|
|
|
40
27
|
format.simple(),
|
|
41
28
|
),
|
|
42
29
|
level: 'debug',
|
|
43
|
-
transports
|
|
30
|
+
transports,
|
|
44
31
|
// todo: deeply merge options
|
|
45
32
|
...(ctx.options || {}),
|
|
46
33
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { read } from 'envix';
|
|
9
|
+
import { transports } from 'winston';
|
|
10
|
+
import type { TransportStreamOptions } from 'winston-transport';
|
|
11
|
+
import { EnvironmentName } from '../../../constants';
|
|
12
|
+
import type { LoggerTransport } from '../types';
|
|
13
|
+
|
|
14
|
+
export class LoggerConsoleTransport extends transports.Console {
|
|
15
|
+
constructor(options: TransportStreamOptions = {}) {
|
|
16
|
+
super({
|
|
17
|
+
level: read('env') === EnvironmentName.PRODUCTION ?
|
|
18
|
+
'info' :
|
|
19
|
+
'debug',
|
|
20
|
+
...options,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createLoggerConsoleTransport(options: TransportStreamOptions = {}) : LoggerTransport {
|
|
26
|
+
return new LoggerConsoleTransport(options);
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Transport from 'winston-transport';
|
|
9
|
+
|
|
10
|
+
export class LoggerMemoryTransport extends Transport {
|
|
11
|
+
public items : Record<PropertyKey, any>[] = [];
|
|
12
|
+
|
|
13
|
+
log(info: Record<PropertyKey, any>, next: () => void): any {
|
|
14
|
+
this.items.push(info);
|
|
15
|
+
|
|
16
|
+
next();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -5,15 +5,37 @@
|
|
|
5
5
|
* view the LICENSE file that was distributed with this source code.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type
|
|
8
|
+
import type Transport from 'winston-transport';
|
|
9
|
+
import type { LoggerOptions } from 'winston';
|
|
9
10
|
|
|
10
|
-
export type
|
|
11
|
+
export type LoggerTransport = Transport;
|
|
11
12
|
|
|
12
13
|
export type LoggerCreateContext = {
|
|
13
14
|
options?: Partial<LoggerOptions>,
|
|
14
|
-
transports?:
|
|
15
|
+
transports?: LoggerTransport[]
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
type LogEntry = {
|
|
19
|
+
/**
|
|
20
|
+
* Log message.
|
|
21
|
+
*/
|
|
22
|
+
message: string | Error,
|
|
23
|
+
[key: string]: any
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface LoggerLevelFn<OUT = any> {
|
|
27
|
+
(message: string, ...meta: Omit<LogEntry, 'message'>[]): OUT,
|
|
28
|
+
(message: LogEntry): OUT,
|
|
29
|
+
(message:string): OUT,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type Logger = {
|
|
33
|
+
emerg: LoggerLevelFn<Logger>,
|
|
34
|
+
alert: LoggerLevelFn<Logger>,
|
|
35
|
+
crit: LoggerLevelFn<Logger>,
|
|
36
|
+
error: LoggerLevelFn<Logger>,
|
|
37
|
+
warn: LoggerLevelFn<Logger>,
|
|
38
|
+
notice: LoggerLevelFn<Logger>,
|
|
39
|
+
info: LoggerLevelFn<Logger>,
|
|
40
|
+
debug: LoggerLevelFn<Logger>,
|
|
19
41
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class CustomError extends Error {
|
|
9
|
+
code: string;
|
|
10
|
+
|
|
11
|
+
constructor(message: string, options: ErrorOptions = {}) {
|
|
12
|
+
super(message, options);
|
|
13
|
+
|
|
14
|
+
this.code = 'foo';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025.
|
|
3
|
+
* Author Peter Placzek (tada5hi)
|
|
4
|
+
* For the full copyright and license information,
|
|
5
|
+
* view the LICENSE file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createLogger } from 'winston';
|
|
9
|
+
import { LoggerMemoryTransport } from '../../src';
|
|
10
|
+
import { CustomError } from '../data/error';
|
|
11
|
+
|
|
12
|
+
describe('src/logger', () => {
|
|
13
|
+
it('should log simple message', () => {
|
|
14
|
+
const memoryTransport = new LoggerMemoryTransport();
|
|
15
|
+
|
|
16
|
+
const logger = createLogger({
|
|
17
|
+
transports: [
|
|
18
|
+
memoryTransport,
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
logger.info('This is a log message', {
|
|
23
|
+
foo: 'bar',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const [item] = memoryTransport.items;
|
|
27
|
+
expect(item).toBeDefined();
|
|
28
|
+
expect(item.message).toEqual('This is a log message');
|
|
29
|
+
expect(item.foo).toEqual('bar');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should log error', () => {
|
|
33
|
+
const memoryTransport = new LoggerMemoryTransport();
|
|
34
|
+
|
|
35
|
+
const logger = createLogger({
|
|
36
|
+
transports: [
|
|
37
|
+
memoryTransport,
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const error = new CustomError('This is a log message');
|
|
42
|
+
logger.info({
|
|
43
|
+
message: error,
|
|
44
|
+
foo: 'bar',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const [item] = memoryTransport.items;
|
|
48
|
+
expect(item).toBeDefined();
|
|
49
|
+
|
|
50
|
+
expect(item.message?.message).toEqual(error.message);
|
|
51
|
+
expect(item.message?.stack).toEqual(error.stack);
|
|
52
|
+
expect(item.message?.code).toEqual(error.code);
|
|
53
|
+
|
|
54
|
+
expect(item.foo).toEqual('bar');
|
|
55
|
+
expect(item.code).toBeUndefined();
|
|
56
|
+
});
|
|
57
|
+
});
|