@privateaim/server-kit 0.8.13 → 0.8.15

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 (73) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/index.cjs +494 -63
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.mjs +452 -65
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/services/domain-event/index.d.ts +1 -1
  7. package/dist/services/domain-event/index.d.ts.map +1 -1
  8. package/dist/services/domain-event/module.d.ts +4 -4
  9. package/dist/services/domain-event/module.d.ts.map +1 -1
  10. package/dist/services/domain-event/redis/module.d.ts +2 -2
  11. package/dist/services/domain-event/redis/module.d.ts.map +1 -1
  12. package/dist/services/domain-event/singleton.d.ts +1 -0
  13. package/dist/services/domain-event/singleton.d.ts.map +1 -1
  14. package/dist/services/domain-event/socket/module.d.ts +2 -2
  15. package/dist/services/domain-event/socket/module.d.ts.map +1 -1
  16. package/dist/services/domain-event/types.d.ts +29 -0
  17. package/dist/services/domain-event/types.d.ts.map +1 -0
  18. package/dist/services/domain-event/utils.d.ts +2 -2
  19. package/dist/services/domain-event/utils.d.ts.map +1 -1
  20. package/dist/services/index.d.ts +2 -0
  21. package/dist/services/index.d.ts.map +1 -1
  22. package/dist/services/log-store/entities/base.d.ts +7 -0
  23. package/dist/services/log-store/entities/base.d.ts.map +1 -0
  24. package/dist/services/log-store/entities/index.d.ts +3 -0
  25. package/dist/services/log-store/entities/index.d.ts.map +1 -0
  26. package/dist/services/log-store/entities/loki.d.ts +13 -0
  27. package/dist/services/log-store/entities/loki.d.ts.map +1 -0
  28. package/dist/services/log-store/entities/memory.d.ts +11 -0
  29. package/dist/services/log-store/entities/memory.d.ts.map +1 -0
  30. package/dist/services/log-store/index.d.ts +4 -0
  31. package/dist/services/log-store/index.d.ts.map +1 -0
  32. package/dist/services/log-store/singleton.d.ts +6 -0
  33. package/dist/services/log-store/singleton.d.ts.map +1 -0
  34. package/dist/services/log-store/types.d.ts +27 -0
  35. package/dist/services/log-store/types.d.ts.map +1 -0
  36. package/dist/services/logger/module.d.ts.map +1 -1
  37. package/dist/services/logger/store.d.ts +14 -0
  38. package/dist/services/logger/store.d.ts.map +1 -0
  39. package/dist/services/logger/types.d.ts +3 -0
  40. package/dist/services/logger/types.d.ts.map +1 -1
  41. package/dist/services/loki/index.d.ts +3 -0
  42. package/dist/services/loki/index.d.ts.map +1 -0
  43. package/dist/services/loki/module.d.ts +3 -0
  44. package/dist/services/loki/module.d.ts.map +1 -0
  45. package/dist/services/loki/singleton.d.ts +6 -0
  46. package/dist/services/loki/singleton.d.ts.map +1 -0
  47. package/package.json +12 -9
  48. package/src/services/domain-event/index.ts +1 -1
  49. package/src/services/domain-event/module.ts +8 -8
  50. package/src/services/domain-event/redis/module.ts +28 -9
  51. package/src/services/domain-event/singleton.ts +6 -2
  52. package/src/services/domain-event/socket/module.ts +20 -9
  53. package/src/services/domain-event/types.ts +50 -0
  54. package/src/services/domain-event/utils.ts +2 -2
  55. package/src/services/index.ts +2 -0
  56. package/src/services/log-store/entities/base.ts +25 -0
  57. package/src/services/log-store/entities/index.ts +9 -0
  58. package/src/services/log-store/entities/loki.ts +158 -0
  59. package/src/services/log-store/entities/memory.ts +100 -0
  60. package/src/services/log-store/index.ts +10 -0
  61. package/src/services/log-store/singleton.ts +36 -0
  62. package/src/services/log-store/types.ts +42 -0
  63. package/src/services/logger/module.ts +20 -5
  64. package/src/services/logger/store.ts +70 -0
  65. package/src/services/logger/types.ts +4 -1
  66. package/src/services/loki/index.ts +9 -0
  67. package/src/services/loki/module.ts +30 -0
  68. package/src/services/loki/singleton.ts +26 -0
  69. package/test/unit/log-store.spec.ts +21 -0
  70. package/test/unit/logger.spec.ts +36 -0
  71. package/dist/services/domain-event/type.d.ts +0 -15
  72. package/dist/services/domain-event/type.d.ts.map +0 -1
  73. package/src/services/domain-event/type.ts +0 -27
@@ -0,0 +1,100 @@
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 type { Log } from '@privateaim/kit';
9
+ import { LogLevel } from '@privateaim/kit';
10
+ import type {
11
+ LogInput, LogStore, LogStoreQueryOptions,
12
+ } from '../types';
13
+ import { nanoSeconds } from '../../loki';
14
+ import { BaseLogStore } from './base';
15
+
16
+ export class MemoryLogStore extends BaseLogStore implements LogStore {
17
+ public readonly items : Log[];
18
+
19
+ constructor(labels?: Record<string, string>) {
20
+ super();
21
+
22
+ this.items = [];
23
+ this.labels = labels;
24
+ }
25
+
26
+ // todo: apply query options
27
+ async delete(): Promise<void> {
28
+ return Promise.resolve();
29
+ }
30
+
31
+ async query(options: LogStoreQueryOptions = {}): Promise<[Log[], number]> {
32
+ // todo: apply all query options
33
+
34
+ const data = this.items.filter((item) => {
35
+ if (options.labels) {
36
+ if (!item.labels) {
37
+ return false;
38
+ }
39
+
40
+ const labelKeys = Object.keys(options.labels);
41
+ for (let i = 0; i < labelKeys.length; i++) {
42
+ if (!item.labels[labelKeys[i]]) {
43
+ return false;
44
+ }
45
+
46
+ if (item.labels[labelKeys[i]] !== options.labels[labelKeys[i]]) {
47
+ return false;
48
+ }
49
+ }
50
+ }
51
+
52
+ return true;
53
+ });
54
+
55
+ return [
56
+ data,
57
+ data.length,
58
+ ];
59
+ }
60
+
61
+ async write(message: string | LogInput, labels?: Record<string, string>): Promise<Log> {
62
+ let data : Log;
63
+
64
+ if (typeof message === 'string') {
65
+ const labelsNormalized = {
66
+ ...this.labels,
67
+ ...(labels || {}),
68
+ };
69
+ const level = (labelsNormalized.level || LogLevel.DEBUG) as LogLevel;
70
+ delete labelsNormalized.level;
71
+
72
+ data = {
73
+ message,
74
+ level,
75
+ time: nanoSeconds(),
76
+ labels: labelsNormalized,
77
+ };
78
+ } else {
79
+ const labelsNormalized = {
80
+ ...this.labels,
81
+ ...(message.labels || {}),
82
+ ...(labels || {}),
83
+ };
84
+
85
+ const level = (message.level || labelsNormalized.level || LogLevel.DEBUG) as LogLevel;
86
+ delete labelsNormalized.level;
87
+
88
+ data = {
89
+ ...message,
90
+ level,
91
+ time: message.time || nanoSeconds(),
92
+ labels: labelsNormalized,
93
+ };
94
+ }
95
+
96
+ this.items.push(data);
97
+
98
+ return data;
99
+ }
100
+ }
@@ -0,0 +1,10 @@
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 * from './entities';
9
+ export * from './singleton';
10
+ export * from './types';
@@ -0,0 +1,36 @@
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 { LogStore } from './types';
11
+ import { LokiLogStore, MemoryLogStore } from './entities';
12
+ import { isLokiClientUsable, useLokiClient } from '../loki';
13
+
14
+ const instance = singa<LogStore>({
15
+ name: 'logStore',
16
+ factory: () => {
17
+ if (isLokiClientUsable()) {
18
+ const loki = useLokiClient();
19
+ return new LokiLogStore(loki);
20
+ }
21
+
22
+ return new MemoryLogStore();
23
+ },
24
+ });
25
+
26
+ export function isLogStoreUsable() {
27
+ return instance.has() || instance.hasFactory();
28
+ }
29
+
30
+ export function setLogStoreFactory(input: Factory<LogStore>) {
31
+ instance.setFactory(input);
32
+ }
33
+
34
+ export function useLogStore(): LogStore {
35
+ return instance.use();
36
+ }
@@ -0,0 +1,42 @@
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 type { Log, LogLevel } from '@privateaim/kit';
9
+
10
+ export type LogStoreQueryOptions = {
11
+ labels?: Record<string, string>
12
+ start?: number,
13
+ end?: number,
14
+ sort?: 'DESC' | 'ASC',
15
+ limit?: number
16
+ };
17
+
18
+ export type LogStoreDeleteOptions = {
19
+ labels?: Record<string, string>
20
+ start?: number,
21
+ end?: number,
22
+ };
23
+
24
+ export type LogInput = Omit<Log, 'labels' | 'time' | 'level'> & {
25
+ time?: bigint,
26
+ level?: `${LogLevel}`,
27
+ labels?: Record<string, string>
28
+ };
29
+
30
+ export interface LogStore {
31
+ setLabels(labels: Record<string, string>): void;
32
+
33
+ getLabels() : Record<string, string>;
34
+
35
+ extendLabels(labels: Record<string, string>): void;
36
+
37
+ write(message: string | LogInput, labels?: Record<string, string>) : Promise<Log>;
38
+
39
+ query(options?: LogStoreQueryOptions) : Promise<[Log[], number]>;
40
+
41
+ delete(options?: LogStoreDeleteOptions) : Promise<void>;
42
+ }
@@ -11,9 +11,11 @@ import { EnvironmentName } from 'typeorm-extension';
11
11
  import type { Logger } from 'winston';
12
12
  import { createLogger as create, format, transports } from 'winston';
13
13
  import type { LoggerCreateContext, LoggerTransports } from './types';
14
+ import { LogStoreTransport } from './store';
15
+ import { useLogStore } from '../log-store';
14
16
 
15
17
  export function createLogger(ctx: LoggerCreateContext = {}) : Logger {
16
- const { directory, options = {} } = ctx;
18
+ const store = ctx.store || useLogStore();
17
19
 
18
20
  let loggerTransports : LoggerTransports;
19
21
  if (read('env') === EnvironmentName.PRODUCTION) {
@@ -22,23 +24,37 @@ export function createLogger(ctx: LoggerCreateContext = {}) : Logger {
22
24
  level: 'info',
23
25
  }),
24
26
  new transports.File({
25
- filename: path.join(directory || process.cwd(), 'access.log'),
27
+ filename: path.join(ctx.directory || process.cwd(), 'access.log'),
26
28
  level: 'http',
27
29
  maxsize: 10 * 1024 * 1024, // 10MB
28
30
  maxFiles: 5,
29
31
  }),
30
32
  new transports.File({
31
- filename: path.join(directory || process.cwd(), 'error.log'),
33
+ filename: path.join(ctx.directory || process.cwd(), 'error.log'),
32
34
  level: 'warn',
33
35
  maxsize: 10 * 1024 * 1024, // 10MB
34
36
  maxFiles: 5,
35
37
  }),
38
+ new LogStoreTransport(
39
+ store,
40
+ {
41
+ level: 'http',
42
+ labels: ctx.labels,
43
+ },
44
+ ),
36
45
  ];
37
46
  } else {
38
47
  loggerTransports = [
39
48
  new transports.Console({
40
49
  level: 'debug',
41
50
  }),
51
+ new LogStoreTransport(
52
+ store,
53
+ {
54
+ level: 'http',
55
+ labels: ctx.labels,
56
+ },
57
+ ),
42
58
  ];
43
59
  }
44
60
 
@@ -46,12 +62,11 @@ export function createLogger(ctx: LoggerCreateContext = {}) : Logger {
46
62
  format: format.combine(
47
63
  format.errors({ stack: true }),
48
64
  format.timestamp(),
49
- format.colorize(),
50
65
  format.simple(),
51
66
  ),
52
67
  level: 'debug',
53
68
  transports: loggerTransports,
54
69
  // todo: deeply merge options
55
- ...(options || {}),
70
+ ...(ctx.options || {}),
56
71
  });
57
72
  }
@@ -0,0 +1,70 @@
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 type { TransportStreamOptions } from 'winston-transport';
9
+ import WinstonTransport from 'winston-transport';
10
+ import type { LogInput, LogStore } from '../log-store';
11
+
12
+ type LogStoreTransportOptions = TransportStreamOptions & {
13
+ labels?: Record<string, string>
14
+ };
15
+
16
+ export class LogStoreTransport extends WinstonTransport {
17
+ protected instance : LogStore;
18
+
19
+ protected labels : Record<string, string>;
20
+
21
+ constructor(store: LogStore, options: LogStoreTransportOptions = {}) {
22
+ super(options);
23
+
24
+ this.instance = store;
25
+ this.labels = options.labels || {};
26
+ }
27
+
28
+ log(info: Record<PropertyKey, any>, next: () => void): any {
29
+ const {
30
+ message, timestamp, stack, ...data
31
+ } = info;
32
+
33
+ const labels : Record<string, string> = {
34
+ ...this.labels,
35
+ };
36
+ const keys = Object.keys(data);
37
+ for (let i = 0; i < keys.length; i++) {
38
+ if (typeof keys[i] !== 'string') {
39
+ continue;
40
+ }
41
+
42
+ const value = data[keys[i]];
43
+
44
+ if (
45
+ typeof value === 'string' ||
46
+ typeof value === 'number' ||
47
+ typeof value === 'boolean'
48
+ ) {
49
+ labels[keys[i]] = `${value}`;
50
+ }
51
+ }
52
+
53
+ let date : Date;
54
+ if (typeof timestamp === 'string') {
55
+ date = new Date(`${timestamp}`);
56
+ } else {
57
+ date = new Date();
58
+ }
59
+
60
+ const payload : LogInput = {
61
+ message: stack || message,
62
+ time: BigInt(date.getTime()) * 1_000_000n,
63
+ labels,
64
+ };
65
+
66
+ Promise.resolve()
67
+ .then(() => this.instance.write(payload))
68
+ .then(() => next());
69
+ }
70
+ }
@@ -6,10 +6,13 @@
6
6
  */
7
7
 
8
8
  import type { Logger, LoggerOptions } from 'winston';
9
+ import type { LogStore } from '../log-store';
9
10
 
10
11
  export type LoggerCreateContext = {
11
12
  directory?: string,
12
- options?: Partial<LoggerOptions>
13
+ options?: Partial<LoggerOptions>,
14
+ store?: LogStore,
15
+ labels?: Record<string, string>,
13
16
  };
14
17
 
15
18
  export type LoggerTransports = LoggerOptions['transports'];
@@ -0,0 +1,9 @@
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
+ export * from './module';
9
+ export * from './singleton';
@@ -0,0 +1,30 @@
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 {
9
+ LokiClient,
10
+ CompactorDeletionRequestCreate as LokiCompactorDeletionRequestCreate,
11
+ Config as LokiConfig,
12
+ ConfigInput as LokiConfigInput,
13
+ DistributorPushStream as LokiDistributorPushStream,
14
+ QuerierQueryRangeOptions as LokiQuerierQueryRangeOptions,
15
+ QuerierQueryResult as LokiQuerierQueryResult,
16
+ createClient as createLokiClient,
17
+ nanoSeconds,
18
+ } from '@hapic/loki';
19
+
20
+ export {
21
+ LokiClient,
22
+ LokiConfig,
23
+ LokiConfigInput,
24
+ LokiCompactorDeletionRequestCreate,
25
+ LokiDistributorPushStream,
26
+ LokiQuerierQueryRangeOptions,
27
+ LokiQuerierQueryResult,
28
+ createLokiClient,
29
+ nanoSeconds,
30
+ };
@@ -0,0 +1,26 @@
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 { LokiClient } from '@hapic/loki';
11
+
12
+ const instance = singa<LokiClient>({
13
+ name: 'loki',
14
+ });
15
+
16
+ export function setLokiFactory(factory: Factory<LokiClient>) {
17
+ instance.setFactory(factory);
18
+ }
19
+
20
+ export function isLokiClientUsable() {
21
+ return instance.has() || instance.hasFactory();
22
+ }
23
+
24
+ export function useLokiClient() {
25
+ return instance.use();
26
+ }
@@ -0,0 +1,21 @@
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 { MemoryLogStore } from '../../src';
9
+
10
+ describe('log-store', () => {
11
+ it('should permit label management', () => {
12
+ const store = new MemoryLogStore();
13
+ store.setLabels({ app: 'app' });
14
+ store.extendLabels({ foo: 'bar' });
15
+
16
+ expect(store.getLabels()).toEqual({
17
+ app: 'app',
18
+ foo: 'bar',
19
+ });
20
+ });
21
+ });
@@ -0,0 +1,36 @@
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 { wait } from '@privateaim/kit';
9
+ import { MemoryLogStore, createLogger } from '../../src';
10
+
11
+ describe('logger', () => {
12
+ it('should work with store', async () => {
13
+ const store = new MemoryLogStore();
14
+ store.setLabels({ app: 'app' });
15
+
16
+ const logger = createLogger({
17
+ store,
18
+ });
19
+
20
+ logger.info('foo', { meta: 'bar' });
21
+
22
+ await wait(0);
23
+
24
+ expect(store.items).toHaveLength(1);
25
+
26
+ const [item] = store.items;
27
+
28
+ expect(item.level).toEqual('info');
29
+ expect(item.message).toEqual('foo');
30
+ expect(item.labels).toEqual({
31
+ app: 'app',
32
+ meta: 'bar',
33
+ });
34
+ expect(item.time).toBeDefined();
35
+ });
36
+ });
@@ -1,15 +0,0 @@
1
- import type { DomainEventRecord } from '@privateaim/kit';
2
- export type DomainEventChannelName = string | ((id?: string | number) => string);
3
- export type DomainEventDestination = {
4
- namespace?: string;
5
- channel: DomainEventChannelName;
6
- };
7
- export type DomainEventDestinations = DomainEventDestination[];
8
- export type DomainEventPublishContext<T extends DomainEventRecord = DomainEventRecord> = {
9
- data: T;
10
- destinations: DomainEventDestinations;
11
- };
12
- export interface IDomainEventPublisher {
13
- publish(ctx: DomainEventPublishContext): Promise<void>;
14
- }
15
- //# sourceMappingURL=type.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/services/domain-event/type.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC;AACjF,MAAM,MAAM,sBAAsB,GAAG;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,sBAAsB,CAAA;CAClC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,sBAAsB,EAAE,CAAC;AAE/D,MAAM,MAAM,yBAAyB,CACjC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB,IAC/C;IACA,IAAI,EAAE,CAAC,CAAC;IACR,YAAY,EAAE,uBAAuB,CAAA;CACxC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IAClC,OAAO,CAAC,GAAG,EAAE,yBAAyB,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D"}
@@ -1,27 +0,0 @@
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 type { DomainEventRecord } from '@privateaim/kit';
9
-
10
- export type DomainEventChannelName = string | ((id?: string | number) => string);
11
- export type DomainEventDestination = {
12
- namespace?: string,
13
- channel: DomainEventChannelName
14
- };
15
-
16
- export type DomainEventDestinations = DomainEventDestination[];
17
-
18
- export type DomainEventPublishContext<
19
- T extends DomainEventRecord = DomainEventRecord,
20
- > = {
21
- data: T,
22
- destinations: DomainEventDestinations
23
- };
24
-
25
- export interface IDomainEventPublisher {
26
- publish(ctx: DomainEventPublishContext) : Promise<void>;
27
- }