@hiliosai/sdk 0.1.0 → 0.1.1

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.
@@ -0,0 +1,85 @@
1
+ import env from '@ltv/env';
2
+ import type {BrokerOptions} from 'moleculer';
3
+ import os from 'os';
4
+
5
+ import {bulkheadConfig} from './bulkhead';
6
+ import {ChannelsMiddleware} from './channels';
7
+ import {circuitBreakerConfig} from './circuit-breaker';
8
+ import logger from './logger';
9
+ import {metricsConfig} from './metrics';
10
+ import {registryConfig} from './registry';
11
+ import {retryPolicyConfig} from './retry-policy';
12
+ import {tracingConfig} from './tracing';
13
+ import {trackingConfig} from './tracking';
14
+
15
+ const pkgNm = env.string('NAMESPACE', 'hios');
16
+
17
+ const nodeID =
18
+ env.string('NODE_ID') ?? `${pkgNm}-${os.hostname()}-${process.pid}`;
19
+
20
+ // Export default config for backward compatibility
21
+ const configs: BrokerOptions = {
22
+ namespace: pkgNm,
23
+ nodeID,
24
+ metadata: {},
25
+ logger,
26
+ // Default log level for built-in console logger. It can be overwritten in logger options above.
27
+ // Available values: trace, debug, info, warn, error, fatal
28
+ logLevel: 'info',
29
+
30
+ cacher: env.string('REDIS_URL', 'Memory'),
31
+
32
+ // Define a serializer.
33
+ // Available values: "JSON", "Avro", "ProtoBuf", "MsgPack", "Notepack", "Thrift".
34
+ // More info: https://moleculer.services/docs/0.14/networking.html#Serialization
35
+ serializer: 'JSON',
36
+
37
+ // Number of milliseconds to wait before reject a request with a RequestTimeout error. Disabled: 0
38
+ requestTimeout: 10 * 1000,
39
+
40
+ // Retry policy settings. More info: https://moleculer.services/docs/0.14/fault-tolerance.html#Retry
41
+ retryPolicy: retryPolicyConfig,
42
+
43
+ // Limit of calling level. If it reaches the limit, broker will throw an MaxCallLevelError error. (Infinite loop protection)
44
+ maxCallLevel: 100,
45
+
46
+ // Number of seconds to send heartbeat packet to other nodes.
47
+ heartbeatInterval: 10,
48
+ // Number of seconds to wait before setting node to unavailable status.
49
+ heartbeatTimeout: 30,
50
+
51
+ // Cloning the params of context if enabled. High performance impact, use it with caution!
52
+ contextParamsCloning: false,
53
+
54
+ // Tracking requests and waiting for running requests before shuting down. More info: https://moleculer.services/docs/0.14/context.html#Context-tracking
55
+ tracking: trackingConfig,
56
+
57
+ // Disable built-in request & emit balancer. (Transporter must support it, as well.). More info: https://moleculer.services/docs/0.14/networking.html#Disabled-balancer
58
+ disableBalancer: false,
59
+
60
+ // Settings of Service Registry. More info: https://moleculer.services/docs/0.14/registry.html
61
+ registry: registryConfig,
62
+
63
+ // Settings of Circuit Breaker. More info: https://moleculer.services/docs/0.14/fault-tolerance.html#Circuit-Breaker
64
+ circuitBreaker: circuitBreakerConfig,
65
+
66
+ // Settings of bulkhead feature. More info: https://moleculer.services/docs/0.14/fault-tolerance.html#Bulkhead
67
+ bulkhead: bulkheadConfig,
68
+
69
+ // Enable action & event parameter validation. More info: https://moleculer.services/docs/0.14/validating.html
70
+ validator: 'Fastest',
71
+
72
+ // errorHandler: null,
73
+
74
+ transporter: env.string('TRANSPORTER_URL'),
75
+
76
+ // Enable/disable built-in metrics function. More info: https://moleculer.services/docs/0.14/metrics.html
77
+ metrics: metricsConfig,
78
+
79
+ // Enable built-in tracing function. More info: https://moleculer.services/docs/0.14/tracing.html
80
+ tracing: tracingConfig,
81
+
82
+ middlewares: [ChannelsMiddleware],
83
+ };
84
+
85
+ export default configs;
@@ -0,0 +1,17 @@
1
+ const loggerConfig = {
2
+ type: 'Console',
3
+ options: {
4
+ // Using colors on the output
5
+ colors: true,
6
+ // Print module names with different colors (like docker-compose for containers)
7
+ moduleColors: false,
8
+ // Line formatter. It can be "json", "short", "simple", "full", a `Function` or a template string like "{timestamp} {level} {nodeID}/{mod}: {msg}"
9
+ formatter: 'full',
10
+ // Custom object printer. If not defined, it uses the `util.inspect` method.
11
+ objectPrinter: null,
12
+ // Auto-padding the module name in order to messages begin at the same column.
13
+ autoPadding: false,
14
+ },
15
+ };
16
+
17
+ export default loggerConfig;
@@ -0,0 +1,20 @@
1
+ import type {MetricRegistry} from 'moleculer';
2
+
3
+ export const metricsConfig = {
4
+ enabled: false,
5
+ // Available built-in reporters: "Console", "CSV", "Event", "Prometheus", "Datadog", "StatsD"
6
+ reporter: {
7
+ type: 'Prometheus',
8
+ options: {
9
+ // HTTP port
10
+ port: 3030,
11
+ // HTTP URL path
12
+ path: '/metrics',
13
+ // Default labels which are appended to all metrics labels
14
+ defaultLabels: (registry: MetricRegistry) => ({
15
+ namespace: registry.broker.namespace,
16
+ nodeID: registry.broker.nodeID,
17
+ }),
18
+ },
19
+ },
20
+ };
@@ -0,0 +1,7 @@
1
+ export const registryConfig = {
2
+ // Define balancing strategy. More info: https://moleculer.services/docs/0.14/balancing.html
3
+ // Available values: "RoundRobin", "Random", "CpuUsage", "Latency", "Shard"
4
+ strategy: 'RoundRobin',
5
+ // Enable local action call preferring. Always call the local action instance if available.
6
+ preferLocal: true,
7
+ };
@@ -0,0 +1,17 @@
1
+ import type {Errors} from 'moleculer';
2
+
3
+ export const retryPolicyConfig = {
4
+ // Enable feature
5
+ enabled: false,
6
+ // Count of retries
7
+ retries: 5,
8
+ // First delay in milliseconds.
9
+ delay: 100,
10
+ // Maximum delay in milliseconds.
11
+ maxDelay: 1000,
12
+ // Backoff factor for delay. 2 means exponential backoff.
13
+ factor: 2,
14
+ // A function to check failed requests.
15
+ check: (err: Errors.MoleculerError | Error) =>
16
+ !!(err as Errors.MoleculerError).retryable,
17
+ };
@@ -0,0 +1,6 @@
1
+ export const tracingConfig = {
2
+ enabled: true,
3
+ exporter: 'Console',
4
+ events: true,
5
+ stackTrace: true,
6
+ };
@@ -0,0 +1,6 @@
1
+ export const trackingConfig = {
2
+ // Enable feature
3
+ enabled: false,
4
+ // Number of milliseconds to wait before shuting down the process.
5
+ shutdownTimeout: 5000,
6
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Base datasource interface that all datasources should implement
3
+ * Provides common structure and optional lifecycle methods
4
+ */
5
+ export interface BaseDatasource {
6
+ /**
7
+ * Datasource name for identification and logging
8
+ */
9
+ readonly name: string;
10
+
11
+ /**
12
+ * Optional initialization method
13
+ * Called when datasource is instantiated
14
+ */
15
+ init?(): Promise<void> | void;
16
+
17
+ /**
18
+ * Optional connection method
19
+ * Called when service starts if implemented
20
+ */
21
+ connect?(): Promise<void> | void;
22
+
23
+ /**
24
+ * Optional disconnection method
25
+ * Called when service stops if implemented
26
+ */
27
+ disconnect?(): Promise<void> | void;
28
+
29
+ /**
30
+ * Optional health check method
31
+ * Can be used to verify datasource is working correctly
32
+ */
33
+ healthCheck?(): Promise<boolean> | boolean;
34
+
35
+ /**
36
+ * Optional method to clear/reset the datasource
37
+ */
38
+ clear?(): Promise<void> | void;
39
+ }
40
+
41
+ /**
42
+ * Abstract base class for datasources that provides default implementations
43
+ * @example
44
+ * ```typescript
45
+ * export class UserDatasource extends AbstractDatasource {
46
+ * readonly name = 'users';
47
+ *
48
+ * private users: User[] = [];
49
+ *
50
+ * async findById(id: string): Promise<User | undefined> {
51
+ * return this.users.find(u => u.id === id);
52
+ * }
53
+ *
54
+ * async create(user: User): Promise<User> {
55
+ * this.users.push(user);
56
+ * return user;
57
+ * }
58
+ * }
59
+ * ```
60
+ */
61
+ export abstract class AbstractDatasource implements BaseDatasource {
62
+ abstract readonly name: string;
63
+
64
+ /**
65
+ * Default health check - always returns true
66
+ * Override for custom health check logic
67
+ */
68
+ async healthCheck(): Promise<boolean> {
69
+ return true;
70
+ }
71
+
72
+ /**
73
+ * Default clear method - does nothing
74
+ * Override to implement clearing logic
75
+ */
76
+ async clear(): Promise<void> {
77
+ // Default implementation - does nothing
78
+ }
79
+ }
@@ -0,0 +1 @@
1
+ export * from './base.datasource';
@@ -110,7 +110,10 @@ export const ContextHelpersMiddleware = {
110
110
  ctx.broker.logger.info('Audit log', {
111
111
  action,
112
112
  resource: resource
113
- ? {type: typeof resource, id: (resource as Record<string, unknown>).id}
113
+ ? {
114
+ type: typeof resource,
115
+ id: (resource as Record<string, unknown>).id,
116
+ }
114
117
  : undefined,
115
118
  userId: ctx.meta.user?.id,
116
119
  tenantId: ctx.meta.tenantId,
@@ -1,10 +1,12 @@
1
1
  import type {Context} from 'moleculer';
2
+ import type {BaseDatasource} from '../datasources';
2
3
 
3
4
  /**
4
5
  * Datasource constructor registry type
6
+ * All datasources should implement BaseDatasource interface
5
7
  */
6
8
  export interface DatasourceConstructorRegistry {
7
- [key: string]: new () => object;
9
+ [key: string]: new () => BaseDatasource | object;
8
10
  }
9
11
 
10
12
  /**
@@ -3,4 +3,3 @@ export * from './memoize.middleware';
3
3
  export * from './health.middleware';
4
4
  export * from './permissions.middleware';
5
5
  export * from './context-helpers.middleware';
6
- export * from './cache.middleware';
@@ -0,0 +1,118 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type {ServiceSchema} from 'moleculer';
3
+ import type {BaseDatasource} from '../datasources';
4
+ import type {AppContext} from '../types/context';
5
+
6
+ /**
7
+ * Datasource constructor registry type
8
+ * All datasources should implement BaseDatasource interface
9
+ */
10
+ export interface DatasourceConstructorRegistry {
11
+ [key: string]: new () => BaseDatasource | object;
12
+ }
13
+
14
+ /**
15
+ * Datasource instance registry type
16
+ */
17
+ export interface DatasourceInstanceRegistry {
18
+ [key: string]: object;
19
+ }
20
+
21
+ /**
22
+ * Creates a Moleculer mixin for datasource injection
23
+ * Simple mixin that just instantiates datasources and injects them into context
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import {DatasourceMixin} from '@pkg/sdk';
28
+ *
29
+ * export default {
30
+ * name: 'users',
31
+ * mixins: [DatasourceMixin({
32
+ * userDb: UserDatasource,
33
+ * cache: CacheDatasource,
34
+ * })],
35
+ * actions: {
36
+ * get: {
37
+ * handler(ctx) {
38
+ * // Access datasources via ctx.datasources
39
+ * return ctx.datasources.userDb.findById(ctx.params.id);
40
+ * }
41
+ * }
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ export function DatasourceMixin(
47
+ datasourceConstructors: DatasourceConstructorRegistry = {}
48
+ ): Partial<ServiceSchema> {
49
+ // Initialize datasources once
50
+ const datasourceInstances: DatasourceInstanceRegistry = {};
51
+
52
+ for (const [key, DatasourceClass] of Object.entries(datasourceConstructors)) {
53
+ datasourceInstances[key] = new DatasourceClass();
54
+ }
55
+
56
+ return {
57
+ /**
58
+ * Service created lifecycle hook
59
+ * Initialize datasources and store on service
60
+ */
61
+ async created() {
62
+ // Call init() on datasources that have it
63
+ for (const [, datasource] of Object.entries(datasourceInstances)) {
64
+ if (typeof (datasource as any).init === 'function') {
65
+ await (datasource as any).init();
66
+ }
67
+ }
68
+
69
+ // Store instances on the service for access in methods
70
+ (this as any).$datasources = datasourceInstances;
71
+ },
72
+
73
+ /**
74
+ * Service started lifecycle hook
75
+ * Connect datasources that have connect method
76
+ */
77
+ async started() {
78
+ // Call connect() on datasources that have it
79
+ for (const [, datasource] of Object.entries(datasourceInstances)) {
80
+ if (typeof (datasource as any).connect === 'function') {
81
+ await (datasource as any).connect();
82
+ }
83
+ }
84
+ },
85
+
86
+ /**
87
+ * Service stopped lifecycle hook
88
+ * Disconnect datasources that have disconnect method
89
+ */
90
+ async stopped() {
91
+ // Call disconnect() on datasources that have it
92
+ for (const [, datasource] of Object.entries(datasourceInstances)) {
93
+ if (typeof (datasource as any).disconnect === 'function') {
94
+ await (datasource as any).disconnect();
95
+ }
96
+ }
97
+ },
98
+
99
+ /**
100
+ * Hooks to inject datasources into context
101
+ */
102
+ hooks: {
103
+ before: {
104
+ '*': function injectDatasources(ctx) {
105
+ // Inject datasources into the context
106
+ (ctx as AppContext).datasources = (this as any).$datasources ?? {};
107
+ },
108
+ },
109
+ },
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Type helper for services that use the datasource mixin
115
+ */
116
+ export type ServiceWithDatasources<T extends DatasourceInstanceRegistry> = {
117
+ datasources: T;
118
+ };
@@ -0,0 +1 @@
1
+ export * from './datasource.mixin';