@k-msg/provider
Version:
Complete provider system with adapters and implementations for K-Message platform
5 lines (3 loc) • 18.4 kB
JavaScript
import{fail as B,KMsgError as ie,KMsgErrorCode as oe,ok as we,readRuntimeEnv as k}from"@k-msg/core";import{SolapiMessageService as Ee}from"solapi";var G={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 H(e){return G[e]}function _e(){return Object.values(G)}import{fail as ee,KMsgError as ye,KMsgErrorCode as ke,ok as q}from"@k-msg/core";function I(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}import{KMsgError as h,KMsgErrorCode as R}from"@k-msg/core";import{ApiKeyError as de,BadRequestError as pe,ClientError as fe,DefaultError as ce,MessageNotReceivedError as ue,NetworkError as ge,ServerError as me}from"solapi";function T(e,t){if(e instanceof h)return e;let n=I(e)?e:{};if(e instanceof de)return new h(R.AUTHENTICATION_FAILED,e.message,{providerId:t});if(e instanceof pe)return new h(R.INVALID_REQUEST,e.message,{providerId:t,validationErrors:n.validationErrors});if(e instanceof ue)return new h(R.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 ge)return new h(R.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 fe){let o=typeof n.httpStatus==="number"?n.httpStatus:void 0,l=typeof o==="number"&&o>=400&&o<500;return new h(l?R.INVALID_REQUEST:R.PROVIDER_ERROR,e.message,{providerId:t,httpStatus:o,errorCode:n.errorCode,errorMessage:n.errorMessage,url:n.url})}if(e instanceof me)return new h(R.PROVIDER_ERROR,e.message,{providerId:t,httpStatus:typeof n.httpStatus==="number"?n.httpStatus:void 0});if(e instanceof ce)return new h(R.PROVIDER_ERROR,e.message,{providerId:t,errorCode:n.errorCode,errorMessage:n.errorMessage});return new h(R.UNKNOWN_ERROR,e instanceof Error?e.message:String(e),{providerId:t})}import{KMsgError as Se,KMsgErrorCode as Ie}from"@k-msg/core";function J(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 V(e){if(typeof e!=="string"||e.trim().length===0)return;let t=new Date(e);if(Number.isNaN(t.getTime()))return;return t}function U(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 Y(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 F(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 Se(Ie.INVALID_REQUEST,"SOLAPI image upload requires `options.imageUrl` or `options.media.image.ref` (url/path).",{providerId:e.providerId})}function P(e){return I(e)&&typeof e.fileId==="string"?e.fileId:void 0}function Z(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,l=o.providerMessageId.trim();if(!l)return ee(new ye(ke.INVALID_REQUEST,"providerMessageId is required",{providerId:t}));try{let r=await n.getMessages({messageId:l,limit:1}),s=(I(r)?r:{}).messageList;if(!s||typeof s!=="object"||Array.isArray(s))return q(null);let b=s,A=b[l],_=Object.values(b).find((L)=>{if(!I(L))return!1;let m=L.messageId;return typeof m==="string"?m===l:!1}),f=I(A)?A:I(_)?_:void 0;if(!f)return q(null);let w=typeof f.statusCode==="string"?f.statusCode:void 0,M=J(w),O=V(f.dateSent),E=V(f.dateCompleted);return q({providerId:t,providerMessageId:l,status:M,statusCode:w,statusMessage:typeof f.statusMessage==="string"?f.statusMessage:void 0,sentAt:O,deliveredAt:E,raw:f})}catch(r){return ee(T(r,t))}}import{KMsgError as u,KMsgErrorCode as g,ok as be}from"@k-msg/core";function he(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 Re(e){let{options:t,response:n,providerId:o,warnings:l}=e,r=Ae(n);return{messageId:t.messageId||crypto.randomUUID(),providerId:o,providerMessageId:r,status:"SENT",type:t.type,to:t.to,...Array.isArray(l)&&l.length>0?{warnings:l}:{},raw:n}}async function Me(e){let{options:t,providerId:n,config:o,client:l}=e,r=Z(t),y=typeof t.from==="string"&&t.from.length>0?t.from:o.defaultFrom,s={to:U(t.to),type:r},b=typeof t.options?.country==="string"&&t.options.country.length>0?t.options.country:typeof o.defaultCountry==="string"?o.defaultCountry:void 0;if(b)s.country=b;let A=t.options?.customFields;if(A&&typeof A==="object"){let i={};for(let[a,d]of Object.entries(A)){if(d===void 0)continue;i[a]=typeof d==="string"?d:String(d)}if(Object.keys(i).length>0)s.customFields=i}if(r==="SMS"||r==="LMS"||r==="MMS"||r==="VOICE"||r==="FAX"||String(r).startsWith("RCS_")){if(!y||y.length===0)throw new u(g.INVALID_REQUEST,"from is required (options.from or config.defaultFrom)",{providerId:n,type:t.type});s.from=U(y)}else if(y)s.from=U(y);if(r==="SMS"||r==="LMS"||r==="MMS"){let i=t,a=i.text;if(a.length===0)throw new u(g.INVALID_REQUEST,"text is required for SMS/LMS/MMS",{providerId:n,type:t.type});s.text=a;let d=i.subject;if(d)s.subject=d;if(r==="MMS"){let p=F({imageUrl:i.imageUrl,media:i.media,providerId:n});if(!p)throw new u(g.INVALID_REQUEST,"image is required for MMS (options.imageUrl or options.media.image.ref)",{providerId:n});let c=await l.uploadFile(p,"MMS"),S=P(c);if(typeof S==="string"&&S.length>0)s.imageId=S;else throw new u(g.PROVIDER_ERROR,"Failed to upload MMS image",{providerId:n})}return s}if(r==="ATA"){let i=t,a=i.failover,d=typeof i.kakao?.profileId==="string"&&i.kakao.profileId.length>0?i.kakao.profileId:o.kakaoPfId;if(!d||d.length===0)throw new u(g.INVALID_REQUEST,"kakao profileId is required (options.kakao.profileId or config.kakaoPfId)",{providerId:n});let p=typeof a?.fallbackContent==="string"&&a.fallbackContent.trim().length>0?a.fallbackContent.trim():void 0,c=typeof a?.fallbackTitle==="string"&&a.fallbackTitle.trim().length>0?a.fallbackTitle.trim():void 0,S=a?.enabled===!0?!1:a?.enabled===!1?!0:i.kakao?.disableSms;if(p)s.text=p;if(c)s.subject=c;return s.kakaoOptions={pfId:d,templateId:i.templateId,variables:v(i.variables),disableSms:S,adFlag:i.kakao?.adFlag,buttons:Array.isArray(i.kakao?.buttons)?i.kakao.buttons:void 0,imageId:i.kakao?.imageId},s}if(r==="CTA"||r==="CTI"){let i=t,a=i.text;if(a.length===0)throw new u(g.INVALID_REQUEST,"text is required for FRIENDTALK",{providerId:n});let d=typeof i.kakao?.profileId==="string"&&i.kakao.profileId.length>0?i.kakao.profileId:o.kakaoPfId;if(!d||d.length===0)throw new u(g.INVALID_REQUEST,"kakao profileId is required (options.kakao.profileId or config.kakaoPfId)",{providerId:n});let p=Y(i.buttons),c=Array.isArray(i.kakao?.buttons)?i.kakao.buttons:p,S=typeof i.kakao?.imageLink==="string"&&i.kakao.imageLink.length>0?i.kakao.imageLink:void 0,C=Array.isArray(c)&&c.length>0?c[0]:void 0,x=I(C)&&typeof C.linkMo==="string"?C.linkMo:void 0,z=S??x,X;if(r==="CTI"){let $=F({imageUrl:i.imageUrl,media:i.media,providerId:n});if(!$)throw new u(g.INVALID_REQUEST,"image is required for CTI (friendtalk image) (options.imageUrl or options.media.image.ref)",{providerId:n});if(!z)throw new u(g.INVALID_REQUEST,"imageLink is required for friendtalk image upload (options.kakao.imageLink or WL button)",{providerId:n});let le=await l.uploadFile($,"KAKAO",void 0,z),K=P(le);if(typeof K==="string"&&K.length>0)X=K;else throw new u(g.PROVIDER_ERROR,"Failed to upload friendtalk image",{providerId:n})}return s.text=a,s.kakaoOptions={pfId:d,variables:v(i.variables),disableSms:i.kakao?.disableSms,adFlag:i.kakao?.adFlag,buttons:c,imageId:X},s}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 u(g.INVALID_REQUEST,"naver talkId is required (options.naver.talkId or config.naverTalkId)",{providerId:n});let d=typeof i.naver?.templateId==="string"&&i.naver.templateId.length>0?i.naver.templateId:i.templateId,p={...v(i.variables),...v(i.naver?.variables)};return s.naverOptions={talkId:a,templateId:d,variables:p,disableSms:i.naver?.disableSms,buttons:Array.isArray(i.naver?.buttons)?i.naver.buttons:void 0},s}if(r==="VOICE"){let i=t,a=i.text;if(a.length===0)throw new u(g.INVALID_REQUEST,"text is required for VOICE",{providerId:n});let d=i.voice?.voiceType,p=d==="FEMALE"||d==="MALE"?d:"FEMALE";return s.text=a,s.voiceOptions=i.voice?{...i.voice,voiceType:p}:{voiceType:p},s}if(r==="FAX"){let a=t.fax,p=Array.isArray(a?.fileIds)?a.fileIds.filter((c)=>typeof c==="string"&&c.length>0):[];if(p.length===0){let c=Array.isArray(a?.fileUrls)?a.fileUrls.filter((S)=>typeof S==="string"&&S.length>0):[];if(c.length===0)throw new u(g.INVALID_REQUEST,"fax.fileIds or fax.fileUrls is required",{providerId:n});p=[];for(let S of c){let C=await l.uploadFile(S,"FAX"),x=P(C);if(typeof x==="string"&&x.length>0)p.push(x)}}if(p.length===0)throw new u(g.PROVIDER_ERROR,"Failed to resolve fax fileIds",{providerId:n});return s.faxOptions={fileIds:p},s}let _=t,f=_.rcs,w=typeof f?.brandId==="string"&&f.brandId.length>0?f.brandId:o.rcsBrandId;if(!w||w.length===0)throw new u(g.INVALID_REQUEST,"rcs brandId is required (options.rcs.brandId or config.rcsBrandId)",{providerId:n});let M={brandId:w,buttons:Array.isArray(f?.buttons)?f.buttons:void 0,copyAllowed:f?.copyAllowed,mmsType:f?.mmsType,commercialType:f?.commercialType,disableSms:f?.disableSms,variables:{...v(_.variables),...v(f?.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 u(g.INVALID_REQUEST,"text is required for RCS text types",{providerId:n});if(s.text=O,E)s.subject=E}let L=f?.additionalBody,m=I(L)?L:void 0,se=m&&typeof m.imageId==="string"&&m.imageId.length>0?m.imageId:m&&typeof m.imaggeId==="string"&&m.imaggeId.length>0?m.imaggeId:void 0,D=(i)=>{let a=m?{...m}:{},d=typeof a.title==="string"&&a.title.length>0?a.title:E||"RCS",p=typeof a.description==="string"&&a.description.length>0?a.description:O||"",c=se??i,S={...a,title:d,description:p};if(typeof c==="string"&&c.length>0)S.imaggeId=c;return S};if(r==="RCS_MMS"){let i=t,a=F({imageUrl:i.imageUrl,media:i.media,providerId:n});if(a){let d=await l.uploadFile(a,"RCS"),p=P(d);if(typeof p==="string"&&p.length>0)M.additionalBody=D(p);else if(m)M.additionalBody=D(void 0)}else if(m)M.additionalBody=D(void 0)}else if(m)M.additionalBody=D(void 0);return s.rcsOptions=M,s}async function ne(e){let{providerId:t,client:n,config:o,options:l}=e,r=he(l,t),y=await Me({options:l,providerId:t,config:o,client:n}),s=Oe(l),b;if(typeof n.sendOne==="function")b=await n.sendOne(s?{...y,scheduledDate:s}:y,o.appId);else if(typeof n.send==="function")b=await n.send(y,ve(o,s));else throw new u(g.PROVIDER_ERROR,"SOLAPI SDK client does not expose a compatible send method",{providerId:t});return be(Re({options:l,response:b,providerId:t,warnings:r}))}function Oe(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 ve(e,t){return{showMessageList:!0,...e.appId?{appId:e.appId}:{},...t?{scheduledDate:t}:{}}}function Ae(e){let t=I(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(!I(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=H(this.id);if(!e)throw new ie(oe.INVALID_REQUEST,`Onboarding spec missing for provider: ${this.id}`,{providerId:this.id});return e}constructor(e,t){if(!e||typeof e!=="object")throw new ie(oe.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 Ee(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(l){return B(T(l,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],l=Number.NaN;for(let r of o){if(typeof r==="number"&&Number.isFinite(r)){l=r;break}if(typeof r==="string"){let y=Number(r);if(Number.isFinite(y)){l=y;break}}}if(!Number.isFinite(l))return B(T(Error("Invalid balance response from SOLAPI"),this.id));return we({providerId:this.id,channel:e?.channel,amount:l,currency:"KRW",raw:t})}catch(t){return B(T(t,this.id))}}}var j=(e)=>new N(e),Q=()=>{let e={apiKey:k("SOLAPI_API_KEY")||"",apiSecret:k("SOLAPI_API_SECRET")||"",baseUrl:k("SOLAPI_BASE_URL")||"https://api.solapi.com",defaultFrom:k("SOLAPI_DEFAULT_FROM"),kakaoPfId:k("SOLAPI_KAKAO_PF_ID"),rcsBrandId:k("SOLAPI_RCS_BRAND_ID"),naverTalkId:k("SOLAPI_NAVER_TALK_ID"),appId:k("SOLAPI_APP_ID"),defaultCountry:k("SOLAPI_DEFAULT_COUNTRY"),debug:k("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 re(){}var tt=re,nt=j,it=Q,ot=W,rt=N;export{tt as initializeSolapi,nt as createSolapiProvider,it as createDefaultSolapiProvider,ot as SolapiProviderFactory,rt as SolapiProvider};
//# debugId=AE8D1D70FA6CBEE264756E2164756E21
//# sourceMappingURL=index.mjs.map