@apostlejs/whatsapp 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("zod");function t(e){return e&&e.__esModule?e:{default:e}}var a=t(require("node:crypto")),n=Object.defineProperty,r=(e,t)=>{for(var a in t)n(e,a,{get:t[a],enumerable:!0})},s={};r(s,{endpoints:()=>c,flowAction:()=>p,flowCanSendMessageStatus:()=>w,flowCategory:()=>d,flowMediaData:()=>u,flowMetadata:()=>f,flowScreen:()=>m,flowStatus:()=>y,flowValidationError:()=>g,flowsEndpoints:()=>o,messagesEndpoints:()=>i,wabaEndpoints:()=>l});var o={create:{url:"/{waba_id}/flows",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},updateMetadata:{url:"/{flow_id}",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},readMany:{url:"/{waba_id}/flows",method:"get",response:null},delete:{url:"/{flow_id}",method:"delete",response:null},read:{url:"/{flow_id}",method:"get",request:{query:null},response:null},updateJson:{url:"/{flow_id}/assets",method:"post",request:{body:null},response:null},getPreview:{url:"/{flow_id}?fields=preview.invalidate(false)",method:"get",response:null},publish:{url:"/{flow_id}/publish",method:"post",response:null}},i={send:{url:"/{number_id}/messages",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null}},l={updateEncryption:{url:"/{number_id}/whatsapp_business_encryption",method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},request:{body:null}},registerNumber:{url:"/{number_id}/register",method:"post",headers:{"Content-Type":"application/json"},request:{body:null}}},c={flows:o,messages:i,waba:l},p=e.z.enum(["INIT","BACK","data_exchange","navigate","ping"]),d=e.z.enum(["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]),u=e.z.object({media_id:e.z.string(),cdn_url:e.z.string(),file_name:e.z.string(),encryption_metadata:e.z.object({encrypted_hash:e.z.string(),iv:e.z.string(),encryption_key:e.z.string(),hmac_key:e.z.string(),plaintext_hash:e.z.string()})}),f=e.z.object({name:e.z.string(),categories:e.z.array(d),application_id:e.z.string().optional(),endpoint_uri:e.z.string().optional()}),m=e.z.union([e.z.literal("SUCCESS"),e.z.string()]),y=e.z.enum(["DRAFT","PUBLISHED","DEPRECATED","BLOCKED","THROTTLED"]),w=e.z.enum(["AVAILABLE","LIMITED","BLOCKED"]),g=e.z.object({error:e.z.string(),error_type:e.z.string(),message:e.z.string(),line_start:e.z.number(),line_end:e.z.number(),column_start:e.z.number(),column_end:e.z.number()}),_={};r(_,{actions:()=>j,flows:()=>R,parsers:()=>K,security:()=>q,toGraphLanguageTag:()=>z,utils:()=>G});var h={};r(h,{create:()=>A,delete:()=>C,get:()=>N,getMany:()=>O,getPreview:()=>T,publish:()=>x,updateJson:()=>P,updateMetadata:()=>I});var b={};r(b,{settings:()=>E});var v={},E={setup:e=>{Object.assign(v,e)},get:e=>{const t=v[e]??process.env[e];if(!t)throw new Error(`Missing environment variable: ${e}`);return t}};async function S(e,t,a){const n=`https://graph.facebook.com/v${E.get("GRAPH_API_VERSION")}`,r={Authorization:`Bearer ${E.get("META_APP_ACCESS_TOKEN")}`};let s=e.url;const o={...r,...e.headers,...t.headers},i=new URLSearchParams(t.query).toString();"/"===s[0]&&(s=s.slice(1)),s=`${n}/${s}`,i&&(s=`${s}?${i}`);const l={waba_id:E.get("WHATSAPP_ACCOUNT_ID"),number_id:E.get("WHATSAPP_NUMBER_ID"),...t.params};for(const e in l)s=s.replace(`{${e}}`,l[e]);const c=t.body;let p;if(c&&a?.asFormData){const e=new FormData;for(const t in c)e.append(t,c[t]);p=e}else c&&a?.asUrlEncoded?p=new URLSearchParams(c).toString():c&&(p=JSON.stringify(c));return await fetch(s,{method:e.method,headers:o,body:p}).then((e=>e.json())).then((e=>{if("error"in e)throw{response:{data:e.error}};return e})).catch((async e=>{const t=e.response.data.message,a=e.response.data.error_data?.details||"",n=t.match(/^\(#\d+\)/),r=n?n[0]:"",s=t.replace(/^\(#\d+\)\s*/,""),o=a.startsWith(s)?`${r} ${a}`:`${t}: ${a}`;throw e.response.data.message=o,e.response.data}))}async function A(e){const t=c.flows.create;return await S(t,{body:e})}async function I(e,t){const a=c.flows.updateMetadata;return await S(a,{body:t,params:{flow_id:e}})}async function P(e,t){const a=c.flows.updateJson,n={name:"flow.json",asset_type:"FLOW_JSON",file:new Blob([JSON.stringify(t,null,2)],{type:"application/json"})};return await S(a,{params:{flow_id:e},body:n},{asFormData:!0})}async function T(e,t){const a=c.flows.getPreview,{preview:n}=await S(a,{params:{flow_id:e}}),r=n.preview_url.replaceAll("\\",""),s=Object.entries(t??{}).reduce(((e,[t,a])=>(e[t]="flow_action_payload"===t?encodeURIComponent(JSON.stringify(a)):a.toString(),e)),{});return`${r}&${new URLSearchParams(s).toString()}`}async function C(e){const t=c.flows.delete;return await S(t,{params:{flow_id:e}})}async function N(e,t){const a=c.flows.read;return await S(a,{params:{flow_id:e},query:{fields:t?.fields.join(",")??""}})}async function O(){const e=c.flows.readMany;return await S(e,{})}async function x(e){const t=c.flows.publish;return await S(t,{params:{flow_id:e}})}var B=e=>e.split("?")[0],R={createToken:({flow_name:e,flow_parameters:t,chatId:a})=>{const n=new URLSearchParams({chat_id:a});if(n.set("flow_identifier",crypto.randomUUID()),t)for(const[e,a]of Object.entries(t))n.set(e,a.toString());return`${e}?${decodeURIComponent(n.toString())}`},getName:B,destructureFlowToken:e=>{const t=B(e);let a=e.split("?")?.[1];"&"===a?.[0]&&(a=a.slice(1));const n=new URLSearchParams(a),r=n.get("chat_id")||n.get("chatId"),s=n.get("flow_identifier")||n.get("flowIdentifier"),o={};for(const[e,t]of n.entries())"chat_id"!==e&&"chatId"!==e&&"flow_identifier"!==e&&"flowIdentifier"!==e&&(o[e]=t);return{paramsString:a,flowName:t,chatId:r,flowIdentifier:s,flowParameters:o}}},U={"ar-SA":"ar","bn-BD":"bn","bn-IN":"bn","cs-CZ":"cs","da-DK":"da","de-AT":"de","de-CH":"de","de-DE":"de","el-GR":"el","en-AU":"en","en-CA":"en","en-GB":"en_GB","en-IE":"en","en-IN":"en","en-NZ":"en","en-ZA":"en","en-US":"en_US","es-AR":"es_AR","es-CL":"es","es-CO":"es","es-ES":"es_ES","es-MX":"es_MX","es-US":"es","fi-FI":"fi","fr-BE":"fr","fr-CA":"fr","fr-CH":"fr","fr-FR":"fr","he-IL":"he","hi-IN":"hi","hu-HU":"hu","id-ID":"id","it-CH":"it","it-IT":"it","ja-JP":"ja","ko-KR":"ko","nl-BE":"nl","nl-NL":"nl","no-NO":"nb","pl-PL":"pl","pt-PT":"pt_PT","pt-BR":"pt_BR","ro-RO":"ro","ru-RU":"ru","sk-SK":"sk","sv-SE":"sv","ta-IN":"ta","ta-LK":"ta","th-TH":"th","tr-TR":"tr","zh-CN":"zh_CN","zh-HK":"zh_HK","zh-TW":"zh_TW"},z=e=>{const t=U[e];return void 0===t?"en":t},M=e=>{const t={to:e.to,messaging_product:"whatsapp",recipient_type:"individual"};return e.reply&&(t.context={message_id:e.reply}),e.showUrlPreviewImage&&(t.preview_url=e.showUrlPreviewImage),t},D={flow:e=>{if("flow"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{flow:n,...r}=t,s=M(a);n.mode||(n.mode=E.get("WHATSAPP_FLOWS_MODE"));const o=R.createToken({chatId:s.to,flow_name:n.name,flow_parameters:n.parameters});return{type:"interactive",...s,interactive:{...r,type:"flow",action:{name:"flow",parameters:{flow_name:n.name,flow_token:o,flow_message_version:"3",flow_cta:n.button,flow_action:n.action??"navigate",mode:n.mode,...n.payload&&{flow_action_payload:{...n.payload,screen:n.payload.screen.toUpperCase()}}}}}}},text:e=>{if("text"!==e.message.type)throw new Error("Invalid type");const{message:{text:t},...a}=e,n={type:"text",text:{body:t},...M(a)};return e.message.previewUrl&&n.text&&(n.text.preview_url=e.message.previewUrl),n},list:e=>{if("list"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{list:n,type:r,...s}=t;return{...M(a),type:"interactive",interactive:{...s,type:r,action:n}}},button:e=>{if("button"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{buttons:n,type:r,...s}=t;return{...M(a),type:"interactive",interactive:{...s,type:r,action:{buttons:n.map((e=>({reply:{id:e.id,title:e.text},type:"reply"})))}}}},template:e=>{if("template"!==e.message.type)throw new Error("Invalid type");const{message:{type:t,...a},...n}=e,r={...M(n),type:t,template:{...a,language:{code:z(a.language)}}};return r.template&&(r.template.namespace=process.env.WHATSAPP_MESSAGE_NAMESPACE),r},media:e=>{if("media"!==e.message.type)throw new Error("Invalid type");const{message:{type:t,...a},...n}=e,r=Object.entries(a)[0],[s,{ref:o,...i}]=r,l=i,c=M(n);return Number.isNaN(Number(o))?l.link=o:l.id=o,{type:s,[s]:l,...c}},contact:e=>{if("contact"!==e.message.type)throw new Error("Invalid type");const{message:{contacts:t},...a}=e;return{type:"contacts",contacts:t,...M(a)}}};function L(e){const{encrypted_aes_key:t,encrypted_flow_data:n,initial_vector:r}=e,s=a.default.createPrivateKey({key:E.get("WHATSAPP_ACCOUNT_ENCRYPTION_PRIVATE_KEY"),passphrase:E.get("WHATSAPP_ACCOUNT_ENCRYPTION_PASSPHRASE")}),o=a.default.privateDecrypt({key:s,padding:a.default.constants.RSA_PKCS1_OAEP_PADDING,oaepHash:"sha256"},Buffer.from(t,"base64")),i=Buffer.from(n,"base64"),l=Buffer.from(r,"base64"),c=i.subarray(0,-16),p=i.subarray(-16),d=a.default.createDecipheriv("aes-128-gcm",o,l);d.setAuthTag(p);const u=Buffer.concat([d.update(c),d.final()]).toString("utf-8");return{payload:JSON.parse(u),encryptionMetadata:{aesKeyBuffer:o,initialVectorBuffer:l}}}function H(e){const t=new URL(e.url).searchParams,a=t.get("hub.verify_token"),n=t.get("hub.challenge");return!(!a||!n)&&a===E.get("WHATSAPP_WEBHOOK_KEY")&&n}async function k(e,t){const n=e.headers.get("x-hub-signature-256");if(!n)return!1;const r=n.replace("sha256=",""),s=a.default.createHmac("sha256",E.get("WHATSAPP_WEBHOOK_KEY")).update(t,"utf-8").digest("hex");return!!a.default.timingSafeEqual(Buffer.from(r),Buffer.from(s))}var K={toGraph:{sendMessage:e=>D[e.message.type](e),flowResponse:(e,t)=>{if(!t.data)throw new Error("Missing data in flow response");if(!t.screen)throw new Error("Missing screen in flow response");if(t.screen=t.screen.toUpperCase(),"SUCCESS"===t.screen){const a=t.data??{};t.data={extension_message_response:{params:{flow_token:e,...a}}}}return t}},toSDK:{webhook:async function(e){try{if("GET"===e.method){const t=H(e);if(!t)throw new Error("Invalid request");return{type:"healthCheck",payload:t}}const a=await e.text(),n=JSON.parse(a);if("entry"in n){if(!k(e,a))throw new Error("Invalid Signature");const r=n.entry.flatMap((e=>e.changes)).filter((e=>"messages"===e.field)).flatMap((e=>e.value.messages)).filter(Boolean).map((e=>function(e){const t=e.from,a={id:e.id,type:e.type,timestamp:e.timestamp,metadata:{forwarded:e.context?.forwarded,frequentlyForwarded:e.context?.frequently_forwarded}},n=function(e){if(!(e.audio||e.document||e.video||e.image))return null;const t=e.audio?.id??e.document?.id??e.image?.id??e.video?.id,a=e.audio?.mime_type??e.document?.mime_type??e.image?.mime_type??e.video?.mime_type,n=["audio","document","image","video"].find((t=>e[t])),r=e.image?.sha256??e.document?.sha256??e.video?.sha256;return{id:t,type:n,caption:e.document?.caption??e.image?.caption??e.video?.caption??null,mime_type:a,sha256:r,filename:e.document?.filename??e.video?.filename}}(e),r=function(e){return e.text?.body??e.button?.text??e.interactive?.button_reply?.title??e.interactive?.list_reply?.title??e.document?.caption??null}(e),s=function(e){return e.interactive?{...e.interactive?.button_reply||e.button?{button:{id:e.interactive?.button_reply?.id??null,title:e.button?.text??e.interactive.button_reply?.title,payload:e.button?.payload??null}}:{},...e.interactive.list_reply?{selectedOption:{id:e.interactive.list_reply.id,title:e.interactive.list_reply.title,description:e.interactive.list_reply.description}}:{},...e.interactive.nfm_reply?{flowResponse:JSON.parse(e.interactive.nfm_reply.response_json)}:{}}:null}(e);return{...a,...s&&{interaction:s},...n&&{media:n},text:r,chatId:t}}(e)));return{type:"application",payload:{messageReceived:(t=r,t.length?t:void 0)}}}if("encrypted_flow_data"in n)return{type:"flowExchange",payload:{...L(n),pingResponse:{data:{status:"active"}}}};throw new Error("Unrecognized event")}catch{return{error:!0}}var t}}},j={messages:{send:async function(e){const t=K.toGraph.sendMessage(e),a=c.messages.send;return await S(a,{body:t})}},flows:{...h},waba:{registerNumber:async function({dataRegion:e,pin:t}){const a=c.waba.registerNumber,n={messaging_product:"whatsapp",pin:t};return e&&(n.data_localization_region=e),await S(a,{body:n},{asUrlEncoded:!0})},encryption:{upload:async function(e){const t=c.waba.updateEncryption,a={business_public_key:e};return await S(t,{body:a},{asUrlEncoded:!0})}}}};async function W(e){return await fetch(e).then((async e=>{const t=await e.arrayBuffer();return Buffer.from(t)}))}var q={verifyHub:H,verifySignature:k,generateWabaEncryption:async function(){const e=a.default.randomUUID(),{publicKey:t,privateKey:n}=a.default.generateKeyPairSync("rsa",{modulusLength:2048,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem",cipher:"aes-256-cbc",passphrase:e}});return{passphrase:e,publicKey:t,privateKey:n}},decryptFlowBody:L,encryptFlowResponse:function(e,t){const n=[];for(const e of Buffer.from(t.initialVectorBuffer).entries())n.push(~e[1]);const r=a.default.createCipheriv("aes-128-gcm",Buffer.from(t.aesKeyBuffer),Buffer.from(n));return Buffer.concat([r.update(JSON.stringify(e),"utf-8"),r.final(),r.getAuthTag()]).toString("base64")},decryptFlowMedia:async function(e){const t=[];for(const n of e){let{cdn_url:e,encryption_metadata:r}=n;if("EXAMPLE_DATA__CDN_URL_WILL_COME_IN_THIS_FIELD"===e){e="https://picsum.photos/seed/picsum/200";const a=await W(e);t.push(a);continue}const s=await W(e),{iv:o,encryption_key:i,hmac_key:l,encrypted_hash:c,plaintext_hash:p}=r,d={iv:Buffer.from(o,"base64"),encryption_key:Buffer.from(i,"base64"),hmac_key:Buffer.from(l,"base64")};if(a.default.createHash("sha256").update(s).digest("base64")!==c)throw new Error("Encrypted hash validation failed");const u=s.subarray(0,s.length-10),f=s.subarray(-10);if(!a.default.createHmac("sha256",d.hmac_key).update(Buffer.concat([d.iv,u])).digest().subarray(0,10).equals(f))throw new Error("HMAC validation failed");const m=a.default.createDecipheriv("aes-256-cbc",d.encryption_key,d.iv),y=m.update(u),w=Buffer.concat([y,m.final()]);if(a.default.createHash("sha256").update(w).digest("base64")!==p)throw new Error("Decrypted media hash validation failed");t.push(w)}return t}},G={flows:R},$=null;exports.actions=j,exports.createWhatsapp=()=>$||($=(()=>{const{settings:e}=b,{actions:t,flows:a,parsers:n,security:r,utils:o,toGraphLanguageTag:i}=_,{endpoints:l}=s;return{settings:e,sdk:{actions:t,flows:a,parsers:n,security:r,utils:o,toGraphLanguageTag:i},graph:{endpoints:l}}})()),exports.endpoints=c,exports.flowAction=p,exports.flowCanSendMessageStatus=w,exports.flowCategory=d,exports.flowMediaData=u,exports.flowMetadata=f,exports.flowScreen=m,exports.flowStatus=y,exports.flowValidationError=g,exports.flows=R,exports.flowsEndpoints=o,exports.messagesEndpoints=i,exports.parsers=K,exports.security=q,exports.toGraphLanguageTag=z,exports.utils=G,exports.wabaEndpoints=l;
1
+ "use strict";var e=require("zod");function t(e){return e&&e.__esModule?e:{default:e}}var a=t(require("node:crypto")),n=Object.defineProperty,r=(e,t)=>{for(var a in t)n(e,a,{get:t[a],enumerable:!0})},o={};r(o,{endpoints:()=>c,flowAction:()=>p,flowCanSendMessageStatus:()=>w,flowCategory:()=>d,flowMediaData:()=>u,flowMetadata:()=>f,flowScreen:()=>m,flowStatus:()=>y,flowValidationError:()=>_,flowsEndpoints:()=>s,messagesEndpoints:()=>i,wabaEndpoints:()=>l});var s={create:{url:"/{waba_id}/flows",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},updateMetadata:{url:"/{flow_id}",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},readMany:{url:"/{waba_id}/flows",method:"get",response:null},delete:{url:"/{flow_id}",method:"delete",response:null},read:{url:"/{flow_id}",method:"get",request:{query:null},response:null},updateJson:{url:"/{flow_id}/assets",method:"post",request:{body:null},response:null},getPreview:{url:"/{flow_id}?fields=preview.invalidate(false)",method:"get",response:null},publish:{url:"/{flow_id}/publish",method:"post",response:null}},i={send:{url:"/{number_id}/messages",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null}},l={updateEncryption:{url:"/{number_id}/whatsapp_business_encryption",method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},request:{body:null}},registerNumber:{url:"/{number_id}/register",method:"post",headers:{"Content-Type":"application/json"},request:{body:null}}},c={flows:s,messages:i,waba:l},p=e.z.enum(["INIT","BACK","data_exchange","navigate","ping"]),d=e.z.enum(["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]),u=e.z.object({media_id:e.z.string(),cdn_url:e.z.string(),file_name:e.z.string(),encryption_metadata:e.z.object({encrypted_hash:e.z.string(),iv:e.z.string(),encryption_key:e.z.string(),hmac_key:e.z.string(),plaintext_hash:e.z.string()})}),f=e.z.object({name:e.z.string(),categories:e.z.array(d),application_id:e.z.string().optional(),endpoint_uri:e.z.string().optional()}),m=e.z.union([e.z.literal("SUCCESS"),e.z.string()]),y=e.z.enum(["DRAFT","PUBLISHED","DEPRECATED","BLOCKED","THROTTLED"]),w=e.z.enum(["AVAILABLE","LIMITED","BLOCKED"]),_=e.z.object({error:e.z.string(),error_type:e.z.string(),message:e.z.string(),line_start:e.z.number(),line_end:e.z.number(),column_start:e.z.number(),column_end:e.z.number()}),g={};r(g,{actions:()=>j,flows:()=>R,parsers:()=>K,security:()=>G,toGraphLanguageTag:()=>z,utils:()=>q});var h={};r(h,{create:()=>A,delete:()=>C,get:()=>N,getMany:()=>O,getPreview:()=>T,publish:()=>x,updateJson:()=>P,updateMetadata:()=>I});var b={};r(b,{settings:()=>E});var v={},E={setup:e=>{Object.assign(v,e)},get:e=>{const t=v[e]??process.env[e];if(!t)throw new Error(`Missing environment variable: ${e}`);return t}};async function S(e,t,a){const n=`https://graph.facebook.com/v${E.get("GRAPH_API_VERSION")}`,r={Authorization:`Bearer ${E.get("META_APP_ACCESS_TOKEN")}`};let o=e.url;const s={...r,...e.headers,...t.headers},i=new URLSearchParams(t.query).toString();"/"===o[0]&&(o=o.slice(1)),o=`${n}/${o}`,i&&(o=`${o}?${i}`);const l={waba_id:E.get("WHATSAPP_ACCOUNT_ID"),number_id:E.get("WHATSAPP_NUMBER_ID"),...t.params};for(const e in l)o=o.replace(`{${e}}`,l[e]);const c=t.body;let p;if(c&&a?.asFormData){const e=new FormData;for(const t in c)e.append(t,c[t]);p=e}else c&&a?.asUrlEncoded?p=new URLSearchParams(c).toString():c&&(p=JSON.stringify(c));return await fetch(o,{method:e.method,headers:s,body:p}).then((e=>e.json())).then((e=>{if("error"in e)throw{response:{data:e.error}};return e})).catch((async e=>{const t=e.response.data.message,a=e.response.data.error_data?.details||"",n=t.match(/^\(#\d+\)/),r=n?n[0]:"",o=t.replace(/^\(#\d+\)\s*/,""),s=a.startsWith(o)?`${r} ${a}`:`${t}: ${a}`;throw e.response.data.message=s,e.response.data}))}async function A(e){const t=c.flows.create;return await S(t,{body:{name:e.name,categories:e.categories,clone_flow_id:e.clone_flow_id,endpoint_uri:e.endpoint_uri,publish:e.publish,flow_json:JSON.stringify(e.flow_json,null,2)}})}async function I(e,t){const a=c.flows.updateMetadata;return await S(a,{body:t,params:{flow_id:e}})}async function P(e,t){const a=c.flows.updateJson,n={name:"flow.json",asset_type:"FLOW_JSON",file:new Blob([JSON.stringify(t,null,2)],{type:"application/json"})};return await S(a,{params:{flow_id:e},body:n},{asFormData:!0})}async function T(e,t){const a=c.flows.getPreview,{preview:n}=await S(a,{params:{flow_id:e}}),r=n.preview_url.replaceAll("\\",""),o=Object.entries(t??{}).reduce(((e,[t,a])=>(e[t]="flow_action_payload"===t?encodeURIComponent(JSON.stringify(a)):a.toString(),e)),{});return`${r}&${new URLSearchParams(o).toString()}`}async function C(e){const t=c.flows.delete;return await S(t,{params:{flow_id:e}})}async function N(e,t){const a=c.flows.read;return await S(a,{params:{flow_id:e},query:{fields:t?.fields.join(",")??""}})}async function O(){const e=c.flows.readMany;return await S(e,{})}async function x(e){const t=c.flows.publish;return await S(t,{params:{flow_id:e}})}var B=e=>e.split("?")[0],R={createToken:({flow_name:e,flow_parameters:t,chatId:a})=>{const n=new URLSearchParams({chat_id:a});if(n.set("flow_identifier",crypto.randomUUID()),t)for(const[e,a]of Object.entries(t))n.set(e,a.toString());return`${e}?${decodeURIComponent(n.toString())}`},getName:B,destructureFlowToken:e=>{const t=B(e);let a=e.split("?")?.[1];"&"===a?.[0]&&(a=a.slice(1));const n=new URLSearchParams(a),r=n.get("chat_id")||n.get("chatId"),o=n.get("flow_identifier")||n.get("flowIdentifier"),s={};for(const[e,t]of n.entries())"chat_id"!==e&&"chatId"!==e&&"flow_identifier"!==e&&"flowIdentifier"!==e&&(s[e]=t);return{paramsString:a,flowName:t,chatId:r,flowIdentifier:o,flowParameters:s}}},U={"ar-SA":"ar","bn-BD":"bn","bn-IN":"bn","cs-CZ":"cs","da-DK":"da","de-AT":"de","de-CH":"de","de-DE":"de","el-GR":"el","en-AU":"en","en-CA":"en","en-GB":"en_GB","en-IE":"en","en-IN":"en","en-NZ":"en","en-ZA":"en","en-US":"en_US","es-AR":"es_AR","es-CL":"es","es-CO":"es","es-ES":"es_ES","es-MX":"es_MX","es-US":"es","fi-FI":"fi","fr-BE":"fr","fr-CA":"fr","fr-CH":"fr","fr-FR":"fr","he-IL":"he","hi-IN":"hi","hu-HU":"hu","id-ID":"id","it-CH":"it","it-IT":"it","ja-JP":"ja","ko-KR":"ko","nl-BE":"nl","nl-NL":"nl","no-NO":"nb","pl-PL":"pl","pt-PT":"pt_PT","pt-BR":"pt_BR","ro-RO":"ro","ru-RU":"ru","sk-SK":"sk","sv-SE":"sv","ta-IN":"ta","ta-LK":"ta","th-TH":"th","tr-TR":"tr","zh-CN":"zh_CN","zh-HK":"zh_HK","zh-TW":"zh_TW"},z=e=>{const t=U[e];return void 0===t?"en":t},M=e=>{const t={to:e.to,messaging_product:"whatsapp",recipient_type:"individual"};return e.reply&&(t.context={message_id:e.reply}),e.showUrlPreviewImage&&(t.preview_url=e.showUrlPreviewImage),t},D={flow:e=>{if("flow"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{flow:n,...r}=t,o=M(a);n.mode||(n.mode=E.get("WHATSAPP_FLOWS_MODE"));const s=R.createToken({chatId:o.to,flow_name:n.name,flow_parameters:n.parameters});return{type:"interactive",...o,interactive:{...r,type:"flow",action:{name:"flow",parameters:{flow_name:n.name,flow_token:s,flow_message_version:"3",flow_cta:n.button,flow_action:n.action??"navigate",mode:n.mode,...n.payload&&{flow_action_payload:{...n.payload,screen:n.payload.screen.toUpperCase()}}}}}}},text:e=>{if("text"!==e.message.type)throw new Error("Invalid type");const{message:{text:t},...a}=e,n={type:"text",text:{body:t},...M(a)};return e.message.previewUrl&&n.text&&(n.text.preview_url=e.message.previewUrl),n},list:e=>{if("list"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{list:n,type:r,...o}=t;return{...M(a),type:"interactive",interactive:{...o,type:r,action:n}}},button:e=>{if("button"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{buttons:n,type:r,...o}=t;return{...M(a),type:"interactive",interactive:{...o,type:r,action:{buttons:n.map((e=>({reply:{id:e.id,title:e.text},type:"reply"})))}}}},template:e=>{if("template"!==e.message.type)throw new Error("Invalid type");const{message:{type:t,...a},...n}=e,r={...M(n),type:t,template:{...a,language:{code:z(a.language)}}};return r.template&&(r.template.namespace=process.env.WHATSAPP_MESSAGE_NAMESPACE),r},media:e=>{if("media"!==e.message.type)throw new Error("Invalid type");const{message:{type:t,...a},...n}=e,r=Object.entries(a)[0],[o,{ref:s,...i}]=r,l=i,c=M(n);return Number.isNaN(Number(s))?l.link=s:l.id=s,{type:o,[o]:l,...c}},contact:e=>{if("contact"!==e.message.type)throw new Error("Invalid type");const{message:{contacts:t},...a}=e;return{type:"contacts",contacts:t,...M(a)}}};function k(e){const{encrypted_aes_key:t,encrypted_flow_data:n,initial_vector:r}=e,o=a.default.createPrivateKey({key:E.get("WHATSAPP_ACCOUNT_ENCRYPTION_PRIVATE_KEY"),passphrase:E.get("WHATSAPP_ACCOUNT_ENCRYPTION_PASSPHRASE")}),s=a.default.privateDecrypt({key:o,padding:a.default.constants.RSA_PKCS1_OAEP_PADDING,oaepHash:"sha256"},Buffer.from(t,"base64")),i=Buffer.from(n,"base64"),l=Buffer.from(r,"base64"),c=i.subarray(0,-16),p=i.subarray(-16),d=a.default.createDecipheriv("aes-128-gcm",s,l);d.setAuthTag(p);const u=Buffer.concat([d.update(c),d.final()]).toString("utf-8");return{payload:JSON.parse(u),encryptionMetadata:{aesKeyBuffer:s,initialVectorBuffer:l}}}function L(e){const t=new URL(e.url).searchParams,a=t.get("hub.verify_token"),n=t.get("hub.challenge");return!(!a||!n)&&a===E.get("WHATSAPP_WEBHOOK_KEY")&&n}async function H(e,t){const n=e.headers.get("x-hub-signature-256");if(!n)return!1;const r=n.replace("sha256=",""),o=a.default.createHmac("sha256",E.get("WHATSAPP_WEBHOOK_KEY")).update(t,"utf-8").digest("hex");return!!a.default.timingSafeEqual(Buffer.from(r),Buffer.from(o))}var K={toGraph:{sendMessage:e=>D[e.message.type](e),flowResponse:(e,t)=>{if(!t.data)throw new Error("Missing data in flow response");if(!t.screen)throw new Error("Missing screen in flow response");if(t.screen=t.screen.toUpperCase(),"SUCCESS"===t.screen){const a=t.data??{};t.data={extension_message_response:{params:{flow_token:e,...a}}}}return t}},toSDK:{webhook:async function(e){if("GET"===e.method){const t=L(e);if(!t)throw new Error("Invalid health check hub");return{type:"healthCheck",payload:t}}const t=await e.text(),a=JSON.parse(t);if("entry"in a){if(!H(e,t))throw new Error("Invalid application webhook signature");const r=a.entry.flatMap((e=>e.changes)).filter((e=>"messages"===e.field)).flatMap((e=>e.value.messages)).filter(Boolean).map((e=>function(e){const t=e.from,a={id:e.id,type:e.type,timestamp:e.timestamp,metadata:{forwarded:e.context?.forwarded,frequentlyForwarded:e.context?.frequently_forwarded}},n=function(e){if(!(e.audio||e.document||e.video||e.image))return null;const t=e.audio?.id??e.document?.id??e.image?.id??e.video?.id,a=e.audio?.mime_type??e.document?.mime_type??e.image?.mime_type??e.video?.mime_type,n=["audio","document","image","video"].find((t=>e[t])),r=e.image?.sha256??e.document?.sha256??e.video?.sha256;return{id:t,type:n,caption:e.document?.caption??e.image?.caption??e.video?.caption??null,mime_type:a,sha256:r,filename:e.document?.filename??e.video?.filename}}(e),r=function(e){return e.text?.body??e.button?.text??e.interactive?.button_reply?.title??e.interactive?.list_reply?.title??e.document?.caption??null}(e),o=function(e){return e.interactive?{...e.interactive?.button_reply||e.button?{button:{id:e.interactive?.button_reply?.id??null,title:e.button?.text??e.interactive.button_reply?.title,payload:e.button?.payload??null}}:{},...e.interactive.list_reply?{selectedOption:{id:e.interactive.list_reply.id,title:e.interactive.list_reply.title,description:e.interactive.list_reply.description}}:{},...e.interactive.nfm_reply?{flowResponse:JSON.parse(e.interactive.nfm_reply.response_json)}:{}}:null}(e);return{...a,...o&&{interaction:o},...n&&{media:n},text:r,chatId:t}}(e)));return{type:"application",payload:{messageReceived:(n=r,n.length?n:void 0)}}}var n;if("encrypted_flow_data"in a)return{type:"flowExchange",payload:{...k(a),pingResponse:{data:{status:"active"}}}};throw new Error("Unrecognized event")}}},j={messages:{send:async function(e){const t=K.toGraph.sendMessage(e),a=c.messages.send;return await S(a,{body:t})}},flows:{...h},waba:{registerNumber:async function({dataRegion:e,pin:t}){const a=c.waba.registerNumber,n={messaging_product:"whatsapp",pin:t};return e&&(n.data_localization_region=e),await S(a,{body:n},{asUrlEncoded:!0})},encryption:{upload:async function(e){const t=c.waba.updateEncryption,a={business_public_key:e};return await S(t,{body:a},{asUrlEncoded:!0})}}}};async function W(e){return await fetch(e).then((async e=>{const t=await e.arrayBuffer();return Buffer.from(t)}))}var G={verifyHub:L,verifySignature:H,generateWabaEncryption:async function(){const e=a.default.randomUUID(),{publicKey:t,privateKey:n}=a.default.generateKeyPairSync("rsa",{modulusLength:2048,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem",cipher:"aes-256-cbc",passphrase:e}});return{passphrase:e,publicKey:t,privateKey:n}},decryptFlowBody:k,encryptFlowResponse:function(e,t){const n=[];for(const e of Buffer.from(t.initialVectorBuffer).entries())n.push(~e[1]);const r=a.default.createCipheriv("aes-128-gcm",Buffer.from(t.aesKeyBuffer),Buffer.from(n));return Buffer.concat([r.update(JSON.stringify(e),"utf-8"),r.final(),r.getAuthTag()]).toString("base64")},decryptFlowMedia:async function(e){const t=[];for(const n of e){let{cdn_url:e,encryption_metadata:r}=n;if("EXAMPLE_DATA__CDN_URL_WILL_COME_IN_THIS_FIELD"===e){e="https://picsum.photos/seed/picsum/200";const a=await W(e);t.push(a);continue}const o=await W(e),{iv:s,encryption_key:i,hmac_key:l,encrypted_hash:c,plaintext_hash:p}=r,d={iv:Buffer.from(s,"base64"),encryption_key:Buffer.from(i,"base64"),hmac_key:Buffer.from(l,"base64")};if(a.default.createHash("sha256").update(o).digest("base64")!==c)throw new Error("Encrypted hash validation failed");const u=o.subarray(0,o.length-10),f=o.subarray(-10);if(!a.default.createHmac("sha256",d.hmac_key).update(Buffer.concat([d.iv,u])).digest().subarray(0,10).equals(f))throw new Error("HMAC validation failed");const m=a.default.createDecipheriv("aes-256-cbc",d.encryption_key,d.iv),y=m.update(u),w=Buffer.concat([y,m.final()]);if(a.default.createHash("sha256").update(w).digest("base64")!==p)throw new Error("Decrypted media hash validation failed");t.push(w)}return t}},q={flows:R},$=null;exports.actions=j,exports.createWhatsapp=()=>$||($=(()=>{const{settings:e}=b,{actions:t,flows:a,parsers:n,security:r,utils:s,toGraphLanguageTag:i}=g,{endpoints:l}=o;return{settings:e,sdk:{actions:t,flows:a,parsers:n,security:r,utils:s,toGraphLanguageTag:i},graph:{endpoints:l}}})()),exports.endpoints=c,exports.flowAction=p,exports.flowCanSendMessageStatus=w,exports.flowCategory=d,exports.flowMediaData=u,exports.flowMetadata=f,exports.flowScreen=m,exports.flowStatus=y,exports.flowValidationError=_,exports.flows=R,exports.flowsEndpoints=s,exports.messagesEndpoints=i,exports.parsers=K,exports.security=G,exports.toGraphLanguageTag=z,exports.utils=q,exports.wabaEndpoints=l;
package/dist/index.d.cts CHANGED
@@ -1984,9 +1984,7 @@ interface WaSDKEvent<T extends WaSDKEventType = WaSDKEventType> {
1984
1984
  payload: WaSDKEventPayload[T];
1985
1985
  }
1986
1986
 
1987
- declare function webhook(request: Request): Promise<WaSDKEvent<"healthCheck"> | WaSDKEvent<"application"> | WaSDKEvent<"flowExchange"> | {
1988
- error: boolean;
1989
- }>;
1987
+ declare function webhook(request: Request): Promise<WaSDKEvent<"healthCheck"> | WaSDKEvent<"application"> | WaSDKEvent<"flowExchange">>;
1990
1988
 
1991
1989
  declare function uploadWabaEncryption(publicKey: string): Promise<unknown>;
1992
1990
 
@@ -2005,7 +2003,9 @@ declare const actions: {
2005
2003
  send: typeof send;
2006
2004
  };
2007
2005
  flows: {
2008
- create(body: WhatsappCreateFlowRequestBody): Promise<WhatsappCreateFlowResponse>;
2006
+ create(body: Omit<WhatsappCreateFlowRequestBody, "flow_json"> & {
2007
+ flow_json: Record<string, AnyType>;
2008
+ }): Promise<WhatsappCreateFlowResponse>;
2009
2009
  updateMetadata(flow_id: string, body: WhatsappFlowUpdateMetadataRequestBody): Promise<WhatsappFlowUpdateMetadataResponse>;
2010
2010
  updateJson(flow_id: string, json: Record<string, AnyType>): Promise<WhatsappUpdateFlowJsonResponse>;
2011
2011
  getPreview(flow_id: string, query?: WhatsappGetFlowWebPreviewPageRequestQuery): Promise<string>;
@@ -2107,7 +2107,9 @@ declare const createWhatsapp: () => {
2107
2107
  send: typeof send;
2108
2108
  };
2109
2109
  flows: {
2110
- create(body: WhatsappCreateFlowRequestBody): Promise<WhatsappCreateFlowResponse>;
2110
+ create(body: Omit<WhatsappCreateFlowRequestBody, "flow_json"> & {
2111
+ flow_json: Record<string, AnyType>;
2112
+ }): Promise<WhatsappCreateFlowResponse>;
2111
2113
  updateMetadata(flow_id: string, body: WhatsappFlowUpdateMetadataRequestBody): Promise<WhatsappFlowUpdateMetadataResponse>;
2112
2114
  updateJson(flow_id: string, json: Record<string, AnyType>): Promise<WhatsappUpdateFlowJsonResponse>;
2113
2115
  getPreview(flow_id: string, query?: WhatsappGetFlowWebPreviewPageRequestQuery): Promise<string>;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.0.3",
7
+ "version": "0.0.5",
8
8
  "type": "module",
9
9
  "files": [
10
10
  "dist"