@k-msg/channel 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,2466 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ActionType: () => ActionType,
24
+ BusinessVerifier: () => BusinessVerifier,
25
+ ChannelCRUD: () => ChannelCRUD,
26
+ ChannelCreateRequestSchema: () => ChannelCreateRequestSchema,
27
+ ChannelFiltersSchema: () => ChannelFiltersSchema,
28
+ ChannelService: () => ChannelService,
29
+ ChannelStatus: () => ChannelStatus,
30
+ ChannelType: () => ChannelType,
31
+ DocumentStatus: () => DocumentStatus,
32
+ DocumentType: () => DocumentType,
33
+ KakaoChannelManager: () => KakaoChannelManager,
34
+ KakaoSenderNumberManager: () => KakaoSenderNumberManager,
35
+ NumberVerifier: () => NumberVerifier,
36
+ PermissionManager: () => PermissionManager,
37
+ PermissionScope: () => PermissionScope,
38
+ ResourceType: () => ResourceType,
39
+ SenderNumberCategory: () => SenderNumberCategory,
40
+ SenderNumberCreateRequestSchema: () => SenderNumberCreateRequestSchema,
41
+ SenderNumberFiltersSchema: () => SenderNumberFiltersSchema,
42
+ SenderNumberStatus: () => SenderNumberStatus,
43
+ VerificationMethod: () => VerificationMethod,
44
+ VerificationStatus: () => VerificationStatus,
45
+ VerificationType: () => VerificationType
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // src/types/channel.types.ts
50
+ var import_zod = require("zod");
51
+ var ChannelType = /* @__PURE__ */ ((ChannelType2) => {
52
+ ChannelType2["KAKAO_ALIMTALK"] = "KAKAO_ALIMTALK";
53
+ ChannelType2["KAKAO_FRIENDTALK"] = "KAKAO_FRIENDTALK";
54
+ ChannelType2["SMS"] = "SMS";
55
+ ChannelType2["LMS"] = "LMS";
56
+ ChannelType2["MMS"] = "MMS";
57
+ return ChannelType2;
58
+ })(ChannelType || {});
59
+ var ChannelStatus = /* @__PURE__ */ ((ChannelStatus2) => {
60
+ ChannelStatus2["PENDING"] = "PENDING";
61
+ ChannelStatus2["VERIFYING"] = "VERIFYING";
62
+ ChannelStatus2["ACTIVE"] = "ACTIVE";
63
+ ChannelStatus2["SUSPENDED"] = "SUSPENDED";
64
+ ChannelStatus2["BLOCKED"] = "BLOCKED";
65
+ ChannelStatus2["DELETED"] = "DELETED";
66
+ return ChannelStatus2;
67
+ })(ChannelStatus || {});
68
+ var SenderNumberStatus = /* @__PURE__ */ ((SenderNumberStatus2) => {
69
+ SenderNumberStatus2["PENDING"] = "PENDING";
70
+ SenderNumberStatus2["VERIFYING"] = "VERIFYING";
71
+ SenderNumberStatus2["VERIFIED"] = "VERIFIED";
72
+ SenderNumberStatus2["REJECTED"] = "REJECTED";
73
+ SenderNumberStatus2["BLOCKED"] = "BLOCKED";
74
+ return SenderNumberStatus2;
75
+ })(SenderNumberStatus || {});
76
+ var SenderNumberCategory = /* @__PURE__ */ ((SenderNumberCategory3) => {
77
+ SenderNumberCategory3["BUSINESS"] = "BUSINESS";
78
+ SenderNumberCategory3["PERSONAL"] = "PERSONAL";
79
+ SenderNumberCategory3["GOVERNMENT"] = "GOVERNMENT";
80
+ SenderNumberCategory3["NON_PROFIT"] = "NON_PROFIT";
81
+ return SenderNumberCategory3;
82
+ })(SenderNumberCategory || {});
83
+ var VerificationStatus = /* @__PURE__ */ ((VerificationStatus2) => {
84
+ VerificationStatus2["NOT_REQUIRED"] = "NOT_REQUIRED";
85
+ VerificationStatus2["PENDING"] = "PENDING";
86
+ VerificationStatus2["UNDER_REVIEW"] = "UNDER_REVIEW";
87
+ VerificationStatus2["VERIFIED"] = "VERIFIED";
88
+ VerificationStatus2["REJECTED"] = "REJECTED";
89
+ return VerificationStatus2;
90
+ })(VerificationStatus || {});
91
+ var DocumentType = /* @__PURE__ */ ((DocumentType2) => {
92
+ DocumentType2["BUSINESS_REGISTRATION"] = "BUSINESS_REGISTRATION";
93
+ DocumentType2["BUSINESS_LICENSE"] = "BUSINESS_LICENSE";
94
+ DocumentType2["ID_CARD"] = "ID_CARD";
95
+ DocumentType2["AUTHORIZATION_LETTER"] = "AUTHORIZATION_LETTER";
96
+ DocumentType2["OTHER"] = "OTHER";
97
+ return DocumentType2;
98
+ })(DocumentType || {});
99
+ var DocumentStatus = /* @__PURE__ */ ((DocumentStatus2) => {
100
+ DocumentStatus2["UPLOADED"] = "UPLOADED";
101
+ DocumentStatus2["VERIFIED"] = "VERIFIED";
102
+ DocumentStatus2["REJECTED"] = "REJECTED";
103
+ return DocumentStatus2;
104
+ })(DocumentStatus || {});
105
+ var ChannelCreateRequestSchema = import_zod.z.object({
106
+ name: import_zod.z.string().min(1).max(100),
107
+ type: import_zod.z.nativeEnum(ChannelType),
108
+ provider: import_zod.z.string().min(1),
109
+ profileKey: import_zod.z.string().min(1),
110
+ businessInfo: import_zod.z.object({
111
+ name: import_zod.z.string().min(1),
112
+ registrationNumber: import_zod.z.string().min(1),
113
+ category: import_zod.z.string().min(1),
114
+ contactPerson: import_zod.z.string().min(1),
115
+ contactEmail: import_zod.z.string().email(),
116
+ contactPhone: import_zod.z.string().regex(/^[0-9-+\s()]+$/)
117
+ }).optional(),
118
+ kakaoInfo: import_zod.z.object({
119
+ plusFriendId: import_zod.z.string().min(1),
120
+ brandName: import_zod.z.string().min(1),
121
+ logoUrl: import_zod.z.string().url().optional(),
122
+ description: import_zod.z.string().max(500).optional()
123
+ }).optional()
124
+ });
125
+ var SenderNumberCreateRequestSchema = import_zod.z.object({
126
+ phoneNumber: import_zod.z.string().regex(/^[0-9]{10,11}$/),
127
+ category: import_zod.z.nativeEnum(SenderNumberCategory),
128
+ businessInfo: import_zod.z.object({
129
+ businessName: import_zod.z.string().min(1),
130
+ businessRegistrationNumber: import_zod.z.string().min(1),
131
+ contactPerson: import_zod.z.string().min(1),
132
+ contactEmail: import_zod.z.string().email()
133
+ }).optional()
134
+ });
135
+ var ChannelFiltersSchema = import_zod.z.object({
136
+ provider: import_zod.z.string().optional(),
137
+ type: import_zod.z.nativeEnum(ChannelType).optional(),
138
+ status: import_zod.z.nativeEnum(ChannelStatus).optional(),
139
+ verified: import_zod.z.boolean().optional(),
140
+ createdAfter: import_zod.z.date().optional(),
141
+ createdBefore: import_zod.z.date().optional()
142
+ });
143
+ var SenderNumberFiltersSchema = import_zod.z.object({
144
+ channelId: import_zod.z.string().optional(),
145
+ status: import_zod.z.nativeEnum(SenderNumberStatus).optional(),
146
+ category: import_zod.z.nativeEnum(SenderNumberCategory).optional(),
147
+ verified: import_zod.z.boolean().optional()
148
+ });
149
+
150
+ // src/kakao/channel.ts
151
+ var KakaoChannelManager = class {
152
+ constructor() {
153
+ this.channels = /* @__PURE__ */ new Map();
154
+ }
155
+ async createChannel(request) {
156
+ this.validateKakaoChannelRequest(request);
157
+ const channelId = this.generateChannelId();
158
+ const channel = {
159
+ id: channelId,
160
+ name: request.name,
161
+ provider: request.provider,
162
+ type: request.type,
163
+ status: "PENDING" /* PENDING */,
164
+ profileKey: request.profileKey,
165
+ senderNumbers: [],
166
+ metadata: {
167
+ businessInfo: request.businessInfo,
168
+ kakaoInfo: request.kakaoInfo,
169
+ limits: {
170
+ dailyMessageLimit: 1e4,
171
+ monthlyMessageLimit: 3e5,
172
+ rateLimit: 10
173
+ // 10 messages per second
174
+ },
175
+ features: {
176
+ supportsBulkSending: true,
177
+ supportsScheduling: true,
178
+ supportsButtons: true,
179
+ maxButtonCount: 5
180
+ }
181
+ },
182
+ verification: {
183
+ status: request.businessInfo ? "PENDING" /* PENDING */ : "NOT_REQUIRED" /* NOT_REQUIRED */,
184
+ documents: []
185
+ },
186
+ createdAt: /* @__PURE__ */ new Date(),
187
+ updatedAt: /* @__PURE__ */ new Date()
188
+ };
189
+ this.channels.set(channelId, channel);
190
+ if (request.businessInfo) {
191
+ await this.initiateBusinessVerification(channel);
192
+ }
193
+ return channel;
194
+ }
195
+ validateKakaoChannelRequest(request) {
196
+ if (request.type !== "KAKAO_ALIMTALK" /* KAKAO_ALIMTALK */ && request.type !== "KAKAO_FRIENDTALK" /* KAKAO_FRIENDTALK */) {
197
+ throw new Error("Invalid channel type for Kakao channel");
198
+ }
199
+ if (!request.kakaoInfo?.plusFriendId) {
200
+ throw new Error("Plus Friend ID is required for Kakao channels");
201
+ }
202
+ if (!request.kakaoInfo?.brandName) {
203
+ throw new Error("Brand name is required for Kakao channels");
204
+ }
205
+ if (!this.isValidPlusFriendId(request.kakaoInfo.plusFriendId)) {
206
+ throw new Error("Invalid Plus Friend ID format");
207
+ }
208
+ }
209
+ isValidPlusFriendId(plusFriendId) {
210
+ const regex = /^@[a-zA-Z0-9_-]{3,30}$/;
211
+ return regex.test(plusFriendId);
212
+ }
213
+ async initiateBusinessVerification(channel) {
214
+ channel.verification.status = "UNDER_REVIEW" /* UNDER_REVIEW */;
215
+ channel.status = "VERIFYING" /* VERIFYING */;
216
+ channel.updatedAt = /* @__PURE__ */ new Date();
217
+ setTimeout(() => {
218
+ this.completeVerification(channel.id, true);
219
+ }, 5e3);
220
+ }
221
+ async completeVerification(channelId, approved, rejectionReason) {
222
+ const channel = this.channels.get(channelId);
223
+ if (!channel) {
224
+ throw new Error("Channel not found");
225
+ }
226
+ if (approved) {
227
+ channel.verification.status = "VERIFIED" /* VERIFIED */;
228
+ channel.verification.verifiedAt = /* @__PURE__ */ new Date();
229
+ channel.status = "ACTIVE" /* ACTIVE */;
230
+ } else {
231
+ channel.verification.status = "REJECTED" /* REJECTED */;
232
+ channel.verification.rejectedAt = /* @__PURE__ */ new Date();
233
+ channel.verification.rejectionReason = rejectionReason || "Verification failed";
234
+ channel.status = "SUSPENDED" /* SUSPENDED */;
235
+ }
236
+ channel.updatedAt = /* @__PURE__ */ new Date();
237
+ }
238
+ async getChannel(channelId) {
239
+ return this.channels.get(channelId) || null;
240
+ }
241
+ async updateChannel(channelId, updates) {
242
+ const channel = this.channels.get(channelId);
243
+ if (!channel) {
244
+ throw new Error("Channel not found");
245
+ }
246
+ if (updates.metadata?.kakaoInfo?.plusFriendId && !this.isValidPlusFriendId(updates.metadata.kakaoInfo.plusFriendId)) {
247
+ throw new Error("Invalid Plus Friend ID format");
248
+ }
249
+ Object.assign(channel, updates, { updatedAt: /* @__PURE__ */ new Date() });
250
+ return channel;
251
+ }
252
+ async deleteChannel(channelId) {
253
+ const channel = this.channels.get(channelId);
254
+ if (!channel) {
255
+ return false;
256
+ }
257
+ channel.status = "DELETED" /* DELETED */;
258
+ channel.updatedAt = /* @__PURE__ */ new Date();
259
+ return true;
260
+ }
261
+ async listChannels(filters) {
262
+ let channels = Array.from(this.channels.values());
263
+ if (filters) {
264
+ if (filters.status) {
265
+ channels = channels.filter((c) => c.status === filters.status);
266
+ }
267
+ if (filters.type) {
268
+ channels = channels.filter((c) => c.type === filters.type);
269
+ }
270
+ if (filters.verified !== void 0) {
271
+ const verifiedStatus = filters.verified ? "VERIFIED" /* VERIFIED */ : "PENDING" /* PENDING */;
272
+ channels = channels.filter((c) => c.verification.status === verifiedStatus);
273
+ }
274
+ }
275
+ return channels.filter((c) => c.status !== "DELETED" /* DELETED */);
276
+ }
277
+ async suspendChannel(channelId, reason) {
278
+ const channel = this.channels.get(channelId);
279
+ if (!channel) {
280
+ throw new Error("Channel not found");
281
+ }
282
+ channel.status = "SUSPENDED" /* SUSPENDED */;
283
+ channel.updatedAt = /* @__PURE__ */ new Date();
284
+ console.log(`Channel ${channelId} suspended: ${reason}`);
285
+ }
286
+ async reactivateChannel(channelId) {
287
+ const channel = this.channels.get(channelId);
288
+ if (!channel) {
289
+ throw new Error("Channel not found");
290
+ }
291
+ if (channel.verification.status !== "VERIFIED" /* VERIFIED */) {
292
+ throw new Error("Channel must be verified before reactivation");
293
+ }
294
+ channel.status = "ACTIVE" /* ACTIVE */;
295
+ channel.updatedAt = /* @__PURE__ */ new Date();
296
+ }
297
+ async checkChannelHealth(channelId) {
298
+ const channel = this.channels.get(channelId);
299
+ if (!channel) {
300
+ throw new Error("Channel not found");
301
+ }
302
+ const issues = [];
303
+ const recommendations = [];
304
+ if (channel.status !== "ACTIVE" /* ACTIVE */) {
305
+ issues.push(`Channel status is ${channel.status}`);
306
+ }
307
+ if (channel.verification.status !== "VERIFIED" /* VERIFIED */ && channel.verification.status !== "NOT_REQUIRED" /* NOT_REQUIRED */) {
308
+ issues.push(`Channel verification is ${channel.verification.status}`);
309
+ }
310
+ if (channel.senderNumbers.length === 0) {
311
+ recommendations.push("Add at least one verified sender number");
312
+ }
313
+ if (!channel.metadata.businessInfo) {
314
+ recommendations.push("Complete business information for better deliverability");
315
+ }
316
+ return {
317
+ isHealthy: issues.length === 0,
318
+ issues,
319
+ recommendations
320
+ };
321
+ }
322
+ generateChannelId() {
323
+ return `kakao_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
324
+ }
325
+ };
326
+
327
+ // src/kakao/sender-number.ts
328
+ var KakaoSenderNumberManager = class {
329
+ constructor() {
330
+ this.senderNumbers = /* @__PURE__ */ new Map();
331
+ this.verificationCodes = /* @__PURE__ */ new Map();
332
+ }
333
+ async addSenderNumber(channelId, request) {
334
+ this.validatePhoneNumber(request.phoneNumber);
335
+ const existingNumber = this.findSenderNumberByPhone(request.phoneNumber);
336
+ if (existingNumber) {
337
+ throw new Error("Phone number is already registered");
338
+ }
339
+ const senderNumberId = this.generateSenderNumberId();
340
+ const senderNumber = {
341
+ id: senderNumberId,
342
+ phoneNumber: request.phoneNumber,
343
+ status: "PENDING" /* PENDING */,
344
+ category: request.category,
345
+ metadata: {
346
+ businessName: request.businessInfo?.businessName,
347
+ businessRegistrationNumber: request.businessInfo?.businessRegistrationNumber,
348
+ contactPerson: request.businessInfo?.contactPerson,
349
+ contactEmail: request.businessInfo?.contactEmail
350
+ },
351
+ createdAt: /* @__PURE__ */ new Date(),
352
+ updatedAt: /* @__PURE__ */ new Date()
353
+ };
354
+ this.senderNumbers.set(senderNumberId, senderNumber);
355
+ await this.initiateVerification(senderNumber);
356
+ return senderNumber;
357
+ }
358
+ validatePhoneNumber(phoneNumber) {
359
+ const regex = /^(010|011|016|017|018|019)[0-9]{7,8}$/;
360
+ if (!regex.test(phoneNumber)) {
361
+ throw new Error("Invalid Korean phone number format");
362
+ }
363
+ }
364
+ findSenderNumberByPhone(phoneNumber) {
365
+ return Array.from(this.senderNumbers.values()).find((sn) => sn.phoneNumber === phoneNumber);
366
+ }
367
+ async initiateVerification(senderNumber) {
368
+ const verificationCode = this.generateVerificationCode();
369
+ const expiresAt = new Date(Date.now() + 5 * 60 * 1e3);
370
+ this.verificationCodes.set(senderNumber.id, {
371
+ code: verificationCode,
372
+ expiresAt
373
+ });
374
+ senderNumber.status = "VERIFYING" /* VERIFYING */;
375
+ senderNumber.verificationCode = verificationCode;
376
+ senderNumber.updatedAt = /* @__PURE__ */ new Date();
377
+ console.log(`Verification code for ${senderNumber.phoneNumber}: ${verificationCode}`);
378
+ await this.sendVerificationSMS(senderNumber.phoneNumber, verificationCode);
379
+ }
380
+ async sendVerificationSMS(phoneNumber, code) {
381
+ console.log(`Sending SMS to ${phoneNumber}: Your verification code is ${code}`);
382
+ }
383
+ async verifySenderNumber(senderNumberId, code) {
384
+ const senderNumber = this.senderNumbers.get(senderNumberId);
385
+ if (!senderNumber) {
386
+ throw new Error("Sender number not found");
387
+ }
388
+ const verification = this.verificationCodes.get(senderNumberId);
389
+ if (!verification) {
390
+ throw new Error("No verification code found");
391
+ }
392
+ if (/* @__PURE__ */ new Date() > verification.expiresAt) {
393
+ throw new Error("Verification code has expired");
394
+ }
395
+ if (verification.code !== code) {
396
+ return false;
397
+ }
398
+ senderNumber.status = "VERIFIED" /* VERIFIED */;
399
+ senderNumber.verifiedAt = /* @__PURE__ */ new Date();
400
+ senderNumber.updatedAt = /* @__PURE__ */ new Date();
401
+ delete senderNumber.verificationCode;
402
+ this.verificationCodes.delete(senderNumberId);
403
+ return true;
404
+ }
405
+ async resendVerificationCode(senderNumberId) {
406
+ const senderNumber = this.senderNumbers.get(senderNumberId);
407
+ if (!senderNumber) {
408
+ throw new Error("Sender number not found");
409
+ }
410
+ if (senderNumber.status !== "VERIFYING" /* VERIFYING */) {
411
+ throw new Error("Sender number is not in verifying status");
412
+ }
413
+ const lastVerification = this.verificationCodes.get(senderNumberId);
414
+ if (lastVerification) {
415
+ const timeSinceLastCode = Date.now() - (lastVerification.expiresAt.getTime() - 5 * 60 * 1e3);
416
+ if (timeSinceLastCode < 60 * 1e3) {
417
+ throw new Error("Please wait before requesting a new verification code");
418
+ }
419
+ }
420
+ await this.initiateVerification(senderNumber);
421
+ }
422
+ async getSenderNumber(senderNumberId) {
423
+ return this.senderNumbers.get(senderNumberId) || null;
424
+ }
425
+ async listSenderNumbers(filters) {
426
+ let senderNumbers = Array.from(this.senderNumbers.values());
427
+ if (filters) {
428
+ if (filters.status) {
429
+ senderNumbers = senderNumbers.filter((sn) => sn.status === filters.status);
430
+ }
431
+ if (filters.category) {
432
+ senderNumbers = senderNumbers.filter((sn) => sn.category === filters.category);
433
+ }
434
+ if (filters.verified !== void 0) {
435
+ if (filters.verified) {
436
+ senderNumbers = senderNumbers.filter((sn) => sn.status === "VERIFIED" /* VERIFIED */);
437
+ } else {
438
+ senderNumbers = senderNumbers.filter((sn) => sn.status !== "VERIFIED" /* VERIFIED */);
439
+ }
440
+ }
441
+ }
442
+ return senderNumbers;
443
+ }
444
+ async updateSenderNumber(senderNumberId, updates) {
445
+ const senderNumber = this.senderNumbers.get(senderNumberId);
446
+ if (!senderNumber) {
447
+ throw new Error("Sender number not found");
448
+ }
449
+ const allowedUpdates = { ...updates };
450
+ delete allowedUpdates.id;
451
+ delete allowedUpdates.phoneNumber;
452
+ delete allowedUpdates.verifiedAt;
453
+ delete allowedUpdates.createdAt;
454
+ Object.assign(senderNumber, allowedUpdates, { updatedAt: /* @__PURE__ */ new Date() });
455
+ return senderNumber;
456
+ }
457
+ async deleteSenderNumber(senderNumberId) {
458
+ const senderNumber = this.senderNumbers.get(senderNumberId);
459
+ if (!senderNumber) {
460
+ return false;
461
+ }
462
+ if (await this.isSenderNumberInUse(senderNumberId)) {
463
+ throw new Error("Cannot delete sender number that is currently in use");
464
+ }
465
+ this.senderNumbers.delete(senderNumberId);
466
+ this.verificationCodes.delete(senderNumberId);
467
+ return true;
468
+ }
469
+ async isSenderNumberInUse(senderNumberId) {
470
+ return false;
471
+ }
472
+ async blockSenderNumber(senderNumberId, reason) {
473
+ const senderNumber = this.senderNumbers.get(senderNumberId);
474
+ if (!senderNumber) {
475
+ throw new Error("Sender number not found");
476
+ }
477
+ senderNumber.status = "BLOCKED" /* BLOCKED */;
478
+ senderNumber.updatedAt = /* @__PURE__ */ new Date();
479
+ console.log(`Sender number ${senderNumberId} blocked: ${reason}`);
480
+ }
481
+ async unblockSenderNumber(senderNumberId) {
482
+ const senderNumber = this.senderNumbers.get(senderNumberId);
483
+ if (!senderNumber) {
484
+ throw new Error("Sender number not found");
485
+ }
486
+ if (senderNumber.status !== "BLOCKED" /* BLOCKED */) {
487
+ throw new Error("Sender number is not blocked");
488
+ }
489
+ senderNumber.status = senderNumber.verifiedAt ? "VERIFIED" /* VERIFIED */ : "PENDING" /* PENDING */;
490
+ senderNumber.updatedAt = /* @__PURE__ */ new Date();
491
+ }
492
+ async validateSenderNumberForSending(senderNumberId) {
493
+ const senderNumber = this.senderNumbers.get(senderNumberId);
494
+ const errors = [];
495
+ if (!senderNumber) {
496
+ errors.push("Sender number not found");
497
+ return { isValid: false, errors };
498
+ }
499
+ if (senderNumber.status !== "VERIFIED" /* VERIFIED */) {
500
+ errors.push(`Sender number status is ${senderNumber.status}, must be verified`);
501
+ }
502
+ if (!senderNumber.verifiedAt) {
503
+ errors.push("Sender number has not been verified");
504
+ }
505
+ if (senderNumber.verifiedAt) {
506
+ const oneYearAgo = new Date(Date.now() - 365 * 24 * 60 * 60 * 1e3);
507
+ if (senderNumber.verifiedAt < oneYearAgo) {
508
+ errors.push("Sender number verification has expired");
509
+ }
510
+ }
511
+ return {
512
+ isValid: errors.length === 0,
513
+ errors
514
+ };
515
+ }
516
+ generateSenderNumberId() {
517
+ return `sn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
518
+ }
519
+ generateVerificationCode() {
520
+ return Math.floor(1e5 + Math.random() * 9e5).toString();
521
+ }
522
+ // Cleanup expired verification codes
523
+ cleanup() {
524
+ const now = /* @__PURE__ */ new Date();
525
+ for (const [id, verification] of this.verificationCodes) {
526
+ if (now > verification.expiresAt) {
527
+ this.verificationCodes.delete(id);
528
+ const senderNumber = this.senderNumbers.get(id);
529
+ if (senderNumber && senderNumber.status === "VERIFYING" /* VERIFYING */) {
530
+ senderNumber.status = "PENDING" /* PENDING */;
531
+ delete senderNumber.verificationCode;
532
+ senderNumber.updatedAt = /* @__PURE__ */ new Date();
533
+ }
534
+ }
535
+ }
536
+ }
537
+ };
538
+
539
+ // src/management/crud.ts
540
+ var import_events = require("events");
541
+ var ChannelCRUD = class extends import_events.EventEmitter {
542
+ constructor(options = {}) {
543
+ super();
544
+ this.options = options;
545
+ this.channels = /* @__PURE__ */ new Map();
546
+ this.senderNumbers = /* @__PURE__ */ new Map();
547
+ this.auditLogs = [];
548
+ this.defaultOptions = {
549
+ enableAuditLog: true,
550
+ enableEventEmission: true,
551
+ defaultPageSize: 20,
552
+ maxPageSize: 100,
553
+ enableSoftDelete: true,
554
+ autoCleanup: true,
555
+ cleanupInterval: 36e5
556
+ // 1 hour
557
+ };
558
+ this.options = { ...this.defaultOptions, ...options };
559
+ if (this.options.autoCleanup) {
560
+ this.startAutoCleanup();
561
+ }
562
+ }
563
+ // Channel CRUD Operations
564
+ async createChannel(request, userId) {
565
+ const channelId = this.generateChannelId();
566
+ const channel = {
567
+ id: channelId,
568
+ name: request.name,
569
+ provider: request.provider,
570
+ type: request.type,
571
+ status: "PENDING" /* PENDING */,
572
+ profileKey: request.profileKey,
573
+ senderNumbers: [],
574
+ metadata: {
575
+ businessInfo: request.businessInfo,
576
+ kakaoInfo: request.kakaoInfo,
577
+ limits: this.getDefaultLimits(request.type),
578
+ features: this.getDefaultFeatures(request.type)
579
+ },
580
+ verification: {
581
+ status: request.businessInfo ? "PENDING" /* PENDING */ : "NOT_REQUIRED" /* NOT_REQUIRED */,
582
+ documents: []
583
+ },
584
+ createdAt: /* @__PURE__ */ new Date(),
585
+ updatedAt: /* @__PURE__ */ new Date()
586
+ };
587
+ this.channels.set(channelId, channel);
588
+ if (this.options.enableAuditLog) {
589
+ this.addAuditLog("channel", channelId, "create", userId, void 0, channel);
590
+ }
591
+ if (this.options.enableEventEmission) {
592
+ this.emit("channel:created", { channel, userId });
593
+ }
594
+ return channel;
595
+ }
596
+ async getChannel(channelId, userId) {
597
+ const channel = this.channels.get(channelId);
598
+ if (channel && this.options.enableAuditLog) {
599
+ this.addAuditLog("channel", channelId, "read", userId);
600
+ }
601
+ return channel || null;
602
+ }
603
+ async updateChannel(channelId, updates, userId) {
604
+ const channel = this.channels.get(channelId);
605
+ if (!channel) {
606
+ throw new Error(`Channel ${channelId} not found`);
607
+ }
608
+ const before = this.options.enableAuditLog ? { ...channel } : void 0;
609
+ const updatedChannel = {
610
+ ...channel,
611
+ ...updates,
612
+ id: channelId,
613
+ // Ensure ID doesn't change
614
+ updatedAt: /* @__PURE__ */ new Date()
615
+ };
616
+ this.channels.set(channelId, updatedChannel);
617
+ if (this.options.enableAuditLog) {
618
+ this.addAuditLog("channel", channelId, "update", userId, before, updatedChannel);
619
+ }
620
+ if (this.options.enableEventEmission) {
621
+ this.emit("channel:updated", {
622
+ channel: updatedChannel,
623
+ previousChannel: channel,
624
+ userId
625
+ });
626
+ }
627
+ return updatedChannel;
628
+ }
629
+ async deleteChannel(channelId, userId) {
630
+ const channel = this.channels.get(channelId);
631
+ if (!channel) {
632
+ return false;
633
+ }
634
+ if (this.options.enableSoftDelete) {
635
+ channel.status = "DELETED" /* DELETED */;
636
+ channel.updatedAt = /* @__PURE__ */ new Date();
637
+ } else {
638
+ this.channels.delete(channelId);
639
+ for (const [id, senderNumber] of this.senderNumbers) {
640
+ }
641
+ }
642
+ if (this.options.enableAuditLog) {
643
+ this.addAuditLog("channel", channelId, "delete", userId, channel);
644
+ }
645
+ if (this.options.enableEventEmission) {
646
+ this.emit("channel:deleted", { channel, userId });
647
+ }
648
+ return true;
649
+ }
650
+ async listChannels(filters = {}, pagination = { page: 1, limit: this.options.defaultPageSize }) {
651
+ let channels = Array.from(this.channels.values());
652
+ if (filters.provider) {
653
+ channels = channels.filter((c) => c.provider === filters.provider);
654
+ }
655
+ if (filters.type) {
656
+ channels = channels.filter((c) => c.type === filters.type);
657
+ }
658
+ if (filters.status) {
659
+ channels = channels.filter((c) => c.status === filters.status);
660
+ }
661
+ if (filters.verified !== void 0) {
662
+ const targetStatus = filters.verified ? "VERIFIED" /* VERIFIED */ : "PENDING" /* PENDING */;
663
+ channels = channels.filter((c) => c.verification.status === targetStatus);
664
+ }
665
+ if (filters.createdAfter) {
666
+ channels = channels.filter((c) => c.createdAt >= filters.createdAfter);
667
+ }
668
+ if (filters.createdBefore) {
669
+ channels = channels.filter((c) => c.createdAt <= filters.createdBefore);
670
+ }
671
+ if (!filters.status || filters.status !== "DELETED" /* DELETED */) {
672
+ channels = channels.filter((c) => c.status !== "DELETED" /* DELETED */);
673
+ }
674
+ const sortBy = pagination.sortBy || "createdAt";
675
+ const sortOrder = pagination.sortOrder || "desc";
676
+ channels.sort((a, b) => {
677
+ let aValue, bValue;
678
+ switch (sortBy) {
679
+ case "name":
680
+ aValue = a.name;
681
+ bValue = b.name;
682
+ break;
683
+ case "createdAt":
684
+ aValue = a.createdAt.getTime();
685
+ bValue = b.createdAt.getTime();
686
+ break;
687
+ case "updatedAt":
688
+ aValue = a.updatedAt.getTime();
689
+ bValue = b.updatedAt.getTime();
690
+ break;
691
+ default:
692
+ aValue = a.createdAt.getTime();
693
+ bValue = b.createdAt.getTime();
694
+ }
695
+ if (sortOrder === "asc") {
696
+ return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
697
+ } else {
698
+ return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
699
+ }
700
+ });
701
+ const total = channels.length;
702
+ const limit = Math.min(pagination.limit, this.options.maxPageSize);
703
+ const page = Math.max(1, pagination.page);
704
+ const offset = (page - 1) * limit;
705
+ const paginatedChannels = channels.slice(offset, offset + limit);
706
+ return {
707
+ data: paginatedChannels,
708
+ total,
709
+ page,
710
+ limit,
711
+ totalPages: Math.ceil(total / limit),
712
+ hasNext: offset + limit < total,
713
+ hasPrev: page > 1
714
+ };
715
+ }
716
+ // Sender Number CRUD Operations
717
+ async createSenderNumber(channelId, request, userId) {
718
+ const channel = this.channels.get(channelId);
719
+ if (!channel) {
720
+ throw new Error(`Channel ${channelId} not found`);
721
+ }
722
+ const senderNumberId = this.generateSenderNumberId();
723
+ const senderNumber = {
724
+ id: senderNumberId,
725
+ phoneNumber: request.phoneNumber,
726
+ status: "PENDING" /* PENDING */,
727
+ category: request.category,
728
+ metadata: {
729
+ businessName: request.businessInfo?.businessName,
730
+ businessRegistrationNumber: request.businessInfo?.businessRegistrationNumber,
731
+ contactPerson: request.businessInfo?.contactPerson,
732
+ contactEmail: request.businessInfo?.contactEmail
733
+ },
734
+ createdAt: /* @__PURE__ */ new Date(),
735
+ updatedAt: /* @__PURE__ */ new Date()
736
+ };
737
+ this.senderNumbers.set(senderNumberId, senderNumber);
738
+ channel.senderNumbers.push(senderNumber);
739
+ channel.updatedAt = /* @__PURE__ */ new Date();
740
+ if (this.options.enableAuditLog) {
741
+ this.addAuditLog("senderNumber", senderNumberId, "create", userId, void 0, senderNumber);
742
+ }
743
+ if (this.options.enableEventEmission) {
744
+ this.emit("senderNumber:created", { senderNumber, channelId, userId });
745
+ }
746
+ return senderNumber;
747
+ }
748
+ async getSenderNumber(senderNumberId, userId) {
749
+ const senderNumber = this.senderNumbers.get(senderNumberId);
750
+ if (senderNumber && this.options.enableAuditLog) {
751
+ this.addAuditLog("senderNumber", senderNumberId, "read", userId);
752
+ }
753
+ return senderNumber || null;
754
+ }
755
+ async updateSenderNumber(senderNumberId, updates, userId) {
756
+ const senderNumber = this.senderNumbers.get(senderNumberId);
757
+ if (!senderNumber) {
758
+ throw new Error(`Sender number ${senderNumberId} not found`);
759
+ }
760
+ const before = this.options.enableAuditLog ? { ...senderNumber } : void 0;
761
+ const updatedSenderNumber = {
762
+ ...senderNumber,
763
+ ...updates,
764
+ id: senderNumberId,
765
+ // Ensure ID doesn't change
766
+ updatedAt: /* @__PURE__ */ new Date()
767
+ };
768
+ this.senderNumbers.set(senderNumberId, updatedSenderNumber);
769
+ for (const channel of this.channels.values()) {
770
+ const index = channel.senderNumbers.findIndex((sn) => sn.id === senderNumberId);
771
+ if (index !== -1) {
772
+ channel.senderNumbers[index] = updatedSenderNumber;
773
+ channel.updatedAt = /* @__PURE__ */ new Date();
774
+ break;
775
+ }
776
+ }
777
+ if (this.options.enableAuditLog) {
778
+ this.addAuditLog("senderNumber", senderNumberId, "update", userId, before, updatedSenderNumber);
779
+ }
780
+ if (this.options.enableEventEmission) {
781
+ this.emit("senderNumber:updated", {
782
+ senderNumber: updatedSenderNumber,
783
+ previousSenderNumber: senderNumber,
784
+ userId
785
+ });
786
+ }
787
+ return updatedSenderNumber;
788
+ }
789
+ async deleteSenderNumber(senderNumberId, userId) {
790
+ const senderNumber = this.senderNumbers.get(senderNumberId);
791
+ if (!senderNumber) {
792
+ return false;
793
+ }
794
+ this.senderNumbers.delete(senderNumberId);
795
+ for (const channel of this.channels.values()) {
796
+ const index = channel.senderNumbers.findIndex((sn) => sn.id === senderNumberId);
797
+ if (index !== -1) {
798
+ channel.senderNumbers.splice(index, 1);
799
+ channel.updatedAt = /* @__PURE__ */ new Date();
800
+ break;
801
+ }
802
+ }
803
+ if (this.options.enableAuditLog) {
804
+ this.addAuditLog("senderNumber", senderNumberId, "delete", userId, senderNumber);
805
+ }
806
+ if (this.options.enableEventEmission) {
807
+ this.emit("senderNumber:deleted", { senderNumber, userId });
808
+ }
809
+ return true;
810
+ }
811
+ async listSenderNumbers(filters = {}, pagination = { page: 1, limit: this.options.defaultPageSize }) {
812
+ let senderNumbers = Array.from(this.senderNumbers.values());
813
+ if (filters.channelId) {
814
+ const channel = this.channels.get(filters.channelId);
815
+ if (channel) {
816
+ senderNumbers = channel.senderNumbers;
817
+ } else {
818
+ senderNumbers = [];
819
+ }
820
+ }
821
+ if (filters.status) {
822
+ senderNumbers = senderNumbers.filter((sn) => sn.status === filters.status);
823
+ }
824
+ if (filters.category) {
825
+ senderNumbers = senderNumbers.filter((sn) => sn.category === filters.category);
826
+ }
827
+ if (filters.verified !== void 0) {
828
+ if (filters.verified) {
829
+ senderNumbers = senderNumbers.filter((sn) => sn.status === "VERIFIED" /* VERIFIED */);
830
+ } else {
831
+ senderNumbers = senderNumbers.filter((sn) => sn.status !== "VERIFIED" /* VERIFIED */);
832
+ }
833
+ }
834
+ const sortBy = pagination.sortBy || "createdAt";
835
+ const sortOrder = pagination.sortOrder || "desc";
836
+ senderNumbers.sort((a, b) => {
837
+ let aValue, bValue;
838
+ switch (sortBy) {
839
+ case "phoneNumber":
840
+ aValue = a.phoneNumber;
841
+ bValue = b.phoneNumber;
842
+ break;
843
+ case "createdAt":
844
+ aValue = a.createdAt.getTime();
845
+ bValue = b.createdAt.getTime();
846
+ break;
847
+ case "updatedAt":
848
+ aValue = a.updatedAt.getTime();
849
+ bValue = b.updatedAt.getTime();
850
+ break;
851
+ default:
852
+ aValue = a.createdAt.getTime();
853
+ bValue = b.createdAt.getTime();
854
+ }
855
+ if (sortOrder === "asc") {
856
+ return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
857
+ } else {
858
+ return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
859
+ }
860
+ });
861
+ const total = senderNumbers.length;
862
+ const limit = Math.min(pagination.limit, this.options.maxPageSize);
863
+ const page = Math.max(1, pagination.page);
864
+ const offset = (page - 1) * limit;
865
+ const paginatedSenderNumbers = senderNumbers.slice(offset, offset + limit);
866
+ return {
867
+ data: paginatedSenderNumbers,
868
+ total,
869
+ page,
870
+ limit,
871
+ totalPages: Math.ceil(total / limit),
872
+ hasNext: offset + limit < total,
873
+ hasPrev: page > 1
874
+ };
875
+ }
876
+ // Audit and Analytics
877
+ getAuditLogs(entityType, entityId, limit = 100) {
878
+ let logs = [...this.auditLogs];
879
+ if (entityType) {
880
+ logs = logs.filter((log) => log.entityType === entityType);
881
+ }
882
+ if (entityId) {
883
+ logs = logs.filter((log) => log.entityId === entityId);
884
+ }
885
+ return logs.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()).slice(0, limit);
886
+ }
887
+ getStatistics() {
888
+ const channels = Array.from(this.channels.values());
889
+ const senderNumbers = Array.from(this.senderNumbers.values());
890
+ const channelsByStatus = {};
891
+ const channelsByType = {};
892
+ const channelsByProvider = {};
893
+ channels.forEach((channel) => {
894
+ channelsByStatus[channel.status] = (channelsByStatus[channel.status] || 0) + 1;
895
+ channelsByType[channel.type] = (channelsByType[channel.type] || 0) + 1;
896
+ channelsByProvider[channel.provider] = (channelsByProvider[channel.provider] || 0) + 1;
897
+ });
898
+ const senderNumbersByStatus = {};
899
+ const senderNumbersByCategory = {};
900
+ senderNumbers.forEach((senderNumber) => {
901
+ senderNumbersByStatus[senderNumber.status] = (senderNumbersByStatus[senderNumber.status] || 0) + 1;
902
+ senderNumbersByCategory[senderNumber.category] = (senderNumbersByCategory[senderNumber.category] || 0) + 1;
903
+ });
904
+ return {
905
+ channels: {
906
+ total: channels.length,
907
+ byStatus: channelsByStatus,
908
+ byType: channelsByType,
909
+ byProvider: channelsByProvider
910
+ },
911
+ senderNumbers: {
912
+ total: senderNumbers.length,
913
+ byStatus: senderNumbersByStatus,
914
+ byCategory: senderNumbersByCategory
915
+ }
916
+ };
917
+ }
918
+ // Cleanup and Maintenance
919
+ cleanup() {
920
+ let deletedChannels = 0;
921
+ let expiredAuditLogs = 0;
922
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
923
+ for (const [id, channel] of this.channels) {
924
+ if (channel.status === "DELETED" /* DELETED */ && channel.updatedAt < thirtyDaysAgo) {
925
+ this.channels.delete(id);
926
+ deletedChannels++;
927
+ }
928
+ }
929
+ const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1e3);
930
+ const originalLogCount = this.auditLogs.length;
931
+ this.auditLogs = this.auditLogs.filter((log) => log.timestamp >= ninetyDaysAgo);
932
+ expiredAuditLogs = originalLogCount - this.auditLogs.length;
933
+ return { deletedChannels, expiredAuditLogs };
934
+ }
935
+ destroy() {
936
+ if (this.cleanupTimer) {
937
+ clearInterval(this.cleanupTimer);
938
+ this.cleanupTimer = void 0;
939
+ }
940
+ this.removeAllListeners();
941
+ this.channels.clear();
942
+ this.senderNumbers.clear();
943
+ this.auditLogs = [];
944
+ }
945
+ addAuditLog(entityType, entityId, action, userId, before, after) {
946
+ const auditLog = {
947
+ id: this.generateAuditLogId(),
948
+ entityType,
949
+ entityId,
950
+ action,
951
+ userId,
952
+ timestamp: /* @__PURE__ */ new Date(),
953
+ changes: before || after ? { before, after } : void 0
954
+ };
955
+ this.auditLogs.push(auditLog);
956
+ if (this.auditLogs.length > 1e4) {
957
+ this.auditLogs = this.auditLogs.slice(-1e4);
958
+ }
959
+ }
960
+ getDefaultLimits(channelType) {
961
+ switch (channelType) {
962
+ case "KAKAO_ALIMTALK" /* KAKAO_ALIMTALK */:
963
+ return {
964
+ dailyMessageLimit: 1e4,
965
+ monthlyMessageLimit: 3e5,
966
+ rateLimit: 10
967
+ };
968
+ case "KAKAO_FRIENDTALK" /* KAKAO_FRIENDTALK */:
969
+ return {
970
+ dailyMessageLimit: 1e3,
971
+ monthlyMessageLimit: 3e4,
972
+ rateLimit: 5
973
+ };
974
+ case "SMS" /* SMS */:
975
+ case "LMS" /* LMS */:
976
+ case "MMS" /* MMS */:
977
+ return {
978
+ dailyMessageLimit: 1e3,
979
+ monthlyMessageLimit: 3e4,
980
+ rateLimit: 3
981
+ };
982
+ default:
983
+ return {
984
+ dailyMessageLimit: 1e3,
985
+ monthlyMessageLimit: 3e4,
986
+ rateLimit: 1
987
+ };
988
+ }
989
+ }
990
+ getDefaultFeatures(channelType) {
991
+ switch (channelType) {
992
+ case "KAKAO_ALIMTALK" /* KAKAO_ALIMTALK */:
993
+ return {
994
+ supportsBulkSending: true,
995
+ supportsScheduling: true,
996
+ supportsButtons: true,
997
+ maxButtonCount: 5
998
+ };
999
+ case "KAKAO_FRIENDTALK" /* KAKAO_FRIENDTALK */:
1000
+ return {
1001
+ supportsBulkSending: true,
1002
+ supportsScheduling: true,
1003
+ supportsButtons: false,
1004
+ maxButtonCount: 0
1005
+ };
1006
+ default:
1007
+ return {
1008
+ supportsBulkSending: false,
1009
+ supportsScheduling: false,
1010
+ supportsButtons: false,
1011
+ maxButtonCount: 0
1012
+ };
1013
+ }
1014
+ }
1015
+ startAutoCleanup() {
1016
+ this.cleanupTimer = setInterval(() => {
1017
+ this.cleanup();
1018
+ }, this.options.cleanupInterval);
1019
+ }
1020
+ generateChannelId() {
1021
+ return `ch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1022
+ }
1023
+ generateSenderNumberId() {
1024
+ return `sn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1025
+ }
1026
+ generateAuditLogId() {
1027
+ return `audit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1028
+ }
1029
+ };
1030
+
1031
+ // src/management/permissions.ts
1032
+ var import_events2 = require("events");
1033
+ var ResourceType = /* @__PURE__ */ ((ResourceType2) => {
1034
+ ResourceType2["CHANNEL"] = "channel";
1035
+ ResourceType2["SENDER_NUMBER"] = "senderNumber";
1036
+ ResourceType2["TEMPLATE"] = "template";
1037
+ ResourceType2["MESSAGE"] = "message";
1038
+ ResourceType2["USER"] = "user";
1039
+ ResourceType2["ROLE"] = "role";
1040
+ ResourceType2["AUDIT_LOG"] = "auditLog";
1041
+ ResourceType2["ANALYTICS"] = "analytics";
1042
+ return ResourceType2;
1043
+ })(ResourceType || {});
1044
+ var ActionType = /* @__PURE__ */ ((ActionType2) => {
1045
+ ActionType2["CREATE"] = "create";
1046
+ ActionType2["READ"] = "read";
1047
+ ActionType2["UPDATE"] = "update";
1048
+ ActionType2["DELETE"] = "delete";
1049
+ ActionType2["VERIFY"] = "verify";
1050
+ ActionType2["SUSPEND"] = "suspend";
1051
+ ActionType2["ACTIVATE"] = "activate";
1052
+ ActionType2["SEND"] = "send";
1053
+ ActionType2["MANAGE"] = "manage";
1054
+ return ActionType2;
1055
+ })(ActionType || {});
1056
+ var PermissionScope = /* @__PURE__ */ ((PermissionScope2) => {
1057
+ PermissionScope2["GLOBAL"] = "global";
1058
+ PermissionScope2["ORGANIZATION"] = "organization";
1059
+ PermissionScope2["TEAM"] = "team";
1060
+ PermissionScope2["PERSONAL"] = "personal";
1061
+ return PermissionScope2;
1062
+ })(PermissionScope || {});
1063
+ var PermissionManager = class extends import_events2.EventEmitter {
1064
+ // 5 minutes
1065
+ constructor() {
1066
+ super();
1067
+ this.users = /* @__PURE__ */ new Map();
1068
+ this.roles = /* @__PURE__ */ new Map();
1069
+ this.userRoleCache = /* @__PURE__ */ new Map();
1070
+ this.permissionCache = /* @__PURE__ */ new Map();
1071
+ this.cacheExpiry = /* @__PURE__ */ new Map();
1072
+ this.CACHE_DURATION = 5 * 60 * 1e3;
1073
+ this.initializeSystemRoles();
1074
+ }
1075
+ // User Management
1076
+ async createUser(userData) {
1077
+ const userId = this.generateUserId();
1078
+ const user = {
1079
+ ...userData,
1080
+ id: userId,
1081
+ createdAt: /* @__PURE__ */ new Date(),
1082
+ updatedAt: /* @__PURE__ */ new Date()
1083
+ };
1084
+ this.users.set(userId, user);
1085
+ this.updateUserRoleCache(userId, user.roles.map((r) => r.id));
1086
+ this.emit("user:created", { user });
1087
+ return user;
1088
+ }
1089
+ async getUser(userId) {
1090
+ return this.users.get(userId) || null;
1091
+ }
1092
+ async updateUser(userId, updates) {
1093
+ const user = this.users.get(userId);
1094
+ if (!user) {
1095
+ throw new Error("User not found");
1096
+ }
1097
+ const updatedUser = {
1098
+ ...user,
1099
+ ...updates,
1100
+ id: userId,
1101
+ updatedAt: /* @__PURE__ */ new Date()
1102
+ };
1103
+ this.users.set(userId, updatedUser);
1104
+ if (updates.roles) {
1105
+ this.updateUserRoleCache(userId, updates.roles.map((r) => r.id));
1106
+ }
1107
+ this.clearUserPermissionCache(userId);
1108
+ this.emit("user:updated", { user: updatedUser, previousUser: user });
1109
+ return updatedUser;
1110
+ }
1111
+ async deleteUser(userId) {
1112
+ const user = this.users.get(userId);
1113
+ if (!user) {
1114
+ return false;
1115
+ }
1116
+ this.users.delete(userId);
1117
+ this.userRoleCache.delete(userId);
1118
+ this.clearUserPermissionCache(userId);
1119
+ this.emit("user:deleted", { user });
1120
+ return true;
1121
+ }
1122
+ // Role Management
1123
+ async createRole(roleData) {
1124
+ const roleId = this.generateRoleId();
1125
+ const role = {
1126
+ ...roleData,
1127
+ id: roleId,
1128
+ createdAt: /* @__PURE__ */ new Date(),
1129
+ updatedAt: /* @__PURE__ */ new Date()
1130
+ };
1131
+ this.roles.set(roleId, role);
1132
+ this.emit("role:created", { role });
1133
+ return role;
1134
+ }
1135
+ async getRole(roleId) {
1136
+ return this.roles.get(roleId) || null;
1137
+ }
1138
+ async updateRole(roleId, updates) {
1139
+ const role = this.roles.get(roleId);
1140
+ if (!role) {
1141
+ throw new Error("Role not found");
1142
+ }
1143
+ if (role.isSystem && updates.permissions) {
1144
+ throw new Error("Cannot modify permissions of system roles");
1145
+ }
1146
+ const updatedRole = {
1147
+ ...role,
1148
+ ...updates,
1149
+ id: roleId,
1150
+ updatedAt: /* @__PURE__ */ new Date()
1151
+ };
1152
+ this.roles.set(roleId, updatedRole);
1153
+ this.clearRolePermissionCache(roleId);
1154
+ this.emit("role:updated", { role: updatedRole, previousRole: role });
1155
+ return updatedRole;
1156
+ }
1157
+ async deleteRole(roleId) {
1158
+ const role = this.roles.get(roleId);
1159
+ if (!role) {
1160
+ return false;
1161
+ }
1162
+ if (role.isSystem) {
1163
+ throw new Error("Cannot delete system roles");
1164
+ }
1165
+ const usersWithRole = Array.from(this.users.values()).filter((user) => user.roles.some((r) => r.id === roleId));
1166
+ if (usersWithRole.length > 0) {
1167
+ throw new Error("Cannot delete role that is assigned to users");
1168
+ }
1169
+ this.roles.delete(roleId);
1170
+ this.emit("role:deleted", { role });
1171
+ return true;
1172
+ }
1173
+ // Permission Management
1174
+ async assignRoleToUser(userId, roleId) {
1175
+ const user = this.users.get(userId);
1176
+ const role = this.roles.get(roleId);
1177
+ if (!user) {
1178
+ throw new Error("User not found");
1179
+ }
1180
+ if (!role) {
1181
+ throw new Error("Role not found");
1182
+ }
1183
+ if (user.roles.some((r) => r.id === roleId)) {
1184
+ return;
1185
+ }
1186
+ user.roles.push(role);
1187
+ user.updatedAt = /* @__PURE__ */ new Date();
1188
+ this.updateUserRoleCache(userId, user.roles.map((r) => r.id));
1189
+ this.clearUserPermissionCache(userId);
1190
+ this.emit("role:assigned", { userId, roleId });
1191
+ }
1192
+ async removeRoleFromUser(userId, roleId) {
1193
+ const user = this.users.get(userId);
1194
+ if (!user) {
1195
+ throw new Error("User not found");
1196
+ }
1197
+ const roleIndex = user.roles.findIndex((r) => r.id === roleId);
1198
+ if (roleIndex === -1) {
1199
+ return;
1200
+ }
1201
+ user.roles.splice(roleIndex, 1);
1202
+ user.updatedAt = /* @__PURE__ */ new Date();
1203
+ this.updateUserRoleCache(userId, user.roles.map((r) => r.id));
1204
+ this.clearUserPermissionCache(userId);
1205
+ this.emit("role:removed", { userId, roleId });
1206
+ }
1207
+ // Permission Checking
1208
+ async checkPermission(check) {
1209
+ const cacheKey = this.getCacheKey(check);
1210
+ const cached = this.getFromCache(cacheKey);
1211
+ if (cached) {
1212
+ return cached;
1213
+ }
1214
+ const result = await this.performPermissionCheck(check);
1215
+ this.setCache(cacheKey, result);
1216
+ return result;
1217
+ }
1218
+ async hasPermission(userId, resource, action, resourceId, context) {
1219
+ const result = await this.checkPermission({
1220
+ userId,
1221
+ resource,
1222
+ action,
1223
+ resourceId,
1224
+ context
1225
+ });
1226
+ return result.granted;
1227
+ }
1228
+ async requirePermission(userId, resource, action, resourceId, context) {
1229
+ const hasAccess = await this.hasPermission(userId, resource, action, resourceId, context);
1230
+ if (!hasAccess) {
1231
+ throw new Error(`Access denied: ${action} on ${resource}`);
1232
+ }
1233
+ }
1234
+ // Utility Methods
1235
+ async getUserPermissions(userId) {
1236
+ const user = this.users.get(userId);
1237
+ if (!user) {
1238
+ return [];
1239
+ }
1240
+ const permissions = [];
1241
+ for (const role of user.roles) {
1242
+ permissions.push(...role.permissions);
1243
+ }
1244
+ const uniquePermissions = permissions.filter(
1245
+ (permission, index, self) => index === self.findIndex((p) => p.id === permission.id)
1246
+ );
1247
+ return uniquePermissions;
1248
+ }
1249
+ async getUserRoles(userId) {
1250
+ const user = this.users.get(userId);
1251
+ return user ? user.roles : [];
1252
+ }
1253
+ listUsers(filters) {
1254
+ let users = Array.from(this.users.values());
1255
+ if (filters?.isActive !== void 0) {
1256
+ users = users.filter((u) => u.isActive === filters.isActive);
1257
+ }
1258
+ if (filters?.roleId) {
1259
+ users = users.filter((u) => u.roles.some((r) => r.id === filters.roleId));
1260
+ }
1261
+ return users;
1262
+ }
1263
+ listRoles() {
1264
+ return Array.from(this.roles.values());
1265
+ }
1266
+ // Private Methods
1267
+ async performPermissionCheck(check) {
1268
+ const user = this.users.get(check.userId);
1269
+ if (!user) {
1270
+ return {
1271
+ granted: false,
1272
+ reason: "User not found",
1273
+ matchedPermissions: [],
1274
+ deniedReasons: ["User not found"]
1275
+ };
1276
+ }
1277
+ if (!user.isActive) {
1278
+ return {
1279
+ granted: false,
1280
+ reason: "User is inactive",
1281
+ matchedPermissions: [],
1282
+ deniedReasons: ["User is inactive"]
1283
+ };
1284
+ }
1285
+ const matchedPermissions = [];
1286
+ const deniedReasons = [];
1287
+ for (const role of user.roles) {
1288
+ for (const permission of role.permissions) {
1289
+ if (this.doesPermissionMatch(permission, check)) {
1290
+ if (await this.checkConditions(permission, check)) {
1291
+ matchedPermissions.push(permission);
1292
+ } else {
1293
+ deniedReasons.push(`Conditions not met for permission ${permission.id}`);
1294
+ }
1295
+ }
1296
+ }
1297
+ }
1298
+ const granted = matchedPermissions.length > 0;
1299
+ return {
1300
+ granted,
1301
+ reason: granted ? void 0 : "No matching permissions found",
1302
+ matchedPermissions,
1303
+ deniedReasons: granted ? [] : deniedReasons
1304
+ };
1305
+ }
1306
+ doesPermissionMatch(permission, check) {
1307
+ return permission.resource === check.resource && permission.action === check.action;
1308
+ }
1309
+ async checkConditions(permission, check) {
1310
+ if (!permission.conditions || permission.conditions.length === 0) {
1311
+ return true;
1312
+ }
1313
+ for (const condition of permission.conditions) {
1314
+ if (!await this.evaluateCondition(condition, check)) {
1315
+ return false;
1316
+ }
1317
+ }
1318
+ return true;
1319
+ }
1320
+ async evaluateCondition(condition, check) {
1321
+ let actualValue;
1322
+ switch (condition.field) {
1323
+ case "userId":
1324
+ actualValue = check.userId;
1325
+ break;
1326
+ case "organizationId":
1327
+ actualValue = check.context?.organizationId;
1328
+ break;
1329
+ case "teamId":
1330
+ actualValue = check.context?.teamId;
1331
+ break;
1332
+ case "resourceOwnerId":
1333
+ actualValue = check.context?.resourceOwnerId;
1334
+ break;
1335
+ default:
1336
+ actualValue = check.context?.metadata?.[condition.field];
1337
+ }
1338
+ switch (condition.operator) {
1339
+ case "equals":
1340
+ return actualValue === condition.value;
1341
+ case "not_equals":
1342
+ return actualValue !== condition.value;
1343
+ case "in":
1344
+ return Array.isArray(condition.value) && condition.value.includes(actualValue);
1345
+ case "not_in":
1346
+ return Array.isArray(condition.value) && !condition.value.includes(actualValue);
1347
+ case "contains":
1348
+ return String(actualValue).includes(String(condition.value));
1349
+ case "starts_with":
1350
+ return String(actualValue).startsWith(String(condition.value));
1351
+ default:
1352
+ return false;
1353
+ }
1354
+ }
1355
+ initializeSystemRoles() {
1356
+ const superAdminRole = {
1357
+ id: "super-admin",
1358
+ name: "Super Admin",
1359
+ description: "Full system access",
1360
+ isSystem: true,
1361
+ permissions: [
1362
+ // Global permissions for all resources and actions
1363
+ ...Object.values(ResourceType).flatMap(
1364
+ (resource) => Object.values(ActionType).map((action) => ({
1365
+ id: `super-admin-${resource}-${action}`,
1366
+ resource,
1367
+ action,
1368
+ scope: "global" /* GLOBAL */
1369
+ }))
1370
+ )
1371
+ ],
1372
+ createdAt: /* @__PURE__ */ new Date(),
1373
+ updatedAt: /* @__PURE__ */ new Date()
1374
+ };
1375
+ const channelAdminRole = {
1376
+ id: "channel-admin",
1377
+ name: "Channel Admin",
1378
+ description: "Manage channels and sender numbers",
1379
+ isSystem: true,
1380
+ permissions: [
1381
+ {
1382
+ id: "channel-admin-channel-manage",
1383
+ resource: "channel" /* CHANNEL */,
1384
+ action: "manage" /* MANAGE */,
1385
+ scope: "organization" /* ORGANIZATION */
1386
+ },
1387
+ {
1388
+ id: "channel-admin-sender-manage",
1389
+ resource: "senderNumber" /* SENDER_NUMBER */,
1390
+ action: "manage" /* MANAGE */,
1391
+ scope: "organization" /* ORGANIZATION */
1392
+ }
1393
+ ],
1394
+ createdAt: /* @__PURE__ */ new Date(),
1395
+ updatedAt: /* @__PURE__ */ new Date()
1396
+ };
1397
+ const messageSenderRole = {
1398
+ id: "message-sender",
1399
+ name: "Message Sender",
1400
+ description: "Send messages using configured channels",
1401
+ isSystem: true,
1402
+ permissions: [
1403
+ {
1404
+ id: "message-sender-channel-read",
1405
+ resource: "channel" /* CHANNEL */,
1406
+ action: "read" /* READ */,
1407
+ scope: "organization" /* ORGANIZATION */
1408
+ },
1409
+ {
1410
+ id: "message-sender-message-send",
1411
+ resource: "message" /* MESSAGE */,
1412
+ action: "send" /* SEND */,
1413
+ scope: "organization" /* ORGANIZATION */
1414
+ }
1415
+ ],
1416
+ createdAt: /* @__PURE__ */ new Date(),
1417
+ updatedAt: /* @__PURE__ */ new Date()
1418
+ };
1419
+ const viewerRole = {
1420
+ id: "viewer",
1421
+ name: "Viewer",
1422
+ description: "Read-only access",
1423
+ isSystem: true,
1424
+ permissions: [
1425
+ {
1426
+ id: "viewer-channel-read",
1427
+ resource: "channel" /* CHANNEL */,
1428
+ action: "read" /* READ */,
1429
+ scope: "organization" /* ORGANIZATION */
1430
+ },
1431
+ {
1432
+ id: "viewer-sender-read",
1433
+ resource: "senderNumber" /* SENDER_NUMBER */,
1434
+ action: "read" /* READ */,
1435
+ scope: "organization" /* ORGANIZATION */
1436
+ },
1437
+ {
1438
+ id: "viewer-analytics-read",
1439
+ resource: "analytics" /* ANALYTICS */,
1440
+ action: "read" /* READ */,
1441
+ scope: "organization" /* ORGANIZATION */
1442
+ }
1443
+ ],
1444
+ createdAt: /* @__PURE__ */ new Date(),
1445
+ updatedAt: /* @__PURE__ */ new Date()
1446
+ };
1447
+ this.roles.set(superAdminRole.id, superAdminRole);
1448
+ this.roles.set(channelAdminRole.id, channelAdminRole);
1449
+ this.roles.set(messageSenderRole.id, messageSenderRole);
1450
+ this.roles.set(viewerRole.id, viewerRole);
1451
+ }
1452
+ updateUserRoleCache(userId, roleIds) {
1453
+ this.userRoleCache.set(userId, new Set(roleIds));
1454
+ }
1455
+ clearUserPermissionCache(userId) {
1456
+ const keysToDelete = [];
1457
+ for (const key of this.permissionCache.keys()) {
1458
+ if (key.startsWith(`${userId}:`)) {
1459
+ keysToDelete.push(key);
1460
+ }
1461
+ }
1462
+ keysToDelete.forEach((key) => {
1463
+ this.permissionCache.delete(key);
1464
+ this.cacheExpiry.delete(key);
1465
+ });
1466
+ }
1467
+ clearRolePermissionCache(roleId) {
1468
+ for (const [userId, roleIds] of this.userRoleCache) {
1469
+ if (roleIds.has(roleId)) {
1470
+ this.clearUserPermissionCache(userId);
1471
+ }
1472
+ }
1473
+ }
1474
+ getCacheKey(check) {
1475
+ const contextKey = check.context ? JSON.stringify(check.context) : "";
1476
+ return `${check.userId}:${check.resource}:${check.action}:${check.resourceId || ""}:${contextKey}`;
1477
+ }
1478
+ getFromCache(key) {
1479
+ const expiry = this.cacheExpiry.get(key);
1480
+ if (!expiry || expiry < Date.now()) {
1481
+ this.permissionCache.delete(key);
1482
+ this.cacheExpiry.delete(key);
1483
+ return null;
1484
+ }
1485
+ return this.permissionCache.get(key) || null;
1486
+ }
1487
+ setCache(key, result) {
1488
+ this.permissionCache.set(key, result);
1489
+ this.cacheExpiry.set(key, Date.now() + this.CACHE_DURATION);
1490
+ }
1491
+ generateUserId() {
1492
+ return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1493
+ }
1494
+ generateRoleId() {
1495
+ return `role_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1496
+ }
1497
+ };
1498
+
1499
+ // src/verification/business.verify.ts
1500
+ var import_events3 = require("events");
1501
+ var BusinessVerifier = class extends import_events3.EventEmitter {
1502
+ constructor(options = {}) {
1503
+ super();
1504
+ this.options = options;
1505
+ this.verificationRequests = /* @__PURE__ */ new Map();
1506
+ this.documentValidators = /* @__PURE__ */ new Map();
1507
+ this.defaultOptions = {
1508
+ enableAutoVerification: true,
1509
+ requiredDocuments: ["BUSINESS_REGISTRATION" /* BUSINESS_REGISTRATION */],
1510
+ autoApprovalThreshold: 80,
1511
+ requireManualReview: false,
1512
+ documentRetentionDays: 365,
1513
+ enableExternalAPIs: false
1514
+ };
1515
+ this.options = { ...this.defaultOptions, ...options };
1516
+ this.initializeDocumentValidators();
1517
+ }
1518
+ /**
1519
+ * Submit business verification request
1520
+ */
1521
+ async submitVerification(channelId, businessInfo, documents) {
1522
+ const requestId = this.generateRequestId();
1523
+ this.validateRequiredDocuments(documents);
1524
+ const verificationRequest = {
1525
+ id: requestId,
1526
+ channelId,
1527
+ businessInfo,
1528
+ documents: documents.map((doc) => ({
1529
+ ...doc,
1530
+ status: "UPLOADED" /* UPLOADED */
1531
+ })),
1532
+ status: "PENDING" /* PENDING */,
1533
+ submittedAt: /* @__PURE__ */ new Date()
1534
+ };
1535
+ this.verificationRequests.set(requestId, verificationRequest);
1536
+ this.emit("verification:submitted", { verificationRequest });
1537
+ if (this.options.enableAutoVerification) {
1538
+ await this.processAutoVerification(requestId);
1539
+ }
1540
+ return verificationRequest;
1541
+ }
1542
+ /**
1543
+ * Get verification request by ID
1544
+ */
1545
+ getVerificationRequest(requestId) {
1546
+ return this.verificationRequests.get(requestId) || null;
1547
+ }
1548
+ /**
1549
+ * Get verification request by channel ID
1550
+ */
1551
+ getVerificationByChannelId(channelId) {
1552
+ for (const request of this.verificationRequests.values()) {
1553
+ if (request.channelId === channelId) {
1554
+ return request;
1555
+ }
1556
+ }
1557
+ return null;
1558
+ }
1559
+ /**
1560
+ * Manually approve verification
1561
+ */
1562
+ async approveVerification(requestId, reviewerId, notes) {
1563
+ const request = this.verificationRequests.get(requestId);
1564
+ if (!request) {
1565
+ throw new Error("Verification request not found");
1566
+ }
1567
+ request.status = "VERIFIED" /* VERIFIED */;
1568
+ request.reviewedAt = /* @__PURE__ */ new Date();
1569
+ request.reviewedBy = reviewerId;
1570
+ request.reviewNotes = notes;
1571
+ request.documents.forEach((doc) => {
1572
+ doc.status = "VERIFIED" /* VERIFIED */;
1573
+ });
1574
+ this.emit("verification:approved", { verificationRequest: request, reviewerId });
1575
+ return request;
1576
+ }
1577
+ /**
1578
+ * Manually reject verification
1579
+ */
1580
+ async rejectVerification(requestId, reviewerId, reason) {
1581
+ const request = this.verificationRequests.get(requestId);
1582
+ if (!request) {
1583
+ throw new Error("Verification request not found");
1584
+ }
1585
+ request.status = "REJECTED" /* REJECTED */;
1586
+ request.reviewedAt = /* @__PURE__ */ new Date();
1587
+ request.reviewedBy = reviewerId;
1588
+ request.reviewNotes = reason;
1589
+ request.documents.forEach((doc) => {
1590
+ if (doc.status === "UPLOADED" /* UPLOADED */) {
1591
+ doc.status = "REJECTED" /* REJECTED */;
1592
+ }
1593
+ });
1594
+ this.emit("verification:rejected", { verificationRequest: request, reviewerId, reason });
1595
+ return request;
1596
+ }
1597
+ /**
1598
+ * Update verification request with additional documents
1599
+ */
1600
+ async addDocument(requestId, document) {
1601
+ const request = this.verificationRequests.get(requestId);
1602
+ if (!request) {
1603
+ throw new Error("Verification request not found");
1604
+ }
1605
+ if (request.status !== "PENDING" /* PENDING */ && request.status !== "UNDER_REVIEW" /* UNDER_REVIEW */) {
1606
+ throw new Error("Cannot add documents to completed verification");
1607
+ }
1608
+ document.status = "UPLOADED" /* UPLOADED */;
1609
+ request.documents.push(document);
1610
+ if (this.options.enableAutoVerification) {
1611
+ await this.processAutoVerification(requestId);
1612
+ }
1613
+ this.emit("verification:document_added", { verificationRequest: request, document });
1614
+ return request;
1615
+ }
1616
+ /**
1617
+ * List verification requests with filters
1618
+ */
1619
+ listVerificationRequests(filters) {
1620
+ let requests = Array.from(this.verificationRequests.values());
1621
+ if (filters?.status) {
1622
+ requests = requests.filter((r) => r.status === filters.status);
1623
+ }
1624
+ if (filters?.channelId) {
1625
+ requests = requests.filter((r) => r.channelId === filters.channelId);
1626
+ }
1627
+ if (filters?.submittedAfter) {
1628
+ requests = requests.filter((r) => r.submittedAt >= filters.submittedAfter);
1629
+ }
1630
+ if (filters?.submittedBefore) {
1631
+ requests = requests.filter((r) => r.submittedAt <= filters.submittedBefore);
1632
+ }
1633
+ return requests.sort((a, b) => b.submittedAt.getTime() - a.submittedAt.getTime());
1634
+ }
1635
+ /**
1636
+ * Get verification statistics
1637
+ */
1638
+ getVerificationStats() {
1639
+ const requests = Array.from(this.verificationRequests.values());
1640
+ const byStatus = {};
1641
+ let totalProcessingTime = 0;
1642
+ let processedCount = 0;
1643
+ let autoApprovedCount = 0;
1644
+ requests.forEach((request) => {
1645
+ byStatus[request.status] = (byStatus[request.status] || 0) + 1;
1646
+ if (request.reviewedAt) {
1647
+ const processingTime = request.reviewedAt.getTime() - request.submittedAt.getTime();
1648
+ totalProcessingTime += processingTime;
1649
+ processedCount++;
1650
+ if (!request.reviewedBy && request.status === "VERIFIED" /* VERIFIED */) {
1651
+ autoApprovedCount++;
1652
+ }
1653
+ }
1654
+ });
1655
+ return {
1656
+ total: requests.length,
1657
+ byStatus,
1658
+ averageProcessingTime: processedCount > 0 ? totalProcessingTime / processedCount : 0,
1659
+ autoApprovalRate: processedCount > 0 ? autoApprovedCount / processedCount * 100 : 0
1660
+ };
1661
+ }
1662
+ // Private Methods
1663
+ async processAutoVerification(requestId) {
1664
+ const request = this.verificationRequests.get(requestId);
1665
+ if (!request) return;
1666
+ request.status = "UNDER_REVIEW" /* UNDER_REVIEW */;
1667
+ this.emit("verification:auto_processing_started", { verificationRequest: request });
1668
+ const autoResults = [];
1669
+ try {
1670
+ const businessCheck = await this.verifyBusinessRegistration(request.businessInfo);
1671
+ autoResults.push(businessCheck);
1672
+ for (const document of request.documents) {
1673
+ const docValidation = await this.validateDocument(document);
1674
+ autoResults.push({
1675
+ checkType: "document_validation",
1676
+ status: docValidation.isValid ? "passed" : "failed",
1677
+ score: docValidation.confidence,
1678
+ details: `Document validation: ${docValidation.issues.length} issues found`,
1679
+ metadata: { documentId: document.id, issues: docValidation.issues }
1680
+ });
1681
+ }
1682
+ const addressCheck = await this.verifyAddress(request.businessInfo.address);
1683
+ autoResults.push(addressCheck);
1684
+ const phoneCheck = await this.verifyPhoneNumber(request.businessInfo.contactInfo.phoneNumber);
1685
+ autoResults.push(phoneCheck);
1686
+ request.autoVerificationResults = autoResults;
1687
+ const overallScore = autoResults.reduce((sum, result) => sum + result.score, 0) / autoResults.length;
1688
+ if (overallScore >= this.options.autoApprovalThreshold && !this.options.requireManualReview) {
1689
+ request.status = "VERIFIED" /* VERIFIED */;
1690
+ request.reviewedAt = /* @__PURE__ */ new Date();
1691
+ request.reviewNotes = `Auto-approved with score: ${overallScore.toFixed(1)}`;
1692
+ request.documents.forEach((doc) => {
1693
+ doc.status = "VERIFIED" /* VERIFIED */;
1694
+ });
1695
+ this.emit("verification:auto_approved", { verificationRequest: request, score: overallScore });
1696
+ } else {
1697
+ this.emit("verification:manual_review_required", { verificationRequest: request, score: overallScore });
1698
+ }
1699
+ } catch (error) {
1700
+ request.status = "PENDING" /* PENDING */;
1701
+ this.emit("verification:auto_processing_failed", {
1702
+ verificationRequest: request,
1703
+ error: error instanceof Error ? error.message : "Unknown error"
1704
+ });
1705
+ }
1706
+ }
1707
+ validateRequiredDocuments(documents) {
1708
+ const providedTypes = new Set(documents.map((doc) => doc.type));
1709
+ const missingTypes = this.options.requiredDocuments.filter((type) => !providedTypes.has(type));
1710
+ if (missingTypes.length > 0) {
1711
+ throw new Error(`Missing required documents: ${missingTypes.join(", ")}`);
1712
+ }
1713
+ }
1714
+ async verifyBusinessRegistration(businessInfo) {
1715
+ const score = this.calculateBusinessRegistrationScore(businessInfo);
1716
+ return {
1717
+ checkType: "business_registry",
1718
+ status: score >= 70 ? "passed" : score >= 50 ? "warning" : "failed",
1719
+ score,
1720
+ details: `Business registration verification completed`,
1721
+ metadata: {
1722
+ businessName: businessInfo.businessName,
1723
+ registrationNumber: businessInfo.businessRegistrationNumber
1724
+ }
1725
+ };
1726
+ }
1727
+ calculateBusinessRegistrationScore(businessInfo) {
1728
+ let score = 0;
1729
+ if (/^\d{3}-\d{2}-\d{5}$/.test(businessInfo.businessRegistrationNumber)) {
1730
+ score += 30;
1731
+ }
1732
+ if (businessInfo.businessName.length >= 2 && businessInfo.businessName.length <= 100) {
1733
+ score += 20;
1734
+ }
1735
+ const now = /* @__PURE__ */ new Date();
1736
+ const establishedDate = new Date(businessInfo.establishedDate);
1737
+ const yearsOld = (now.getTime() - establishedDate.getTime()) / (1e3 * 60 * 60 * 24 * 365);
1738
+ if (yearsOld >= 0 && yearsOld <= 100) {
1739
+ score += 20;
1740
+ }
1741
+ if (businessInfo.contactInfo.email && businessInfo.contactInfo.phoneNumber) {
1742
+ score += 15;
1743
+ }
1744
+ if (businessInfo.address.street && businessInfo.address.city && businessInfo.address.postalCode) {
1745
+ score += 15;
1746
+ }
1747
+ return Math.min(100, score);
1748
+ }
1749
+ async validateDocument(document) {
1750
+ const validator = this.documentValidators.get(document.type);
1751
+ if (!validator) {
1752
+ return {
1753
+ isValid: false,
1754
+ confidence: 0,
1755
+ issues: [{
1756
+ type: "format",
1757
+ severity: "critical",
1758
+ message: `No validator available for document type: ${document.type}`
1759
+ }]
1760
+ };
1761
+ }
1762
+ return await validator(document);
1763
+ }
1764
+ async verifyAddress(address) {
1765
+ let score = 0;
1766
+ if (address.street && address.city && address.postalCode) {
1767
+ score += 40;
1768
+ }
1769
+ if (/^\d{5}$/.test(address.postalCode)) {
1770
+ score += 30;
1771
+ }
1772
+ if (address.country === "KR" || address.country === "Korea") {
1773
+ score += 30;
1774
+ }
1775
+ return {
1776
+ checkType: "address_verification",
1777
+ status: score >= 70 ? "passed" : score >= 50 ? "warning" : "failed",
1778
+ score,
1779
+ details: "Address verification completed",
1780
+ metadata: { address }
1781
+ };
1782
+ }
1783
+ async verifyPhoneNumber(phoneNumber) {
1784
+ const isValidFormat = /^(010|011|016|017|018|019)[0-9]{7,8}$/.test(phoneNumber);
1785
+ const score = isValidFormat ? 100 : 0;
1786
+ return {
1787
+ checkType: "phone_verification",
1788
+ status: isValidFormat ? "passed" : "failed",
1789
+ score,
1790
+ details: isValidFormat ? "Phone number format is valid" : "Invalid phone number format",
1791
+ metadata: { phoneNumber }
1792
+ };
1793
+ }
1794
+ initializeDocumentValidators() {
1795
+ this.documentValidators.set("BUSINESS_REGISTRATION" /* BUSINESS_REGISTRATION */, async (doc) => {
1796
+ const issues = [];
1797
+ let confidence = 80;
1798
+ if (!doc.fileName.match(/\.(pdf|jpg|jpeg|png)$/i)) {
1799
+ issues.push({
1800
+ type: "format",
1801
+ severity: "medium",
1802
+ message: "Unsupported file format"
1803
+ });
1804
+ confidence -= 20;
1805
+ }
1806
+ return {
1807
+ isValid: issues.length === 0 || issues.every((i) => i.severity !== "critical"),
1808
+ confidence: Math.max(0, confidence),
1809
+ issues
1810
+ };
1811
+ });
1812
+ this.documentValidators.set("BUSINESS_LICENSE" /* BUSINESS_LICENSE */, async (doc) => {
1813
+ const issues = [];
1814
+ let confidence = 75;
1815
+ if (!doc.fileName.match(/\.(pdf|jpg|jpeg|png)$/i)) {
1816
+ issues.push({
1817
+ type: "format",
1818
+ severity: "medium",
1819
+ message: "Unsupported file format"
1820
+ });
1821
+ confidence -= 15;
1822
+ }
1823
+ return {
1824
+ isValid: issues.length === 0 || issues.every((i) => i.severity !== "critical"),
1825
+ confidence: Math.max(0, confidence),
1826
+ issues
1827
+ };
1828
+ });
1829
+ this.documentValidators.set("ID_CARD" /* ID_CARD */, async (doc) => {
1830
+ const issues = [];
1831
+ let confidence = 70;
1832
+ if (!doc.fileName.match(/\.(jpg|jpeg|png)$/i)) {
1833
+ issues.push({
1834
+ type: "format",
1835
+ severity: "high",
1836
+ message: "ID card should be an image file"
1837
+ });
1838
+ confidence -= 30;
1839
+ }
1840
+ return {
1841
+ isValid: issues.length === 0 || issues.every((i) => i.severity !== "critical"),
1842
+ confidence: Math.max(0, confidence),
1843
+ issues
1844
+ };
1845
+ });
1846
+ const defaultValidator = async (doc) => {
1847
+ const issues = [];
1848
+ let confidence = 60;
1849
+ if (!doc.fileName || doc.fileName.length === 0) {
1850
+ issues.push({
1851
+ type: "format",
1852
+ severity: "critical",
1853
+ message: "File name is required"
1854
+ });
1855
+ confidence = 0;
1856
+ }
1857
+ return {
1858
+ isValid: issues.length === 0 || issues.every((i) => i.severity !== "critical"),
1859
+ confidence: Math.max(0, confidence),
1860
+ issues
1861
+ };
1862
+ };
1863
+ this.documentValidators.set("AUTHORIZATION_LETTER" /* AUTHORIZATION_LETTER */, defaultValidator);
1864
+ this.documentValidators.set("OTHER" /* OTHER */, defaultValidator);
1865
+ }
1866
+ generateRequestId() {
1867
+ return `biz_verify_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1868
+ }
1869
+ };
1870
+
1871
+ // src/verification/number.verify.ts
1872
+ var import_events4 = require("events");
1873
+ var VerificationType = /* @__PURE__ */ ((VerificationType2) => {
1874
+ VerificationType2["SMS"] = "sms";
1875
+ VerificationType2["VOICE_CALL"] = "voice_call";
1876
+ VerificationType2["HYBRID"] = "hybrid";
1877
+ return VerificationType2;
1878
+ })(VerificationType || {});
1879
+ var VerificationMethod = /* @__PURE__ */ ((VerificationMethod2) => {
1880
+ VerificationMethod2["SMS"] = "sms";
1881
+ VerificationMethod2["VOICE_CALL"] = "voice_call";
1882
+ VerificationMethod2["MISSED_CALL"] = "missed_call";
1883
+ return VerificationMethod2;
1884
+ })(VerificationMethod || {});
1885
+ var NumberVerifier = class extends import_events4.EventEmitter {
1886
+ constructor(options = {}) {
1887
+ super();
1888
+ this.options = options;
1889
+ this.verificationRequests = /* @__PURE__ */ new Map();
1890
+ this.phoneNumberCache = /* @__PURE__ */ new Map();
1891
+ this.rateLimitTracker = /* @__PURE__ */ new Map();
1892
+ this.dailyAttemptTracker = /* @__PURE__ */ new Map();
1893
+ this.blockedNumbers = /* @__PURE__ */ new Set();
1894
+ this.defaultOptions = {
1895
+ codeLength: 6,
1896
+ codeExpiryMinutes: 5,
1897
+ maxAttempts: 3,
1898
+ maxDailyAttempts: 10,
1899
+ smsTemplate: "\uC778\uC99D\uBC88\uD638: {code}. {expiry}\uBD84 \uB0B4\uC5D0 \uC785\uB825\uD574\uC8FC\uC138\uC694.",
1900
+ voiceTemplate: "\uC778\uC99D\uBC88\uD638\uB294 {code}\uC785\uB2C8\uB2E4. \uB2E4\uC2DC \uD55C \uBC88, {code}\uC785\uB2C8\uB2E4.",
1901
+ rateLimitMinutes: 1,
1902
+ enableVoiceFallback: true,
1903
+ enableMissedCallVerification: false,
1904
+ blockedNumbers: [],
1905
+ allowedCountries: ["KR"]
1906
+ };
1907
+ this.options = { ...this.defaultOptions, ...options };
1908
+ this.options.blockedNumbers?.forEach((number) => {
1909
+ this.blockedNumbers.add(number);
1910
+ });
1911
+ }
1912
+ /**
1913
+ * Start phone number verification process
1914
+ */
1915
+ async startVerification(senderNumberId, phoneNumber, verificationType = "sms" /* SMS */, metadata = {}) {
1916
+ const phoneInfo = await this.getPhoneNumberInfo(phoneNumber);
1917
+ if (!phoneInfo.isValid) {
1918
+ throw new Error("Invalid phone number format");
1919
+ }
1920
+ if (this.isNumberBlocked(phoneNumber)) {
1921
+ throw new Error("Phone number is blocked");
1922
+ }
1923
+ if (this.isRateLimited(phoneNumber)) {
1924
+ throw new Error("Rate limit exceeded. Please try again later.");
1925
+ }
1926
+ if (this.isDailyLimitExceeded(phoneNumber)) {
1927
+ throw new Error("Daily verification attempt limit exceeded");
1928
+ }
1929
+ const requestId = this.generateRequestId();
1930
+ const verificationCode = this.generateVerificationCode();
1931
+ const expiresAt = new Date(Date.now() + this.options.codeExpiryMinutes * 60 * 1e3);
1932
+ const verificationRequest = {
1933
+ id: requestId,
1934
+ senderNumberId,
1935
+ phoneNumber,
1936
+ verificationType,
1937
+ verificationCode,
1938
+ status: "pending" /* PENDING */,
1939
+ attempts: [],
1940
+ expiresAt,
1941
+ createdAt: /* @__PURE__ */ new Date(),
1942
+ metadata
1943
+ };
1944
+ this.verificationRequests.set(requestId, verificationRequest);
1945
+ this.updateRateLimit(phoneNumber);
1946
+ this.updateDailyAttempts(phoneNumber);
1947
+ this.emit("verification:started", { verificationRequest, phoneInfo });
1948
+ await this.sendVerificationCode(verificationRequest, phoneInfo);
1949
+ return verificationRequest;
1950
+ }
1951
+ /**
1952
+ * Verify the provided code
1953
+ */
1954
+ async verifyCode(requestId, providedCode) {
1955
+ const request = this.verificationRequests.get(requestId);
1956
+ if (!request) {
1957
+ return {
1958
+ success: false,
1959
+ status: "failed" /* FAILED */,
1960
+ message: "Verification request not found"
1961
+ };
1962
+ }
1963
+ if (request.status === "verified" /* VERIFIED */) {
1964
+ return {
1965
+ success: true,
1966
+ status: "verified" /* VERIFIED */,
1967
+ message: "Already verified"
1968
+ };
1969
+ }
1970
+ if (/* @__PURE__ */ new Date() > request.expiresAt) {
1971
+ request.status = "expired" /* EXPIRED */;
1972
+ this.emit("verification:expired", { verificationRequest: request });
1973
+ return {
1974
+ success: false,
1975
+ status: "expired" /* EXPIRED */,
1976
+ message: "Verification code has expired"
1977
+ };
1978
+ }
1979
+ if (request.status === "blocked" /* BLOCKED */) {
1980
+ return {
1981
+ success: false,
1982
+ status: "blocked" /* BLOCKED */,
1983
+ message: "Verification blocked due to too many failed attempts"
1984
+ };
1985
+ }
1986
+ const isCodeValid = this.validateCode(request.verificationCode, providedCode);
1987
+ if (isCodeValid) {
1988
+ request.status = "verified" /* VERIFIED */;
1989
+ request.completedAt = /* @__PURE__ */ new Date();
1990
+ this.emit("verification:success", { verificationRequest: request });
1991
+ return {
1992
+ success: true,
1993
+ status: "verified" /* VERIFIED */,
1994
+ message: "Phone number verified successfully"
1995
+ };
1996
+ } else {
1997
+ const failedAttempt = {
1998
+ attemptNumber: request.attempts.length + 1,
1999
+ attemptedAt: /* @__PURE__ */ new Date(),
2000
+ method: "sms" /* SMS */,
2001
+ // Assuming SMS for verification attempts
2002
+ status: "failed"
2003
+ };
2004
+ request.attempts.push(failedAttempt);
2005
+ const failedAttempts = request.attempts.filter((a) => a.status === "failed").length;
2006
+ if (failedAttempts >= this.options.maxAttempts) {
2007
+ request.status = "blocked" /* BLOCKED */;
2008
+ this.emit("verification:blocked", { verificationRequest: request });
2009
+ return {
2010
+ success: false,
2011
+ status: "blocked" /* BLOCKED */,
2012
+ message: "Too many failed attempts. Verification blocked."
2013
+ };
2014
+ } else {
2015
+ request.status = "failed" /* FAILED */;
2016
+ this.emit("verification:failed_attempt", {
2017
+ verificationRequest: request,
2018
+ attemptsRemaining: this.options.maxAttempts - failedAttempts
2019
+ });
2020
+ return {
2021
+ success: false,
2022
+ status: "failed" /* FAILED */,
2023
+ message: `Invalid code. ${this.options.maxAttempts - failedAttempts} attempts remaining.`
2024
+ };
2025
+ }
2026
+ }
2027
+ }
2028
+ /**
2029
+ * Resend verification code
2030
+ */
2031
+ async resendCode(requestId, method) {
2032
+ const request = this.verificationRequests.get(requestId);
2033
+ if (!request) {
2034
+ throw new Error("Verification request not found");
2035
+ }
2036
+ if (request.status === "verified" /* VERIFIED */) {
2037
+ throw new Error("Verification already completed");
2038
+ }
2039
+ if (request.status === "blocked" /* BLOCKED */) {
2040
+ throw new Error("Verification is blocked");
2041
+ }
2042
+ if (this.isRateLimited(request.phoneNumber)) {
2043
+ throw new Error("Rate limit exceeded. Please wait before requesting a new code.");
2044
+ }
2045
+ request.verificationCode = this.generateVerificationCode();
2046
+ request.expiresAt = new Date(Date.now() + this.options.codeExpiryMinutes * 60 * 1e3);
2047
+ request.status = "pending" /* PENDING */;
2048
+ this.updateRateLimit(request.phoneNumber);
2049
+ const phoneInfo = await this.getPhoneNumberInfo(request.phoneNumber);
2050
+ if (method) {
2051
+ await this.sendVerificationByMethod(request, phoneInfo, method);
2052
+ } else {
2053
+ await this.sendVerificationCode(request, phoneInfo);
2054
+ }
2055
+ this.emit("verification:resent", { verificationRequest: request });
2056
+ return request;
2057
+ }
2058
+ /**
2059
+ * Get verification request status
2060
+ */
2061
+ getVerificationStatus(requestId) {
2062
+ return this.verificationRequests.get(requestId) || null;
2063
+ }
2064
+ /**
2065
+ * Cancel verification request
2066
+ */
2067
+ async cancelVerification(requestId) {
2068
+ const request = this.verificationRequests.get(requestId);
2069
+ if (!request) {
2070
+ return false;
2071
+ }
2072
+ if (request.status === "verified" /* VERIFIED */) {
2073
+ return false;
2074
+ }
2075
+ this.verificationRequests.delete(requestId);
2076
+ this.emit("verification:cancelled", { verificationRequest: request });
2077
+ return true;
2078
+ }
2079
+ /**
2080
+ * Block a phone number from verification
2081
+ */
2082
+ blockPhoneNumber(phoneNumber, reason) {
2083
+ this.blockedNumbers.add(phoneNumber);
2084
+ for (const [requestId, request] of this.verificationRequests) {
2085
+ if (request.phoneNumber === phoneNumber && request.status !== "verified" /* VERIFIED */) {
2086
+ request.status = "blocked" /* BLOCKED */;
2087
+ }
2088
+ }
2089
+ this.emit("phone:blocked", { phoneNumber, reason });
2090
+ }
2091
+ /**
2092
+ * Unblock a phone number
2093
+ */
2094
+ unblockPhoneNumber(phoneNumber) {
2095
+ this.blockedNumbers.delete(phoneNumber);
2096
+ this.emit("phone:unblocked", { phoneNumber });
2097
+ }
2098
+ /**
2099
+ * Get verification statistics
2100
+ */
2101
+ getVerificationStats() {
2102
+ const requests = Array.from(this.verificationRequests.values());
2103
+ const byStatus = {};
2104
+ const byMethod = {};
2105
+ let totalCompletionTime = 0;
2106
+ let completedCount = 0;
2107
+ requests.forEach((request) => {
2108
+ byStatus[request.status] = (byStatus[request.status] || 0) + 1;
2109
+ if (request.attempts.length > 0) {
2110
+ const primaryMethod = request.attempts[0].method;
2111
+ byMethod[primaryMethod] = (byMethod[primaryMethod] || 0) + 1;
2112
+ }
2113
+ if (request.completedAt) {
2114
+ const completionTime = request.completedAt.getTime() - request.createdAt.getTime();
2115
+ totalCompletionTime += completionTime;
2116
+ completedCount++;
2117
+ }
2118
+ });
2119
+ const successCount = byStatus["verified" /* VERIFIED */] || 0;
2120
+ const successRate = requests.length > 0 ? successCount / requests.length * 100 : 0;
2121
+ return {
2122
+ total: requests.length,
2123
+ byStatus,
2124
+ byMethod,
2125
+ successRate,
2126
+ averageCompletionTime: completedCount > 0 ? totalCompletionTime / completedCount : 0
2127
+ };
2128
+ }
2129
+ /**
2130
+ * Clean up expired verification requests
2131
+ */
2132
+ cleanup() {
2133
+ const now = /* @__PURE__ */ new Date();
2134
+ let cleanedCount = 0;
2135
+ for (const [requestId, request] of this.verificationRequests) {
2136
+ if (now > request.expiresAt && request.status !== "verified" /* VERIFIED */) {
2137
+ request.status = "expired" /* EXPIRED */;
2138
+ this.verificationRequests.delete(requestId);
2139
+ cleanedCount++;
2140
+ }
2141
+ }
2142
+ const rateWindow = this.options.rateLimitMinutes * 60 * 1e3;
2143
+ for (const [phoneNumber, timestamps] of this.rateLimitTracker) {
2144
+ const validTimestamps = timestamps.filter((ts) => now.getTime() - ts.getTime() < rateWindow);
2145
+ if (validTimestamps.length === 0) {
2146
+ this.rateLimitTracker.delete(phoneNumber);
2147
+ } else {
2148
+ this.rateLimitTracker.set(phoneNumber, validTimestamps);
2149
+ }
2150
+ }
2151
+ return cleanedCount;
2152
+ }
2153
+ // Private Methods
2154
+ async sendVerificationCode(request, phoneInfo) {
2155
+ let method;
2156
+ switch (request.verificationType) {
2157
+ case "sms" /* SMS */:
2158
+ method = "sms" /* SMS */;
2159
+ break;
2160
+ case "voice_call" /* VOICE_CALL */:
2161
+ method = "voice_call" /* VOICE_CALL */;
2162
+ break;
2163
+ case "hybrid" /* HYBRID */:
2164
+ method = phoneInfo.lineType === "landline" ? "voice_call" /* VOICE_CALL */ : "sms" /* SMS */;
2165
+ break;
2166
+ default:
2167
+ method = "sms" /* SMS */;
2168
+ }
2169
+ await this.sendVerificationByMethod(request, phoneInfo, method);
2170
+ }
2171
+ async sendVerificationByMethod(request, phoneInfo, method) {
2172
+ const attempt = {
2173
+ attemptNumber: request.attempts.length + 1,
2174
+ attemptedAt: /* @__PURE__ */ new Date(),
2175
+ method,
2176
+ status: "sent"
2177
+ };
2178
+ const startTime = Date.now();
2179
+ try {
2180
+ switch (method) {
2181
+ case "sms" /* SMS */:
2182
+ await this.sendSMS(request, phoneInfo);
2183
+ break;
2184
+ case "voice_call" /* VOICE_CALL */:
2185
+ await this.sendVoiceCall(request, phoneInfo);
2186
+ break;
2187
+ case "missed_call" /* MISSED_CALL */:
2188
+ await this.sendMissedCall(request, phoneInfo);
2189
+ break;
2190
+ }
2191
+ attempt.status = "delivered";
2192
+ attempt.responseTime = Date.now() - startTime;
2193
+ request.status = "code_sent" /* CODE_SENT */;
2194
+ } catch (error) {
2195
+ attempt.status = "failed";
2196
+ attempt.failureReason = error instanceof Error ? error.message : "Unknown error";
2197
+ attempt.responseTime = Date.now() - startTime;
2198
+ if (method === "sms" /* SMS */ && this.options.enableVoiceFallback && request.attempts.filter((a) => a.method === "voice_call" /* VOICE_CALL */).length === 0) {
2199
+ attempt.status = "failed";
2200
+ request.attempts.push(attempt);
2201
+ await this.sendVerificationByMethod(request, phoneInfo, "voice_call" /* VOICE_CALL */);
2202
+ return;
2203
+ }
2204
+ request.status = "failed" /* FAILED */;
2205
+ throw error;
2206
+ }
2207
+ request.attempts.push(attempt);
2208
+ }
2209
+ async sendSMS(request, phoneInfo) {
2210
+ if (!this.options.smsProvider) {
2211
+ throw new Error("SMS provider not configured");
2212
+ }
2213
+ const message = this.options.smsTemplate.replace("{code}", request.verificationCode).replace("{expiry}", this.options.codeExpiryMinutes.toString());
2214
+ const result = await this.options.smsProvider.sendSMS(request.phoneNumber, message);
2215
+ if (result.status === "failed") {
2216
+ throw new Error(result.error || "SMS sending failed");
2217
+ }
2218
+ }
2219
+ async sendVoiceCall(request, phoneInfo) {
2220
+ if (!this.options.voiceProvider) {
2221
+ throw new Error("Voice provider not configured");
2222
+ }
2223
+ const message = this.options.voiceTemplate.replace("{code}", request.verificationCode.split("").join(" "));
2224
+ const result = await this.options.voiceProvider.makeCall(request.phoneNumber, message);
2225
+ if (result.status === "failed") {
2226
+ throw new Error(result.error || "Voice call failed");
2227
+ }
2228
+ }
2229
+ async sendMissedCall(request, phoneInfo) {
2230
+ if (!this.options.voiceProvider?.makeMissedCall) {
2231
+ throw new Error("Missed call verification not supported");
2232
+ }
2233
+ const result = await this.options.voiceProvider.makeMissedCall(request.phoneNumber);
2234
+ if (result.status === "failed") {
2235
+ throw new Error(result.error || "Missed call failed");
2236
+ }
2237
+ if (result.missedCallNumber) {
2238
+ const codeFromNumber = result.missedCallNumber.slice(-this.options.codeLength);
2239
+ request.verificationCode = codeFromNumber;
2240
+ }
2241
+ }
2242
+ async getPhoneNumberInfo(phoneNumber) {
2243
+ if (this.phoneNumberCache.has(phoneNumber)) {
2244
+ return this.phoneNumberCache.get(phoneNumber);
2245
+ }
2246
+ const phoneInfo = this.parseKoreanPhoneNumber(phoneNumber);
2247
+ this.phoneNumberCache.set(phoneNumber, phoneInfo);
2248
+ return phoneInfo;
2249
+ }
2250
+ parseKoreanPhoneNumber(phoneNumber) {
2251
+ const cleaned = phoneNumber.replace(/\D/g, "");
2252
+ const mobilePattern = /^(010|011|016|017|018|019)(\d{7,8})$/;
2253
+ const landlinePattern = /^(02|031|032|033|041|042|043|044|051|052|053|054|055|061|062|063|064)(\d{7,8})$/;
2254
+ let isValid = false;
2255
+ let isPossible = false;
2256
+ let lineType = "unknown";
2257
+ let carrier;
2258
+ if (mobilePattern.test(cleaned)) {
2259
+ isValid = true;
2260
+ isPossible = true;
2261
+ lineType = "mobile";
2262
+ const prefix = cleaned.substring(0, 3);
2263
+ switch (prefix) {
2264
+ case "010":
2265
+ carrier = "Multiple carriers";
2266
+ break;
2267
+ case "011":
2268
+ carrier = "SK Telecom";
2269
+ break;
2270
+ case "016":
2271
+ carrier = "KT";
2272
+ break;
2273
+ case "017":
2274
+ carrier = "LG U+";
2275
+ break;
2276
+ case "018":
2277
+ carrier = "SK Telecom";
2278
+ break;
2279
+ case "019":
2280
+ carrier = "LG U+";
2281
+ break;
2282
+ }
2283
+ } else if (landlinePattern.test(cleaned)) {
2284
+ isValid = true;
2285
+ isPossible = true;
2286
+ lineType = "landline";
2287
+ } else if (cleaned.length >= 10 && cleaned.length <= 11) {
2288
+ isPossible = true;
2289
+ }
2290
+ return {
2291
+ phoneNumber: cleaned,
2292
+ countryCode: "82",
2293
+ nationalNumber: cleaned,
2294
+ carrier,
2295
+ lineType,
2296
+ isValid,
2297
+ isPossible,
2298
+ region: "KR"
2299
+ };
2300
+ }
2301
+ isNumberBlocked(phoneNumber) {
2302
+ return this.blockedNumbers.has(phoneNumber);
2303
+ }
2304
+ isRateLimited(phoneNumber) {
2305
+ const timestamps = this.rateLimitTracker.get(phoneNumber) || [];
2306
+ const rateWindow = this.options.rateLimitMinutes * 60 * 1e3;
2307
+ const now = /* @__PURE__ */ new Date();
2308
+ const recentAttempts = timestamps.filter((ts) => now.getTime() - ts.getTime() < rateWindow);
2309
+ return recentAttempts.length >= 1;
2310
+ }
2311
+ isDailyLimitExceeded(phoneNumber) {
2312
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2313
+ const dailyData = this.dailyAttemptTracker.get(phoneNumber);
2314
+ if (!dailyData || dailyData.date !== today) {
2315
+ return false;
2316
+ }
2317
+ return dailyData.count >= this.options.maxDailyAttempts;
2318
+ }
2319
+ updateRateLimit(phoneNumber) {
2320
+ const timestamps = this.rateLimitTracker.get(phoneNumber) || [];
2321
+ timestamps.push(/* @__PURE__ */ new Date());
2322
+ this.rateLimitTracker.set(phoneNumber, timestamps);
2323
+ }
2324
+ updateDailyAttempts(phoneNumber) {
2325
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2326
+ const dailyData = this.dailyAttemptTracker.get(phoneNumber);
2327
+ if (!dailyData || dailyData.date !== today) {
2328
+ this.dailyAttemptTracker.set(phoneNumber, { date: today, count: 1 });
2329
+ } else {
2330
+ dailyData.count++;
2331
+ }
2332
+ }
2333
+ validateCode(expected, provided) {
2334
+ return expected === provided.replace(/\s/g, "");
2335
+ }
2336
+ generateVerificationCode() {
2337
+ const length = this.options.codeLength;
2338
+ let code = "";
2339
+ for (let i = 0; i < length; i++) {
2340
+ code += Math.floor(Math.random() * 10).toString();
2341
+ }
2342
+ return code;
2343
+ }
2344
+ generateRequestId() {
2345
+ return `phone_verify_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2346
+ }
2347
+ };
2348
+
2349
+ // src/services/channel.service.ts
2350
+ var ChannelService = class {
2351
+ constructor() {
2352
+ this.channels = /* @__PURE__ */ new Map();
2353
+ this.senderNumbers = /* @__PURE__ */ new Map();
2354
+ }
2355
+ async createChannel(channel) {
2356
+ const newChannel = {
2357
+ ...channel,
2358
+ id: this.generateChannelId(),
2359
+ createdAt: /* @__PURE__ */ new Date(),
2360
+ updatedAt: /* @__PURE__ */ new Date()
2361
+ };
2362
+ this.channels.set(newChannel.id, newChannel);
2363
+ return newChannel;
2364
+ }
2365
+ async getChannel(channelId) {
2366
+ return this.channels.get(channelId) || null;
2367
+ }
2368
+ async listChannels(providerId) {
2369
+ const channels = Array.from(this.channels.values());
2370
+ if (providerId) {
2371
+ return channels.filter((c) => c.providerId === providerId);
2372
+ }
2373
+ return channels;
2374
+ }
2375
+ async updateChannel(channelId, updates) {
2376
+ const channel = this.channels.get(channelId);
2377
+ if (!channel) {
2378
+ throw new Error(`Channel ${channelId} not found`);
2379
+ }
2380
+ const updatedChannel = {
2381
+ ...channel,
2382
+ ...updates,
2383
+ updatedAt: /* @__PURE__ */ new Date()
2384
+ };
2385
+ this.channels.set(channelId, updatedChannel);
2386
+ return updatedChannel;
2387
+ }
2388
+ async deleteChannel(channelId) {
2389
+ this.channels.delete(channelId);
2390
+ for (const [key, senderNumber] of this.senderNumbers.entries()) {
2391
+ if (senderNumber.channelId === channelId) {
2392
+ this.senderNumbers.delete(key);
2393
+ }
2394
+ }
2395
+ }
2396
+ async addSenderNumber(channelId, phoneNumber, name) {
2397
+ const channel = this.channels.get(channelId);
2398
+ if (!channel) {
2399
+ throw new Error(`Channel ${channelId} not found`);
2400
+ }
2401
+ const senderNumber = {
2402
+ phoneNumber,
2403
+ name,
2404
+ status: "PENDING" /* PENDING */,
2405
+ channelId
2406
+ };
2407
+ this.senderNumbers.set(phoneNumber, senderNumber);
2408
+ return senderNumber;
2409
+ }
2410
+ async verifySenderNumber(phoneNumber) {
2411
+ const senderNumber = this.senderNumbers.get(phoneNumber);
2412
+ if (!senderNumber) {
2413
+ return {
2414
+ success: false,
2415
+ status: "not_found",
2416
+ error: "Sender number not found"
2417
+ };
2418
+ }
2419
+ const verificationCode = Math.floor(Math.random() * 9e5) + 1e5;
2420
+ senderNumber.verifiedAt = /* @__PURE__ */ new Date();
2421
+ senderNumber.status = "VERIFIED" /* VERIFIED */;
2422
+ this.senderNumbers.set(phoneNumber, senderNumber);
2423
+ return {
2424
+ success: true,
2425
+ status: "verified",
2426
+ verificationCode: verificationCode.toString()
2427
+ };
2428
+ }
2429
+ async getSenderNumbers(channelId) {
2430
+ const senderNumbers = Array.from(this.senderNumbers.values());
2431
+ if (channelId) {
2432
+ return senderNumbers.filter((s) => s.channelId === channelId);
2433
+ }
2434
+ return senderNumbers;
2435
+ }
2436
+ generateChannelId() {
2437
+ return `ch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2438
+ }
2439
+ };
2440
+ // Annotate the CommonJS export names for ESM import in node:
2441
+ 0 && (module.exports = {
2442
+ ActionType,
2443
+ BusinessVerifier,
2444
+ ChannelCRUD,
2445
+ ChannelCreateRequestSchema,
2446
+ ChannelFiltersSchema,
2447
+ ChannelService,
2448
+ ChannelStatus,
2449
+ ChannelType,
2450
+ DocumentStatus,
2451
+ DocumentType,
2452
+ KakaoChannelManager,
2453
+ KakaoSenderNumberManager,
2454
+ NumberVerifier,
2455
+ PermissionManager,
2456
+ PermissionScope,
2457
+ ResourceType,
2458
+ SenderNumberCategory,
2459
+ SenderNumberCreateRequestSchema,
2460
+ SenderNumberFiltersSchema,
2461
+ SenderNumberStatus,
2462
+ VerificationMethod,
2463
+ VerificationStatus,
2464
+ VerificationType
2465
+ });
2466
+ //# sourceMappingURL=index.cjs.map