@bravophone/webphone 0.2.1 → 0.4.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/README.md CHANGED
@@ -1,21 +1,48 @@
1
1
  # @bravophone/webphone
2
2
 
3
- Webphone BRAVOPHONE embutível em qualquer página web. Uma linha de `<script>` e o
4
- softphone aparece como uma janela flutuante arrastável, com as mesmas
5
- funcionalidades da extensão de navegador.
3
+ Webphone BRAVOPHONE embutível em qualquer página web: o softphone aparece como
4
+ uma janela flutuante arrastável, com as mesmas funcionalidades da extensão de
5
+ navegador.
6
6
 
7
7
  ```html
8
- <script src="https://cdn.jsdelivr.net/npm/@bravophone/webphone@0.1"></script>
9
8
  <script>
10
- Bravophone.init({ token: 'TOKEN_DO_USUARIO' })
9
+ // Resolve a versão publicada e carrega o bundle dela.
10
+ // Duas etapas de propósito: a consulta tem 5 min de cache, o bundle é
11
+ // immutable. Assim as correções chegam em minutos, sem revalidar 30 kB a cada
12
+ // visita — e sem os 7 dias de cache que a URL sem versão carrega.
13
+ // cache:'no-store' na CONSULTA (~1 kB): sem ele, a resposta fica até 5 min
14
+ // no navegador e uma publicação recém-saída não aparece — foi o que exigiu
15
+ // Ctrl+Shift+R nos testes. O bundle continua vindo de cache immutable, então
16
+ // o custo é uma requisição pequena por carregamento, não 30 kB.
17
+ fetch('https://data.jsdelivr.com/v1/packages/npm/@bravophone/webphone/resolved',
18
+ { cache: 'no-store' })
19
+ .then((r) => r.json())
20
+ .then(({ version }) => {
21
+ const s = document.createElement('script')
22
+ s.src = `https://cdn.jsdelivr.net/npm/@bravophone/webphone@${version}/dist/bravophone.umd.js`
23
+ s.onload = () => Bravophone.init({ session: SESSAO_DO_LOGIN })
24
+ s.onerror = () => console.error('Bravophone: falha ao carregar do CDN')
25
+ document.head.appendChild(s)
26
+ })
11
27
  </script>
12
28
  ```
13
29
 
30
+ Cole **inline**, não como arquivo externo — um arquivo externo teria o mesmo
31
+ problema de cache que este trecho existe para evitar.
32
+ Detalhes em [Manter o cliente sempre atualizado](#manter-o-cliente-sempre-atualizado).
33
+
34
+ Para travar numa versão (integração de terceiros, ou build com SRI):
35
+
36
+ ```html
37
+ <script src="https://cdn.jsdelivr.net/npm/@bravophone/webphone@0.3.0/dist/bravophone.umd.js"></script>
38
+ <script>Bravophone.init({ token: TOKEN_DO_USUARIO })</script>
39
+ ```
40
+
14
41
  ```js
15
42
  // ou via npm
16
43
  import Bravophone from '@bravophone/webphone'
17
44
 
18
- Bravophone.init({ token, position: 'bottom-right' })
45
+ Bravophone.init({ token })
19
46
  Bravophone.on('call:incoming', ({ number }) => console.log('ligação de', number))
20
47
  await Bravophone.call('11987654321')
21
48
  ```
@@ -55,9 +82,9 @@ todas — é o [`host/shim/chrome-shim.js`](host/shim/chrome-shim.js).
55
82
  │ │ Shadow DOM · janela arrastável │ │
56
83
  │ │ API pública · ponte postMessage │ │
57
84
  │ │ │ │
58
- │ │ ┌─ <iframe> ──────────────────┐ │ │
59
- │ │ │ origem FIXA: │ │ │
60
- │ │ │ webphone.bravophone.com │ │ │
85
+ │ │ ┌─ <iframe srcdoc> ───────────┐ │ │
86
+ │ │ │ origem: a do próprio site │ │ │
87
+ │ │ │ arquivos: CDN (jsDelivr) │ │ │
61
88
  │ │ │ │ │ │
62
89
  │ │ │ chrome-shim.js │ │ │
63
90
  │ │ │ libwebphone.js (604 KB) │ │ │
@@ -78,7 +105,7 @@ Testei mentalmente as duas rotas; o iframe ganha em quatro frentes de uma vez:
78
105
  1. **CSS.** O bundle traz Tailwind + `dark-theme.css` globais. Injetado na página do
79
106
  cliente, ele quebraria o site do cliente — e o CSS do cliente quebraria o webphone.
80
107
  2. **CORS, e este é o argumento decisivo.** Dentro do iframe, todo request para
81
- `api.bravophone.com`, `reports.teambravotech.com` e `devices.wavoip.com` sai com
108
+ `pabx.teambravotech.com` e `devices.wavoip.com` sai com
82
109
  `Origin: https://webphone.bravophone.com` — **uma origem só, fixa**. Sem iframe,
83
110
  cada cliente novo exigiria liberar mais uma origem no CORS de três backends. Com
84
111
  iframe, a lista de origens do backend nunca cresce.
@@ -255,6 +282,7 @@ carrega (o mock em `?host=mock` continua funcionando).
255
282
  | `npm start` | Sobe as duas origens locais (5173 site, 5174 host) |
256
283
  | `npm test` | 100 asserções: shim, geometria da janela e aba de abertura |
257
284
  | `npm run audit:theme` | Procura texto invisível no tema escuro |
285
+ | `npm run purge` | Limpa o cache do CDN nas URLs sem versão fixa (roda sozinho após o publish) |
258
286
 
259
287
  Abra **http://localhost:5173/**.
260
288
 
@@ -308,7 +336,7 @@ sync, ou versione por query string); `index.html` sempre com `no-cache`.
308
336
 
309
337
  ### 2. Backends — CORS
310
338
 
311
- Liberar **uma única origem** em `api.bravophone.com`, `reports.teambravotech.com` e
339
+ Liberar **uma única origem** em `api.bravophone.com`, `pabx.teambravotech.com` e
312
340
  `devices.wavoip.com`:
313
341
 
314
342
  ```
@@ -330,6 +358,89 @@ um major não quebre a página deles.
330
358
 
331
359
  ---
332
360
 
361
+ ## Manter o cliente sempre atualizado
362
+
363
+ A intuição diz para usar a URL sem versão. **É a pior escolha para isso**, e os
364
+ headers do CDN mostram por quê:
365
+
366
+ ```
367
+ sem versão / @0.2 → max-age=604800 (7 dias no navegador do usuário)
368
+ @0.2.1 exata → immutable (eterno, mas fixo)
369
+ ```
370
+
371
+ A URL sem versão é entregue com sete dias de cache **na máquina de quem
372
+ acessa**. Publicar uma correção não alcança essa pessoa: `npm run purge` limpa
373
+ as bordas do CDN, não o cache que já está no navegador dela.
374
+
375
+ O caminho que resolve é [`examples/loader-latest.js`](examples/loader-latest.js),
376
+ que separa as duas coisas:
377
+
378
+ 1. pergunta ao CDN qual é a versão atual — resposta com **5 min** de cache;
379
+ 2. carrega o bundle daquela versão **exata** — URL imutável, cache eterno.
380
+
381
+ Uma publicação chega em até cinco minutos, e o arquivo pesado vem de um cache
382
+ que nunca precisa ser revalidado. O custo é uma requisição de ~1 kB antes do
383
+ bundle, quase sempre servida do cache.
384
+
385
+ ```js
386
+ const { version } = await (await fetch(
387
+ 'https://data.jsdelivr.com/v1/packages/npm/@bravophone/webphone/resolved'
388
+ )).json()
389
+
390
+ const s = document.createElement('script')
391
+ s.src = `https://cdn.jsdelivr.net/npm/@bravophone/webphone@${version}/dist/bravophone.umd.js`
392
+ document.head.appendChild(s)
393
+ ```
394
+
395
+ Cole isso **inline** na página, não como arquivo externo — um arquivo externo
396
+ teria o mesmo problema de cache que estamos evitando.
397
+
398
+ ### Se uma publicação não aparecer
399
+
400
+ Três caches diferentes, do mais provável ao menos:
401
+
402
+ | O que está velho | Como saber | Solução |
403
+ |---|---|---|
404
+ | **A consulta de versão** | `Bravophone.version` mostra a anterior | Já resolvido: o snippet usa `cache: 'no-store'` |
405
+ | **A página do integrador** | o próprio snippet mudou e não teve efeito | Não sirva o HTML com `max-age` longo |
406
+ | **O bundle** | — | Não acontece: a URL é versionada e `immutable` |
407
+
408
+ Durante o desenvolvimento, `Ctrl+Shift+R` limpa os três de uma vez — foi o que
409
+ funcionou nos primeiros testes. Em produção não há como pedir isso ao usuário,
410
+ e é por isso que o `no-store` está na consulta: sem ele, quem carregou a página
411
+ nos últimos cinco minutos continua na versão anterior.
412
+
413
+ Se ainda assim algo ficar para trás, `npm run purge` limpa as bordas do CDN —
414
+ mas lembre que ele não alcança o navegador de ninguém.
415
+
416
+ Os assets do webphone acompanham automaticamente: o `public_path` é gravado com
417
+ a versão do pacote, então carregar o SDK 0.2.1 carrega o host 0.2.1.
418
+
419
+ ## A sessão que o webphone espera
420
+
421
+ `init()` recebe a resposta do `/api/voxfree/login` inteira:
422
+
423
+ ```js
424
+ Bravophone.init({
425
+ session: {
426
+ vxToken: '…', // obrigatório
427
+ expiresIn: 3600, // segundos
428
+ sip: '…', // sem isto o webphone não registra
429
+ ramal: '…', // idem
430
+ tenant: '…',
431
+ clienteId: '…',
432
+ ramaisUrl: '…',
433
+ },
434
+ })
435
+ ```
436
+
437
+ O SDK grava essas chaves onde o bundle as procura, **antes** dele avaliar — a
438
+ sessão já sobe autenticada, sem piscar a tela de login.
439
+
440
+ `token: '…'` continua aceito como atalho para `{ vxToken }`, mas **sozinho não
441
+ basta**: o webphone carrega, não registra, e o RouteSelector avisa "faça login
442
+ pelo webphone" — justamente o que a auto-autenticação existe para evitar.
443
+
333
444
  ## Documentação
334
445
 
335
446
  | Documento | Para quem |
@@ -346,7 +457,7 @@ um major não quebre a página deles.
346
457
  | Opção | Tipo | Padrão | Descrição |
347
458
  |---|---|---|---|
348
459
  | `token` | `string` | — | Token de sessão emitido pelo seu backend |
349
- | `mode` | `'hosted' \| 'srcdoc'` | `'hosted'` | Como o webphone é carregado — ver abaixo |
460
+ | `mode` | `'srcdoc' \| 'hosted'` | `'srcdoc'` | Como o webphone é carregado — ver abaixo |
350
461
  | `hostUrl` | `string` | `https://webphone.bravophone.com/` | Origem do webphone (só no modo `hosted`) |
351
462
  | `position` | `string` | `'bottom-right'` | Canto inicial |
352
463
  | `open` | `boolean` | `false` | Abrir já visível |
@@ -385,7 +496,7 @@ Bravophone.init({ token }) // hospedado (padrão)
385
496
  Bravophone.init({ token, mode: 'srcdoc' }) // na origem do próprio site
386
497
  ```
387
498
 
388
- | | `hosted` (padrão) | `srcdoc` |
499
+ | | `srcdoc` (padrão) | `hosted` |
389
500
  |---|---|---|
390
501
  | Onde o iframe roda | `webphone.bravophone.com` | origem do próprio site |
391
502
  | De onde vêm os arquivos | do host | do CDN, travados nesta versão |
@@ -395,7 +506,7 @@ Bravophone.init({ token, mode: 'srcdoc' }) // na origem do próprio site
395
506
  | Você precisa manter | o domínio do host | nada |
396
507
 
397
508
  **Antes de oferecer o `srcdoc` a um cliente, a origem dele precisa estar na
398
- allowlist de CORS de `api.bravophone.com`, `reports.teambravotech.com` e
509
+ allowlist de CORS de `api.bravophone.com`, `pabx.teambravotech.com` e
399
510
  `devices.wavoip.com`.** Sem isso o webphone carrega, aparece na tela e não
400
511
  registra — o navegador descarta as respostas. Isso é trabalho no **nosso**
401
512
  backend: o integrador não tem como liberar CORS de um servidor que não é dele.