@hiliosai/sdk 0.1.12 → 0.1.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 (53) hide show
  1. package/dist/index.d.ts +904 -0
  2. package/dist/index.js +1809 -0
  3. package/package.json +10 -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,11 +0,0 @@
1
- // Export all Prisma extensions for easy importing
2
- export { softDeleteExtension } from './soft-delete.extension';
3
- export { createTenantExtension } from './tenant.extension';
4
- export { retryExtension } from './retry.extension';
5
-
6
- // Re-export extension interfaces
7
- export type {
8
- SoftDeleteExtension,
9
- AuditTrailExtension,
10
- TenantExtension,
11
- } from '../prisma.datasource';
@@ -1,91 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /**
3
- * Retry Extension for Prisma Transactions
4
- * Automatically retries failed transactions with exponential backoff
5
- *
6
- * Usage:
7
- * ```typescript
8
- * import { retryExtension } from './extensions/retry.extension';
9
- *
10
- * class MyPrismaDS extends PrismaDatasource<PrismaClient> {
11
- * protected applyExtensions(client: PrismaClient) {
12
- * return client.$extends(retryExtension);
13
- * }
14
- * }
15
- * ```
16
- */
17
-
18
- export const retryExtension = {
19
- name: 'Retry',
20
-
21
- client: {
22
- async $retryTransaction<T>(
23
- fn: (tx: any) => Promise<T>,
24
- options: {
25
- maxRetries?: number;
26
- baseDelay?: number;
27
- maxDelay?: number;
28
- retryableErrors?: string[];
29
- } = {}
30
- ): Promise<T> {
31
- const {
32
- maxRetries = 3,
33
- baseDelay = 100,
34
- maxDelay = 5000,
35
- retryableErrors = [
36
- 'P2034', // Transaction failed due to a write conflict
37
- 'P2002', // Unique constraint failed
38
- 'P5000', // Raw query failed
39
- ],
40
- } = options;
41
-
42
- let lastError: Error | undefined;
43
-
44
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
45
- try {
46
- return await (this as any).$transaction(fn, {
47
- timeout: 10000,
48
- maxWait: 5000,
49
- });
50
- } catch (error: any) {
51
- lastError = error;
52
-
53
- // Check if error is retryable
54
- const isRetryable = retryableErrors.some(
55
- (code) => error.code === code || error.message?.includes(code)
56
- );
57
-
58
- if (!isRetryable || attempt === maxRetries) {
59
- throw error;
60
- }
61
-
62
- // Calculate delay with exponential backoff and jitter
63
- const delay = Math.min(
64
- baseDelay * Math.pow(2, attempt) + Math.random() * 100,
65
- maxDelay
66
- );
67
-
68
- // Log retry attempt (in production, use proper logger)
69
- if (
70
- typeof globalThis !== 'undefined' &&
71
- (globalThis as any).console
72
- ) {
73
- (globalThis as any).console.warn(
74
- `Transaction retry ${
75
- attempt + 1
76
- }/${maxRetries} after ${delay}ms:`,
77
- {
78
- error: error.message,
79
- code: error.code,
80
- }
81
- );
82
- }
83
-
84
- await new Promise((resolve) => setTimeout(resolve, delay));
85
- }
86
- }
87
-
88
- throw lastError ?? new Error('Transaction failed after maximum retries');
89
- },
90
- },
91
- };
@@ -1,114 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /**
3
- * Soft Delete Extension for Prisma
4
- * Automatically handles soft deletes by setting deletedAt instead of removing records
5
- *
6
- * Usage:
7
- * ```typescript
8
- * import { softDeleteExtension } from './extensions/soft-delete.extension';
9
- *
10
- * class MyPrismaDS extends PrismaDatasource<PrismaClient> {
11
- * protected applyExtensions(client: PrismaClient) {
12
- * return client.$extends(softDeleteExtension);
13
- * }
14
- * }
15
- * ```
16
- *
17
- * Requires models to have `deletedAt DateTime?` field
18
- */
19
-
20
- export const softDeleteExtension = {
21
- name: 'SoftDelete',
22
-
23
- query: {
24
- // Apply to all models
25
- $allModels: {
26
- // Override findMany to exclude soft deleted records
27
- async findMany({args, query}: any) {
28
- // Add deletedAt: null filter if not already specified
29
- args.where ??= {};
30
- if (args.where.deletedAt === undefined) {
31
- args.where.deletedAt = null;
32
- }
33
- return query(args);
34
- },
35
-
36
- // Override findUnique to exclude soft deleted records
37
- async findUnique({args, query}: any) {
38
- args.where ??= {};
39
- if (args.where.deletedAt === undefined) {
40
- args.where.deletedAt = null;
41
- }
42
- return query(args);
43
- },
44
-
45
- // Override findFirst to exclude soft deleted records
46
- async findFirst({args, query}: any) {
47
- args.where ??= {};
48
- if (args.where.deletedAt === undefined) {
49
- args.where.deletedAt = null;
50
- }
51
- return query(args);
52
- },
53
-
54
- // Override delete to set deletedAt instead
55
- async delete({args, query}: any) {
56
- return query({
57
- ...args,
58
- data: {deletedAt: new Date()},
59
- });
60
- },
61
-
62
- // Override deleteMany to set deletedAt instead
63
- async deleteMany({args, query}: any) {
64
- return query({
65
- ...args,
66
- data: {deletedAt: new Date()},
67
- });
68
- },
69
- },
70
- },
71
-
72
- model: {
73
- $allModels: {
74
- // Add restore method to all models
75
- async restore<T>(this: T, where: any): Promise<any> {
76
- const context =
77
- (globalThis as any).Prisma?.getExtensionContext?.(this) ?? this;
78
-
79
- return (context as any).update({
80
- where,
81
- data: {deletedAt: null},
82
- });
83
- },
84
-
85
- // Add findWithDeleted method to include soft deleted records
86
- async findWithDeleted<T>(this: T, args: any): Promise<any> {
87
- const context =
88
- (globalThis as any).Prisma?.getExtensionContext?.(this) ?? this;
89
-
90
- return (context as any).findMany({
91
- ...args,
92
- where: {
93
- ...args.where,
94
- // Don't filter by deletedAt
95
- },
96
- });
97
- },
98
-
99
- // Add findDeleted method to find only soft deleted records
100
- async findDeleted<T>(this: T, args: any): Promise<any> {
101
- const context =
102
- (globalThis as any).Prisma?.getExtensionContext?.(this) ?? this;
103
-
104
- return (context as any).findMany({
105
- ...args,
106
- where: {
107
- ...args.where,
108
- deletedAt: {not: null},
109
- },
110
- });
111
- },
112
- },
113
- },
114
- };
@@ -1,105 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /**
3
- * Multi-Tenant Extension for Prisma
4
- * Automatically filters queries by tenant context for multi-tenant applications
5
- *
6
- * Usage:
7
- * ```typescript
8
- * import { createTenantExtension } from './extensions/tenant.extension';
9
- *
10
- * class MyPrismaDS extends PrismaDatasource<PrismaClient> {
11
- * protected applyExtensions(client: PrismaClient) {
12
- * return client.$extends(createTenantExtension());
13
- * }
14
- * }
15
- * ```
16
- *
17
- * Requires models to have `tenantId String` field
18
- */
19
-
20
- interface TenantContext {
21
- currentTenantId: string | null;
22
- }
23
-
24
- export function createTenantExtension() {
25
- const tenantContext: TenantContext = {
26
- currentTenantId: null,
27
- };
28
-
29
- return {
30
- name: 'Tenant',
31
-
32
- client: {
33
- // Set tenant context
34
- $setTenant(tenantId: string) {
35
- tenantContext.currentTenantId = tenantId;
36
- },
37
-
38
- // Get current tenant
39
- $getCurrentTenant() {
40
- return tenantContext.currentTenantId;
41
- },
42
-
43
- // Clear tenant context
44
- $clearTenant() {
45
- tenantContext.currentTenantId = null;
46
- },
47
- },
48
-
49
- query: {
50
- $allModels: {
51
- // Automatically add tenantId filter to all read operations
52
- async findMany({args, query}: any) {
53
- if (tenantContext.currentTenantId) {
54
- args.where ??= {};
55
- args.where.tenantId ??= tenantContext.currentTenantId;
56
- }
57
- return query(args);
58
- },
59
-
60
- async findUnique({args, query}: any) {
61
- if (tenantContext.currentTenantId) {
62
- args.where ??= {};
63
- args.where.tenantId ??= tenantContext.currentTenantId;
64
- }
65
- return query(args);
66
- },
67
-
68
- async findFirst({args, query}: any) {
69
- if (tenantContext.currentTenantId) {
70
- args.where ??= {};
71
- args.where.tenantId ??= tenantContext.currentTenantId;
72
- }
73
- return query(args);
74
- },
75
-
76
- // Automatically add tenantId to create operations
77
- async create({args, query}: any) {
78
- if (tenantContext.currentTenantId) {
79
- args.data ??= {};
80
- args.data.tenantId ??= tenantContext.currentTenantId;
81
- }
82
- return query(args);
83
- },
84
-
85
- // Add tenantId filter to update operations
86
- async update({args, query}: any) {
87
- if (tenantContext.currentTenantId) {
88
- args.where ??= {};
89
- args.where.tenantId ??= tenantContext.currentTenantId;
90
- }
91
- return query(args);
92
- },
93
-
94
- // Add tenantId filter to delete operations
95
- async delete({args, query}: any) {
96
- if (tenantContext.currentTenantId) {
97
- args.where ??= {};
98
- args.where.tenantId ??= tenantContext.currentTenantId;
99
- }
100
- return query(args);
101
- },
102
- },
103
- },
104
- };
105
- }
@@ -1,3 +0,0 @@
1
- export * from './base.datasource';
2
- export * from './prisma.datasource';
3
- export * from './extensions';
@@ -1,317 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import {isDev, isTest} from '../env';
3
- import type {AppContext} from '../types/context';
4
- import {AbstractDatasource} from './base.datasource';
5
-
6
- // Generic Prisma Client interface - will be provided by generated client
7
- // Made very permissive to work with any generated Prisma client
8
- interface PrismaClientLike {
9
- $connect(): Promise<void>;
10
- $disconnect(): Promise<void>;
11
- $queryRaw: any;
12
- $transaction: any;
13
- $extends: any;
14
- [key: string]: any;
15
- }
16
-
17
- // Extension interfaces for common production patterns
18
- export interface SoftDeleteExtension {
19
- // Adds soft delete functionality to models
20
- softDelete: {
21
- [model: string]: {
22
- delete: (args: any) => Promise<any>;
23
- deleteMany: (args: any) => Promise<any>;
24
- restore: (args: any) => Promise<any>;
25
- };
26
- };
27
- }
28
-
29
- export interface AuditTrailExtension {
30
- // Adds audit trail functionality
31
- $auditTrail: {
32
- getHistory: (model: string, id: string) => Promise<any[]>;
33
- getChanges: (
34
- model: string,
35
- id: string,
36
- from?: Date,
37
- to?: Date
38
- ) => Promise<any[]>;
39
- };
40
- }
41
-
42
- export interface TenantExtension {
43
- // Adds multi-tenant filtering
44
- $tenant: {
45
- setContext: (tenantId: string) => void;
46
- getCurrentTenant: () => string | null;
47
- };
48
- }
49
-
50
- // Global singleton pattern for Prisma Client
51
- declare global {
52
- var __prisma: PrismaClientLike | undefined;
53
- }
54
-
55
- /**
56
- * Prisma datasource that follows singleton pattern and best practices
57
- * Supports both provided client instances and automatic initialization
58
- *
59
- * @example
60
- * ```typescript
61
- * // Option 1: Let datasource create singleton client
62
- * datasources: {
63
- * prisma: PrismaDatasource
64
- * }
65
- *
66
- * // Option 2: Provide custom client
67
- * const customClient = new PrismaClient({...});
68
- * datasources: {
69
- * prisma: () => new PrismaDatasource(customClient)
70
- * }
71
- * ```
72
- */
73
- export class PrismaDatasource<
74
- TPrismaClient extends PrismaClientLike = PrismaClientLike,
75
- TContext = AppContext
76
- > extends AbstractDatasource<TContext> {
77
- readonly name = 'prisma';
78
- private _client: TPrismaClient | null = null;
79
- private providedClient: TPrismaClient | null = null;
80
-
81
- constructor(prismaClient?: TPrismaClient) {
82
- super();
83
- this.providedClient = prismaClient ?? null;
84
- }
85
-
86
- /**
87
- * Get Prisma client instance (singleton pattern)
88
- */
89
- get client(): TPrismaClient {
90
- this._client ??= this.initializePrismaClient();
91
-
92
- // Update tenant context from current service context
93
- this.updateTenantFromContext();
94
-
95
- return this._client;
96
- }
97
-
98
- /**
99
- * Initialize Prisma client using singleton pattern or provided instance
100
- */
101
- private initializePrismaClient(): TPrismaClient {
102
- // Option 1: Use provided client instance
103
- if (this.providedClient) {
104
- this.broker.logger.info('Using provided PrismaClient instance');
105
- return this.providedClient;
106
- }
107
-
108
- // Option 2: Use global singleton (recommended for most cases)
109
- if (!globalThis.__prisma) {
110
- this.broker.logger.info('Creating new PrismaClient singleton');
111
- const baseClient = this.createClient();
112
- globalThis.__prisma = this.applyExtensions(baseClient);
113
- } else {
114
- this.broker.logger.info('Using existing PrismaClient singleton');
115
- }
116
-
117
- return globalThis.__prisma as TPrismaClient;
118
- }
119
-
120
- /**
121
- * Create a new Prisma client instance
122
- * Override this method in subclasses to provide the actual PrismaClient
123
- */
124
- protected createClient(): TPrismaClient {
125
- throw new Error(
126
- 'createClient() must be implemented by subclass or provide PrismaClient in constructor. ' +
127
- 'Example: class MyPrismaDataSource extends PrismaDatasource { ' +
128
- 'protected createClient() { return new PrismaClient(); } }'
129
- );
130
- }
131
-
132
- /**
133
- * Apply extensions to the Prisma client
134
- * Override this method to add production extensions like soft delete, audit trails, etc.
135
- */
136
- protected applyExtensions(client: TPrismaClient): TPrismaClient {
137
- // Base implementation returns client as-is
138
- // Override in subclass to add extensions:
139
- // return client.$extends(softDeleteExtension).$extends(auditExtension)
140
- return client;
141
- }
142
-
143
- /**
144
- * Get extended client with all applied extensions
145
- */
146
- get extendedClient(): TPrismaClient {
147
- return this.applyExtensions(this.client);
148
- }
149
-
150
- /**
151
- * Initialize datasource - called after broker injection
152
- */
153
- async init(): Promise<void> {
154
- this.broker.logger.info('Initializing Prisma datasource');
155
- }
156
-
157
- /**
158
- * Called automatically when context is injected
159
- * Sets tenant context from service context if available
160
- */
161
- private updateTenantFromContext(): void {
162
- const tenantId = (this.context as any)?.meta?.tenantId;
163
- if (tenantId && typeof tenantId === 'string') {
164
- this.setTenantContext(tenantId);
165
- }
166
- }
167
-
168
- /**
169
- * Connect to database - called when service starts
170
- */
171
- async connect(): Promise<void> {
172
- try {
173
- this.broker.logger.info('Connecting to database via Prisma');
174
- await this.client.$connect();
175
- this.broker.logger.info('Successfully connected to database');
176
- } catch (error) {
177
- this.broker.logger.error('Failed to connect to database:', error);
178
- throw error;
179
- }
180
- }
181
-
182
- /**
183
- * Disconnect from database - called when service stops
184
- */
185
- async disconnect(): Promise<void> {
186
- try {
187
- this.broker.logger.info('Disconnecting from database');
188
- await this.client.$disconnect();
189
- this.broker.logger.info('Successfully disconnected from database');
190
- } catch (error) {
191
- this.broker.logger.error('Error disconnecting from database:', error);
192
- throw error;
193
- }
194
- }
195
-
196
- /**
197
- * Health check - verify database connectivity
198
- */
199
- async healthCheck(): Promise<boolean> {
200
- try {
201
- this.broker.logger.info('Running Prisma health check');
202
-
203
- // Simple connectivity test
204
- await this.client.$queryRaw`SELECT 1`;
205
-
206
- this.broker.logger.info('Prisma health check passed');
207
- return true;
208
- } catch (error) {
209
- this.broker.logger.error('Prisma health check failed:', error);
210
- return false;
211
- }
212
- }
213
-
214
- /**
215
- * Clear/reset data - useful for testing
216
- */
217
- async clear(): Promise<void> {
218
- if (isTest || isDev) {
219
- this.broker.logger.warn(
220
- 'Clearing database (only allowed in test/dev mode)'
221
- );
222
-
223
- // Get all model names dynamically
224
- const modelNames = Object.keys(this.client).filter(
225
- (key) => !key.startsWith('_') && !key.startsWith('$')
226
- );
227
-
228
- // Delete all data in reverse order (to handle foreign keys)
229
- for (const modelName of modelNames.reverse()) {
230
- try {
231
- const model = this.client[modelName as keyof TPrismaClient] as {
232
- deleteMany?: () => Promise<unknown>;
233
- };
234
- if (model.deleteMany) {
235
- await model.deleteMany();
236
- }
237
- } catch (error) {
238
- // Some models might not support deleteMany, skip them
239
- this.broker.logger.debug(`Could not clear ${modelName}:`, error);
240
- }
241
- }
242
- } else {
243
- throw new Error(
244
- 'Database clear is only allowed in test/development environments'
245
- );
246
- }
247
- }
248
-
249
- /**
250
- * Transaction wrapper with proper error handling
251
- */
252
- async transaction<T>(
253
- fn: (tx: TPrismaClient) => Promise<T>,
254
- options?: {
255
- maxWait?: number;
256
- timeout?: number;
257
- }
258
- ): Promise<T> {
259
- try {
260
- return await this.client.$transaction(
261
- (tx: any) => fn(tx as TPrismaClient),
262
- options
263
- );
264
- } catch (error) {
265
- this.broker.logger.error('Transaction failed:', error);
266
- throw error;
267
- }
268
- }
269
-
270
- /**
271
- * Set tenant context for multi-tenant applications
272
- * Requires tenant extension to be applied
273
- */
274
- setTenantContext(tenantId: string): void {
275
- const tenantClient = this.client as any;
276
- if (tenantClient.$setTenant) {
277
- tenantClient.$setTenant(tenantId);
278
- this.broker.logger.debug('Tenant context set:', {tenantId});
279
- } else {
280
- this.broker.logger.warn(
281
- 'Tenant extension not available - setTenantContext ignored'
282
- );
283
- }
284
- }
285
-
286
- /**
287
- * Get current tenant context
288
- */
289
- getCurrentTenant(): string | null {
290
- const tenantClient = this.client as any;
291
- if (tenantClient.$getCurrentTenant) {
292
- return tenantClient.$getCurrentTenant();
293
- }
294
- return null;
295
- }
296
-
297
- /**
298
- * Execute with tenant context (automatically restores previous context)
299
- */
300
- async withTenant<T>(tenantId: string, fn: () => Promise<T>): Promise<T> {
301
- const previousTenant = this.getCurrentTenant();
302
-
303
- try {
304
- this.setTenantContext(tenantId);
305
- return await fn();
306
- } finally {
307
- if (previousTenant) {
308
- this.setTenantContext(previousTenant);
309
- } else {
310
- const tenantClient = this.client as any;
311
- if (tenantClient.$clearTenant) {
312
- tenantClient.$clearTenant();
313
- }
314
- }
315
- }
316
- }
317
- }
package/src/env.ts DELETED
@@ -1,12 +0,0 @@
1
- import env from '@ltv/env';
2
-
3
- const nodeEnv = env.string('NODE_ENV', 'development');
4
-
5
- export const isDev = nodeEnv === 'development';
6
- export const isTest = nodeEnv === 'test';
7
- export const isProd = nodeEnv === 'production';
8
- export const REDIS_URL = env.string('REDIS_URL');
9
-
10
- export default env;
11
- export type Env = typeof env;
12
- export {nodeEnv};
@@ -1,33 +0,0 @@
1
- export class AuthenticationError extends Error {
2
- public readonly code = 'AUTH_REQUIRED';
3
- public readonly statusCode = 401;
4
- public readonly data: Record<string, unknown> | undefined;
5
-
6
- constructor(message: string, data?: Record<string, unknown>) {
7
- super(message);
8
- this.name = 'AuthenticationError';
9
- this.data = data;
10
-
11
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
12
- if (Error.captureStackTrace) {
13
- Error.captureStackTrace(this, AuthenticationError);
14
- }
15
- }
16
- }
17
-
18
- export class TenantError extends Error {
19
- public readonly code = 'TENANT_REQUIRED';
20
- public readonly statusCode = 401;
21
- public readonly data: Record<string, unknown> | undefined;
22
-
23
- constructor(message: string, data?: Record<string, unknown>) {
24
- super(message);
25
- this.name = 'TenantError';
26
- this.data = data;
27
-
28
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
29
- if (Error.captureStackTrace) {
30
- Error.captureStackTrace(this, TenantError);
31
- }
32
- }
33
- }
@@ -1,2 +0,0 @@
1
- export * from './permission.error';
2
- export * from './auth.error';