k-msg 0.1.2

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 ADDED
@@ -0,0 +1,415 @@
1
+ # k-msg
2
+
3
+ πŸš€ **Korean Multi-Channel Messaging Platform** - The complete solution for AlimTalk, FriendTalk, SMS, and LMS messaging in Korea.
4
+
5
+ [![npm version](https://badge.fury.io/js/k-msg.svg)](https://badge.fury.io/js/k-msg)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ # Using npm
13
+ npm install k-msg
14
+
15
+ # Using bun (recommended)
16
+ bun add k-msg
17
+
18
+ # Using yarn
19
+ yarn add k-msg
20
+ ```
21
+
22
+ ## Features
23
+
24
+ ### 🌟 **Core Capabilities**
25
+ - **🏒 Multi-Provider Support** - IWINV, Aligo, Kakao Business, NHN KCP
26
+ - **πŸ’¬ Rich Messaging** - AlimTalk, FriendTalk, SMS, LMS with media support
27
+ - **πŸ”„ Smart Fallback** - Automatic provider switching and message type fallback
28
+ - **πŸ“± Mobile-First** - Optimized for Korean mobile messaging ecosystem
29
+
30
+ ### πŸ› οΈ **Developer Experience**
31
+ - **πŸ“ Template Engine** - Dynamic templates with variable substitution (`#{variable}`)
32
+ - **πŸ”’ Type Safety** - Full TypeScript support with strict type checking
33
+ - **πŸ§ͺ Testing Tools** - Built-in test utilities and mock providers
34
+ - **πŸ“š Rich Documentation** - Comprehensive guides and API references
35
+
36
+ ### πŸ“Š **Analytics & Monitoring**
37
+ - **πŸ“ˆ Real-time Analytics** - Message delivery, open rates, click tracking
38
+ - **🎯 Smart Insights** - AI-powered optimization recommendations
39
+ - **🚨 Health Monitoring** - Provider health checks and system monitoring
40
+ - **πŸ“‹ Custom Reports** - Flexible reporting with CSV/JSON export
41
+
42
+ ### ⚑ **Performance & Reliability**
43
+ - **πŸ”„ Retry Logic** - Exponential backoff with jitter
44
+ - **🚦 Rate Limiting** - Built-in rate limiting and queue management
45
+ - **πŸŽͺ Circuit Breaker** - Automatic failure recovery
46
+ - **πŸ“‘ Webhook System** - Real-time event notifications
47
+
48
+ ## πŸš€ Quick Start
49
+
50
+ ### Basic Usage with KMsg
51
+
52
+ ```typescript
53
+ import { KMsg, IWINVProvider, ok, fail } from 'k-msg';
54
+
55
+ // Initialize KMsg with IWINV provider
56
+ const kmsg = new KMsg(new IWINVProvider({
57
+ apiKey: process.env.IWINV_API_KEY!,
58
+ baseUrl: 'https://alimtalk.bizservice.iwinv.kr'
59
+ }));
60
+
61
+ // Send an SMS message
62
+ const smsResult = await kmsg.send({
63
+ type: 'SMS',
64
+ to: '01012345678',
65
+ from: '01000000000',
66
+ text: 'Hello #{name}, this is a test SMS!',
67
+ variables: { name: '홍길동' }
68
+ });
69
+
70
+ if (result.isSuccess) {
71
+ console.log('βœ… Message sent successfully:', result.value.messageId);
72
+ } else {
73
+ console.error('❌ Failed to send message:', result.error.message);
74
+ }
75
+ ```
76
+
77
+ ### Manual Adapter Setup (Advanced)
78
+
79
+ ```typescript
80
+ import { IWINVAdapter, KMsg } from 'k-msg';
81
+
82
+ // Create adapter instance
83
+ const adapter = new IWINVAdapter({
84
+ apiKey: process.env.IWINV_API_KEY!,
85
+ baseUrl: 'https://alimtalk.bizservice.iwinv.kr',
86
+ senderNumber: '01012345678'
87
+ });
88
+
89
+ // Create KMsg client with custom adapter
90
+ const kmsg = new KMsg(adapter);
91
+
92
+ // Send message with full control
93
+ const result = await kmsg.send({
94
+ type: 'ALIMTALK',
95
+ templateId: 'AUTH_OTP',
96
+ to: '01012345678',
97
+ variables: {
98
+ name: 'κΉ€μ² μˆ˜',
99
+ code: '987654'
100
+ }
101
+ });
102
+ ```
103
+
104
+ ## πŸ“š Detailed Usage Guide
105
+
106
+ ### Template Management
107
+
108
+ ```typescript
109
+ import { IWINVAdapter, TemplateService } from 'k-msg';
110
+
111
+ const adapter = new IWINVAdapter(config);
112
+ const templateService = new TemplateService(adapter);
113
+
114
+ // Create a new template
115
+ const template = await templateService.create({
116
+ code: 'order_confirmation',
117
+ name: 'order_confirmation',
118
+ content: '주문이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. 주문번호: #{orderNumber}',
119
+ category: 'TRANSACTION'
120
+ });
121
+
122
+ // List all templates
123
+ const result = await templateService.list();
124
+ if (result.isSuccess) {
125
+ console.log('Available templates:', result.value.length);
126
+ }
127
+ ```
128
+
129
+ ### Error Handling with Result Pattern
130
+
131
+ ```typescript
132
+ import { KMsg, ok, fail } from 'k-msg';
133
+
134
+ const result = await kmsg.send({
135
+ type: 'ALIMTALK',
136
+ to: '01012345678',
137
+ templateId: 'template_name',
138
+ variables: {}
139
+ });
140
+
141
+ if (result.isFailure) {
142
+ // Handle failure
143
+ const error = result.error;
144
+ console.error('Send failed:', error.message);
145
+
146
+ // You can check error codes or types
147
+ if (error.message.includes('template not found')) {
148
+ // Handle specific error
149
+ }
150
+ } else {
151
+ // Handle success
152
+ const { messageId, status } = result.value;
153
+ console.log(`Success: ${messageId} (${status})`);
154
+ }
155
+ ```
156
+
157
+ ## Template Management
158
+
159
+ ```typescript
160
+ // Create template
161
+ const templates = await platform.templates();
162
+ const newTemplate = await templates.create(
163
+ 'welcome_message',
164
+ 'Welcome #{name}! Your account is ready.',
165
+ 'GENERAL'
166
+ );
167
+
168
+ // List templates
169
+ const templateList = await templates.list(1, 20);
170
+ ```
171
+
172
+ ## Analytics & Monitoring
173
+
174
+ ```typescript
175
+ import { AnalyticsService, InsightEngine } from 'k-message';
176
+
177
+ const analytics = new AnalyticsService();
178
+
179
+ // Get delivery metrics
180
+ const metrics = await analytics.getDeliveryMetrics({
181
+ timeRange: '24h',
182
+ provider: 'iwinv'
183
+ });
184
+
185
+ // Generate insights
186
+ const insights = new InsightEngine();
187
+ const recommendations = await insights.generateInsights(metrics);
188
+ ```
189
+
190
+ ## Webhook Integration
191
+
192
+ ```typescript
193
+ import { WebhookManager } from 'k-message';
194
+
195
+ const webhookManager = new WebhookManager();
196
+
197
+ // Register webhook endpoint
198
+ await webhookManager.registerEndpoint({
199
+ url: 'https://your-app.com/webhook',
200
+ events: ['MESSAGE_SENT', 'MESSAGE_DELIVERED', 'MESSAGE_FAILED'],
201
+ secret: 'your-webhook-secret'
202
+ });
203
+ ```
204
+
205
+ ## πŸ“¦ Package Architecture
206
+
207
+ k-msg is built as a modular monorepo. You can install the complete package or individual components:
208
+
209
+ ### Complete Package (Recommended)
210
+ ```bash
211
+ bun add k-msg # Includes all packages
212
+ ```
213
+
214
+ ### Individual Packages
215
+
216
+ | Package | Description | Usage |
217
+ |---------|-------------|-------|
218
+ | `@k-msg/core` | Foundation (types, errors, retry) | Base types and error handling |
219
+ | `@k-msg/provider` | Provider system (IWINV, etc.) | Multi-provider abstraction |
220
+ | `@k-msg/messaging` | Message sending & queues | Core messaging functionality |
221
+ | `@k-msg/template` | Template parsing & management | Dynamic template system |
222
+ | `@k-msg/analytics` | Metrics & reporting | Performance analytics |
223
+ | `@k-msg/webhook` | Event notifications | Real-time webhooks |
224
+ | `@k-msg/channel` | Channel & sender management | Business verification |
225
+
226
+ ### Dependency Graph
227
+ ```
228
+ core (foundation)
229
+ β”œβ”€β”€ provider β†’ core
230
+ β”œβ”€β”€ template β†’ core
231
+ β”œβ”€β”€ messaging β†’ core, provider, template
232
+ β”œβ”€β”€ analytics β†’ core, messaging
233
+ β”œβ”€β”€ webhook β†’ core, messaging
234
+ β”œβ”€β”€ channel β†’ core
235
+ └── k-msg β†’ all packages
236
+ ```
237
+
238
+ ## βš™οΈ Configuration
239
+
240
+ ### Environment Variables
241
+
242
+ ```bash
243
+ # Required
244
+ IWINV_API_KEY=your-iwinv-api-key
245
+
246
+ # Optional
247
+ IWINV_BASE_URL=https://alimtalk.bizservice.iwinv.kr
248
+ NODE_ENV=development
249
+ PORT=3000
250
+
251
+ # Debug mode
252
+ DEBUG=k-msg:*
253
+ ```
254
+
255
+ ### TypeScript Configuration
256
+
257
+ ```typescript
258
+ interface MessageServiceConfig {
259
+ apiKey: string;
260
+ baseUrl?: string;
261
+ debug?: boolean;
262
+ autoLoad?: boolean;
263
+ timeout?: number;
264
+ retryAttempts?: number;
265
+
266
+ // Advanced options
267
+ customHandlers?: {
268
+ templateLoader?: (provider: Provider) => Promise<Template[]>;
269
+ errorHandler?: (error: Error, context: string) => void;
270
+ };
271
+
272
+ // Provider-specific settings
273
+ providerSpecific?: {
274
+ iwinv?: {
275
+ templateCategories?: string[];
276
+ maxVariables?: number;
277
+ enableBulkSending?: boolean;
278
+ };
279
+ };
280
+ }
281
+ ```
282
+
283
+ ### Factory Configuration Options
284
+
285
+ ```typescript
286
+ // Simple configuration
287
+ const service1 = MessageServiceFactory.createIWINVService({
288
+ apiKey: 'your-key',
289
+ debug: true
290
+ });
291
+
292
+ // Advanced configuration
293
+ const service2 = MessageServiceFactory.createService(provider, {
294
+ debug: true,
295
+ autoLoad: false,
296
+ customHandlers: {
297
+ errorHandler: (error, context) => {
298
+ console.error(`[${context}] ${error.message}`);
299
+ // Send to monitoring service
300
+ }
301
+ },
302
+ providerSpecific: {
303
+ iwinv: {
304
+ templateCategories: ['AUTHENTICATION', 'NOTIFICATION'],
305
+ enableBulkSending: false
306
+ }
307
+ }
308
+ });
309
+ ```
310
+
311
+ ## πŸ§ͺ Testing
312
+
313
+ ```typescript
314
+ import { MessageServiceFactory, TestUtils } from 'k-msg';
315
+
316
+ // Create test service with mock provider
317
+ const testService = MessageServiceFactory.createIWINVService({
318
+ apiKey: 'test-key',
319
+ debug: true
320
+ });
321
+
322
+ // Test message sending
323
+ const result = await testService.sendMessage(
324
+ '01012345678',
325
+ 'test_template',
326
+ { name: 'ν…ŒμŠ€νŠΈ μ‚¬μš©μž' }
327
+ );
328
+
329
+ // Use test utilities
330
+ const testMessage = TestUtils.createTestMessage({
331
+ templateCode: 'AUTH_OTP',
332
+ phoneNumber: '01012345678'
333
+ });
334
+
335
+ const mockResult = TestUtils.createMockResult({
336
+ success: true,
337
+ messageId: 'test-message-001'
338
+ });
339
+ ```
340
+
341
+ ### Run Tests
342
+
343
+ ```bash
344
+ # Run all tests
345
+ bun test
346
+
347
+ # Run tests with coverage
348
+ bun test --coverage
349
+
350
+ # Run specific package tests
351
+ bun test packages/core
352
+ bun test packages/messaging
353
+
354
+ # Run tests by type
355
+ bun test:unit # Fast unit tests
356
+ bun test:integration # Integration tests
357
+ bun test:e2e # End-to-end tests (requires real API keys)
358
+ ```
359
+
360
+ ## πŸ“– Examples
361
+
362
+ Check out the `/examples` directory for complete working examples:
363
+
364
+ - **Basic Usage** - Simple message sending
365
+ - **Web Server** - Hono/Express integration
366
+ - **Bulk Messaging** - Batch message processing
367
+ - **Template Management** - Dynamic template creation
368
+ - **Analytics Dashboard** - Real-time monitoring
369
+ - **Webhook Integration** - Event-driven architecture
370
+
371
+ ## 🀝 Contributing
372
+
373
+ We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.
374
+
375
+ ### Development Setup
376
+
377
+ ```bash
378
+ # Clone the repository
379
+ git clone https://github.com/k-otp/k-msg.git
380
+ cd k-msg
381
+
382
+ # Install dependencies
383
+ bun install
384
+
385
+ # Build all packages
386
+ bun run build:all
387
+
388
+ # Run tests
389
+ bun test
390
+
391
+ # Start development server
392
+ bun run dev
393
+ ```
394
+
395
+ ## πŸ“„ License
396
+
397
+ MIT License - see [LICENSE](../../LICENSE) for details.
398
+
399
+ ## πŸ†˜ Support
400
+
401
+ - **Documentation**: [GitHub Wiki](https://github.com/k-otp/k-msg/wiki)
402
+ - **API Reference**: [TypeDoc](https://k-otp.github.io/k-msg/)
403
+ - **Issues**: [GitHub Issues](https://github.com/k-otp/k-msg/issues)
404
+ - **Discussions**: [GitHub Discussions](https://github.com/k-otp/k-msg/discussions)
405
+ - **Discord**: [Join our Discord](https://discord.gg/k-msg)
406
+
407
+ ### Commercial Support
408
+
409
+ For enterprise support, consulting, or custom integrations, please contact us at [support@k-msg.dev](mailto:support@k-msg.dev).
410
+
411
+ ---
412
+
413
+ **Made with ❀️ for the Korean developer community**
414
+
415
+ *K-Message is not affiliated with Kakao Corp or any messaging service providers.*
@@ -0,0 +1,15 @@
1
+ /**
2
+ * K-Message: Korean Multi-Channel Messaging Platform
3
+ * Unified package that re-exports core functionality for easy use
4
+ */
5
+ export { fail, ok, type Result } from "@k-msg/core";
6
+ export { KMsg } from "@k-msg/messaging";
7
+ export type { AligoConfig, ProviderMessageRequest, ProviderMessageResult, } from "@k-msg/provider";
8
+ export { AligoProvider, IWINVProvider } from "@k-msg/provider";
9
+ export {
10
+ /** @deprecated Use KMsg instead */
11
+ createKMsgAnalytics,
12
+ /** @deprecated Use KMsg instead */
13
+ createKMsgSender,
14
+ /** @deprecated Use KMsg instead */
15
+ createKMsgTemplates, } from "./modules";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ var{defineProperty:M,getOwnPropertyNames:k,getOwnPropertyDescriptor:D}=Object,y=Object.prototype.hasOwnProperty;var x=new WeakMap,w=(K)=>{var O=x.get(K),A;if(O)return O;if(O=M({},"__esModule",{value:!0}),K&&typeof K==="object"||typeof K==="function")k(K).map((j)=>!y.call(O,j)&&M(O,j,{get:()=>K[j],enumerable:!(A=D(K,j))||A.enumerable}));return x.set(K,O),O};var I=(K,O)=>{for(var A in O)M(K,A,{get:O[A],enumerable:!0,configurable:!0,set:(j)=>O[A]=()=>j})};var N={};I(N,{ok:()=>$.ok,fail:()=>$.fail,createKMsgTemplates:()=>U,createKMsgSender:()=>R,createKMsgAnalytics:()=>z,KMsg:()=>F.KMsg,IWINVProvider:()=>q.IWINVProvider,AligoProvider:()=>q.AligoProvider});module.exports=w(N);var $=require("@k-msg/core"),F=require("@k-msg/messaging"),q=require("@k-msg/provider");var _=require("@k-msg/provider");function R(K){let O=new _.IWINVProvider({apiKey:K.iwinvApiKey,baseUrl:K.iwinvBaseUrl||"https://alimtalk.bizservice.iwinv.kr",debug:!1});return{async sendMessage(A,j,E){try{let B=await O.send({templateCode:j,phoneNumber:A,variables:E});if(!B.isSuccess){let G=B.error,H=G instanceof Error?G.message:typeof G==="string"?G:"Provider reported a failure";throw Error(H)}return{messageId:B.value.messageId,status:"SENT",templateCode:j,phoneNumber:A,variables:E,error:null,sentAt:new Date().toISOString()}}catch(B){return{messageId:null,status:"FAILED",templateCode:j,phoneNumber:A,variables:E,error:B instanceof Error?B.message:"Unknown error",sentAt:new Date().toISOString()}}},async sendBulk(A,j,E){let B=`batch_${Date.now()}`,J=E?.batchSize||10,G=E?.batchDelay||1000,H=[],Q=0,L=0;for(let X=0;X<A.length;X+=J){let W=A.slice(X,X+J).map(async(Y)=>{try{let Z=await O.send({templateCode:j,phoneNumber:Y.phoneNumber,variables:Y.variables});if(Z.isSuccess&&Z.value.messageId)return Q++,{messageId:Z.value.messageId,phoneNumber:Y.phoneNumber,status:"SENT",variables:Y.variables,error:null};else return L++,{messageId:null,phoneNumber:Y.phoneNumber,status:"FAILED",variables:Y.variables,error:Z.error?.message||"Unknown error"}}catch(Z){return L++,{messageId:null,phoneNumber:Y.phoneNumber,status:"FAILED",variables:Y.variables,error:Z instanceof Error?Z.message:"Unknown error"}}}),P=await Promise.all(W);if(H.push(...P),X+J<A.length&&G>0)await new Promise((Y)=>setTimeout(Y,G))}return{batchId:B,templateCode:j,totalCount:A.length,successCount:Q,failureCount:L,processedAt:new Date().toISOString(),results:H}},async getStatus(A){try{let j=await O.getStatus(A);return{messageId:A,status:j,checkedAt:new Date().toISOString(),deliveredAt:j==="DELIVERED"?new Date().toISOString():void 0,failedAt:j==="FAILED"?new Date().toISOString():void 0}}catch(j){return{messageId:A,status:"UNKNOWN",error:j instanceof Error?j.message:"Failed to check status",checkedAt:new Date().toISOString()}}}}}function U(K){let O=new _.IWINVProvider({apiKey:K.iwinvApiKey,baseUrl:K.iwinvBaseUrl||"https://alimtalk.bizservice.iwinv.kr",debug:!1});return{async create(A,j,E){try{let B=this.parseVariables(j),J={templateId:A,status:"pending",message:"Template creation not implemented yet"};return{id:J.templateId,code:J.templateId,content:j,description:E,variables:B,status:J.status,message:J.message,createdAt:new Date().toISOString()}}catch(B){return{id:null,code:A,content:j,description:E,variables:this.parseVariables(j),status:"FAILED",error:B instanceof Error?B.message:"Unknown error",createdAt:new Date().toISOString()}}},async list(A){try{return[]}catch(j){return console.warn("Failed to list templates:",j),[]}},async validate(A){let j=this.parseVariables(A),E=[];if(A.length===0)E.push("Template content cannot be empty");if(A.length>1000)E.push("Template content too long (max 1000 chars)");return{isValid:E.length===0,errors:E,variables:j}},parseVariables(A){let j=A.match(/#{([^}]+)}/g)||[],E=new Set(j.map((B)=>B.slice(2,-1)));return Array.from(E).map((B)=>({name:B,type:"string",required:!0}))},async test(A,j){try{let E={code:A,content:`Template ${A}`,variables:[]},J=this.parseVariables(E.content).filter((L)=>L.required&&!(L.name in j)).map((L)=>L.name);if(J.length>0)return{templateCode:A,renderedContent:null,isValid:!1,errors:[`Missing required variables: ${J.join(", ")}`]};let G=E.content;for(let[L,X]of Object.entries(j)){let T=new RegExp(`#{${L}}`,"g");G=G.replace(T,String(X))}let H=G.match(/#{([^}]+)}/g),Q=H?[`Unreplaced variables found: ${H.join(", ")}`]:[];return{templateCode:A,renderedContent:G,isValid:!H,warnings:Q}}catch(E){return{templateCode:A,renderedContent:null,isValid:!1,error:E instanceof Error?E.message:"Template test failed"}}}}}function z(K){let O=new _.IWINVProvider({apiKey:K.iwinvApiKey,baseUrl:K.iwinvBaseUrl||"https://alimtalk.bizservice.iwinv.kr",debug:!1});return{async getMessageStats(A="day"){try{let j=new Date,E=new Date;switch(A){case"day":E.setDate(j.getDate()-1);break;case"week":E.setDate(j.getDate()-7);break;case"month":E.setMonth(j.getMonth()-1);break}let B={sentMessages:0,failedMessages:0,totalMessages:0,deliveredMessages:0,deliveryRate:0,breakdown:{byTemplate:{},byDay:{},byHour:{}}};return{period:A,totalSent:B.sentMessages,totalDelivered:B.deliveredMessages,totalFailed:B.failedMessages,deliveryRate:B.deliveryRate,periodStart:E.toISOString(),periodEnd:j.toISOString(),breakdown:B.breakdown}}catch(j){return console.warn("Failed to get message stats:",j),{period:A,totalSent:0,totalDelivered:0,totalFailed:0,deliveryRate:0,periodStart:new Date().toISOString(),periodEnd:new Date().toISOString(),error:j instanceof Error?j.message:"Unknown error"}}},async getTemplateUsage(A){try{let j=new Date;if(new Date().setMonth(j.getMonth()-1),A){let G={sent:0,delivered:0,failed:0,deliveryRate:0,averageDeliveryTime:0};return{templateCode:A,totalUsage:G.sent,successCount:G.delivered,failureCount:G.failed,successRate:G.deliveryRate,lastUsed:new Date().toISOString(),averageDeliveryTime:G.averageDeliveryTime}}let B={breakdown:{byTemplate:{}},deliveryRate:0};return Object.entries(B.breakdown.byTemplate).map(([G,H])=>{let Q=Number(H)||0,L=Math.round(Q*(B.deliveryRate/100)),X=Q-L;return{templateCode:G,totalUsage:Q,successCount:L,failureCount:X,successRate:B.deliveryRate}}).sort((G,H)=>H.totalUsage-G.totalUsage)}catch(j){return console.warn("Failed to get template usage:",j),A?{templateCode:A,totalUsage:0,successCount:0,failureCount:0,successRate:0,error:j instanceof Error?j.message:"Unknown error"}:[]}},async generateReport(A,j="json"){try{let E=new Date,B=new Date;switch(A){case"daily":B.setDate(E.getDate()-1);break;case"weekly":B.setDate(E.getDate()-7);break;case"monthly":B.setMonth(E.getMonth()-1);break}let J={sentMessages:0,deliveredMessages:0,failedMessages:0,totalMessages:0,deliveryRate:0,failureRate:0,breakdown:{byTemplate:{},byDay:{},byHour:{}}},G=await this.getTemplateUsage(),H=Array.isArray(G)?G.slice(0,3).map((L)=>L.templateCode):[],Q={reportType:A,generatedAt:new Date().toISOString(),period:{from:B.toISOString(),to:E.toISOString()},summary:{totalMessages:J.totalMessages,successRate:J.deliveryRate,failureRate:J.failureRate,topTemplates:H},breakdown:{byTemplate:J.breakdown.byTemplate,byDay:J.breakdown.byDay,byHour:J.breakdown.byHour}};return j==="csv"?this.toCsv(Q):Q}catch(E){console.warn("Failed to generate report:",E);let B={reportType:A,generatedAt:new Date().toISOString(),error:E instanceof Error?E.message:"Unknown error",summary:{totalMessages:0,successRate:0,failureRate:0,topTemplates:[]}};return j==="csv"?this.toCsv(B):B}},async getDeliveryStats(A){try{let j=new Date;new Date().setDate(j.getDate()-7);let B={sentMessages:0,deliveredMessages:0,failedMessages:0,totalMessages:0,deliveryRate:0,failureRate:0,breakdown:{byTemplate:{}}},J=B;if(A){let H=B.breakdown.byTemplate[A]||0,Q=Math.round(H*(B.deliveryRate/100)),L=Math.round(H*(B.failureRate/100)),X=H-Q-L;return{templateCode:A,delivered:Q,pending:X,failed:L,total:H,breakdown:{DELIVERED:H>0?Q/H*100:0,PENDING:H>0?X/H*100:0,FAILED:H>0?L/H*100:0}}}let G=Math.max(0,B.totalMessages-B.deliveredMessages-B.failedMessages);return{templateCode:void 0,delivered:B.deliveredMessages,pending:G,failed:B.failedMessages,total:B.totalMessages,breakdown:{DELIVERED:B.deliveryRate,PENDING:B.totalMessages>0?G/B.totalMessages*100:0,FAILED:B.failureRate}}}catch(j){return console.warn("Failed to get delivery stats:",j),{templateCode:A,delivered:0,pending:0,failed:0,total:0,breakdown:{DELIVERED:0,PENDING:0,FAILED:0},error:j instanceof Error?j.message:"Unknown error"}}},toCsv(A){return JSON.stringify(A,null,2).replace(/[{}]/g,"").replace(/"/g,"").replace(/:/g,",")}}}
2
+
3
+ //# debugId=22963F33A80EC0B864756E2164756E21
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/modules/index.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * K-Message: Korean Multi-Channel Messaging Platform\n * Unified package that re-exports core functionality for easy use\n */\n\n// Re-export core result types\nexport { fail, ok, type Result } from \"@k-msg/core\";\n// Re-export the main KMsg client\nexport { KMsg } from \"@k-msg/messaging\";\n\n// Re-export essential types\nexport type {\n AligoConfig,\n ProviderMessageRequest,\n ProviderMessageResult,\n} from \"@k-msg/provider\";\n// Re-export the most essential classes for basic usage\nexport { AligoProvider, IWINVProvider } from \"@k-msg/provider\";\n\n// Re-export simple modules for CLI/scripts\nexport {\n /** @deprecated Use KMsg instead */\n createKMsgAnalytics,\n /** @deprecated Use KMsg instead */\n createKMsgSender,\n /** @deprecated Use KMsg instead */\n createKMsgTemplates,\n} from \"./modules\";\n\n// Remove admin exports for now as they are incomplete\n",
6
+ "import { IWINVProvider } from \"@k-msg/provider\";\n\n/**\n * Simple message sender for CLI/scripts\n *\n * @example\n * ```typescript\n * import { createKMsgSender } from 'k-msg/modules';\n *\n * const sender = createKMsgSender({\n * iwinvApiKey: process.env.IWINV_API_KEY!\n * });\n *\n * // User defines their own templates and variables\n * await sender.sendMessage('01012345678', 'USER_OTP_TEMPLATE', {\n * code: '123456',\n * serviceName: 'MyApp',\n * expireMinutes: 3\n * });\n * ```\n */\nexport function createKMsgSender(config: {\n iwinvApiKey: string;\n iwinvBaseUrl?: string;\n}) {\n const provider = new IWINVProvider({\n apiKey: config.iwinvApiKey,\n baseUrl: config.iwinvBaseUrl || \"https://alimtalk.bizservice.iwinv.kr\",\n debug: false,\n });\n\n return {\n /**\n * Send message with custom template and variables\n * User defines their own template structure\n */\n async sendMessage(\n phoneNumber: string,\n templateCode: string,\n variables: Record<string, any>,\n ) {\n try {\n const result = (await provider.send({\n templateCode: templateCode as any,\n phoneNumber,\n variables,\n } as any)) as any;\n\n if (!result.isSuccess) {\n // Handle provider-level failure (e.g., invalid template, etc.)\n const error = result.error;\n const errorMessage =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"Provider reported a failure\";\n throw new Error(errorMessage);\n }\n\n const data = result.value;\n\n return {\n messageId: data.messageId,\n status: \"SENT\" as const,\n templateCode,\n phoneNumber,\n variables,\n error: null,\n sentAt: new Date().toISOString(),\n };\n } catch (error) {\n return {\n messageId: null,\n status: \"FAILED\" as const,\n templateCode,\n phoneNumber,\n variables,\n error: error instanceof Error ? error.message : \"Unknown error\",\n sentAt: new Date().toISOString(),\n };\n }\n },\n\n /**\n * Send bulk messages with user-defined template\n */\n async sendBulk(\n recipients: Array<{\n phoneNumber: string;\n variables: Record<string, any>;\n }>,\n templateCode: string,\n options?: {\n batchSize?: number;\n batchDelay?: number;\n },\n ) {\n const batchId = `batch_${Date.now()}`;\n const batchSize = options?.batchSize || 10;\n const batchDelay = options?.batchDelay || 1000;\n\n const results = [];\n let successCount = 0;\n let failureCount = 0;\n\n // Process recipients in batches\n for (let i = 0; i < recipients.length; i += batchSize) {\n const batch = recipients.slice(i, i + batchSize);\n\n // Process batch concurrently\n const batchPromises = batch.map(async (recipient) => {\n try {\n const result = (await provider.send({\n templateCode: templateCode as any,\n phoneNumber: recipient.phoneNumber,\n variables: recipient.variables,\n } as any)) as any;\n\n if (result.isSuccess && result.value.messageId) {\n successCount++;\n return {\n messageId: result.value.messageId,\n phoneNumber: recipient.phoneNumber,\n status: \"SENT\" as const,\n variables: recipient.variables,\n error: null,\n };\n } else {\n failureCount++;\n return {\n messageId: null,\n phoneNumber: recipient.phoneNumber,\n status: \"FAILED\" as const,\n variables: recipient.variables,\n error: result.error?.message || \"Unknown error\",\n };\n }\n } catch (error) {\n failureCount++;\n return {\n messageId: null,\n phoneNumber: recipient.phoneNumber,\n status: \"FAILED\" as const,\n variables: recipient.variables,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n });\n\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n\n // Add delay between batches (except for the last batch)\n if (i + batchSize < recipients.length && batchDelay > 0) {\n await new Promise((resolve) => setTimeout(resolve, batchDelay));\n }\n }\n\n return {\n batchId,\n templateCode,\n totalCount: recipients.length,\n successCount,\n failureCount,\n processedAt: new Date().toISOString(),\n results,\n };\n },\n\n /**\n * Check message delivery status\n */\n async getStatus(messageId: string) {\n try {\n const status = (await (provider as any).getStatus(messageId)) as any;\n\n return {\n messageId,\n status: status,\n checkedAt: new Date().toISOString(),\n deliveredAt:\n status === \"DELIVERED\" ? new Date().toISOString() : undefined,\n failedAt: status === \"FAILED\" ? new Date().toISOString() : undefined,\n };\n } catch (error) {\n return {\n messageId,\n status: \"UNKNOWN\",\n error:\n error instanceof Error ? error.message : \"Failed to check status\",\n checkedAt: new Date().toISOString(),\n };\n }\n },\n\n /**\n * Get platform instance for advanced usage (when implemented)\n */\n // getPlatform: () => platform\n };\n}\n\n/**\n * Template manager for CLI/scripts\n *\n * @example\n * ```typescript\n * const templates = createKMsgTemplates({ ... });\n * await templates.create('user_welcome', 'Welcome #{name}! Your account #{accountId} is ready.');\n * ```\n */\nexport function createKMsgTemplates(config: {\n iwinvApiKey: string;\n iwinvBaseUrl?: string;\n}) {\n const provider = new IWINVProvider({\n apiKey: config.iwinvApiKey,\n baseUrl: config.iwinvBaseUrl || \"https://alimtalk.bizservice.iwinv.kr\",\n debug: false,\n });\n\n return {\n /**\n * Create template with user-defined structure\n */\n async create(templateCode: string, content: string, description: string) {\n try {\n // Parse variables from template content\n const variables = this.parseVariables(content);\n\n // TODO: Implement createTemplate when available\n const result = {\n templateId: templateCode,\n status: \"pending\",\n message: \"Template creation not implemented yet\",\n };\n\n return {\n id: result.templateId,\n code: result.templateId,\n content,\n description,\n variables,\n status: result.status,\n message: result.message,\n createdAt: new Date().toISOString(),\n };\n } catch (error) {\n return {\n id: null,\n code: templateCode,\n content,\n description,\n variables: this.parseVariables(content),\n status: \"FAILED\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n createdAt: new Date().toISOString(),\n };\n }\n },\n\n /**\n * List user templates\n */\n async list(filters?: { status?: string }) {\n try {\n // TODO: Implement templates.list when available\n return [];\n } catch (error) {\n console.warn(\"Failed to list templates:\", error);\n return [];\n }\n },\n\n /**\n * Validate template syntax and extract variables\n */\n async validate(content: string) {\n const variables = this.parseVariables(content);\n const errors: string[] = [];\n\n // Basic validation\n if (content.length === 0) {\n errors.push(\"Template content cannot be empty\");\n }\n\n if (content.length > 1000) {\n errors.push(\"Template content too long (max 1000 chars)\");\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n variables,\n };\n },\n\n /**\n * Parse variables from template content\n */\n parseVariables(content: string) {\n const matches = content.match(/#{([^}]+)}/g) || [];\n // Use a Set to efficiently get unique variable names\n const uniqueNames = new Set(matches.map((match) => match.slice(2, -1)));\n\n return Array.from(uniqueNames).map((name) => ({\n name,\n type: \"string\",\n required: true,\n }));\n },\n\n /**\n * Test template with sample variables\n */\n async test(templateCode: string, sampleVariables: Record<string, any>) {\n try {\n // TODO: Implement template validation when available\n const template = {\n code: templateCode,\n content: `Template ${templateCode}`,\n variables: [],\n };\n\n // Parse variables from template content\n const requiredVariables = this.parseVariables(template.content);\n const missingVariables = requiredVariables\n .filter((v) => v.required && !(v.name in sampleVariables))\n .map((v) => v.name);\n\n if (missingVariables.length > 0) {\n return {\n templateCode,\n renderedContent: null,\n isValid: false,\n errors: [\n `Missing required variables: ${missingVariables.join(\", \")}`,\n ],\n };\n }\n\n // Render template with sample variables\n let renderedContent = template.content;\n for (const [key, value] of Object.entries(sampleVariables)) {\n const regex = new RegExp(`#{${key}}`, \"g\");\n renderedContent = renderedContent.replace(regex, String(value));\n }\n\n // Check for unreplaced variables\n const unreplacedVars = renderedContent.match(/#{([^}]+)}/g);\n const warnings = unreplacedVars\n ? [`Unreplaced variables found: ${unreplacedVars.join(\", \")}`]\n : [];\n\n return {\n templateCode,\n renderedContent,\n isValid: !unreplacedVars,\n warnings,\n };\n } catch (error) {\n return {\n templateCode,\n renderedContent: null,\n isValid: false,\n error:\n error instanceof Error ? error.message : \"Template test failed\",\n };\n }\n },\n };\n}\n\n/**\n * Analytics reader for scripts/reporting\n */\nexport function createKMsgAnalytics(config: {\n iwinvApiKey: string;\n iwinvBaseUrl?: string;\n}) {\n const provider = new IWINVProvider({\n apiKey: config.iwinvApiKey,\n baseUrl: config.iwinvBaseUrl || \"https://alimtalk.bizservice.iwinv.kr\",\n debug: false,\n });\n\n return {\n /**\n * Get message statistics for specified period\n */\n async getMessageStats(period: \"day\" | \"week\" | \"month\" = \"day\") {\n try {\n const now = new Date();\n const periodStart = new Date();\n\n switch (period) {\n case \"day\":\n periodStart.setDate(now.getDate() - 1);\n break;\n case \"week\":\n periodStart.setDate(now.getDate() - 7);\n break;\n case \"month\":\n periodStart.setMonth(now.getMonth() - 1);\n break;\n }\n\n // TODO: Implement analytics when available\n const usage = {\n sentMessages: 0,\n failedMessages: 0,\n totalMessages: 0,\n deliveredMessages: 0,\n deliveryRate: 0,\n breakdown: { byTemplate: {}, byDay: {}, byHour: {} },\n };\n\n return {\n period,\n totalSent: usage.sentMessages,\n totalDelivered: usage.deliveredMessages,\n totalFailed: usage.failedMessages,\n deliveryRate: usage.deliveryRate,\n periodStart: periodStart.toISOString(),\n periodEnd: now.toISOString(),\n breakdown: usage.breakdown,\n };\n } catch (error) {\n console.warn(\"Failed to get message stats:\", error);\n return {\n period,\n totalSent: 0,\n totalDelivered: 0,\n totalFailed: 0,\n deliveryRate: 0,\n periodStart: new Date().toISOString(),\n periodEnd: new Date().toISOString(),\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n },\n\n /**\n * Get template usage analytics\n */\n async getTemplateUsage(templateCode?: string) {\n try {\n const now = new Date();\n const monthAgo = new Date();\n monthAgo.setMonth(now.getMonth() - 1);\n\n if (templateCode) {\n // TODO: Implement getTemplateStats when available\n const stats = {\n sent: 0,\n delivered: 0,\n failed: 0,\n deliveryRate: 0,\n averageDeliveryTime: 0,\n };\n\n return {\n templateCode,\n totalUsage: stats.sent,\n successCount: stats.delivered,\n failureCount: stats.failed,\n successRate: stats.deliveryRate,\n lastUsed: new Date().toISOString(),\n averageDeliveryTime: stats.averageDeliveryTime,\n };\n }\n\n // Get usage for all templates by getting overall stats\n // TODO: provider.analytics.getUsage\n const usage = {\n breakdown: { byTemplate: {} },\n deliveryRate: 0,\n };\n\n const templateUsage = Object.entries(usage.breakdown.byTemplate).map(\n ([code, count]) => {\n const countNum = Number(count) || 0;\n const successCount = Math.round(\n countNum * (usage.deliveryRate / 100),\n );\n const failureCount = countNum - successCount;\n\n return {\n templateCode: code,\n totalUsage: countNum,\n successCount,\n failureCount,\n successRate: usage.deliveryRate,\n };\n },\n );\n\n return templateUsage.sort((a, b) => b.totalUsage - a.totalUsage);\n } catch (error) {\n console.warn(\"Failed to get template usage:\", error);\n return templateCode\n ? {\n templateCode,\n totalUsage: 0,\n successCount: 0,\n failureCount: 0,\n successRate: 0,\n error: error instanceof Error ? error.message : \"Unknown error\",\n }\n : [];\n }\n },\n\n /**\n * Generate usage reports\n */\n async generateReport(\n type: \"daily\" | \"weekly\" | \"monthly\",\n format: \"json\" | \"csv\" = \"json\",\n ) {\n try {\n const now = new Date();\n const periodStart = new Date();\n\n switch (type) {\n case \"daily\":\n periodStart.setDate(now.getDate() - 1);\n break;\n case \"weekly\":\n periodStart.setDate(now.getDate() - 7);\n break;\n case \"monthly\":\n periodStart.setMonth(now.getMonth() - 1);\n break;\n }\n\n // TODO: provider.analytics.getUsage\n const usage = {\n sentMessages: 0,\n deliveredMessages: 0,\n failedMessages: 0,\n totalMessages: 0,\n deliveryRate: 0,\n failureRate: 0,\n breakdown: {\n byTemplate: {} as Record<string, number>,\n byDay: {} as Record<string, number>,\n byHour: {} as Record<string, number>,\n },\n };\n\n const templateUsage = await this.getTemplateUsage();\n const topTemplates = Array.isArray(templateUsage)\n ? templateUsage.slice(0, 3).map((t) => t.templateCode)\n : [];\n\n const data = {\n reportType: type,\n generatedAt: new Date().toISOString(),\n period: {\n from: periodStart.toISOString(),\n to: now.toISOString(),\n },\n summary: {\n totalMessages: usage.totalMessages,\n successRate: usage.deliveryRate,\n failureRate: usage.failureRate,\n topTemplates,\n },\n breakdown: {\n byTemplate: usage.breakdown.byTemplate,\n byDay: usage.breakdown.byDay,\n byHour: usage.breakdown.byHour,\n },\n };\n\n return format === \"csv\" ? this.toCsv(data) : data;\n } catch (error) {\n console.warn(\"Failed to generate report:\", error);\n const fallbackData = {\n reportType: type,\n generatedAt: new Date().toISOString(),\n error: error instanceof Error ? error.message : \"Unknown error\",\n summary: {\n totalMessages: 0,\n successRate: 0,\n failureRate: 0,\n topTemplates: [],\n },\n };\n return format === \"csv\" ? this.toCsv(fallbackData) : fallbackData;\n }\n },\n\n /**\n * Get delivery status breakdown\n */\n async getDeliveryStats(templateCode?: string) {\n try {\n const now = new Date();\n const weekAgo = new Date();\n weekAgo.setDate(now.getDate() - 7);\n\n // TODO: provider.analytics.getUsage\n const usage = {\n sentMessages: 0,\n deliveredMessages: 0,\n failedMessages: 0,\n totalMessages: 0,\n deliveryRate: 0,\n failureRate: 0,\n breakdown: {\n byTemplate: {} as Record<string, number>,\n },\n };\n\n const filtered = usage;\n if (templateCode) {\n const templateCount = usage.breakdown.byTemplate[templateCode] || 0;\n const templateDelivered = Math.round(\n templateCount * (usage.deliveryRate / 100),\n );\n const templateFailed = Math.round(\n templateCount * (usage.failureRate / 100),\n );\n const templatePending =\n templateCount - templateDelivered - templateFailed;\n\n return {\n templateCode,\n delivered: templateDelivered,\n pending: templatePending,\n failed: templateFailed,\n total: templateCount,\n breakdown: {\n DELIVERED:\n templateCount > 0\n ? (templateDelivered / templateCount) * 100\n : 0,\n PENDING:\n templateCount > 0 ? (templatePending / templateCount) * 100 : 0,\n FAILED:\n templateCount > 0 ? (templateFailed / templateCount) * 100 : 0,\n },\n };\n }\n\n const pending = Math.max(\n 0,\n usage.totalMessages - usage.deliveredMessages - usage.failedMessages,\n );\n\n return {\n templateCode: undefined,\n delivered: usage.deliveredMessages,\n pending,\n failed: usage.failedMessages,\n total: usage.totalMessages,\n breakdown: {\n DELIVERED: usage.deliveryRate,\n PENDING:\n usage.totalMessages > 0\n ? (pending / usage.totalMessages) * 100\n : 0,\n FAILED: usage.failureRate,\n },\n };\n } catch (error) {\n console.warn(\"Failed to get delivery stats:\", error);\n return {\n templateCode,\n delivered: 0,\n pending: 0,\n failed: 0,\n total: 0,\n breakdown: {\n DELIVERED: 0,\n PENDING: 0,\n FAILED: 0,\n },\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n },\n\n /**\n * Convert data to CSV format\n */\n toCsv(data: any): string {\n // Simple CSV conversion\n return JSON.stringify(data, null, 2)\n .replace(/[{}]/g, \"\")\n .replace(/\"/g, \"\")\n .replace(/:/g, \",\");\n },\n };\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "22963F33A80EC0B864756E2164756E21",
10
+ "names": []
11
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import{fail as P,ok as k}from"@k-msg/core";import{KMsg as y}from"@k-msg/messaging";import{AligoProvider as I,IWINVProvider as N}from"@k-msg/provider";import{IWINVProvider as _}from"@k-msg/provider";function T(X){let Z=new _({apiKey:X.iwinvApiKey,baseUrl:X.iwinvBaseUrl||"https://alimtalk.bizservice.iwinv.kr",debug:!1});return{async sendMessage(B,j,E){try{let A=await Z.send({templateCode:j,phoneNumber:B,variables:E});if(!A.isSuccess){let G=A.error,H=G instanceof Error?G.message:typeof G==="string"?G:"Provider reported a failure";throw Error(H)}return{messageId:A.value.messageId,status:"SENT",templateCode:j,phoneNumber:B,variables:E,error:null,sentAt:new Date().toISOString()}}catch(A){return{messageId:null,status:"FAILED",templateCode:j,phoneNumber:B,variables:E,error:A instanceof Error?A.message:"Unknown error",sentAt:new Date().toISOString()}}},async sendBulk(B,j,E){let A=`batch_${Date.now()}`,J=E?.batchSize||10,G=E?.batchDelay||1000,H=[],L=0,K=0;for(let O=0;O<B.length;O+=J){let q=B.slice(O,O+J).map(async(Q)=>{try{let Y=await Z.send({templateCode:j,phoneNumber:Q.phoneNumber,variables:Q.variables});if(Y.isSuccess&&Y.value.messageId)return L++,{messageId:Y.value.messageId,phoneNumber:Q.phoneNumber,status:"SENT",variables:Q.variables,error:null};else return K++,{messageId:null,phoneNumber:Q.phoneNumber,status:"FAILED",variables:Q.variables,error:Y.error?.message||"Unknown error"}}catch(Y){return K++,{messageId:null,phoneNumber:Q.phoneNumber,status:"FAILED",variables:Q.variables,error:Y instanceof Error?Y.message:"Unknown error"}}}),M=await Promise.all(q);if(H.push(...M),O+J<B.length&&G>0)await new Promise((Q)=>setTimeout(Q,G))}return{batchId:A,templateCode:j,totalCount:B.length,successCount:L,failureCount:K,processedAt:new Date().toISOString(),results:H}},async getStatus(B){try{let j=await Z.getStatus(B);return{messageId:B,status:j,checkedAt:new Date().toISOString(),deliveredAt:j==="DELIVERED"?new Date().toISOString():void 0,failedAt:j==="FAILED"?new Date().toISOString():void 0}}catch(j){return{messageId:B,status:"UNKNOWN",error:j instanceof Error?j.message:"Failed to check status",checkedAt:new Date().toISOString()}}}}}function x(X){let Z=new _({apiKey:X.iwinvApiKey,baseUrl:X.iwinvBaseUrl||"https://alimtalk.bizservice.iwinv.kr",debug:!1});return{async create(B,j,E){try{let A=this.parseVariables(j),J={templateId:B,status:"pending",message:"Template creation not implemented yet"};return{id:J.templateId,code:J.templateId,content:j,description:E,variables:A,status:J.status,message:J.message,createdAt:new Date().toISOString()}}catch(A){return{id:null,code:B,content:j,description:E,variables:this.parseVariables(j),status:"FAILED",error:A instanceof Error?A.message:"Unknown error",createdAt:new Date().toISOString()}}},async list(B){try{return[]}catch(j){return console.warn("Failed to list templates:",j),[]}},async validate(B){let j=this.parseVariables(B),E=[];if(B.length===0)E.push("Template content cannot be empty");if(B.length>1000)E.push("Template content too long (max 1000 chars)");return{isValid:E.length===0,errors:E,variables:j}},parseVariables(B){let j=B.match(/#{([^}]+)}/g)||[],E=new Set(j.map((A)=>A.slice(2,-1)));return Array.from(E).map((A)=>({name:A,type:"string",required:!0}))},async test(B,j){try{let E={code:B,content:`Template ${B}`,variables:[]},J=this.parseVariables(E.content).filter((K)=>K.required&&!(K.name in j)).map((K)=>K.name);if(J.length>0)return{templateCode:B,renderedContent:null,isValid:!1,errors:[`Missing required variables: ${J.join(", ")}`]};let G=E.content;for(let[K,O]of Object.entries(j)){let $=new RegExp(`#{${K}}`,"g");G=G.replace($,String(O))}let H=G.match(/#{([^}]+)}/g),L=H?[`Unreplaced variables found: ${H.join(", ")}`]:[];return{templateCode:B,renderedContent:G,isValid:!H,warnings:L}}catch(E){return{templateCode:B,renderedContent:null,isValid:!1,error:E instanceof Error?E.message:"Template test failed"}}}}}function R(X){let Z=new _({apiKey:X.iwinvApiKey,baseUrl:X.iwinvBaseUrl||"https://alimtalk.bizservice.iwinv.kr",debug:!1});return{async getMessageStats(B="day"){try{let j=new Date,E=new Date;switch(B){case"day":E.setDate(j.getDate()-1);break;case"week":E.setDate(j.getDate()-7);break;case"month":E.setMonth(j.getMonth()-1);break}let A={sentMessages:0,failedMessages:0,totalMessages:0,deliveredMessages:0,deliveryRate:0,breakdown:{byTemplate:{},byDay:{},byHour:{}}};return{period:B,totalSent:A.sentMessages,totalDelivered:A.deliveredMessages,totalFailed:A.failedMessages,deliveryRate:A.deliveryRate,periodStart:E.toISOString(),periodEnd:j.toISOString(),breakdown:A.breakdown}}catch(j){return console.warn("Failed to get message stats:",j),{period:B,totalSent:0,totalDelivered:0,totalFailed:0,deliveryRate:0,periodStart:new Date().toISOString(),periodEnd:new Date().toISOString(),error:j instanceof Error?j.message:"Unknown error"}}},async getTemplateUsage(B){try{let j=new Date;if(new Date().setMonth(j.getMonth()-1),B){let G={sent:0,delivered:0,failed:0,deliveryRate:0,averageDeliveryTime:0};return{templateCode:B,totalUsage:G.sent,successCount:G.delivered,failureCount:G.failed,successRate:G.deliveryRate,lastUsed:new Date().toISOString(),averageDeliveryTime:G.averageDeliveryTime}}let A={breakdown:{byTemplate:{}},deliveryRate:0};return Object.entries(A.breakdown.byTemplate).map(([G,H])=>{let L=Number(H)||0,K=Math.round(L*(A.deliveryRate/100)),O=L-K;return{templateCode:G,totalUsage:L,successCount:K,failureCount:O,successRate:A.deliveryRate}}).sort((G,H)=>H.totalUsage-G.totalUsage)}catch(j){return console.warn("Failed to get template usage:",j),B?{templateCode:B,totalUsage:0,successCount:0,failureCount:0,successRate:0,error:j instanceof Error?j.message:"Unknown error"}:[]}},async generateReport(B,j="json"){try{let E=new Date,A=new Date;switch(B){case"daily":A.setDate(E.getDate()-1);break;case"weekly":A.setDate(E.getDate()-7);break;case"monthly":A.setMonth(E.getMonth()-1);break}let J={sentMessages:0,deliveredMessages:0,failedMessages:0,totalMessages:0,deliveryRate:0,failureRate:0,breakdown:{byTemplate:{},byDay:{},byHour:{}}},G=await this.getTemplateUsage(),H=Array.isArray(G)?G.slice(0,3).map((K)=>K.templateCode):[],L={reportType:B,generatedAt:new Date().toISOString(),period:{from:A.toISOString(),to:E.toISOString()},summary:{totalMessages:J.totalMessages,successRate:J.deliveryRate,failureRate:J.failureRate,topTemplates:H},breakdown:{byTemplate:J.breakdown.byTemplate,byDay:J.breakdown.byDay,byHour:J.breakdown.byHour}};return j==="csv"?this.toCsv(L):L}catch(E){console.warn("Failed to generate report:",E);let A={reportType:B,generatedAt:new Date().toISOString(),error:E instanceof Error?E.message:"Unknown error",summary:{totalMessages:0,successRate:0,failureRate:0,topTemplates:[]}};return j==="csv"?this.toCsv(A):A}},async getDeliveryStats(B){try{let j=new Date;new Date().setDate(j.getDate()-7);let A={sentMessages:0,deliveredMessages:0,failedMessages:0,totalMessages:0,deliveryRate:0,failureRate:0,breakdown:{byTemplate:{}}},J=A;if(B){let H=A.breakdown.byTemplate[B]||0,L=Math.round(H*(A.deliveryRate/100)),K=Math.round(H*(A.failureRate/100)),O=H-L-K;return{templateCode:B,delivered:L,pending:O,failed:K,total:H,breakdown:{DELIVERED:H>0?L/H*100:0,PENDING:H>0?O/H*100:0,FAILED:H>0?K/H*100:0}}}let G=Math.max(0,A.totalMessages-A.deliveredMessages-A.failedMessages);return{templateCode:void 0,delivered:A.deliveredMessages,pending:G,failed:A.failedMessages,total:A.totalMessages,breakdown:{DELIVERED:A.deliveryRate,PENDING:A.totalMessages>0?G/A.totalMessages*100:0,FAILED:A.failureRate}}}catch(j){return console.warn("Failed to get delivery stats:",j),{templateCode:B,delivered:0,pending:0,failed:0,total:0,breakdown:{DELIVERED:0,PENDING:0,FAILED:0},error:j instanceof Error?j.message:"Unknown error"}}},toCsv(B){return JSON.stringify(B,null,2).replace(/[{}]/g,"").replace(/"/g,"").replace(/:/g,",")}}}export{k as ok,P as fail,x as createKMsgTemplates,T as createKMsgSender,R as createKMsgAnalytics,y as KMsg,N as IWINVProvider,I as AligoProvider};
2
+
3
+ //# debugId=D9B07AD8895618F264756E2164756E21
4
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/modules/index.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * K-Message: Korean Multi-Channel Messaging Platform\n * Unified package that re-exports core functionality for easy use\n */\n\n// Re-export core result types\nexport { fail, ok, type Result } from \"@k-msg/core\";\n// Re-export the main KMsg client\nexport { KMsg } from \"@k-msg/messaging\";\n\n// Re-export essential types\nexport type {\n AligoConfig,\n ProviderMessageRequest,\n ProviderMessageResult,\n} from \"@k-msg/provider\";\n// Re-export the most essential classes for basic usage\nexport { AligoProvider, IWINVProvider } from \"@k-msg/provider\";\n\n// Re-export simple modules for CLI/scripts\nexport {\n /** @deprecated Use KMsg instead */\n createKMsgAnalytics,\n /** @deprecated Use KMsg instead */\n createKMsgSender,\n /** @deprecated Use KMsg instead */\n createKMsgTemplates,\n} from \"./modules\";\n\n// Remove admin exports for now as they are incomplete\n",
6
+ "import { IWINVProvider } from \"@k-msg/provider\";\n\n/**\n * Simple message sender for CLI/scripts\n *\n * @example\n * ```typescript\n * import { createKMsgSender } from 'k-msg/modules';\n *\n * const sender = createKMsgSender({\n * iwinvApiKey: process.env.IWINV_API_KEY!\n * });\n *\n * // User defines their own templates and variables\n * await sender.sendMessage('01012345678', 'USER_OTP_TEMPLATE', {\n * code: '123456',\n * serviceName: 'MyApp',\n * expireMinutes: 3\n * });\n * ```\n */\nexport function createKMsgSender(config: {\n iwinvApiKey: string;\n iwinvBaseUrl?: string;\n}) {\n const provider = new IWINVProvider({\n apiKey: config.iwinvApiKey,\n baseUrl: config.iwinvBaseUrl || \"https://alimtalk.bizservice.iwinv.kr\",\n debug: false,\n });\n\n return {\n /**\n * Send message with custom template and variables\n * User defines their own template structure\n */\n async sendMessage(\n phoneNumber: string,\n templateCode: string,\n variables: Record<string, any>,\n ) {\n try {\n const result = (await provider.send({\n templateCode: templateCode as any,\n phoneNumber,\n variables,\n } as any)) as any;\n\n if (!result.isSuccess) {\n // Handle provider-level failure (e.g., invalid template, etc.)\n const error = result.error;\n const errorMessage =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"Provider reported a failure\";\n throw new Error(errorMessage);\n }\n\n const data = result.value;\n\n return {\n messageId: data.messageId,\n status: \"SENT\" as const,\n templateCode,\n phoneNumber,\n variables,\n error: null,\n sentAt: new Date().toISOString(),\n };\n } catch (error) {\n return {\n messageId: null,\n status: \"FAILED\" as const,\n templateCode,\n phoneNumber,\n variables,\n error: error instanceof Error ? error.message : \"Unknown error\",\n sentAt: new Date().toISOString(),\n };\n }\n },\n\n /**\n * Send bulk messages with user-defined template\n */\n async sendBulk(\n recipients: Array<{\n phoneNumber: string;\n variables: Record<string, any>;\n }>,\n templateCode: string,\n options?: {\n batchSize?: number;\n batchDelay?: number;\n },\n ) {\n const batchId = `batch_${Date.now()}`;\n const batchSize = options?.batchSize || 10;\n const batchDelay = options?.batchDelay || 1000;\n\n const results = [];\n let successCount = 0;\n let failureCount = 0;\n\n // Process recipients in batches\n for (let i = 0; i < recipients.length; i += batchSize) {\n const batch = recipients.slice(i, i + batchSize);\n\n // Process batch concurrently\n const batchPromises = batch.map(async (recipient) => {\n try {\n const result = (await provider.send({\n templateCode: templateCode as any,\n phoneNumber: recipient.phoneNumber,\n variables: recipient.variables,\n } as any)) as any;\n\n if (result.isSuccess && result.value.messageId) {\n successCount++;\n return {\n messageId: result.value.messageId,\n phoneNumber: recipient.phoneNumber,\n status: \"SENT\" as const,\n variables: recipient.variables,\n error: null,\n };\n } else {\n failureCount++;\n return {\n messageId: null,\n phoneNumber: recipient.phoneNumber,\n status: \"FAILED\" as const,\n variables: recipient.variables,\n error: result.error?.message || \"Unknown error\",\n };\n }\n } catch (error) {\n failureCount++;\n return {\n messageId: null,\n phoneNumber: recipient.phoneNumber,\n status: \"FAILED\" as const,\n variables: recipient.variables,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n });\n\n const batchResults = await Promise.all(batchPromises);\n results.push(...batchResults);\n\n // Add delay between batches (except for the last batch)\n if (i + batchSize < recipients.length && batchDelay > 0) {\n await new Promise((resolve) => setTimeout(resolve, batchDelay));\n }\n }\n\n return {\n batchId,\n templateCode,\n totalCount: recipients.length,\n successCount,\n failureCount,\n processedAt: new Date().toISOString(),\n results,\n };\n },\n\n /**\n * Check message delivery status\n */\n async getStatus(messageId: string) {\n try {\n const status = (await (provider as any).getStatus(messageId)) as any;\n\n return {\n messageId,\n status: status,\n checkedAt: new Date().toISOString(),\n deliveredAt:\n status === \"DELIVERED\" ? new Date().toISOString() : undefined,\n failedAt: status === \"FAILED\" ? new Date().toISOString() : undefined,\n };\n } catch (error) {\n return {\n messageId,\n status: \"UNKNOWN\",\n error:\n error instanceof Error ? error.message : \"Failed to check status\",\n checkedAt: new Date().toISOString(),\n };\n }\n },\n\n /**\n * Get platform instance for advanced usage (when implemented)\n */\n // getPlatform: () => platform\n };\n}\n\n/**\n * Template manager for CLI/scripts\n *\n * @example\n * ```typescript\n * const templates = createKMsgTemplates({ ... });\n * await templates.create('user_welcome', 'Welcome #{name}! Your account #{accountId} is ready.');\n * ```\n */\nexport function createKMsgTemplates(config: {\n iwinvApiKey: string;\n iwinvBaseUrl?: string;\n}) {\n const provider = new IWINVProvider({\n apiKey: config.iwinvApiKey,\n baseUrl: config.iwinvBaseUrl || \"https://alimtalk.bizservice.iwinv.kr\",\n debug: false,\n });\n\n return {\n /**\n * Create template with user-defined structure\n */\n async create(templateCode: string, content: string, description: string) {\n try {\n // Parse variables from template content\n const variables = this.parseVariables(content);\n\n // TODO: Implement createTemplate when available\n const result = {\n templateId: templateCode,\n status: \"pending\",\n message: \"Template creation not implemented yet\",\n };\n\n return {\n id: result.templateId,\n code: result.templateId,\n content,\n description,\n variables,\n status: result.status,\n message: result.message,\n createdAt: new Date().toISOString(),\n };\n } catch (error) {\n return {\n id: null,\n code: templateCode,\n content,\n description,\n variables: this.parseVariables(content),\n status: \"FAILED\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n createdAt: new Date().toISOString(),\n };\n }\n },\n\n /**\n * List user templates\n */\n async list(filters?: { status?: string }) {\n try {\n // TODO: Implement templates.list when available\n return [];\n } catch (error) {\n console.warn(\"Failed to list templates:\", error);\n return [];\n }\n },\n\n /**\n * Validate template syntax and extract variables\n */\n async validate(content: string) {\n const variables = this.parseVariables(content);\n const errors: string[] = [];\n\n // Basic validation\n if (content.length === 0) {\n errors.push(\"Template content cannot be empty\");\n }\n\n if (content.length > 1000) {\n errors.push(\"Template content too long (max 1000 chars)\");\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n variables,\n };\n },\n\n /**\n * Parse variables from template content\n */\n parseVariables(content: string) {\n const matches = content.match(/#{([^}]+)}/g) || [];\n // Use a Set to efficiently get unique variable names\n const uniqueNames = new Set(matches.map((match) => match.slice(2, -1)));\n\n return Array.from(uniqueNames).map((name) => ({\n name,\n type: \"string\",\n required: true,\n }));\n },\n\n /**\n * Test template with sample variables\n */\n async test(templateCode: string, sampleVariables: Record<string, any>) {\n try {\n // TODO: Implement template validation when available\n const template = {\n code: templateCode,\n content: `Template ${templateCode}`,\n variables: [],\n };\n\n // Parse variables from template content\n const requiredVariables = this.parseVariables(template.content);\n const missingVariables = requiredVariables\n .filter((v) => v.required && !(v.name in sampleVariables))\n .map((v) => v.name);\n\n if (missingVariables.length > 0) {\n return {\n templateCode,\n renderedContent: null,\n isValid: false,\n errors: [\n `Missing required variables: ${missingVariables.join(\", \")}`,\n ],\n };\n }\n\n // Render template with sample variables\n let renderedContent = template.content;\n for (const [key, value] of Object.entries(sampleVariables)) {\n const regex = new RegExp(`#{${key}}`, \"g\");\n renderedContent = renderedContent.replace(regex, String(value));\n }\n\n // Check for unreplaced variables\n const unreplacedVars = renderedContent.match(/#{([^}]+)}/g);\n const warnings = unreplacedVars\n ? [`Unreplaced variables found: ${unreplacedVars.join(\", \")}`]\n : [];\n\n return {\n templateCode,\n renderedContent,\n isValid: !unreplacedVars,\n warnings,\n };\n } catch (error) {\n return {\n templateCode,\n renderedContent: null,\n isValid: false,\n error:\n error instanceof Error ? error.message : \"Template test failed\",\n };\n }\n },\n };\n}\n\n/**\n * Analytics reader for scripts/reporting\n */\nexport function createKMsgAnalytics(config: {\n iwinvApiKey: string;\n iwinvBaseUrl?: string;\n}) {\n const provider = new IWINVProvider({\n apiKey: config.iwinvApiKey,\n baseUrl: config.iwinvBaseUrl || \"https://alimtalk.bizservice.iwinv.kr\",\n debug: false,\n });\n\n return {\n /**\n * Get message statistics for specified period\n */\n async getMessageStats(period: \"day\" | \"week\" | \"month\" = \"day\") {\n try {\n const now = new Date();\n const periodStart = new Date();\n\n switch (period) {\n case \"day\":\n periodStart.setDate(now.getDate() - 1);\n break;\n case \"week\":\n periodStart.setDate(now.getDate() - 7);\n break;\n case \"month\":\n periodStart.setMonth(now.getMonth() - 1);\n break;\n }\n\n // TODO: Implement analytics when available\n const usage = {\n sentMessages: 0,\n failedMessages: 0,\n totalMessages: 0,\n deliveredMessages: 0,\n deliveryRate: 0,\n breakdown: { byTemplate: {}, byDay: {}, byHour: {} },\n };\n\n return {\n period,\n totalSent: usage.sentMessages,\n totalDelivered: usage.deliveredMessages,\n totalFailed: usage.failedMessages,\n deliveryRate: usage.deliveryRate,\n periodStart: periodStart.toISOString(),\n periodEnd: now.toISOString(),\n breakdown: usage.breakdown,\n };\n } catch (error) {\n console.warn(\"Failed to get message stats:\", error);\n return {\n period,\n totalSent: 0,\n totalDelivered: 0,\n totalFailed: 0,\n deliveryRate: 0,\n periodStart: new Date().toISOString(),\n periodEnd: new Date().toISOString(),\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n },\n\n /**\n * Get template usage analytics\n */\n async getTemplateUsage(templateCode?: string) {\n try {\n const now = new Date();\n const monthAgo = new Date();\n monthAgo.setMonth(now.getMonth() - 1);\n\n if (templateCode) {\n // TODO: Implement getTemplateStats when available\n const stats = {\n sent: 0,\n delivered: 0,\n failed: 0,\n deliveryRate: 0,\n averageDeliveryTime: 0,\n };\n\n return {\n templateCode,\n totalUsage: stats.sent,\n successCount: stats.delivered,\n failureCount: stats.failed,\n successRate: stats.deliveryRate,\n lastUsed: new Date().toISOString(),\n averageDeliveryTime: stats.averageDeliveryTime,\n };\n }\n\n // Get usage for all templates by getting overall stats\n // TODO: provider.analytics.getUsage\n const usage = {\n breakdown: { byTemplate: {} },\n deliveryRate: 0,\n };\n\n const templateUsage = Object.entries(usage.breakdown.byTemplate).map(\n ([code, count]) => {\n const countNum = Number(count) || 0;\n const successCount = Math.round(\n countNum * (usage.deliveryRate / 100),\n );\n const failureCount = countNum - successCount;\n\n return {\n templateCode: code,\n totalUsage: countNum,\n successCount,\n failureCount,\n successRate: usage.deliveryRate,\n };\n },\n );\n\n return templateUsage.sort((a, b) => b.totalUsage - a.totalUsage);\n } catch (error) {\n console.warn(\"Failed to get template usage:\", error);\n return templateCode\n ? {\n templateCode,\n totalUsage: 0,\n successCount: 0,\n failureCount: 0,\n successRate: 0,\n error: error instanceof Error ? error.message : \"Unknown error\",\n }\n : [];\n }\n },\n\n /**\n * Generate usage reports\n */\n async generateReport(\n type: \"daily\" | \"weekly\" | \"monthly\",\n format: \"json\" | \"csv\" = \"json\",\n ) {\n try {\n const now = new Date();\n const periodStart = new Date();\n\n switch (type) {\n case \"daily\":\n periodStart.setDate(now.getDate() - 1);\n break;\n case \"weekly\":\n periodStart.setDate(now.getDate() - 7);\n break;\n case \"monthly\":\n periodStart.setMonth(now.getMonth() - 1);\n break;\n }\n\n // TODO: provider.analytics.getUsage\n const usage = {\n sentMessages: 0,\n deliveredMessages: 0,\n failedMessages: 0,\n totalMessages: 0,\n deliveryRate: 0,\n failureRate: 0,\n breakdown: {\n byTemplate: {} as Record<string, number>,\n byDay: {} as Record<string, number>,\n byHour: {} as Record<string, number>,\n },\n };\n\n const templateUsage = await this.getTemplateUsage();\n const topTemplates = Array.isArray(templateUsage)\n ? templateUsage.slice(0, 3).map((t) => t.templateCode)\n : [];\n\n const data = {\n reportType: type,\n generatedAt: new Date().toISOString(),\n period: {\n from: periodStart.toISOString(),\n to: now.toISOString(),\n },\n summary: {\n totalMessages: usage.totalMessages,\n successRate: usage.deliveryRate,\n failureRate: usage.failureRate,\n topTemplates,\n },\n breakdown: {\n byTemplate: usage.breakdown.byTemplate,\n byDay: usage.breakdown.byDay,\n byHour: usage.breakdown.byHour,\n },\n };\n\n return format === \"csv\" ? this.toCsv(data) : data;\n } catch (error) {\n console.warn(\"Failed to generate report:\", error);\n const fallbackData = {\n reportType: type,\n generatedAt: new Date().toISOString(),\n error: error instanceof Error ? error.message : \"Unknown error\",\n summary: {\n totalMessages: 0,\n successRate: 0,\n failureRate: 0,\n topTemplates: [],\n },\n };\n return format === \"csv\" ? this.toCsv(fallbackData) : fallbackData;\n }\n },\n\n /**\n * Get delivery status breakdown\n */\n async getDeliveryStats(templateCode?: string) {\n try {\n const now = new Date();\n const weekAgo = new Date();\n weekAgo.setDate(now.getDate() - 7);\n\n // TODO: provider.analytics.getUsage\n const usage = {\n sentMessages: 0,\n deliveredMessages: 0,\n failedMessages: 0,\n totalMessages: 0,\n deliveryRate: 0,\n failureRate: 0,\n breakdown: {\n byTemplate: {} as Record<string, number>,\n },\n };\n\n const filtered = usage;\n if (templateCode) {\n const templateCount = usage.breakdown.byTemplate[templateCode] || 0;\n const templateDelivered = Math.round(\n templateCount * (usage.deliveryRate / 100),\n );\n const templateFailed = Math.round(\n templateCount * (usage.failureRate / 100),\n );\n const templatePending =\n templateCount - templateDelivered - templateFailed;\n\n return {\n templateCode,\n delivered: templateDelivered,\n pending: templatePending,\n failed: templateFailed,\n total: templateCount,\n breakdown: {\n DELIVERED:\n templateCount > 0\n ? (templateDelivered / templateCount) * 100\n : 0,\n PENDING:\n templateCount > 0 ? (templatePending / templateCount) * 100 : 0,\n FAILED:\n templateCount > 0 ? (templateFailed / templateCount) * 100 : 0,\n },\n };\n }\n\n const pending = Math.max(\n 0,\n usage.totalMessages - usage.deliveredMessages - usage.failedMessages,\n );\n\n return {\n templateCode: undefined,\n delivered: usage.deliveredMessages,\n pending,\n failed: usage.failedMessages,\n total: usage.totalMessages,\n breakdown: {\n DELIVERED: usage.deliveryRate,\n PENDING:\n usage.totalMessages > 0\n ? (pending / usage.totalMessages) * 100\n : 0,\n FAILED: usage.failureRate,\n },\n };\n } catch (error) {\n console.warn(\"Failed to get delivery stats:\", error);\n return {\n templateCode,\n delivered: 0,\n pending: 0,\n failed: 0,\n total: 0,\n breakdown: {\n DELIVERED: 0,\n PENDING: 0,\n FAILED: 0,\n },\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n },\n\n /**\n * Convert data to CSV format\n */\n toCsv(data: any): string {\n // Simple CSV conversion\n return JSON.stringify(data, null, 2)\n .replace(/[{}]/g, \"\")\n .replace(/\"/g, \"\")\n .replace(/:/g, \",\");\n },\n };\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "D9B07AD8895618F264756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Simple message sender for CLI/scripts
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { createKMsgSender } from 'k-msg/modules';
7
+ *
8
+ * const sender = createKMsgSender({
9
+ * iwinvApiKey: process.env.IWINV_API_KEY!
10
+ * });
11
+ *
12
+ * // User defines their own templates and variables
13
+ * await sender.sendMessage('01012345678', 'USER_OTP_TEMPLATE', {
14
+ * code: '123456',
15
+ * serviceName: 'MyApp',
16
+ * expireMinutes: 3
17
+ * });
18
+ * ```
19
+ */
20
+ export declare function createKMsgSender(config: {
21
+ iwinvApiKey: string;
22
+ iwinvBaseUrl?: string;
23
+ }): {
24
+ /**
25
+ * Send message with custom template and variables
26
+ * User defines their own template structure
27
+ */
28
+ sendMessage(phoneNumber: string, templateCode: string, variables: Record<string, any>): Promise<{
29
+ messageId: any;
30
+ status: "SENT";
31
+ templateCode: string;
32
+ phoneNumber: string;
33
+ variables: Record<string, any>;
34
+ error: null;
35
+ sentAt: string;
36
+ } | {
37
+ messageId: null;
38
+ status: "FAILED";
39
+ templateCode: string;
40
+ phoneNumber: string;
41
+ variables: Record<string, any>;
42
+ error: string;
43
+ sentAt: string;
44
+ }>;
45
+ /**
46
+ * Send bulk messages with user-defined template
47
+ */
48
+ sendBulk(recipients: Array<{
49
+ phoneNumber: string;
50
+ variables: Record<string, any>;
51
+ }>, templateCode: string, options?: {
52
+ batchSize?: number;
53
+ batchDelay?: number;
54
+ }): Promise<{
55
+ batchId: string;
56
+ templateCode: string;
57
+ totalCount: number;
58
+ successCount: number;
59
+ failureCount: number;
60
+ processedAt: string;
61
+ results: ({
62
+ messageId: any;
63
+ phoneNumber: string;
64
+ status: "SENT";
65
+ variables: Record<string, any>;
66
+ error: null;
67
+ } | {
68
+ messageId: null;
69
+ phoneNumber: string;
70
+ status: "FAILED";
71
+ variables: Record<string, any>;
72
+ error: any;
73
+ })[];
74
+ }>;
75
+ /**
76
+ * Check message delivery status
77
+ */
78
+ getStatus(messageId: string): Promise<{
79
+ messageId: string;
80
+ status: any;
81
+ checkedAt: string;
82
+ deliveredAt: string | undefined;
83
+ failedAt: string | undefined;
84
+ error?: undefined;
85
+ } | {
86
+ messageId: string;
87
+ status: string;
88
+ error: string;
89
+ checkedAt: string;
90
+ deliveredAt?: undefined;
91
+ failedAt?: undefined;
92
+ }>;
93
+ };
94
+ /**
95
+ * Template manager for CLI/scripts
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const templates = createKMsgTemplates({ ... });
100
+ * await templates.create('user_welcome', 'Welcome #{name}! Your account #{accountId} is ready.');
101
+ * ```
102
+ */
103
+ export declare function createKMsgTemplates(config: {
104
+ iwinvApiKey: string;
105
+ iwinvBaseUrl?: string;
106
+ }): {
107
+ /**
108
+ * Create template with user-defined structure
109
+ */
110
+ create(templateCode: string, content: string, description: string): Promise<{
111
+ id: string;
112
+ code: string;
113
+ content: string;
114
+ description: string;
115
+ variables: {
116
+ name: string;
117
+ type: string;
118
+ required: boolean;
119
+ }[];
120
+ status: string;
121
+ message: string;
122
+ createdAt: string;
123
+ error?: undefined;
124
+ } | {
125
+ id: null;
126
+ code: string;
127
+ content: string;
128
+ description: string;
129
+ variables: {
130
+ name: string;
131
+ type: string;
132
+ required: boolean;
133
+ }[];
134
+ status: string;
135
+ error: string;
136
+ createdAt: string;
137
+ message?: undefined;
138
+ }>;
139
+ /**
140
+ * List user templates
141
+ */
142
+ list(filters?: {
143
+ status?: string;
144
+ }): Promise<never[]>;
145
+ /**
146
+ * Validate template syntax and extract variables
147
+ */
148
+ validate(content: string): Promise<{
149
+ isValid: boolean;
150
+ errors: string[];
151
+ variables: {
152
+ name: string;
153
+ type: string;
154
+ required: boolean;
155
+ }[];
156
+ }>;
157
+ /**
158
+ * Parse variables from template content
159
+ */
160
+ parseVariables(content: string): {
161
+ name: string;
162
+ type: string;
163
+ required: boolean;
164
+ }[];
165
+ /**
166
+ * Test template with sample variables
167
+ */
168
+ test(templateCode: string, sampleVariables: Record<string, any>): Promise<{
169
+ templateCode: string;
170
+ renderedContent: null;
171
+ isValid: boolean;
172
+ errors: string[];
173
+ warnings?: undefined;
174
+ error?: undefined;
175
+ } | {
176
+ templateCode: string;
177
+ renderedContent: string;
178
+ isValid: boolean;
179
+ warnings: string[];
180
+ errors?: undefined;
181
+ error?: undefined;
182
+ } | {
183
+ templateCode: string;
184
+ renderedContent: null;
185
+ isValid: boolean;
186
+ error: string;
187
+ errors?: undefined;
188
+ warnings?: undefined;
189
+ }>;
190
+ };
191
+ /**
192
+ * Analytics reader for scripts/reporting
193
+ */
194
+ export declare function createKMsgAnalytics(config: {
195
+ iwinvApiKey: string;
196
+ iwinvBaseUrl?: string;
197
+ }): {
198
+ /**
199
+ * Get message statistics for specified period
200
+ */
201
+ getMessageStats(period?: "day" | "week" | "month"): Promise<{
202
+ period: "day" | "week" | "month";
203
+ totalSent: number;
204
+ totalDelivered: number;
205
+ totalFailed: number;
206
+ deliveryRate: number;
207
+ periodStart: string;
208
+ periodEnd: string;
209
+ breakdown: {
210
+ byTemplate: {};
211
+ byDay: {};
212
+ byHour: {};
213
+ };
214
+ error?: undefined;
215
+ } | {
216
+ period: "day" | "week" | "month";
217
+ totalSent: number;
218
+ totalDelivered: number;
219
+ totalFailed: number;
220
+ deliveryRate: number;
221
+ periodStart: string;
222
+ periodEnd: string;
223
+ error: string;
224
+ breakdown?: undefined;
225
+ }>;
226
+ /**
227
+ * Get template usage analytics
228
+ */
229
+ getTemplateUsage(templateCode?: string): Promise<{
230
+ templateCode: string;
231
+ totalUsage: number;
232
+ successCount: number;
233
+ failureCount: number;
234
+ successRate: number;
235
+ }[] | {
236
+ templateCode: string;
237
+ totalUsage: number;
238
+ successCount: number;
239
+ failureCount: number;
240
+ successRate: number;
241
+ lastUsed: string;
242
+ averageDeliveryTime: number;
243
+ error?: undefined;
244
+ } | {
245
+ templateCode: string;
246
+ totalUsage: number;
247
+ successCount: number;
248
+ failureCount: number;
249
+ successRate: number;
250
+ error: string;
251
+ lastUsed?: undefined;
252
+ averageDeliveryTime?: undefined;
253
+ }>;
254
+ /**
255
+ * Generate usage reports
256
+ */
257
+ generateReport(type: "daily" | "weekly" | "monthly", format?: "json" | "csv"): Promise<string | {
258
+ reportType: "daily" | "weekly" | "monthly";
259
+ generatedAt: string;
260
+ period: {
261
+ from: string;
262
+ to: string;
263
+ };
264
+ summary: {
265
+ totalMessages: number;
266
+ successRate: number;
267
+ failureRate: number;
268
+ topTemplates: string[];
269
+ };
270
+ breakdown: {
271
+ byTemplate: Record<string, number>;
272
+ byDay: Record<string, number>;
273
+ byHour: Record<string, number>;
274
+ };
275
+ } | {
276
+ reportType: "daily" | "weekly" | "monthly";
277
+ generatedAt: string;
278
+ error: string;
279
+ summary: {
280
+ totalMessages: number;
281
+ successRate: number;
282
+ failureRate: number;
283
+ topTemplates: never[];
284
+ };
285
+ }>;
286
+ /**
287
+ * Get delivery status breakdown
288
+ */
289
+ getDeliveryStats(templateCode?: string): Promise<{
290
+ templateCode: string;
291
+ delivered: number;
292
+ pending: number;
293
+ failed: number;
294
+ total: number;
295
+ breakdown: {
296
+ DELIVERED: number;
297
+ PENDING: number;
298
+ FAILED: number;
299
+ };
300
+ error?: undefined;
301
+ } | {
302
+ templateCode: undefined;
303
+ delivered: number;
304
+ pending: number;
305
+ failed: number;
306
+ total: number;
307
+ breakdown: {
308
+ DELIVERED: number;
309
+ PENDING: number;
310
+ FAILED: number;
311
+ };
312
+ error?: undefined;
313
+ } | {
314
+ templateCode: string | undefined;
315
+ delivered: number;
316
+ pending: number;
317
+ failed: number;
318
+ total: number;
319
+ breakdown: {
320
+ DELIVERED: number;
321
+ PENDING: number;
322
+ FAILED: number;
323
+ };
324
+ error: string;
325
+ }>;
326
+ /**
327
+ * Convert data to CSV format
328
+ */
329
+ toCsv(data: any): string;
330
+ };
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "k-msg",
3
+ "version": "0.1.2",
4
+ "packageManager": "bun@1.3.8",
5
+ "description": "K-Message: Korean Multi-Channel Messaging Platform - Unified Package",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.mjs",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "bun run build:esm && bun run build:cjs && bun run build:types",
19
+ "build:esm": "bun build ./src/index.ts --outdir ./dist --format esm --minify --sourcemap --entry-naming '[name].mjs' --external 'bun:test' --external '@k-msg/*'",
20
+ "build:cjs": "bun build ./src/index.ts --outdir ./dist --format cjs --minify --sourcemap --entry-naming '[name].js' --external 'bun:test' --external '@k-msg/*'",
21
+ "build:types": "tsc",
22
+ "dev": "bun --watch src/index.ts",
23
+ "test": "bun test",
24
+ "clean": "rm -rf dist",
25
+ "pack": "bun pm pack",
26
+ "publish": "bun publish --access public"
27
+ },
28
+ "dependencies": {
29
+ "@k-msg/core": "0.1.1",
30
+ "@k-msg/messaging": "0.1.1",
31
+ "@k-msg/template": "0.1.1",
32
+ "@k-msg/webhook": "0.1.1",
33
+ "@k-msg/analytics": "0.1.1",
34
+ "@k-msg/channel": "0.1.1",
35
+ "@k-msg/provider": "0.1.1",
36
+ "zod": "^4.0.14"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.7.2",
40
+ "@types/bun": "latest",
41
+ "@types/node": "^22.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "react": "^18.0.0 || ^19.0.0",
45
+ "typescript": "^5"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "react": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "files": [
53
+ "dist",
54
+ "README.md"
55
+ ],
56
+ "keywords": [
57
+ "korea",
58
+ "messaging",
59
+ "alimtalk",
60
+ "sms",
61
+ "lms",
62
+ "kakaotalk",
63
+ "friendtalk",
64
+ "multi-channel",
65
+ "notification",
66
+ "typescript"
67
+ ],
68
+ "repository": {
69
+ "type": "git",
70
+ "url": "git+https://github.com/k-otp/k-msg.git"
71
+ },
72
+ "homepage": "https://github.com/k-otp/k-msg",
73
+ "bugs": {
74
+ "url": "https://github.com/k-otp/k-msg/issues"
75
+ },
76
+ "license": "MIT",
77
+ "author": "imjlk"
78
+ }