@k-msg/core 0.3.0 → 0.5.0
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.
- package/README.md +52 -288
- package/dist/index.d.ts +0 -4
- package/dist/index.js +26 -26
- package/dist/index.js.map +5 -11
- package/dist/index.mjs +26 -26
- package/dist/index.mjs.map +5 -11
- package/dist/provider.d.ts +9 -6
- package/dist/test-utils.d.ts +6 -1
- package/dist/types/index.d.ts +0 -3
- package/dist/types/message.d.ts +173 -19
- package/package.json +1 -1
- package/dist/platform.d.ts +0 -73
- package/dist/provider-registry.d.ts +0 -135
- package/dist/router/round-robin-router-provider.d.ts +0 -20
- package/dist/types/platform.d.ts +0 -100
- package/dist/types/provider.d.ts +0 -76
- package/dist/types/standard.d.ts +0 -87
- package/dist/universal-provider.d.ts +0 -55
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# @k-msg/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Core types and utilities for the `k-msg` ecosystem.
|
|
4
|
+
|
|
5
|
+
This package intentionally stays low-level:
|
|
6
|
+
|
|
7
|
+
- Standard message model: `MessageType`, `SendInput`, `SendOptions`, `SendResult`
|
|
8
|
+
- Provider interface: `Provider`
|
|
9
|
+
- Result pattern: `Result`, `ok`, `fail`
|
|
10
|
+
- Errors: `KMsgError`, `KMsgErrorCode`
|
|
11
|
+
- Resilience helpers: retry / circuit-breaker / rate-limit / health monitor
|
|
12
|
+
|
|
13
|
+
If you want the end-user "send" experience, use `KMsg` from `@k-msg/messaging` (or `k-msg`).
|
|
4
14
|
|
|
5
15
|
## Installation
|
|
6
16
|
|
|
@@ -10,300 +20,54 @@ npm install @k-msg/core
|
|
|
10
20
|
bun add @k-msg/core
|
|
11
21
|
```
|
|
12
22
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- **Dependency Tracking**: External service availability checks
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
### Basic Platform Setup
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { AlimTalkPlatform } from '@k-msg/core';
|
|
44
|
-
|
|
45
|
-
const platform = new AlimTalkPlatform({
|
|
46
|
-
providers: ['iwinv'],
|
|
47
|
-
defaultProvider: 'iwinv',
|
|
48
|
-
features: {
|
|
49
|
-
enableBulkSending: true,
|
|
50
|
-
enableScheduling: true,
|
|
51
|
-
enableAnalytics: true
|
|
23
|
+
## Example: Implement a Provider
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
fail,
|
|
28
|
+
KMsgError,
|
|
29
|
+
KMsgErrorCode,
|
|
30
|
+
ok,
|
|
31
|
+
type MessageType,
|
|
32
|
+
type Provider,
|
|
33
|
+
type ProviderHealthStatus,
|
|
34
|
+
type Result,
|
|
35
|
+
type SendOptions,
|
|
36
|
+
type SendResult,
|
|
37
|
+
} from "@k-msg/core";
|
|
38
|
+
|
|
39
|
+
export class MyProvider implements Provider {
|
|
40
|
+
readonly id = "my-provider";
|
|
41
|
+
readonly name = "My Provider";
|
|
42
|
+
readonly supportedTypes: readonly MessageType[] = ["SMS"];
|
|
43
|
+
|
|
44
|
+
async healthCheck(): Promise<ProviderHealthStatus> {
|
|
45
|
+
return { healthy: true, issues: [] };
|
|
52
46
|
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Get platform capabilities
|
|
56
|
-
const info = platform.getInfo();
|
|
57
|
-
console.log(`Platform: ${info.name}, Version: ${info.version}`);
|
|
58
|
-
console.log(`Supported providers: ${info.supportedProviders.join(', ')}`);
|
|
59
|
-
|
|
60
|
-
// Perform comprehensive health check
|
|
61
|
-
const health = await platform.healthCheck();
|
|
62
|
-
console.log(`Health: ${health.status}, Services: ${Object.keys(health.services).length}`);
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Round-robin Provider Rotation
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
import { RoundRobinRouterProvider, type BaseProvider } from "@k-msg/core";
|
|
69
|
-
|
|
70
|
-
const router = new RoundRobinRouterProvider({
|
|
71
|
-
id: "router",
|
|
72
|
-
providers: [providerA, providerB] satisfies BaseProvider[],
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await router.send({
|
|
76
|
-
channel: "SMS",
|
|
77
|
-
templateCode: "SMS_DIRECT",
|
|
78
|
-
phoneNumber: "01012345678",
|
|
79
|
-
variables: {},
|
|
80
|
-
text: "hello",
|
|
81
|
-
});
|
|
82
|
-
```
|
|
83
47
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
console.log(`Message sent: ${result.data.messageId}`);
|
|
96
|
-
} else {
|
|
97
|
-
console.error(`Send failed: ${result.error.message}`);
|
|
98
|
-
|
|
99
|
-
// Handle specific error types
|
|
100
|
-
if (result.error.code === KMessageErrorCode.PROVIDER_TIMEOUT) {
|
|
101
|
-
// Retry with different provider
|
|
102
|
-
} else if (result.error.code === KMessageErrorCode.TEMPLATE_NOT_FOUND) {
|
|
103
|
-
// Create template or use fallback
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Method 2: Try-Catch with Structured Errors
|
|
108
|
-
try {
|
|
109
|
-
const response = await provider.sendMessage(message);
|
|
110
|
-
console.log('Success:', response);
|
|
111
|
-
} catch (error) {
|
|
112
|
-
if (error instanceof KMessageError) {
|
|
113
|
-
console.error(`[${error.code}] ${error.message}`);
|
|
114
|
-
console.error('Context:', error.context);
|
|
115
|
-
|
|
116
|
-
// Access original cause if available
|
|
117
|
-
if (error.cause) {
|
|
118
|
-
console.error('Root cause:', error.cause);
|
|
48
|
+
async send(options: SendOptions): Promise<Result<SendResult, KMsgError>> {
|
|
49
|
+
const messageId = options.messageId || crypto.randomUUID();
|
|
50
|
+
|
|
51
|
+
if (options.type !== "SMS") {
|
|
52
|
+
return fail(
|
|
53
|
+
new KMsgError(
|
|
54
|
+
KMsgErrorCode.INVALID_REQUEST,
|
|
55
|
+
`Unsupported type: ${options.type}`,
|
|
56
|
+
{ providerId: this.id, type: options.type },
|
|
57
|
+
),
|
|
58
|
+
);
|
|
119
59
|
}
|
|
120
|
-
} else {
|
|
121
|
-
// Handle unexpected errors
|
|
122
|
-
console.error('Unexpected error:', error);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Retry Configuration
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
import { RetryManager, RetryConfig } from '@k-msg/core';
|
|
131
|
-
|
|
132
|
-
// Custom retry configuration
|
|
133
|
-
const retryConfig: RetryConfig = {
|
|
134
|
-
maxAttempts: 3,
|
|
135
|
-
baseDelay: 1000, // 1 second
|
|
136
|
-
maxDelay: 10000, // 10 seconds
|
|
137
|
-
backoffMultiplier: 2,
|
|
138
|
-
jitterType: 'full', // full, half, none
|
|
139
|
-
retryableErrors: [
|
|
140
|
-
KMessageErrorCode.NETWORK_ERROR,
|
|
141
|
-
KMessageErrorCode.PROVIDER_TIMEOUT,
|
|
142
|
-
KMessageErrorCode.RATE_LIMIT_EXCEEDED
|
|
143
|
-
]
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const retryManager = new RetryManager(retryConfig);
|
|
147
|
-
|
|
148
|
-
// Execute with retry
|
|
149
|
-
const result = await retryManager.execute(async () => {
|
|
150
|
-
return await provider.sendMessage(message);
|
|
151
|
-
});
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Health Check Implementation
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
import { HealthChecker, HealthStatus } from '@k-msg/core';
|
|
158
|
-
|
|
159
|
-
const healthChecker = new HealthChecker();
|
|
160
|
-
|
|
161
|
-
// Register custom health checks
|
|
162
|
-
healthChecker.register('database', async () => {
|
|
163
|
-
const connected = await checkDatabaseConnection();
|
|
164
|
-
return {
|
|
165
|
-
status: connected ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY,
|
|
166
|
-
details: { connected, lastCheck: new Date() }
|
|
167
|
-
};
|
|
168
|
-
});
|
|
169
60
|
|
|
170
|
-
|
|
171
|
-
const responseTime = await measureApiResponseTime();
|
|
172
|
-
return {
|
|
173
|
-
status: responseTime < 1000 ? HealthStatus.HEALTHY : HealthStatus.DEGRADED,
|
|
174
|
-
details: { responseTime, threshold: 1000 }
|
|
175
|
-
};
|
|
176
|
-
});
|
|
61
|
+
// ... send SMS here ...
|
|
177
62
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
## API Reference
|
|
185
|
-
|
|
186
|
-
### Core Types
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
// Platform configuration
|
|
190
|
-
interface AlimTalkPlatformConfig {
|
|
191
|
-
providers: string[];
|
|
192
|
-
defaultProvider: string;
|
|
193
|
-
features: PlatformFeatures;
|
|
194
|
-
retryConfig?: RetryConfig;
|
|
195
|
-
healthCheckConfig?: HealthCheckConfig;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Error types
|
|
199
|
-
enum KMessageErrorCode {
|
|
200
|
-
UNKNOWN = 'UNKNOWN',
|
|
201
|
-
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
202
|
-
PROVIDER_ERROR = 'PROVIDER_ERROR',
|
|
203
|
-
TEMPLATE_ERROR = 'TEMPLATE_ERROR',
|
|
204
|
-
VALIDATION_ERROR = 'VALIDATION_ERROR',
|
|
205
|
-
RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
|
|
206
|
-
// ... more error codes
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Health check results
|
|
210
|
-
interface HealthCheckResult {
|
|
211
|
-
status: HealthStatus;
|
|
212
|
-
services: Record<string, ServiceHealthInfo>;
|
|
213
|
-
timestamp: Date;
|
|
214
|
-
totalChecks: number;
|
|
215
|
-
passedChecks: number;
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### Test Utilities
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
import { TestData, TestAssertions } from '@k-msg/core/test-utils';
|
|
223
|
-
|
|
224
|
-
// Generate test data
|
|
225
|
-
const testMessage = TestData.createMessage({
|
|
226
|
-
provider: 'iwinv',
|
|
227
|
-
templateCode: 'AUTH_OTP',
|
|
228
|
-
phoneNumber: '01012345678'
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// Test assertions
|
|
232
|
-
await TestAssertions.assertMessageSent(result);
|
|
233
|
-
TestAssertions.assertErrorType(error, KMessageErrorCode.TEMPLATE_NOT_FOUND);
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## Advanced Usage
|
|
237
|
-
|
|
238
|
-
### Custom Error Types
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
import { KMessageError, KMessageErrorCode } from '@k-msg/core';
|
|
242
|
-
|
|
243
|
-
class CustomProviderError extends KMessageError {
|
|
244
|
-
constructor(
|
|
245
|
-
message: string,
|
|
246
|
-
public readonly providerId: string,
|
|
247
|
-
cause?: Error
|
|
248
|
-
) {
|
|
249
|
-
super(
|
|
250
|
-
message,
|
|
251
|
-
KMessageErrorCode.PROVIDER_ERROR,
|
|
252
|
-
{ operation: 'send_message', provider: providerId },
|
|
253
|
-
cause
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Plugin Integration
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
import { AlimTalkPlatform, PlatformPlugin } from '@k-msg/core';
|
|
263
|
-
|
|
264
|
-
class LoggingPlugin implements PlatformPlugin {
|
|
265
|
-
name = 'logging';
|
|
266
|
-
|
|
267
|
-
async initialize(platform: AlimTalkPlatform) {
|
|
268
|
-
platform.on('message_sent', (event) => {
|
|
269
|
-
console.log(`Message sent: ${event.messageId}`);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
platform.on('error', (event) => {
|
|
273
|
-
console.error(`Error: ${event.error.message}`);
|
|
63
|
+
return ok({
|
|
64
|
+
messageId,
|
|
65
|
+
providerId: this.id,
|
|
66
|
+
status: "SENT",
|
|
67
|
+
type: options.type,
|
|
68
|
+
to: options.to,
|
|
274
69
|
});
|
|
275
70
|
}
|
|
276
71
|
}
|
|
277
|
-
|
|
278
|
-
const platform = new AlimTalkPlatform(config);
|
|
279
|
-
await platform.use(new LoggingPlugin());
|
|
280
72
|
```
|
|
281
73
|
|
|
282
|
-
## Best Practices
|
|
283
|
-
|
|
284
|
-
1. **Always use Result pattern** for error handling in production code
|
|
285
|
-
2. **Configure retries** appropriately for your use case
|
|
286
|
-
3. **Implement health checks** for all external dependencies
|
|
287
|
-
4. **Use structured logging** with error context information
|
|
288
|
-
5. **Monitor error rates** and adjust retry policies accordingly
|
|
289
|
-
|
|
290
|
-
## Testing
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
# Run unit tests
|
|
294
|
-
bun test
|
|
295
|
-
|
|
296
|
-
# Run with coverage
|
|
297
|
-
bun test --coverage
|
|
298
|
-
|
|
299
|
-
# Run specific test files
|
|
300
|
-
bun test retry.test.ts
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Contributing
|
|
304
|
-
|
|
305
|
-
See the main [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
|
|
306
|
-
|
|
307
|
-
## License
|
|
308
|
-
|
|
309
|
-
MIT License - see [LICENSE](../../LICENSE) for details.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,8 @@ export * from "./config";
|
|
|
2
2
|
export * from "./errors";
|
|
3
3
|
export * from "./health";
|
|
4
4
|
export * from "./logger";
|
|
5
|
-
export * from "./platform";
|
|
6
5
|
export * from "./provider";
|
|
7
|
-
export * from "./provider-registry";
|
|
8
6
|
export * from "./resilience/index";
|
|
9
7
|
export * from "./result";
|
|
10
|
-
export * from "./router/round-robin-router-provider";
|
|
11
8
|
export * from "./test-utils";
|
|
12
9
|
export * from "./types/index";
|
|
13
|
-
export * from "./universal-provider";
|