@k-msg/provider
Version:
Complete provider system with adapters and implementations for K-Message platform
5 lines (3 loc) • 27.8 kB
JavaScript
import{fail as F,KMsgError as w,KMsgErrorCode as A,ok as Le,readRuntimeEnv as L}from"@k-msg/core";var Te={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 ve(e){return Te[e]}function Ge(){return Object.values(Te)}function O(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function V(e){if(!e)return{};try{return JSON.parse(e)}catch{return e}}function J(e,t){return O(e)?e:t}import{KMsgErrorCode as z}from"@k-msg/core";function He(e){let t="",n=0;while(n<e.length){let r=e[n++]??0,i=e[n++]??0,o=e[n++]??0,s=r<<16|i<<8|o;t+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[s>>18&63],t+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[s>>12&63],t+=n-2<e.length?"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[s>>6&63]:"=",t+=n-1<e.length?"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[s&63]:"="}return t}function ne(e){return He(new TextEncoder().encode(e))}function he(e){let t=e.sendEndpoint||"/api/v2/send/";return t.startsWith("/")?t:`/${t}`}function X(e){let n={AUTH:ne(e.apiKey),"Content-Type":"application/json;charset=UTF-8"};if(typeof e.xForwardedFor==="string"&&e.xForwardedFor.length>0)n["X-Forwarded-For"]=e.xForwardedFor;if(e.extraHeaders&&typeof e.extraHeaders==="object")return{...n,...e.extraHeaders};return n}function Z(e){switch(e){case 201:case 206:case 401:case 403:return z.AUTHENTICATION_FAILED;case 429:return z.RATE_LIMIT_EXCEEDED;case 519:return z.INSUFFICIENT_BALANCE;case 404:case 501:return z.TEMPLATE_NOT_FOUND;case 502:case 503:case 504:case 505:case 506:case 507:case 508:case 509:case 510:case 511:case 512:case 513:case 514:case 515:case 516:case 517:case 540:return z.INVALID_REQUEST;case 518:return z.PROVIDER_ERROR;default:if(e>=500)return z.PROVIDER_ERROR;return z.INVALID_REQUEST}}function oe(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"){let t=e.trim();if(t.length===0)return;let n=Number(t);if(Number.isFinite(n))return n}return}function it(e){if(typeof e!=="string")return;let t=e.trim().toUpperCase();if(!t)return;switch(t){case"Y":case"APPROVED":return"Y";case"I":case"INSPECTION":return"I";case"R":case"REJECTED":return"R";case"PENDING":return"I";default:return}}function ot(e){switch(typeof e==="string"?e.trim().toUpperCase():""){case"Y":return"APPROVED";case"I":return"INSPECTION";case"R":return"REJECTED";default:return"PENDING"}}var we="https://alimtalk.bizservice.iwinv.kr",Ee="https://sms.bizservice.iwinv.kr";import{fail as P,KMsgError as H,KMsgErrorCode as Q,ok as ee}from"@k-msg/core";import{KMsgError as se,KMsgErrorCode as ae}from"@k-msg/core";function Ke(e){if(typeof DOMException<"u"&&e instanceof DOMException&&e.name==="TimeoutError")return!0;return typeof e==="object"&&e!==null&&"code"in e&&e.code===ae.NETWORK_TIMEOUT}function ze(e){return typeof DOMException<"u"&&e instanceof DOMException&&e.name==="AbortError"}function Be(e){if(e instanceof Error)return e.message;if(typeof e==="string"&&e.length>0)return e;return"Provider request aborted"}function $e(e,t,n){if(!t?.aborted)return;if(e instanceof se)return e;let r=t.reason??e,i=ze(r)?e:r;return new se(Ke(r)?ae.NETWORK_TIMEOUT:ae.REQUEST_ABORTED,Be(i),{providerId:n})}function de(e,t){if(e instanceof se)return e;return new se(ae.NETWORK_ERROR,e instanceof Error?e.message:String(e),{providerId:t})}function B(e,t,n){return $e(e,t,n)??de(e,n)}async function j(e,t,n,r){let i=e?.fetch??globalThis.fetch;if(typeof i!=="function")throw de(Error("No fetch implementation is available; provide ProviderRequestContext.fetch or use a runtime with native fetch support"),r);try{return await i(t,{...n,signal:e?.signal})}catch(o){throw B(o,e?.signal,r)}}import{KMsgErrorCode as re}from"@k-msg/core";function U(e){return e.replace(/[^0-9]/g,"")}function q(){return Ee}function $(e){if(e.smsApiKey&&e.smsAuthKey)return ne(`${e.smsApiKey}&${e.smsAuthKey}`);let t=e.smsAuthKey||e.smsApiKey;if(!t)return"";return ne(`${e.apiKey}&${t}`)}function Y(e){return $(e).length>0}function ue(e){if(typeof e==="number"&&Number.isFinite(e))return e.toString();if(typeof e==="string")return e.trim();return""}function le(e,t){return{"0":"전송 성공","1":"메시지가 전송되지 않았습니다.","11":"운영 중인 서비스가 아닙니다.","12":"요금제 충전 중입니다. 잠시 후 다시 시도해 보시기 바랍니다.","13":"등록되지 않은 발신번호입니다.","14":"인증 요청이 올바르지 않습니다.","15":"등록하지 않은 IP에서는 발송되지 않습니다.","21":"장문 메시지는 2000 Bytes까지만 입력이 가능합니다.","22":"제목 입력 가능 문자가 올바르지 않습니다.","23":"제목은 40 Byte까지만 입력이 가능합니다.","31":"파일 업로드는 100KB까지 가능합니다.","32":"허용되지 않는 파일 확장자입니다.","33":"이미지 업로드에 실패했습니다.","41":"수신 번호를 입력하여 주세요.","42":"예약 전송 가능 시간이 아닙니다.","43":"날짜와 시간 표현 형식에 맞춰 입력하여 주십시오.","44":"최대 1000건 전송 가능합니다.","50":"SMS 자동 충전 한도를 초과하였습니다.","202":"SMS API 인증 실패 또는 SMS 서비스 권한이 없습니다.","206":"등록하지 않은 IP에서는 발송되지 않습니다."}[e]||t}function me(e,t){if(e==="14"||e==="15"||e==="202"||e==="206")return re.AUTHENTICATION_FAILED;if(e==="50")return re.INSUFFICIENT_BALANCE;if(e==="13"||e==="21"||e==="22"||e==="23"||e==="31"||e==="32"||e==="33"||e==="41"||e==="42"||e==="43"||e==="44")return re.INVALID_REQUEST;if(!t)return re.NETWORK_ERROR;return re.PROVIDER_ERROR}function fe(e,t){if(t&&t.trim().length>0)return t.trim();return e.slice(0,20)}function Ae(e,t){if(e==="06")return"DELIVERED";if(e==="1000")return"DELIVERED";if(typeof t==="string"){if(t.includes("전송 성공"))return"DELIVERED";if(t.includes("대기")||t.includes("처리중"))return"PENDING"}if(e==="00"||e==="01")return"PENDING";if(!e&&!t)return"UNKNOWN";return"FAILED"}function ge(e,t){return new Date(e.getTime()+t*24*60*60*1000)}function ye(e){let t=(n)=>n.toString().padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}`}function pe(e){if(typeof e!=="string")return;let t=e.trim();if(!t)return;let n=/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})$/.exec(t);if(!n)return;let r=Number(n[1]),i=Number(n[2]),o=Number(n[3]),s=Number(n[4]),u=Number(n[5]),p=Number(n[6]);if(!Number.isFinite(r)||!Number.isFinite(i)||!Number.isFinite(o)||!Number.isFinite(s)||!Number.isFinite(u)||!Number.isFinite(p))return;let m=new Date(r,i-1,o,s,u,p);if(Number.isNaN(m.getTime()))return;return m}function ie(e){let t=(n)=>n.toString().padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}function Ie(e){let t=(n)=>n.toString().padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())} ${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}`}async function Re(e){let{providerId:t,config:n,query:r,context:i}=e,o=r.providerMessageId.trim();if(!o)return P(new H(Q.INVALID_REQUEST,"providerMessageId is required",{providerId:t}));let s=U(r.to);if(!s)return P(new H(Q.INVALID_REQUEST,"to is required",{providerId:t}));let u=Number(o),p=Number.isFinite(u)?u:void 0,m=ie(ge(r.requestedAt,-1)),f=ie(new Date),g={pageNum:1,pageSize:15,phone:s,startDate:m,endDate:f,...p!==void 0?{seqNo:p}:{},...r.scheduledAt instanceof Date&&!Number.isNaN(r.scheduledAt.getTime())?{reserve:"Y"}:{}},x=`${n.baseUrl}/api/history/`;try{let a=await j(i,x,{method:"POST",headers:X(n),body:JSON.stringify(g)},t),y=await a.text(),_=V(y),l=J(_,{}),R=l.code,D=typeof R==="number"?R:void 0,d=typeof l.message==="string"&&l.message.length>0?l.message:"IWINV history query failed";if(!a.ok||D!==200)return P(new H(Z(D??oe(_)??a.status),d,{providerId:t,originalCode:R??a.status}));let T=l.list,S=Array.isArray(T)?T:[];if(S.length===0)return ee(null);let c=(()=>{if(p===void 0)return S[0];return S.find((b)=>O(b)&&b.seqNo===p)??S[0]})();if(!O(c))return ee(null);let E=typeof c.statusCode==="string"?c.statusCode:void 0,v=typeof c.statusCodeName==="string"?c.statusCodeName:void 0,I=pe(c.sendDate),K=pe(c.receiveDate),M=E==="OK"||typeof v==="string"&&v.includes("성공");return ee({providerId:t,providerMessageId:o,status:M?"DELIVERED":I?"FAILED":"PENDING",statusCode:E,statusMessage:v,sentAt:I,deliveredAt:M?K||I:void 0,failedAt:!M&&I?I:void 0,raw:c})}catch(a){return P(B(a,i?.signal,t))}}async function De(e){let{providerId:t,config:n,query:r,context:i}=e;if(!Y(n))return P(new H(Q.INVALID_REQUEST,"SMS v2 configuration missing (smsApiKey/smsAuthKey)",{providerId:t}));if(!n.smsCompanyId||n.smsCompanyId.length===0)return P(new H(Q.INVALID_REQUEST,"smsCompanyId required for history (config.smsCompanyId)",{providerId:t}));let o=r.providerMessageId.trim();if(!o)return P(new H(Q.INVALID_REQUEST,"providerMessageId is required",{providerId:t}));let s=U(r.to);if(!s)return P(new H(Q.INVALID_REQUEST,"to is required",{providerId:t}));let u=ge(r.requestedAt,-1),p=new Date;if(p.getTime()-u.getTime()>7776000000)return P(new H(Q.INVALID_REQUEST,"SMS history date range must be within 90 days",{providerId:t}));let g={version:"1.0",companyid:n.smsCompanyId,startDate:ye(u),endDate:ye(p),requestNo:o,pageNum:1,pageSize:15,phone:s},a={"Content-Type":"application/json;charset=UTF-8",secret:$(n)};if(typeof n.xForwardedFor==="string"&&n.xForwardedFor.length>0)a["X-Forwarded-For"]=n.xForwardedFor;let y=n.extraHeaders&&typeof n.extraHeaders==="object"?{...a,...n.extraHeaders}:a,_=`${q()}/api/history/`;try{let l=await j(i,_,{method:"POST",headers:y,body:JSON.stringify(g)},t),R=await l.text(),D=V(R),d=J(D,{}),T=d.resultCode,S=typeof T==="number"?T:typeof T==="string"?Number(T):NaN,c=typeof d.message==="string"&&d.message.length>0?d.message:"IWINV SMS history query failed";if(!l.ok||S!==0)return P(new H(Q.PROVIDER_ERROR,c,{providerId:t,originalCode:T??l.status}));let E=d.list,v=Array.isArray(E)?E:[];if(v.length===0)return ee(null);let I=(()=>{return v.find((W)=>{if(!O(W))return!1;let k=W.requestNo;return k!==void 0&&k!==null?String(k)===o:!1})??v[0]})();if(!O(I))return ee(null);let K=typeof I.sendStatusCode==="string"?I.sendStatusCode:void 0,M=typeof I.sendStatusMsg==="string"?I.sendStatusMsg:typeof I.sendStatus==="string"?I.sendStatus:void 0,G=pe(I.sendDate),b=Ae(K,M);return ee({providerId:t,providerMessageId:o,status:b,statusCode:K,statusMessage:M,sentAt:G,deliveredAt:b==="DELIVERED"?G:void 0,failedAt:b==="FAILED"?G:void 0,raw:I})}catch(l){return P(B(l,i?.signal,t))}}import{fail as N,KMsgError as h,KMsgErrorCode as C,ok as Ne}from"@k-msg/core";import{fail as xe,KMsgError as ke,KMsgErrorCode as Oe,ok as be}from"@k-msg/core";function Pe(e){let t=e.split(/[?#]/,1)[0]??e,n=t.lastIndexOf(".");if(n<=0||n===t.length-1)return"";return t.slice(n).toLowerCase()}function Se(e){switch(Pe(e)){case".jpg":case".jpeg":return"image/jpeg";case".png":return"image/png";case".gif":return"image/gif";case".webp":return"image/webp";default:return}}function Ce(e,t){let n=e&&typeof e==="object"?e:{},i=(n.media&&typeof n.media==="object"?n.media:void 0)?.image;if(i&&typeof i==="object"){if(i.bytes instanceof Uint8Array)return be({bytes:i.bytes,filename:typeof i.filename==="string"?i.filename:void 0,contentType:typeof i.contentType==="string"?i.contentType:void 0});if(i.blob instanceof Blob)return be({blob:i.blob,filename:typeof i.filename==="string"?i.filename:void 0,contentType:typeof i.contentType==="string"?i.contentType:void 0});if(typeof i.ref==="string"&&i.ref.trim().length>0)return xe(new ke(Oe.INVALID_REQUEST,"IWINV MMS caller must provide blob/bytes in options.media.image",{providerId:t,field:"media.image.ref"}))}let o=n.imageUrl;if(typeof o==="string"&&o.trim().length>0)return xe(new ke(Oe.INVALID_REQUEST,"IWINV MMS caller must provide blob/bytes in options.media.image",{providerId:t,field:"imageUrl"}));return be(void 0)}async function _e(e){if("blob"in e){let o=e.contentType||e.blob.type||"application/octet-stream",s=e.filename||"image",u=e.contentType&&e.contentType!==e.blob.type?new Blob([await e.blob.arrayBuffer()],{type:o}):e.blob;return{blob:u,filename:s,contentType:o,size:u.size}}let t=e.contentType||"application/octet-stream",n=new Uint8Array(e.bytes.byteLength);n.set(e.bytes);let r=new Blob([n],{type:t}),i=e.filename||"image";return{blob:r,filename:i,contentType:t,size:r.size}}function Me(e){if(Pe(e.filename).length>0)return e.filename;let n=Se(e.filename)==="image/png"?".png":Se(e.filename)==="image/gif"?".gif":Se(e.filename)==="image/webp"?".webp":e.contentType==="image/png"?".png":e.contentType==="image/gif"?".gif":e.contentType==="image/webp"?".webp":".jpg";return`${e.filename}${n}`}async function Ve(e){let{providerId:t,config:n,options:r,context:i}=e,o=r.templateId;if(!o||o.length===0)return N(new h(C.INVALID_REQUEST,"templateId is required for ALIMTALK",{providerId:t}));let s=r.options?.scheduledAt,u=s instanceof Date&&!Number.isNaN(s.getTime()),p=u?"Y":"N",m=u?ie(s):void 0,f=U(r.to);if(!f)return N(new h(C.INVALID_REQUEST,"to is required",{providerId:t}));let g=r.providerOptions?.templateParam,x=Array.isArray(g)?g.map((b)=>b===null||b===void 0?"":String(b)):Object.values(r.variables||{}).map((b)=>b===null||b===void 0?"":String(b)),a=r.failover,y=(typeof r.from==="string"&&r.from.length>0?r.from:n.senderNumber||n.smsSenderNumber)||"",_=y?U(y):"",l=typeof r.providerOptions?.reSend==="string"?r.providerOptions.reSend.trim().toUpperCase():"",R=l==="Y"||l==="N"?l:void 0,D=a?.enabled===!0?"Y":a?.enabled===!1?"N":void 0,d=R??D??(_?"Y":"N"),S=(typeof r.providerOptions?.resendCallback==="string"?U(r.providerOptions.resendCallback):"")||_;if(d==="Y"&&!S)return N(new h(C.INVALID_REQUEST,"resendCallback is required when reSend is 'Y' (options.from or providerOptions.resendCallback)",{providerId:t}));let c=typeof r.providerOptions?.resendType==="string"?r.providerOptions.resendType.trim().toUpperCase():"",E=c==="Y"||c==="N"?c:void 0;if(typeof r.providerOptions?.resendType==="string"&&r.providerOptions.resendType.length>0&&!E)return N(new h(C.INVALID_REQUEST,"resendType must be 'Y' or 'N'",{providerId:t}));let v=a?.fallbackChannel==="lms"?"Y":a?.fallbackChannel==="sms"?"N":void 0,I=typeof r.providerOptions?.resendTitle==="string"&&r.providerOptions.resendTitle.trim().length>0?r.providerOptions.resendTitle.trim():typeof a?.fallbackTitle==="string"&&a.fallbackTitle.trim().length>0?a.fallbackTitle.trim():void 0,K=typeof r.providerOptions?.resendContent==="string"&&r.providerOptions.resendContent.trim().length>0?r.providerOptions.resendContent.trim():typeof a?.fallbackContent==="string"&&a.fallbackContent.trim().length>0?a.fallbackContent.trim():void 0,M={templateCode:o,reserve:p,...m?{sendDate:m}:{},list:[{phone:f,templateParam:x.length>0?x:void 0}],reSend:d,...S?{resendCallback:S}:{},...E??v?{resendType:E??v}:{},...I?{resendTitle:I}:{},...K?{resendContent:K}:{}},G=`${n.baseUrl}${he(n)}`;try{let b=await j(i,G,{method:"POST",headers:X(n),body:JSON.stringify(M)},t),ce=await b.text(),W=V(ce),k=O(W)?W:{code:oe(W)??b.status,message:ce||String(W||"")};if(!b.ok||k.code!==200)return N(new h(Z(k.code),k.message||"IWINV send failed",{providerId:t,originalCode:k.code}));return Ne({messageId:r.messageId||crypto.randomUUID(),providerId:t,providerMessageId:typeof k.seqNo==="number"?String(k.seqNo):void 0,status:u?"PENDING":"SENT",type:r.type,to:r.to,raw:k})}catch(b){return N(B(b,i?.signal,t))}}async function Qe(e){let{providerId:t,config:n,options:r,to:i,from:o,text:s,scheduledAtValid:u,scheduledAt:p,context:m}=e;if(r.type!=="MMS")return N(new h(C.INVALID_REQUEST,"IWINVProvider: MMS handler called with non-MMS options",{providerId:t,type:r.type}));let f=fe(s,r.subject),g=Ce(r,t);if(g.isFailure)return g;let x=g.value;if(!x)return N(new h(C.INVALID_REQUEST,"image is required for MMS; caller must provide options.media.image.blob or bytes",{providerId:t}));let a;try{a=await _e(x)}catch(d){return N(de(d,t))}if(a.size>102400)return N(new h(C.INVALID_REQUEST,"MMS image must be <= 100KB",{providerId:t,bytes:a.size}));let y=new FormData;if(y.append("version","1.0"),y.append("from",o),y.append("to",i),y.append("title",f),y.append("text",s),u&&p)y.append("date",Ie(p));y.append("image",a.blob,Me(a));let l={secret:$(n)};if(typeof n.xForwardedFor==="string"&&n.xForwardedFor.length>0)l["X-Forwarded-For"]=n.xForwardedFor;let R={...l};if(n.extraHeaders&&typeof n.extraHeaders==="object")for(let[d,T]of Object.entries(n.extraHeaders)){if(d.toLowerCase()==="content-type")continue;R[d]=T}let D=`${q()}/api/v2/send/`;try{let d=await j(m,D,{method:"POST",headers:R,body:y},t),T=await d.text(),S=V(T),c=O(S)?S:{resultCode:S},E=c.resultCode??c.code,v=ue(E),I=typeof c.message==="string"&&c.message.length>0?c.message:le(v,"MMS send failed");if(!(d.ok&&v==="0"))return N(new h(me(v,d.ok),I,{providerId:t,originalCode:E}));let M=typeof c.requestNo==="string"&&c.requestNo.length>0?c.requestNo:typeof c.msgid==="string"&&c.msgid.length>0?c.msgid:void 0;return Ne({messageId:r.messageId||crypto.randomUUID(),providerId:t,providerMessageId:M,status:u?"PENDING":"SENT",type:r.type,to:r.to,raw:c})}catch(d){return N(B(d,m?.signal,t))}}async function Fe(e){let{providerId:t,config:n,options:r,context:i}=e;if(!Y(n))return N(new h(C.INVALID_REQUEST,"SMS v2 configuration missing (smsApiKey/smsAuthKey)",{providerId:t}));let o=U(r.to);if(!o)return N(new h(C.INVALID_REQUEST,"to is required",{providerId:t}));let s=r.text;if(!s||s.trim().length===0)return N(new h(C.INVALID_REQUEST,"text is required for SMS/LMS/MMS",{providerId:t}));let u=(typeof r.from==="string"&&r.from.length>0?r.from:n.smsSenderNumber||n.senderNumber)||"",p=u?U(u):"";if(!p)return N(new h(C.INVALID_REQUEST,"from is required for SMS/LMS/MMS (options.from or config.smsSenderNumber)",{providerId:t}));let m=r.options?.scheduledAt,f=m instanceof Date&&!Number.isNaN(m.getTime());if(r.type==="MMS")return await Qe({providerId:t,config:n,options:r,to:o,from:p,text:s,scheduledAtValid:f,scheduledAt:f?m:void 0,context:i});let g={version:"1.0",from:p,to:[o],text:s};if(r.type==="LMS")g.title=fe(s,r.subject);else{let l=typeof r.providerOptions?.msgType==="string"&&r.providerOptions.msgType.trim().length>0?r.providerOptions.msgType.trim():void 0;g.msgType=l||r.type}if(f)g.date=Ie(m);let a={"Content-Type":"application/json;charset=UTF-8",secret:$(n)};if(typeof n.xForwardedFor==="string"&&n.xForwardedFor.length>0)a["X-Forwarded-For"]=n.xForwardedFor;let y=n.extraHeaders&&typeof n.extraHeaders==="object"?{...a,...n.extraHeaders}:a,_=`${q()}/api/v2/send/`;try{let l=await j(i,_,{method:"POST",headers:y,body:JSON.stringify(g)},t),R=await l.text(),D=V(R),d=O(D)?D:{resultCode:D},T=d.resultCode??d.code,S=ue(T),c=typeof d.message==="string"&&d.message.length>0?d.message:le(S,"SMS send failed");if(!(l.ok&&S==="0"))return N(new h(me(S,l.ok),c,{providerId:t,originalCode:T}));let v=typeof d.requestNo==="string"&&d.requestNo.length>0?d.requestNo:typeof d.msgid==="string"&&d.msgid.length>0?d.msgid:void 0;return Ne({messageId:r.messageId||crypto.randomUUID(),providerId:t,providerMessageId:v,status:f?"PENDING":"SENT",type:r.type,to:r.to,raw:d})}catch(l){return N(B(l,i?.signal,t))}}function We(e){return{...e,baseUrl:we,sendEndpoint:e.sendEndpoint||"/api/v2/send/"}}function je(){return{apiKey:L("IWINV_API_KEY")||"",smsApiKey:L("IWINV_SMS_API_KEY"),smsAuthKey:L("IWINV_SMS_AUTH_KEY"),smsCompanyId:L("IWINV_SMS_COMPANY_ID"),senderNumber:L("IWINV_SENDER_NUMBER")||L("IWINV_SMS_SENDER_NUMBER"),smsSenderNumber:L("IWINV_SMS_SENDER_NUMBER"),sendEndpoint:L("IWINV_SEND_ENDPOINT")||"/api/v2/send/",xForwardedFor:L("IWINV_X_FORWARDED_FOR"),debug:L("NODE_ENV")==="development"}}class te{id="iwinv";name="IWINV Messaging Provider";supportedTypes;transportCapabilities={abortSignal:"supported",injectableFetch:"supported"};config;getOnboardingSpec(){let e=ve(this.id);if(!e)throw new w(A.INVALID_REQUEST,`Onboarding spec missing for provider: ${this.id}`,{providerId:this.id});return e}constructor(e){if(!e||typeof e!=="object")throw new w(A.INVALID_REQUEST,"IWINVProvider requires a config object",{providerId:this.id});if(!e.apiKey||e.apiKey.length===0)throw new w(A.INVALID_REQUEST,"IWINVProvider requires `apiKey` configuration",{providerId:this.id});this.config=We(e);let t=["ALIMTALK"];if(Y(this.config))t.push("SMS","LMS","MMS");this.supportedTypes=t}async healthCheck(){let e=Date.now(),t=[];try{try{new URL(this.config.baseUrl)}catch{t.push("Invalid baseUrl")}if(Y(this.config))try{new URL(q())}catch{t.push("Invalid smsBaseUrl")}return{healthy:t.length===0,issues:t,latencyMs:Date.now()-e,data:{provider:this.id,baseUrl:this.config.baseUrl,smsBaseUrl:q()}}}catch(n){return t.push(n instanceof Error?n.message:String(n)),{healthy:!1,issues:t,latencyMs:Date.now()-e}}}async send(e,t){let n=e.messageId||crypto.randomUUID(),r={...e,messageId:n};switch(r.type){case"ALIMTALK":return Ve({providerId:this.id,config:this.config,options:r,context:t});case"SMS":case"LMS":case"MMS":return Fe({providerId:this.id,config:this.config,options:r,context:t});default:return F(new w(A.INVALID_REQUEST,`IWINVProvider does not support type ${r.type}`,{providerId:this.id,type:r.type}))}}async getDeliveryStatus(e,t){switch(e.type){case"ALIMTALK":return Re({providerId:this.id,config:this.config,query:e,context:t});case"SMS":case"LMS":case"MMS":return De({providerId:this.id,config:this.config,query:e,context:t});default:return F(new w(A.INVALID_REQUEST,`IWINVProvider does not support type ${e.type}`,{providerId:this.id,type:e.type}))}}async getBalance(e){let t=e?.channel??"ALIMTALK";switch(t){case"ALIMTALK":return this.getAlimTalkBalance(t);case"SMS":case"LMS":case"MMS":return this.getSmsBalance(t);default:return F(new w(A.INVALID_REQUEST,`IWINVProvider does not support balance query for type ${t}`,{providerId:this.id,type:t}))}}async getAlimTalkBalance(e){let t=`${this.config.baseUrl}/api/charge/`;try{let n=await fetch(t,{method:"POST",headers:X(this.config),body:JSON.stringify({})}),r=await n.text(),i=V(r),o=J(i,{}),s=o.code,u=typeof s==="string"?Number(s):void 0,p=typeof s==="number"?s:typeof u==="number"&&Number.isFinite(u)?u:void 0,m=typeof o.message==="string"&&o.message.length>0?o.message:"IWINV AlimTalk charge query failed";if(!n.ok||p!==200)return F(new w(Z(p??n.status),m,{providerId:this.id,originalCode:s??n.status}));let f=o.charge,g=typeof f==="number"?f:typeof f==="string"?Number(f):NaN;if(!Number.isFinite(g))return F(new w(A.PROVIDER_ERROR,"Invalid charge value from IWINV AlimTalk charge API",{providerId:this.id,raw:o}));return Le({providerId:this.id,channel:e,amount:g,currency:"KRW",raw:o})}catch(n){return F(new w(A.NETWORK_ERROR,n instanceof Error?n.message:String(n),{providerId:this.id}))}}async getSmsBalance(e){if(!this.config.smsApiKey||!this.config.smsAuthKey)return F(new w(A.INVALID_REQUEST,"smsApiKey and smsAuthKey are required for SMS/LMS/MMS balance query",{providerId:this.id}));let n={"Content-Type":"application/json;charset=UTF-8",secret:$(this.config)};if(typeof this.config.xForwardedFor==="string"&&this.config.xForwardedFor.length>0)n["X-Forwarded-For"]=this.config.xForwardedFor;let r=this.config.extraHeaders&&typeof this.config.extraHeaders==="object"?{...n,...this.config.extraHeaders}:n,i=`${q()}/api/charge/`;try{let o=await fetch(i,{method:"POST",headers:r,body:JSON.stringify({version:"1.0"})}),s=await o.text(),u=V(s),p=J(u,{}),m=p.code??p.resultCode,f=typeof m==="string"?Number(m):void 0,g=typeof m==="number"?m:typeof f==="number"&&Number.isFinite(f)?f:NaN,x=typeof p.message==="string"&&p.message.length>0?p.message:"IWINV SMS charge query failed";if(!o.ok||g!==0)return F(new w(A.PROVIDER_ERROR,x,{providerId:this.id,originalCode:m??o.status}));let a=p.charge,y=typeof a==="number"?a:typeof a==="string"?Number(a):NaN;if(!Number.isFinite(y))return F(new w(A.PROVIDER_ERROR,"Invalid charge value from IWINV SMS charge API",{providerId:this.id,raw:p}));return Le({providerId:this.id,channel:e,amount:y,currency:"KRW",raw:p})}catch(o){return F(new w(A.NETWORK_ERROR,o instanceof Error?o.message:String(o),{providerId:this.id}))}}}var Ye=(e)=>new te(e),Ue=()=>{let e=je();if(!e.apiKey)throw new w(A.INVALID_REQUEST,"IWINV_API_KEY environment variable is required",{providerId:"iwinv"});return new te(e)};class qe{static create(e){return new te(e)}static createDefault(){return Ue()}}var zt={SUCCESS:200,AUTH_FAILED:201,BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,TOO_MANY_REQUESTS:429,INTERNAL_SERVER_ERROR:500};export{te as default,Ye as createIWINVSendProvider,Ue as createDefaultIWINVSendProvider,zt as IWINV_STATUS_CODES,qe as IWINVSendProviderFactory,te as IWINVSendProvider};
//# debugId=3D47A7CDD85D35F864756E2164756E21
//# sourceMappingURL=send.mjs.map