@dawntech/dispatcher 0.2.9 → 0.2.11
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.d.mts +71 -67
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -65,6 +65,10 @@ declare namespace Vnd {
|
|
|
65
65
|
content: unknown;
|
|
66
66
|
date: string;
|
|
67
67
|
status?: string;
|
|
68
|
+
reason?: {
|
|
69
|
+
code: number;
|
|
70
|
+
description: string;
|
|
71
|
+
};
|
|
68
72
|
[key: string]: unknown;
|
|
69
73
|
}
|
|
70
74
|
interface Contact {
|
|
@@ -127,6 +131,63 @@ interface DateInterval {
|
|
|
127
131
|
type PartiallyOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
128
132
|
type PartiallyRequiredStrict<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
129
133
|
|
|
134
|
+
declare class DispatcherRepository {
|
|
135
|
+
private static readonly INDEXED_STATUSES;
|
|
136
|
+
private static readonly INDEXED_STATES;
|
|
137
|
+
private client;
|
|
138
|
+
private keyPrefix;
|
|
139
|
+
constructor(dispatcherId: string, redisUrl: string);
|
|
140
|
+
setup(): Promise<void>;
|
|
141
|
+
teardown(): Promise<void>;
|
|
142
|
+
get redis(): IORedis;
|
|
143
|
+
getManifestKey(): string;
|
|
144
|
+
getKey(messageId: string): string;
|
|
145
|
+
getStateKey(state: MessageState): string;
|
|
146
|
+
getStatusKey(status: MessageStatus): string;
|
|
147
|
+
getContactKey(contactId: string): string;
|
|
148
|
+
getDescriptorKey(descriptorId: string): string;
|
|
149
|
+
getQueueKey(name: 'scheduled' | 'queued' | 'dispatched' | 'expiration' | 'retention'): string;
|
|
150
|
+
upsertMessage(message: Message, ttl?: number): Promise<void>;
|
|
151
|
+
getMessage(messageId: string): Promise<Message | null>;
|
|
152
|
+
getMessages(filter: {
|
|
153
|
+
status?: MessageStatus;
|
|
154
|
+
state?: MessageState;
|
|
155
|
+
size?: number;
|
|
156
|
+
skip?: number;
|
|
157
|
+
}): Promise<Message[]>;
|
|
158
|
+
getQueueSize(): Promise<number>;
|
|
159
|
+
evictOldest(count: number): Promise<number>;
|
|
160
|
+
getExpiredMessages(count?: number): Promise<string[]>;
|
|
161
|
+
getRetentionMessages(count?: number): Promise<string[]>;
|
|
162
|
+
incrementMetric(key: string, value?: number): Promise<number>;
|
|
163
|
+
getMetric(key: string): Promise<number>;
|
|
164
|
+
deleteMessage(messageId: string): Promise<void>;
|
|
165
|
+
private deleteMessageData;
|
|
166
|
+
countMessages(filter: {
|
|
167
|
+
status?: MessageStatus;
|
|
168
|
+
state?: MessageState;
|
|
169
|
+
}): Promise<number>;
|
|
170
|
+
getMetrics(descriptorId?: string): Promise<{
|
|
171
|
+
cumulative: {
|
|
172
|
+
dispatched: number;
|
|
173
|
+
delivered: number;
|
|
174
|
+
failed: number;
|
|
175
|
+
};
|
|
176
|
+
queues: {
|
|
177
|
+
queued: number;
|
|
178
|
+
scheduled: number;
|
|
179
|
+
dispatched: number;
|
|
180
|
+
};
|
|
181
|
+
status: Record<string, number>;
|
|
182
|
+
}>;
|
|
183
|
+
getDescriptors(): Promise<{
|
|
184
|
+
id: string;
|
|
185
|
+
count: number;
|
|
186
|
+
}[]>;
|
|
187
|
+
writeManifest(manifest: DispatcherManifest): Promise<void>;
|
|
188
|
+
getManifest(): Promise<DispatcherManifest | null>;
|
|
189
|
+
}
|
|
190
|
+
|
|
130
191
|
type Contact = Omit<Vnd.Iris.Contact, 'identity'>;
|
|
131
192
|
type MessageData = {
|
|
132
193
|
type: string;
|
|
@@ -266,6 +327,7 @@ interface DispatcherOptions {
|
|
|
266
327
|
duration: number;
|
|
267
328
|
};
|
|
268
329
|
};
|
|
330
|
+
repository?: DispatcherRepository;
|
|
269
331
|
}
|
|
270
332
|
interface QueryFilter {
|
|
271
333
|
contactId?: string;
|
|
@@ -347,7 +409,10 @@ declare class Blip {
|
|
|
347
409
|
* @param contactId - Contact identifier to filter notifications
|
|
348
410
|
* @returns DispatchState or null if not found
|
|
349
411
|
*/
|
|
350
|
-
getDispatchState(messageId: string, contactId: string): Promise<
|
|
412
|
+
getDispatchState(messageId: string, contactId: string): Promise<{
|
|
413
|
+
state: DispatchState;
|
|
414
|
+
reason?: string;
|
|
415
|
+
} | null>;
|
|
351
416
|
/**
|
|
352
417
|
* Get the first message received after a specific message ID
|
|
353
418
|
* @param contactId - Contact identifier
|
|
@@ -397,63 +462,6 @@ declare class DispatcherDescriptor {
|
|
|
397
462
|
emit(event: CallbackEvent, message: Message, api: Blip, dispatcherId: string): void;
|
|
398
463
|
}
|
|
399
464
|
|
|
400
|
-
declare class DispatcherRepository {
|
|
401
|
-
private static readonly INDEXED_STATUSES;
|
|
402
|
-
private static readonly INDEXED_STATES;
|
|
403
|
-
private client;
|
|
404
|
-
private keyPrefix;
|
|
405
|
-
constructor(dispatcherId: string, redisUrl: string);
|
|
406
|
-
setup(): Promise<void>;
|
|
407
|
-
teardown(): Promise<void>;
|
|
408
|
-
get redis(): IORedis;
|
|
409
|
-
getManifestKey(): string;
|
|
410
|
-
getKey(messageId: string): string;
|
|
411
|
-
getStateKey(state: MessageState): string;
|
|
412
|
-
getStatusKey(status: MessageStatus): string;
|
|
413
|
-
getContactKey(contactId: string): string;
|
|
414
|
-
getDescriptorKey(descriptorId: string): string;
|
|
415
|
-
getQueueKey(name: 'scheduled' | 'queued' | 'dispatched' | 'expiration' | 'retention'): string;
|
|
416
|
-
upsertMessage(message: Message, ttl?: number): Promise<void>;
|
|
417
|
-
getMessage(messageId: string): Promise<Message | null>;
|
|
418
|
-
getMessages(filter: {
|
|
419
|
-
status?: MessageStatus;
|
|
420
|
-
state?: MessageState;
|
|
421
|
-
size?: number;
|
|
422
|
-
skip?: number;
|
|
423
|
-
}): Promise<Message[]>;
|
|
424
|
-
getQueueSize(): Promise<number>;
|
|
425
|
-
evictOldest(count: number): Promise<number>;
|
|
426
|
-
getExpiredMessages(count?: number): Promise<string[]>;
|
|
427
|
-
getRetentionMessages(count?: number): Promise<string[]>;
|
|
428
|
-
incrementMetric(key: string, value?: number): Promise<number>;
|
|
429
|
-
getMetric(key: string): Promise<number>;
|
|
430
|
-
deleteMessage(messageId: string): Promise<void>;
|
|
431
|
-
private deleteMessageData;
|
|
432
|
-
countMessages(filter: {
|
|
433
|
-
status?: MessageStatus;
|
|
434
|
-
state?: MessageState;
|
|
435
|
-
}): Promise<number>;
|
|
436
|
-
getMetrics(descriptorId?: string): Promise<{
|
|
437
|
-
cumulative: {
|
|
438
|
-
dispatched: number;
|
|
439
|
-
delivered: number;
|
|
440
|
-
failed: number;
|
|
441
|
-
};
|
|
442
|
-
queues: {
|
|
443
|
-
queued: number;
|
|
444
|
-
scheduled: number;
|
|
445
|
-
dispatched: number;
|
|
446
|
-
};
|
|
447
|
-
status: Record<string, number>;
|
|
448
|
-
}>;
|
|
449
|
-
getDescriptors(): Promise<{
|
|
450
|
-
id: string;
|
|
451
|
-
count: number;
|
|
452
|
-
}[]>;
|
|
453
|
-
writeManifest(manifest: DispatcherManifest): Promise<void>;
|
|
454
|
-
getManifest(): Promise<DispatcherManifest | null>;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
465
|
declare class DispatcherQuery {
|
|
458
466
|
private repository;
|
|
459
467
|
constructor(repository: DispatcherRepository);
|
|
@@ -464,14 +472,13 @@ declare class DispatcherQuery {
|
|
|
464
472
|
|
|
465
473
|
declare class Dispatcher {
|
|
466
474
|
readonly id: string;
|
|
467
|
-
|
|
475
|
+
readonly repository: DispatcherRepository;
|
|
468
476
|
private stateMachine;
|
|
469
477
|
private api;
|
|
470
478
|
private callbacks;
|
|
471
479
|
private descriptors;
|
|
472
480
|
private queue;
|
|
473
481
|
private worker;
|
|
474
|
-
private redis;
|
|
475
482
|
private queueName;
|
|
476
483
|
private maxRetries;
|
|
477
484
|
private retryIntervals;
|
|
@@ -484,7 +491,8 @@ declare class Dispatcher {
|
|
|
484
491
|
private timeoutMonitorRunning;
|
|
485
492
|
private timeoutTimer;
|
|
486
493
|
readonly query: DispatcherQuery;
|
|
487
|
-
|
|
494
|
+
get redis(): IORedis;
|
|
495
|
+
constructor(id: string, redisUrl: string, connection: ConnectionConfig, options?: DispatcherOptions);
|
|
488
496
|
setup(): Promise<void>;
|
|
489
497
|
private _doSetup;
|
|
490
498
|
teardown(): Promise<void>;
|
|
@@ -531,7 +539,7 @@ type MonitorAlert = {
|
|
|
531
539
|
};
|
|
532
540
|
declare class DispatcherMonitor extends EventEmitter {
|
|
533
541
|
private id;
|
|
534
|
-
private
|
|
542
|
+
private dispatcher;
|
|
535
543
|
private options;
|
|
536
544
|
private queue;
|
|
537
545
|
private worker;
|
|
@@ -540,13 +548,9 @@ declare class DispatcherMonitor extends EventEmitter {
|
|
|
540
548
|
private lastAlerts;
|
|
541
549
|
private activeAlerts;
|
|
542
550
|
private isRunning;
|
|
543
|
-
constructor(
|
|
551
|
+
constructor(dispatcher: Dispatcher, options: MonitorOptions);
|
|
544
552
|
start(): Promise<void>;
|
|
545
553
|
stop(): Promise<void>;
|
|
546
|
-
/**
|
|
547
|
-
* Independent metrics collection directly from Repository
|
|
548
|
-
*/
|
|
549
|
-
private collectMetrics;
|
|
550
554
|
private check;
|
|
551
555
|
private cleanHistory;
|
|
552
556
|
private evaluateRule;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var tt=Object.create;var k=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var it=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty;var nt=(c,t)=>()=>(t||c((t={exports:{}}).exports,t),t.exports),at=(c,t)=>{for(var e in t)k(c,e,{get:t[e],enumerable:!0})},_=(c,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of st(t))!rt.call(c,i)&&i!==e&&k(c,i,{get:()=>t[i],enumerable:!(s=et(t,i))||s.enumerable});return c};var z=(c,t,e)=>(e=c!=null?tt(it(c)):{},_(t||!c||!c.__esModule?k(e,"default",{value:c,enumerable:!0}):e,c)),ot=c=>_(k({},"__esModule",{value:!0}),c);var Y=nt((Lt,ut)=>{ut.exports={name:"@dawntech/dispatcher",version:"0.2.9",description:"A TypeScript Node.js package for sending push messages in conversational chatbots on the Blip platform.",main:"dist/index.js",module:"dist/index.mjs",types:"dist/index.d.ts",exports:{".":{import:{types:"./dist/index.d.mts",default:"./dist/index.mjs"},require:{types:"./dist/index.d.ts",default:"./dist/index.js"}}},scripts:{start:"node dist/index.js",build:"tsup","build:watch":"tsup --watch",dev:"tsx watch src/server.ts","test:basic":"tsx tests/integration/scenarios/1-basic-send.ts","test:contact":"tsx tests/integration/scenarios/2-contact-update.ts","test:schedule":"tsx tests/integration/scenarios/3-scheduling.ts","test:status":"tsx tests/integration/scenarios/4-status-config.ts","test:intent":"tsx tests/integration/scenarios/5-intent.ts","test:rate-limit":"tsx tests/integration/scenarios/6-rate-limiting.ts","test:high-load":"tsx tests/integration/scenarios/7-high-load.ts","test:retries":"tsx tests/integration/scenarios/8-retries.ts","test:expiry":"tsx tests/integration/scenarios/9-expiration.ts","test:cluster":"tsx tests/integration/scenarios/10-cluster.ts","test:monitor":"tsx tests/integration/scenarios/11-monitor.ts",test:"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","test:lint":'eslint "src/**/*.ts" "tests/**/*.ts"',"test:blip-api":"tsx tests/blip-api.ts",clean:"rm -rf dist",setup:"bash scripts/setup.sh","docker:up":"docker-compose up -d","docker:down":"docker-compose down","docker:logs":"docker-compose logs -f","docker:redis-cli":"docker-compose exec redis redis-cli","docker:clean":"docker-compose down -v","docker:restart":"docker-compose restart","docker:tools":"docker-compose --profile tools up -d",format:'prettier --write "src/**/*.ts" "tests/**/*.ts"',"format:check":'prettier --check "src/**/*.ts" "tests/**/*.ts"',prepublishOnly:"npm run build"},packageManager:"npm@11.8.0",devDependencies:{"@types/body-parser":"^1.19.6","@types/cors":"^2.8.19","@types/debug":"^4.1.12","@types/express":"^5.0.6","@types/ioredis":"^4.28.10","@types/jest":"^29.5.14","@types/lodash":"^4.17.20","@types/node":"^20.19.24",eslint:"^9.39.3",husky:"^9.1.7",jest:"^29.7.0","lint-staged":"^16.2.6",prettier:"^3.6.2","ts-jest":"^29.2.5",tsup:"^8.5.1",tsx:"^4.7.0",typescript:"^5.3.0","typescript-eslint":"^8.56.1"},keywords:[],author:"",license:"ISC",engines:{node:">=24.0.0"},dependencies:{axios:"^1.13.1","body-parser":"^2.2.2",bullmq:"^5.67.1",cors:"^2.8.6",debug:"^4.4.3",dotenv:"^17.2.3",express:"^5.2.1",ioredis:"^5.9.2",lodash:"^4.17.21","rate-limiter-flexible":"^9.0.1",redis:"^5.9.0",uuid:"^13.0.0"}}});var lt={};at(lt,{Blip:()=>T,BlipError:()=>D,Channel:()=>H,DispatchState:()=>G,Dispatcher:()=>O,DispatcherDescriptor:()=>x,DispatcherMonitor:()=>q,DispatcherRepository:()=>F,MessageState:()=>M,MessageStatus:()=>w,Vnd:()=>f,Weekdays:()=>V,enableLogger:()=>j,getLogger:()=>m});module.exports=ot(lt);var x=class{constructor(t,e,s){this.callbacks={};this.id=t,this.transformFn=e,this.contactIdTransform=s?.toContactId||(i=>i),this.options=s}transform(t){return this.transformFn(t)}toContactId(t){return this.contactIdTransform(t)}get messageOptions(){return this.options}on(t,e){let s=this.options?.finalStatus||"DELIVERED";if(t==="read"&&s!=="READ"&&s!=="REPLIED")throw new Error(`Cannot listen to 'read' event when finalStatus is '${s}'. Set finalStatus to 'READ' or 'REPLIED'.`);if(t==="replied"&&s!=="REPLIED")throw new Error(`Cannot listen to 'replied' event when finalStatus is '${s}'. Set finalStatus to 'REPLIED'.`);return this.callbacks[t]=e,this}emit(t,e,s,i){this.callbacks[t]?.(e,s,i)}};var W=require("uuid"),K=require("bullmq");var M=(r=>(r.INIT="INIT",r.DISPATCHED="DISPATCHED",r.SCHEDULED="SCHEDULED",r.QUEUED="QUEUED",r.FINAL="FINAL",r))(M||{}),w=(o=>(o.INIT="INIT",o.PENDING="PENDING",o.SENDING="SENDING",o.DELIVERED="DELIVERED",o.READ="READ",o.REPLIED="REPLIED",o.FAILED="FAILED",o.CANCELED="CANCELED",o))(w||{}),G=(r=>(r.ACCEPTED="accepted",r.DISPATCHED="dispatched",r.RECEIVED="received",r.CONSUMED="consumed",r.FAILED="failed",r))(G||{}),H=(p=>(p.BLIP_CHAT="BLIP_CHAT",p.EMAIL="EMAIL",p.MESSENGER="MESSENGER",p.SKYPE="SKYPE",p.SMS_TAKE="SMS_TAKE",p.SMS_TANGRAM="SMS_TANGRAM",p.TELEGRAM="TELEGRAM",p.WHATSAPP="WHATSAPP",p.INSTAGRAM="INSTAGRAM",p.GOOGLE_RCS="GOOGLE_RCS",p.MICROSOFT_TEAMS="MICROSOFT_TEAMS",p.APPLE_BUSINESS_CHAT="APPLE_BUSINESS_CHAT",p.WORKPLACE="WORKPLACE",p))(H||{}),V=(n=>(n[n.MONDAY=1]="MONDAY",n[n.TUESDAY=2]="TUESDAY",n[n.WEDNESDAY=4]="WEDNESDAY",n[n.THURSDAY=8]="THURSDAY",n[n.FRIDAY=16]="FRIDAY",n[n.SATURDAY=32]="SATURDAY",n[n.SUNDAY=64]="SUNDAY",n))(V||{});var A=z(require("debug")),P={debug:"debug",info:"info",warn:"warn",error:"error"},Q=new Map;function ct(c){if(c==null)return c;if(typeof c=="object")try{return JSON.stringify(c,null,2)}catch{return c}return c}function R(c){return(...t)=>{let e=t.map(ct);c(...e)}}function m(c){if(!Q.has(c)){let t=(0,A.default)(`${c}:${P.debug}`),e=(0,A.default)(`${c}:${P.info}`),s=(0,A.default)(`${c}:${P.warn}`),i=(0,A.default)(`${c}:${P.error}`);i.log=console.error.bind(console);let r={debug:R(t),info:R(e),warn:R(s),error:R(i)};Q.set(c,r)}return Q.get(c)}function j(c){let t=A.default.disable();A.default.enable(`${t},${c}`)}var J=m("StateMachine"),L=class{constructor(t,e,s){this.id=t;this.repository=e;this.emit=s}async transition(t,e,s,i){let r=t.state,a=t.status;r==="FINAL"&&e!=="FINAL"&&J.warn(`[transition] Attempting to move from FINAL back to ${e}`,{messageId:t.messageId}),t.state=e,t.status=s,i&&Object.assign(t,i);let n=new Date().toISOString();return s==="SENDING"&&!t.acceptedAt&&a!=="SENDING"&&(t.acceptedAt=n),s==="DELIVERED"&&!t.deliveredAt&&(t.deliveredAt=n),s==="READ"&&!t.readAt&&(t.readAt=n),s==="REPLIED"&&!t.repliedAt&&(t.repliedAt=n,t.readAt||(t.readAt=n)),s==="FAILED"&&!t.failedAt&&(t.failedAt=n),e==="DISPATCHED"&&!t.sentAt&&"PENDING",await this.repository.upsertMessage(t),a!==s&&(s==="REPLIED"&&a!=="READ"&&this.emit("read",t),this.emitStatusEvent(t,s)),e==="SCHEDULED"&&r!=="SCHEDULED"&&this.emit("scheduled",t),J.debug(`[transition] ${t.messageId} : ${r}/${a} -> ${e}/${s}`),t}emitStatusEvent(t,e){switch(e){case"SENDING":this.emit("sending",t);break;case"DELIVERED":this.emit("delivered",t);break;case"READ":this.emit("read",t);break;case"REPLIED":this.emit("replied",t);break;case"FAILED":this.emit("failed",t);break;case"CANCELED":this.emit("canceled",t);break}}};var B=z(require("axios")),C=require("uuid");var f;(t=>{let c;(i=>{let e;(g=>(g.GET="get",g.SET="set",g.DELETE="delete",g.OBSERVE="observe",g.SUBSCRIBE="subscribe",g.MERGE="merge"))(e=i.Method||(i.Method={}));let s;(n=>(n.SUCCESS="success",n.FAILURE="failure"))(s=i.Status||(i.Status={}))})(c=t.Lime||(t.Lime={}))})(f||(f={}));var l=m("Blip"),T=class{constructor(t,e,s=3e4){let i=`https://${t}.http.msging.net`;this.client=B.default.create({baseURL:i,timeout:s,headers:{"Content-Type":"application/json",Authorization:e}}),this.client.interceptors.response.use(r=>r,r=>{if(r.response){let a=r.response.data?.reason||{code:r.response.status,description:r.response.statusText||"Unknown error"};throw new D(a.description,a.code)}else throw r.request?new D("No response from server",0):new D(r.message,0)})}async postCommand(t){let e={...t,id:t.id||(0,C.v4)()};l.debug("[postCommand] payload",e);let i=(await this.client.post("/commands",e)).data;if(i.status!==f.Lime.Status.SUCCESS)throw l.error("[postCommand] failed",{method:e.method,uri:e.uri,status:i.status,reason:i.reason}),new D(i.reason?.description||"Command failed",i.reason?.code||0);return l.debug("[postCommand] succeeded",e.uri),i}async postMessage(t){let e={...t,id:t.id||(0,C.v4)()};return l.info("[postMessage] payload",e),(await this.client.post("/messages",e)).data}async mergeContact(t,e){l.info("[mergeContact] called with",{contactId:t,data:e});let s={...e,identity:t},i={method:f.Lime.Method.MERGE,uri:"/contacts",type:"application/vnd.lime.contact+json",resource:s};await this.postCommand(i)}async sendMessage(t,e,s){l.info("[sendMessage] called with",{contactId:t,message:e,id:s});let i=s||(0,C.v4)(),r={id:i,to:t,type:e.type,content:e.content};return await this.postMessage(r),l.info("[sendMessage] sent",{contactId:t,messageId:i}),i}async getDispatchState(t,e){l.info("[getDispatchState] called with",{messageId:t,contactId:e});let s={method:f.Lime.Method.GET,uri:`/threads/${e}?$take=100`,to:"postmaster@msging.net"};try{let i=await this.postCommand(s);if(!i.resource||!i.resource.items||i.resource.items.length===0)return l.debug("[getDispatchState] no messages found in thread",{messageId:t,contactId:e}),null;let r=i.resource.items.find(n=>n.id===t);if(!r)return l.debug("[getDispatchState] message not found in recent thread",{messageId:t,contactId:e}),null;let a=r.status;return l.info("[getDispatchState] state retrieved",{messageId:t,contactId:e,state:a}),a}catch(i){if(i instanceof D&&i.code===67)return l.debug("[getDispatchState] resource not found",{messageId:t,contactId:e}),null;throw l.error("[getDispatchState] failed",{messageId:t,contactId:e,error:i}),i}}async getMessageAfter(t,e){l.info("[getMessageAfter] called with",{contactId:t,messageId:e});let s=e,i=0,r=10;for(;i<r;){let a={method:f.Lime.Method.GET,uri:`/threads/${t}?$skip=0&$take=1&$order=asc&messageId=${s}`,to:"postmaster@msging.net"};try{let n=await this.postCommand(a);if(!n.resource||!n.resource.items||n.resource.items.length===0)return l.debug("[getMessageAfter] no message found after",{contactId:t,messageId:s}),null;let o=n.resource.items[0];if(o.direction==="received")return l.info("[getMessageAfter] found received message",{contactId:t,messageId:s,nextMessageId:o.id}),o;l.debug("[getMessageAfter] skipping sent message",{contactId:t,messageId:o.id}),s=o.id,i++}catch(n){if(n instanceof D&&n.code===67)return l.debug("[getMessageAfter] resource not found",{contactId:t,messageId:s}),null;throw l.error("[getMessageAfter] failed",{contactId:t,messageId:s,error:n}),n}}return l.warn("[getMessageAfter] max traversal attempts reached",{contactId:t,startMessageId:e}),null}async sendEvent(t,e,s,i){l.info("[sendEvent] called with",{contactId:t,category:e,action:s,extras:i});let r={to:"postmaster@analytics.msging.net",method:f.Lime.Method.SET,type:"application/vnd.iris.eventTrack+json",uri:"/event-track",resource:{category:e,action:s,contact:{identity:t},extras:i}};await this.postCommand(r)}async setState(t,e,s="onboarding"){l.info("[setState] called with",{contactId:t,botId:e,stateId:s});let i={uri:`/flow-id?shortName=${e}`,to:"postmaster@builder.msging.net",method:f.Lime.Method.GET},r=await this.postCommand(i);if(!r.resource)throw l.error("[setState] flow ID not found",{botId:e}),new D(`Flow ID not found for bot: ${e}`,404);let a=r.resource,n={method:f.Lime.Method.SET,uri:`/contexts/${t}/stateid@${a}`,resource:s,type:"text/plain"};await this.postCommand(n);let o={method:f.Lime.Method.SET,uri:`/contexts/${t}/master-state`,resource:`${e}@msging.net`,type:"text/plain"};await this.postCommand(o)}},D=class c extends Error{constructor(t,e){super(t),this.name="BlipError",this.code=e,Object.setPrototypeOf(this,c.prototype)}};var dt=m("DispatcherQuery"),N=class{constructor(t){this.repository=t}get client(){return this.repository.redis}async query(t){let e=[];if(t.contactId&&e.push(this.repository.getContactKey(t.contactId)),t.descriptorId&&e.push(this.repository.getDescriptorKey(t.descriptorId)),t.status){let u=Array.isArray(t.status)?t.status:[t.status];u.length===1?e.push(this.repository.getStatusKey(u[0])):u.length>1&&e.push(this.repository.getStatusKey(u[0]))}if(t.state){let u=Array.isArray(t.state)?t.state:[t.state];u.length===1&&e.push(this.repository.getStateKey(u[0]))}let s=[];if(e.length>0)s=await this.client.sinter(e);else{let u=Object.values(w).map(d=>this.repository.getStatusKey(d));s=await this.client.sunion(u)}let i=t.skip??0,r=t.size??50,a=s.slice(i,i+r),n=[],o=[];for(let u of a){let d=await this.repository.getMessage(u);if(d){if(t.status&&!(Array.isArray(t.status)?t.status:[t.status]).includes(d.status)||t.state&&!(Array.isArray(t.state)?t.state:[t.state]).includes(d.state))continue;n.push(d)}else o.push(u)}return o.length>0&&this.cleanupIndices(o,t),n}async cleanupIndices(t,e){let s=this.client.pipeline();e.contactId&&s.srem(this.repository.getContactKey(e.contactId),t),e.descriptorId&&s.srem(this.repository.getDescriptorKey(e.descriptorId),t),e.status&&(Array.isArray(e.status)?e.status:[e.status]).forEach(r=>{s.srem(this.repository.getStatusKey(r),t)}),e.state&&(Array.isArray(e.state)?e.state:[e.state]).forEach(r=>{s.srem(this.repository.getStateKey(r),t)}),await s.exec(),dt.debug("[cleanupIndices] Removed expired IDs from checked indices",{count:t.length})}};var{version:pt}=Y(),h=m("Dispatcher"),O=class{constructor(t,e,s,i){this.callbacks={};this.descriptors=new Map;this.isRunning=!1;this.setupCompleted=!1;this.setupPromise=null;this.timeoutMonitorRunning=!1;this.timeoutTimer=null;this.id=t,this.repository=e,this.redis=this.repository.redis,this.stateMachine=new L(this.id,this.repository,(r,a)=>{this.emit(r,a),this.descriptors.get(a.descriptorId)?.emit(r,a,this.api,this.id)}),this.api=new T(s.contract,s.key),this.queueName=`dispatcher-${this.id.replace(/:/g,"-")}`,this.maxRetries=i?.maxRetries??0,this.retryIntervals=i?.retryIntervals??[1*1e3,5*1e3,15*1e3],this.timeouts={pending:i?.timeouts?.pending??120*1e3,sending:i?.timeouts?.sending??120*1e3},this.retention=i?.retention??2880*60*1e3,this.pollingIntervals={scheduled:i?.pollingIntervals?.scheduled??30*1e3,pending:i?.pollingIntervals?.pending??10*1e3,sending:i?.pollingIntervals?.sending??10*1e3,delivered:i?.pollingIntervals?.delivered??1800*1e3,read:i?.pollingIntervals?.read??1800*1e3,queue:i?.pollingIntervals?.queue??1*1e3},this.query=new N(this.repository),this.queue=new K.Queue(this.queueName,{connection:this.redis,defaultJobOptions:{removeOnComplete:!0,removeOnFail:!0}}),this.worker=new K.Worker(this.queueName,async r=>{try{await this.processJob(r)}catch(a){throw h.error(`[Worker] Job ${r.name} failed`,a),a}},{connection:this.redis,concurrency:i?.batchSize||50,limiter:i?.rateLimits?.global?{max:i.rateLimits.global.points,duration:i.rateLimits.global.duration*1e3}:void 0}),this.worker.on("error",r=>h.error("[Worker] Error",r)),this.worker.on("failed",(r,a)=>h.error(`[Worker] Job ${r?.id} failed`,a))}async setup(){return this.setupPromise?this.setupPromise:(this.setupPromise=this._doSetup(),this.setupPromise)}async _doSetup(){await this.repository.setup();let t=await this.repository.getManifest();await this.repository.writeManifest({version:pt,createdAt:t?.createdAt??new Date().toISOString(),updatedAt:new Date().toISOString()}),await this.queue.waitUntilReady(),this.isRunning=!0,this.setupCompleted=!0,this.startTimeoutMonitor(),h.info("[setup] Dispatcher started (BullMQ)",{queue:this.queueName})}async teardown(){this.isRunning=!1,this.timeoutTimer&&(clearInterval(this.timeoutTimer),this.timeoutTimer=null),await this.queue.close(),await this.worker.close(),await this.repository.teardown(),h.info("[teardown] Dispatcher stopped")}on(t,e){return this.callbacks[t]=e,this}async getMetrics(){let t={total:0,byState:{},byStatus:{},cumulative:{dispatched:await this.repository.getMetric("dispatched"),delivered:await this.repository.getMetric("delivered"),failed:await this.repository.getMetric("failed")}},e=Object.values(M);for(let i of e)t.byState[i]=await this.repository.countMessages({state:i});let s=Object.values(w);for(let i of s)t.byStatus[i]=await this.repository.countMessages({status:i});return t.total=Object.values(t.byState).reduce((i,r)=>i+(r||0),0),t}emit(t,e){this.callbacks[t]?.(e,this.api,this.id)}async send(t,e,s,i){this.descriptors.set(t.id,t);let r=t.toContactId(e),a=t.transform(s),n=new Date().toISOString(),o={messageId:(0,W.v4)(),contactId:r,descriptorId:t.id,payload:a,status:"INIT",state:"INIT",createdAt:n,attempts:0,retries:this.maxRetries},d={...t.messageOptions,...i},{schedule:g,...E}=d;o.options=E,this.emit("dispatch",o),t.emit("dispatch",o,this.api,this.id);let p=this.calculateScheduledTime(g,d.shifts),y=0;if(p){o.scheduledTo=p,o.state="SCHEDULED";let I=new Date(p).getTime();y=Math.max(0,I-Date.now()),this.emit("scheduled",o),t.emit("scheduled",o,this.api,this.id),h.info("[send] message scheduled",{messageId:o.messageId,scheduledTo:p,delay:y})}else o.state="QUEUED",o.status="INIT",h.info("[send] message queued",{messageId:o.messageId});return o.expiresAt=new Date(Date.now()+(o.state==="SCHEDULED"?y+this.retention:this.retention)).toISOString(),await this.stateMachine.transition(o,o.state,o.status),await this.queue.add("send",{messageId:o.messageId},{jobId:o.messageId,delay:y,priority:1}),o}async cancel(t){let e=await this.repository.getMessage(t);if(!e)return h.warn("[cancel] message not found",{messageId:t}),!1;if(e.state==="FINAL")return h.warn("[cancel] message already final",{messageId:t,status:e.status}),!1;let s=await this.queue.getJob(t);return s&&(await s.remove(),h.info("[cancel] removed job from queue",{messageId:t})),await this.stateMachine.transition(e,"FINAL","CANCELED"),h.info("[cancel] message canceled",{messageId:t}),!0}async processJob(t){let{messageId:e}=t.data,s=await this.repository.getMessage(e);if(!s){h.warn(`[processJob] Message not found: ${e}`);return}let i=this.descriptors.get(s.descriptorId)||null;switch(t.name){case"send":await this.handleSendJob(s,i);break;case"check":await this.handleCheckJob(s,i);break;default:h.warn(`[processJob] Unknown job name: ${t.name}`)}}async handleSendJob(t,e){if(t.state==="FINAL"){h.warn("[handleSendJob] Message already final, skipping",{messageId:t.messageId});return}if(t.state==="DISPATCHED"){h.warn("[handleSendJob] Message already dispatched, scheduling check instead",{messageId:t.messageId}),await this.rescheduleCheck(t,0);return}t.lastDispatchAttemptAt=new Date().toISOString(),await this.stateMachine.transition(t,"DISPATCHED","PENDING");try{await this.api.sendMessage(t.contactId,t.payload,t.messageId),await this.handlePostSendOperations(t,t.options),t.sentAt=new Date().toISOString(),await this.stateMachine.transition(t,"DISPATCHED","PENDING"),h.info("[handleSendJob] Message sent to API",{messageId:t.messageId}),await this.repository.incrementMetric("dispatched"),await this.queue.add("check",{messageId:t.messageId},{delay:this.pollingIntervals.pending,priority:5})}catch(s){let i=s instanceof Error?s:new Error(String(s));await this.handleDispatchFailure(t,e,i)}}async handlePostSendOperations(t,e={}){let s={...e.contact||{}};if(e.intent)if(typeof e.intent=="string")s.intent=e.intent;else{s.intent=e.intent.intent;let{intent:i,...r}=e.intent;Object.entries(r).forEach(([a,n])=>{n!=null&&(s[a]=typeof n=="object"?JSON.stringify(n):String(n))})}Object.keys(s).length>0&&await this.api.mergeContact(t.contactId,s),e.state&&await this.api.setState(t.contactId,e.state.botId,e.state.stateId)}async handleCheckJob(t,e){if(t.state!=="FINAL"){if(this.checkAndHandleTimeout(t)){await this.handleTimeout(t,e);return}try{let s=await this.api.getDispatchState(t.messageId,t.contactId);if(!s){await this.rescheduleCheck(t,this.pollingIntervals.pending);return}let i=this.pollingIntervals.pending;switch(s){case"accepted":t.status!=="SENDING"&&await this.stateMachine.transition(t,t.state,"SENDING"),i=this.pollingIntervals.sending;break;case"received":case"consumed":i=await this.handleDeliveredState(t,s,i);break;case"failed":await this.handleDispatchFailure(t,e,new Error("Dispatch failed from Gateway"));return}t.state!=="FINAL"&&await this.rescheduleCheck(t,i)}catch(s){h.error("[handleCheckJob] Error",s),await this.rescheduleCheck(t,this.pollingIntervals.pending)}}}checkAndHandleTimeout(t){let e=new Date;if(t.status==="PENDING"){let s=t.lastDispatchAttemptAt||t.sentAt||t.createdAt;if(e.getTime()-new Date(s).getTime()>this.timeouts.pending)return!0}return!!(t.status==="SENDING"&&t.acceptedAt&&e.getTime()-new Date(t.acceptedAt).getTime()>this.timeouts.sending)}async handleTimeout(t,e){await this.stateMachine.transition(t,"FINAL","FAILED",{error:"Timeout Exceeded"}),h.info("[handleTimeout] Message timed out",{messageId:t.messageId})}startTimeoutMonitor(){this.timeoutTimer||(this.timeoutTimer=setInterval(()=>{this.timeoutMonitorRunning||(this.timeoutMonitorRunning=!0,this._runTimeoutMonitorCycle().finally(()=>{this.timeoutMonitorRunning=!1}))},1e4))}async _runTimeoutMonitorCycle(){try{let t=await this.repository.getMessages({status:"PENDING"}),e=await this.repository.getMessages({status:"SENDING"});for(let i of[...t,...e])if(this.checkAndHandleTimeout(i)){let r=await this.repository.getMessage(i.messageId);if(r&&r.state!=="FINAL"&&(r.status==="PENDING"||r.status==="SENDING")){let a=this.descriptors.get(r.descriptorId)||null;await this.handleTimeout(r,a)}}let s=await this.repository.getRetentionMessages(100);if(s.length>0){h.debug("[CleanupMonitor] Cleaning up expired messages",{count:s.length});for(let i of s)await this.repository.deleteMessage(i)}}catch(t){h.error("[TimeoutMonitor] Error during scan",t)}}async rescheduleCheck(t,e){await this.queue.add("check",{messageId:t.messageId},{delay:Math.max(0,e),priority:5})}async handleDeliveredState(t,e,s){if(await this.api.getMessageAfter(t.contactId,t.messageId))return t.status!=="REPLIED"&&t.status!=="READ"&&(await this.repository.incrementMetric("delivered"),await this.stateMachine.transition(t,"FINAL","REPLIED")),s;let r=e==="consumed"?"READ":"DELIVERED";t.status!==r&&(await this.repository.incrementMetric("delivered"),await this.stateMachine.transition(t,t.state,r));let a=t.options?.finalStatus||"DELIVERED";if(this.getStatusRank(t.status)>=this.getStatusRank(a))await this.stateMachine.transition(t,"FINAL",t.status);else return this.pollingIntervals.delivered;return s}async handleDispatchFailure(t,e,s){if(t.attempts=(t.attempts??0)+1,t.error=s.message,h.error("[handleDispatchFailure]",{messageId:t.messageId,attempts:t.attempts,maxRetries:this.maxRetries,error:s.message}),t.attempts<=this.maxRetries){t.retries=this.maxRetries-t.attempts;let i=this.retryIntervals[t.attempts-1]||this.retryIntervals[this.retryIntervals.length-1];await this.stateMachine.transition(t,"SCHEDULED",t.status),this.emit("retry",t),e?.emit("retry",t,this.api,this.id),await this.queue.add("send",{messageId:t.messageId},{jobId:t.messageId,delay:i,priority:1}),h.info("[handleDispatchFailure] Rescheduled retry",{messageId:t.messageId,retryDelay:i})}else t.retries=0,await this.stateMachine.transition(t,"FINAL","FAILED"),await this.repository.incrementMetric("failed")}calculateScheduledTime(t,e){if(t)return t;if(!e||e.length===0)return;let s=new Date;return this.isWithinShifts(s,e)?void 0:this.findNextShiftTime(s,e)?.toISOString()}isWithinShifts(t,e){let s=t.getDay(),i=s===0?64:Math.pow(2,s-1);for(let r of e){if((r.days&i)===0)continue;let a=r.gmt||"-3",n=parseInt(a,10),o=new Date(t.getTime()-n*60*60*1e3),u=o.getHours()*60+o.getMinutes(),[d,g]=r.start.split(":").map(Number),[E,p]=r.end.split(":").map(Number),y=d*60+g,I=E*60+p;if(u>=y&&u<I)return!0}return!1}findNextShiftTime(t,e){for(let i=0;i<=7;i++){let r=new Date(t);r.setDate(r.getDate()+i);let a=r.getDay(),n=a===0?64:Math.pow(2,a-1),o=e.filter(u=>(u.days&n)!==0);if(o.length!==0){o.sort((u,d)=>{let[g,E]=u.start.split(":").map(Number),[p,y]=d.start.split(":").map(Number);return g*60+E-(p*60+y)});for(let u of o){let d=u.gmt||"-3",g=parseInt(d,10),[E,p]=u.start.split(":").map(Number),y=new Date(r);y.setHours(E,p,0,0);let I=new Date(y.getTime()+g*60*60*1e3);if(i===0){if(I>t)return I}else return I}}}}getStatusRank(t){return{INIT:0,PENDING:1,SENDING:2,DELIVERED:3,READ:4,REPLIED:5,FAILED:6,CANCELED:6}[t]||0}static sanitizeContactId(t,e){if(t.includes("@"))return t;let s={BLIP_CHAT:"@0mn.io",EMAIL:"@mailgun.gw.msging.net",MESSENGER:"@messenger.gw.msging.net",SKYPE:"@skype.gw.msging.net",SMS_TAKE:"@take.io",SMS_TANGRAM:"@tangram.com.br",TELEGRAM:"@telegram.gw.msging.net",WHATSAPP:"@wa.gw.msging.net",INSTAGRAM:"@instagram.gw.msging.net",GOOGLE_RCS:"@googlercs.gw.msging.net",MICROSOFT_TEAMS:"@abs.gw.msging.net",APPLE_BUSINESS_CHAT:"@businesschat.gw.msging.net",WORKPLACE:"@workplace.gw.msging.net"};if(!s[e])throw new Error(`Unknown channel: ${e}`);return e==="WHATSAPP"&&t.startsWith("+")&&(t=t.slice(1)),`${t}${s[e]}`}};var X=require("events"),$=require("bullmq");var v=m("DispatcherMonitor"),q=class extends X.EventEmitter{constructor(e,s,i){super();this.history=[];this.lastAlerts={};this.activeAlerts=new Set;this.isRunning=!1;this.id=e,this.repository=s,this.options={interval:6e4,historySize:1e3,...i},this.queueName=`monitor-${this.id}`,this.queue=new $.Queue(this.queueName,{connection:s.redis,defaultJobOptions:{removeOnComplete:!0,removeOnFail:!0}}),this.worker=new $.Worker(this.queueName,async r=>{r.name==="check"&&await this.check()},{connection:s.redis}),this.worker.on("error",r=>v.error("[MonitorWorker] Error",r)),this.worker.on("failed",(r,a)=>v.error(`[MonitorWorker] Job ${r?.id} failed`,a))}async start(){this.isRunning||(v.info("[Monitor] Started"),await this.queue.obliterate({force:!0}),await this.queue.add("check",{},{repeat:{every:this.options.interval,immediately:!0}}),this.isRunning=!0)}async stop(){this.isRunning=!1,await this.queue.close(),await this.worker.close(),v.info("[Monitor] Stopped")}async collectMetrics(){let e=this.repository,s={total:0,byState:{},byStatus:{},cumulative:{dispatched:0,delivered:0,failed:0}},r=Object.values(M).map(async d=>{s.byState[d]=await e.countMessages({state:d})}),n=Object.values(w).map(async d=>{s.byStatus[d]=await e.countMessages({status:d})}),u=["dispatched","delivered","failed"].map(async d=>{s.cumulative[d]=await e.getMetric(d)});return await Promise.all([...r,...n,...u]),s.total=Object.values(s.byState).reduce((d,g)=>d+(g||0),0),s}async check(){try{let e=await this.collectMetrics(),s=Date.now();this.history.push({timestamp:s,metrics:e}),this.cleanHistory();for(let i of this.options.rules)await this.evaluateRule(i,e,s)}catch(e){v.error("[Monitor] Error during check",e)}}cleanHistory(){let e=this.options.historySize;this.history.length>e&&(this.history=this.history.slice(this.history.length-e));let s=Math.max(...this.options.rules.map(r=>r.window||0)),i=Date.now()-s-6e4;if(this.history.length>0&&this.history[0].timestamp<i){let r=this.history.findIndex(a=>a.timestamp>=i);r>0&&(this.history=this.history.slice(r))}}async evaluateRule(e,s,i){let r=`${e.type}`,a=!1,n=0,o={};switch(e.type){case"queue_size":let u=s.byState.FINAL||0;n=s.total-u,a=n>e.threshold,o={current:n,threshold:e.threshold};break;case"failure_rate":if(!e.window){v.warn("[Monitor] failure_rate rule missing window");return}let d=this.findSnapshotAt(i-e.window);if(!d)return;let g=s.cumulative.failed,E=d.metrics.cumulative.failed,p=g-E,y=s.cumulative.dispatched,I=d.metrics.cumulative.dispatched,U=y-I;U===0?n=0:n=p/U,a=n>e.threshold,o={rate:(n*100).toFixed(2)+"%",threshold:(e.threshold*100).toFixed(2)+"%",failed:p,dispatched:U,window:e.window};break}a?this.activeAlerts.has(r)?e.debounce&&!this.isDebounced(r,e.debounce)&&this.emitAlert(r,e,n,o):(this.emitAlert(r,e,n,o),this.activeAlerts.add(r)):this.activeAlerts.has(r)&&(this.resolveAlert(r,e),this.activeAlerts.delete(r))}isDebounced(e,s){if(!s)return!1;let i=this.lastAlerts[e];return i?Date.now()-i<s:!1}emitAlert(e,s,i,r){v.warn(`[Monitor] Alert triggered: ${s.type}`,r),this.lastAlerts[e]=Date.now();let a={type:s.type,message:`${s.type} exceeded threshold`,level:"warning",details:r,timestamp:new Date().toISOString()};this.emit("alert",a)}resolveAlert(e,s){v.info(`[Monitor] Alert resolved: ${s.type}`);let i={type:s.type,message:`${s.type} resolved`,level:"warning",details:{},timestamp:new Date().toISOString()};this.emit("resolved",i)}findSnapshotAt(e){if(this.history.length===0)return null;for(let s of this.history)if(s.timestamp>=e)return s;return this.history[0]}};var Z=z(require("ioredis")),S=m("Repository"),b=class b{constructor(t,e){this.client=new Z.default(e,{maxRetriesPerRequest:null}),this.keyPrefix=`dwn-dispatcher:${t}`,this.client.on("error",s=>{S.error("[client] Redis error",s)})}async setup(){if(this.client.status==="ready"){S.debug("[setup] Redis already connected, skipping");return}this.client.status==="wait"&&await this.client.connect(),S.info("[setup] Repository connected",{status:this.client.status})}async teardown(){this.client.status!=="end"&&(await this.client.quit(),S.info("[teardown] Repository disconnected"))}get redis(){return this.client}getManifestKey(){return`${this.keyPrefix}:manifest`}getKey(t){return`${this.keyPrefix}:message:${t}`}getStateKey(t){return`${this.keyPrefix}:index:state:${t.toLowerCase()}`}getStatusKey(t){return`${this.keyPrefix}:index:status:${t.toLowerCase()}`}getContactKey(t){return`${this.keyPrefix}:index:contact:${t}`}getDescriptorKey(t){return`${this.keyPrefix}:index:descriptor:${t}`}getQueueKey(t){return`${this.keyPrefix}:queue:${t.toLowerCase()}`}async upsertMessage(t,e){let s=this.getKey(t.messageId),i=JSON.stringify(t),r=this.client.pipeline();r.set(s,i),t.contactId&&r.sadd(this.getContactKey(t.contactId),t.messageId),t.descriptorId&&r.sadd(this.getDescriptorKey(t.descriptorId),t.messageId);for(let n of b.INDEXED_STATUSES){let o=this.getStatusKey(n);t.status===n?r.sadd(o,t.messageId):r.srem(o,t.messageId)}for(let n of b.INDEXED_STATES){let o=this.getStateKey(n);t.state===n?r.sadd(o,t.messageId):r.srem(o,t.messageId)}let a=Date.now()+(e||36e5*24*2);if(t.state==="SCHEDULED"&&t.scheduledTo&&(a=new Date(t.scheduledTo).getTime()+(e||0)),r.zadd(this.getQueueKey("retention"),a,t.messageId),t.state==="SCHEDULED"&&t.scheduledTo){let n=new Date(t.scheduledTo).getTime();r.zadd(this.getQueueKey("scheduled"),n,t.messageId)}else r.zrem(this.getQueueKey("scheduled"),t.messageId);if(t.state==="QUEUED"){let n=new Date(t.createdAt||Date.now()).getTime();r.zadd(this.getQueueKey("queued"),n,t.messageId)}else r.zrem(this.getQueueKey("queued"),t.messageId);if(t.state==="DISPATCHED"){let n=new Date(t.createdAt||Date.now()).getTime();r.zadd(this.getQueueKey("dispatched"),n,t.messageId)}else r.zrem(this.getQueueKey("dispatched"),t.messageId);await r.exec(),S.debug("[upsertMessage]",{messageId:t.messageId,status:t.status,state:t.state})}async getMessage(t){let e=this.getKey(t),s=await this.client.get(e);return s?JSON.parse(s):null}async getMessages(t){let e=[];if(t.state==="SCHEDULED"){let i=Date.now(),r=t.skip??0,a=t.size??0;a>0?e=await this.client.zrangebyscore(this.getQueueKey("scheduled"),0,i,"LIMIT",r,a):e=await this.client.zrangebyscore(this.getQueueKey("scheduled"),0,i)}else if(t.state==="QUEUED")e=await this.client.zrange(this.getQueueKey("queued"),t.skip??0,(t.skip??0)+(t.size?t.size-1:-1));else if(t.state==="DISPATCHED")e=await this.client.zrange(this.getQueueKey("dispatched"),t.skip??0,(t.skip??0)+(t.size?t.size-1:-1));else if(t.status){let i=await this.client.smembers(this.getStatusKey(t.status)),r=t.skip??0,a=t.size;e=a?i.slice(r,r+a):i.slice(r)}else if(t.state)try{let i=await this.client.smembers(this.getStateKey(t.state)),r=t.skip??0,a=t.size;e=a?i.slice(r,r+a):i.slice(r)}catch{return[]}else return S.warn("[getMessages] no filter provided"),[];let s=[];for(let i of e){let r=await this.getMessage(i);if(r){if(t.status&&r.status!==t.status||t.state&&r.state!==t.state)continue;s.push(r)}}return S.debug("[getMessages]",{count:s.length,filter:t}),s}async getQueueSize(){return await this.client.zcard(this.getQueueKey("dispatched"))}async evictOldest(t){if(t<=0)return 0;let e=await this.client.zpopmin(this.getQueueKey("dispatched"),t),s=0;for(let i=0;i<e.length;i+=2){let r=e[i];await this.deleteMessage(r),s++}return s}async getExpiredMessages(t=50){let e=Date.now();return await this.client.zrangebyscore(this.getQueueKey("expiration"),0,e,"LIMIT",0,t)}async getRetentionMessages(t=50){let e=Date.now();return await this.client.zrangebyscore(this.getQueueKey("retention"),0,e,"LIMIT",0,t)}async incrementMetric(t,e=1){let s=`${this.keyPrefix}:metrics:${t}`;return await this.client.incrby(s,e)}async getMetric(t){let e=`${this.keyPrefix}:metrics:${t}`,s=await this.client.get(e);return s?parseInt(s,10):0}async deleteMessage(t){let e=await this.getMessage(t);e&&await this.deleteMessageData(t,e)}async deleteMessageData(t,e){let s=this.client.pipeline();s.del(this.getKey(t)),s.zrem(this.getQueueKey("scheduled"),t),s.zrem(this.getQueueKey("queued"),t),s.zrem(this.getQueueKey("dispatched"),t),s.zrem(this.getQueueKey("expiration"),t),s.zrem(this.getQueueKey("retention"),t);for(let i of b.INDEXED_STATUSES)s.srem(this.getStatusKey(i),t);for(let i of b.INDEXED_STATES)s.srem(this.getStateKey(i),t);e.contactId&&s.srem(this.getContactKey(e.contactId),t),e.descriptorId&&s.srem(this.getDescriptorKey(e.descriptorId),t),await s.exec()}async countMessages(t){if(t.state==="SCHEDULED")return await this.client.zcard(this.getQueueKey("scheduled"));if(t.state==="QUEUED")return await this.client.zcard(this.getQueueKey("queued"));if(t.state==="DISPATCHED")return await this.client.zcard(this.getQueueKey("dispatched"));if(t.status)return await this.client.scard(this.getStatusKey(t.status));if(t.state)try{return await this.client.scard(this.getStateKey(t.state))}catch{return 0}return 0}async getMetrics(t){let e={cumulative:{dispatched:0,delivered:0,failed:0},queues:{queued:0,scheduled:0,dispatched:0},status:{}},s=async a=>{if(t){let n=this.getDescriptorKey(t);return(await this.client.sinter(a,n)).length}return await this.client.scard(a)};for(let a of b.INDEXED_STATUSES){let n=await s(this.getStatusKey(a));e.status[a]=n,a==="DELIVERED"&&(e.cumulative.delivered=n),a==="FAILED"&&(e.cumulative.failed=n)}let i=this.getStateKey("DISPATCHED"),r=await s(i);return e.cumulative.dispatched=r+e.cumulative.delivered+e.cumulative.failed,t?(e.queues.queued=await s(this.getStateKey("QUEUED")),e.queues.scheduled=await s(this.getStateKey("SCHEDULED")),e.queues.dispatched=r):(e.queues.queued=await this.client.zcard(this.getQueueKey("queued")),e.queues.scheduled=await this.client.zcard(this.getQueueKey("scheduled")),e.queues.dispatched=await this.client.zcard(this.getQueueKey("dispatched"))),e}async getDescriptors(){let t=this.getDescriptorKey("*"),e=`${this.keyPrefix}:index:descriptor:`,s=await this.client.keys(t),i=[];for(let r of s){let a=r.slice(e.length);if(a){let n=await this.client.scard(r);i.push({id:a,count:n})}}return i.sort((r,a)=>a.count-r.count),i}async writeManifest(t){let e=this.getManifestKey();await this.client.hset(e,{version:t.version,createdAt:t.createdAt,updatedAt:t.updatedAt}),S.info("[writeManifest] Manifest written",{key:e})}async getManifest(){let t=this.getManifestKey(),e=await this.client.hgetall(t);return!e||Object.keys(e).length===0?null:e}};b.INDEXED_STATUSES=["INIT","PENDING","SENDING","DELIVERED","READ","REPLIED","FAILED","CANCELED"],b.INDEXED_STATES=["INIT","DISPATCHED","SCHEDULED","QUEUED","FINAL"];var F=b;0&&(module.exports={Blip,BlipError,Channel,DispatchState,Dispatcher,DispatcherDescriptor,DispatcherMonitor,DispatcherRepository,MessageState,MessageStatus,Vnd,Weekdays,enableLogger,getLogger});
|
|
1
|
+
"use strict";var tt=Object.create;var x=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var it=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty;var nt=(c,t)=>()=>(t||c((t={exports:{}}).exports,t),t.exports),at=(c,t)=>{for(var e in t)x(c,e,{get:t[e],enumerable:!0})},_=(c,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of st(t))!rt.call(c,i)&&i!==e&&x(c,i,{get:()=>t[i],enumerable:!(s=et(t,i))||s.enumerable});return c};var z=(c,t,e)=>(e=c!=null?tt(it(c)):{},_(t||!c||!c.__esModule?x(e,"default",{value:c,enumerable:!0}):e,c)),ot=c=>_(x({},"__esModule",{value:!0}),c);var W=nt((qt,ut)=>{ut.exports={name:"@dawntech/dispatcher",version:"0.2.11",description:"A TypeScript Node.js package for sending push messages in conversational chatbots on the Blip platform.",main:"dist/index.js",module:"dist/index.mjs",types:"dist/index.d.ts",exports:{".":{import:{types:"./dist/index.d.mts",default:"./dist/index.mjs"},require:{types:"./dist/index.d.ts",default:"./dist/index.js"}}},scripts:{start:"node dist/index.js",build:"tsup","build:watch":"tsup --watch",dev:"tsx watch src/server.ts","test:basic":"tsx tests/integration/scenarios/1-basic-send.ts","test:contact":"tsx tests/integration/scenarios/2-contact-update.ts","test:schedule":"tsx tests/integration/scenarios/3-scheduling.ts","test:status":"tsx tests/integration/scenarios/4-status-config.ts","test:intent":"tsx tests/integration/scenarios/5-intent.ts","test:rate-limit":"tsx tests/integration/scenarios/6-rate-limiting.ts","test:high-load":"tsx tests/integration/scenarios/7-high-load.ts","test:retries":"tsx tests/integration/scenarios/8-retries.ts","test:expiry":"tsx tests/integration/scenarios/9-expiration.ts","test:cluster":"tsx tests/integration/scenarios/10-cluster.ts","test:monitor":"tsx tests/integration/scenarios/11-monitor.ts",test:"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","test:lint":'eslint "src/**/*.ts" "tests/**/*.ts"',"test:blip-api":"tsx tests/blip-api.ts",clean:"rm -rf dist",setup:"bash scripts/setup.sh","docker:up":"docker-compose up -d","docker:down":"docker-compose down","docker:logs":"docker-compose logs -f","docker:redis-cli":"docker-compose exec redis redis-cli","docker:clean":"docker-compose down -v","docker:restart":"docker-compose restart","docker:tools":"docker-compose --profile tools up -d",format:'prettier --write "src/**/*.ts" "tests/**/*.ts"',"format:check":'prettier --check "src/**/*.ts" "tests/**/*.ts"',prepublishOnly:"npm run build"},packageManager:"npm@11.8.0",devDependencies:{"@types/body-parser":"^1.19.6","@types/cors":"^2.8.19","@types/debug":"^4.1.12","@types/express":"^5.0.6","@types/ioredis":"^4.28.10","@types/jest":"^29.5.14","@types/lodash":"^4.17.20","@types/node":"^20.19.24",eslint:"^9.39.3",husky:"^9.1.7",jest:"^29.7.0","lint-staged":"^16.2.6",prettier:"^3.6.2","ts-jest":"^29.2.5",tsup:"^8.5.1",tsx:"^4.7.0",typescript:"^5.3.0","typescript-eslint":"^8.56.1"},keywords:[],author:"",license:"ISC",engines:{node:">=24.0.0"},dependencies:{axios:"^1.13.1","body-parser":"^2.2.2",bullmq:"^5.67.1",cors:"^2.8.6",debug:"^4.4.3",dotenv:"^17.2.3",express:"^5.2.1",ioredis:"^5.9.2",lodash:"^4.17.21","rate-limiter-flexible":"^9.0.1",redis:"^5.9.0",uuid:"^13.0.0"}}});var ht={};at(ht,{Blip:()=>k,BlipError:()=>D,Channel:()=>H,DispatchState:()=>G,Dispatcher:()=>K,DispatcherDescriptor:()=>R,DispatcherMonitor:()=>$,DispatcherRepository:()=>T,MessageState:()=>A,MessageStatus:()=>b,Vnd:()=>y,Weekdays:()=>V,enableLogger:()=>j,getLogger:()=>m});module.exports=ot(ht);var R=class{constructor(t,e,s){this.callbacks={};this.id=t,this.transformFn=e,this.contactIdTransform=s?.toContactId||(i=>i),this.options=s}transform(t){return this.transformFn(t)}toContactId(t){return this.contactIdTransform(t)}get messageOptions(){return this.options}on(t,e){let s=this.options?.finalStatus||"DELIVERED";if(t==="read"&&s!=="READ"&&s!=="REPLIED")throw new Error(`Cannot listen to 'read' event when finalStatus is '${s}'. Set finalStatus to 'READ' or 'REPLIED'.`);if(t==="replied"&&s!=="REPLIED")throw new Error(`Cannot listen to 'replied' event when finalStatus is '${s}'. Set finalStatus to 'REPLIED'.`);return this.callbacks[t]=e,this}emit(t,e,s,i){this.callbacks[t]?.(e,s,i)}};var X=require("uuid"),q=require("bullmq");var A=(r=>(r.INIT="INIT",r.DISPATCHED="DISPATCHED",r.SCHEDULED="SCHEDULED",r.QUEUED="QUEUED",r.FINAL="FINAL",r))(A||{}),b=(o=>(o.INIT="INIT",o.PENDING="PENDING",o.SENDING="SENDING",o.DELIVERED="DELIVERED",o.READ="READ",o.REPLIED="REPLIED",o.FAILED="FAILED",o.CANCELED="CANCELED",o))(b||{}),G=(r=>(r.ACCEPTED="accepted",r.DISPATCHED="dispatched",r.RECEIVED="received",r.CONSUMED="consumed",r.FAILED="failed",r))(G||{}),H=(d=>(d.BLIP_CHAT="BLIP_CHAT",d.EMAIL="EMAIL",d.MESSENGER="MESSENGER",d.SKYPE="SKYPE",d.SMS_TAKE="SMS_TAKE",d.SMS_TANGRAM="SMS_TANGRAM",d.TELEGRAM="TELEGRAM",d.WHATSAPP="WHATSAPP",d.INSTAGRAM="INSTAGRAM",d.GOOGLE_RCS="GOOGLE_RCS",d.MICROSOFT_TEAMS="MICROSOFT_TEAMS",d.APPLE_BUSINESS_CHAT="APPLE_BUSINESS_CHAT",d.WORKPLACE="WORKPLACE",d))(H||{}),V=(n=>(n[n.MONDAY=1]="MONDAY",n[n.TUESDAY=2]="TUESDAY",n[n.WEDNESDAY=4]="WEDNESDAY",n[n.THURSDAY=8]="THURSDAY",n[n.FRIDAY=16]="FRIDAY",n[n.SATURDAY=32]="SATURDAY",n[n.SUNDAY=64]="SUNDAY",n))(V||{});var v=z(require("debug")),P={debug:"debug",info:"info",warn:"warn",error:"error"},Q=new Map;function ct(c){if(c==null)return c;if(typeof c=="object")try{return JSON.stringify(c,null,2)}catch{return c}return c}function L(c){return(...t)=>{let e=t.map(ct);c(...e)}}function m(c){if(!Q.has(c)){let t=(0,v.default)(`${c}:${P.debug}`),e=(0,v.default)(`${c}:${P.info}`),s=(0,v.default)(`${c}:${P.warn}`),i=(0,v.default)(`${c}:${P.error}`);i.log=console.error.bind(console);let r={debug:L(t),info:L(e),warn:L(s),error:L(i)};Q.set(c,r)}return Q.get(c)}function j(c){let t=v.default.disable();v.default.enable(`${t},${c}`)}var J=z(require("ioredis")),M=m("Repository"),I=class I{constructor(t,e){this.client=new J.default(e,{maxRetriesPerRequest:null}),this.keyPrefix=`dwn-dispatcher:${t}`,this.client.on("error",s=>{M.error("[client] Redis error",s)})}async setup(){if(this.client.status==="ready"){M.debug("[setup] Redis already connected, skipping");return}this.client.status==="wait"&&await this.client.connect(),M.info("[setup] Repository connected",{status:this.client.status})}async teardown(){this.client.status!=="end"&&(await this.client.quit(),M.info("[teardown] Repository disconnected"))}get redis(){return this.client}getManifestKey(){return`${this.keyPrefix}:manifest`}getKey(t){return`${this.keyPrefix}:message:${t}`}getStateKey(t){return`${this.keyPrefix}:index:state:${t.toLowerCase()}`}getStatusKey(t){return`${this.keyPrefix}:index:status:${t.toLowerCase()}`}getContactKey(t){return`${this.keyPrefix}:index:contact:${t}`}getDescriptorKey(t){return`${this.keyPrefix}:index:descriptor:${t}`}getQueueKey(t){return`${this.keyPrefix}:queue:${t.toLowerCase()}`}async upsertMessage(t,e){let s=this.getKey(t.messageId),i=JSON.stringify(t),r=this.client.pipeline();r.set(s,i),t.contactId&&r.sadd(this.getContactKey(t.contactId),t.messageId),t.descriptorId&&r.sadd(this.getDescriptorKey(t.descriptorId),t.messageId);for(let n of I.INDEXED_STATUSES){let o=this.getStatusKey(n);t.status===n?r.sadd(o,t.messageId):r.srem(o,t.messageId)}for(let n of I.INDEXED_STATES){let o=this.getStateKey(n);t.state===n?r.sadd(o,t.messageId):r.srem(o,t.messageId)}let a=Date.now()+(e||36e5*24*2);if(t.state==="SCHEDULED"&&t.scheduledTo&&(a=new Date(t.scheduledTo).getTime()+(e||0)),r.zadd(this.getQueueKey("retention"),a,t.messageId),t.state==="SCHEDULED"&&t.scheduledTo){let n=new Date(t.scheduledTo).getTime();r.zadd(this.getQueueKey("scheduled"),n,t.messageId)}else r.zrem(this.getQueueKey("scheduled"),t.messageId);if(t.state==="QUEUED"){let n=new Date(t.createdAt||Date.now()).getTime();r.zadd(this.getQueueKey("queued"),n,t.messageId)}else r.zrem(this.getQueueKey("queued"),t.messageId);if(t.state==="DISPATCHED"){let n=new Date(t.createdAt||Date.now()).getTime();r.zadd(this.getQueueKey("dispatched"),n,t.messageId)}else r.zrem(this.getQueueKey("dispatched"),t.messageId);await r.exec(),M.debug("[upsertMessage]",{messageId:t.messageId,status:t.status,state:t.state})}async getMessage(t){let e=this.getKey(t),s=await this.client.get(e);return s?JSON.parse(s):null}async getMessages(t){let e=[];if(t.state==="SCHEDULED"){let i=Date.now(),r=t.skip??0,a=t.size??0;a>0?e=await this.client.zrangebyscore(this.getQueueKey("scheduled"),0,i,"LIMIT",r,a):e=await this.client.zrangebyscore(this.getQueueKey("scheduled"),0,i)}else if(t.state==="QUEUED")e=await this.client.zrange(this.getQueueKey("queued"),t.skip??0,(t.skip??0)+(t.size?t.size-1:-1));else if(t.state==="DISPATCHED")e=await this.client.zrange(this.getQueueKey("dispatched"),t.skip??0,(t.skip??0)+(t.size?t.size-1:-1));else if(t.status){let i=await this.client.smembers(this.getStatusKey(t.status)),r=t.skip??0,a=t.size;e=a?i.slice(r,r+a):i.slice(r)}else if(t.state)try{let i=await this.client.smembers(this.getStateKey(t.state)),r=t.skip??0,a=t.size;e=a?i.slice(r,r+a):i.slice(r)}catch{return[]}else return M.warn("[getMessages] no filter provided"),[];let s=[];for(let i of e){let r=await this.getMessage(i);if(r){if(t.status&&r.status!==t.status||t.state&&r.state!==t.state)continue;s.push(r)}}return M.debug("[getMessages]",{count:s.length,filter:t}),s}async getQueueSize(){return await this.client.zcard(this.getQueueKey("dispatched"))}async evictOldest(t){if(t<=0)return 0;let e=await this.client.zpopmin(this.getQueueKey("dispatched"),t),s=0;for(let i=0;i<e.length;i+=2){let r=e[i];await this.deleteMessage(r),s++}return s}async getExpiredMessages(t=50){let e=Date.now();return await this.client.zrangebyscore(this.getQueueKey("expiration"),0,e,"LIMIT",0,t)}async getRetentionMessages(t=50){let e=Date.now();return await this.client.zrangebyscore(this.getQueueKey("retention"),0,e,"LIMIT",0,t)}async incrementMetric(t,e=1){let s=`${this.keyPrefix}:metrics:${t}`;return await this.client.incrby(s,e)}async getMetric(t){let e=`${this.keyPrefix}:metrics:${t}`,s=await this.client.get(e);return s?parseInt(s,10):0}async deleteMessage(t){let e=await this.getMessage(t);e&&await this.deleteMessageData(t,e)}async deleteMessageData(t,e){let s=this.client.pipeline();s.del(this.getKey(t)),s.zrem(this.getQueueKey("scheduled"),t),s.zrem(this.getQueueKey("queued"),t),s.zrem(this.getQueueKey("dispatched"),t),s.zrem(this.getQueueKey("expiration"),t),s.zrem(this.getQueueKey("retention"),t);for(let i of I.INDEXED_STATUSES)s.srem(this.getStatusKey(i),t);for(let i of I.INDEXED_STATES)s.srem(this.getStateKey(i),t);e.contactId&&s.srem(this.getContactKey(e.contactId),t),e.descriptorId&&s.srem(this.getDescriptorKey(e.descriptorId),t),await s.exec()}async countMessages(t){if(t.state==="SCHEDULED")return await this.client.zcard(this.getQueueKey("scheduled"));if(t.state==="QUEUED")return await this.client.zcard(this.getQueueKey("queued"));if(t.state==="DISPATCHED")return await this.client.zcard(this.getQueueKey("dispatched"));if(t.status)return await this.client.scard(this.getStatusKey(t.status));if(t.state)try{return await this.client.scard(this.getStateKey(t.state))}catch{return 0}return 0}async getMetrics(t){let e={cumulative:{dispatched:0,delivered:0,failed:0},queues:{queued:0,scheduled:0,dispatched:0},status:{}},s=async a=>{if(t){let n=this.getDescriptorKey(t);return(await this.client.sinter(a,n)).length}return await this.client.scard(a)};for(let a of I.INDEXED_STATUSES){let n=await s(this.getStatusKey(a));e.status[a]=n,a==="DELIVERED"&&(e.cumulative.delivered=n),a==="FAILED"&&(e.cumulative.failed=n)}let i=this.getStateKey("DISPATCHED"),r=await s(i);return e.cumulative.dispatched=r+e.cumulative.delivered+e.cumulative.failed,t?(e.queues.queued=await s(this.getStateKey("QUEUED")),e.queues.scheduled=await s(this.getStateKey("SCHEDULED")),e.queues.dispatched=r):(e.queues.queued=await this.client.zcard(this.getQueueKey("queued")),e.queues.scheduled=await this.client.zcard(this.getQueueKey("scheduled")),e.queues.dispatched=await this.client.zcard(this.getQueueKey("dispatched"))),e}async getDescriptors(){let t=this.getDescriptorKey("*"),e=`${this.keyPrefix}:index:descriptor:`,s=await this.client.keys(t),i=[];for(let r of s){let a=r.slice(e.length);if(a){let n=await this.client.scard(r);i.push({id:a,count:n})}}return i.sort((r,a)=>a.count-r.count),i}async writeManifest(t){let e=this.getManifestKey();await this.client.hset(e,{version:t.version,createdAt:t.createdAt,updatedAt:t.updatedAt}),M.info("[writeManifest] Manifest written",{key:e})}async getManifest(){let t=this.getManifestKey(),e=await this.client.hgetall(t);return!e||Object.keys(e).length===0?null:e}};I.INDEXED_STATUSES=["INIT","PENDING","SENDING","DELIVERED","READ","REPLIED","FAILED","CANCELED"],I.INDEXED_STATES=["INIT","DISPATCHED","SCHEDULED","QUEUED","FINAL"];var T=I;var B=m("StateMachine"),C=class{constructor(t,e,s){this.id=t;this.repository=e;this.emit=s}async transition(t,e,s,i){let r=t.state,a=t.status;r==="FINAL"&&e!=="FINAL"&&B.warn(`[transition] Attempting to move from FINAL back to ${e}`,{messageId:t.messageId}),t.state=e,t.status=s,i&&Object.assign(t,i);let n=new Date().toISOString();return s==="SENDING"&&!t.acceptedAt&&a!=="SENDING"&&(t.acceptedAt=n),s==="DELIVERED"&&!t.deliveredAt&&(t.deliveredAt=n),s==="READ"&&!t.readAt&&(t.readAt=n),s==="REPLIED"&&!t.repliedAt&&(t.repliedAt=n,t.readAt||(t.readAt=n)),s==="FAILED"&&!t.failedAt&&(t.failedAt=n),e==="DISPATCHED"&&!t.sentAt&&"PENDING",await this.repository.upsertMessage(t),a!==s&&(s==="REPLIED"&&a!=="READ"&&this.emit("read",t),this.emitStatusEvent(t,s)),e==="SCHEDULED"&&r!=="SCHEDULED"&&this.emit("scheduled",t),B.debug(`[transition] ${t.messageId} : ${r}/${a} -> ${e}/${s}`),t}emitStatusEvent(t,e){switch(e){case"SENDING":this.emit("sending",t);break;case"DELIVERED":this.emit("delivered",t);break;case"READ":this.emit("read",t);break;case"REPLIED":this.emit("replied",t);break;case"FAILED":this.emit("failed",t);break;case"CANCELED":this.emit("canceled",t);break}}};var Y=z(require("axios")),N=require("uuid");var y;(t=>{let c;(i=>{let e;(g=>(g.GET="get",g.SET="set",g.DELETE="delete",g.OBSERVE="observe",g.SUBSCRIBE="subscribe",g.MERGE="merge"))(e=i.Method||(i.Method={}));let s;(n=>(n.SUCCESS="success",n.FAILURE="failure"))(s=i.Status||(i.Status={}))})(c=t.Lime||(t.Lime={}))})(y||(y={}));var p=m("Blip"),k=class{constructor(t,e,s=3e4){let i=`https://${t}.http.msging.net`;this.client=Y.default.create({baseURL:i,timeout:s,headers:{"Content-Type":"application/json",Authorization:e}}),this.client.interceptors.response.use(r=>r,r=>{if(r.response){let a=r.response.data?.reason||{code:r.response.status,description:r.response.statusText||"Unknown error"};throw new D(a.description,a.code)}else throw r.request?new D("No response from server",0):new D(r.message,0)})}async postCommand(t){let e={...t,id:t.id||(0,N.v4)()};p.debug("[postCommand] payload",e);let i=(await this.client.post("/commands",e)).data;if(i.status!==y.Lime.Status.SUCCESS)throw p.error("[postCommand] failed",{method:e.method,uri:e.uri,status:i.status,reason:i.reason}),new D(i.reason?.description||"Command failed",i.reason?.code||0);return p.debug("[postCommand] succeeded",e.uri),i}async postMessage(t){let e={...t,id:t.id||(0,N.v4)()};return p.info("[postMessage] payload",e),(await this.client.post("/messages",e)).data}async mergeContact(t,e){p.info("[mergeContact] called with",{contactId:t,data:e});let s={...e,identity:t},i={method:y.Lime.Method.MERGE,uri:"/contacts",type:"application/vnd.lime.contact+json",resource:s};await this.postCommand(i)}async sendMessage(t,e,s){p.info("[sendMessage] called with",{contactId:t,message:e,id:s});let i=s||(0,N.v4)(),r={id:i,to:t,type:e.type,content:e.content};return await this.postMessage(r),p.info("[sendMessage] sent",{contactId:t,messageId:i}),i}async getDispatchState(t,e){p.info("[getDispatchState] called with",{messageId:t,contactId:e});let s={method:y.Lime.Method.GET,uri:`/threads/${e}?$take=100`,to:"postmaster@msging.net"};try{let i=await this.postCommand(s);if(!i.resource||!i.resource.items||i.resource.items.length===0)return p.debug("[getDispatchState] no messages found in thread",{messageId:t,contactId:e}),null;let r=i.resource.items.find(n=>n.id===t);if(!r)return p.debug("[getDispatchState] message not found in recent thread",{messageId:t,contactId:e}),null;let a=r.status;return p.info("[getDispatchState] state retrieved",{messageId:t,contactId:e,state:a,reason:r.reason?.description}),{state:a,reason:r.reason?.description}}catch(i){if(i instanceof D&&i.code===67)return p.debug("[getDispatchState] resource not found",{messageId:t,contactId:e}),null;throw p.error("[getDispatchState] failed",{messageId:t,contactId:e,error:i}),i}}async getMessageAfter(t,e){p.info("[getMessageAfter] called with",{contactId:t,messageId:e});let s=e,i=0,r=10;for(;i<r;){let a={method:y.Lime.Method.GET,uri:`/threads/${t}?$skip=0&$take=1&$order=asc&messageId=${s}`,to:"postmaster@msging.net"};try{let n=await this.postCommand(a);if(!n.resource||!n.resource.items||n.resource.items.length===0)return p.debug("[getMessageAfter] no message found after",{contactId:t,messageId:s}),null;let o=n.resource.items[0];if(o.direction==="received")return p.info("[getMessageAfter] found received message",{contactId:t,messageId:s,nextMessageId:o.id}),o;p.debug("[getMessageAfter] skipping sent message",{contactId:t,messageId:o.id}),s=o.id,i++}catch(n){if(n instanceof D&&n.code===67)return p.debug("[getMessageAfter] resource not found",{contactId:t,messageId:s}),null;throw p.error("[getMessageAfter] failed",{contactId:t,messageId:s,error:n}),n}}return p.warn("[getMessageAfter] max traversal attempts reached",{contactId:t,startMessageId:e}),null}async sendEvent(t,e,s,i){p.info("[sendEvent] called with",{contactId:t,category:e,action:s,extras:i});let r={to:"postmaster@analytics.msging.net",method:y.Lime.Method.SET,type:"application/vnd.iris.eventTrack+json",uri:"/event-track",resource:{category:e,action:s,contact:{identity:t},extras:i}};await this.postCommand(r)}async setState(t,e,s="onboarding"){p.info("[setState] called with",{contactId:t,botId:e,stateId:s});let i={uri:`/flow-id?shortName=${e}`,to:"postmaster@builder.msging.net",method:y.Lime.Method.GET},r=await this.postCommand(i);if(!r.resource)throw p.error("[setState] flow ID not found",{botId:e}),new D(`Flow ID not found for bot: ${e}`,404);let a=r.resource,n={method:y.Lime.Method.SET,uri:`/contexts/${t}/stateid@${a}`,resource:s,type:"text/plain"};await this.postCommand(n);let o={method:y.Lime.Method.SET,uri:`/contexts/${t}/master-state`,resource:`${e}@msging.net`,type:"text/plain"};await this.postCommand(o)}},D=class c extends Error{constructor(t,e){super(t),this.name="BlipError",this.code=e,Object.setPrototypeOf(this,c.prototype)}};var dt=m("DispatcherQuery"),O=class{constructor(t){this.repository=t}get client(){return this.repository.redis}async query(t){let e=[];if(t.contactId&&e.push(this.repository.getContactKey(t.contactId)),t.descriptorId&&e.push(this.repository.getDescriptorKey(t.descriptorId)),t.status){let u=Array.isArray(t.status)?t.status:[t.status];u.length===1?e.push(this.repository.getStatusKey(u[0])):u.length>1&&e.push(this.repository.getStatusKey(u[0]))}if(t.state){let u=Array.isArray(t.state)?t.state:[t.state];u.length===1&&e.push(this.repository.getStateKey(u[0]))}let s=[];if(e.length>0)s=await this.client.sinter(e);else{let u=Object.values(b).map(l=>this.repository.getStatusKey(l));s=await this.client.sunion(u)}let i=t.skip??0,r=t.size??50,a=s.slice(i,i+r),n=[],o=[];for(let u of a){let l=await this.repository.getMessage(u);if(l){if(t.status&&!(Array.isArray(t.status)?t.status:[t.status]).includes(l.status)||t.state&&!(Array.isArray(t.state)?t.state:[t.state]).includes(l.state))continue;n.push(l)}else o.push(u)}return o.length>0&&this.cleanupIndices(o,t),n}async cleanupIndices(t,e){let s=this.client.pipeline();e.contactId&&s.srem(this.repository.getContactKey(e.contactId),t),e.descriptorId&&s.srem(this.repository.getDescriptorKey(e.descriptorId),t),e.status&&(Array.isArray(e.status)?e.status:[e.status]).forEach(r=>{s.srem(this.repository.getStatusKey(r),t)}),e.state&&(Array.isArray(e.state)?e.state:[e.state]).forEach(r=>{s.srem(this.repository.getStateKey(r),t)}),await s.exec(),dt.debug("[cleanupIndices] Removed expired IDs from checked indices",{count:t.length})}};var{version:pt}=W(),h=m("Dispatcher"),K=class{constructor(t,e,s,i){this.callbacks={};this.descriptors=new Map;this.isRunning=!1;this.setupCompleted=!1;this.setupPromise=null;this.timeoutMonitorRunning=!1;this.timeoutTimer=null;this.id=t,this.repository=i?.repository??new T(t,e),this.stateMachine=new C(this.id,this.repository,(r,a)=>{this.emit(r,a),this.descriptors.get(a.descriptorId)?.emit(r,a,this.api,this.id)}),this.api=new k(s.contract,s.key),this.queueName=`dispatcher-${this.id.replace(/:/g,"-")}`,this.maxRetries=i?.maxRetries??0,this.retryIntervals=i?.retryIntervals??[1*1e3,5*1e3,15*1e3],this.timeouts={pending:i?.timeouts?.pending??120*1e3,sending:i?.timeouts?.sending??120*1e3},this.retention=i?.retention??2880*60*1e3,this.pollingIntervals={scheduled:i?.pollingIntervals?.scheduled??30*1e3,pending:i?.pollingIntervals?.pending??10*1e3,sending:i?.pollingIntervals?.sending??10*1e3,delivered:i?.pollingIntervals?.delivered??1800*1e3,read:i?.pollingIntervals?.read??1800*1e3,queue:i?.pollingIntervals?.queue??1*1e3},this.query=new O(this.repository),this.queue=new q.Queue(this.queueName,{connection:this.repository.redis,defaultJobOptions:{removeOnComplete:!0,removeOnFail:!0}}),this.worker=new q.Worker(this.queueName,async r=>{try{await this.processJob(r)}catch(a){throw h.error(`[Worker] Job ${r.name} failed`,a),a}},{connection:this.repository.redis,concurrency:i?.batchSize||50,limiter:i?.rateLimits?.global?{max:i.rateLimits.global.points,duration:i.rateLimits.global.duration*1e3}:void 0}),this.worker.on("error",r=>h.error("[Worker] Error",r)),this.worker.on("failed",(r,a)=>h.error(`[Worker] Job ${r?.id} failed`,a))}get redis(){return this.repository.redis}async setup(){return this.setupPromise?this.setupPromise:(this.setupPromise=this._doSetup(),this.setupPromise)}async _doSetup(){await this.repository.setup();let t=await this.repository.getManifest();await this.repository.writeManifest({version:pt,createdAt:t?.createdAt??new Date().toISOString(),updatedAt:new Date().toISOString()}),await this.queue.waitUntilReady(),this.isRunning=!0,this.setupCompleted=!0,this.startTimeoutMonitor(),h.info("[setup] Dispatcher started (BullMQ)",{queue:this.queueName})}async teardown(){this.isRunning=!1,this.timeoutTimer&&(clearInterval(this.timeoutTimer),this.timeoutTimer=null),await this.queue.close(),await this.worker.close(),await this.repository.teardown(),h.info("[teardown] Dispatcher stopped")}on(t,e){return this.callbacks[t]=e,this}async getMetrics(){let t={total:0,byState:{},byStatus:{},cumulative:{dispatched:await this.repository.getMetric("dispatched"),delivered:await this.repository.getMetric("delivered"),failed:await this.repository.getMetric("failed")}},e=Object.values(A);for(let i of e)t.byState[i]=await this.repository.countMessages({state:i});let s=Object.values(b);for(let i of s)t.byStatus[i]=await this.repository.countMessages({status:i});return t.total=Object.values(t.byState).reduce((i,r)=>i+(r||0),0),t}emit(t,e){this.callbacks[t]?.(e,this.api,this.id)}async send(t,e,s,i){this.descriptors.set(t.id,t);let r=t.toContactId(e),a=t.transform(s),n=new Date().toISOString(),o={messageId:(0,X.v4)(),contactId:r,descriptorId:t.id,payload:a,status:"INIT",state:"INIT",createdAt:n,attempts:0,retries:this.maxRetries},l={...t.messageOptions,...i},{schedule:g,...w}=l;o.options=w,this.emit("dispatch",o),t.emit("dispatch",o,this.api,this.id);let d=this.calculateScheduledTime(g,l.shifts),f=0;if(d){o.scheduledTo=d,o.state="SCHEDULED";let E=new Date(d).getTime();f=Math.max(0,E-Date.now()),this.emit("scheduled",o),t.emit("scheduled",o,this.api,this.id),h.info("[send] message scheduled",{messageId:o.messageId,scheduledTo:d,delay:f})}else o.state="QUEUED",o.status="INIT",h.info("[send] message queued",{messageId:o.messageId});return o.expiresAt=new Date(Date.now()+(o.state==="SCHEDULED"?f+this.retention:this.retention)).toISOString(),await this.stateMachine.transition(o,o.state,o.status),await this.queue.add("send",{messageId:o.messageId},{jobId:o.messageId,delay:f,priority:1}),o}async cancel(t){let e=await this.repository.getMessage(t);if(!e)return h.warn("[cancel] message not found",{messageId:t}),!1;if(e.state==="FINAL")return h.warn("[cancel] message already final",{messageId:t,status:e.status}),!1;let s=await this.queue.getJob(t);return s&&(await s.remove(),h.info("[cancel] removed job from queue",{messageId:t})),await this.stateMachine.transition(e,"FINAL","CANCELED"),h.info("[cancel] message canceled",{messageId:t}),!0}async processJob(t){let{messageId:e}=t.data,s=await this.repository.getMessage(e);if(!s){h.warn(`[processJob] Message not found: ${e}`);return}let i=this.descriptors.get(s.descriptorId)||null;switch(t.name){case"send":await this.handleSendJob(s,i);break;case"check":await this.handleCheckJob(s,i);break;default:h.warn(`[processJob] Unknown job name: ${t.name}`)}}async handleSendJob(t,e){if(t.state==="FINAL"){h.warn("[handleSendJob] Message already final, skipping",{messageId:t.messageId});return}if(t.state==="DISPATCHED"){h.warn("[handleSendJob] Message already dispatched, scheduling check instead",{messageId:t.messageId}),await this.rescheduleCheck(t,0);return}t.lastDispatchAttemptAt=new Date().toISOString(),await this.stateMachine.transition(t,"DISPATCHED","PENDING");try{await this.api.sendMessage(t.contactId,t.payload,t.messageId),await this.handlePostSendOperations(t,t.options),t.sentAt=new Date().toISOString(),await this.stateMachine.transition(t,"DISPATCHED","PENDING"),h.info("[handleSendJob] Message sent to API",{messageId:t.messageId}),await this.repository.incrementMetric("dispatched"),await this.queue.add("check",{messageId:t.messageId},{delay:this.pollingIntervals.pending,priority:5})}catch(s){let i=s instanceof Error?s:new Error(String(s));await this.handleDispatchFailure(t,e,i)}}async handlePostSendOperations(t,e={}){let s={...e.contact||{}};if(e.intent)if(typeof e.intent=="string")s.intent=e.intent;else{s.intent=e.intent.intent;let{intent:i,...r}=e.intent;Object.entries(r).forEach(([a,n])=>{n!=null&&(s[a]=typeof n=="object"?JSON.stringify(n):String(n))})}Object.keys(s).length>0&&await this.api.mergeContact(t.contactId,s),e.state&&await this.api.setState(t.contactId,e.state.botId,e.state.stateId)}async handleCheckJob(t,e){if(t.state!=="FINAL"){if(this.checkAndHandleTimeout(t)){await this.handleTimeout(t,e);return}try{let s=await this.api.getDispatchState(t.messageId,t.contactId);if(!s){await this.rescheduleCheck(t,this.pollingIntervals.pending);return}let i=this.pollingIntervals.pending;switch(s.state){case"accepted":t.status!=="SENDING"&&await this.stateMachine.transition(t,t.state,"SENDING"),i=this.pollingIntervals.sending;break;case"received":case"consumed":i=await this.handleDeliveredState(t,s.state,i);break;case"failed":await this.handleDispatchFailure(t,e,new Error(s.reason??"Dispatch failed from Gateway"));return}t.state!=="FINAL"&&await this.rescheduleCheck(t,i)}catch(s){h.error("[handleCheckJob] Error",s),await this.rescheduleCheck(t,this.pollingIntervals.pending)}}}checkAndHandleTimeout(t){let e=new Date;if(t.status==="PENDING"){let s=t.lastDispatchAttemptAt||t.sentAt||t.createdAt;if(e.getTime()-new Date(s).getTime()>this.timeouts.pending)return!0}return!!(t.status==="SENDING"&&t.acceptedAt&&e.getTime()-new Date(t.acceptedAt).getTime()>this.timeouts.sending)}async handleTimeout(t,e){await this.stateMachine.transition(t,"FINAL","FAILED",{error:"Timeout Exceeded"}),h.info("[handleTimeout] Message timed out",{messageId:t.messageId})}startTimeoutMonitor(){this.timeoutTimer||(this.timeoutTimer=setInterval(()=>{this.timeoutMonitorRunning||(this.timeoutMonitorRunning=!0,this._runTimeoutMonitorCycle().finally(()=>{this.timeoutMonitorRunning=!1}))},1e4))}async _runTimeoutMonitorCycle(){try{let t=await this.repository.getMessages({status:"PENDING"}),e=await this.repository.getMessages({status:"SENDING"});for(let i of[...t,...e])if(this.checkAndHandleTimeout(i)){let r=await this.repository.getMessage(i.messageId);if(r&&r.state!=="FINAL"&&(r.status==="PENDING"||r.status==="SENDING")){let a=this.descriptors.get(r.descriptorId)||null;await this.handleTimeout(r,a)}}let s=await this.repository.getRetentionMessages(100);if(s.length>0){h.debug("[CleanupMonitor] Cleaning up expired messages",{count:s.length});for(let i of s)await this.repository.deleteMessage(i)}}catch(t){h.error("[TimeoutMonitor] Error during scan",t)}}async rescheduleCheck(t,e){await this.queue.add("check",{messageId:t.messageId},{delay:Math.max(0,e),priority:5})}async handleDeliveredState(t,e,s){if(await this.api.getMessageAfter(t.contactId,t.messageId))return t.status!=="REPLIED"&&t.status!=="READ"&&(await this.repository.incrementMetric("delivered"),await this.stateMachine.transition(t,"FINAL","REPLIED")),s;let r=e==="consumed"?"READ":"DELIVERED";t.status!==r&&(await this.repository.incrementMetric("delivered"),await this.stateMachine.transition(t,t.state,r));let a=t.options?.finalStatus||"DELIVERED";if(this.getStatusRank(t.status)>=this.getStatusRank(a))await this.stateMachine.transition(t,"FINAL",t.status);else return this.pollingIntervals.delivered;return s}async handleDispatchFailure(t,e,s){if(t.attempts=(t.attempts??0)+1,t.error=s.message,h.error("[handleDispatchFailure]",{messageId:t.messageId,attempts:t.attempts,maxRetries:this.maxRetries,error:s.message}),t.attempts<=this.maxRetries){t.retries=this.maxRetries-t.attempts;let i=this.retryIntervals[t.attempts-1]||this.retryIntervals[this.retryIntervals.length-1];await this.stateMachine.transition(t,"SCHEDULED",t.status),this.emit("retry",t),e?.emit("retry",t,this.api,this.id),await this.queue.add("send",{messageId:t.messageId},{jobId:t.messageId,delay:i,priority:1}),h.info("[handleDispatchFailure] Rescheduled retry",{messageId:t.messageId,retryDelay:i})}else t.retries=0,await this.stateMachine.transition(t,"FINAL","FAILED"),await this.repository.incrementMetric("failed")}calculateScheduledTime(t,e){if(t)return t;if(!e||e.length===0)return;let s=new Date;return this.isWithinShifts(s,e)?void 0:this.findNextShiftTime(s,e)?.toISOString()}isWithinShifts(t,e){let s=t.getDay(),i=s===0?64:Math.pow(2,s-1);for(let r of e){if((r.days&i)===0)continue;let a=r.gmt||"-3",n=parseInt(a,10),o=new Date(t.getTime()-n*60*60*1e3),u=o.getHours()*60+o.getMinutes(),[l,g]=r.start.split(":").map(Number),[w,d]=r.end.split(":").map(Number),f=l*60+g,E=w*60+d;if(u>=f&&u<E)return!0}return!1}findNextShiftTime(t,e){for(let i=0;i<=7;i++){let r=new Date(t);r.setDate(r.getDate()+i);let a=r.getDay(),n=a===0?64:Math.pow(2,a-1),o=e.filter(u=>(u.days&n)!==0);if(o.length!==0){o.sort((u,l)=>{let[g,w]=u.start.split(":").map(Number),[d,f]=l.start.split(":").map(Number);return g*60+w-(d*60+f)});for(let u of o){let l=u.gmt||"-3",g=parseInt(l,10),[w,d]=u.start.split(":").map(Number),f=new Date(r);f.setHours(w,d,0,0);let E=new Date(f.getTime()+g*60*60*1e3);if(i===0){if(E>t)return E}else return E}}}}getStatusRank(t){return{INIT:0,PENDING:1,SENDING:2,DELIVERED:3,READ:4,REPLIED:5,FAILED:6,CANCELED:6}[t]||0}static sanitizeContactId(t,e){if(t.includes("@"))return t;let s={BLIP_CHAT:"@0mn.io",EMAIL:"@mailgun.gw.msging.net",MESSENGER:"@messenger.gw.msging.net",SKYPE:"@skype.gw.msging.net",SMS_TAKE:"@take.io",SMS_TANGRAM:"@tangram.com.br",TELEGRAM:"@telegram.gw.msging.net",WHATSAPP:"@wa.gw.msging.net",INSTAGRAM:"@instagram.gw.msging.net",GOOGLE_RCS:"@googlercs.gw.msging.net",MICROSOFT_TEAMS:"@abs.gw.msging.net",APPLE_BUSINESS_CHAT:"@businesschat.gw.msging.net",WORKPLACE:"@workplace.gw.msging.net"};if(!s[e])throw new Error(`Unknown channel: ${e}`);return e==="WHATSAPP"&&t.startsWith("+")&&(t=t.slice(1)),`${t}${s[e]}`}};var Z=require("events"),F=require("bullmq");var S=m("DispatcherMonitor"),$=class extends Z.EventEmitter{constructor(e,s){super();this.history=[];this.lastAlerts={};this.activeAlerts=new Set;this.isRunning=!1;this.id=e.id,this.dispatcher=e,this.options={interval:6e4,historySize:1e3,...s},this.queueName=`monitor-${this.id}`,this.queue=new F.Queue(this.queueName,{connection:e.redis,defaultJobOptions:{removeOnComplete:!0,removeOnFail:!0}}),this.worker=new F.Worker(this.queueName,async i=>{i.name==="check"&&await this.check()},{connection:e.redis}),this.worker.on("error",i=>S.error("[MonitorWorker] Error",i)),this.worker.on("failed",(i,r)=>S.error(`[MonitorWorker] Job ${i?.id} failed`,r))}async start(){this.isRunning||(S.info("[Monitor] Started"),await this.queue.obliterate({force:!0}),await this.queue.add("check",{},{repeat:{every:this.options.interval,immediately:!0}}),this.isRunning=!0)}async stop(){this.isRunning=!1,await this.queue.close(),await this.worker.close(),S.info("[Monitor] Stopped")}async check(){try{let e=await this.dispatcher.getMetrics(),s=Date.now();this.history.push({timestamp:s,metrics:e}),this.cleanHistory();for(let i of this.options.rules)await this.evaluateRule(i,e,s)}catch(e){S.error("[Monitor] Error during check",e)}}cleanHistory(){let e=this.options.historySize;this.history.length>e&&(this.history=this.history.slice(this.history.length-e));let s=Math.max(...this.options.rules.map(r=>r.window||0)),i=Date.now()-s-6e4;if(this.history.length>0&&this.history[0].timestamp<i){let r=this.history.findIndex(a=>a.timestamp>=i);r>0&&(this.history=this.history.slice(r))}}async evaluateRule(e,s,i){let r=`${e.type}`,a=!1,n=0,o={};switch(e.type){case"queue_size":let u=s.byState.FINAL||0;n=s.total-u,a=n>e.threshold,o={current:n,threshold:e.threshold};break;case"failure_rate":if(!e.window){S.warn("[Monitor] failure_rate rule missing window");return}let l=this.findSnapshotAt(i-e.window);if(!l)return;let g=s.cumulative.failed,w=l.metrics.cumulative.failed,d=g-w,f=s.cumulative.dispatched,E=l.metrics.cumulative.dispatched,U=f-E;U===0?n=0:n=d/U,a=n>e.threshold,o={rate:(n*100).toFixed(2)+"%",threshold:(e.threshold*100).toFixed(2)+"%",failed:d,dispatched:U,window:e.window};break}a?this.activeAlerts.has(r)?e.debounce&&!this.isDebounced(r,e.debounce)&&this.emitAlert(r,e,n,o):(this.emitAlert(r,e,n,o),this.activeAlerts.add(r)):this.activeAlerts.has(r)&&(this.resolveAlert(r,e),this.activeAlerts.delete(r))}isDebounced(e,s){if(!s)return!1;let i=this.lastAlerts[e];return i?Date.now()-i<s:!1}emitAlert(e,s,i,r){S.warn(`[Monitor] Alert triggered: ${s.type}`,r),this.lastAlerts[e]=Date.now();let a={type:s.type,message:`${s.type} exceeded threshold`,level:"warning",details:r,timestamp:new Date().toISOString()};this.emit("alert",a)}resolveAlert(e,s){S.info(`[Monitor] Alert resolved: ${s.type}`);let i={type:s.type,message:`${s.type} resolved`,level:"warning",details:{},timestamp:new Date().toISOString()};this.emit("resolved",i)}findSnapshotAt(e){if(this.history.length===0)return null;for(let s of this.history)if(s.timestamp>=e)return s;return this.history[0]}};0&&(module.exports={Blip,BlipError,Channel,DispatchState,Dispatcher,DispatcherDescriptor,DispatcherMonitor,DispatcherRepository,MessageState,MessageStatus,Vnd,Weekdays,enableLogger,getLogger});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|