UNPKG

@k-msg/provider

Version:

Complete provider system with adapters and implementations for K-Message platform

5 lines (3 loc) 19.5 kB
var{defineProperty:q,getOwnPropertyNames:le,getOwnPropertyDescriptor:de}=Object,pe=Object.prototype.hasOwnProperty;var G=new WeakMap,fe=(e)=>{var t=G.get(e),n;if(t)return t;if(t=q({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")le(e).map((o)=>!pe.call(t,o)&&q(t,o,{get:()=>e[o],enumerable:!(n=de(e,o))||n.enumerable}));return G.set(e,t),t};var ce=(e,t)=>{for(var n in t)q(e,n,{get:t[n],enumerable:!0,configurable:!0,set:(o)=>t[n]=()=>o})};var ve={};ce(ve,{initializeSolapi:()=>be,createSolapiProvider:()=>he,createDefaultSolapiProvider:()=>Re,SolapiProviderFactory:()=>Me,SolapiProvider:()=>Oe});module.exports=fe(ve);var c=require("@k-msg/core"),ie=require("solapi");var H={iwinv:{providerId:"iwinv",providerName:"IWINV Messaging Provider",channelOnboarding:"manual",templateLifecycleApi:"available",plusIdPolicy:"optional",plusIdInference:"unsupported",liveTestSupport:"supported",checks:[{id:"channel_registered_in_console",title:"Kakao channel is registered in IWINV console",description:"IWINV channel onboarding is manual. Confirm channel registration and approval in console.",kind:"manual",severity:"blocker",scopes:["doctor","preflight"]},{id:"iwinv_config_required",title:"IWINV config has required keys",kind:"config",severity:"blocker",scopes:["doctor","preflight"],configKeys:["apiKey"]},{id:"template_capability_available",title:"Template lifecycle APIs are available",kind:"capability",severity:"warning",scopes:["doctor","preflight"],capabilityMethods:["listTemplates","getTemplate","createTemplate","updateTemplate","deleteTemplate"]},{id:"template_list_probe",title:"Template list API probe",kind:"api_probe",severity:"warning",scopes:["doctor","preflight"],probeOperation:"list_templates"}],notes:["Channel add/auth is not available via IWINV public API in current integration.","Template APIs are available and can be probed."]},aligo:{providerId:"aligo",providerName:"Aligo Smart SMS",channelOnboarding:"api",templateLifecycleApi:"available",plusIdPolicy:"required_if_no_inference",plusIdInference:"supported",liveTestSupport:"supported",checks:[{id:"aligo_config_required",title:"Aligo config has required keys",kind:"config",severity:"blocker",scopes:["doctor","preflight"],configKeys:["apiKey","userId"]},{id:"channel_api_capability_available",title:"Kakao channel APIs are available",kind:"capability",severity:"warning",scopes:["doctor","preflight"],capabilityMethods:["listKakaoChannels","requestKakaoChannelAuth","addKakaoChannel"]},{id:"channel_list_probe",title:"Kakao channel list API probe",kind:"api_probe",severity:"warning",scopes:["doctor","preflight"],probeOperation:"list_kakao_channels"},{id:"template_list_probe",title:"Template list API probe",kind:"api_probe",severity:"warning",scopes:["doctor","preflight"],probeOperation:"list_templates"}]},solapi:{providerId:"solapi",providerName:"SOLAPI Messaging Provider",channelOnboarding:"none",templateLifecycleApi:"unavailable",plusIdPolicy:"required_if_no_inference",plusIdInference:"unsupported",liveTestSupport:"partial",checks:[{id:"solapi_config_required",title:"SOLAPI config has required keys",kind:"config",severity:"blocker",scopes:["doctor","preflight"],configKeys:["apiKey","apiSecret"]}],notes:["SOLAPI ALIMTALK requires kakao profileId/pfId, but plusId inference is not available in current integration."]},mock:{providerId:"mock",providerName:"Mock Provider",channelOnboarding:"api",templateLifecycleApi:"available",plusIdPolicy:"optional",plusIdInference:"supported",liveTestSupport:"none",checks:[{id:"mock_template_capability_available",title:"Mock template APIs are available",kind:"capability",severity:"info",scopes:["doctor","preflight"],capabilityMethods:["listTemplates","getTemplate","createTemplate"]}]}};function J(e){return H[e]}function we(){return Object.values(H)}var h=require("@k-msg/core");function y(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}var g=require("@k-msg/core"),k=require("solapi");function T(e,t){if(e instanceof g.KMsgError)return e;let n=y(e)?e:{};if(e instanceof k.ApiKeyError)return new g.KMsgError(g.KMsgErrorCode.AUTHENTICATION_FAILED,e.message,{providerId:t});if(e instanceof k.BadRequestError)return new g.KMsgError(g.KMsgErrorCode.INVALID_REQUEST,e.message,{providerId:t,validationErrors:n.validationErrors});if(e instanceof k.MessageNotReceivedError)return new g.KMsgError(g.KMsgErrorCode.PROVIDER_ERROR,e.message,{providerId:t,failedMessageList:Array.isArray(n.failedMessageList)?n.failedMessageList:void 0,totalCount:typeof n.totalCount==="number"?n.totalCount:void 0});if(e instanceof k.NetworkError)return new g.KMsgError(g.KMsgErrorCode.NETWORK_ERROR,e.message,{providerId:t,url:typeof n.url==="string"?n.url:void 0,method:typeof n.method==="string"?n.method:void 0,isRetryable:typeof n.isRetryable==="boolean"?n.isRetryable:!0});if(e instanceof k.ClientError){let o=typeof n.httpStatus==="number"?n.httpStatus:void 0,d=typeof o==="number"&&o>=400&&o<500;return new g.KMsgError(d?g.KMsgErrorCode.INVALID_REQUEST:g.KMsgErrorCode.PROVIDER_ERROR,e.message,{providerId:t,httpStatus:o,errorCode:n.errorCode,errorMessage:n.errorMessage,url:n.url})}if(e instanceof k.ServerError)return new g.KMsgError(g.KMsgErrorCode.PROVIDER_ERROR,e.message,{providerId:t,httpStatus:typeof n.httpStatus==="number"?n.httpStatus:void 0});if(e instanceof k.DefaultError)return new g.KMsgError(g.KMsgErrorCode.PROVIDER_ERROR,e.message,{providerId:t,errorCode:n.errorCode,errorMessage:n.errorMessage});return new g.KMsgError(g.KMsgErrorCode.UNKNOWN_ERROR,e instanceof Error?e.message:String(e),{providerId:t})}var U=require("@k-msg/core");function Y(e){if(!e)return"UNKNOWN";if(e==="2000")return"PENDING";if(e==="3000")return"SENT";if(e==="4000")return"DELIVERED";if(/^[123]\\d{3}$/.test(e))return"FAILED";return"UNKNOWN"}function B(e){if(typeof e!=="string"||e.trim().length===0)return;let t=new Date(e);if(Number.isNaN(t.getTime()))return;return t}function F(e){let t=e.trim();if(t.startsWith("+"))return`+${t.slice(1).replace(/\\D/g,"")}`;return t.replace(/\\D/g,"")}function v(e){let t={};if(!e)return t;for(let[n,o]of Object.entries(e)){if(o===void 0)continue;t[n]=o===null?"":o instanceof Date?o.toISOString():typeof o==="string"?o:String(o)}return t}function Z(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e){if(!n)continue;if(n.type!=="WL")continue;if(!n.name||!n.urlMobile)continue;t.push({buttonName:n.name,buttonType:"WL",linkMo:n.urlMobile,linkPc:n.urlPc})}return t.length>0?t:void 0}function K(e){let t=typeof e.imageUrl==="string"&&e.imageUrl.trim().length>0?e.imageUrl.trim():void 0;if(t)return t;let n=e.media?.image;if(!n)return;if("ref"in n){let o=n.ref.trim();return o.length>0?o:void 0}throw new U.KMsgError(U.KMsgErrorCode.INVALID_REQUEST,"SOLAPI image upload requires `options.imageUrl` or `options.media.image.ref` (url/path).",{providerId:e.providerId})}function P(e){return y(e)&&typeof e.fileId==="string"?e.fileId:void 0}function ee(e){switch(e.type){case"ALIMTALK":return"ATA";case"FRIENDTALK":return typeof e.imageUrl==="string"&&e.imageUrl.trim().length>0||Boolean(e.media?.image)?"CTI":"CTA";default:return e.type}}async function te(e){let{providerId:t,client:n,query:o}=e,d=o.providerMessageId.trim();if(!d)return h.fail(new h.KMsgError(h.KMsgErrorCode.INVALID_REQUEST,"providerMessageId is required",{providerId:t}));try{let r=await n.getMessages({messageId:d,limit:1}),l=(y(r)?r:{}).messageList;if(!l||typeof l!=="object"||Array.isArray(l))return h.ok(null);let R=l,A=R[d],_=Object.values(R).find((L)=>{if(!y(L))return!1;let S=L.messageId;return typeof S==="string"?S===d:!1}),u=y(A)?A:y(_)?_:void 0;if(!u)return h.ok(null);let w=typeof u.statusCode==="string"?u.statusCode:void 0,M=Y(w),O=B(u.dateSent),E=B(u.dateCompleted);return h.ok({providerId:t,providerMessageId:d,status:M,statusCode:w,statusMessage:typeof u.statusMessage==="string"?u.statusMessage:void 0,sentAt:O,deliveredAt:E,raw:u})}catch(r){return h.fail(T(r,t))}}var s=require("@k-msg/core");function ue(e,t){if(e.type!=="ALIMTALK")return;if(e.failover?.enabled!==!0)return;return[{code:"FAILOVER_PARTIAL_PROVIDER",message:"SOLAPI failover mapping is partial. API-level fallback may be attempted for non-Kakao-user failures.",details:{providerId:t,mappedFields:["kakao.disableSms","text","subject"],unsupportedFields:["fallbackChannel"]}}]}function ge(e){let{options:t,response:n,providerId:o,warnings:d}=e,r=ye(n);return{messageId:t.messageId||crypto.randomUUID(),providerId:o,providerMessageId:r,status:"SENT",type:t.type,to:t.to,...Array.isArray(d)&&d.length>0?{warnings:d}:{},raw:n}}async function me(e){let{options:t,providerId:n,config:o,client:d}=e,r=ee(t),b=typeof t.from==="string"&&t.from.length>0?t.from:o.defaultFrom,l={to:F(t.to),type:r},R=typeof t.options?.country==="string"&&t.options.country.length>0?t.options.country:typeof o.defaultCountry==="string"?o.defaultCountry:void 0;if(R)l.country=R;let A=t.options?.customFields;if(A&&typeof A==="object"){let i={};for(let[a,p]of Object.entries(A)){if(p===void 0)continue;i[a]=typeof p==="string"?p:String(p)}if(Object.keys(i).length>0)l.customFields=i}if(r==="SMS"||r==="LMS"||r==="MMS"||r==="VOICE"||r==="FAX"||String(r).startsWith("RCS_")){if(!b||b.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"from is required (options.from or config.defaultFrom)",{providerId:n,type:t.type});l.from=F(b)}else if(b)l.from=F(b);if(r==="SMS"||r==="LMS"||r==="MMS"){let i=t,a=i.text;if(a.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"text is required for SMS/LMS/MMS",{providerId:n,type:t.type});l.text=a;let p=i.subject;if(p)l.subject=p;if(r==="MMS"){let f=K({imageUrl:i.imageUrl,media:i.media,providerId:n});if(!f)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"image is required for MMS (options.imageUrl or options.media.image.ref)",{providerId:n});let m=await d.uploadFile(f,"MMS"),I=P(m);if(typeof I==="string"&&I.length>0)l.imageId=I;else throw new s.KMsgError(s.KMsgErrorCode.PROVIDER_ERROR,"Failed to upload MMS image",{providerId:n})}return l}if(r==="ATA"){let i=t,a=i.failover,p=typeof i.kakao?.profileId==="string"&&i.kakao.profileId.length>0?i.kakao.profileId:o.kakaoPfId;if(!p||p.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"kakao profileId is required (options.kakao.profileId or config.kakaoPfId)",{providerId:n});let f=typeof a?.fallbackContent==="string"&&a.fallbackContent.trim().length>0?a.fallbackContent.trim():void 0,m=typeof a?.fallbackTitle==="string"&&a.fallbackTitle.trim().length>0?a.fallbackTitle.trim():void 0,I=a?.enabled===!0?!1:a?.enabled===!1?!0:i.kakao?.disableSms;if(f)l.text=f;if(m)l.subject=m;return l.kakaoOptions={pfId:p,templateId:i.templateId,variables:v(i.variables),disableSms:I,adFlag:i.kakao?.adFlag,buttons:Array.isArray(i.kakao?.buttons)?i.kakao.buttons:void 0,imageId:i.kakao?.imageId},l}if(r==="CTA"||r==="CTI"){let i=t,a=i.text;if(a.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"text is required for FRIENDTALK",{providerId:n});let p=typeof i.kakao?.profileId==="string"&&i.kakao.profileId.length>0?i.kakao.profileId:o.kakaoPfId;if(!p||p.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"kakao profileId is required (options.kakao.profileId or config.kakaoPfId)",{providerId:n});let f=Z(i.buttons),m=Array.isArray(i.kakao?.buttons)?i.kakao.buttons:f,I=typeof i.kakao?.imageLink==="string"&&i.kakao.imageLink.length>0?i.kakao.imageLink:void 0,C=Array.isArray(m)&&m.length>0?m[0]:void 0,x=y(C)&&typeof C.linkMo==="string"?C.linkMo:void 0,z=I??x,X;if(r==="CTI"){let $=K({imageUrl:i.imageUrl,media:i.media,providerId:n});if(!$)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"image is required for CTI (friendtalk image) (options.imageUrl or options.media.image.ref)",{providerId:n});if(!z)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"imageLink is required for friendtalk image upload (options.kakao.imageLink or WL button)",{providerId:n});let se=await d.uploadFile($,"KAKAO",void 0,z),V=P(se);if(typeof V==="string"&&V.length>0)X=V;else throw new s.KMsgError(s.KMsgErrorCode.PROVIDER_ERROR,"Failed to upload friendtalk image",{providerId:n})}return l.text=a,l.kakaoOptions={pfId:p,variables:v(i.variables),disableSms:i.kakao?.disableSms,adFlag:i.kakao?.adFlag,buttons:m,imageId:X},l}if(r==="NSA"){let i=t,a=typeof i.naver?.talkId==="string"&&i.naver.talkId.length>0?i.naver.talkId:o.naverTalkId;if(!a||a.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"naver talkId is required (options.naver.talkId or config.naverTalkId)",{providerId:n});let p=typeof i.naver?.templateId==="string"&&i.naver.templateId.length>0?i.naver.templateId:i.templateId,f={...v(i.variables),...v(i.naver?.variables)};return l.naverOptions={talkId:a,templateId:p,variables:f,disableSms:i.naver?.disableSms,buttons:Array.isArray(i.naver?.buttons)?i.naver.buttons:void 0},l}if(r==="VOICE"){let i=t,a=i.text;if(a.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"text is required for VOICE",{providerId:n});let p=i.voice?.voiceType,f=p==="FEMALE"||p==="MALE"?p:"FEMALE";return l.text=a,l.voiceOptions=i.voice?{...i.voice,voiceType:f}:{voiceType:f},l}if(r==="FAX"){let a=t.fax,f=Array.isArray(a?.fileIds)?a.fileIds.filter((m)=>typeof m==="string"&&m.length>0):[];if(f.length===0){let m=Array.isArray(a?.fileUrls)?a.fileUrls.filter((I)=>typeof I==="string"&&I.length>0):[];if(m.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"fax.fileIds or fax.fileUrls is required",{providerId:n});f=[];for(let I of m){let C=await d.uploadFile(I,"FAX"),x=P(C);if(typeof x==="string"&&x.length>0)f.push(x)}}if(f.length===0)throw new s.KMsgError(s.KMsgErrorCode.PROVIDER_ERROR,"Failed to resolve fax fileIds",{providerId:n});return l.faxOptions={fileIds:f},l}let _=t,u=_.rcs,w=typeof u?.brandId==="string"&&u.brandId.length>0?u.brandId:o.rcsBrandId;if(!w||w.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"rcs brandId is required (options.rcs.brandId or config.rcsBrandId)",{providerId:n});let M={brandId:w,buttons:Array.isArray(u?.buttons)?u.buttons:void 0,copyAllowed:u?.copyAllowed,mmsType:u?.mmsType,commercialType:u?.commercialType,disableSms:u?.disableSms,variables:{...v(_.variables),...v(u?.variables)}};if(r==="RCS_TPL"||r==="RCS_ITPL"||r==="RCS_LTPL"){let i=t;M.templateId=typeof i.rcs?.templateId==="string"&&i.rcs.templateId.length>0?i.rcs.templateId:i.templateId}let O,E;if(r==="RCS_SMS"||r==="RCS_LMS"||r==="RCS_MMS"){let i=t;if(O=i.text,E=i.subject,!O||O.length===0)throw new s.KMsgError(s.KMsgErrorCode.INVALID_REQUEST,"text is required for RCS text types",{providerId:n});if(l.text=O,E)l.subject=E}let L=u?.additionalBody,S=y(L)?L:void 0,ae=S&&typeof S.imageId==="string"&&S.imageId.length>0?S.imageId:S&&typeof S.imaggeId==="string"&&S.imaggeId.length>0?S.imaggeId:void 0,D=(i)=>{let a=S?{...S}:{},p=typeof a.title==="string"&&a.title.length>0?a.title:E||"RCS",f=typeof a.description==="string"&&a.description.length>0?a.description:O||"",m=ae??i,I={...a,title:p,description:f};if(typeof m==="string"&&m.length>0)I.imaggeId=m;return I};if(r==="RCS_MMS"){let i=t,a=K({imageUrl:i.imageUrl,media:i.media,providerId:n});if(a){let p=await d.uploadFile(a,"RCS"),f=P(p);if(typeof f==="string"&&f.length>0)M.additionalBody=D(f);else if(S)M.additionalBody=D(void 0)}else if(S)M.additionalBody=D(void 0)}else if(S)M.additionalBody=D(void 0);return l.rcsOptions=M,l}async function ne(e){let{providerId:t,client:n,config:o,options:d}=e,r=ue(d,t),b=await me({options:d,providerId:t,config:o,client:n}),l=Se(d),R;if(typeof n.sendOne==="function")R=await n.sendOne(l?{...b,scheduledDate:l}:b,o.appId);else if(typeof n.send==="function")R=await n.send(b,Ie(o,l));else throw new s.KMsgError(s.KMsgErrorCode.PROVIDER_ERROR,"SOLAPI SDK client does not expose a compatible send method",{providerId:t});return s.ok(ge({options:d,response:R,providerId:t,warnings:r}))}function Se(e){let t=e.options?.scheduledAt;if(t instanceof Date)return Number.isNaN(t.getTime())?void 0:t.toISOString();return typeof t==="string"&&t.length>0?t:void 0}function Ie(e,t){return{showMessageList:!0,...e.appId?{appId:e.appId}:{},...t?{scheduledDate:t}:{}}}function ye(e){let t=y(e)?e:{};if(typeof t.messageId==="string"&&t.messageId.length>0)return t.messageId;let n=t.messageList;if(!Array.isArray(n))return;for(let o of n){if(!y(o))continue;if(typeof o.messageId==="string"&&o.messageId.length>0)return o.messageId}return}class N{id="solapi";name="SOLAPI Messaging Provider";supportedTypes=["ALIMTALK","FRIENDTALK","SMS","LMS","MMS","NSA","VOICE","FAX","RCS_SMS","RCS_LMS","RCS_MMS","RCS_TPL","RCS_ITPL","RCS_LTPL"];transportCapabilities={abortSignal:"unsupported",injectableFetch:"unsupported"};config;client;getOnboardingSpec(){let e=J(this.id);if(!e)throw new c.KMsgError(c.KMsgErrorCode.INVALID_REQUEST,`Onboarding spec missing for provider: ${this.id}`,{providerId:this.id});return e}constructor(e,t){if(!e||typeof e!=="object")throw new c.KMsgError(c.KMsgErrorCode.INVALID_REQUEST,"SolapiProvider requires a config object",{providerId:this.id});if(!e.apiKey||e.apiKey.length===0)throw Error("SolapiProvider requires `apiKey`");if(!e.apiSecret||e.apiSecret.length===0)throw Error("SolapiProvider requires `apiSecret`");this.config={...e,baseUrl:typeof e.baseUrl==="string"&&e.baseUrl.length>0?e.baseUrl:"https://api.solapi.com"},this.client=t??new ie.SolapiMessageService(this.config.apiKey,this.config.apiSecret)}async healthCheck(){let e=[],t=Date.now();try{if(!this.config.apiKey)e.push("Missing apiKey");if(!this.config.apiSecret)e.push("Missing apiSecret");if(this.config.baseUrl)try{new URL(this.config.baseUrl)}catch{e.push("Invalid baseUrl")}return{healthy:e.length===0,issues:e,latencyMs:Date.now()-t,data:{provider:this.id,baseUrl:this.config.baseUrl}}}catch(n){return e.push(n instanceof Error?n.message:String(n)),{healthy:!1,issues:e,latencyMs:Date.now()-t}}}async send(e,t){let n=e.messageId||crypto.randomUUID(),o={...e,messageId:n};try{return await ne({providerId:this.id,client:this.client,config:this.config,options:o})}catch(d){return c.fail(T(d,this.id))}}async getDeliveryStatus(e,t){return te({providerId:this.id,client:this.client,query:e})}async getBalance(e){try{let t=await this.client.getBalance(),n=t,o=[n.balance,n.point],d=Number.NaN;for(let r of o){if(typeof r==="number"&&Number.isFinite(r)){d=r;break}if(typeof r==="string"){let b=Number(r);if(Number.isFinite(b)){d=b;break}}}if(!Number.isFinite(d))return c.fail(T(Error("Invalid balance response from SOLAPI"),this.id));return c.ok({providerId:this.id,channel:e?.channel,amount:d,currency:"KRW",raw:t})}catch(t){return c.fail(T(t,this.id))}}}var j=(e)=>new N(e),Q=()=>{let e={apiKey:c.readRuntimeEnv("SOLAPI_API_KEY")||"",apiSecret:c.readRuntimeEnv("SOLAPI_API_SECRET")||"",baseUrl:c.readRuntimeEnv("SOLAPI_BASE_URL")||"https://api.solapi.com",defaultFrom:c.readRuntimeEnv("SOLAPI_DEFAULT_FROM"),kakaoPfId:c.readRuntimeEnv("SOLAPI_KAKAO_PF_ID"),rcsBrandId:c.readRuntimeEnv("SOLAPI_RCS_BRAND_ID"),naverTalkId:c.readRuntimeEnv("SOLAPI_NAVER_TALK_ID"),appId:c.readRuntimeEnv("SOLAPI_APP_ID"),defaultCountry:c.readRuntimeEnv("SOLAPI_DEFAULT_COUNTRY"),debug:c.readRuntimeEnv("NODE_ENV")==="development"};if(!e.apiKey||!e.apiSecret)throw Error("SOLAPI_API_KEY and SOLAPI_API_SECRET environment variables are required");return j(e)};class W{static create(e){return new N(e)}static createDefault(){return Q()}}function oe(){}var be=oe,he=j,Re=Q,Me=W,Oe=N; //# debugId=7223E3A2A7D603F264756E2164756E21 //# sourceMappingURL=index.js.map