@omnizapi/connect 1.0.4 → 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 +17 -0
- package/README.md +61 -15
- package/dist/cjs/index.js +47 -0
- package/dist/esm/index.mjs +47 -0
- package/dist/omnizapi.min.js +1 -1
- package/dist/types/src/types/index.d.ts +6 -0
- package/examples/vanilla.html +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
3
20
|
## [1.0.4] - 2026-09-11
|
|
4
21
|
|
|
5
22
|
### Correcao
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
|
|
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
|
|
@@ -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
|
|
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
|
|
48
|
-
| [HTML com CDN](https://unpkg.com/browse/@omnizapi/connect@1.0
|
|
49
|
-
| [React / Next.js](https://unpkg.com/browse/@omnizapi/connect@1.0
|
|
50
|
-
| [Angular](https://unpkg.com/browse/@omnizapi/connect@1.0
|
|
51
|
-
| [Vue](https://unpkg.com/browse/@omnizapi/connect@1.0
|
|
52
|
-
| [Opcoes](https://unpkg.com/browse/@omnizapi/connect@1.0
|
|
53
|
-
| [Erros](https://unpkg.com/browse/@omnizapi/connect@1.0
|
|
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
|
|
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 }>` |
|
|
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
|
|
|
@@ -176,7 +222,7 @@ O Meta Embedded Signup **nativo** nao fornece `attemptId` nem uma referencia cus
|
|
|
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
|
|
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`.
|
|
180
226
|
|
|
181
227
|
## CSP da pagina que roda o login nativo
|
|
182
228
|
|
|
@@ -251,4 +297,4 @@ O hook de versao `scripts/sync-version.mjs` sincroniza os links UNPKG e scripts
|
|
|
251
297
|
|
|
252
298
|
## Licenca
|
|
253
299
|
|
|
254
|
-
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
|
|
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
|
@@ -415,6 +415,25 @@ function isFailureReason(value) {
|
|
|
415
415
|
"unsupported_channel"
|
|
416
416
|
].includes(value);
|
|
417
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
|
+
}
|
|
418
437
|
function failure(channelId, type, reason) {
|
|
419
438
|
return { success: false, reason, channelId, type, code: null, wabaId: null, phoneId: null };
|
|
420
439
|
}
|
|
@@ -559,6 +578,7 @@ var OmnizapiClientImpl = class {
|
|
|
559
578
|
let messageListener;
|
|
560
579
|
let nativeLoginStarted = false;
|
|
561
580
|
let callbackReceived = false;
|
|
581
|
+
let qrCodeCall = null;
|
|
562
582
|
const releaseNativeLogin = () => {
|
|
563
583
|
if (runtime.nativeLoginOwner === owner) {
|
|
564
584
|
runtime.nativeLoginPending = false;
|
|
@@ -718,6 +738,9 @@ var OmnizapiClientImpl = class {
|
|
|
718
738
|
fail("unsupported_channel");
|
|
719
739
|
return;
|
|
720
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
|
+
}
|
|
721
744
|
const appId = (_b = stringOrNull(channelType === "META_INSTAGRAM" ? info.appInstagramId : info.appId)) != null ? _b : "";
|
|
722
745
|
const configId = (_c = stringOrNull(info.appConfig)) != null ? _c : "";
|
|
723
746
|
if (channelType.startsWith("META_") && !appId || channelType === "META_WHATSAPP" && !configId) {
|
|
@@ -845,13 +868,37 @@ var OmnizapiClientImpl = class {
|
|
|
845
868
|
url = new URL(`${this.constants.front}/connect/${channelType === "BOT_TELEGRAM" ? "telegram" : "whatsapp-web"}`);
|
|
846
869
|
url.searchParams.set("channelId", channelId);
|
|
847
870
|
url.searchParams.set("pk", this.publicKey);
|
|
871
|
+
if (channelType === "Z_API_WHATSAPP") url.searchParams.set("qr", "message");
|
|
848
872
|
}
|
|
849
873
|
if (channelType !== "META_MESSENGER" && channelType !== "META_INSTAGRAM") {
|
|
850
874
|
url.searchParams.set("attemptId", attemptId);
|
|
851
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
|
+
};
|
|
852
895
|
messageListener = (event) => {
|
|
853
896
|
if (!isLive() || !popup || event.origin !== frontOrigin || event.source !== popup) return;
|
|
854
897
|
const data = event.data;
|
|
898
|
+
if (isRecord(data) && data.type === "qr-code-request") {
|
|
899
|
+
answerQrCodeRequest(data, popup);
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
855
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(
|
|
856
903
|
(key) => data[key] != null && typeof data[key] !== "string"
|
|
857
904
|
)) return;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -386,6 +386,25 @@ function isFailureReason(value) {
|
|
|
386
386
|
"unsupported_channel"
|
|
387
387
|
].includes(value);
|
|
388
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
|
+
}
|
|
389
408
|
function failure(channelId, type, reason) {
|
|
390
409
|
return { success: false, reason, channelId, type, code: null, wabaId: null, phoneId: null };
|
|
391
410
|
}
|
|
@@ -530,6 +549,7 @@ var OmnizapiClientImpl = class {
|
|
|
530
549
|
let messageListener;
|
|
531
550
|
let nativeLoginStarted = false;
|
|
532
551
|
let callbackReceived = false;
|
|
552
|
+
let qrCodeCall = null;
|
|
533
553
|
const releaseNativeLogin = () => {
|
|
534
554
|
if (runtime.nativeLoginOwner === owner) {
|
|
535
555
|
runtime.nativeLoginPending = false;
|
|
@@ -689,6 +709,9 @@ var OmnizapiClientImpl = class {
|
|
|
689
709
|
fail("unsupported_channel");
|
|
690
710
|
return;
|
|
691
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
|
+
}
|
|
692
715
|
const appId = (_b = stringOrNull(channelType === "META_INSTAGRAM" ? info.appInstagramId : info.appId)) != null ? _b : "";
|
|
693
716
|
const configId = (_c = stringOrNull(info.appConfig)) != null ? _c : "";
|
|
694
717
|
if (channelType.startsWith("META_") && !appId || channelType === "META_WHATSAPP" && !configId) {
|
|
@@ -816,13 +839,37 @@ var OmnizapiClientImpl = class {
|
|
|
816
839
|
url = new URL(`${this.constants.front}/connect/${channelType === "BOT_TELEGRAM" ? "telegram" : "whatsapp-web"}`);
|
|
817
840
|
url.searchParams.set("channelId", channelId);
|
|
818
841
|
url.searchParams.set("pk", this.publicKey);
|
|
842
|
+
if (channelType === "Z_API_WHATSAPP") url.searchParams.set("qr", "message");
|
|
819
843
|
}
|
|
820
844
|
if (channelType !== "META_MESSENGER" && channelType !== "META_INSTAGRAM") {
|
|
821
845
|
url.searchParams.set("attemptId", attemptId);
|
|
822
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
|
+
};
|
|
823
866
|
messageListener = (event) => {
|
|
824
867
|
if (!isLive() || !popup || event.origin !== frontOrigin || event.source !== popup) return;
|
|
825
868
|
const data = event.data;
|
|
869
|
+
if (isRecord(data) && data.type === "qr-code-request") {
|
|
870
|
+
answerQrCodeRequest(data, popup);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
826
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(
|
|
827
874
|
(key) => data[key] != null && typeof data[key] !== "string"
|
|
828
875
|
)) return;
|
package/dist/omnizapi.min.js
CHANGED
|
@@ -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",G);try{r.fbAsyncInit===V&&(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!==V||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)},G=()=>z(void 0,new o("SDK_LOAD_FAILED","The Meta SDK could not be loaded."),!0),V=()=>{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:V}:{configurable:!0,enumerable:!0,writable:!0,value:V}),i.addEventListener("load",P),i.addEventListener("error",G),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){let t;try{t=new URL(r)}catch(n){return!1}return t.protocol==="https:"&&(t.hostname==="facebook.com"||t.hostname.endsWith(".facebook.com"))}function Le(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"),G=new AbortController,V=Date.now()+p,F=!1,h=null,u=null,l=null,y,L,x,K,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(K)try{e.removeEventListener("message",K)}catch(U){}try{G.abort()}catch(U){}let W=l;l=null;try{W==null||W.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()>=V&&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)),W=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"}},G.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=Le(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,G.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})};K=S=>{if(!T()||!Pe(S.origin))return;let R=S.data;if(typeof R=="string")try{R=JSON.parse(R)}catch(Ne){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_ONLY_WABA"||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",K),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:W,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",W),K=w=>{if(!T()||!l||w.origin!==D||w.source!==l)return;let m=w.data;!v(m)||m.type!=="after-connect"||m.attemptId!==W||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",K),!!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;
|
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
|
-
<!-- Version pinned to 1.0
|
|
14
|
-
<script src="https://unpkg.com/@omnizapi/connect@1.0
|
|
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");
|