@hiliosai/sdk 0.1.12 → 0.1.14

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 (53) hide show
  1. package/dist/index.d.ts +904 -0
  2. package/dist/index.js +1809 -0
  3. package/package.json +11 -2
  4. package/src/configs/constants.ts +0 -135
  5. package/src/configs/index.ts +0 -2
  6. package/src/configs/moleculer/bulkhead.ts +0 -8
  7. package/src/configs/moleculer/channels.ts +0 -102
  8. package/src/configs/moleculer/circuit-breaker.ts +0 -17
  9. package/src/configs/moleculer/index.ts +0 -98
  10. package/src/configs/moleculer/logger.ts +0 -17
  11. package/src/configs/moleculer/metrics.ts +0 -20
  12. package/src/configs/moleculer/registry.ts +0 -7
  13. package/src/configs/moleculer/retry-policy.ts +0 -17
  14. package/src/configs/moleculer/tracing.ts +0 -6
  15. package/src/configs/moleculer/tracking.ts +0 -6
  16. package/src/configs/permissions.ts +0 -109
  17. package/src/datasources/base.datasource.ts +0 -111
  18. package/src/datasources/extensions/index.ts +0 -11
  19. package/src/datasources/extensions/retry.extension.ts +0 -91
  20. package/src/datasources/extensions/soft-delete.extension.ts +0 -114
  21. package/src/datasources/extensions/tenant.extension.ts +0 -105
  22. package/src/datasources/index.ts +0 -3
  23. package/src/datasources/prisma.datasource.ts +0 -317
  24. package/src/env.ts +0 -12
  25. package/src/errors/auth.error.ts +0 -33
  26. package/src/errors/index.ts +0 -2
  27. package/src/errors/permission.error.ts +0 -17
  28. package/src/index.ts +0 -10
  29. package/src/middlewares/context-helpers.middleware.ts +0 -162
  30. package/src/middlewares/datasource.middleware.ts +0 -73
  31. package/src/middlewares/health.middleware.ts +0 -134
  32. package/src/middlewares/index.ts +0 -5
  33. package/src/middlewares/memoize.middleware.ts +0 -33
  34. package/src/middlewares/permissions.middleware.ts +0 -162
  35. package/src/mixins/datasource.mixin.ts +0 -111
  36. package/src/mixins/index.ts +0 -1
  37. package/src/service/define-integration.ts +0 -404
  38. package/src/service/define-service.ts +0 -58
  39. package/src/types/channels.ts +0 -60
  40. package/src/types/context.ts +0 -64
  41. package/src/types/datasource.ts +0 -23
  42. package/src/types/index.ts +0 -9
  43. package/src/types/integration.ts +0 -28
  44. package/src/types/message.ts +0 -128
  45. package/src/types/platform.ts +0 -39
  46. package/src/types/service.ts +0 -209
  47. package/src/types/tenant.ts +0 -4
  48. package/src/types/user.ts +0 -16
  49. package/src/utils/context-cache.ts +0 -70
  50. package/src/utils/index.ts +0 -8
  51. package/src/utils/permission-calculator.ts +0 -62
  52. package/tsconfig.json +0 -13
  53. package/tsup.config.ts +0 -5
@@ -1,134 +0,0 @@
1
- import env from '@ltv/env';
2
- import http from 'http';
3
- import type {Service} from 'moleculer';
4
-
5
- const HEALTH_CHECK_PORT = env.int('HEALTH_CHECK_PORT', 3301);
6
- const HEALTH_CHECK_READINESS_PATH = env.string(
7
- 'HEALTH_CHECK_READINESS_PATH',
8
- '/readyz'
9
- );
10
- const HEALTH_CHECK_LIVENESS_PATH = env.string(
11
- 'HEALTH_CHECK_LIVENESS_PATH',
12
- '/livez'
13
- );
14
-
15
- interface HealthCheckOptions {
16
- port?: number;
17
- readiness?: {
18
- path?: string;
19
- };
20
- liveness?: {
21
- path?: string;
22
- };
23
- }
24
-
25
- interface HealthCheckConfig {
26
- port: number;
27
- readiness: {
28
- path: string;
29
- };
30
- liveness: {
31
- path: string;
32
- };
33
- }
34
-
35
- // Default configuration constants
36
- export const HEALTH_CHECK_DEFAULTS = {
37
- PORT: HEALTH_CHECK_PORT,
38
- READINESS_PATH: HEALTH_CHECK_READINESS_PATH,
39
- LIVENESS_PATH: HEALTH_CHECK_LIVENESS_PATH,
40
- } as const;
41
-
42
- export function CreateHealthCheckMiddleware(opts: HealthCheckOptions = {}) {
43
- // Merge user options with defaults
44
- const config: HealthCheckConfig = {
45
- port: opts.port ?? HEALTH_CHECK_DEFAULTS.PORT,
46
- readiness: {
47
- path: opts.readiness?.path ?? HEALTH_CHECK_DEFAULTS.READINESS_PATH,
48
- },
49
- liveness: {
50
- path: opts.liveness?.path ?? HEALTH_CHECK_DEFAULTS.LIVENESS_PATH,
51
- },
52
- };
53
-
54
- type HealthState = 'down' | 'starting' | 'up' | 'stopping';
55
-
56
- let state: HealthState = 'down';
57
- let server: http.Server;
58
-
59
- function handler(req: http.IncomingMessage, res: http.ServerResponse) {
60
- // Prevent headers from being sent multiple times
61
- if (res.headersSent) {
62
- return;
63
- }
64
-
65
- if (req.url === config.readiness.path || req.url === config.liveness.path) {
66
- const resHeader = {
67
- 'Content-Type': 'application/json; charset=utf-8',
68
- };
69
-
70
- const content = {
71
- state,
72
- uptime: process.uptime(),
73
- timestamp: Date.now(),
74
- };
75
-
76
- if (req.url === config.readiness.path) {
77
- // Readiness if the broker started successfully.
78
- // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes
79
- res.writeHead(state === 'up' ? 200 : 503, resHeader);
80
- } else {
81
- // Liveness if the broker is not stopped.
82
- // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command
83
- res.writeHead(state !== 'down' ? 200 : 503, resHeader);
84
- }
85
-
86
- res.end(JSON.stringify(content, null, 2));
87
- } else {
88
- res.writeHead(404, http.STATUS_CODES[404] ?? 'Not Found', {});
89
- res.end();
90
- }
91
- }
92
-
93
- return {
94
- created(broker: Service) {
95
- state = 'starting';
96
-
97
- server = http.createServer(handler);
98
- server.listen(config.port, (err?: Error | undefined) => {
99
- if (err) {
100
- return broker.logger.error(
101
- 'Unable to start health-check server',
102
- err
103
- );
104
- }
105
-
106
- broker.logger.info('');
107
- broker.logger.info('K8s health-check server listening on');
108
- broker.logger.info(
109
- ` http://localhost:${config.port}${config.readiness.path}`
110
- );
111
- broker.logger.info(
112
- ` http://localhost:${config.port}${config.liveness.path}`
113
- );
114
- broker.logger.info('');
115
- });
116
- },
117
-
118
- // After broker started
119
- started() {
120
- state = 'up';
121
- },
122
-
123
- // Before broker stopping
124
- stopping() {
125
- state = 'stopping';
126
- },
127
-
128
- // After broker stopped
129
- stopped() {
130
- state = 'down';
131
- server.close();
132
- },
133
- };
134
- }
@@ -1,5 +0,0 @@
1
- export * from './datasource.middleware';
2
- export * from './memoize.middleware';
3
- export * from './health.middleware';
4
- export * from './permissions.middleware';
5
- export * from './context-helpers.middleware';
@@ -1,33 +0,0 @@
1
- import type {ServiceSchema, ServiceSettingSchema} from 'moleculer';
2
-
3
- export interface MemoizeMixinOptions {
4
- ttl?: number;
5
- }
6
-
7
- export function MemoizeMixin(
8
- options?: MemoizeMixinOptions
9
- ): ServiceSchema<ServiceSettingSchema> {
10
- return {
11
- name: '',
12
- methods: {
13
- async memoize(name: string, params: unknown, fn: () => Promise<unknown>) {
14
- if (!this.broker.cacher) return fn();
15
-
16
- const key = this.broker.cacher.defaultKeygen(
17
- `${this.name}:memoize-${name}`,
18
- params as object | null,
19
- {},
20
- []
21
- );
22
-
23
- let res = await this.broker.cacher.get(key);
24
- if (res) return res;
25
-
26
- res = (await fn()) as unknown as object;
27
- this.broker.cacher.set(key, res, options?.ttl);
28
-
29
- return res;
30
- },
31
- },
32
- };
33
- }
@@ -1,162 +0,0 @@
1
- import type {ActionSchema, Service} from 'moleculer';
2
-
3
- import {PERMISSIONS, ROLE_PERMISSIONS} from '../configs';
4
- import {isDev} from '../env';
5
-
6
- import {PermissionError} from '../errors';
7
- import type {AppContext} from '../types';
8
-
9
- export type Permission =
10
- | keyof typeof PERMISSIONS
11
- | string
12
- | ((ctx: AppContext, action: ActionSchema) => Promise<boolean> | boolean);
13
-
14
- // Middleware interface for type safety
15
- export interface ActionWithPermissions extends ActionSchema {
16
- permissions?: Permission | Permission[];
17
- }
18
-
19
- const permissionHandlers = {
20
- [PERMISSIONS.AUTHENTICATED]: async (ctx: AppContext) => !!ctx.meta.user?.id,
21
-
22
- [PERMISSIONS.TENANT_OWNER]: async (ctx: AppContext) =>
23
- ctx.meta.user?.roles.includes(PERMISSIONS.OWNER) ?? false,
24
-
25
- [PERMISSIONS.TENANT_MEMBER]: async (ctx: AppContext) =>
26
- !!(
27
- ctx.meta.user?.tenantId &&
28
- ctx.meta.tenantId &&
29
- ctx.meta.user.tenantId === ctx.meta.tenantId
30
- ),
31
- };
32
-
33
- export const PermissionsMiddleware = {
34
- // Wrap local action handlers
35
- localAction(
36
- handler: (...args: unknown[]) => unknown,
37
- action: ActionWithPermissions
38
- ) {
39
- // If permissions are not defined, return original handler
40
- if (!action.permissions) {
41
- return handler;
42
- }
43
-
44
- const permissions = Array.isArray(action.permissions)
45
- ? action.permissions
46
- : [action.permissions];
47
-
48
- const permissionNames: string[] = [];
49
- const permissionFunctions: Array<
50
- (ctx: AppContext, action: ActionSchema) => Promise<boolean> | boolean
51
- > = [];
52
-
53
- // Process each permission
54
- permissions.forEach((permission) => {
55
- if (typeof permission === 'function') {
56
- // Add custom permission function
57
- permissionFunctions.push(permission);
58
- return;
59
- }
60
-
61
- if (typeof permission === 'string') {
62
- // Check if it's a built-in permission handler
63
- if (permission in permissionHandlers) {
64
- const handler =
65
- permissionHandlers[permission as keyof typeof permissionHandlers];
66
- permissionFunctions.push(handler);
67
- return;
68
- }
69
-
70
- // Otherwise, treat as a permission name to check against user roles
71
- permissionNames.push(permission);
72
- return;
73
- }
74
- });
75
-
76
- return async function CheckPermissionsMiddleware(
77
- this: Service,
78
- ctx: AppContext
79
- ) {
80
- let hasAccess = false;
81
-
82
- // Check if user has OWNER role (super admin within tenant)
83
- if (ctx.meta.user?.roles.includes(PERMISSIONS.OWNER)) {
84
- hasAccess = true;
85
- }
86
-
87
- // REMOVED: DEVELOPER role bypass - security vulnerability
88
- // Developers must have explicit permissions like any other role
89
-
90
- // Check custom permission functions
91
- if (!hasAccess && permissionFunctions.length > 0) {
92
- const results = await Promise.allSettled(
93
- permissionFunctions.map((fn) => fn(ctx, action))
94
- );
95
-
96
- hasAccess = results.some(
97
- (result) => result.status === 'fulfilled' && !!result.value
98
- );
99
-
100
- // Log failed permissions without exposing details
101
- const failures = results.filter((r) => r.status === 'rejected');
102
- if (failures.length > 0) {
103
- ctx.broker.logger.warn(
104
- `${failures.length} permission functions failed`,
105
- {
106
- action: action.name,
107
- userId: ctx.meta.user?.id,
108
- }
109
- );
110
- }
111
- }
112
-
113
- // Check named permissions against user roles
114
- if (!hasAccess && permissionNames.length > 0) {
115
- const userRoles = ctx.meta.user?.roles ?? [];
116
-
117
- // Check if user has any role that includes the required permissions
118
- hasAccess = userRoles.some((role: string) => {
119
- const rolePermissions = ROLE_PERMISSIONS[role] ?? [];
120
- return permissionNames.some((permName) =>
121
- rolePermissions.includes(permName)
122
- );
123
- });
124
- }
125
-
126
- // Throw error if access denied
127
- if (!hasAccess) {
128
- const user = ctx.meta.user;
129
- ctx.broker.logger.warn('Access denied:', {
130
- action: action.name,
131
- userId: user?.id,
132
- userRoles: user?.roles,
133
- tenantId: ctx.meta.tenantId,
134
- requiredPermissions: permissions,
135
- });
136
-
137
- // Sanitize error details for production
138
- const errorDetails = isDev
139
- ? {
140
- action: action.name,
141
- requiredPermissions: permissions.map((p) =>
142
- typeof p === 'function' ? '[Function]' : String(p)
143
- ),
144
- userRoles: user?.roles ?? [],
145
- userId: user?.id,
146
- tenantId: ctx.meta.tenantId,
147
- }
148
- : {
149
- action: action.name,
150
- };
151
-
152
- throw new PermissionError(
153
- 'You do not have permission to perform this action',
154
- errorDetails
155
- );
156
- }
157
-
158
- // Call the original handler
159
- return handler.call(this, ctx);
160
- };
161
- },
162
- };
@@ -1,111 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type {ServiceSchema} from 'moleculer';
3
- import type {
4
- DatasourceConstructorRegistry,
5
- DatasourceInstanceRegistry,
6
- } from '../middlewares/datasource.middleware';
7
- import type {AppContext} from '../types/context';
8
-
9
- /**
10
- * Creates a Moleculer mixin for datasource injection
11
- * Simple mixin that just instantiates datasources and injects them into context
12
- *
13
- * @example
14
- * ```typescript
15
- * import {DatasourceMixin} from '@pkg/sdk';
16
- *
17
- * export default {
18
- * name: 'users',
19
- * mixins: [DatasourceMixin({
20
- * userDb: UserDatasource,
21
- * cache: CacheDatasource,
22
- * })],
23
- * actions: {
24
- * get: {
25
- * handler(ctx) {
26
- * // Access datasources via ctx.datasources
27
- * return ctx.datasources.userDb.findById(ctx.params.id);
28
- * }
29
- * }
30
- * }
31
- * }
32
- * ```
33
- */
34
- export function DatasourceMixin(
35
- datasourceConstructors: DatasourceConstructorRegistry = {}
36
- ): Partial<ServiceSchema> {
37
- // Initialize datasources once
38
- const datasourceInstances: DatasourceInstanceRegistry = {};
39
-
40
- for (const [key, DatasourceClass] of Object.entries(datasourceConstructors)) {
41
- datasourceInstances[key] = new DatasourceClass();
42
- }
43
-
44
- return {
45
- /**
46
- * Service created lifecycle hook
47
- * Initialize datasources and store on service
48
- */
49
- async created() {
50
- // Inject broker into datasources
51
- for (const [, datasource] of Object.entries(datasourceInstances)) {
52
- (datasource as any).broker = this.broker;
53
- }
54
-
55
- // Call init() on datasources that have it
56
- for (const [, datasource] of Object.entries(datasourceInstances)) {
57
- if (typeof (datasource as any).init === 'function') {
58
- await (datasource as any).init();
59
- }
60
- }
61
-
62
- // Store instances on the service for access in methods
63
- (this as any).$datasources = datasourceInstances;
64
- },
65
-
66
- /**
67
- * Service started lifecycle hook
68
- * Connect datasources that have connect method
69
- */
70
- async started() {
71
- // Call connect() on datasources that have it
72
- for (const [, datasource] of Object.entries(datasourceInstances)) {
73
- if (typeof (datasource as any).connect === 'function') {
74
- await (datasource as any).connect();
75
- }
76
- }
77
- },
78
-
79
- /**
80
- * Service stopped lifecycle hook
81
- * Disconnect datasources that have disconnect method
82
- */
83
- async stopped() {
84
- // Call disconnect() on datasources that have it
85
- for (const [, datasource] of Object.entries(datasourceInstances)) {
86
- if (typeof (datasource as any).disconnect === 'function') {
87
- await (datasource as any).disconnect();
88
- }
89
- }
90
- },
91
-
92
- /**
93
- * Hooks to inject datasources into context
94
- */
95
- hooks: {
96
- before: {
97
- '*': function injectDatasources(ctx) {
98
- const datasources = (this as any).$datasources ?? {};
99
-
100
- // Inject current context into all datasources
101
- for (const [, datasource] of Object.entries(datasources)) {
102
- (datasource as any).context = ctx;
103
- }
104
-
105
- // Inject datasources into the context
106
- (ctx as AppContext).datasources = datasources;
107
- },
108
- },
109
- },
110
- };
111
- }
@@ -1 +0,0 @@
1
- export * from './datasource.mixin';