@notifica/node 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Notifica
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,444 @@
1
+ <p align="center">
2
+ <h1 align="center">@notifica/node</h1>
3
+ <p align="center">SDK oficial do <a href="https://usenotifica.com.br">Notifica</a> para Node.js</p>
4
+ <p align="center">Infraestrutura de notificações para o Brasil 🇧🇷</p>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/notifica-tech/notifica-node/actions"><img src="https://github.com/notifica-tech/notifica-node/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
9
+ <a href="https://www.npmjs.com/package/@notifica/node"><img src="https://img.shields.io/npm/v/@notifica/node.svg" alt="npm"></a>
10
+ <a href="https://github.com/notifica-tech/notifica-node/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@notifica/node.svg" alt="license"></a>
11
+ <a href="https://www.npmjs.com/package/@notifica/node"><img src="https://img.shields.io/npm/dm/@notifica/node.svg" alt="downloads"></a>
12
+ </p>
13
+
14
+ ---
15
+
16
+ WhatsApp, Email, SMS, Push e In-App em uma API unificada. Zero dependências. TypeScript nativo.
17
+
18
+ ## Instalação
19
+
20
+ ```bash
21
+ npm install @notifica/node
22
+ ```
23
+
24
+ > Requer **Node.js 18+** (usa `fetch` nativo).
25
+
26
+ ## Início Rápido
27
+
28
+ ```typescript
29
+ import { Notifica } from '@notifica/node';
30
+
31
+ const notifica = new Notifica('nk_live_...');
32
+
33
+ // Enviar uma notificação via WhatsApp
34
+ const notification = await notifica.notifications.send({
35
+ channel: 'whatsapp',
36
+ to: '+5511999999999',
37
+ template: 'welcome',
38
+ data: { name: 'João' },
39
+ });
40
+
41
+ console.log(notification.id); // 'notif-uuid'
42
+ console.log(notification.status); // 'pending'
43
+ ```
44
+
45
+ ## Configuração
46
+
47
+ ```typescript
48
+ // Simples — apenas API key
49
+ const notifica = new Notifica('nk_live_...');
50
+
51
+ // Configuração completa
52
+ const notifica = new Notifica({
53
+ apiKey: 'nk_live_...',
54
+ baseUrl: 'https://app.usenotifica.com.br/v1', // padrão
55
+ timeout: 15000, // 15s (padrão: 30s)
56
+ maxRetries: 5, // padrão: 3
57
+ autoIdempotency: true, // padrão: true
58
+ });
59
+ ```
60
+
61
+ | Opção | Tipo | Padrão | Descrição |
62
+ |-------|------|--------|-----------|
63
+ | `apiKey` | `string` | — | **Obrigatório.** Sua API key (`nk_live_...` ou `nk_test_...`) |
64
+ | `baseUrl` | `string` | `https://app.usenotifica.com.br/v1` | URL base da API |
65
+ | `timeout` | `number` | `30000` | Timeout em ms |
66
+ | `maxRetries` | `number` | `3` | Retentativas automáticas em 429/5xx |
67
+ | `autoIdempotency` | `boolean` | `true` | Gerar chave de idempotência automática para POSTs |
68
+
69
+ ---
70
+
71
+ ## Recursos
72
+
73
+ ### Notifications — Envio e consulta
74
+
75
+ ```typescript
76
+ // Enviar notificação
77
+ const notification = await notifica.notifications.send({
78
+ channel: 'whatsapp', // 'email' | 'whatsapp' | 'sms' | 'in_app' | 'push'
79
+ to: '+5511999999999',
80
+ template: 'welcome',
81
+ data: { name: 'João' },
82
+ metadata: { source: 'signup-flow' },
83
+ });
84
+
85
+ // Listar (com paginação manual)
86
+ const { data, meta } = await notifica.notifications.list({
87
+ channel: 'email',
88
+ status: 'delivered',
89
+ limit: 50,
90
+ });
91
+ const page2 = await notifica.notifications.list({ cursor: meta.cursor });
92
+
93
+ // Auto-paginação com async iterator
94
+ for await (const n of notifica.notifications.listAll({ channel: 'email' })) {
95
+ console.log(n.id, n.status);
96
+ }
97
+
98
+ // Detalhes + tentativas de entrega
99
+ const detail = await notifica.notifications.get('notif-uuid');
100
+ const attempts = await notifica.notifications.listAttempts('notif-uuid');
101
+ ```
102
+
103
+ ### Templates — Gerenciamento
104
+
105
+ ```typescript
106
+ // Criar
107
+ const template = await notifica.templates.create({
108
+ channel: 'email',
109
+ slug: 'welcome-email',
110
+ name: 'Email de Boas-Vindas',
111
+ content: 'Olá {{name}}, bem-vindo ao {{company}}!',
112
+ variants: {
113
+ subject: 'Bem-vindo, {{name}}!',
114
+ html_body: '<h1>Olá {{name}}</h1><p>Bem-vindo ao {{company}}!</p>',
115
+ },
116
+ status: 'active',
117
+ });
118
+
119
+ // Listar / obter / atualizar / deletar
120
+ const { data } = await notifica.templates.list({ channel: 'email' });
121
+ await notifica.templates.update('tpl-uuid', { status: 'active' });
122
+ await notifica.templates.delete('tpl-uuid');
123
+
124
+ // Preview com variáveis
125
+ const preview = await notifica.templates.preview('tpl-uuid', {
126
+ variables: { name: 'João', company: 'Empresa Ltda' },
127
+ });
128
+ console.log(preview.rendered.subject); // "Bem-vindo, João!"
129
+
130
+ // Preview de conteúdo arbitrário (para editor em tempo real)
131
+ const livePreview = await notifica.templates.previewContent({
132
+ content: 'Oi {{name}}!',
133
+ channel: 'email',
134
+ variables: { name: 'Maria' },
135
+ });
136
+
137
+ // Validar template
138
+ const validation = await notifica.templates.validate('tpl-uuid');
139
+ console.log(validation.valid, validation.warnings);
140
+ ```
141
+
142
+ ### Workflows — Orquestração
143
+
144
+ ```typescript
145
+ // Criar workflow
146
+ const workflow = await notifica.workflows.create({
147
+ slug: 'welcome-flow',
148
+ name: 'Fluxo de Boas-Vindas',
149
+ steps: [
150
+ { type: 'send', channel: 'email', template: 'welcome-email' },
151
+ { type: 'delay', duration: '1h' },
152
+ { type: 'send', channel: 'whatsapp', template: 'welcome-whatsapp' },
153
+ ],
154
+ });
155
+
156
+ // Disparar workflow
157
+ const run = await notifica.workflows.trigger('welcome-flow', {
158
+ recipient: '+5511999999999',
159
+ data: { name: 'João', plan: 'pro' },
160
+ });
161
+
162
+ // Gerenciar execuções
163
+ const { data: runs } = await notifica.workflows.listRuns({ status: 'running' });
164
+ const runDetail = await notifica.workflows.getRun('run-uuid');
165
+ await notifica.workflows.cancelRun('run-uuid');
166
+ ```
167
+
168
+ **Tipos de step:**
169
+
170
+ | Tipo | Campos | Descrição |
171
+ |------|--------|-----------|
172
+ | `send` | `channel`, `template` | Envia notificação pelo canal |
173
+ | `delay` | `duration` (`5m`, `1h`, `1d`) | Pausa a execução |
174
+ | `fallback` | `channels[]`, `template` | Tenta canais em ordem até sucesso |
175
+
176
+ ### Subscribers — Gerenciamento de destinatários
177
+
178
+ ```typescript
179
+ // Criar/atualizar (upsert por external_id)
180
+ const subscriber = await notifica.subscribers.create({
181
+ external_id: 'user-123',
182
+ email: 'joao@empresa.com.br',
183
+ phone: '+5511999998888',
184
+ name: 'João Silva',
185
+ locale: 'pt_BR',
186
+ timezone: 'America/Sao_Paulo',
187
+ custom_properties: { plan: 'pro' },
188
+ });
189
+
190
+ // Listar (com busca)
191
+ const { data } = await notifica.subscribers.list({ search: 'joao' });
192
+
193
+ // Atualizar / deletar (LGPD — nullifica PII, irreversível!)
194
+ await notifica.subscribers.update('sub-uuid', { name: 'João S.' });
195
+ await notifica.subscribers.delete('sub-uuid');
196
+
197
+ // Preferências de notificação
198
+ const prefs = await notifica.subscribers.getPreferences('sub-uuid');
199
+ await notifica.subscribers.updatePreferences('sub-uuid', {
200
+ preferences: [
201
+ { category: 'marketing', channel: 'email', enabled: false },
202
+ { category: 'transactional', channel: 'whatsapp', enabled: true },
203
+ ],
204
+ });
205
+
206
+ // Import em lote (transacional — tudo ou nada)
207
+ const result = await notifica.subscribers.bulkImport({
208
+ subscribers: [
209
+ { external_id: 'user-1', email: 'a@empresa.com.br', name: 'Ana' },
210
+ { external_id: 'user-2', email: 'b@empresa.com.br', name: 'Bruno' },
211
+ ],
212
+ });
213
+ ```
214
+
215
+ **Notificações In-App:**
216
+
217
+ ```typescript
218
+ const { data } = await notifica.subscribers.listNotifications('sub-uuid', { unread_only: true });
219
+ await notifica.subscribers.markRead('sub-uuid', 'notif-uuid');
220
+ await notifica.subscribers.markAllRead('sub-uuid');
221
+ const count = await notifica.subscribers.getUnreadCount('sub-uuid');
222
+ ```
223
+
224
+ ### Channels — Configuração de canais
225
+
226
+ ```typescript
227
+ const channel = await notifica.channels.create({
228
+ channel: 'email',
229
+ provider: 'aws_ses',
230
+ credentials: { access_key_id: 'AKIA...', secret_access_key: '...', region: 'us-east-1' },
231
+ settings: { from_address: 'noreply@empresa.com.br', from_name: 'Empresa' },
232
+ });
233
+
234
+ const channels = await notifica.channels.list();
235
+ const test = await notifica.channels.test('email');
236
+ ```
237
+
238
+ ### Domains — Domínios de envio
239
+
240
+ ```typescript
241
+ // Registrar domínio e configurar DNS
242
+ const domain = await notifica.domains.create({ domain: 'suaempresa.com.br' });
243
+ // → Configure os registros em domain.dns_records no seu provedor DNS
244
+
245
+ const verified = await notifica.domains.verify(domain.id);
246
+ const health = await notifica.domains.getHealth(domain.id);
247
+ const alerts = await notifica.domains.listAlerts();
248
+ ```
249
+
250
+ ### Webhooks — Eventos outbound
251
+
252
+ ```typescript
253
+ const webhook = await notifica.webhooks.create({
254
+ url: 'https://meuapp.com.br/webhooks/notifica',
255
+ events: ['notification.delivered', 'notification.failed'],
256
+ });
257
+ // ⚠️ Salve webhook.signing_secret — mostrado apenas na criação!
258
+
259
+ await notifica.webhooks.test(webhook.id);
260
+ const deliveries = await notifica.webhooks.listDeliveries(webhook.id);
261
+ ```
262
+
263
+ **Verificação de assinatura:**
264
+
265
+ ```typescript
266
+ app.post('/webhooks/notifica', async (req, res) => {
267
+ const payload = req.body; // raw body string
268
+ const signature = req.headers['x-notifica-signature'];
269
+ const secret = process.env.WEBHOOK_SECRET!;
270
+
271
+ const valid = await notifica.webhooks.verify(payload, signature, secret);
272
+ if (!valid) return res.status(401).send('Assinatura inválida');
273
+
274
+ // Processar evento...
275
+ res.status(200).send('OK');
276
+ });
277
+
278
+ // Ou a versão que lança erro automaticamente:
279
+ await notifica.webhooks.verifyOrThrow(payload, signature, secret);
280
+ ```
281
+
282
+ ### API Keys — Gerenciamento de chaves
283
+
284
+ ```typescript
285
+ const key = await notifica.apiKeys.create({
286
+ key_type: 'secret',
287
+ label: 'Backend Production',
288
+ environment: 'production',
289
+ });
290
+ // ⚠️ Salve key.raw_key — mostrado apenas na criação!
291
+
292
+ const keys = await notifica.apiKeys.list();
293
+ await notifica.apiKeys.revoke('key-uuid');
294
+ ```
295
+
296
+ ### Analytics — Métricas
297
+
298
+ ```typescript
299
+ const overview = await notifica.analytics.overview({ period: '7d' });
300
+ const channels = await notifica.analytics.byChannel({ period: '24h' });
301
+ const timeseries = await notifica.analytics.timeseries({ period: '7d', granularity: 'day' });
302
+ const top = await notifica.analytics.topTemplates({ period: '30d', limit: 5 });
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Tratamento de Erros
308
+
309
+ O SDK lança erros tipados para cada cenário:
310
+
311
+ ```typescript
312
+ import {
313
+ NotificaError, // Erro base
314
+ ApiError, // Qualquer erro da API (4xx, 5xx)
315
+ ValidationError, // 422 — dados inválidos
316
+ RateLimitError, // 429 — rate limit excedido
317
+ TimeoutError, // Timeout de conexão
318
+ } from '@notifica/node';
319
+
320
+ try {
321
+ await notifica.notifications.send({ channel: 'email', to: 'x' });
322
+ } catch (error) {
323
+ if (error instanceof ValidationError) {
324
+ console.log(error.status); // 422
325
+ console.log(error.message); // "Email inválido"
326
+ console.log(error.details); // { email: ["is invalid"] }
327
+ console.log(error.requestId); // "req-abc-123" (para suporte)
328
+ }
329
+ if (error instanceof RateLimitError) {
330
+ console.log(error.retryAfter); // 30 (segundos)
331
+ }
332
+ if (error instanceof ApiError) {
333
+ console.log(error.status, error.code);
334
+ }
335
+ }
336
+ ```
337
+
338
+ ## Idempotência
339
+
340
+ O SDK gera automaticamente chaves de idempotência para todas as requests POST, prevenindo operações duplicadas em caso de retry de rede.
341
+
342
+ ```typescript
343
+ // Automático (padrão)
344
+ await notifica.notifications.send({ channel: 'email', to: 'a@b.com' });
345
+
346
+ // Chave customizada (útil para deduplicação por lógica de negócio)
347
+ await notifica.notifications.send(
348
+ { channel: 'email', to: 'a@b.com' },
349
+ { idempotencyKey: 'signup-user-123' },
350
+ );
351
+ ```
352
+
353
+ ## Retries Automáticos
354
+
355
+ Retenta automaticamente em falhas transientes:
356
+
357
+ - **429** Too Many Requests — respeita header `Retry-After`
358
+ - **5xx** Server errors — backoff exponencial com jitter
359
+
360
+ ```typescript
361
+ const notifica = new Notifica({
362
+ apiKey: 'nk_live_...',
363
+ maxRetries: 5, // padrão: 3
364
+ });
365
+ ```
366
+
367
+ ## Paginação
368
+
369
+ ```typescript
370
+ // 1. Manual (cursor-based)
371
+ let cursor: string | undefined;
372
+ do {
373
+ const page = await notifica.notifications.list({ cursor, limit: 100 });
374
+ for (const n of page.data) { console.log(n.id); }
375
+ cursor = page.meta.has_more ? page.meta.cursor ?? undefined : undefined;
376
+ } while (cursor);
377
+
378
+ // 2. Auto-paginação (async iterator)
379
+ for await (const n of notifica.notifications.listAll()) {
380
+ console.log(n.id);
381
+ }
382
+ ```
383
+
384
+ ## Tipos TypeScript
385
+
386
+ Todos os tipos são exportados:
387
+
388
+ ```typescript
389
+ import type {
390
+ Notification,
391
+ SendNotificationParams,
392
+ Template,
393
+ Workflow,
394
+ WorkflowStep,
395
+ Subscriber,
396
+ Channel,
397
+ NotificationStatus,
398
+ PaginatedResponse,
399
+ } from '@notifica/node';
400
+ ```
401
+
402
+ ---
403
+
404
+ ## Desenvolvimento
405
+
406
+ ```bash
407
+ # Instalar dependências
408
+ npm install
409
+
410
+ # Type check
411
+ npm run typecheck
412
+
413
+ # Rodar testes (requer Node 22+)
414
+ npm test
415
+
416
+ # Build
417
+ npm run build
418
+ ```
419
+
420
+ ## Contribuindo
421
+
422
+ Contribuições são bem-vindas! Por favor:
423
+
424
+ 1. Faça fork do repositório
425
+ 2. Crie sua branch (`git checkout -b feature/minha-feature`)
426
+ 3. Commit suas mudanças (`git commit -m 'feat: minha feature'`)
427
+ 4. Push para a branch (`git push origin feature/minha-feature`)
428
+ 5. Abra um Pull Request
429
+
430
+ ## Requisitos
431
+
432
+ - **Node.js 18+** (usa `fetch` nativo e `crypto.subtle`)
433
+ - **Zero dependências** externas
434
+
435
+ ## Links
436
+
437
+ - [Documentação](https://docs.usenotifica.com.br)
438
+ - [API Reference](https://docs.usenotifica.com.br/api-reference)
439
+ - [Dashboard](https://app.usenotifica.com.br)
440
+ - [GitHub](https://github.com/notifica-tech/notifica-node)
441
+
442
+ ## Licença
443
+
444
+ [MIT](./LICENSE) © [Notifica](https://usenotifica.com.br)