@neetru/sdk 2.3.6 → 3.0.1

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/README.md +84 -22
  3. package/dist/auth.cjs +360 -140
  4. package/dist/auth.cjs.map +1 -1
  5. package/dist/auth.d.cts +1 -2
  6. package/dist/auth.d.ts +1 -2
  7. package/dist/auth.mjs +360 -140
  8. package/dist/auth.mjs.map +1 -1
  9. package/dist/catalog.cjs +21 -2
  10. package/dist/catalog.cjs.map +1 -1
  11. package/dist/catalog.d.cts +1 -2
  12. package/dist/catalog.d.ts +1 -2
  13. package/dist/catalog.mjs +21 -2
  14. package/dist/catalog.mjs.map +1 -1
  15. package/dist/checkout.cjs +21 -2
  16. package/dist/checkout.cjs.map +1 -1
  17. package/dist/checkout.d.cts +1 -2
  18. package/dist/checkout.d.ts +1 -2
  19. package/dist/checkout.mjs +21 -2
  20. package/dist/checkout.mjs.map +1 -1
  21. package/dist/db.cjs +28 -3
  22. package/dist/db.cjs.map +1 -1
  23. package/dist/db.d.cts +1 -2
  24. package/dist/db.d.ts +1 -2
  25. package/dist/db.mjs +28 -3
  26. package/dist/db.mjs.map +1 -1
  27. package/dist/entitlements.cjs +21 -2
  28. package/dist/entitlements.cjs.map +1 -1
  29. package/dist/entitlements.d.cts +1 -2
  30. package/dist/entitlements.d.ts +1 -2
  31. package/dist/entitlements.mjs +21 -2
  32. package/dist/entitlements.mjs.map +1 -1
  33. package/dist/errors.cjs.map +1 -1
  34. package/dist/errors.d.cts +3 -1
  35. package/dist/errors.d.ts +3 -1
  36. package/dist/errors.mjs.map +1 -1
  37. package/dist/index.cjs +408 -146
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +31 -20
  40. package/dist/index.d.ts +31 -20
  41. package/dist/index.mjs +408 -146
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/mocks.cjs +33 -33
  44. package/dist/mocks.cjs.map +1 -1
  45. package/dist/mocks.d.cts +23 -20
  46. package/dist/mocks.d.ts +23 -20
  47. package/dist/mocks.mjs +33 -33
  48. package/dist/mocks.mjs.map +1 -1
  49. package/dist/notifications.cjs +80 -14
  50. package/dist/notifications.cjs.map +1 -1
  51. package/dist/notifications.d.cts +1 -2
  52. package/dist/notifications.d.ts +1 -2
  53. package/dist/notifications.mjs +80 -14
  54. package/dist/notifications.mjs.map +1 -1
  55. package/dist/react.d.cts +1 -2
  56. package/dist/react.d.ts +1 -2
  57. package/dist/support.cjs +72 -12
  58. package/dist/support.cjs.map +1 -1
  59. package/dist/support.d.cts +1 -2
  60. package/dist/support.d.ts +1 -2
  61. package/dist/support.mjs +72 -12
  62. package/dist/support.mjs.map +1 -1
  63. package/dist/telemetry.cjs +23 -4
  64. package/dist/telemetry.cjs.map +1 -1
  65. package/dist/telemetry.d.cts +1 -2
  66. package/dist/telemetry.d.ts +1 -2
  67. package/dist/telemetry.mjs +23 -4
  68. package/dist/telemetry.mjs.map +1 -1
  69. package/dist/{types-CSC-RIdS.d.ts → types-DALIhcbq.d.ts} +130 -74
  70. package/dist/{types-sGGN1vkg.d.cts → types-Lfd3LiAF.d.cts} +130 -74
  71. package/dist/usage.cjs +57 -63
  72. package/dist/usage.cjs.map +1 -1
  73. package/dist/usage.d.cts +1 -2
  74. package/dist/usage.d.ts +1 -2
  75. package/dist/usage.mjs +57 -63
  76. package/dist/usage.mjs.map +1 -1
  77. package/dist/webhooks.cjs +110 -33
  78. package/dist/webhooks.cjs.map +1 -1
  79. package/dist/webhooks.d.cts +1 -2
  80. package/dist/webhooks.d.ts +1 -2
  81. package/dist/webhooks.mjs +110 -33
  82. package/dist/webhooks.mjs.map +1 -1
  83. package/package.json +2 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,191 @@ All notable changes to `@neetru/sdk` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.0.1] - 2026-05-29 — **fix: sincronizar VERSION + docstrings + contract-staging códigos de erro**
9
+
10
+ ### Fixed
11
+
12
+ - `src/index.ts` — `VERSION` constante sincronizada com `package.json` (`'3.0.0'` → `'3.0.1'`). Consumers que fazem `import { VERSION } from '@neetru/sdk'` recebiam `'3.0.0'` enquanto `npm list` mostrava `3.0.1` (LOW-NEW-SDK-01).
13
+ - `src/telemetry.ts` — JSDoc na linha 6 corrigido: path legacy `/api/v1/sdk/telemetry/event` → canônico `/api/sdk/v1/telemetry/event`. O código sempre usou o path correto; apenas a documentação estava stale (LOW-NEW-SDK-02).
14
+ - `src/__tests__/telemetry.test.ts` — comentário de cabeçalho atualizado para não dizer "(legado)" (LOW-NEW-SDK-02).
15
+ - `src/__tests__/contract-staging.test.ts` — `'http_error'` removido de todos os arrays `toContain`. `NeetruErrorCode` não inclui `'http_error'`; respostas HTTP não mapeáveis caem em `'unknown'`. Substituído por `'unknown'` em todos os 7 sites (LOW-NEW-SDK-03).
16
+
17
+ ### Nota
18
+
19
+ Este patch corresponde ao commit `4c40f01` (inline realtime-protocol types) que não atualizou `VERSION` nem o CHANGELOG.
20
+
21
+ ## [3.0.0] - 2026-05-27 — **MAJOR: contract hardening + Universal runtime + paths canonical**
22
+
23
+ > Major rewrite resolvendo 5 CRITICAL + 6 HIGH do review `CHEFE_SDK_REVIEW_2026-05-27`.
24
+ > SDK 2.3.6 estava com **4 dos 11 namespaces broken** contra Core LIVE (`usage`, `webhooks`, `notifications`, `support`) — 13 endpoints batendo 404/400. Tests passavam validando contrato errado (mock fetch aceita qualquer URL).
25
+ >
26
+ > **Justificativa do MAJOR**: paths/body shapes mudaram em 4 namespaces; 2 métodos removidos (`usage.track`, `usage.getQuota`); `verifyWebhookSignature` virou async. Toda CLI release/SaaS inaugural depende disso primeiro.
27
+
28
+ ### BREAKING CHANGES
29
+
30
+ #### 1. `usage` namespace 100% rewrite (CRITICAL-SDK-01)
31
+
32
+ - **REMOVIDO** `usage.track(event, props)` — Core não aceitava esse shape; era vaporware testado contra mock. Use `usage.report(resource, qty, opts)`.
33
+ - **REMOVIDO** `usage.getQuota(metric)` — endpoint `/usage/quota` nunca existiu no Core. `usage.report()` devolve `{value, limit, remaining}` inline em todo POST.
34
+ - Paths corrigidos:
35
+ - `/sdk/v1/usage/record` → `/api/sdk/v1/usage/record` (canonical)
36
+ - `/sdk/v1/entitlements` → `/api/sdk/v1/entitlements` (canonical)
37
+ - `usage.report()` agora envia `Idempotency-Key: <uuid v4>` header automaticamente.
38
+ - `usage.check()` retorna novo field `behavior: "readonly" | null` (inferido de `reason === "limit_exceeded"`).
39
+
40
+ #### 2. `webhooks` namespace — `productId` obrigatório (CRITICAL-SDK-03)
41
+
42
+ - `RegisterWebhookInput.productId` adicionado. Default resolve do `config.productId`; lança `validation_failed` se ambos ausentes.
43
+ - `list(options?)`, `unregister(id, options?)`, `test(id, options?)` agora aceitam `{ productId }` (Core exige query param em todos os verbos).
44
+ - `webhooks.test()` envia `Idempotency-Key` header.
45
+
46
+ #### 3. `notifications` namespace — `productId` obrigatório (CRITICAL-SDK-04)
47
+
48
+ - `SendNotificationInput.productId` adicionado (Core valida via Zod `SendNotificationSchema`).
49
+ - `ListNotificationsOptions.productId` adicionado.
50
+ - `NotificationScopeOptions` novo type para `markRead`/`dismiss`.
51
+ - `notifications.send()` envia `Idempotency-Key` header.
52
+
53
+ #### 4. `support` namespace — endpoint canonical (CRITICAL-SDK-02)
54
+
55
+ - Endpoint mudou de `/api/v1/products/{slug}/tickets` (vaporware — **nunca existiu no Core**) para `/api/sdk/v1/support/tickets` (canonical SDK path).
56
+ - `productId` obrigatório (default `config.productId`).
57
+ - `createTicket()` envia `Idempotency-Key` header.
58
+ - `listMyTickets(options?)` aceita `{ productSlug }` override.
59
+ - **Pré-requisito Core**: endpoint `/api/sdk/v1/support/tickets` precisa ser construído (vide `docs/CORE_CHANGES_REQUIRED.md` §1).
60
+
61
+ #### 5. `verifyWebhookSignature` é async + Universal (HIGH-SDK-03)
62
+
63
+ - **Antes (2.x)**: `(payload, sig, secret) => boolean` — usava `require('node:crypto')` que quebrava em bundle browser/Edge ESM puro. README anunciava "Universal" mas mentia.
64
+ - **Agora (3.0)**: `(payload, sig, secret) => Promise<boolean>` via `WebCrypto.subtle.sign` HMAC-SHA-256 + constant-time XOR compare.
65
+ - Funciona em Node ≥20, browsers modernos, Vercel Edge Functions, Cloudflare Workers.
66
+ - Lança `NeetruError('runtime_unsupported')` se WebCrypto ausente (distingue "ambiente não suporta" de "assinatura inválida").
67
+
68
+ #### 6. `telemetry.event` / `telemetry.track` path canonical (CRITICAL-SDK-05)
69
+
70
+ - `POST /api/v1/sdk/telemetry/event` → `POST /api/sdk/v1/telemetry/event` (canonical SDK pattern).
71
+ - Pré-requisito Core: alias/handler para o canonical path (vide `docs/CORE_CHANGES_REQUIRED.md` §3).
72
+
73
+ #### 7. `baseUrl` allowlist fail-closed (HIGH-SDK-05)
74
+
75
+ - `createNeetruClient` agora chama `validateBaseUrl` antes de aceitar `config.baseUrl`.
76
+ - Allowlist padrão: HTTPS `*.neetru.com` + `localhost`/`127.0.0.1`/`*.test`/`*.localhost`.
77
+ - `NeetruClientConfig.allowedHosts: string[]` novo — adicione hosts custom (suffix match).
78
+ - Bypass: `NEETRU_ALLOW_INSECURE_BASEURL=1` (env) ou `globalThis.NEETRU_ALLOW_INSECURE_BASEURL='1'`. Warning no console quando ativo.
79
+ - Cenário ataque fechado: dev configura `NEETRU_BASE_URL=http://attacker.example/` no `.env`, agora lança `invalid_config` em vez de vazar Bearer.
80
+
81
+ #### 8. Retries default por método (HIGH-SDK-01)
82
+
83
+ - **GET/HEAD**: 2 retries default (= 3 tentativas) — idempotente, seguro.
84
+ - **POST/PUT/PATCH/DELETE**: **0 retries default** — mutations podem duplicar side effects (ex: `FieldValue.increment` no `usage/record`).
85
+ - Caller pode override: `httpRequest(config, { method: 'POST', ..., retries: 2 })`.
86
+ - Combinado com `Idempotency-Key` automático em mutations: dup-write rate → zero.
87
+
88
+ #### 9. Cleanup de stubs deprecated
89
+
90
+ - `initNeetru` (deprecated desde v0.2, no-op em v2.0) **definitivamente removido**.
91
+ - `NeetruConfig` type alias **removido** (use `NeetruClientConfig`).
92
+
93
+ #### 10. `NeetruErrorCode` ganhou `runtime_unsupported`
94
+
95
+ - Lançado por `verifyWebhookSignature` quando WebCrypto ausente. Não é breaking se você usa só `instanceof NeetruError`; é breaking se você fez `switch` no union type (TS error em case missing).
96
+
97
+ ### ADDED
98
+
99
+ - `src/idempotency.ts` — `generateIdempotencyKey()` via `crypto.randomUUID()` (Node ≥20, browsers, Edge).
100
+ - `src/base-url-allowlist.ts` — `validateBaseUrl(url, allowedHosts)` fail-closed.
101
+ - `HttpRequestOptions.idempotencyKey: boolean | string` — injeta header automaticamente em todo POST de mutation.
102
+ - `client.usage.check().behavior: 'readonly' | null` — sinal para `<EntitlementGate mode='readonly'>` decidir UI.
103
+ - `NeetruClientConfig.allowedHosts` — extension point para hosts custom (staging/proxies).
104
+ - `MockUsage.__getReports()` substitui `__getRecords()` (legacy alias mantido).
105
+ - Test suite: `src/__tests__/base-url-allowlist.test.ts` (16 tests), `src/__tests__/idempotency.test.ts` (9 tests).
106
+ - `docs/CORE_CHANGES_REQUIRED.md` — catálogo de 5 endpoints Core a criar/modificar para 3.0 funcionar end-to-end.
107
+
108
+ ### FIXED
109
+
110
+ - `<EntitlementGate>` v1.1 não morre mais em silent `allowed:false` — paths corrigidos no `usage.check` (era dependência transitiva, fixed via CRITICAL-SDK-01).
111
+ - README v2.x prometia "Universal: browser/Node/Edge" mas `verifyWebhookSignature` só funcionava em Node. 3.0 cumpre.
112
+
113
+ ### Migration guide v2.x → v3.0
114
+
115
+ ```ts
116
+ // ❌ Antes (v2.x)
117
+ await client.usage.track('api_call', { route: '/x' });
118
+ const quota = await client.usage.getQuota('api_calls');
119
+
120
+ await client.webhooks.register({
121
+ url: 'https://x.com/h',
122
+ events: ['subscription.activated'],
123
+ });
124
+
125
+ await client.notifications.send({
126
+ userId: 'u1',
127
+ kind: 'order.received',
128
+ title: 'Pedido',
129
+ });
130
+
131
+ await client.support.createTicket({
132
+ subject: 'help',
133
+ message: 'x',
134
+ });
135
+
136
+ // ✅ Depois (v3.0)
137
+ const client = createNeetruClient({
138
+ apiKey: 'nrt_...',
139
+ productId: 'gestovendas', // 🆕 default p/ todos os namespaces
140
+ tenantId: 't-acme', // 🆕 default p/ usage
141
+ });
142
+
143
+ const result = await client.usage.report('api_call', 1);
144
+ // result.value, result.limit, result.remaining inline — não precisa getQuota separado.
145
+
146
+ const allowed = await client.usage.check('api_call');
147
+ // allowed.behavior === 'readonly' quando over limit (use em <EntitlementGate>)
148
+
149
+ await client.webhooks.register({
150
+ // productId default config.productId, ou explícito:
151
+ url: 'https://x.com/h',
152
+ events: ['subscription.activated'],
153
+ });
154
+
155
+ await client.notifications.send({
156
+ userId: 'u1',
157
+ kind: 'order.received',
158
+ title: 'Pedido',
159
+ });
160
+
161
+ await client.support.createTicket({
162
+ subject: 'help',
163
+ message: 'x',
164
+ });
165
+ ```
166
+
167
+ ### Verificação de assinatura de webhook — agora async
168
+
169
+ ```ts
170
+ // ❌ Antes (v2.x) — sync, quebrava em Edge
171
+ import { verifyWebhookSignature } from '@neetru/sdk/webhooks';
172
+ const ok = verifyWebhookSignature(rawPayload, sigHeader, secret);
173
+
174
+ // ✅ Depois (v3.0) — async, Universal
175
+ const ok = await verifyWebhookSignature(rawPayload, sigHeader, secret);
176
+ ```
177
+
178
+ ### Endpoints Core que precisam ser criados/modificados antes do publish 3.0
179
+
180
+ Vide `docs/CORE_CHANGES_REQUIRED.md` completo:
181
+
182
+ 1. **CRITICAL** — `POST/GET /api/sdk/v1/support/tickets` (customer-facing) — 3-4h.
183
+ 2. **HIGH** — Idempotency-Key dedup em endpoints de mutation — 4-6h.
184
+ 3. **HIGH** — `POST /api/sdk/v1/telemetry/event` (canonical) — 1-2h ou alias middleware.
185
+ 4. **LOW** — `behavior` field em `/api/sdk/v1/entitlements` response — 30min.
186
+ 5. **LOW** — Verificar envelope `/api/sdk/v1/datastore/{collection}` — 30min check.
187
+
188
+ ### Tests
189
+
190
+ - **719 tests passing** (era 668 em 2.3.6). Tsc clean. Build clean.
191
+ - Contract tests reescritos para validar paths canonical + body shapes + headers (Idempotency-Key UUID v4 regex match).
192
+
8
193
  ## [2.3.6] - 2026-05-26 — **fix(bundle): webpackIgnore em TODOS os dynamic imports node-only — bug_168368ae [CRITICAL]**
9
194
 
10
195
  ### Corrigido
package/README.md CHANGED
@@ -1,62 +1,79 @@
1
1
  # @neetru/sdk
2
2
 
3
- > Biblioteca runtime oficial pra consumir o ecossistema Neetru a partir de produtos SaaS (Next.js, Node, browser, Edge runtimes).
3
+ > Biblioteca runtime oficial pra consumir o ecossistema Neetru a partir de produtos SaaS (Next.js, Node ≥20, browser, Edge runtimes).
4
4
 
5
5
  <p>
6
6
  <img alt="npm" src="https://img.shields.io/npm/v/@neetru/sdk?logo=npm">
7
7
  <img alt="downloads" src="https://img.shields.io/npm/dm/@neetru/sdk?logo=npm">
8
8
  <img alt="bundle size" src="https://img.shields.io/bundlephobia/minzip/@neetru/sdk">
9
- <img alt="Node" src="https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white">
9
+ <img alt="Node" src="https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white">
10
10
  <img alt="license" src="https://img.shields.io/badge/license-MIT-22c55e">
11
11
  </p>
12
12
 
13
+ > ## ⚠ 3.0 é breaking — leia o [CHANGELOG](./CHANGELOG.md) antes de upgrade
14
+ >
15
+ > SDK 2.x estava com 4 namespaces broken contra Core LIVE. 3.0 corrige paths canonical, exige `productId` em mutations, adiciona `Idempotency-Key` automático, `baseUrl` allowlist fail-closed, e `verifyWebhookSignature` virou Universal async via WebCrypto.
16
+ >
17
+ > **Métodos removidos**: `usage.track`, `usage.getQuota` (use `usage.report` / `usage.check`).
18
+ >
19
+ > **Migration guide completo no CHANGELOG.md**.
20
+
13
21
  ## Instalação
14
22
 
15
23
  ```bash
16
24
  npm install @neetru/sdk
17
25
  ```
18
26
 
19
- ## Hello world
27
+ ## Hello world (3.0)
20
28
 
21
29
  ```ts
22
30
  import { createNeetruClient } from '@neetru/sdk';
23
31
 
24
32
  const neetru = createNeetruClient({
25
33
  apiKey: process.env.NEETRU_API_KEY, // nrt_<keyId>_<secret>
34
+ productId: 'gestovendas', // 3.0: default p/ todos namespaces
35
+ tenantId: 't-acme', // 3.0: default p/ usage
26
36
  env: 'prod', // 'dev' = mocks in-memory
27
37
  });
28
38
 
29
39
  // Auth
30
40
  const user = await neetru.auth.signIn();
31
- console.log('logado como:', user?.email);
32
41
 
33
- // Entitlement check
34
- const canAi = await neetru.entitlements.check('gestovendas', 'ai_recommendations');
35
- if (canAi) { /* mostrar feature */ }
42
+ // Meter consumo (canonical /api/sdk/v1/usage/record)
43
+ const r = await neetru.usage.report('api_call', 1);
44
+ console.log(r.value, '/', r.limit, '— restam', r.remaining);
45
+
46
+ // Entitlement check com behavior=readonly
47
+ const check = await neetru.usage.check('api_call');
48
+ if (!check.allowed && check.behavior === 'readonly') {
49
+ // UI degradada — read-only sem quebrar engajamento
50
+ }
36
51
  ```
37
52
 
38
53
  ## Princípios
39
54
 
40
55
  - **Vendor-neutral** — superfície pública NÃO vaza Firebase/Stripe/etc. Backend Neetru pode mudar no futuro sem reescrever produto.
41
56
  - **Tree-shakable** — ESM-first, sem side-effects. Importe só os namespaces que usa.
42
- - **Universal** — browser, Node ≥18, Edge runtimes (Vercel Edge, Cloudflare Workers). Usa `fetch` global.
57
+ - **Universal** — browser, Node ≥20, Edge runtimes (Vercel Edge, Cloudflare Workers). Usa `fetch` global + WebCrypto.
58
+ - **Fail-closed segurança** — `baseUrl` allowlist (`*.neetru.com` + localhost) rejeita phishing/MITM. Bypass: `NEETRU_ALLOW_INSECURE_BASEURL=1`.
59
+ - **Idempotency-Key automático** — mutations (`usage.report`, `webhooks.test`, `notifications.send`, `support.createTicket`) injetam UUID v4 header. `retries: 0` default em POSTs para defesa em camada.
43
60
  - **Tipado** — erros via `NeetruError` com `.code`, `.status`, `.requestId`.
44
61
  - **Dev mode** — `NEETRU_ENV=dev` ativa mocks automáticos — zero rede em testes locais.
45
62
 
46
- ## Namespaces v1.2
47
-
48
- | Namespace | O que oferece |
49
- |---|---|
50
- | `auth` | OIDC sign-in / sign-out + dev fixture user |
51
- | `catalog` | Produtos públicos (`list` / `get`) |
52
- | `entitlements` | Verificação `(productSlug, feature)` boolean ou detalhado |
53
- | `telemetry` | `event(...)` + `log(...)` per-product |
54
- | `usage` | `track` / `getQuota` / `report` / `check` (metering canônico) |
55
- | `support` | `createTicket` / `listMyTickets` |
56
- | `db` | Coleções tenant-scoped (`list` / `get` / `set` / `add` / `update` / `remove`) |
57
- | `checkout` | Stripe Checkout intent (`start` / `get` / `cancel` + auto-redirect) |
58
- | `webhooks` ⭐ v1.2 | Produtos registram URL pra receber eventos Core (subscription.activated, usage.quota_exceeded, etc) |
59
- | `notifications` ⭐ v1.2 | Produto envia notification in-app pros SEUS usuários (distinto das staff-only do Core) |
63
+ ## Namespaces 3.0
64
+
65
+ | Namespace | O que oferece | Endpoint |
66
+ |---|---|---|
67
+ | `auth` | OIDC sign-in/out + verifyToken | `auth.neetru.com/api/v1/oauth/*` |
68
+ | `catalog` | Produtos públicos (`list` / `get`) | `/api/sdk/v1/catalog` |
69
+ | `entitlements` | Verificação boolean simples (legacy compat) | `/api/v1/sdk/entitlements/check` |
70
+ | `telemetry` | `event(...)` + `track(...)` + `log(...)` | `/api/sdk/v1/telemetry/{event,log}` |
71
+ | `usage` | `report` / `check` (metering + entitlement full) | `/api/sdk/v1/usage/record`, `/api/sdk/v1/entitlements` |
72
+ | `support` | `createTicket` / `listMyTickets` | `/api/sdk/v1/support/tickets` ⭐ 3.0 |
73
+ | `db` | Coleções tenant-scoped (offline-first v2.0) | `/api/sdk/v1/datastore/*` |
74
+ | `checkout` | Stripe Checkout intent | `/api/v1/checkout/intents` |
75
+ | `webhooks` | Produtos registram URL pra receber eventos | `/api/sdk/v1/webhooks` |
76
+ | `notifications` | Produto envia notification in-app | `/api/sdk/v1/notifications` |
60
77
 
61
78
  ## Exemplos por namespace
62
79
 
@@ -78,6 +95,26 @@ Eventos recebidos no seu endpoint chegam com:
78
95
  - `X-Neetru-Signature: sha256=<hmac>` (se `secret` registrado)
79
96
  - `X-Neetru-Timestamp: <ms>` (replay protection — rejeitar > 5min skew)
80
97
 
98
+ #### Verificar assinatura no consumer (3.0 — async + Universal)
99
+
100
+ ```ts
101
+ import { verifyWebhookSignature } from '@neetru/sdk';
102
+
103
+ // Next.js Route Handler (Edge OU Node runtime — funciona em ambos)
104
+ export async function POST(req: Request) {
105
+ const raw = await req.text();
106
+ const sig = req.headers.get('X-Neetru-Signature');
107
+ const ok = await verifyWebhookSignature(raw, sig, process.env.NEETRU_WEBHOOK_SECRET!);
108
+ if (!ok) return new Response('unauthorized', { status: 401 });
109
+
110
+ const event = JSON.parse(raw);
111
+ // ... handle event
112
+ return Response.json({ ok: true });
113
+ }
114
+ ```
115
+
116
+ > **Breaking 3.0**: era `boolean` sync usando `node:crypto`. Agora `Promise<boolean>` via WebCrypto.subtle — funciona em Cloudflare Workers, Vercel Edge, browsers e Node ≥20.
117
+
81
118
  ### Notifications produto → user (v1.2)
82
119
 
83
120
  ```ts
@@ -184,6 +221,31 @@ const neetru = createNeetruClient({
184
221
  - Zero dependências runtime (usa `fetch` global)
185
222
  - Bundle minzip <10KB (todos namespaces somados, tree-shake-friendly)
186
223
 
224
+ ## Testes de contrato (staging)
225
+
226
+ O arquivo `src/__tests__/contract-staging.test.ts` contém testes que exercitam o HTTP real contra um ambiente de staging. Por default, todos os testes são skippados quando `NEETRU_STAGING_URL` não estiver definido.
227
+
228
+ Para rodar:
229
+
230
+ ```bash
231
+ NEETRU_STAGING_URL=https://api.staging.neetru.com \
232
+ NEETRU_STAGING_API_KEY=nrt_<keyId>_<secret> \
233
+ npx vitest run src/__tests__/contract-staging.test.ts
234
+ ```
235
+
236
+ Variáveis de ambiente:
237
+
238
+ | Variável | Obrigatória | Descrição |
239
+ |---|---|---|
240
+ | `NEETRU_STAGING_URL` | Sim | URL base da API de staging |
241
+ | `NEETRU_STAGING_API_KEY` | Sim | API key no formato `nrt_<keyId>_<secret>` |
242
+ | `NEETRU_STAGING_PRODUCT_ID` | Não | productId cadastrado no staging (default: `gestovendas`) |
243
+ | `NEETRU_STAGING_TENANT_ID` | Não | tenantId válido no staging (default: `t-acme`) |
244
+
245
+ Namespaces cobertos: `catalog.list`, `catalog.get`, `entitlements.check`, `entitlements.checkDetailed`, `usage.report`, `usage.check`, `telemetry.event`, `telemetry.log`, `auth.getUser`, `auth.signIn`.
246
+
247
+ Os testes assertam apenas o shape/tipos da resposta, nunca valores exatos — staging pode ter dados variáveis mas o contrato de formato deve ser estável.
248
+
187
249
  ## Mais info
188
250
 
189
251
  - **Repo:** [github.com/Neetru/neetru-core](https://github.com/Neetru/neetru-core)