@omnizapi/connect 1.0.1 → 1.0.2
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/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +74 -28
- package/dist/cjs/index.js +322 -120
- package/dist/esm/index.mjs +322 -120
- package/dist/omnizapi.min.js +1 -1
- package/dist/types/src/core/client.d.ts +1 -1
- package/dist/types/src/core/facebook-sdk.d.ts +11 -0
- package/dist/types/src/core/runtime.d.ts +16 -0
- package/dist/types/src/types/index.d.ts +3 -2
- package/examples/angular.ts +5 -3
- package/examples/basic.ts +5 -3
- package/examples/error-handling.ts +29 -11
- package/examples/react.tsx +23 -10
- package/examples/vanilla.html +3 -3
- package/examples/vue.vue +5 -3
- package/examples/with-options.ts +16 -10
- package/package.json +11 -3
package/dist/omnizapi.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(()=>{var e=class extends Error{constructor(o,i){super(i);this.code=o;this.name="OmnizapiError"}};var se={PRODUCTION:{api:"https://api.omni.z-api.io",front:"https://app.omni.z-api.io"},STAGING:{api:"https://api.staging.omni.z-api.io",front:"https://app.staging.omni.z-api.io"},SANDBOX:{api:"",front:""}},ae=({environment:n="PRODUCTION",apiUrl:t,frontUrl:o}={})=>{if(!Object.prototype.hasOwnProperty.call(se,n))throw new e("INVALID_CONFIGURATION","Unknown environment.");let i=se[n],r={api:t!=null?t:i.api,front:o!=null?o:i.front};for(let c of["api","front"]){let f;try{f=new URL(r[c])}catch(l){throw new e("INVALID_CONFIGURATION",`A valid ${c}Url is required for this environment.`)}if(!["http:","https:"].includes(f.protocol)||f.username||f.password||f.href.includes("?")||f.href.includes("#"))throw new e("INVALID_CONFIGURATION",`${c}Url must be an HTTP(S) base URL without credentials, query or fragment.`);r[c]=f.href.replace(/\/+$/,"")}return r};var ce=Symbol.for("omnizapi.connect.runtime.v1");function W(n){let t=n,o=t[ce];return o||(o={nativeLoginPending:!1,failedFacebookScripts:new WeakSet},Object.defineProperty(t,ce,{configurable:!0,value:o})),o}function le(n){return!!n&&!("__buffer"in n)&&typeof n.init=="function"&&typeof n.login=="function"}function ue(n,t){if(t.aborted)return Promise.reject(new e("SDK_LOAD_FAILED","The Meta SDK load was cancelled."));if(le(n.FB))return Promise.resolve(n.FB);let o=W(n),i=o.facebookLoading;if(i)return i;let r=Promise.resolve().then(()=>new Promise((c,f)=>{let l=!1,s,E=!1,_,I,O=(h,a)=>{if(!l){l=!0,clearTimeout(K),t.removeEventListener("abort",N),s==null||s.removeEventListener("load",U),s==null||s.removeEventListener("error",G);try{n.fbAsyncInit===k&&(_?Object.defineProperty(n,"fbAsyncInit",_):Reflect.deleteProperty(n,"fbAsyncInit"))}catch(P){}if(a&&s&&(t.aborted||o.failedFacebookScripts.add(s),E))try{s.remove()}catch(P){}o.facebookLoading===r&&delete o.facebookLoading,h?c(h):f(a)}},U=()=>{if(l)return;let h=n.FB;le(h)&&O(h)},G=()=>O(void 0,new e("SDK_LOAD_FAILED","The Meta SDK could not be loaded.")),N=()=>O(void 0,new e("SDK_LOAD_FAILED","The Meta SDK load was cancelled.")),k=()=>{if(!l){try{I==null||I.call(n)}catch(h){}U()}},K=setTimeout(()=>O(void 0,new e("SDK_LOAD_FAILED","The Meta SDK load timed out.")),30*1e3);try{if(t.addEventListener("abort",N,{once:!0}),t.aborted){N();return}_=Object.getOwnPropertyDescriptor(n,"fbAsyncInit"),I=typeof n.fbAsyncInit=="function"?n.fbAsyncInit:void 0,Object.defineProperty(n,"fbAsyncInit",_&&!_.configurable?{value:k}:{configurable:!0,enumerable:!0,writable:!0,value:k});let h=n.document,a=h.getElementById("facebook-jssdk"),P=(a==null?void 0:a.tagName.toLowerCase())==="script"?a:void 0;s=(P&&!o.failedFacebookScripts.has(P)?P:void 0)||Array.from(h.scripts).find(y=>{if(o.failedFacebookScripts.has(y))return!1;try{let F=new URL(y.src);return F.origin==="https://connect.facebook.net"&&/^\/[^/]+\/sdk\.js$/.test(F.pathname)}catch(F){return!1}}),s||(s=h.createElement("script"),E=!0,a||(s.id="facebook-jssdk"),s.src="https://connect.facebook.net/en_US/sdk.js",s.crossOrigin="anonymous",s.async=!0,s.defer=!0),s.addEventListener("load",U),s.addEventListener("error",G),U(),!l&&E&&h.head.appendChild(s)}catch(h){G()}}));return o.facebookLoading=r,r}var de=(n,t,o=600,i=700)=>{let r=window,c=r.document,f=r.screenLeft!==void 0?r.screenLeft:r.screenX,l=r.screenTop!==void 0?r.screenTop:r.screenY,s=r.innerWidth?r.innerWidth:c.documentElement.clientWidth?c.documentElement.clientWidth:r.screen.width,E=r.innerHeight?r.innerHeight:c.documentElement.clientHeight?c.documentElement.clientHeight:r.screen.height,_=s/2-o/2+f,I=E/2-i/2+l;return r.open(n,t,`scrollbars=yes, width=${o}, height=${i}, top=${I}, left=${_}`)};var Ie=600*1e3,Re=30*1e3;function L(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function p(n){return typeof n=="string"&&n.trim()?n:null}function pe(n){if(n==="."||n==="..")throw new e("INVALID_OPTIONS","The channelId cannot be a URL path navigation segment.");try{return encodeURIComponent(n)}catch(t){throw new e("INVALID_OPTIONS","The channelId cannot be encoded as a URL path.")}}function Oe(n){return["META_MESSENGER","META_INSTAGRAM","META_WHATSAPP","BOT_TELEGRAM","Z_API_WHATSAPP"].includes(n)}function fe(n){return typeof n=="string"&&["cancelled","popup_closed","not_completed","invalid_channel","origin_not_allowed","unsupported_channel"].includes(n)}function j(n,t,o){return{success:!1,reason:o,channelId:n,type:t,code:null,wabaId:null,phoneId:null}}function me(n,t,o){return typeof fetch!="function"||typeof AbortController!="function"?Promise.reject(new e("INVALID_CONFIGURATION","Fetch and AbortController are required.")):new Promise((i,r)=>{let c=new AbortController,f=!1,l=(I,O)=>{f||(f=!0,clearTimeout(E),o==null||o.removeEventListener("abort",s),c.abort(),O?r(O):i(I))},s=()=>l(void 0,new e("NETWORK_ERROR","The API request was cancelled.")),E=setTimeout(()=>l(void 0,new e("TIMEOUT","The API request timed out.")),Re);if(o==null||o.addEventListener("abort",s,{once:!0}),o!=null&&o.aborted){s();return}(async()=>{let I;try{I=await fetch(n.href,{...t,signal:c.signal})}catch(O){l(void 0,new e("NETWORK_ERROR","The API request failed."));return}if(!f){if(!I||typeof I.ok!="boolean"){l(void 0,new e("INVALID_RESPONSE","The API response is invalid."));return}if(!I.ok){l(void 0,new e("HTTP_ERROR","The API returned an unsuccessful status."));return}try{let O=await I.json();f||l(O)}catch(O){l(void 0,new e("INVALID_RESPONSE","The API did not return valid JSON."))}}})().catch(()=>l(void 0,new e("NETWORK_ERROR","The API request failed.")))})}var $=class{constructor(t){if(!L(t)||!p(t.publicKey)||Array.from(t.publicKey).some(o=>{let i=o.charCodeAt(0);return i<32||i===127})||t.logo!==void 0&&typeof t.logo!="string"||t.apiUrl!==void 0&&!p(t.apiUrl)||t.frontUrl!==void 0&&!p(t.frontUrl))throw new e("INVALID_CONFIGURATION","Valid client properties and a publicKey are required.");this.publicKey=t.publicKey,this.logo=t.logo,this.constants=ae({environment:t.environment,apiUrl:t.apiUrl,frontUrl:t.frontUrl})}async getListMessengerPages(t,o){if(!p(t)||!p(o))throw new e("INVALID_OPTIONS","A channelId and authorization code are required.");return me(new URL(`${this.constants.api}/v1/channels/${pe(t)}/list-messenger-pages`),{method:"POST",body:JSON.stringify({redirectUri:new URL(`${this.constants.front}/oauth/proxy-messenger`).href,code:o}),headers:{"Content-Type":"application/json",Authorization:this.publicKey}})}async connect(t){var o;try{if(typeof window=="undefined"||!window.document||!window.location)throw new e("BROWSER_REQUIRED","Connecting requires a browser window.");let i=window,r=W(i);if(r.activeConnection)throw new e("CONNECTION_IN_PROGRESS","A connection is already in progress in this window.");if(!L(t))throw new e("INVALID_OPTIONS","Connection options are required.");let{channelId:c,direct:f,getQRCode:l,timeoutMs:s=Ie}=t;if(typeof c!="string"||f!==void 0&&typeof f!="boolean"||l!==void 0&&typeof l!="function"||typeof s!="number"||!Number.isFinite(s)||s<=0||s>2147483647)throw new e("INVALID_OPTIONS","Invalid channelId, direct, getQRCode or timeoutMs option.");if(!c.trim())return j(c,null,"invalid_channel");let E=pe(c);if(typeof i.open!="function"||typeof i.addEventListener!="function"||typeof i.removeEventListener!="function"||typeof i.location.origin!="string"||typeof i.location.href!="string"||typeof((o=i.crypto)==null?void 0:o.getRandomValues)!="function"||typeof AbortController!="function"||typeof fetch!="function")throw new e("INVALID_CONFIGURATION","Required browser APIs are unavailable.");let _=i.location.origin;if(_==="null")return j(c,null,"origin_not_allowed");let I=i.location.href,O=new URL(this.constants.front).origin;return await new Promise((U,G)=>{let N=Symbol("omnizapi-connection"),k=new AbortController,K=Date.now()+s,h=!1,a=null,P=null,y=null,F,Q,z,J=!1,H=!1,X=()=>{r.nativeLoginOwner===N&&(r.nativeLoginPending=!1,delete r.nativeLoginOwner)},Z=[],S=g=>{if(h)return;if(h=!0,clearTimeout(F),clearInterval(Q),z)try{i.removeEventListener("message",z)}catch(M){}try{k.abort()}catch(M){}let D=y;y=null;try{D==null||D.close()}catch(M){}for(let{name:M,previous:m,value:v}of Z)try{let T=Object.getOwnPropertyDescriptor(i,M);if(!T||!("value"in T)||T.value!==v)continue;m?Object.defineProperty(i,M,m):Reflect.deleteProperty(i,M)}catch(T){}r.activeConnection===N&&delete r.activeConnection,J&&(H||!(g instanceof e)&&g.success)&&X(),g instanceof e?G(g):U(g)},b=()=>(!h&&Date.now()>=K&&S(new e("TIMEOUT","The connection timed out.")),!h),C=g=>S(j(c,a,g)),Y=g=>{!b()||!a||S({success:!0,channelId:c,type:a,code:p(g.code),wabaId:p(g.wabaId),phoneId:p(g.phoneId),coexistence:g.coexistence===!0,redirectUri:P,botToken:p(g.botToken),pageId:p(g.pageId),accessToken:p(g.accessToken)})};try{r.activeConnection=N;let g=i.crypto.getRandomValues(new Uint8Array(16)),D=Array.from(g,m=>m.toString(16).padStart(2,"0")).join("");for(let[m,v]of[["__omnizapiPublicKey",this.publicKey],["__omnizapiCurrentChannel",c],["__omnizapiGetQrCode",l]]){let T=Object.getOwnPropertyDescriptor(i,m);Object.defineProperty(i,m,T&&!T.configurable?{value:v}:{configurable:!0,enumerable:!0,writable:!0,value:v}),Z.push({name:m,previous:T,value:v})}F=setTimeout(()=>S(new e("TIMEOUT","The connection timed out.")),s),(async()=>{var ee,ne,te;let m=await me(new URL(`${this.constants.api}/instances/${E}/sdk-info`),{headers:{Authorization:this.publicKey,"Content-Type":"application/json"}},k.signal);if(!b())return;if(!L(m)||!p(m.middleware))throw new e("INVALID_RESPONSE","The channel configuration is invalid.");let v=(ee=m.allowOrigins)!=null?ee:[];if(!Array.isArray(v)||!v.every(u=>typeof u=="string"))throw new e("INVALID_RESPONSE","The channel origins are invalid.");let T=m.middleware;if(a=Oe(T)?T:null,_!==O&&!v.some(u=>_===u.replace(/\/$/,""))){C("origin_not_allowed");return}if(!a){C("unsupported_channel");return}let V=(ne=p(a==="META_INSTAGRAM"?m.appInstagramId:m.appId))!=null?ne:"",B=(te=p(m.appConfig))!=null?te:"";if(a.startsWith("META_")&&!V||a==="META_WHATSAPP"&&!B)throw new e("INVALID_RESPONSE","The channel provider configuration is invalid.");if(a==="META_WHATSAPP"&&(f||m.appCustom||_===O)){if(r.nativeLoginPending)throw new e("CONNECTION_IN_PROGRESS","A previous Meta login is incomplete. Reload this window before retrying a native login.");let u=await ue(i,k.signal);if(!b())return;try{u.init({appId:V,version:"v25.0",autoLogAppEvents:!0,xfbml:!0})}catch(A){throw new e("PROVIDER_ERROR","The Meta SDK could not be initialized.")}if(!b())return;let d=null,x=null,oe=null,ie=!1,re=()=>{!b()||!d||!x||Y({code:d,wabaId:x,phoneId:oe,coexistence:ie})};z=A=>{if(!b()||!["https://www.facebook.com","https://web.facebook.com"].includes(A.origin))return;let R=A.data;if(typeof R=="string")try{R=JSON.parse(R)}catch(_e){return}if(!(!L(R)||R.type!=="WA_EMBEDDED_SIGNUP")){if(R.event==="CANCEL")C("cancelled");else if(R.event==="ERROR")S(new e("PROVIDER_ERROR","Meta could not complete the connection."));else if(R.event==="FINISH"||R.event==="FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING"){if(!L(R.data)||!p(R.data.waba_id)||R.data.phone_number_id!=null&&typeof R.data.phone_number_id!="string")return;x=p(R.data.waba_id),oe=p(R.data.phone_number_id),ie=R.event==="FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING",re()}}},i.addEventListener("message",z),r.nativeLoginPending=!0,r.nativeLoginOwner=N,J=!0;try{u.login(A=>{if(!H){if(H=!0,h){X();return}if(b()){if(!L(A)||A.error){S(new e("PROVIDER_ERROR","Meta returned an invalid login response."));return}if(A.authResponse==null){C("not_completed");return}if(!L(A.authResponse)||!p(A.authResponse.code)){C("not_completed");return}d=p(A.authResponse.code),re()}}},{config_id:B,response_type:"code",override_default_response_type:!0,extras:{setup:{},featureType:"whatsapp_business_app_onboarding",sessionInfoVersion:"3"}})}catch(A){throw new e("PROVIDER_ERROR","The Meta login could not be started.")}return}let w;if(a==="META_MESSENGER"||a==="META_INSTAGRAM"){let u=a==="META_MESSENGER";P=new URL(`${this.constants.front}/oauth/proxy-${u?"messenger":"instagram"}`).href,w=new URL(u?"https://www.facebook.com/dialog/oauth":"https://www.instagram.com/oauth/authorize"),w.searchParams.set("client_id",V),u&&w.searchParams.set("display","popup"),w.searchParams.set("redirect_uri",P),w.searchParams.set("response_type","code"),w.searchParams.set("scope",u?"public_profile,business_management,pages_messaging,pages_show_list,pages_manage_metadata,pages_read_engagement":"instagram_business_basic,instagram_business_manage_messages"),w.searchParams.set("state",JSON.stringify({currentUrl:I,attemptId:D,channelId:c}))}else a==="META_WHATSAPP"?(w=new URL(`${this.constants.front}/oauth/proxy-cloud-api`),w.searchParams.set("configId",B),w.searchParams.set("appId",V),w.searchParams.set("channelId",c),w.searchParams.set("channelType",a),w.searchParams.set("logo",this.logo||new URL(`${this.constants.front}/logos/SVG/Ativo%2016.svg`).href),w.searchParams.set("pk",this.publicKey)):(w=new URL(`${this.constants.front}/connect/${a==="BOT_TELEGRAM"?"telegram":"whatsapp-web"}`),w.searchParams.set("channelId",c));if(a!=="META_MESSENGER"&&a!=="META_INSTAGRAM"&&w.searchParams.set("attemptId",D),z=u=>{if(!b()||!y||u.origin!==O||u.source!==y)return;let d=u.data;!L(d)||d.type!=="after-connect"||d.attemptId!==D||d.channelId!==c||typeof d.success!="boolean"||d.reason!==void 0&&!fe(d.reason)||d.coexistence!==void 0&&typeof d.coexistence!="boolean"||["code","wabaId","phoneId","botToken","pageId","accessToken","redirectUri"].some(x=>d[x]!=null&&typeof d[x]!="string")||(d.success?a!=null&&a.startsWith("META_")&&!p(d.code)||a==="BOT_TELEGRAM"&&!p(d.botToken)?C("not_completed"):Y(d):C(fe(d.reason)?d.reason:"not_completed"))},i.addEventListener("message",z),!!b()){try{let u=de(w.href,"_blank",600,740);if(!u||u===i)throw new e("POPUP_BLOCKED","The connection popup was blocked.");y=u}catch(u){throw new e("POPUP_BLOCKED","The connection popup could not be opened.")}Q=setInterval(()=>{if(b())try{y!=null&&y.closed&&C("popup_closed")}catch(u){S(new e("PROVIDER_ERROR","The connection popup could not be monitored."))}},500)}})().catch(m=>{b()&&S(m instanceof e?m:new e("PROVIDER_ERROR","The connection could not be completed."))})}catch(g){S(new e("INVALID_CONFIGURATION","The connection could not be initialized."))}})}catch(i){throw i instanceof e?i:new e("INVALID_OPTIONS","The connection could not be started with these options.")}}},he=$;var q=class{static newClient(t){return new he(t)}};var ge=q;Object.assign(window,{Omnizapi:ge});})();
|
|
1
|
+
"use strict";(()=>{var o=class extends Error{constructor(n,e){super(e);this.code=n;this.name="OmnizapiError"}};var ue={PRODUCTION:{api:"https://api.omni.z-api.io",front:"https://app.omni.z-api.io"},STAGING:{api:"https://api.staging.omni.z-api.io",front:"https://app.staging.omni.z-api.io"},SANDBOX:{api:"",front:""}},fe=({environment:r="PRODUCTION",apiUrl:t,frontUrl:n}={})=>{if(!Object.prototype.hasOwnProperty.call(ue,r))throw new o("INVALID_CONFIGURATION","Unknown environment.");let e=ue[r],a={api:t!=null?t:e.api,front:n!=null?n:e.front};for(let s of["api","front"]){let c;try{c=new URL(a[s])}catch(f){throw new o("INVALID_CONFIGURATION",`A valid ${s}Url is required for this environment.`)}if(!["http:","https:"].includes(c.protocol)||c.username||c.password||c.href.includes("?")||c.href.includes("#"))throw new o("INVALID_CONFIGURATION",`${s}Url must be an HTTP(S) base URL without credentials, query or fragment.`);a[s]=c.href.replace(/\/+$/,"")}return a};var pe=Symbol.for("omnizapi.connect.runtime.v1");function j(r){var e,a,s;let t=r,n=t[pe];return n||(n={nativeLoginPending:!1,failedFacebookScripts:new WeakSet,erroredFacebookScripts:new WeakSet,facebookAppIds:new WeakMap,facebookInitObservers:new WeakMap},Object.defineProperty(t,pe,{configurable:!0,value:n})),(e=n.facebookAppIds)!=null||(n.facebookAppIds=new WeakMap),(a=n.facebookInitObservers)!=null||(n.facebookInitObservers=new WeakMap),(s=n.erroredFacebookScripts)!=null||(n.erroredFacebookScripts=new WeakSet),n}function he(r){return!!r&&!("__buffer"in r)&&typeof r.init=="function"&&typeof r.login=="function"}function be(r,t){var n;try{let e=Object.getOwnPropertyDescriptor(t,"init"),a=r.facebookInitObservers.get(t);if(a){if((e==null?void 0:e.value)!==a.init)throw new Error("FB.init was replaced.");return a}if(!e||typeof e.value!="function"||!e.writable&&!e.configurable)throw new Error("FB.init cannot be observed.");let s=e.value,c={calls:0,init:function(...f){return c.calls++,r.facebookAppIds.delete(t),Reflect.apply(s,this,f)}};if(Object.defineProperty(t,"init",{...e,value:c.init}),((n=Object.getOwnPropertyDescriptor(t,"init"))==null?void 0:n.value)!==c.init)throw new Error("FB.init was not replaced.");return r.facebookInitObservers.set(t,c),c}catch(e){throw r.facebookAppIds.delete(t),new o("INVALID_CONFIGURATION","The Meta SDK initialization cannot be observed safely. Use the popup flow or reload a fresh page.")}}function Ie(r,t,n){var f;if(r.FB!==t||!t||"__buffer"in t||typeof t.login!="function")throw new o("INVALID_CONFIGURATION","The Meta SDK is not ready or was replaced before initialization.");if(typeof n!="string"||!/^[0-9]+$/.test(n))throw new o("INVALID_CONFIGURATION","A numeric Meta appId is required for native login.");let e=j(r);e.facebookAppIds.get(t)===null&&(e.facebookLoadUncertain||e.facebookLoading||typeof r.fbAsyncInit=="function")&&e.facebookAppIds.delete(t);let a=e.facebookAppIds.get(t);if(a===void 0||a!==null&&!e.facebookInitObservers.has(t))throw e.facebookAppIds.delete(t),new o("INVALID_CONFIGURATION","The host Meta SDK configuration is unknown. Use the popup flow or a fresh page without a host-initialized SDK for native login.");let s=be(e,t);if(a!==null){if(a!==n)throw new o("INVALID_CONFIGURATION","The Meta SDK is already initialized for another app. Use the popup flow or reload a fresh page before changing apps.");return}let c=s.calls;e.facebookAppIds.delete(t);try{t.init({appId:n,version:"v25.0",autoLogAppEvents:!0,xfbml:!0})}catch(p){throw new o("PROVIDER_ERROR","The Meta SDK could not be initialized.")}if(s.calls!==c+1||r.FB!==t||((f=Object.getOwnPropertyDescriptor(t,"init"))==null?void 0:f.value)!==s.init)throw new o("INVALID_CONFIGURATION","The Meta SDK changed during initialization. Use the popup flow or reload a fresh page.");e.facebookAppIds.set(t,n)}function me(r,t){if(t.aborted)return Promise.reject(new o("SDK_LOAD_FAILED","The Meta SDK load was cancelled."));let n=j(r),e=n.facebookLoadObserver;if(!e&&he(r.FB)&&(e={promise:Promise.resolve(r.FB),consumers:0}),!e){let s={consumers:0,promise:Promise.resolve().then(()=>new Promise((c,f)=>{var h;let p=!1,i,_=!1,g=!1,I,P,M,z=(u,l,y=!1)=>{if(!p){p=!0,clearTimeout(F),i==null||i.removeEventListener("load",L),i==null||i.removeEventListener("error",V);try{r.fbAsyncInit===K&&(P?Object.defineProperty(r,"fbAsyncInit",P):Reflect.deleteProperty(r,"fbAsyncInit"))}catch(N){}if(l&&i&&(n.failedFacebookScripts.add(i),y?n.erroredFacebookScripts.add(i):_||(n.facebookLoadUncertain=!0),_))try{i.remove()}catch(N){}n.facebookLoadObserver===s&&delete n.facebookLoadObserver,u?c(u):f(l)}},L=()=>{var y;if(p)return;let u=r.FB;if(!u)return;"__buffer"in u&&(I=u),(((y=I==null?void 0:I.__buffer)==null?void 0:y.opts)!=null||r.fbAsyncInit!==K||i!=null&&i.onload||n.facebookLoading||n.facebookLoadUncertain)&&(g=!1,n.facebookAppIds.get(u)===null&&n.facebookAppIds.delete(u),I&&n.facebookAppIds.get(I)===null&&n.facebookAppIds.delete(I));let l=he(u);if(g&&n.facebookAppIds.get(u)==null){if(!l)n.facebookAppIds.set(u,null);else if(!n.facebookInitObservers.has(u))try{be(n,u),n.facebookAppIds.set(u,null)}catch(N){}}l&&z(u)},V=()=>z(void 0,new o("SDK_LOAD_FAILED","The Meta SDK could not be loaded."),!0),K=()=>{if(!p){try{M==null||M.call(r)}catch(u){}L()}},F=setTimeout(()=>z(void 0,new o("SDK_LOAD_FAILED","The Meta SDK load timed out.")),30*1e3);s.cancelIfUnused=()=>{_&&s.consumers===0&&z(void 0,new o("SDK_LOAD_FAILED","The Meta SDK load was cancelled."))};try{P=Object.getOwnPropertyDescriptor(r,"fbAsyncInit"),M=typeof r.fbAsyncInit=="function"?r.fbAsyncInit:void 0;let u=r.document,l=u.getElementById("facebook-jssdk");if(i=Array.from(u.scripts).find(y=>{try{let N=new URL(y.src);return N.origin!=="https://connect.facebook.net"||!/^\/[^/]+\/sdk\.js$/.test(N.pathname)?!1:n.failedFacebookScripts.has(y)?(n.erroredFacebookScripts.has(y)||(n.facebookLoadUncertain=!0),!1):!0}catch(N){return!1}}),!i){i=u.createElement("script"),_=!0;let y=r.FB;y&&"__buffer"in y&&(I=y),g=!n.facebookLoadUncertain&&!n.facebookLoading&&!M&&(!y||!!I&&n.facebookAppIds.get(y)===null&&((h=I.__buffer)==null?void 0:h.opts)==null),l||(i.id="facebook-jssdk"),i.src="https://connect.facebook.net/en_US/sdk.js",i.crossOrigin="anonymous",i.async=!0,i.defer=!0}if(s.cancelIfUnused(),p)return;Object.defineProperty(r,"fbAsyncInit",P&&!P.configurable?{value:K}:{configurable:!0,enumerable:!0,writable:!0,value:K}),i.addEventListener("load",L),i.addEventListener("error",V),L(),!p&&_&&u.head.appendChild(i)}catch(u){z(void 0,new o("SDK_LOAD_FAILED","The Meta SDK could not be loaded."))}}))};n.facebookLoadObserver=e=s}let a=e;return new Promise((s,c)=>{let f=!1;a.consumers++;let p=(_,g)=>{var I;f||(f=!0,t.removeEventListener("abort",i),a.consumers--,_?s(_):c(g),(I=a.cancelIfUnused)==null||I.call(a))},i=()=>p(void 0,new o("SDK_LOAD_FAILED","The Meta SDK load was cancelled."));a.promise.then(_=>t.aborted?i():p(_),_=>p(void 0,new o("SDK_LOAD_FAILED",_.message))),t.addEventListener("abort",i,{once:!0}),t.aborted&&i()})}var we=(r,t,n=600,e=700)=>{let a=window,s=a.document,c=a.screenLeft!==void 0?a.screenLeft:a.screenX,f=a.screenTop!==void 0?a.screenTop:a.screenY,p=a.innerWidth?a.innerWidth:s.documentElement.clientWidth?s.documentElement.clientWidth:a.screen.width,i=a.innerHeight?a.innerHeight:s.documentElement.clientHeight?s.documentElement.clientHeight:a.screen.height,_=p/2-n/2+c,g=i/2-e/2+f;return a.open(r,t,`scrollbars=yes, width=${n}, height=${e}, top=${g}, left=${_}`)};var Se=600*1e3,ve=30*1e3;function v(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function O(r){return typeof r=="string"&&r.trim()?r:null}function Oe(r){if(r==="."||r==="..")throw new o("INVALID_OPTIONS","The channelId cannot be a URL path navigation segment.");try{return encodeURIComponent(r)}catch(t){throw new o("INVALID_OPTIONS","The channelId cannot be encoded as a URL path.")}}function Pe(r){return["META_MESSENGER","META_INSTAGRAM","META_WHATSAPP","BOT_TELEGRAM","Z_API_WHATSAPP"].includes(r)}function ge(r){return typeof r=="string"&&["cancelled","popup_closed","not_completed","invalid_channel","origin_not_allowed","unsupported_channel"].includes(r)}function Y(r,t,n){return{success:!1,reason:n,channelId:r,type:t,code:null,wabaId:null,phoneId:null}}function Ae(r,t,n){return typeof fetch!="function"||typeof AbortController!="function"?Promise.reject(new o("INVALID_CONFIGURATION","Fetch and AbortController are required.")):new Promise((e,a)=>{let s=new AbortController,c=!1,f=(g,I)=>{c||(c=!0,clearTimeout(i),n==null||n.removeEventListener("abort",p),s.abort(),I?a(I):e(g))},p=()=>f(void 0,new o("ABORTED","The API request was aborted.")),i=setTimeout(()=>f(void 0,new o("TIMEOUT","The API request timed out.")),ve);if(n==null||n.addEventListener("abort",p,{once:!0}),n!=null&&n.aborted){p();return}(async()=>{let g;try{g=await fetch(r.href,{...t,signal:s.signal})}catch(I){f(void 0,new o("NETWORK_ERROR","The API request failed."));return}if(!c){if(!g||typeof g.ok!="boolean"){f(void 0,new o("INVALID_RESPONSE","The API response is invalid."));return}if(!g.ok){f(void 0,new o("HTTP_ERROR","The API returned an unsuccessful status."));return}try{let I=await g.json();c||f(I)}catch(I){f(void 0,new o("INVALID_RESPONSE","The API did not return valid JSON."))}}})().catch(()=>f(void 0,new o("NETWORK_ERROR","The API request failed.")))})}var ee=class{constructor(t){if(!v(t)||!O(t.publicKey)||Array.from(t.publicKey).some(n=>{let e=n.charCodeAt(0);return e<32||e===127})||t.logo!==void 0&&typeof t.logo!="string"||t.apiUrl!==void 0&&!O(t.apiUrl)||t.frontUrl!==void 0&&!O(t.frontUrl))throw new o("INVALID_CONFIGURATION","Valid client properties and a publicKey are required.");this.publicKey=t.publicKey,this.logo=t.logo,this.constants=fe({environment:t.environment,apiUrl:t.apiUrl,frontUrl:t.frontUrl})}async getListMessengerPages(t,n,e){if(!O(t)||!O(n))throw new o("INVALID_OPTIONS","A channelId and authorization code are required.");if(e!==void 0&&(!v(e)||typeof e.aborted!="boolean"||typeof e.addEventListener!="function"||typeof e.removeEventListener!="function"))throw new o("INVALID_OPTIONS","The abort signal is invalid.");if(e!=null&&e.aborted)throw new o("ABORTED","The API request was aborted.");return Ae(new URL(`${this.constants.api}/v1/channels/${Oe(t)}/list-messenger-pages`),{method:"POST",body:JSON.stringify({redirectUri:new URL(`${this.constants.front}/oauth/proxy-messenger`).href,code:n}),headers:{"Content-Type":"application/json",Authorization:this.publicKey}},e)}async connect(t){var n;try{if(typeof window=="undefined"||!window.document||!window.location)throw new o("BROWSER_REQUIRED","Connecting requires a browser window.");let e=window,a=j(e);if(a.activeConnection)throw new o("CONNECTION_IN_PROGRESS","A connection is already in progress in this window.");if(!v(t))throw new o("INVALID_OPTIONS","Connection options are required.");let{channelId:s,direct:c,getQRCode:f,timeoutMs:p=Se,signal:i}=t;if(typeof s!="string"||c!==void 0&&typeof c!="boolean"||f!==void 0&&typeof f!="function"||typeof p!="number"||!Number.isFinite(p)||p<=0||p>2147483647||i!==void 0&&(!v(i)||typeof i.aborted!="boolean"||typeof i.addEventListener!="function"||typeof i.removeEventListener!="function"))throw new o("INVALID_OPTIONS","Invalid channelId, direct, getQRCode, timeoutMs or signal option.");if(i!=null&&i.aborted)throw new o("ABORTED","The connection was aborted.");if(!s.trim())return Y(s,null,"invalid_channel");let _=Oe(s);if(typeof e.open!="function"||typeof e.addEventListener!="function"||typeof e.removeEventListener!="function"||typeof e.location.origin!="string"||typeof e.location.href!="string"||typeof((n=e.crypto)==null?void 0:n.getRandomValues)!="function"||typeof AbortController!="function"||typeof fetch!="function")throw new o("INVALID_CONFIGURATION","Required browser APIs are unavailable.");let g=e.location.origin;if(g==="null")return Y(s,null,"origin_not_allowed");let I=e.location.href,P=new URL(this.constants.front).origin;return await new Promise((M,z)=>{let L=Symbol("omnizapi-connection"),V=new AbortController,K=Date.now()+p,F=!1,h=null,u=null,l=null,y,N,x,W,q=!1,Q=!1,J=()=>{a.nativeLoginOwner===L&&(a.nativeLoginPending=!1,delete a.nativeLoginOwner)},te=[],E=d=>{if(F)return;F=!0,clearTimeout(N),clearInterval(x);try{i==null||i.removeEventListener("abort",X)}catch(U){}if(W)try{e.removeEventListener("message",W)}catch(U){}try{V.abort()}catch(U){}let G=l;l=null;try{G==null||G.close()}catch(U){}for(let{name:U,previous:b,value:D}of te)try{let k=Object.getOwnPropertyDescriptor(e,U);if(!k||!("value"in k)||k.value!==D)continue;b?Object.defineProperty(e,U,b):Reflect.deleteProperty(e,U)}catch(k){}a.activeConnection===L&&delete a.activeConnection,q&&(Q||!(d instanceof o)&&d.success)&&J(),d instanceof o?z(d):M(d)},X=()=>E(new o("ABORTED","The connection was aborted.")),T=()=>(!F&&(i!=null&&i.aborted)&&X(),!F&&Date.now()>=K&&E(new o("TIMEOUT","The connection timed out.")),!F),C=d=>E(Y(s,h,d)),oe=()=>{try{let d=we("about:blank","_blank",600,740);if(!d||d===e)throw new Error("Popup unavailable");if(!T()){d.close();return}l=d}catch(d){throw new o("POPUP_BLOCKED","The connection popup could not be opened.")}},Re=()=>{x||(x=setInterval(()=>{if(T())try{l!=null&&l.closed&&C("popup_closed")}catch(d){E(new o("PROVIDER_ERROR","The connection popup could not be monitored."))}},500))},Te=()=>{clearInterval(x),x=void 0},re=d=>{!T()||!h||E({success:!0,channelId:s,type:h,code:O(d.code),wabaId:O(d.wabaId),phoneId:O(d.phoneId),coexistence:d.coexistence===!0,redirectUri:u,botToken:O(d.botToken),pageId:O(d.pageId),accessToken:O(d.accessToken)})};try{if(a.activeConnection=L,i==null||i.addEventListener("abort",X,{once:!0}),!T())return;let d=e.crypto.getRandomValues(new Uint8Array(16)),G=Array.from(d,b=>b.toString(16).padStart(2,"0")).join("");for(let[b,D]of[["__omnizapiPublicKey",this.publicKey],["__omnizapiCurrentChannel",s],["__omnizapiGetQrCode",f]]){let k=Object.getOwnPropertyDescriptor(e,b);Object.defineProperty(e,b,k&&!k.configurable?{value:D}:{configurable:!0,enumerable:!0,writable:!0,value:D}),te.push({name:b,previous:k,value:D})}N=setTimeout(()=>E(new o("TIMEOUT","The connection timed out.")),p);try{oe()}catch(b){y=b instanceof o?b:new o("POPUP_BLOCKED","The connection popup could not be opened.")}if(!T())return;(async()=>{var ae,se,ce;let b=await Ae(new URL(`${this.constants.api}/instances/${_}/sdk-info`),{headers:{Authorization:this.publicKey,"Content-Type":"application/json"}},V.signal);if(!T())return;if(!v(b)||!O(b.middleware))throw new o("INVALID_RESPONSE","The channel configuration is invalid.");let D=(ae=b.allowOrigins)!=null?ae:[];if(!Array.isArray(D)||!D.every(w=>typeof w=="string"))throw new o("INVALID_RESPONSE","The channel origins are invalid.");let k=b.middleware;if(h=Pe(k)?k:null,g!==P&&!D.some(w=>{try{return g===new URL(w).origin}catch(m){return!1}})){C("origin_not_allowed");return}if(!h){C("unsupported_channel");return}let H=(se=O(h==="META_INSTAGRAM"?b.appInstagramId:b.appId))!=null?se:"",Z=(ce=O(b.appConfig))!=null?ce:"";if(h.startsWith("META_")&&!H||h==="META_WHATSAPP"&&!Z)throw new o("INVALID_RESPONSE","The channel provider configuration is invalid.");let ke=h==="META_WHATSAPP"&&(c||!!b.appCustom||g===P),ie=!1,$;if(ke){if(Te(),a.nativeLoginPending)throw new o("CONNECTION_IN_PROGRESS","A previous Meta login is incomplete. Reload this window before retrying a native login.");if($=await me(e,V.signal),!T())return;try{Ie(e,$,H),ie=!0}catch(w){if(!(w instanceof o&&w.code==="INVALID_CONFIGURATION"))throw w}if(!T())return}if(ie&&$){l==null||l.close(),l=null;let w=null,m=null,B=null,le=!1,de=()=>{!T()||!w||!m||re({code:w,wabaId:m,phoneId:B,coexistence:le})};W=S=>{if(!T()||!["https://www.facebook.com","https://web.facebook.com"].includes(S.origin))return;let R=S.data;if(typeof R=="string")try{R=JSON.parse(R)}catch(Le){return}if(!(!v(R)||R.type!=="WA_EMBEDDED_SIGNUP")){if(R.event==="CANCEL")C("cancelled");else if(R.event==="ERROR")E(new o("PROVIDER_ERROR","Meta could not complete the connection."));else if(R.event==="FINISH"||R.event==="FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING"){if(!v(R.data)||!O(R.data.waba_id)||R.data.phone_number_id!=null&&typeof R.data.phone_number_id!="string")return;m=O(R.data.waba_id),B=O(R.data.phone_number_id),le=R.event==="FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING",de()}}},e.addEventListener("message",W),a.nativeLoginPending=!0,a.nativeLoginOwner=L,q=!0;try{$.login(S=>{if(!Q){if(Q=!0,F){J();return}if(T()){if(!v(S)||S.error){E(new o("PROVIDER_ERROR","Meta returned an invalid login response."));return}if(S.authResponse==null){C("not_completed");return}if(!v(S.authResponse)||!O(S.authResponse.code)){C("not_completed");return}w=O(S.authResponse.code),de()}}},{config_id:Z,response_type:"code",override_default_response_type:!0,extras:{setup:{},featureType:"whatsapp_business_app_onboarding",sessionInfoVersion:"3"}})}catch(S){throw J(),q=!1,new o("PROVIDER_ERROR","The Meta login could not be started.")}return}let A;if(h==="META_MESSENGER"||h==="META_INSTAGRAM"){let w=h==="META_MESSENGER";u=new URL(`${this.constants.front}/oauth/proxy-${w?"messenger":"instagram"}`).href,A=new URL(w?"https://www.facebook.com/dialog/oauth":"https://www.instagram.com/oauth/authorize"),A.searchParams.set("client_id",H),w&&A.searchParams.set("display","popup"),A.searchParams.set("redirect_uri",u),A.searchParams.set("response_type","code"),A.searchParams.set("scope",w?"public_profile,business_management,pages_messaging,pages_show_list,pages_manage_metadata,pages_read_engagement":"instagram_business_basic,instagram_business_manage_messages"),A.searchParams.set("state",JSON.stringify({currentUrl:I,attemptId:G,channelId:s}))}else h==="META_WHATSAPP"?(A=new URL(`${this.constants.front}/oauth/proxy-cloud-api`),A.searchParams.set("configId",Z),A.searchParams.set("appId",H),A.searchParams.set("channelId",s),A.searchParams.set("channelType",h),A.searchParams.set("logo",this.logo||new URL(`${this.constants.front}/logos/SVG/Ativo%2016.svg`).href),A.searchParams.set("pk",this.publicKey)):(A=new URL(`${this.constants.front}/connect/${h==="BOT_TELEGRAM"?"telegram":"whatsapp-web"}`),A.searchParams.set("channelId",s),A.searchParams.set("pk",this.publicKey));if(h!=="META_MESSENGER"&&h!=="META_INSTAGRAM"&&A.searchParams.set("attemptId",G),W=w=>{if(!T()||!l||w.origin!==P||w.source!==l)return;let m=w.data;!v(m)||m.type!=="after-connect"||m.attemptId!==G||m.channelId!==s||typeof m.success!="boolean"||m.reason!==void 0&&!ge(m.reason)||m.coexistence!==void 0&&typeof m.coexistence!="boolean"||["code","wabaId","phoneId","botToken","pageId","accessToken","redirectUri"].some(B=>m[B]!=null&&typeof m[B]!="string")||(m.success?h!=null&&h.startsWith("META_")&&!O(m.code)||h==="BOT_TELEGRAM"&&!O(m.botToken)?C("not_completed"):re(m):C(ge(m.reason)?m.reason:"not_completed"))},e.addEventListener("message",W),!!T()){if(y)throw y;if(l||oe(),l!=null&&l.closed){C("popup_closed");return}Re();try{l==null||l.location.replace(A.href)}catch(w){throw new o("PROVIDER_ERROR","The connection popup could not be navigated.")}}})().catch(b=>{T()&&E(b instanceof o?b:new o("PROVIDER_ERROR","The connection could not be completed."))})}catch(d){E(d instanceof o?d:new o("INVALID_CONFIGURATION","The connection could not be initialized."))}})}catch(e){throw e instanceof o?e:new o("INVALID_OPTIONS","The connection could not be started with these options.")}}},ye=ee;var ne=class{static newClient(t){return new ye(t)}};var _e=ne;Object.assign(window,{Omnizapi:_e});})();
|
|
@@ -4,7 +4,7 @@ declare class OmnizapiClientImpl implements OmnizapiClient {
|
|
|
4
4
|
private readonly logo?;
|
|
5
5
|
private readonly constants;
|
|
6
6
|
constructor(props: OmnizapiClientProps);
|
|
7
|
-
getListMessengerPages(channelId: string, code: string): Promise<unknown>;
|
|
7
|
+
getListMessengerPages(channelId: string, code: string, signal?: AbortSignal): Promise<unknown>;
|
|
8
8
|
connect(options: ConnectionOptions): Promise<ConnectionResult>;
|
|
9
9
|
}
|
|
10
10
|
export default OmnizapiClientImpl;
|
|
@@ -20,5 +20,16 @@ type FacebookWindow = Window & {
|
|
|
20
20
|
FB?: FacebookSdk;
|
|
21
21
|
fbAsyncInit?: () => void;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Call only from a live attempt, before FB.login. Reuses this helper's known app;
|
|
25
|
+
* never switches apps or assumes an untracked host SDK is uninitialized.
|
|
26
|
+
* The public Meta bundle has no FB.getAppId (checked 2026-09-10). We deliberately
|
|
27
|
+
* avoid its private module runtime. Only owned SDKs have init observed: external
|
|
28
|
+
* calls are forwarded unchanged but invalidate the app, as does replacing init.
|
|
29
|
+
* Unknown host configurations and inconclusive external loads require the popup
|
|
30
|
+
* flow or a fresh, SDK-free page. Calls through a saved, pre-wrapper init reference
|
|
31
|
+
* cannot be observed; hosts must use the current FB.init method.
|
|
32
|
+
*/
|
|
33
|
+
export declare function initializeFacebookSdk(host: FacebookWindow, fb: FacebookSdk, appId: string): void;
|
|
23
34
|
export declare function loadFacebookSdk(host: FacebookWindow, signal: AbortSignal): Promise<FacebookSdk>;
|
|
24
35
|
export {};
|
|
@@ -4,8 +4,24 @@ interface SharedRuntime {
|
|
|
4
4
|
nativeLoginPending: boolean;
|
|
5
5
|
/** Attempt that set nativeLoginPending; only it may release the lock. */
|
|
6
6
|
nativeLoginOwner?: symbol;
|
|
7
|
+
/** Promise owned by the published v1 loader; newer copies must leave it alone. */
|
|
7
8
|
facebookLoading?: Promise<FacebookSdk>;
|
|
9
|
+
facebookLoadObserver?: {
|
|
10
|
+
promise: Promise<FacebookSdk>;
|
|
11
|
+
consumers: number;
|
|
12
|
+
cancelIfUnused?: () => void;
|
|
13
|
+
};
|
|
8
14
|
failedFacebookScripts: WeakSet<HTMLScriptElement>;
|
|
15
|
+
/** Unlike the legacy failure set, these scripts emitted a conclusive error. */
|
|
16
|
+
erroredFacebookScripts: WeakSet<HTMLScriptElement>;
|
|
17
|
+
/** null means a known fresh SDK; absent means its configuration is unknown. */
|
|
18
|
+
facebookAppIds: WeakMap<FacebookSdk, string | null>;
|
|
19
|
+
facebookInitObservers: WeakMap<FacebookSdk, {
|
|
20
|
+
init: FacebookSdk["init"];
|
|
21
|
+
calls: number;
|
|
22
|
+
}>;
|
|
23
|
+
/** An expired external script may still execute; only a reload clears this. */
|
|
24
|
+
facebookLoadUncertain?: boolean;
|
|
9
25
|
}
|
|
10
26
|
export declare function getRuntime(host: Window): SharedRuntime;
|
|
11
27
|
export {};
|
|
@@ -6,6 +6,7 @@ export interface ConnectionOptions {
|
|
|
6
6
|
value?: string;
|
|
7
7
|
}>;
|
|
8
8
|
timeoutMs?: number;
|
|
9
|
+
signal?: AbortSignal;
|
|
9
10
|
}
|
|
10
11
|
export type ChannelType = "META_MESSENGER" | "META_INSTAGRAM" | "META_WHATSAPP" | "BOT_TELEGRAM" | "Z_API_WHATSAPP";
|
|
11
12
|
export type ConnectionFailureReason = "cancelled" | "popup_closed" | "not_completed" | "invalid_channel" | "origin_not_allowed" | "unsupported_channel";
|
|
@@ -32,14 +33,14 @@ export interface ConnectionFailure {
|
|
|
32
33
|
phoneId: null;
|
|
33
34
|
}
|
|
34
35
|
export type ConnectionResult = ConnectionSuccess | ConnectionFailure;
|
|
35
|
-
export type OmnizapiErrorCode = "BROWSER_REQUIRED" | "CONNECTION_IN_PROGRESS" | "INVALID_CONFIGURATION" | "INVALID_OPTIONS" | "NETWORK_ERROR" | "HTTP_ERROR" | "INVALID_RESPONSE" | "POPUP_BLOCKED" | "TIMEOUT" | "SDK_LOAD_FAILED" | "PROVIDER_ERROR";
|
|
36
|
+
export type OmnizapiErrorCode = "ABORTED" | "BROWSER_REQUIRED" | "CONNECTION_IN_PROGRESS" | "INVALID_CONFIGURATION" | "INVALID_OPTIONS" | "NETWORK_ERROR" | "HTTP_ERROR" | "INVALID_RESPONSE" | "POPUP_BLOCKED" | "TIMEOUT" | "SDK_LOAD_FAILED" | "PROVIDER_ERROR";
|
|
36
37
|
export declare class OmnizapiError extends Error {
|
|
37
38
|
readonly code: OmnizapiErrorCode;
|
|
38
39
|
constructor(code: OmnizapiErrorCode, message: string);
|
|
39
40
|
}
|
|
40
41
|
export interface OmnizapiClient {
|
|
41
42
|
connect(options: ConnectionOptions): Promise<ConnectionResult>;
|
|
42
|
-
getListMessengerPages(channelId: string, code: string): Promise<unknown>;
|
|
43
|
+
getListMessengerPages(channelId: string, code: string, signal?: AbortSignal): Promise<unknown>;
|
|
43
44
|
}
|
|
44
45
|
export declare enum Environment {
|
|
45
46
|
PRODUCTION = "PRODUCTION",
|
package/examples/angular.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isPlatformBrowser } from "@angular/common";
|
|
2
2
|
import { Component, inject, Input, PLATFORM_ID } from "@angular/core";
|
|
3
3
|
import Omnizapi, { OmnizapiError } from "@omnizapi/connect";
|
|
4
|
+
import type { OmnizapiClient } from "@omnizapi/connect";
|
|
4
5
|
|
|
5
6
|
@Component({
|
|
6
7
|
selector: "app-connect",
|
|
@@ -19,7 +20,7 @@ export class ConnectComponent {
|
|
|
19
20
|
message = "";
|
|
20
21
|
|
|
21
22
|
private readonly platformId = inject(PLATFORM_ID);
|
|
22
|
-
private
|
|
23
|
+
private client: OmnizapiClient | undefined;
|
|
23
24
|
|
|
24
25
|
async handleConnect() {
|
|
25
26
|
if (!isPlatformBrowser(this.platformId) || this.loading) return;
|
|
@@ -27,6 +28,7 @@ export class ConnectComponent {
|
|
|
27
28
|
this.message = "Connecting...";
|
|
28
29
|
|
|
29
30
|
try {
|
|
31
|
+
this.client ??= Omnizapi.newClient({ publicKey: "your-public-key" });
|
|
30
32
|
const result = await this.client.connect({ channelId: this.channelId });
|
|
31
33
|
|
|
32
34
|
if (!result.success) {
|
|
@@ -47,8 +49,8 @@ export class ConnectComponent {
|
|
|
47
49
|
this.message = "Channel connected!";
|
|
48
50
|
} catch (error) {
|
|
49
51
|
this.message = error instanceof OmnizapiError
|
|
50
|
-
? `Connection error (${error.code}).
|
|
51
|
-
: "Could not
|
|
52
|
+
? `Connection error (${error.code}). Check the integration before retrying.`
|
|
53
|
+
: "Could not confirm the connection. Check its backend status before retrying.";
|
|
52
54
|
} finally {
|
|
53
55
|
this.loading = false;
|
|
54
56
|
}
|
package/examples/basic.ts
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* /api/connect-channel is your authenticated backend, not an SDK route.
|
|
6
6
|
*/
|
|
7
7
|
import Omnizapi, { OmnizapiError } from "@omnizapi/connect";
|
|
8
|
+
import type { OmnizapiClient } from "@omnizapi/connect";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
let client: OmnizapiClient | undefined;
|
|
10
11
|
|
|
11
12
|
if (typeof document !== "undefined") {
|
|
12
13
|
const button = document.querySelector<HTMLButtonElement>("#connect-btn");
|
|
@@ -19,6 +20,7 @@ if (typeof document !== "undefined") {
|
|
|
19
20
|
statusElement.textContent = "Connecting...";
|
|
20
21
|
|
|
21
22
|
try {
|
|
23
|
+
client ??= Omnizapi.newClient({ publicKey: "your-public-key" });
|
|
22
24
|
const result = await client.connect({ channelId: "your-channel-id" });
|
|
23
25
|
|
|
24
26
|
if (!result.success) {
|
|
@@ -40,8 +42,8 @@ if (typeof document !== "undefined") {
|
|
|
40
42
|
statusElement.textContent = "Channel connected!";
|
|
41
43
|
} catch (error) {
|
|
42
44
|
statusElement.textContent = error instanceof OmnizapiError
|
|
43
|
-
? `Connection error (${error.code}).
|
|
44
|
-
: "Could not
|
|
45
|
+
? `Connection error (${error.code}). Check the integration before retrying.`
|
|
46
|
+
: "Could not confirm the connection. Check its backend status before retrying.";
|
|
45
47
|
} finally {
|
|
46
48
|
button.disabled = false;
|
|
47
49
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Load after <button id="connect-btn" type="button">Connect channel</button>
|
|
3
|
+
* and <button id="cancel-btn" type="button" disabled>Cancel authorization</button>
|
|
3
4
|
* and <p id="status" role="status"></p>.
|
|
4
5
|
* Negative results resolve; technical SDK errors reject. Neither is logged.
|
|
6
|
+
* AbortSignal support is an Unreleased change; see the repository changelog.
|
|
5
7
|
*/
|
|
6
8
|
import Omnizapi, { OmnizapiError } from "@omnizapi/connect";
|
|
7
|
-
import type { ConnectionFailureReason, OmnizapiErrorCode } from "@omnizapi/connect";
|
|
9
|
+
import type { ConnectionFailureReason, OmnizapiClient, OmnizapiErrorCode } from "@omnizapi/connect";
|
|
8
10
|
|
|
9
11
|
const failureMessages: Record<ConnectionFailureReason, string> = {
|
|
10
12
|
cancelled: "Authorization was cancelled.",
|
|
11
13
|
popup_closed: "The popup was closed before authorization finished.",
|
|
12
|
-
not_completed: "Authorization was not completed.
|
|
14
|
+
not_completed: "Authorization was not completed.",
|
|
13
15
|
invalid_channel: "This channel is invalid or unavailable.",
|
|
14
16
|
origin_not_allowed: "This application origin is not authorized for the channel.",
|
|
15
17
|
unsupported_channel: "This channel type is not supported.",
|
|
@@ -17,32 +19,46 @@ const failureMessages: Record<ConnectionFailureReason, string> = {
|
|
|
17
19
|
|
|
18
20
|
const errorMessages: Record<OmnizapiErrorCode, string> = {
|
|
19
21
|
BROWSER_REQUIRED: "Open this page in a browser to connect.",
|
|
20
|
-
CONNECTION_IN_PROGRESS: "Finish the active attempt
|
|
21
|
-
INVALID_CONFIGURATION: "Check the public key, environment and
|
|
22
|
-
INVALID_OPTIONS: "Check the channel ID and
|
|
22
|
+
CONNECTION_IN_PROGRESS: "Finish the active attempt. If native Meta login is still awaiting its callback, wait for it or reload before another native attempt.",
|
|
23
|
+
INVALID_CONFIGURATION: "Check the public key, environment, base URLs and any conflicting Meta app ID.",
|
|
24
|
+
INVALID_OPTIONS: "Check the channel ID, timeout and AbortSignal options.",
|
|
23
25
|
NETWORK_ERROR: "The connection service could not be reached. Please try again.",
|
|
24
26
|
HTTP_ERROR: "The connection service rejected the request.",
|
|
25
27
|
INVALID_RESPONSE: "The connection service returned an invalid response.",
|
|
26
28
|
POPUP_BLOCKED: "Allow popups for this application, then click Connect again.",
|
|
27
|
-
TIMEOUT: "The operation timed out.
|
|
29
|
+
TIMEOUT: "The operation timed out. Only native Meta login still awaiting its callback blocks a native retry: wait for it or reload.",
|
|
30
|
+
ABORTED: "Authorization was stopped by this application. If native Meta login is still awaiting its callback, wait for it or reload before another native attempt.",
|
|
28
31
|
SDK_LOAD_FAILED: "The provider SDK could not load. Check browser restrictions.",
|
|
29
|
-
PROVIDER_ERROR: "The provider reported an error.
|
|
32
|
+
PROVIDER_ERROR: "The provider reported an error. Check the provider configuration before retrying.",
|
|
30
33
|
};
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
let client: OmnizapiClient | undefined;
|
|
33
36
|
|
|
34
37
|
if (typeof document !== "undefined") {
|
|
35
38
|
const button = document.querySelector<HTMLButtonElement>("#connect-btn");
|
|
39
|
+
const cancelButton = document.querySelector<HTMLButtonElement>("#cancel-btn");
|
|
36
40
|
const statusElement = document.querySelector<HTMLElement>("#status");
|
|
37
41
|
|
|
38
|
-
if (button && statusElement) {
|
|
42
|
+
if (button && cancelButton && statusElement) {
|
|
43
|
+
let controller: AbortController | undefined;
|
|
44
|
+
cancelButton.addEventListener("click", () => { controller?.abort(); });
|
|
45
|
+
|
|
39
46
|
button.addEventListener("click", async () => {
|
|
40
47
|
if (button.disabled) return;
|
|
41
48
|
button.disabled = true;
|
|
42
49
|
statusElement.textContent = "Connecting...";
|
|
43
50
|
|
|
44
51
|
try {
|
|
45
|
-
|
|
52
|
+
client ??= Omnizapi.newClient({ publicKey: "your-public-key" });
|
|
53
|
+
controller = new AbortController();
|
|
54
|
+
cancelButton.disabled = false;
|
|
55
|
+
const result = await client.connect({
|
|
56
|
+
channelId: "your-channel-id",
|
|
57
|
+
signal: controller.signal,
|
|
58
|
+
});
|
|
59
|
+
// Cancel only the SDK authorization, never imply a backend rollback.
|
|
60
|
+
controller = undefined;
|
|
61
|
+
cancelButton.disabled = true;
|
|
46
62
|
|
|
47
63
|
if (!result.success) {
|
|
48
64
|
statusElement.textContent = failureMessages[result.reason];
|
|
@@ -65,8 +81,10 @@ if (typeof document !== "undefined") {
|
|
|
65
81
|
// Do not display/log raw errors: messages may contain sensitive URLs.
|
|
66
82
|
statusElement.textContent = error instanceof OmnizapiError
|
|
67
83
|
? errorMessages[error.code]
|
|
68
|
-
: "Could not
|
|
84
|
+
: "Could not confirm the connection. Check its backend status before retrying.";
|
|
69
85
|
} finally {
|
|
86
|
+
controller = undefined;
|
|
87
|
+
cancelButton.disabled = true;
|
|
70
88
|
button.disabled = false;
|
|
71
89
|
}
|
|
72
90
|
});
|
package/examples/react.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import Omnizapi, { OmnizapiError } from "@omnizapi/connect";
|
|
5
|
+
import type { OmnizapiClient } from "@omnizapi/connect";
|
|
5
6
|
|
|
6
7
|
interface ConnectButtonProps {
|
|
7
8
|
channelId: string;
|
|
@@ -9,20 +10,28 @@ interface ConnectButtonProps {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export function ConnectButton({ channelId, onSuccess }: ConnectButtonProps) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
logo: "https://app.example.com/logo.png",
|
|
15
|
-
}));
|
|
13
|
+
const client = useRef<OmnizapiClient | undefined>(undefined);
|
|
14
|
+
const activeController = useRef<AbortController | undefined>(undefined);
|
|
16
15
|
const [loading, setLoading] = useState(false);
|
|
17
16
|
const [message, setMessage] = useState("");
|
|
18
17
|
|
|
18
|
+
// AbortSignal support is Unreleased. Cleanup does not undo backend changes.
|
|
19
|
+
useEffect(() => () => { activeController.current?.abort(); }, []);
|
|
20
|
+
|
|
19
21
|
async function handleConnect() {
|
|
20
|
-
if (
|
|
22
|
+
if (activeController.current) return;
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
activeController.current = controller;
|
|
21
25
|
setLoading(true);
|
|
22
26
|
setMessage("Connecting...");
|
|
23
27
|
|
|
24
28
|
try {
|
|
25
|
-
|
|
29
|
+
client.current ??= Omnizapi.newClient({
|
|
30
|
+
publicKey: "your-public-key",
|
|
31
|
+
logo: "https://app.example.com/logo.png",
|
|
32
|
+
});
|
|
33
|
+
const result = await client.current.connect({ channelId, signal: controller.signal });
|
|
34
|
+
if (controller.signal.aborted) return;
|
|
26
35
|
|
|
27
36
|
if (!result.success) {
|
|
28
37
|
setMessage(`Connection not completed (${result.reason}).`);
|
|
@@ -34,7 +43,9 @@ export function ConnectButton({ channelId, onSuccess }: ConnectButtonProps) {
|
|
|
34
43
|
method: "POST",
|
|
35
44
|
headers: { "Content-Type": "application/json" },
|
|
36
45
|
body: JSON.stringify(result),
|
|
46
|
+
signal: controller.signal,
|
|
37
47
|
});
|
|
48
|
+
if (controller.signal.aborted) return;
|
|
38
49
|
if (!response.ok) {
|
|
39
50
|
setMessage("The backend could not finalize the connection.");
|
|
40
51
|
return;
|
|
@@ -43,11 +54,13 @@ export function ConnectButton({ channelId, onSuccess }: ConnectButtonProps) {
|
|
|
43
54
|
setMessage("Channel connected!");
|
|
44
55
|
onSuccess?.();
|
|
45
56
|
} catch (error) {
|
|
57
|
+
if (controller.signal.aborted) return;
|
|
46
58
|
setMessage(error instanceof OmnizapiError
|
|
47
|
-
? `Connection error (${error.code}).
|
|
48
|
-
: "Could not
|
|
59
|
+
? `Connection error (${error.code}). Check the integration before retrying.`
|
|
60
|
+
: "Could not confirm the connection. Check its backend status before retrying.");
|
|
49
61
|
} finally {
|
|
50
|
-
|
|
62
|
+
if (activeController.current === controller) activeController.current = undefined;
|
|
63
|
+
if (!controller.signal.aborted) setLoading(false);
|
|
51
64
|
}
|
|
52
65
|
}
|
|
53
66
|
|
package/examples/vanilla.html
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<button id="connect-btn" type="button">Connect channel</button>
|
|
11
11
|
<p id="status" role="status"></p>
|
|
12
12
|
|
|
13
|
-
<!--
|
|
14
|
-
<script src="https://unpkg.com/@omnizapi/connect/dist/omnizapi.min.js"></script>
|
|
13
|
+
<!-- Version pinned to 1.0.2; test your integration and serve this page over HTTP(S). -->
|
|
14
|
+
<script src="https://unpkg.com/@omnizapi/connect@1.0.2/dist/omnizapi.min.js"></script>
|
|
15
15
|
<script>
|
|
16
16
|
const button = document.getElementById("connect-btn");
|
|
17
17
|
const statusElement = document.getElementById("status");
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
// The browser global exposes the factory, not the named error export.
|
|
50
50
|
// Never log raw errors or credentials, including URLs in error messages.
|
|
51
51
|
statusElement.textContent =
|
|
52
|
-
"Could not
|
|
52
|
+
"Could not confirm the connection. Check the integration and backend status before retrying.";
|
|
53
53
|
} finally {
|
|
54
54
|
button.disabled = false;
|
|
55
55
|
button.textContent = "Connect channel";
|
package/examples/vue.vue
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<script setup lang="ts">
|
|
11
11
|
import { ref } from "vue";
|
|
12
12
|
import Omnizapi, { OmnizapiError } from "@omnizapi/connect";
|
|
13
|
+
import type { OmnizapiClient } from "@omnizapi/connect";
|
|
13
14
|
|
|
14
15
|
const props = defineProps<{
|
|
15
16
|
channelId: string;
|
|
@@ -19,7 +20,7 @@ const emit = defineEmits<{
|
|
|
19
20
|
success: [];
|
|
20
21
|
}>();
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
let client: OmnizapiClient | undefined;
|
|
23
24
|
const loading = ref(false);
|
|
24
25
|
const message = ref("");
|
|
25
26
|
|
|
@@ -29,6 +30,7 @@ async function handleConnect() {
|
|
|
29
30
|
message.value = "Connecting...";
|
|
30
31
|
|
|
31
32
|
try {
|
|
33
|
+
client ??= Omnizapi.newClient({ publicKey: "your-public-key" });
|
|
32
34
|
const result = await client.connect({ channelId: props.channelId });
|
|
33
35
|
|
|
34
36
|
if (!result.success) {
|
|
@@ -50,8 +52,8 @@ async function handleConnect() {
|
|
|
50
52
|
emit("success");
|
|
51
53
|
} catch (error) {
|
|
52
54
|
message.value = error instanceof OmnizapiError
|
|
53
|
-
? `Connection error (${error.code}).
|
|
54
|
-
: "Could not
|
|
55
|
+
? `Connection error (${error.code}). Check the integration before retrying.`
|
|
56
|
+
: "Could not confirm the connection. Check its backend status before retrying.";
|
|
55
57
|
} finally {
|
|
56
58
|
loading.value = false;
|
|
57
59
|
}
|
package/examples/with-options.ts
CHANGED
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
* <button type="button" data-omnizapi-connect data-direct="true">Connect directly</button>
|
|
6
6
|
* <p id="status" role="status"></p>
|
|
7
7
|
* Channel config or the connection frontend origin may select native signup
|
|
8
|
-
* even with direct: false
|
|
8
|
+
* even with direct: false, and direct: true degrades to the proxy flow when
|
|
9
|
+
* this page cannot run the native setup (for example a conflicting or unknown
|
|
10
|
+
* host Meta SDK). Only native login still awaiting its FB.login callback
|
|
11
|
+
* blocks another native attempt: wait for that callback or reload the page.
|
|
12
|
+
* Native signup still needs real-browser testing with the Meta SDK's user gesture.
|
|
9
13
|
*/
|
|
10
14
|
import Omnizapi, { Environment, OmnizapiError } from "@omnizapi/connect";
|
|
15
|
+
import type { OmnizapiClient } from "@omnizapi/connect";
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
publicKey: "your-public-key",
|
|
14
|
-
logo: "https://app.example.com/logo.png",
|
|
15
|
-
environment: Environment.SANDBOX,
|
|
16
|
-
apiUrl: "https://api.example.com",
|
|
17
|
-
frontUrl: "https://connect.example.com",
|
|
18
|
-
});
|
|
17
|
+
let client: OmnizapiClient | undefined;
|
|
19
18
|
|
|
20
19
|
if (typeof document !== "undefined") {
|
|
21
20
|
const buttons = document.querySelectorAll<HTMLButtonElement>("[data-omnizapi-connect]");
|
|
@@ -29,6 +28,13 @@ if (typeof document !== "undefined") {
|
|
|
29
28
|
statusElement.textContent = "Connecting...";
|
|
30
29
|
|
|
31
30
|
try {
|
|
31
|
+
client ??= Omnizapi.newClient({
|
|
32
|
+
publicKey: "your-public-key",
|
|
33
|
+
logo: "https://app.example.com/logo.png",
|
|
34
|
+
environment: Environment.SANDBOX,
|
|
35
|
+
apiUrl: "https://api.example.com",
|
|
36
|
+
frontUrl: "https://connect.example.com",
|
|
37
|
+
});
|
|
32
38
|
const result = await client.connect({
|
|
33
39
|
channelId: "your-meta-whatsapp-channel-id",
|
|
34
40
|
direct: button.dataset.direct === "true",
|
|
@@ -53,8 +59,8 @@ if (typeof document !== "undefined") {
|
|
|
53
59
|
statusElement.textContent = "Channel connected!";
|
|
54
60
|
} catch (error) {
|
|
55
61
|
statusElement.textContent = error instanceof OmnizapiError
|
|
56
|
-
? `Connection error (${error.code}). Check
|
|
57
|
-
: "Could not
|
|
62
|
+
? `Connection error (${error.code}). Check the integration before retrying.`
|
|
63
|
+
: "Could not confirm the connection. Check its backend status before retrying.";
|
|
58
64
|
} finally {
|
|
59
65
|
buttons.forEach((item) => { item.disabled = false; });
|
|
60
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnizapi/connect",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Omnizapi SDK for browser applications",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
|
-
"examples"
|
|
25
|
+
"examples",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"CHANGELOG.md"
|
|
26
28
|
],
|
|
27
29
|
"scripts": {
|
|
28
30
|
"dev": "rimraf dist && npm run build:types && dotenv -e .env.local -- node esbuild.config.mjs --watch",
|
|
@@ -30,7 +32,12 @@
|
|
|
30
32
|
"build:types": "tsc -p tsconfig.types.json",
|
|
31
33
|
"lint": "eslint src __tests__ scripts --ext .ts,.mts,.mjs",
|
|
32
34
|
"test": "jest",
|
|
33
|
-
"test:package": "node scripts/test-package.mjs"
|
|
35
|
+
"test:package": "node scripts/test-package.mjs",
|
|
36
|
+
"test:browser": "node scripts/test-browser.mjs",
|
|
37
|
+
"test:lifecycle": "node scripts/test-lifecycle.mjs",
|
|
38
|
+
"check": "node scripts/check.mjs",
|
|
39
|
+
"prepack": "npm run check",
|
|
40
|
+
"version": "node scripts/sync-version.mjs"
|
|
34
41
|
},
|
|
35
42
|
"keywords": [
|
|
36
43
|
"omnizapi",
|
|
@@ -53,6 +60,7 @@
|
|
|
53
60
|
"jest": "^29.5.0",
|
|
54
61
|
"jest-environment-jsdom": "^29.7.0",
|
|
55
62
|
"jsdom": "^20.0.3",
|
|
63
|
+
"playwright": "^1.63.0",
|
|
56
64
|
"rimraf": "^5.0.0",
|
|
57
65
|
"ts-jest": "^29.1.0",
|
|
58
66
|
"typescript": "^5.0.4"
|