@hiliosai/sdk 0.1.4 → 0.1.6

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.
@@ -2,6 +2,7 @@ import omit from 'lodash/omit';
2
2
  import type {ServiceSchema as MoleculerServiceSchema} from 'moleculer';
3
3
 
4
4
  import {MemoizeMixin} from '../middlewares';
5
+ import type {DatasourceConstructorRegistry} from '../middlewares/datasource.middleware';
5
6
  import {DatasourceMixin} from '../mixins';
6
7
  import type {ServiceConfig} from '../types/service';
7
8
 
@@ -34,8 +35,11 @@ import type {ServiceConfig} from '../types/service';
34
35
  * });
35
36
  * ```
36
37
  */
37
- export function defineService<TDatasources = unknown, TSettings = unknown>(
38
- config: ServiceConfig<TSettings, TDatasources>
38
+ export function defineService<
39
+ TSettings = unknown,
40
+ TDatasourceConstructors extends DatasourceConstructorRegistry = DatasourceConstructorRegistry
41
+ >(
42
+ config: ServiceConfig<TSettings, TDatasourceConstructors>
39
43
  ): MoleculerServiceSchema<TSettings> {
40
44
  const propsToOmit = ['datasources'];
41
45
  const serviceSchema = omit(
@@ -0,0 +1,60 @@
1
+ import type {IntegrationPlatform} from './platform';
2
+ import type {NormalizedMessage} from './message';
3
+
4
+ /**
5
+ * Channel event payload types for reliable messaging via @moleculer/channels
6
+ */
7
+
8
+ // Integration message events
9
+ export interface IntegrationMessageReceivedPayload {
10
+ tenantId: string;
11
+ channelId: string;
12
+ platform: IntegrationPlatform;
13
+ message: NormalizedMessage;
14
+ timestamp: number;
15
+ metadata?: Record<string, unknown>;
16
+ }
17
+
18
+ export interface IntegrationMessageSentPayload {
19
+ tenantId: string;
20
+ channelId: string;
21
+ platform: IntegrationPlatform;
22
+ messageId?: string;
23
+ metadata?: Record<string, unknown>;
24
+ timestamp: number;
25
+ }
26
+
27
+ export interface IntegrationMessageFailedPayload {
28
+ tenantId: string;
29
+ channelId: string;
30
+ platform: IntegrationPlatform;
31
+ error: string; // Serialized error message
32
+ message?: NormalizedMessage;
33
+ timestamp: number;
34
+ retryCount?: number;
35
+ metadata?: Record<string, unknown>;
36
+ }
37
+
38
+ // System events
39
+ export interface IntegrationRegisteredPayload {
40
+ tenantId: string;
41
+ channelId: string;
42
+ platform: IntegrationPlatform;
43
+ config: Record<string, unknown>;
44
+ timestamp: number;
45
+ }
46
+
47
+ export interface IntegrationUnregisteredPayload {
48
+ tenantId: string;
49
+ channelId: string;
50
+ platform: IntegrationPlatform;
51
+ timestamp: number;
52
+ }
53
+
54
+ // Re-export constants to avoid duplication
55
+ export {
56
+ CHANNELS,
57
+ INTEGRATION_CHANNELS,
58
+ NAMESPACE,
59
+ type IntegrationChannelName,
60
+ } from '../configs/constants';
@@ -2,15 +2,32 @@ import type {Context} from 'moleculer';
2
2
  import type {Tenant} from './tenant';
3
3
  import type {User} from './user';
4
4
 
5
+ // Moleculer Channels types
6
+ export interface ChannelSendOptions {
7
+ group?: string;
8
+ maxInFlight?: number;
9
+ maxRetries?: number;
10
+ deadLettering?: {
11
+ enabled?: boolean;
12
+ queueName?: string;
13
+ };
14
+ }
15
+
16
+ export interface SendToChannelMethod {
17
+ (
18
+ channelName: string,
19
+ payload: unknown,
20
+ options?: ChannelSendOptions
21
+ ): Promise<void>;
22
+ }
23
+
5
24
  export interface AppMeta {
6
25
  user?: User;
7
26
  tenantId?: string;
8
27
  tenantName?: string;
9
28
  userId?: string;
10
- integrationId?: string;
11
29
  channelId?: string;
12
30
  requestId?: string;
13
- correlationId?: string;
14
31
  userAgent?: string;
15
32
  clientIP?: string;
16
33
  [key: string]: unknown;
@@ -41,4 +58,7 @@ export type AppContext<
41
58
  > = Context<TParams, TMeta, TLocals> &
42
59
  PermissionHelpers & {
43
60
  datasources: TDatasources;
61
+ broker: Context['broker'] & {
62
+ sendToChannel: SendToChannelMethod;
63
+ };
44
64
  };
@@ -6,3 +6,4 @@ export * from './service';
6
6
  export * from './user';
7
7
  export * from './tenant';
8
8
  export * from './datasource';
9
+ export * from './channels';
@@ -20,7 +20,6 @@ export interface BaseSpec {
20
20
  export interface IntegrationConfig {
21
21
  id: string;
22
22
  tenantId: string;
23
- integrationId: string;
24
23
  name: string;
25
24
  version: string;
26
25
  status: IntegrationStatus;
@@ -77,12 +77,11 @@ export interface PlatformMessage {
77
77
  payload: any;
78
78
  }
79
79
 
80
- export interface WebhookEvent {
80
+ export interface WebhookEvent<TPayload = any> {
81
81
  tenantId: string;
82
82
  channelId: string;
83
- integrationId: string;
84
83
  platform: IntegrationPlatform;
85
- payload: any;
84
+ payload: TPayload;
86
85
  headers: Record<string, string>;
87
86
  timestamp: number;
88
87
  }
@@ -9,13 +9,10 @@ import type {
9
9
 
10
10
  import type {DatasourceConstructorRegistry} from '../middlewares/datasource.middleware';
11
11
  import type {AppContext} from './context';
12
+ import type {DatasourceInstanceTypes} from './datasource';
12
13
  import type {BaseSpec, IntegrationConfig} from './integration';
13
- import type {
14
- NormalizedMessage,
15
- PlatformMessage,
16
- SendResult,
17
- WebhookEvent,
18
- } from './message';
14
+ import type {NormalizedMessage, SendResult, WebhookEvent} from './message';
15
+
19
16
 
20
17
  // Type to infer TypeScript types from Moleculer parameter schemas
21
18
  type InferParamsType<T> = T extends Record<string, any>
@@ -121,28 +118,34 @@ export interface ServiceSchema<TSettings = unknown, TDatasources = unknown>
121
118
  // ServiceConfig is what users provide to defineService
122
119
  export type ServiceConfig<
123
120
  TSettings = unknown,
124
- TDatasources = unknown
125
- > = ServiceSchema<TSettings, TDatasources> & {
126
- datasources?: DatasourceConstructorRegistry;
121
+ TDatasourceConstructors extends DatasourceConstructorRegistry = DatasourceConstructorRegistry
122
+ > = ServiceSchema<
123
+ TSettings,
124
+ DatasourceInstanceTypes<TDatasourceConstructors>
125
+ > & {
126
+ datasources?: TDatasourceConstructors;
127
127
  };
128
128
 
129
129
  // Integration-specific types
130
130
  export interface IntegrationServiceConfig<
131
- TPlatformMessage extends PlatformMessage = PlatformMessage,
132
131
  TSettings = unknown,
133
- TContext extends AppContext = AppContext,
134
- TDatasources = unknown
135
- > extends ServiceConfig<TSettings, TDatasources> {
132
+ TDatasourceConstructors extends DatasourceConstructorRegistry = DatasourceConstructorRegistry,
133
+ TContext extends AppContext<
134
+ DatasourceInstanceTypes<TDatasourceConstructors>
135
+ > = AppContext<DatasourceInstanceTypes<TDatasourceConstructors>>
136
+ > extends ServiceConfig<TSettings, TDatasourceConstructors> {
136
137
  name: string;
137
138
  spec: BaseSpec;
138
139
 
139
140
  // Core integration methods
140
- normalize(webhook: WebhookEvent): Promise<NormalizedMessage[]>;
141
- transform?(
142
- message: NormalizedMessage,
143
- config: IntegrationConfig
144
- ): Promise<TPlatformMessage>;
145
- validateWebhook?(webhook: WebhookEvent): boolean;
141
+ normalize<TPayload = any>(
142
+ webhook: WebhookEvent<TPayload>
143
+ ): Promise<NormalizedMessage[]>;
144
+ getChannelConfig(
145
+ ctx: TContext,
146
+ channelId: string
147
+ ): Promise<IntegrationConfig | null>;
148
+ validateWebhook?<TPayload = any>(webhook: WebhookEvent<TPayload>): boolean;
146
149
  sendMessage(
147
150
  ctx: TContext,
148
151
  message: NormalizedMessage,
@@ -170,20 +173,20 @@ export interface IntegrationServiceConfig<
170
173
  validateCredentials?(credentials: Record<string, string>): Promise<boolean>;
171
174
 
172
175
  // Optional signature validation
173
- validateSignature?(webhook: WebhookEvent): boolean;
176
+ validateSignature?<TPayload = any>(webhook: WebhookEvent<TPayload>): boolean;
174
177
  }
175
178
 
176
- export interface IntegrationServiceSchema<
177
- TPlatformMessage extends PlatformMessage = PlatformMessage,
178
- TSettings = unknown
179
- > extends MoleculerServiceSchema<TSettings> {
179
+ export interface IntegrationServiceSchema<TSettings = unknown>
180
+ extends MoleculerServiceSchema<TSettings> {
180
181
  spec: BaseSpec;
181
- normalize(webhook: WebhookEvent): Promise<NormalizedMessage[]>;
182
- transform?(
183
- message: NormalizedMessage,
184
- config: IntegrationConfig
185
- ): Promise<TPlatformMessage>;
186
- validateWebhook?(webhook: WebhookEvent): boolean;
182
+ normalize<TPayload = any>(
183
+ webhook: WebhookEvent<TPayload>
184
+ ): Promise<NormalizedMessage[]>;
185
+ getChannelConfig(
186
+ ctx: AppContext,
187
+ channelId: string
188
+ ): Promise<IntegrationConfig | null>;
189
+ validateWebhook?<TPayload = any>(webhook: WebhookEvent<TPayload>): boolean;
187
190
  sendMessage(
188
191
  ctx: AppContext,
189
192
  message: NormalizedMessage,
@@ -203,5 +206,5 @@ export interface IntegrationServiceSchema<
203
206
  details?: Record<string, any>;
204
207
  }>;
205
208
  validateCredentials?(credentials: Record<string, string>): Promise<boolean>;
206
- validateSignature?(webhook: WebhookEvent): boolean;
209
+ validateSignature?<TPayload = any>(webhook: WebhookEvent<TPayload>): boolean;
207
210
  }
package/src/types/user.ts CHANGED
@@ -3,8 +3,8 @@ export const UserRole = {
3
3
  ADMIN: 'ADMIN',
4
4
  MANAGER: 'MANAGER',
5
5
  AGENT: 'AGENT',
6
- VIEWER: 'VIEWER'
7
- } as const
6
+ VIEWER: 'VIEWER',
7
+ } as const;
8
8
 
9
9
  export interface User {
10
10
  id: string;
@@ -1,394 +0,0 @@
1
- # Integration Service Architecture
2
-
3
- ## Overview
4
-
5
- Integration Services are specialized Moleculer services that provide a standardized way to connect external messaging platforms (WhatsApp, Telegram, Discord, etc.) with the application. They handle webhook reception, message normalization, and platform-specific communication patterns.
6
-
7
- ## Purpose
8
-
9
- The Integration Service pattern solves several key challenges:
10
-
11
- 1. **Platform Abstraction**: Provides unified interface for different messaging platforms
12
- 2. **Webhook Security**: Implements signature validation and replay attack prevention
13
- 3. **Message Normalization**: Converts platform-specific messages to unified format
14
- 4. **Retry Logic**: Handles transient failures with exponential backoff
15
- 5. **Event Broadcasting**: Emits standardized events for message processing
16
- 6. **Health Monitoring**: Provides health checks and status reporting
17
-
18
- ## Architecture
19
-
20
- ```mermaid
21
- graph TB
22
- subgraph "External Platforms"
23
- WA[WhatsApp]
24
- TG[Telegram]
25
- DC[Discord]
26
- SL[Slack]
27
- end
28
-
29
- subgraph "Integration Layer"
30
- subgraph "Integration Services"
31
- WAS[WhatsApp Service]
32
- TGS[Telegram Service]
33
- DCS[Discord Service]
34
- SLS[Slack Service]
35
- end
36
-
37
- subgraph "Service Actions"
38
- RW[i_receiveWebhook]
39
- SM[i_sendMessage]
40
- HC[i_healthCheck]
41
- VW[i_verifyWebhook]
42
- ST[i_status]
43
- VC[i_validateCredentials]
44
- end
45
- end
46
-
47
- subgraph "Core Application"
48
- subgraph "Message Processing"
49
- MP[Message Processor]
50
- MR[Message Router]
51
- MS[Message Store]
52
- end
53
-
54
- subgraph "Event System"
55
- EB[Event Bus]
56
- EH[Event Handlers]
57
- end
58
- end
59
-
60
- subgraph "Infrastructure"
61
- DS[Datasources]
62
- CA[Cache]
63
- PE[Permissions]
64
- CH[Context Helpers]
65
- end
66
-
67
- %% External webhook flows
68
- WA -->|Webhook| WAS
69
- TG -->|Webhook| TGS
70
- DC -->|Webhook| DCS
71
- SL -->|Webhook| SLS
72
-
73
- %% Integration service actions
74
- WAS --> RW
75
- WAS --> SM
76
- WAS --> HC
77
-
78
- %% Service infrastructure
79
- WAS --> DS
80
- TGS --> CA
81
- DCS --> PE
82
- SLS --> CH
83
-
84
- %% Event flows
85
- RW -->|integration.message.received| EB
86
- SM -->|integration.message.sent| EB
87
- SM -->|integration.message.failed| EB
88
-
89
- %% Processing flows
90
- EB --> EH
91
- EH --> MP
92
- MP --> MR
93
- MR --> MS
94
-
95
- %% Response flows
96
- MP -->|Send Response| SM
97
- ```
98
-
99
- ## Integration Service Structure
100
-
101
- ### Core Components
102
-
103
- 1. **defineIntegration()** - Factory function that creates integration services
104
- 2. **Security Helpers** - Webhook validation, timestamp checking, correlation IDs
105
- 3. **Retry Mechanism** - Exponential backoff for failed operations
106
- 4. **Standard Actions** - Predefined actions for common integration operations
107
-
108
- ### Standard Actions
109
-
110
- All integration services automatically get these actions:
111
-
112
- | Action | REST Endpoint | Purpose |
113
- | ----------------------- | ---------------- | ------------------------------------- |
114
- | `i_receiveWebhook` | `POST /webhook` | Receive and process incoming webhooks |
115
- | `i_sendMessage` | `POST /send` | Send messages to external platform |
116
- | `i_healthCheck` | `GET /health` | Check integration health status |
117
- | `i_verifyWebhook` | `GET /webhook` | Verify webhook subscriptions |
118
- | `i_status` | `GET /status` | Get integration status information |
119
- | `i_validateCredentials` | `POST /validate` | Validate platform credentials |
120
-
121
- ### Service Methods
122
-
123
- Integration-specific logic is implemented as service methods:
124
-
125
- | Method | Purpose | Required |
126
- | ----------------------- | --------------------------------------------- | -------- |
127
- | `normalize()` | Convert platform message to unified format | ✅ |
128
- | `sendMessage()` | Send message to platform | ✅ |
129
- | `transform()` | Transform outbound message to platform format | ❌ |
130
- | `validateWebhook()` | Validate webhook payload structure | ❌ |
131
- | `validateSignature()` | Validate webhook signature | ❌ |
132
- | `verifyWebhook()` | Handle webhook verification challenge | ❌ |
133
- | `checkHealth()` | Custom health check logic | ❌ |
134
- | `validateCredentials()` | Validate platform credentials | ❌ |
135
-
136
- ## Usage Example
137
-
138
- ### Basic WhatsApp Integration
139
-
140
- ```typescript
141
- import {defineIntegration} from '@pkg/sdk';
142
- import type {BaseSpec, NormalizedMessage, WebhookEvent} from '@pkg/sdk';
143
-
144
- const whatsappIntegration: BaseSpec = {
145
- id: 'whatsapp-business',
146
- name: 'WhatsApp Business',
147
- platform: 'whatsapp',
148
- version: '1.0.0',
149
- status: 'ACTIVE',
150
- capabilities: ['SEND_MESSAGE', 'RECEIVE_MESSAGE', 'WEBHOOK_VALIDATION']
151
- };
152
-
153
- export default defineIntegration({
154
- name: 'whatsapp',
155
- spec: whatsappIntegration,
156
-
157
- // Required: Convert WhatsApp webhook to normalized message
158
- async normalize(webhook: WebhookEvent): Promise<NormalizedMessage[]> {
159
- const messages: NormalizedMessage[] = [];
160
-
161
- for (const entry of webhook.payload.entry || []) {
162
- for (const change of entry.changes || []) {
163
- if (change.field === 'messages') {
164
- for (const message of change.value.messages || []) {
165
- messages.push({
166
- id: message.id,
167
- conversationId: message.from,
168
- from: {
169
- id: message.from,
170
- name: change.value.contacts?.[0]?.profile?.name,
171
- },
172
- to: {
173
- id: webhook.tenantId,
174
- },
175
- content: {
176
- type: message.type === 'text' ? 'TEXT' : 'FILE',
177
- text: message.text?.body,
178
- media: message.image ? {
179
- url: message.image.id,
180
- mimeType: message.image.mime_type
181
- } : undefined
182
- },
183
- timestamp: parseInt(message.timestamp) * 1000,
184
- platform: 'whatsapp',
185
- metadata: {
186
- phoneNumberId: change.value.metadata.phone_number_id
187
- }
188
- });
189
- }
190
- }
191
- }
192
- }
193
-
194
- return messages;
195
- },
196
-
197
- // Required: Send message to WhatsApp
198
- async sendMessage(ctx, message, config) {
199
- const phoneNumberId = config.credentials.phoneNumberId;
200
- const accessToken = config.credentials.accessToken;
201
-
202
- const response = await fetch(
203
- `https://graph.facebook.com/v18.0/${phoneNumberId}/messages`,
204
- {
205
- method: 'POST',
206
- headers: {
207
- 'Authorization': `Bearer ${accessToken}`,
208
- 'Content-Type': 'application/json'
209
- },
210
- body: JSON.stringify({
211
- messaging_product: 'whatsapp',
212
- to: message.to.id,
213
- text: { body: message.content.text }
214
- })
215
- }
216
- );
217
-
218
- if (!response.ok) {
219
- const error = await response.text();
220
- return {
221
- success: false,
222
- error: new Error(`WhatsApp API error: ${error}`)
223
- };
224
- }
225
-
226
- const result = await response.json();
227
- return {
228
- success: true,
229
- messageId: result.messages[0].id,
230
- metadata: { phoneNumberId }
231
- };
232
- },
233
-
234
- // Optional: Validate webhook signature
235
- validateSignature(webhook) {
236
- const signature = webhook.headers['x-hub-signature-256'];
237
- const secret = process.env.WHATSAPP_WEBHOOK_SECRET;
238
-
239
- if (!signature || !secret) return false;
240
-
241
- const expectedSignature = 'sha256=' +
242
- crypto.createHmac('sha256', secret)
243
- .update(webhook.rawBody || '')
244
- .digest('hex');
245
-
246
- return SecurityHelpers.secureCompare(signature, expectedSignature);
247
- },
248
-
249
- // Optional: Handle webhook verification
250
- verifyWebhook(params) {
251
- const { mode, token, challenge } = params;
252
- const verifyToken = process.env.WHATSAPP_VERIFY_TOKEN;
253
-
254
- if (mode === 'subscribe' && token === verifyToken) {
255
- return challenge;
256
- }
257
- return null;
258
- },
259
-
260
- // Optional: Custom health check
261
- async checkHealth(ctx, config) {
262
- try {
263
- const response = await fetch(
264
- `https://graph.facebook.com/v18.0/${config.credentials.phoneNumberId}`,
265
- {
266
- headers: {
267
- 'Authorization': `Bearer ${config.credentials.accessToken}`
268
- }
269
- }
270
- );
271
-
272
- return {
273
- status: response.ok ? 'healthy' : 'unhealthy',
274
- message: response.ok ? 'WhatsApp API accessible' : 'WhatsApp API error',
275
- details: {
276
- statusCode: response.status,
277
- timestamp: new Date().toISOString()
278
- }
279
- };
280
- } catch (error) {
281
- return {
282
- status: 'unhealthy',
283
- message: error.message,
284
- details: { timestamp: new Date().toISOString() }
285
- };
286
- }
287
- }
288
- });
289
- ```
290
-
291
- ### Advanced Integration with Datasources and Cache
292
-
293
- ```typescript
294
- import {defineIntegration} from '@pkg/sdk';
295
- import {WhatsAppDatasource} from './datasources';
296
-
297
- export default defineIntegration({
298
- name: 'whatsapp',
299
- spec: whatsappIntegration,
300
-
301
- // Configure per-service datasources
302
- datasources: {
303
- whatsapp: WhatsAppDatasource
304
- },
305
-
306
- // Configure per-service cache
307
- cache: {
308
- redisUrl: process.env.WHATSAPP_REDIS_URL,
309
- ttl: 5 * 60 * 1000, // 5 minutes
310
- namespace: 'whatsapp'
311
- },
312
-
313
- async normalize(webhook) {
314
- // Access datasource via context in actions
315
- // this.datasources is not available in methods
316
- // Use dependency injection pattern instead
317
- return await this.processWebhookMessages(webhook);
318
- },
319
-
320
- async sendMessage(ctx, message, config) {
321
- // Use cache for rate limiting
322
- const rateLimitKey = `rate_limit:${config.credentials.phoneNumberId}`;
323
- const currentCount = await ctx.cache.get(rateLimitKey) || 0;
324
-
325
- if (currentCount >= 1000) {
326
- return {
327
- success: false,
328
- error: new Error('Rate limit exceeded')
329
- };
330
- }
331
-
332
- // Send message...
333
- const result = await this.sendToWhatsApp(message, config);
334
-
335
- // Update rate limit counter
336
- await ctx.cache.set(rateLimitKey, currentCount + 1, 60 * 60 * 1000);
337
-
338
- return result;
339
- }
340
- });
341
- ```
342
-
343
- ## Event Flow
344
-
345
- ### Inbound Message Flow
346
-
347
- 1. **Webhook Received** → `i_receiveWebhook` action
348
- 2. **Security Validation** → Timestamp, signature, payload checks
349
- 3. **Message Normalization** → Convert to unified format
350
- 4. **Event Emission** → `integration.message.received` event
351
- 5. **Message Processing** → Application-specific logic
352
- 6. **Response Generation** → Optional automated responses
353
-
354
- ### Outbound Message Flow
355
-
356
- 1. **Send Request** → `i_sendMessage` action
357
- 2. **Message Transformation** → Convert to platform format
358
- 3. **Retry Logic** → Exponential backoff for failures
359
- 4. **Platform API Call** → Send via platform API
360
- 5. **Event Emission** → `integration.message.sent` or `integration.message.failed`
361
- 6. **Status Tracking** → Update message delivery status
362
-
363
- ## Security Features
364
-
365
- ### Webhook Validation
366
-
367
- - **Signature Verification**: HMAC-SHA256 validation using platform secrets
368
- - **Replay Attack Prevention**: Timestamp validation (5-minute window)
369
- - **Timing Attack Protection**: Constant-time string comparison
370
- - **Correlation IDs**: Request tracking for debugging and audit trails
371
-
372
- ### Best Practices
373
-
374
- 1. **Always validate webhook signatures** in production
375
- 2. **Use environment variables** for secrets and tokens
376
- 3. **Implement proper error handling** with structured errors
377
- 4. **Add correlation IDs** for request tracking
378
- 5. **Use retry logic** for transient failures
379
- 6. **Monitor health endpoints** for platform availability
380
- 7. **Cache frequently accessed data** to reduce API calls
381
-
382
- ## Infrastructure Integration
383
-
384
- Integration services automatically inherit:
385
-
386
- - **Datasource Access**: Database connections, external APIs
387
- - **Cache Layer**: Redis/in-memory caching with TTL
388
- - **Permission System**: Role-based access control
389
- - **Context Helpers**: Common utilities and operations
390
- - **Memoization**: Action result caching
391
- - **Health Monitoring**: Automated health checks
392
- - **Event System**: Pub/sub event broadcasting
393
-
394
- This architecture provides a robust, scalable foundation for building messaging platform integrations while maintaining consistency, security, and observability across all platforms.
@@ -1,8 +0,0 @@
1
- import type {DatasourceInstanceTypes} from '../../../types';
2
- import {UserDatasource} from './user.datasource';
3
-
4
- export const datasources = {
5
- user: UserDatasource,
6
- };
7
-
8
- export type UserDatasourceTypes = DatasourceInstanceTypes<typeof datasources>;
@@ -1,7 +0,0 @@
1
- import type {User} from '../../../types';
2
-
3
- export class UserDatasource {
4
- getUser(): User {
5
- return {} as User;
6
- }
7
- }