@omnizapi/connect 1.0.3 → 1.1.0

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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-10-08
4
+
5
+ ### Mudanca de comportamento
6
+
7
+ - `connect` com canal `Z_API_WHATSAPP` passa a exigir `getQRCode`. Sem ele, rejeita com `OmnizapiError` de codigo `INVALID_OPTIONS` depois de ler a configuracao do canal e antes de navegar a janela reservada, que e fechada. Na 1.0.4 a janela abria e a tentativa terminava em `not_completed`. Origem nao liberada continua resolvendo `origin_not_allowed`, porque e conferida antes.
8
+
9
+ ### Novidade
10
+
11
+ - A janela `/connect/whatsapp-web` passa a receber `qr=message` e a pedir o QR por `postMessage` (`qr-code-request`). O SDK confere origem, janela, `attemptId`, `channelId` e `requestId` (ate 64 caracteres), chama o `getQRCode` e responde (`qr-code-response`) so para a origem exata do frontend. Funciona com a aplicacao em qualquer origem liberada para o canal; na 1.0.4 so funcionava na mesma origem da janela.
12
+ - No maximo uma chamada ao `getQRCode` por vez em cada tentativa: um pedido que chega com uma chamada em andamento recebe o resultado dela.
13
+ - A recusa do `getQRCode` chega a janela com `status` e `message`, lidos do corpo da resposta quando existe. O texto tecnico do axios ("Request failed with status code 422") nao e usado. Retorno sem `value` e sem `connected: true` significa QR ainda nao pronto.
14
+
15
+ ### Compatibilidade
16
+
17
+ - Os globais `__omnizapiPublicKey`, `__omnizapiCurrentChannel` e `__omnizapiGetQrCode` continuam publicados durante a tentativa, para a pagina de conexao anterior a `qr=message`, que so os le na mesma origem.
18
+ - Quem fica na 1.0.4, inclusive por unpkg ou jsdelivr com versao fixa, continua sem conseguir conectar `Z_API_WHATSAPP` fora do painel da plataforma: a tentativa termina em `not_completed`.
19
+
20
+ ## [1.0.4] - 2026-09-11
21
+
22
+ ### Correcao
23
+
24
+ - Eventos do Embedded Signup passam a ser aceitos de qualquer origem HTTPS em `facebook.com` ou subdominio (www, web, business). A allowlist exata de duas origens deixava a conexao nativa sem resposta quando o dialogo da Meta era servido de outro host, e a tentativa ficava aberta ate o timeout.
25
+ - `FINISH_ONLY_WABA` conclui a conexao sem numero de telefone, em vez de ser ignorado.
26
+
3
27
  ## [1.0.3] - 2026-09-11
4
28
 
5
29
  ### Correcao
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  SDK Omnizapi para conectar canais Meta WhatsApp, Messenger, Instagram, Telegram e Z-API WhatsApp em aplicacoes web. Coleta o resultado da autorizacao; seu backend valida e finaliza a conexao. Nao envie secret keys ao frontend.
4
4
 
5
- Versao atual: **1.0.3**. As mudancas desta versao estao no [changelog](https://unpkg.com/browse/@omnizapi/connect@1.0.3/CHANGELOG.md), disponivel com o pacote publicado.
5
+ Versao atual: **1.1.0**. As mudancas desta versao estao no [changelog](https://unpkg.com/browse/@omnizapi/connect@1.1.0/CHANGELOG.md), disponivel com o pacote publicado.
6
6
 
7
7
  ## Instalacao e importacao
8
-
8
+ eu
9
9
  ```bash
10
10
  npm install @omnizapi/connect
11
11
  ```
@@ -31,7 +31,7 @@ CommonJS: `const { Omnizapi } = require("@omnizapi/connect")`.
31
31
  No navegador, o bundle expoe o global `Omnizapi`:
32
32
 
33
33
  ```html
34
- <script src="https://unpkg.com/@omnizapi/connect@1.0.3/dist/omnizapi.min.js"></script>
34
+ <script src="https://unpkg.com/@omnizapi/connect@1.1.0/dist/omnizapi.min.js"></script>
35
35
  ```
36
36
 
37
37
  O CDN usa uma versao fixa; homologue sua integracao antes do deploy. Imports ESM/CommonJS e `newClient` sao seguros em SSR; `connect` exige navegador e rejeita com `BROWSER_REQUIRED` no servidor. O bundle de script e exclusivo do navegador.
@@ -44,15 +44,15 @@ Dispare `client.connect({ channelId })` diretamente no clique, sem outro `await`
44
44
 
45
45
  | Exemplo | Uso |
46
46
  | --- | --- |
47
- | [TypeScript basico](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/basic.ts) | Botao e mensagem de status no DOM |
48
- | [HTML com CDN](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/vanilla.html) | Pagina sem bundler |
49
- | [React / Next.js](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/react.tsx) | `'use client'`, UI de conexao por componente |
50
- | [Angular](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/angular.ts) | Componente com guarda de navegador para SSR |
51
- | [Vue](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/vue.vue) | SFC com guarda de navegador para SSR |
52
- | [Opcoes](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/with-options.ts) | SANDBOX, URLs explicitas, logo, fluxo padrao/direto e timeout |
53
- | [Erros](https://unpkg.com/browse/@omnizapi/connect@1.0.3/examples/error-handling.ts) | Mensagens para todos os motivos e codigos tipados |
47
+ | [TypeScript basico](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/basic.ts) | Botao e mensagem de status no DOM |
48
+ | [HTML com CDN](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/vanilla.html) | Pagina sem bundler |
49
+ | [React / Next.js](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/react.tsx) | `'use client'`, UI de conexao por componente |
50
+ | [Angular](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/angular.ts) | Componente com guarda de navegador para SSR |
51
+ | [Vue](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/vue.vue) | SFC com guarda de navegador para SSR |
52
+ | [Opcoes](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/with-options.ts) | SANDBOX, URLs explicitas, logo, fluxo padrao/direto e timeout |
53
+ | [Erros](https://unpkg.com/browse/@omnizapi/connect@1.1.0/examples/error-handling.ts) | Mensagens para todos os motivos e codigos tipados |
54
54
 
55
- Os links abrem o codigo-fonte dos exemplos da versao 1.0.3, incluido no pacote npm. Nao sao demos hospedadas: adapte as chaves, o canal e o backend na sua aplicacao.
55
+ Os links abrem o codigo-fonte dos exemplos da versao 1.1.0, incluido no pacote npm. Nao sao demos hospedadas: adapte as chaves, o canal e o backend na sua aplicacao.
56
56
 
57
57
  Os arquivos `examples/error-handling.ts` e `examples/react.tsx` demonstram `AbortSignal`. No React, o cliente fica em `useRef`, e so e criado no `try` do clique; o cleanup de `useEffect` aborta a tentativa/requisicao pendente sem atualizar a UI desmontada. O exemplo de erros usa `Record<OmnizapiErrorCode, string>` exaustivo e permite cancelar apenas a autorizacao, nao a finalizacao no backend. Nao ha botao para limpar a quarentena nativa.
58
58
 
@@ -88,7 +88,7 @@ Precedencia por URL: **props > `OMNIZAPI_API_URL` / `OMNIZAPI_FRONT_URL` no buil
88
88
  | --- | --- | --- |
89
89
  | `channelId` | `string` | Obrigatorio; canal a conectar |
90
90
  | `direct` | `boolean` | Solicita o Meta WhatsApp Embedded Signup nativo na propria pagina, sem o popup proxy. Com `direct` o SDK **nao reserva** janela no clique, porque o dialogo da Meta abre a sua e navegadores concedem um popup por clique. Use so em pagina onde o clique e dedicado a conexao (o frontend de conexao faz isso em `/oauth/proxy-cloud-api`); em outros canais, ou quando a pagina nao pode rodar o nativo, a janela do proxy e aberta na navegacao e pode ser bloqueada. Sem `direct`, todo Meta WhatsApp usa a janela reservada, inclusive com `appCustom` e na origem do frontend de conexao |
91
- | `getQRCode` | `() => Promise<{ connected?: boolean; value?: string }>` | Callback opcional para integracao QR com o frontend externo; veja os limites abaixo |
91
+ | `getQRCode` | `() => Promise<{ connected?: boolean; value?: string }>` | **Obrigatorio para `Z_API_WHATSAPP`**: devolve o QR do canal, buscado no seu backend; veja [QR code do WhatsApp Web](#qr-code-do-whatsapp-web). Sem ele, `connect` rejeita com `INVALID_OPTIONS` antes de a janela navegar, e a janela reservada no clique e fechada. Ignorado nos outros canais |
92
92
  | `timeoutMs` | `number` | Prazo total positivo e finito em ms, ate `2147483647`; padrao **600000 (10 minutos)** |
93
93
  | `signal` | `AbortSignal` | Opcional; permite interromper a tentativa via `AbortController` |
94
94
 
@@ -131,7 +131,7 @@ Falhas tecnicas rejeitam com `OmnizapiError`; use `instanceof OmnizapiError` e `
131
131
  | `BROWSER_REQUIRED` | Operacao exige navegador |
132
132
  | `CONNECTION_IN_PROGRESS` | Tentativa ativa nesta pagina ou login Meta nativo anterior incompleto; veja os limites abaixo |
133
133
  | `INVALID_CONFIGURATION` | Props, ambiente ou APIs de navegador invalidos; conflitos de app/inicializacao do SDK Meta degradam para o fluxo proxy, sem rejeitar |
134
- | `INVALID_OPTIONS` | Opcoes da operacao invalidas, como `channelId` com tipo incorreto, timeout ou sinal invalido |
134
+ | `INVALID_OPTIONS` | Opcoes da operacao invalidas, como `channelId` com tipo incorreto, timeout ou sinal invalido, ou `Z_API_WHATSAPP` sem `getQRCode` |
135
135
  | `NETWORK_ERROR` | Falha de transporte |
136
136
  | `HTTP_ERROR` | Resposta HTTP sem sucesso |
137
137
  | `INVALID_RESPONSE` | Resposta da API invalida |
@@ -145,14 +145,60 @@ Falhas tecnicas rejeitam com `OmnizapiError`; use `instanceof OmnizapiError` e `
145
145
 
146
146
  Consulta as paginas Messenger usando o codigo OAuth e o canal. Valide o retorno antes de acessa-lo: o schema externo ainda nao foi confirmado, portanto o SDK nao publica DTO de pagina. Nao use cast para inventar esse contrato, nem registre o codigo ou a resposta. A chamada tambem pode rejeitar com erro tecnico e tem timeout HTTP de 30 segundos. O terceiro argumento aceita um `AbortSignal` opcional; um sinal abortado (antes ou durante a chamada) rejeita com `OmnizapiError.code === "ABORTED"`, e um sinal invalido rejeita com `INVALID_OPTIONS`.
147
147
 
148
+ ## QR code do WhatsApp Web
149
+
150
+ Para `Z_API_WHATSAPP`, a janela de conexao da plataforma pede o QR a sua pagina, e o SDK chama o seu `getQRCode`. A busca do QR usa a **Secret Key**, entao acontece no seu backend; a chave nunca vai para o navegador.
151
+
152
+ No navegador:
153
+
154
+ ```js
155
+ const result = await client.connect({
156
+ channelId,
157
+ getQRCode: async () => {
158
+ const response = await fetch(`/api/omni/channels/${encodeURIComponent(channelId)}/qr-code`, { credentials: "include" });
159
+ const body = await response.json();
160
+ // Recusa com status e mensagem: a janela mostra a mensagem quando o status e 4xx.
161
+ if (!response.ok) throw { status: response.status, message: body.message ?? null };
162
+ // Sem value e sem connected: true, a janela entende que o QR ainda nao esta pronto.
163
+ return { connected: body.connected === true, value: body.qrcode ?? undefined };
164
+ },
165
+ });
166
+ ```
167
+
168
+ No seu backend, confira a sessao do usuario e se o canal pertence ao tenant dele **antes** de chamar a API. Um endpoint que entrega o QR de qualquer canal permite que alguem ligue o proprio numero a um canal alheio.
169
+
170
+ ```js
171
+ app.get("/api/omni/channels/:id/qr-code", requireSessionAndChannelOwnership, async (req, res) => {
172
+ const response = await fetch(
173
+ `https://api.omni.z-api.io/v1/channels/${encodeURIComponent(req.params.id)}/zapi/qr-code/image`,
174
+ { headers: { Authorization: `Bearer ${process.env.OMNI_SECRET_KEY}` } },
175
+ );
176
+ const body = await response.json();
177
+ if (!response.ok) {
178
+ // O erro da API vem em error.errors[0]; repasse um texto, e nao o objeto.
179
+ return res.status(response.status).json({ message: body?.error?.errors?.[0]?.description ?? null });
180
+ }
181
+ res.json({ qrcode: body.qrcode, connected: body.connected });
182
+ });
183
+ ```
184
+
185
+ Como o SDK le a recusa do `getQRCode`:
186
+
187
+ - `status`: `status` ou `response.status`, inteiro entre 100 e 599;
188
+ - `message`: `response.data.message`, `response.data.error` ou `response.data.detail`; sem `response`, o proprio `message`. O texto tecnico do axios ("Request failed with status code 422") nao e usado quando ha `response`. Ate 300 caracteres;
189
+ - a janela mostra a mensagem nas recusas 4xx; as demais falhas contam como passageiras, e tres seguidas mostram o erro de rede.
190
+
191
+ A janela manda o pedido para as origens liberadas do canal (menu Seguranca do painel). Se nao conseguir le-las, usa a origem do `referrer`. Com `Referrer-Policy: no-referrer` na sua pagina e as origens ilegiveis, a janela nao tem para onde mandar o pedido, e a conexao termina em erro de rede.
192
+
148
193
  ## Protocolo de popup
149
194
 
150
195
  Este contrato exige deploy coordenado do frontend/proxy e backend externos; nenhum desses servicos e alterado neste repositorio. Nao ha fallback para mensagens sem os identificadores exigidos.
151
196
 
152
- 1. O SDK gera `attemptId` por tentativa. Popups abertos diretamente no `frontUrl` recebem `attemptId` na query; as rotas `/connect/telegram` e `/connect/whatsapp-web` tambem recebem `pk` (a chave publica) na query, permitindo ao frontend consultar as origens autorizadas do canal e restringir o `targetOrigin` da resposta. No OAuth Messenger/Instagram, o `state` JSON e `{ currentUrl, attemptId, channelId }`; o proxy deve preserva-lo e vincula-lo a sessao autorizada, ao canal e ao tenant. `state` e query nao sao prova de autorizacao por si so.
197
+ 1. O SDK gera `attemptId` por tentativa. Popups abertos diretamente no `frontUrl` recebem `attemptId` na query; as rotas `/connect/telegram` e `/connect/whatsapp-web` tambem recebem `pk` (a chave publica) na query, e `/connect/whatsapp-web` recebe `qr=message`, permitindo ao frontend consultar as origens autorizadas do canal e restringir o `targetOrigin` da resposta. No OAuth Messenger/Instagram, o `state` JSON e `{ currentUrl, attemptId, channelId }`; o proxy deve preserva-lo e vincula-lo a sessao autorizada, ao canal e ao tenant. `state` e query nao sao prova de autorizacao por si so.
153
198
  2. A pagina final deve estar na origem exata de `frontUrl` e emitir da **propria janela popup** aberta pelo SDK, nao de iframe, outra janela ou backend. Ela responde ao `window.opener` usando `postMessage` com `type: 'after-connect'`, `attemptId`, `channelId`, `success` e as credenciais aplicaveis. `attemptId` e `channelId` devem corresponder exatamente a tentativa ativa.
154
199
  3. O SDK verifica a origem exata do frontend, `event.source` igual ao popup ativo e os dois identificadores. O emissor usa `targetOrigin` igual a origem exata da aplicacao autorizada, nunca wildcard e nunca uma origem derivada de query/state nao validados.
155
200
  4. Sucesso dos proxies Meta exige `code`; Telegram exige `botToken`. `Z_API_WHATSAPP` pode retornar sucesso autenticado sem credenciais, mas o backend deve confirmar a conexao real para aquele canal/tenant. Para negativo, envie `success: false` e o `reason` tipado aplicavel, sem credenciais.
201
+ 5. Com `qr=message`, a pagina `/connect/whatsapp-web` pede o QR ao `window.opener` com `{ type: 'qr-code-request', attemptId, channelId, requestId }`, um pedido por vez. O SDK confere a origem exata do frontend, `event.source` igual ao popup ativo, os dois identificadores e um `requestId` em texto nao vazio de ate 64 caracteres; pedido que nao confere e ignorado. Com uma chamada ao `getQRCode` em andamento, o pedido novo recebe o resultado dela, sem chamar de novo. A resposta vai so para a origem exata do frontend: `{ type: 'qr-code-response', attemptId, channelId, requestId, connected, value }`, ou `{ ..., error: { status, message } }` quando o `getQRCode` rejeita ou devolve algo invalido. A pagina confere `event.source` igual ao `window.opener`, a origem e os tres identificadores.
156
202
 
157
203
  **Privacidade de `state`: `currentUrl` continua sendo `window.location.href` COMPLETO, incluindo caminho, query e fragmento.** Esta rodada nao reduz esse valor a origem: a compatibilidade dos proxies com uma URL minimizada ainda e desconhecida. Tokens ou outros dados sensiveis na query/fragmento da pagina podem assim ser copiados para o `state` OAuth e expostos ao provedor, proxy, historico ou logs de URLs. Evite iniciar a conexao em paginas com esses dados e nao registre `state` nem URLs completas. Fica pendente verificar com os responsaveis pelos proxies se aceitam apenas a origem antes de propor qualquer mudanca coordenada desse contrato.
158
204
 
@@ -172,11 +218,22 @@ if (window.opener) {
172
218
  }
173
219
  ```
174
220
 
175
- O Meta Embedded Signup **nativo** nao fornece `attemptId` nem uma referencia customizada da janela emissora (`event.source`) para correlacao. Ele usa a allowlist exata `https://www.facebook.com` / `https://web.facebook.com` e o callback `FB.login`. Nao se pode garantir o isolamento de eventos nativos tardios entre tentativas; essas verificacoes nao sao garantia absoluta de seguranca.
221
+ O Meta Embedded Signup **nativo** nao fornece `attemptId` nem uma referencia customizada da janela emissora (`event.source`) para correlacao. Ele aceita eventos `WA_EMBEDDED_SIGNUP` de qualquer origem HTTPS em `facebook.com` ou subdominio (www, web, business), como a Meta orienta, e o callback `FB.login`. Os eventos `FINISH`, `FINISH_ONLY_WABA` e `FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING` concluem a conexao; `CANCEL` e `ERROR` a encerram. Nao se pode garantir o isolamento de eventos nativos tardios entre tentativas; essas verificacoes nao sao garantia absoluta de seguranca.
176
222
 
177
223
  Um login nativo fica em quarentena ate a Meta responder o callback `FB.login` (com codigo, cancelamento ou erro) ou ate a tentativa ter sucesso. O callback, inclusive tardio, libera apenas a quarentena da tentativa a que pertence; nao libera a de outra tentativa. Fechar/cancelar o dialogo ou receber um evento de cancelamento nao garante que esse callback ja tenha chegado. Se um login terminar por timeout, abort ou cancelamento **sem callback**, novas tentativas nativas continuam em `CONNECTION_IN_PROGRESS`: aguarde o callback ou **recarregue a pagina** para tentar outro login nativo. Um callback recebido de cancelamento nao exige reload. Um `FB.login` que falha ao iniciar (excecao sincrona, sem abrir dialogo e sem callback futuro) libera a quarentena imediatamente, sem exigir reload. Essa restricao reduz o risco de atribuir a uma nova tentativa a resposta tardia de um dialogo anterior; nao se aplica aos popups proxy.
178
224
 
179
- Durante uma tentativa, apenas `__omnizapiPublicKey`, `__omnizapiCurrentChannel` e `__omnizapiGetQrCode` sao publicados temporariamente para o frontend externo. Uma funcao QR no `window` da aplicacao nao atravessa origens nem e serializada por `postMessage`: o frontend precisa de uma integracao explicitamente autorizada e compativel com a politica de mesma origem. Este repositorio nao valida por si so o fluxo Z-API real nem confirma essa integracao externa.
225
+ Durante uma tentativa, apenas `__omnizapiPublicKey`, `__omnizapiCurrentChannel` e `__omnizapiGetQrCode` sao publicados temporariamente no `window` da aplicacao, e removidos ao fim. So uma pagina na mesma origem consegue le-los: e o caminho da pagina de conexao anterior a `qr=message`, mantido para o painel da plataforma. De outra origem, o QR viaja por mensagem (item 5 do protocolo), porque uma funcao no `window` da aplicacao nao atravessa origens nem e serializada por `postMessage`.
226
+
227
+ ## CSP da pagina que roda o login nativo
228
+
229
+ O `FB.login` conclui por um iframe oculto de `staticxx.facebook.com` na propria pagina. Se a sua pagina tem Content-Security-Policy e usa `direct: true`, libere:
230
+
231
+ ```
232
+ script-src https://connect.facebook.net
233
+ frame-src https://www.facebook.com https://web.facebook.com https://business.facebook.com https://staticxx.facebook.com
234
+ ```
235
+
236
+ Sem o `frame-src` acima, o dialogo da Meta abre, o usuario conclui, e o dialogo fica preso em `dialog/oauth/business/cancel` sem devolver o resultado. Sem `direct`, o login roda no frontend de conexao da plataforma e a sua pagina nao precisa de nada disso.
180
237
 
181
238
  ## App da Meta proprio (`appCustom`)
182
239
 
@@ -240,4 +297,4 @@ O hook de versao `scripts/sync-version.mjs` sincroniza os links UNPKG e scripts
240
297
 
241
298
  ## Licenca
242
299
 
243
- MIT - Copyright (c) 2026 FourPixel IT. Autor: FourPixel IT. Texto completo no arquivo local `LICENSE` e na [licenca do pacote](https://unpkg.com/browse/@omnizapi/connect@1.0.3/LICENSE) apos a publicacao correspondente.
300
+ MIT - Copyright (c) 2026 FourPixel IT. Autor: FourPixel IT. Texto completo no arquivo local `LICENSE` e na [licenca do pacote](https://unpkg.com/browse/@omnizapi/connect@1.1.0/LICENSE) apos a publicacao correspondente.
package/dist/cjs/index.js CHANGED
@@ -387,6 +387,15 @@ function encodeChannelId(channelId) {
387
387
  throw new OmnizapiError("INVALID_OPTIONS", "The channelId cannot be encoded as a URL path.");
388
388
  }
389
389
  }
390
+ function isFacebookOrigin(origin) {
391
+ let url;
392
+ try {
393
+ url = new URL(origin);
394
+ } catch (e) {
395
+ return false;
396
+ }
397
+ return url.protocol === "https:" && (url.hostname === "facebook.com" || url.hostname.endsWith(".facebook.com"));
398
+ }
390
399
  function isChannelType(value) {
391
400
  return [
392
401
  "META_MESSENGER",
@@ -406,6 +415,25 @@ function isFailureReason(value) {
406
415
  "unsupported_channel"
407
416
  ].includes(value);
408
417
  }
418
+ var QR_REQUEST_ID_MAX_LENGTH = 64;
419
+ function qrCodeAnswer(result) {
420
+ if (!isRecord(result) || result.value != null && typeof result.value !== "string" || result.connected !== void 0 && typeof result.connected !== "boolean") {
421
+ return { error: { status: null, message: null } };
422
+ }
423
+ return { connected: result.connected === true, value: typeof result.value === "string" ? result.value : null };
424
+ }
425
+ var QR_ERROR_MESSAGE_LIMIT = 300;
426
+ function qrCodeError(reason) {
427
+ const source = isRecord(reason) ? reason : {};
428
+ const response = isRecord(source.response) ? source.response : void 0;
429
+ const data = response && isRecord(response.data) ? response.data : {};
430
+ const status = [source.status, response == null ? void 0 : response.status].find(
431
+ (value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599
432
+ );
433
+ const candidates = response ? [data.message, data.error, data.detail] : [source.message];
434
+ const message = candidates.find((value) => typeof value === "string" && value.trim() !== "");
435
+ return { status: status != null ? status : null, message: message ? message.trim().slice(0, QR_ERROR_MESSAGE_LIMIT) : null };
436
+ }
409
437
  function failure(channelId, type, reason) {
410
438
  return { success: false, reason, channelId, type, code: null, wabaId: null, phoneId: null };
411
439
  }
@@ -550,6 +578,7 @@ var OmnizapiClientImpl = class {
550
578
  let messageListener;
551
579
  let nativeLoginStarted = false;
552
580
  let callbackReceived = false;
581
+ let qrCodeCall = null;
553
582
  const releaseNativeLogin = () => {
554
583
  if (runtime.nativeLoginOwner === owner) {
555
584
  runtime.nativeLoginPending = false;
@@ -709,6 +738,9 @@ var OmnizapiClientImpl = class {
709
738
  fail("unsupported_channel");
710
739
  return;
711
740
  }
741
+ if (channelType === "Z_API_WHATSAPP" && !getQRCode) {
742
+ throw new OmnizapiError("INVALID_OPTIONS", "getQRCode is required to connect a Z_API_WHATSAPP channel.");
743
+ }
712
744
  const appId = (_b = stringOrNull(channelType === "META_INSTAGRAM" ? info.appInstagramId : info.appId)) != null ? _b : "";
713
745
  const configId = (_c = stringOrNull(info.appConfig)) != null ? _c : "";
714
746
  if (channelType.startsWith("META_") && !appId || channelType === "META_WHATSAPP" && !configId) {
@@ -747,10 +779,7 @@ var OmnizapiClientImpl = class {
747
779
  succeed({ code, wabaId, phoneId, coexistence });
748
780
  };
749
781
  messageListener = (event) => {
750
- if (!isLive() || ![
751
- "https://www.facebook.com",
752
- "https://web.facebook.com"
753
- ].includes(event.origin)) return;
782
+ if (!isLive() || !isFacebookOrigin(event.origin)) return;
754
783
  let data = event.data;
755
784
  if (typeof data === "string") {
756
785
  try {
@@ -764,7 +793,7 @@ var OmnizapiClientImpl = class {
764
793
  fail("cancelled");
765
794
  } else if (data.event === "ERROR") {
766
795
  finish(new OmnizapiError("PROVIDER_ERROR", "Meta could not complete the connection."));
767
- } else if (data.event === "FINISH" || data.event === "FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING") {
796
+ } else if (data.event === "FINISH" || data.event === "FINISH_ONLY_WABA" || data.event === "FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING") {
768
797
  if (!isRecord(data.data) || !stringOrNull(data.data.waba_id) || data.data.phone_number_id != null && typeof data.data.phone_number_id !== "string") return;
769
798
  wabaId = stringOrNull(data.data.waba_id);
770
799
  phoneId = stringOrNull(data.data.phone_number_id);
@@ -839,13 +868,37 @@ var OmnizapiClientImpl = class {
839
868
  url = new URL(`${this.constants.front}/connect/${channelType === "BOT_TELEGRAM" ? "telegram" : "whatsapp-web"}`);
840
869
  url.searchParams.set("channelId", channelId);
841
870
  url.searchParams.set("pk", this.publicKey);
871
+ if (channelType === "Z_API_WHATSAPP") url.searchParams.set("qr", "message");
842
872
  }
843
873
  if (channelType !== "META_MESSENGER" && channelType !== "META_INSTAGRAM") {
844
874
  url.searchParams.set("attemptId", attemptId);
845
875
  }
876
+ const answerQrCodeRequest = (data, target) => {
877
+ const fetchQrCode = getQRCode;
878
+ if (channelType !== "Z_API_WHATSAPP" || !fetchQrCode || data.attemptId !== attemptId || data.channelId !== channelId || typeof data.requestId !== "string" || !data.requestId || data.requestId.length > QR_REQUEST_ID_MAX_LENGTH) return;
879
+ const requestId = data.requestId;
880
+ const reply = (body) => {
881
+ if (!isLive() || popup !== target) return;
882
+ try {
883
+ target.postMessage({ type: "qr-code-response", attemptId, channelId, requestId, ...body }, frontOrigin);
884
+ } catch (e) {
885
+ }
886
+ };
887
+ const call = qrCodeCall != null ? qrCodeCall : qrCodeCall = Promise.resolve().then(() => fetchQrCode());
888
+ void call.then(
889
+ (result) => reply(qrCodeAnswer(result)),
890
+ (reason) => reply({ error: qrCodeError(reason) })
891
+ ).finally(() => {
892
+ if (qrCodeCall === call) qrCodeCall = null;
893
+ });
894
+ };
846
895
  messageListener = (event) => {
847
896
  if (!isLive() || !popup || event.origin !== frontOrigin || event.source !== popup) return;
848
897
  const data = event.data;
898
+ if (isRecord(data) && data.type === "qr-code-request") {
899
+ answerQrCodeRequest(data, popup);
900
+ return;
901
+ }
849
902
  if (!isRecord(data) || data.type !== "after-connect" || data.attemptId !== attemptId || data.channelId !== channelId || typeof data.success !== "boolean" || data.reason !== void 0 && !isFailureReason(data.reason) || data.coexistence !== void 0 && typeof data.coexistence !== "boolean" || ["code", "wabaId", "phoneId", "botToken", "pageId", "accessToken", "redirectUri"].some(
850
903
  (key) => data[key] != null && typeof data[key] !== "string"
851
904
  )) return;
@@ -358,6 +358,15 @@ function encodeChannelId(channelId) {
358
358
  throw new OmnizapiError("INVALID_OPTIONS", "The channelId cannot be encoded as a URL path.");
359
359
  }
360
360
  }
361
+ function isFacebookOrigin(origin) {
362
+ let url;
363
+ try {
364
+ url = new URL(origin);
365
+ } catch (e) {
366
+ return false;
367
+ }
368
+ return url.protocol === "https:" && (url.hostname === "facebook.com" || url.hostname.endsWith(".facebook.com"));
369
+ }
361
370
  function isChannelType(value) {
362
371
  return [
363
372
  "META_MESSENGER",
@@ -377,6 +386,25 @@ function isFailureReason(value) {
377
386
  "unsupported_channel"
378
387
  ].includes(value);
379
388
  }
389
+ var QR_REQUEST_ID_MAX_LENGTH = 64;
390
+ function qrCodeAnswer(result) {
391
+ if (!isRecord(result) || result.value != null && typeof result.value !== "string" || result.connected !== void 0 && typeof result.connected !== "boolean") {
392
+ return { error: { status: null, message: null } };
393
+ }
394
+ return { connected: result.connected === true, value: typeof result.value === "string" ? result.value : null };
395
+ }
396
+ var QR_ERROR_MESSAGE_LIMIT = 300;
397
+ function qrCodeError(reason) {
398
+ const source = isRecord(reason) ? reason : {};
399
+ const response = isRecord(source.response) ? source.response : void 0;
400
+ const data = response && isRecord(response.data) ? response.data : {};
401
+ const status = [source.status, response == null ? void 0 : response.status].find(
402
+ (value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599
403
+ );
404
+ const candidates = response ? [data.message, data.error, data.detail] : [source.message];
405
+ const message = candidates.find((value) => typeof value === "string" && value.trim() !== "");
406
+ return { status: status != null ? status : null, message: message ? message.trim().slice(0, QR_ERROR_MESSAGE_LIMIT) : null };
407
+ }
380
408
  function failure(channelId, type, reason) {
381
409
  return { success: false, reason, channelId, type, code: null, wabaId: null, phoneId: null };
382
410
  }
@@ -521,6 +549,7 @@ var OmnizapiClientImpl = class {
521
549
  let messageListener;
522
550
  let nativeLoginStarted = false;
523
551
  let callbackReceived = false;
552
+ let qrCodeCall = null;
524
553
  const releaseNativeLogin = () => {
525
554
  if (runtime.nativeLoginOwner === owner) {
526
555
  runtime.nativeLoginPending = false;
@@ -680,6 +709,9 @@ var OmnizapiClientImpl = class {
680
709
  fail("unsupported_channel");
681
710
  return;
682
711
  }
712
+ if (channelType === "Z_API_WHATSAPP" && !getQRCode) {
713
+ throw new OmnizapiError("INVALID_OPTIONS", "getQRCode is required to connect a Z_API_WHATSAPP channel.");
714
+ }
683
715
  const appId = (_b = stringOrNull(channelType === "META_INSTAGRAM" ? info.appInstagramId : info.appId)) != null ? _b : "";
684
716
  const configId = (_c = stringOrNull(info.appConfig)) != null ? _c : "";
685
717
  if (channelType.startsWith("META_") && !appId || channelType === "META_WHATSAPP" && !configId) {
@@ -718,10 +750,7 @@ var OmnizapiClientImpl = class {
718
750
  succeed({ code, wabaId, phoneId, coexistence });
719
751
  };
720
752
  messageListener = (event) => {
721
- if (!isLive() || ![
722
- "https://www.facebook.com",
723
- "https://web.facebook.com"
724
- ].includes(event.origin)) return;
753
+ if (!isLive() || !isFacebookOrigin(event.origin)) return;
725
754
  let data = event.data;
726
755
  if (typeof data === "string") {
727
756
  try {
@@ -735,7 +764,7 @@ var OmnizapiClientImpl = class {
735
764
  fail("cancelled");
736
765
  } else if (data.event === "ERROR") {
737
766
  finish(new OmnizapiError("PROVIDER_ERROR", "Meta could not complete the connection."));
738
- } else if (data.event === "FINISH" || data.event === "FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING") {
767
+ } else if (data.event === "FINISH" || data.event === "FINISH_ONLY_WABA" || data.event === "FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING") {
739
768
  if (!isRecord(data.data) || !stringOrNull(data.data.waba_id) || data.data.phone_number_id != null && typeof data.data.phone_number_id !== "string") return;
740
769
  wabaId = stringOrNull(data.data.waba_id);
741
770
  phoneId = stringOrNull(data.data.phone_number_id);
@@ -810,13 +839,37 @@ var OmnizapiClientImpl = class {
810
839
  url = new URL(`${this.constants.front}/connect/${channelType === "BOT_TELEGRAM" ? "telegram" : "whatsapp-web"}`);
811
840
  url.searchParams.set("channelId", channelId);
812
841
  url.searchParams.set("pk", this.publicKey);
842
+ if (channelType === "Z_API_WHATSAPP") url.searchParams.set("qr", "message");
813
843
  }
814
844
  if (channelType !== "META_MESSENGER" && channelType !== "META_INSTAGRAM") {
815
845
  url.searchParams.set("attemptId", attemptId);
816
846
  }
847
+ const answerQrCodeRequest = (data, target) => {
848
+ const fetchQrCode = getQRCode;
849
+ if (channelType !== "Z_API_WHATSAPP" || !fetchQrCode || data.attemptId !== attemptId || data.channelId !== channelId || typeof data.requestId !== "string" || !data.requestId || data.requestId.length > QR_REQUEST_ID_MAX_LENGTH) return;
850
+ const requestId = data.requestId;
851
+ const reply = (body) => {
852
+ if (!isLive() || popup !== target) return;
853
+ try {
854
+ target.postMessage({ type: "qr-code-response", attemptId, channelId, requestId, ...body }, frontOrigin);
855
+ } catch (e) {
856
+ }
857
+ };
858
+ const call = qrCodeCall != null ? qrCodeCall : qrCodeCall = Promise.resolve().then(() => fetchQrCode());
859
+ void call.then(
860
+ (result) => reply(qrCodeAnswer(result)),
861
+ (reason) => reply({ error: qrCodeError(reason) })
862
+ ).finally(() => {
863
+ if (qrCodeCall === call) qrCodeCall = null;
864
+ });
865
+ };
817
866
  messageListener = (event) => {
818
867
  if (!isLive() || !popup || event.origin !== frontOrigin || event.source !== popup) return;
819
868
  const data = event.data;
869
+ if (isRecord(data) && data.type === "qr-code-request") {
870
+ answerQrCodeRequest(data, popup);
871
+ return;
872
+ }
820
873
  if (!isRecord(data) || data.type !== "after-connect" || data.attemptId !== attemptId || data.channelId !== channelId || typeof data.success !== "boolean" || data.reason !== void 0 && !isFailureReason(data.reason) || data.coexistence !== void 0 && typeof data.coexistence !== "boolean" || ["code", "wabaId", "phoneId", "botToken", "pageId", "accessToken", "redirectUri"].some(
821
874
  (key) => data[key] != null && typeof data[key] !== "string"
822
875
  )) return;
@@ -1 +1 @@
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,b,D,M,z=(u,l,y=!1)=>{if(!p){p=!0,clearTimeout(F),i==null||i.removeEventListener("load",P),i==null||i.removeEventListener("error",V);try{r.fbAsyncInit===K&&(D?Object.defineProperty(r,"fbAsyncInit",D):Reflect.deleteProperty(r,"fbAsyncInit"))}catch(L){}if(l&&i&&(n.failedFacebookScripts.add(i),y?n.erroredFacebookScripts.add(i):_||(n.facebookLoadUncertain=!0),_))try{i.remove()}catch(L){}n.facebookLoadObserver===s&&delete n.facebookLoadObserver,u?c(u):f(l)}},P=()=>{var y;if(p)return;let u=r.FB;if(!u)return;"__buffer"in u&&(b=u),(((y=b==null?void 0:b.__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),b&&n.facebookAppIds.get(b)===null&&n.facebookAppIds.delete(b));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(L){}}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){}P()}},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{D=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 L=new URL(y.src);return L.origin!=="https://connect.facebook.net"||!/^\/[^/]+\/sdk\.js$/.test(L.pathname)?!1:n.failedFacebookScripts.has(y)?(n.erroredFacebookScripts.has(y)||(n.facebookLoadUncertain=!0),!1):!0}catch(L){return!1}}),!i){i=u.createElement("script"),_=!0;let y=r.FB;y&&"__buffer"in y&&(b=y),g=!n.facebookLoadUncertain&&!n.facebookLoading&&!M&&(!y||!!b&&n.facebookAppIds.get(y)===null&&((h=b.__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",D&&!D.configurable?{value:K}:{configurable:!0,enumerable:!0,writable:!0,value:K}),i.addEventListener("load",P),i.addEventListener("error",V),P(),!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 b;f||(f=!0,t.removeEventListener("abort",i),a.consumers--,_?s(_):c(g),(b=a.cancelIfUnused)==null||b.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,b)=>{c||(c=!0,clearTimeout(i),n==null||n.removeEventListener("abort",p),s.abort(),b?a(b):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(b){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 b=await g.json();c||f(b)}catch(b){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 b=e.location.href,D=new URL(this.constants.front).origin;return await new Promise((M,z)=>{let P=Symbol("omnizapi-connection"),V=new AbortController,K=Date.now()+p,F=!1,h=null,u=null,l=null,y,L,x,W,q=!1,Q=!1,J=()=>{a.nativeLoginOwner===P&&(a.nativeLoginPending=!1,delete a.nativeLoginOwner)},te=[],E=d=>{if(F)return;F=!0,clearTimeout(L),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:I,value:C}of te)try{let k=Object.getOwnPropertyDescriptor(e,U);if(!k||!("value"in k)||k.value!==C)continue;I?Object.defineProperty(e,U,I):Reflect.deleteProperty(e,U)}catch(k){}a.activeConnection===P&&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),N=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&&N("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=P,i==null||i.addEventListener("abort",X,{once:!0}),!T())return;let d=e.crypto.getRandomValues(new Uint8Array(16)),G=Array.from(d,I=>I.toString(16).padStart(2,"0")).join("");for(let[I,C]of[["__omnizapiPublicKey",this.publicKey],["__omnizapiCurrentChannel",s],["__omnizapiGetQrCode",f]]){let k=Object.getOwnPropertyDescriptor(e,I);Object.defineProperty(e,I,k&&!k.configurable?{value:C}:{configurable:!0,enumerable:!0,writable:!0,value:C}),te.push({name:I,previous:k,value:C})}if(L=setTimeout(()=>E(new o("TIMEOUT","The connection timed out.")),p),!c)try{oe()}catch(I){y=I instanceof o?I:new o("POPUP_BLOCKED","The connection popup could not be opened.")}if(!T())return;(async()=>{var ae,se,ce;let I=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(I)||!O(I.middleware))throw new o("INVALID_RESPONSE","The channel configuration is invalid.");let C=(ae=I.allowOrigins)!=null?ae:[];if(!Array.isArray(C)||!C.every(w=>typeof w=="string"))throw new o("INVALID_RESPONSE","The channel origins are invalid.");let k=I.middleware;if(h=Pe(k)?k:null,g!==D&&!C.some(w=>{try{return g===new URL(w).origin}catch(m){return!1}})){N("origin_not_allowed");return}if(!h){N("unsupported_channel");return}let H=(se=O(h==="META_INSTAGRAM"?I.appInstagramId:I.appId))!=null?se:"",Z=(ce=O(I.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===!0,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")N("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=P,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){N("not_completed");return}if(!v(S.authResponse)||!O(S.authResponse.code)){N("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:b,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!==D||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)?N("not_completed"):re(m):N(ge(m.reason)?m.reason:"not_completed"))},e.addEventListener("message",W),!!T()){if(y)throw y;if(l||oe(),l!=null&&l.closed){N("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(I=>{T()&&E(I instanceof o?I: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});})();
1
+ "use strict";(()=>{var r=class extends Error{constructor(e,n){super(n);this.code=e;this.name="OmnizapiError"}};var fe={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:""}},pe=({environment:o="PRODUCTION",apiUrl:t,frontUrl:e}={})=>{if(!Object.prototype.hasOwnProperty.call(fe,o))throw new r("INVALID_CONFIGURATION","Unknown environment.");let n=fe[o],i={api:t!=null?t:n.api,front:e!=null?e:n.front};for(let s of["api","front"]){let l;try{l=new URL(i[s])}catch(c){throw new r("INVALID_CONFIGURATION",`A valid ${s}Url is required for this environment.`)}if(!["http:","https:"].includes(l.protocol)||l.username||l.password||l.href.includes("?")||l.href.includes("#"))throw new r("INVALID_CONFIGURATION",`${s}Url must be an HTTP(S) base URL without credentials, query or fragment.`);i[s]=l.href.replace(/\/+$/,"")}return i};var he=Symbol.for("omnizapi.connect.runtime.v1");function j(o){var n,i,s;let t=o,e=t[he];return e||(e={nativeLoginPending:!1,failedFacebookScripts:new WeakSet,erroredFacebookScripts:new WeakSet,facebookAppIds:new WeakMap,facebookInitObservers:new WeakMap},Object.defineProperty(t,he,{configurable:!0,value:e})),(n=e.facebookAppIds)!=null||(e.facebookAppIds=new WeakMap),(i=e.facebookInitObservers)!=null||(e.facebookInitObservers=new WeakMap),(s=e.erroredFacebookScripts)!=null||(e.erroredFacebookScripts=new WeakSet),e}function Ie(o){return!!o&&!("__buffer"in o)&&typeof o.init=="function"&&typeof o.login=="function"}function be(o,t){var e;try{let n=Object.getOwnPropertyDescriptor(t,"init"),i=o.facebookInitObservers.get(t);if(i){if((n==null?void 0:n.value)!==i.init)throw new Error("FB.init was replaced.");return i}if(!n||typeof n.value!="function"||!n.writable&&!n.configurable)throw new Error("FB.init cannot be observed.");let s=n.value,l={calls:0,init:function(...c){return l.calls++,o.facebookAppIds.delete(t),Reflect.apply(s,this,c)}};if(Object.defineProperty(t,"init",{...n,value:l.init}),((e=Object.getOwnPropertyDescriptor(t,"init"))==null?void 0:e.value)!==l.init)throw new Error("FB.init was not replaced.");return o.facebookInitObservers.set(t,l),l}catch(n){throw o.facebookAppIds.delete(t),new r("INVALID_CONFIGURATION","The Meta SDK initialization cannot be observed safely. Use the popup flow or reload a fresh page.")}}function me(o,t,e){var c;if(o.FB!==t||!t||"__buffer"in t||typeof t.login!="function")throw new r("INVALID_CONFIGURATION","The Meta SDK is not ready or was replaced before initialization.");if(typeof e!="string"||!/^[0-9]+$/.test(e))throw new r("INVALID_CONFIGURATION","A numeric Meta appId is required for native login.");let n=j(o);n.facebookAppIds.get(t)===null&&(n.facebookLoadUncertain||n.facebookLoading||typeof o.fbAsyncInit=="function")&&n.facebookAppIds.delete(t);let i=n.facebookAppIds.get(t);if(i===void 0||i!==null&&!n.facebookInitObservers.has(t))throw n.facebookAppIds.delete(t),new r("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(n,t);if(i!==null){if(i!==e)throw new r("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 l=s.calls;n.facebookAppIds.delete(t);try{t.init({appId:e,version:"v25.0",autoLogAppEvents:!0,xfbml:!0})}catch(b){throw new r("PROVIDER_ERROR","The Meta SDK could not be initialized.")}if(s.calls!==l+1||o.FB!==t||((c=Object.getOwnPropertyDescriptor(t,"init"))==null?void 0:c.value)!==s.init)throw new r("INVALID_CONFIGURATION","The Meta SDK changed during initialization. Use the popup flow or reload a fresh page.");n.facebookAppIds.set(t,e)}function we(o,t){if(t.aborted)return Promise.reject(new r("SDK_LOAD_FAILED","The Meta SDK load was cancelled."));let e=j(o),n=e.facebookLoadObserver;if(!n&&Ie(o.FB)&&(n={promise:Promise.resolve(o.FB),consumers:0}),!n){let s={consumers:0,promise:Promise.resolve().then(()=>new Promise((l,c)=>{var p;let b=!1,a,R=!1,y=!1,w,v,W,G=(I,d,_=!1)=>{if(!b){b=!0,clearTimeout(U),a==null||a.removeEventListener("load",L),a==null||a.removeEventListener("error",V);try{o.fbAsyncInit===K&&(v?Object.defineProperty(o,"fbAsyncInit",v):Reflect.deleteProperty(o,"fbAsyncInit"))}catch(N){}if(d&&a&&(e.failedFacebookScripts.add(a),_?e.erroredFacebookScripts.add(a):R||(e.facebookLoadUncertain=!0),R))try{a.remove()}catch(N){}e.facebookLoadObserver===s&&delete e.facebookLoadObserver,I?l(I):c(d)}},L=()=>{var _;if(b)return;let I=o.FB;if(!I)return;"__buffer"in I&&(w=I),(((_=w==null?void 0:w.__buffer)==null?void 0:_.opts)!=null||o.fbAsyncInit!==K||a!=null&&a.onload||e.facebookLoading||e.facebookLoadUncertain)&&(y=!1,e.facebookAppIds.get(I)===null&&e.facebookAppIds.delete(I),w&&e.facebookAppIds.get(w)===null&&e.facebookAppIds.delete(w));let d=Ie(I);if(y&&e.facebookAppIds.get(I)==null){if(!d)e.facebookAppIds.set(I,null);else if(!e.facebookInitObservers.has(I))try{be(e,I),e.facebookAppIds.set(I,null)}catch(N){}}d&&G(I)},V=()=>G(void 0,new r("SDK_LOAD_FAILED","The Meta SDK could not be loaded."),!0),K=()=>{if(!b){try{W==null||W.call(o)}catch(I){}L()}},U=setTimeout(()=>G(void 0,new r("SDK_LOAD_FAILED","The Meta SDK load timed out.")),30*1e3);s.cancelIfUnused=()=>{R&&s.consumers===0&&G(void 0,new r("SDK_LOAD_FAILED","The Meta SDK load was cancelled."))};try{v=Object.getOwnPropertyDescriptor(o,"fbAsyncInit"),W=typeof o.fbAsyncInit=="function"?o.fbAsyncInit:void 0;let I=o.document,d=I.getElementById("facebook-jssdk");if(a=Array.from(I.scripts).find(_=>{try{let N=new URL(_.src);return N.origin!=="https://connect.facebook.net"||!/^\/[^/]+\/sdk\.js$/.test(N.pathname)?!1:e.failedFacebookScripts.has(_)?(e.erroredFacebookScripts.has(_)||(e.facebookLoadUncertain=!0),!1):!0}catch(N){return!1}}),!a){a=I.createElement("script"),R=!0;let _=o.FB;_&&"__buffer"in _&&(w=_),y=!e.facebookLoadUncertain&&!e.facebookLoading&&!W&&(!_||!!w&&e.facebookAppIds.get(_)===null&&((p=w.__buffer)==null?void 0:p.opts)==null),d||(a.id="facebook-jssdk"),a.src="https://connect.facebook.net/en_US/sdk.js",a.crossOrigin="anonymous",a.async=!0,a.defer=!0}if(s.cancelIfUnused(),b)return;Object.defineProperty(o,"fbAsyncInit",v&&!v.configurable?{value:K}:{configurable:!0,enumerable:!0,writable:!0,value:K}),a.addEventListener("load",L),a.addEventListener("error",V),L(),!b&&R&&I.head.appendChild(a)}catch(I){G(void 0,new r("SDK_LOAD_FAILED","The Meta SDK could not be loaded."))}}))};e.facebookLoadObserver=n=s}let i=n;return new Promise((s,l)=>{let c=!1;i.consumers++;let b=(R,y)=>{var w;c||(c=!0,t.removeEventListener("abort",a),i.consumers--,R?s(R):l(y),(w=i.cancelIfUnused)==null||w.call(i))},a=()=>b(void 0,new r("SDK_LOAD_FAILED","The Meta SDK load was cancelled."));i.promise.then(R=>t.aborted?a():b(R),R=>b(void 0,new r("SDK_LOAD_FAILED",R.message))),t.addEventListener("abort",a,{once:!0}),t.aborted&&a()})}var ge=(o,t,e=600,n=700)=>{let i=window,s=i.document,l=i.screenLeft!==void 0?i.screenLeft:i.screenX,c=i.screenTop!==void 0?i.screenTop:i.screenY,b=i.innerWidth?i.innerWidth:s.documentElement.clientWidth?s.documentElement.clientWidth:i.screen.width,a=i.innerHeight?i.innerHeight:s.documentElement.clientHeight?s.documentElement.clientHeight:i.screen.height,R=b/2-e/2+l,y=a/2-n/2+c;return i.open(o,t,`scrollbars=yes, width=${e}, height=${n}, top=${y}, left=${R}`)};var Le=600*1e3,Ne=30*1e3;function k(o){return typeof o=="object"&&o!==null&&!Array.isArray(o)}function A(o){return typeof o=="string"&&o.trim()?o:null}function Ae(o){if(o==="."||o==="..")throw new r("INVALID_OPTIONS","The channelId cannot be a URL path navigation segment.");try{return encodeURIComponent(o)}catch(t){throw new r("INVALID_OPTIONS","The channelId cannot be encoded as a URL path.")}}function Ce(o){let t;try{t=new URL(o)}catch(e){return!1}return t.protocol==="https:"&&(t.hostname==="facebook.com"||t.hostname.endsWith(".facebook.com"))}function De(o){return["META_MESSENGER","META_INSTAGRAM","META_WHATSAPP","BOT_TELEGRAM","Z_API_WHATSAPP"].includes(o)}function Oe(o){return typeof o=="string"&&["cancelled","popup_closed","not_completed","invalid_channel","origin_not_allowed","unsupported_channel"].includes(o)}var Fe=64;function Ue(o){return!k(o)||o.value!=null&&typeof o.value!="string"||o.connected!==void 0&&typeof o.connected!="boolean"?{error:{status:null,message:null}}:{connected:o.connected===!0,value:typeof o.value=="string"?o.value:null}}var Me=300;function ze(o){let t=k(o)?o:{},e=k(t.response)?t.response:void 0,n=e&&k(e.data)?e.data:{},i=[t.status,e==null?void 0:e.status].find(c=>typeof c=="number"&&Number.isInteger(c)&&c>=100&&c<=599),l=(e?[n.message,n.error,n.detail]:[t.message]).find(c=>typeof c=="string"&&c.trim()!=="");return{status:i!=null?i:null,message:l?l.trim().slice(0,Me):null}}function te(o,t,e){return{success:!1,reason:e,channelId:o,type:t,code:null,wabaId:null,phoneId:null}}function ye(o,t,e){return typeof fetch!="function"||typeof AbortController!="function"?Promise.reject(new r("INVALID_CONFIGURATION","Fetch and AbortController are required.")):new Promise((n,i)=>{let s=new AbortController,l=!1,c=(y,w)=>{l||(l=!0,clearTimeout(a),e==null||e.removeEventListener("abort",b),s.abort(),w?i(w):n(y))},b=()=>c(void 0,new r("ABORTED","The API request was aborted.")),a=setTimeout(()=>c(void 0,new r("TIMEOUT","The API request timed out.")),Ne);if(e==null||e.addEventListener("abort",b,{once:!0}),e!=null&&e.aborted){b();return}(async()=>{let y;try{y=await fetch(o.href,{...t,signal:s.signal})}catch(w){c(void 0,new r("NETWORK_ERROR","The API request failed."));return}if(!l){if(!y||typeof y.ok!="boolean"){c(void 0,new r("INVALID_RESPONSE","The API response is invalid."));return}if(!y.ok){c(void 0,new r("HTTP_ERROR","The API returned an unsuccessful status."));return}try{let w=await y.json();l||c(w)}catch(w){c(void 0,new r("INVALID_RESPONSE","The API did not return valid JSON."))}}})().catch(()=>c(void 0,new r("NETWORK_ERROR","The API request failed.")))})}var oe=class{constructor(t){if(!k(t)||!A(t.publicKey)||Array.from(t.publicKey).some(e=>{let n=e.charCodeAt(0);return n<32||n===127})||t.logo!==void 0&&typeof t.logo!="string"||t.apiUrl!==void 0&&!A(t.apiUrl)||t.frontUrl!==void 0&&!A(t.frontUrl))throw new r("INVALID_CONFIGURATION","Valid client properties and a publicKey are required.");this.publicKey=t.publicKey,this.logo=t.logo,this.constants=pe({environment:t.environment,apiUrl:t.apiUrl,frontUrl:t.frontUrl})}async getListMessengerPages(t,e,n){if(!A(t)||!A(e))throw new r("INVALID_OPTIONS","A channelId and authorization code are required.");if(n!==void 0&&(!k(n)||typeof n.aborted!="boolean"||typeof n.addEventListener!="function"||typeof n.removeEventListener!="function"))throw new r("INVALID_OPTIONS","The abort signal is invalid.");if(n!=null&&n.aborted)throw new r("ABORTED","The API request was aborted.");return ye(new URL(`${this.constants.api}/v1/channels/${Ae(t)}/list-messenger-pages`),{method:"POST",body:JSON.stringify({redirectUri:new URL(`${this.constants.front}/oauth/proxy-messenger`).href,code:e}),headers:{"Content-Type":"application/json",Authorization:this.publicKey}},n)}async connect(t){var e;try{if(typeof window=="undefined"||!window.document||!window.location)throw new r("BROWSER_REQUIRED","Connecting requires a browser window.");let n=window,i=j(n);if(i.activeConnection)throw new r("CONNECTION_IN_PROGRESS","A connection is already in progress in this window.");if(!k(t))throw new r("INVALID_OPTIONS","Connection options are required.");let{channelId:s,direct:l,getQRCode:c,timeoutMs:b=Le,signal:a}=t;if(typeof s!="string"||l!==void 0&&typeof l!="boolean"||c!==void 0&&typeof c!="function"||typeof b!="number"||!Number.isFinite(b)||b<=0||b>2147483647||a!==void 0&&(!k(a)||typeof a.aborted!="boolean"||typeof a.addEventListener!="function"||typeof a.removeEventListener!="function"))throw new r("INVALID_OPTIONS","Invalid channelId, direct, getQRCode, timeoutMs or signal option.");if(a!=null&&a.aborted)throw new r("ABORTED","The connection was aborted.");if(!s.trim())return te(s,null,"invalid_channel");let R=Ae(s);if(typeof n.open!="function"||typeof n.addEventListener!="function"||typeof n.removeEventListener!="function"||typeof n.location.origin!="string"||typeof n.location.href!="string"||typeof((e=n.crypto)==null?void 0:e.getRandomValues)!="function"||typeof AbortController!="function"||typeof fetch!="function")throw new r("INVALID_CONFIGURATION","Required browser APIs are unavailable.");let y=n.location.origin;if(y==="null")return te(s,null,"origin_not_allowed");let w=n.location.href,v=new URL(this.constants.front).origin;return await new Promise((W,G)=>{let L=Symbol("omnizapi-connection"),V=new AbortController,K=Date.now()+b,U=!1,p=null,I=null,d=null,_,N,H,x,J=!1,X=!1,B=null,Y=()=>{i.nativeLoginOwner===L&&(i.nativeLoginPending=!1,delete i.nativeLoginOwner)},ie=[],P=h=>{if(U)return;U=!0,clearTimeout(N),clearInterval(H);try{a==null||a.removeEventListener("abort",ee)}catch(M){}if(x)try{n.removeEventListener("message",x)}catch(M){}try{V.abort()}catch(M){}let D=d;d=null;try{D==null||D.close()}catch(M){}for(let{name:M,previous:g,value:F}of ie)try{let S=Object.getOwnPropertyDescriptor(n,M);if(!S||!("value"in S)||S.value!==F)continue;g?Object.defineProperty(n,M,g):Reflect.deleteProperty(n,M)}catch(S){}i.activeConnection===L&&delete i.activeConnection,J&&(X||!(h instanceof r)&&h.success)&&Y(),h instanceof r?G(h):W(h)},ee=()=>P(new r("ABORTED","The connection was aborted.")),T=()=>(!U&&(a!=null&&a.aborted)&&ee(),!U&&Date.now()>=K&&P(new r("TIMEOUT","The connection timed out.")),!U),C=h=>P(te(s,p,h)),ae=()=>{try{let h=ge("about:blank","_blank",600,740);if(!h||h===n)throw new Error("Popup unavailable");if(!T()){h.close();return}d=h}catch(h){throw new r("POPUP_BLOCKED","The connection popup could not be opened.")}},Te=()=>{H||(H=setInterval(()=>{if(T())try{d!=null&&d.closed&&C("popup_closed")}catch(h){P(new r("PROVIDER_ERROR","The connection popup could not be monitored."))}},500))},ke=()=>{clearInterval(H),H=void 0},se=h=>{!T()||!p||P({success:!0,channelId:s,type:p,code:A(h.code),wabaId:A(h.wabaId),phoneId:A(h.phoneId),coexistence:h.coexistence===!0,redirectUri:I,botToken:A(h.botToken),pageId:A(h.pageId),accessToken:A(h.accessToken)})};try{if(i.activeConnection=L,a==null||a.addEventListener("abort",ee,{once:!0}),!T())return;let h=n.crypto.getRandomValues(new Uint8Array(16)),D=Array.from(h,g=>g.toString(16).padStart(2,"0")).join("");for(let[g,F]of[["__omnizapiPublicKey",this.publicKey],["__omnizapiCurrentChannel",s],["__omnizapiGetQrCode",c]]){let S=Object.getOwnPropertyDescriptor(n,g);Object.defineProperty(n,g,S&&!S.configurable?{value:F}:{configurable:!0,enumerable:!0,writable:!0,value:F}),ie.push({name:g,previous:S,value:F})}if(N=setTimeout(()=>P(new r("TIMEOUT","The connection timed out.")),b),!l)try{ae()}catch(g){_=g instanceof r?g:new r("POPUP_BLOCKED","The connection popup could not be opened.")}if(!T())return;(async()=>{var le,de,ue;let g=await ye(new URL(`${this.constants.api}/instances/${R}/sdk-info`),{headers:{Authorization:this.publicKey,"Content-Type":"application/json"}},V.signal);if(!T())return;if(!k(g)||!A(g.middleware))throw new r("INVALID_RESPONSE","The channel configuration is invalid.");let F=(le=g.allowOrigins)!=null?le:[];if(!Array.isArray(F)||!F.every(u=>typeof u=="string"))throw new r("INVALID_RESPONSE","The channel origins are invalid.");let S=g.middleware;if(p=De(S)?S:null,y!==v&&!F.some(u=>{try{return y===new URL(u).origin}catch(f){return!1}})){C("origin_not_allowed");return}if(!p){C("unsupported_channel");return}if(p==="Z_API_WHATSAPP"&&!c)throw new r("INVALID_OPTIONS","getQRCode is required to connect a Z_API_WHATSAPP channel.");let $=(de=A(p==="META_INSTAGRAM"?g.appInstagramId:g.appId))!=null?de:"",ne=(ue=A(g.appConfig))!=null?ue:"";if(p.startsWith("META_")&&!$||p==="META_WHATSAPP"&&!ne)throw new r("INVALID_RESPONSE","The channel provider configuration is invalid.");let Ee=p==="META_WHATSAPP"&&l===!0,ce=!1,Q;if(Ee){if(ke(),i.nativeLoginPending)throw new r("CONNECTION_IN_PROGRESS","A previous Meta login is incomplete. Reload this window before retrying a native login.");if(Q=await we(n,V.signal),!T())return;try{me(n,Q,$),ce=!0}catch(u){if(!(u instanceof r&&u.code==="INVALID_CONFIGURATION"))throw u}if(!T())return}if(ce&&Q){d==null||d.close(),d=null;let u=null,f=null,z=null,Z=!1,q=()=>{!T()||!u||!f||se({code:u,wabaId:f,phoneId:z,coexistence:Z})};x=E=>{if(!T()||!Ce(E.origin))return;let m=E.data;if(typeof m=="string")try{m=JSON.parse(m)}catch(Pe){return}if(!(!k(m)||m.type!=="WA_EMBEDDED_SIGNUP")){if(m.event==="CANCEL")C("cancelled");else if(m.event==="ERROR")P(new r("PROVIDER_ERROR","Meta could not complete the connection."));else if(m.event==="FINISH"||m.event==="FINISH_ONLY_WABA"||m.event==="FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING"){if(!k(m.data)||!A(m.data.waba_id)||m.data.phone_number_id!=null&&typeof m.data.phone_number_id!="string")return;f=A(m.data.waba_id),z=A(m.data.phone_number_id),Z=m.event==="FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING",q()}}},n.addEventListener("message",x),i.nativeLoginPending=!0,i.nativeLoginOwner=L,J=!0;try{Q.login(E=>{if(!X){if(X=!0,U){Y();return}if(T()){if(!k(E)||E.error){P(new r("PROVIDER_ERROR","Meta returned an invalid login response."));return}if(E.authResponse==null){C("not_completed");return}if(!k(E.authResponse)||!A(E.authResponse.code)){C("not_completed");return}u=A(E.authResponse.code),q()}}},{config_id:ne,response_type:"code",override_default_response_type:!0,extras:{setup:{},featureType:"whatsapp_business_app_onboarding",sessionInfoVersion:"3"}})}catch(E){throw Y(),J=!1,new r("PROVIDER_ERROR","The Meta login could not be started.")}return}let O;if(p==="META_MESSENGER"||p==="META_INSTAGRAM"){let u=p==="META_MESSENGER";I=new URL(`${this.constants.front}/oauth/proxy-${u?"messenger":"instagram"}`).href,O=new URL(u?"https://www.facebook.com/dialog/oauth":"https://www.instagram.com/oauth/authorize"),O.searchParams.set("client_id",$),u&&O.searchParams.set("display","popup"),O.searchParams.set("redirect_uri",I),O.searchParams.set("response_type","code"),O.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"),O.searchParams.set("state",JSON.stringify({currentUrl:w,attemptId:D,channelId:s}))}else p==="META_WHATSAPP"?(O=new URL(`${this.constants.front}/oauth/proxy-cloud-api`),O.searchParams.set("configId",ne),O.searchParams.set("appId",$),O.searchParams.set("channelId",s),O.searchParams.set("channelType",p),O.searchParams.set("logo",this.logo||new URL(`${this.constants.front}/logos/SVG/Ativo%2016.svg`).href),O.searchParams.set("pk",this.publicKey)):(O=new URL(`${this.constants.front}/connect/${p==="BOT_TELEGRAM"?"telegram":"whatsapp-web"}`),O.searchParams.set("channelId",s),O.searchParams.set("pk",this.publicKey),p==="Z_API_WHATSAPP"&&O.searchParams.set("qr","message"));p!=="META_MESSENGER"&&p!=="META_INSTAGRAM"&&O.searchParams.set("attemptId",D);let Se=(u,f)=>{let z=c;if(p!=="Z_API_WHATSAPP"||!z||u.attemptId!==D||u.channelId!==s||typeof u.requestId!="string"||!u.requestId||u.requestId.length>Fe)return;let Z=u.requestId,q=m=>{if(!(!T()||d!==f))try{f.postMessage({type:"qr-code-response",attemptId:D,channelId:s,requestId:Z,...m},v)}catch(Pe){}},E=B!=null?B:B=Promise.resolve().then(()=>z());E.then(m=>q(Ue(m)),m=>q({error:ze(m)})).finally(()=>{B===E&&(B=null)})};if(x=u=>{if(!T()||!d||u.origin!==v||u.source!==d)return;let f=u.data;if(k(f)&&f.type==="qr-code-request"){Se(f,d);return}!k(f)||f.type!=="after-connect"||f.attemptId!==D||f.channelId!==s||typeof f.success!="boolean"||f.reason!==void 0&&!Oe(f.reason)||f.coexistence!==void 0&&typeof f.coexistence!="boolean"||["code","wabaId","phoneId","botToken","pageId","accessToken","redirectUri"].some(z=>f[z]!=null&&typeof f[z]!="string")||(f.success?p!=null&&p.startsWith("META_")&&!A(f.code)||p==="BOT_TELEGRAM"&&!A(f.botToken)?C("not_completed"):se(f):C(Oe(f.reason)?f.reason:"not_completed"))},n.addEventListener("message",x),!!T()){if(_)throw _;if(d||ae(),d!=null&&d.closed){C("popup_closed");return}Te();try{d==null||d.location.replace(O.href)}catch(u){throw new r("PROVIDER_ERROR","The connection popup could not be navigated.")}}})().catch(g=>{T()&&P(g instanceof r?g:new r("PROVIDER_ERROR","The connection could not be completed."))})}catch(h){P(h instanceof r?h:new r("INVALID_CONFIGURATION","The connection could not be initialized."))}})}catch(n){throw n instanceof r?n:new r("INVALID_OPTIONS","The connection could not be started with these options.")}}},_e=oe;var re=class{static newClient(t){return new _e(t)}};var Re=re;Object.assign(window,{Omnizapi:Re});})();
@@ -1,6 +1,12 @@
1
1
  export interface ConnectionOptions {
2
2
  channelId: string;
3
3
  direct?: boolean;
4
+ /**
5
+ * Required for Z_API_WHATSAPP: returns the channel QR code, fetched by your backend with the
6
+ * Secret Key. Without it, connect rejects with INVALID_OPTIONS and closes the window reserved
7
+ * on click before it navigates.
8
+ * Reject with `{ status, message }` to show a refusal; omit `value` while the QR is not ready.
9
+ */
4
10
  getQRCode?: () => Promise<{
5
11
  connected?: boolean;
6
12
  value?: string;
@@ -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
- <!-- Version pinned to 1.0.3; test your integration and serve this page over HTTP(S). -->
14
- <script src="https://unpkg.com/@omnizapi/connect@1.0.3/dist/omnizapi.min.js"></script>
13
+ <!-- Version pinned to 1.1.0; test your integration and serve this page over HTTP(S). -->
14
+ <script src="https://unpkg.com/@omnizapi/connect@1.1.0/dist/omnizapi.min.js"></script>
15
15
  <script>
16
16
  const button = document.getElementById("connect-btn");
17
17
  const statusElement = document.getElementById("status");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnizapi/connect",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Omnizapi SDK for browser applications",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.mjs",