@neetru/sdk 3.1.14 → 3.2.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,59 @@ 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.2.1] - 2026-07-27 — **fix(db-sql): hot-swap do pool quando host/port/user/senha mudam (bug_pdv_agiliza)**
9
+
10
+ Release **patch** (bug crítico de conexão em produção — bloqueava login/queries em qualquer produto que renovasse lease sem reiniciar o processo).
11
+
12
+ ### Contexto
13
+
14
+ Bug real em produção (pdv-agiliza, 2026-07-24/27): `SqlLeaseManager._swapPool()` só recriava o `pg.Pool` quando `leaseId` OU `credentialVersion` mudavam entre renovações. O Core reemite `renew` com o MESMO `leaseId` de propósito, e `credentialVersion` é um placeholder que nunca muda — então essa condição nunca disparava via renovação normal, só na primeira conexão do processo (`fetchLease` inicial). O app renovava a lease a cada poucos minutos (200 OK, confirmado nos logs do Core) mas o `pg.Pool` real, construído uma vez no boot, nunca era recriado — incluindo o certificado de cliente mTLS efêmero, que expira em minutos. Resultado: qualquer processo de longa duração ficava preso pra sempre no host/certificado do boot, mesmo recebendo material de conexão fresco a cada renovação. Só um restart completo do processo corrigia — não é uma solução, é sintoma do bug.
15
+
16
+ ### Corrigido
17
+
18
+ - `_swapPool()` agora usa `hasConnectionChanged()`, que compara TODOS os campos que afetam a conexão real (`host`, `port`, `dbName`, `user`, `password`, `sslca`, `clientCert`, `clientKey`) além de `leaseId`/`credentialVersion` — superset estrito do comportamento anterior, não remove nenhum gatilho existente. Qualquer mudança de conexão propagada por `renew` agora recria o pool corretamente, sem depender do Core lembrar de bumpar `credentialVersion`.
19
+ - 2 testes novos cobrindo host e password mudando com `leaseId`/`credentialVersion` iguais (o cenário exato do bug). Suite completa: 982/982.
20
+ - `NeetruLoginButton` não resetava `isLoading` após `signIn()` resolver com sucesso (em vez de navegar — ex.: ambiente sem redirect real, teste, ou erro silencioso pós-chamada) — só resetava no `catch`. Agora reseta em ambos os caminhos. (Commit incluído nesta release; nunca teve versão própria publicada.)
21
+
22
+ ## [3.2.0] - 2026-07-24 — **feat(react): `<NeetruLoginButton>` — botão padronizado "Entrar com Neetru"**
23
+
24
+ Release **minor** (nova API pública aditiva, subpath `@neetru/sdk/react`).
25
+
26
+ ### Contexto
27
+
28
+ Cada produto SaaS Neetru inventava o próprio botão/estilo pra iniciar o login OIDC (`client.auth.signIn()`), sem nenhum padrão visual em comum. `auth.neetru.com/oauth/login` já tem um botão de referência ("Entrar com Google", preto/BMW) — faltava o equivalente para os produtos oferecerem "Entrar com Neetru" na própria tela de login, com a MESMA identidade visual.
29
+
30
+ ### Adicionado
31
+
32
+ - `<NeetruLoginButton client={neetru} .../>` (`@neetru/sdk/react`) — botão pronto que chama `client.auth.signIn({ redirectUri, scope, postLoginRedirect })` no click. Visual BMW idêntico ao "Entrar com Google" de referência (altura 48px, `rounded-none`, uppercase, tracking 0.08em) com o ícone-chama Neetru (mesmo asset de `src/components/logo.tsx` no Core) no lugar do ícone do provider.
33
+ - `theme`: `"dark"` (default, preto — CTA primário) ou `"light"` (branco/borda preta — secundário, ou sobre fundo escuro).
34
+ - `label`: sobrescreve o texto default `"Entrar com Neetru"`.
35
+ - `onError`: dispara se `signIn()` lançar antes do redirect (config ausente, runtime sem WebCrypto/sessionStorage).
36
+ - Estilos **inline** de propósito — roda idêntico em qualquer app consumidor do SDK, com ou sem Tailwind.
37
+ - 7 testes novos (render, temas, click→signIn, loading/disabled, onError).
38
+
39
+ ## [3.1.15] - 2026-07-22 — **feat(build-guard): withNeetruBuildGuard confirma o env realmente compilado (incidente pdv-agiliza)**
40
+
41
+ Release **patch** (fecha bug crítico de auth em produção junto com `@neetru/cli >= 2.26.2`).
42
+
43
+ ### Contexto
44
+
45
+ `.env.local` (fica no disco do dev) vence `.env.production` na precedência de env do Next.js durante `next build` — o bundle de produção podia compilar com `NEXT_PUBLIC_NEETRU_ENV=dev` sem nenhum aviso, ativando `MockAuth` no browser (`MockAuth.signIn()` só finge login em memória, nunca navega pro IdP real — o botão "Entrar com Neetru" não fazia nada).
46
+
47
+ ### Added
48
+
49
+ - **`withNeetruBuildGuard`** — novo export, **subpath dedicado `@neetru/sdk/build-guard`** (não o barrel principal `.` — é o único módulo do SDK com import estático de `node:fs`/`node:path`, mantido fora do bundle universal browser/Edge de propósito). Envolve `next.config.mjs`:
50
+ ```js
51
+ import { withNeetruBuildGuard } from '@neetru/sdk/build-guard';
52
+ export default withNeetruBuildGuard({ ...nextConfig });
53
+ ```
54
+ Grava `.next/neetru-build-manifest.json` com `{builtEnv, productId, oidcClientId, builtAt}` — os valores `NEXT_PUBLIC_NEETRU_*` realmente resolvidos no momento da compilação. `neetru build` (`@neetru/cli >= 2.26.2`) lê esse arquivo depois do build e recusa empacotar o tarball se o valor divergir do ambiente esperado do deploy.
55
+ - Checa a convenção `NEXT_PUBLIC_NEETRU_ENV`/`_PRODUCT_ID`/`_OIDC_CLIENT_ID` (a mesma usada pelo bootstrap Next.js recomendado do ecossistema, ex. `src/lib/neetru/client.ts`) — não é uma checagem universal de qualquer forma de configurar o client.
56
+
57
+ ## [3.1.14] - 2026-07-16 — **feat(dev-suite): env `dev-local` aponta baseUrl pro Core local (F1.3)**
58
+
59
+ - `NEETRU_ENV=dev-local` (emulador `neetru dev up`) resolve `baseUrl` pro Core local por default; um `baseUrl` explícito no config sempre vence.
60
+
8
61
  ## [3.1.13] - 2026-07-14 — **fix(auth): refresh_token rotation mantém a sessão viva além de ~1h (bug_339a100e)**
9
62
 
10
63
  Release **patch** (bugfix de segurança/continuidade de sessão).
package/README.md CHANGED
@@ -75,6 +75,38 @@ if (!check.allowed && check.behavior === 'readonly') {
75
75
  | `webhooks` | Produtos registram URL pra receber eventos | `/api/sdk/v1/webhooks` |
76
76
  | `notifications` | Produto envia notification in-app | `/api/sdk/v1/notifications` |
77
77
 
78
+ ## Subpath exports
79
+
80
+ O barrel principal (`import ... from '@neetru/sdk'`) é universal — roda em browser, Node e Edge runtimes, sem nenhum Node builtin. Código Node-only ou React-only vive em **subpaths dedicados**, pra não vazar dependência pro bundle universal:
81
+
82
+ | Subpath | Conteúdo |
83
+ |---|---|
84
+ | `@neetru/sdk/auth` | Namespace de auth isolado |
85
+ | `@neetru/sdk/react` | Hooks React (`useUser`, etc) + componentes (`<NeetruLoginButton>`, `<CheckoutLink>`, `<EntitlementGate>`) |
86
+ | `@neetru/sdk/db` | Cliente de banco tenant-scoped |
87
+ | `@neetru/sdk/db/react` | Hooks React do namespace `db` |
88
+ | `@neetru/sdk/mocks` | `MockAuth`, `MockUsage`, `MockSupport`, etc — pra testes/dev |
89
+ | `@neetru/sdk/firestore-compat` | Camada de compat com API estilo Firestore |
90
+ | `@neetru/sdk/build-guard` | **Node-only** — `withNeetruBuildGuard`, ver abaixo |
91
+ | `@neetru/sdk/catalog`, `/entitlements`, `/telemetry`, `/usage`, `/support`, `/errors`, `/checkout`, `/webhooks`, `/notifications`, `/ai` | Cada namespace isolado, pra tree-shaking fino |
92
+
93
+ ### `withNeetruBuildGuard` — confirma o env compilado no build (3.1.15)
94
+
95
+ Fecha um bug de precedência do Next.js: `.env.local` (fica no disco do dev) vence `.env.production` durante `next build` pra qualquer chave em comum — um build "de prod" podia compilar com `NEXT_PUBLIC_NEETRU_ENV=dev` sem aviso, ativando `MockAuth` no bundle do browser em produção (o login real parava de funcionar silenciosamente).
96
+
97
+ ```js
98
+ // next.config.mjs
99
+ import { withNeetruBuildGuard } from '@neetru/sdk/build-guard';
100
+
101
+ export default withNeetruBuildGuard({
102
+ // ...seu nextConfig normal
103
+ });
104
+ ```
105
+
106
+ Grava `.next/neetru-build-manifest.json` com o `NEXT_PUBLIC_NEETRU_ENV`/`_PRODUCT_ID`/`_OIDC_CLIENT_ID` realmente resolvidos no build. `neetru build` (`@neetru/cli >= 2.26.2`) lê esse arquivo depois de compilar e **recusa empacotar o tarball** se o valor divergir do ambiente esperado do deploy — em vez de deployar um bundle silenciosamente quebrado.
107
+
108
+ Só disponível via `@neetru/sdk/build-guard` (não o barrel `.`) — é o único módulo do SDK com import estático de `node:fs`/`node:path`, mantido fora do bundle universal de propósito.
109
+
78
110
  ## Exemplos por namespace
79
111
 
80
112
  ### Login OIDC — Authorization Code + PKCE (3.1.3)
@@ -113,6 +145,36 @@ const user = await neetru.auth.verifyToken(idTokenDoCookie);
113
145
  `localStorage['neetru_id_token']` (alternativa SPA; o padrão recomendado é o
114
146
  cookie httpOnly do backend).
115
147
 
148
+ ### `<NeetruLoginButton>` — botão padronizado "Entrar com Neetru" (3.2.0)
149
+
150
+ Subpath `@neetru/sdk/react`. Botão pronto que chama `client.auth.signIn()` no
151
+ click — mesmo visual BMW do botão preto "Entrar com Google" que
152
+ `auth.neetru.com` usa hoje (altura, tipografia, tracking, `rounded-none`),
153
+ com o ícone-chama Neetru no lugar do ícone do provider. É O botão padrão que
154
+ qualquer produto SaaS Neetru deveria usar na própria tela de login, em vez
155
+ de cada um inventar o próprio estilo.
156
+
157
+ ```tsx
158
+ import { NeetruLoginButton } from '@neetru/sdk/react';
159
+
160
+ <NeetruLoginButton
161
+ client={neetru}
162
+ redirectUri="https://app.exemplo.com/auth/callback"
163
+ postLoginRedirect="/dashboard"
164
+ theme="dark" // ou "light" — ver abaixo
165
+ />
166
+ ```
167
+
168
+ - `theme`: `"dark"` (default, preto — CTA primário) ou `"light"` (branco com
169
+ borda preta — secundário, ou sobre fundo escuro).
170
+ - `label`: sobrescreve o texto (default `"Entrar com Neetru"`).
171
+ - `onError`: dispara se `signIn()` lançar ANTES do redirect (ex:
172
+ `oidcClientId` ausente, `sessionStorage`/WebCrypto indisponível). No
173
+ caminho feliz em browser real isso nunca dispara — a navegação interrompe
174
+ o JS antes de qualquer `catch`.
175
+ - Estilos são **inline** (não Tailwind) de propósito: o componente roda em
176
+ qualquer app consumidor do SDK, com ou sem Tailwind configurado.
177
+
116
178
  ### Webhooks outbound (v1.2)
117
179
 
118
180
  ```ts
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ function _interopNamespace(e) {
7
+ if (e && e.__esModule) return e;
8
+ var n = Object.create(null);
9
+ if (e) {
10
+ Object.keys(e).forEach(function (k) {
11
+ if (k !== 'default') {
12
+ var d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ });
19
+ }
20
+ n.default = e;
21
+ return Object.freeze(n);
22
+ }
23
+
24
+ var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
25
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
26
+
27
+ // src/build-guard.ts
28
+ function withNeetruBuildGuard(nextConfig, options = {}) {
29
+ const prevWebpack = nextConfig.webpack;
30
+ return {
31
+ ...nextConfig,
32
+ webpack(config, context) {
33
+ const result = typeof prevWebpack === "function" ? prevWebpack(config, context) : config;
34
+ if (!context.dev && context.isServer) {
35
+ try {
36
+ const outDir = path__namespace.resolve(process.cwd(), options.outputDir ?? ".next");
37
+ fs__namespace.mkdirSync(outDir, { recursive: true });
38
+ const manifest = {
39
+ builtEnv: process.env.NEXT_PUBLIC_NEETRU_ENV ?? null,
40
+ productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID ?? null,
41
+ oidcClientId: process.env.NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID ?? null,
42
+ builtAt: (/* @__PURE__ */ new Date()).toISOString()
43
+ };
44
+ fs__namespace.writeFileSync(
45
+ path__namespace.join(outDir, "neetru-build-manifest.json"),
46
+ JSON.stringify(manifest, null, 2),
47
+ "utf8"
48
+ );
49
+ } catch (err) {
50
+ if (typeof console !== "undefined") {
51
+ console.warn(
52
+ "[neetru-sdk] withNeetruBuildGuard: falha ao gravar neetru-build-manifest.json (ignorado, build segue):",
53
+ err
54
+ );
55
+ }
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+ };
61
+ }
62
+
63
+ exports.withNeetruBuildGuard = withNeetruBuildGuard;
64
+ //# sourceMappingURL=build-guard.cjs.map
65
+ //# sourceMappingURL=build-guard.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/build-guard.ts"],"names":["path","fs"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAwFO,SAAS,oBAAA,CACd,UAAA,EACA,OAAA,GAAuC,EAAC,EACrC;AACH,EAAA,MAAM,cAAc,UAAA,CAAW,OAAA;AAE/B,EAAA,OAAO;AAAA,IACL,GAAG,UAAA;AAAA,IACH,OAAA,CAAQ,QAAiB,OAAA,EAAyC;AAChE,MAAA,MAAM,SAAS,OAAO,WAAA,KAAgB,aAAa,WAAA,CAAY,MAAA,EAAQ,OAAO,CAAA,GAAI,MAAA;AAKlF,MAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,IAAO,OAAA,CAAQ,QAAA,EAAU;AACpC,QAAA,IAAI;AACF,UAAA,MAAM,SAAcA,eAAA,CAAA,OAAA,CAAQ,OAAA,CAAQ,KAAI,EAAG,OAAA,CAAQ,aAAa,OAAO,CAAA;AACvE,UAAGC,aAAA,CAAA,SAAA,CAAU,MAAA,EAAQ,EAAE,SAAA,EAAW,MAAM,CAAA;AACxC,UAAA,MAAM,QAAA,GAAgC;AAAA,YACpC,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,sBAAA,IAA0B,IAAA;AAAA,YAChD,SAAA,EAAW,OAAA,CAAQ,GAAA,CAAI,6BAAA,IAAiC,IAAA;AAAA,YACxD,YAAA,EAAc,OAAA,CAAQ,GAAA,CAAI,iCAAA,IAAqC,IAAA;AAAA,YAC/D,OAAA,EAAA,iBAAS,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,WAClC;AACA,UAAGA,aAAA,CAAA,aAAA;AAAA,YACID,eAAA,CAAA,IAAA,CAAK,QAAQ,4BAA4B,CAAA;AAAA,YAC9C,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM,CAAC,CAAA;AAAA,YAChC;AAAA,WACF;AAAA,QACF,SAAS,GAAA,EAAK;AAMZ,UAAA,IAAI,OAAO,YAAY,WAAA,EAAa;AAClC,YAAA,OAAA,CAAQ,IAAA;AAAA,cACN,wGAAA;AAAA,cACA;AAAA,aACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AACF","file":"build-guard.cjs","sourcesContent":["/**\r\n * `withNeetruBuildGuard` — wrapper de `next.config.mjs` que grava\r\n * `.next/neetru-build-manifest.json` ao final do build com os valores\r\n * `NEXT_PUBLIC_NEETRU_*` que realmente foram resolvidos pelo Next.js.\r\n *\r\n * Existe pra fechar um bug de precedência de env do Next.js (incidente\r\n * 2026-07-22, pdv-agiliza): `.env.local` (fica no disco do dev) vence\r\n * `.env.production` durante `next build` pra qualquer chave em comum —\r\n * então um build \"de prod\" podia compilar com `NEXT_PUBLIC_NEETRU_ENV=dev`\r\n * sem nenhum aviso, ativando `MockAuth` no bundle do browser em produção\r\n * (o botão \"Entrar com Neetru\" não fazia nada, porque `MockAuth.signIn()`\r\n * só finge um login em memória — nunca navega pro IdP real).\r\n *\r\n * O manifesto não inspeciona o JS compilado (minificação/webpack\r\n * `DefinePlugin` tornam isso frágil — o nome da chave some, só sobra o\r\n * literal). Em vez disso grava, em texto puro, o que o processo Node do\r\n * build efetivamente tinha em `process.env` no momento da compilação\r\n * server-side — reflete fielmente a resolução real do Next.js/dotenv,\r\n * incluindo qualquer override via spawn-env (`@neetru/cli` injeta\r\n * `NEXT_PUBLIC_NEETRU_ENV` assim desde a v2.26.2). `neetru build` lê este\r\n * arquivo depois do build e recusa empacotar o tarball se o valor não\r\n * bater com o ambiente esperado do deploy.\r\n *\r\n * Uso, em `next.config.mjs`:\r\n * ```js\r\n * import { withNeetruBuildGuard } from '@neetru/sdk/build-guard';\r\n * export default withNeetruBuildGuard({ ...nextConfig });\r\n * ```\r\n *\r\n * Subpath dedicado (`@neetru/sdk/build-guard`, não o barrel `.`) de\r\n * propósito: este arquivo é o único no SDK que importa `node:fs`/`node:path`\r\n * estaticamente. O entry `.` promete rodar em browser/Node/Edge sem Node\r\n * builtins — reexportar o guard ali vazaria essa dependência pro bundle\r\n * universal (achado real da revisão: `esbuild --platform=browser` falha com\r\n * \"Could not resolve fs/path\" se o guard entra no barrel principal).\r\n *\r\n * IMPORTANTE — isto NÃO é uma checagem universal de \"o env está certo\": o\r\n * guard grava literalmente as 3 env vars `NEXT_PUBLIC_NEETRU_ENV` /\r\n * `_PRODUCT_ID` / `_OIDC_CLIENT_ID`, que são a convenção recomendada de\r\n * bootstrap Next.js do ecossistema Neetru (o que `neetru-pdvagiliza` e\r\n * outros produtos usam em `src/lib/neetru/client.ts` pra montar\r\n * `createNeetruClient({env, productId, oidcClientId})`) — **não** é o mesmo\r\n * que o fallback interno `NEETRU_ENV`/`globalThis.NEETRU_ENV` que\r\n * `resolveEnv()` (`src/auth.ts`) usa quando NENHUM `env` é passado\r\n * explicitamente pro client. Se o seu produto bootstrap o client de outra\r\n * forma (nome de env var diferente, ou sem prefixo `NEXT_PUBLIC_`, ou\r\n * hardcoded), este guard não detecta drift pra esse caso — ele só confirma\r\n * que a convenção documentada acima foi respeitada.\r\n *\r\n * Composável com qualquer wrapper que já use a chave `webpack` do config\r\n * (ex: `next-pwa`) — a função `webpack` anterior, se houver, é chamada\r\n * primeiro e o retorno dela é repassado adiante sem alteração.\r\n */\r\nimport * as fs from 'node:fs';\r\nimport * as path from 'node:path';\r\n\r\n/** Formato do `.next/neetru-build-manifest.json`. */\r\nexport interface NeetruBuildManifest {\r\n /** `NEXT_PUBLIC_NEETRU_ENV` resolvido no momento do build (`dev|workspace|prod`), ou `null` se ausente. */\r\n builtEnv: string | null;\r\n /** `NEXT_PUBLIC_NEETRU_PRODUCT_ID` resolvido, ou `null`. */\r\n productId: string | null;\r\n /** `NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` resolvido, ou `null`. */\r\n oidcClientId: string | null;\r\n /** ISO timestamp de quando o manifesto foi gravado. */\r\n builtAt: string;\r\n}\r\n\r\nexport interface WithNeetruBuildGuardOptions {\r\n /** Diretório de saída do Next (default `.next`, relativo ao cwd do build). */\r\n outputDir?: string;\r\n}\r\n\r\n/**\r\n * Formato mínimo de `next.config.mjs` que o wrapper precisa entender — só\r\n * o suficiente pra compor a chave `webpack` sem acoplar o SDK ao pacote\r\n * `next` como dependência de tipos.\r\n */\r\ntype MinimalWebpackContext = {\r\n buildId: string;\r\n dev: boolean;\r\n isServer: boolean;\r\n [key: string]: unknown;\r\n};\r\ntype MinimalNextConfig = Record<string, unknown> & {\r\n webpack?: (config: unknown, context: MinimalWebpackContext) => unknown;\r\n};\r\n\r\nexport function withNeetruBuildGuard<T extends MinimalNextConfig>(\r\n nextConfig: T,\r\n options: WithNeetruBuildGuardOptions = {},\r\n): T {\r\n const prevWebpack = nextConfig.webpack;\r\n\r\n return {\r\n ...nextConfig,\r\n webpack(config: unknown, context: MinimalWebpackContext): unknown {\r\n const result = typeof prevWebpack === 'function' ? prevWebpack(config, context) : config;\r\n\r\n // Grava só na passada server (roda 1x por build, diferente da passada\r\n // client que pode rodar múltiplas vezes) e só fora de `next dev` —\r\n // `next build` é sempre produção de artefato de deploy.\r\n if (!context.dev && context.isServer) {\r\n try {\r\n const outDir = path.resolve(process.cwd(), options.outputDir ?? '.next');\r\n fs.mkdirSync(outDir, { recursive: true });\r\n const manifest: NeetruBuildManifest = {\r\n builtEnv: process.env.NEXT_PUBLIC_NEETRU_ENV ?? null,\r\n productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID ?? null,\r\n oidcClientId: process.env.NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID ?? null,\r\n builtAt: new Date().toISOString(),\r\n };\r\n fs.writeFileSync(\r\n path.join(outDir, 'neetru-build-manifest.json'),\r\n JSON.stringify(manifest, null, 2),\r\n 'utf8',\r\n );\r\n } catch (err) {\r\n // Best-effort — o guard nunca pode quebrar o build do produto,\r\n // mas uma falha de escrita silenciosa aqui esconderia o problema\r\n // (achado da revisão: sem log, ninguém percebe que o manifesto não\r\n // foi gravado e `neetru build` trata \"manifesto ausente\" como\r\n // aviso, não bloqueio — visibilidade aqui é a única forma de notar).\r\n if (typeof console !== 'undefined') {\r\n console.warn(\r\n '[neetru-sdk] withNeetruBuildGuard: falha ao gravar neetru-build-manifest.json (ignorado, build segue):',\r\n err,\r\n );\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n },\r\n } as T;\r\n}\r\n"]}
@@ -0,0 +1,32 @@
1
+ /** Formato do `.next/neetru-build-manifest.json`. */
2
+ interface NeetruBuildManifest {
3
+ /** `NEXT_PUBLIC_NEETRU_ENV` resolvido no momento do build (`dev|workspace|prod`), ou `null` se ausente. */
4
+ builtEnv: string | null;
5
+ /** `NEXT_PUBLIC_NEETRU_PRODUCT_ID` resolvido, ou `null`. */
6
+ productId: string | null;
7
+ /** `NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` resolvido, ou `null`. */
8
+ oidcClientId: string | null;
9
+ /** ISO timestamp de quando o manifesto foi gravado. */
10
+ builtAt: string;
11
+ }
12
+ interface WithNeetruBuildGuardOptions {
13
+ /** Diretório de saída do Next (default `.next`, relativo ao cwd do build). */
14
+ outputDir?: string;
15
+ }
16
+ /**
17
+ * Formato mínimo de `next.config.mjs` que o wrapper precisa entender — só
18
+ * o suficiente pra compor a chave `webpack` sem acoplar o SDK ao pacote
19
+ * `next` como dependência de tipos.
20
+ */
21
+ type MinimalWebpackContext = {
22
+ buildId: string;
23
+ dev: boolean;
24
+ isServer: boolean;
25
+ [key: string]: unknown;
26
+ };
27
+ type MinimalNextConfig = Record<string, unknown> & {
28
+ webpack?: (config: unknown, context: MinimalWebpackContext) => unknown;
29
+ };
30
+ declare function withNeetruBuildGuard<T extends MinimalNextConfig>(nextConfig: T, options?: WithNeetruBuildGuardOptions): T;
31
+
32
+ export { type NeetruBuildManifest, type WithNeetruBuildGuardOptions, withNeetruBuildGuard };
@@ -0,0 +1,32 @@
1
+ /** Formato do `.next/neetru-build-manifest.json`. */
2
+ interface NeetruBuildManifest {
3
+ /** `NEXT_PUBLIC_NEETRU_ENV` resolvido no momento do build (`dev|workspace|prod`), ou `null` se ausente. */
4
+ builtEnv: string | null;
5
+ /** `NEXT_PUBLIC_NEETRU_PRODUCT_ID` resolvido, ou `null`. */
6
+ productId: string | null;
7
+ /** `NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` resolvido, ou `null`. */
8
+ oidcClientId: string | null;
9
+ /** ISO timestamp de quando o manifesto foi gravado. */
10
+ builtAt: string;
11
+ }
12
+ interface WithNeetruBuildGuardOptions {
13
+ /** Diretório de saída do Next (default `.next`, relativo ao cwd do build). */
14
+ outputDir?: string;
15
+ }
16
+ /**
17
+ * Formato mínimo de `next.config.mjs` que o wrapper precisa entender — só
18
+ * o suficiente pra compor a chave `webpack` sem acoplar o SDK ao pacote
19
+ * `next` como dependência de tipos.
20
+ */
21
+ type MinimalWebpackContext = {
22
+ buildId: string;
23
+ dev: boolean;
24
+ isServer: boolean;
25
+ [key: string]: unknown;
26
+ };
27
+ type MinimalNextConfig = Record<string, unknown> & {
28
+ webpack?: (config: unknown, context: MinimalWebpackContext) => unknown;
29
+ };
30
+ declare function withNeetruBuildGuard<T extends MinimalNextConfig>(nextConfig: T, options?: WithNeetruBuildGuardOptions): T;
31
+
32
+ export { type NeetruBuildManifest, type WithNeetruBuildGuardOptions, withNeetruBuildGuard };
@@ -0,0 +1,42 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ // src/build-guard.ts
5
+ function withNeetruBuildGuard(nextConfig, options = {}) {
6
+ const prevWebpack = nextConfig.webpack;
7
+ return {
8
+ ...nextConfig,
9
+ webpack(config, context) {
10
+ const result = typeof prevWebpack === "function" ? prevWebpack(config, context) : config;
11
+ if (!context.dev && context.isServer) {
12
+ try {
13
+ const outDir = path.resolve(process.cwd(), options.outputDir ?? ".next");
14
+ fs.mkdirSync(outDir, { recursive: true });
15
+ const manifest = {
16
+ builtEnv: process.env.NEXT_PUBLIC_NEETRU_ENV ?? null,
17
+ productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID ?? null,
18
+ oidcClientId: process.env.NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID ?? null,
19
+ builtAt: (/* @__PURE__ */ new Date()).toISOString()
20
+ };
21
+ fs.writeFileSync(
22
+ path.join(outDir, "neetru-build-manifest.json"),
23
+ JSON.stringify(manifest, null, 2),
24
+ "utf8"
25
+ );
26
+ } catch (err) {
27
+ if (typeof console !== "undefined") {
28
+ console.warn(
29
+ "[neetru-sdk] withNeetruBuildGuard: falha ao gravar neetru-build-manifest.json (ignorado, build segue):",
30
+ err
31
+ );
32
+ }
33
+ }
34
+ }
35
+ return result;
36
+ }
37
+ };
38
+ }
39
+
40
+ export { withNeetruBuildGuard };
41
+ //# sourceMappingURL=build-guard.mjs.map
42
+ //# sourceMappingURL=build-guard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/build-guard.ts"],"names":[],"mappings":";;;;AAwFO,SAAS,oBAAA,CACd,UAAA,EACA,OAAA,GAAuC,EAAC,EACrC;AACH,EAAA,MAAM,cAAc,UAAA,CAAW,OAAA;AAE/B,EAAA,OAAO;AAAA,IACL,GAAG,UAAA;AAAA,IACH,OAAA,CAAQ,QAAiB,OAAA,EAAyC;AAChE,MAAA,MAAM,SAAS,OAAO,WAAA,KAAgB,aAAa,WAAA,CAAY,MAAA,EAAQ,OAAO,CAAA,GAAI,MAAA;AAKlF,MAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,IAAO,OAAA,CAAQ,QAAA,EAAU;AACpC,QAAA,IAAI;AACF,UAAA,MAAM,SAAc,IAAA,CAAA,OAAA,CAAQ,OAAA,CAAQ,KAAI,EAAG,OAAA,CAAQ,aAAa,OAAO,CAAA;AACvE,UAAG,EAAA,CAAA,SAAA,CAAU,MAAA,EAAQ,EAAE,SAAA,EAAW,MAAM,CAAA;AACxC,UAAA,MAAM,QAAA,GAAgC;AAAA,YACpC,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,sBAAA,IAA0B,IAAA;AAAA,YAChD,SAAA,EAAW,OAAA,CAAQ,GAAA,CAAI,6BAAA,IAAiC,IAAA;AAAA,YACxD,YAAA,EAAc,OAAA,CAAQ,GAAA,CAAI,iCAAA,IAAqC,IAAA;AAAA,YAC/D,OAAA,EAAA,iBAAS,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,WAClC;AACA,UAAG,EAAA,CAAA,aAAA;AAAA,YACI,IAAA,CAAA,IAAA,CAAK,QAAQ,4BAA4B,CAAA;AAAA,YAC9C,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM,CAAC,CAAA;AAAA,YAChC;AAAA,WACF;AAAA,QACF,SAAS,GAAA,EAAK;AAMZ,UAAA,IAAI,OAAO,YAAY,WAAA,EAAa;AAClC,YAAA,OAAA,CAAQ,IAAA;AAAA,cACN,wGAAA;AAAA,cACA;AAAA,aACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AACF","file":"build-guard.mjs","sourcesContent":["/**\r\n * `withNeetruBuildGuard` — wrapper de `next.config.mjs` que grava\r\n * `.next/neetru-build-manifest.json` ao final do build com os valores\r\n * `NEXT_PUBLIC_NEETRU_*` que realmente foram resolvidos pelo Next.js.\r\n *\r\n * Existe pra fechar um bug de precedência de env do Next.js (incidente\r\n * 2026-07-22, pdv-agiliza): `.env.local` (fica no disco do dev) vence\r\n * `.env.production` durante `next build` pra qualquer chave em comum —\r\n * então um build \"de prod\" podia compilar com `NEXT_PUBLIC_NEETRU_ENV=dev`\r\n * sem nenhum aviso, ativando `MockAuth` no bundle do browser em produção\r\n * (o botão \"Entrar com Neetru\" não fazia nada, porque `MockAuth.signIn()`\r\n * só finge um login em memória — nunca navega pro IdP real).\r\n *\r\n * O manifesto não inspeciona o JS compilado (minificação/webpack\r\n * `DefinePlugin` tornam isso frágil — o nome da chave some, só sobra o\r\n * literal). Em vez disso grava, em texto puro, o que o processo Node do\r\n * build efetivamente tinha em `process.env` no momento da compilação\r\n * server-side — reflete fielmente a resolução real do Next.js/dotenv,\r\n * incluindo qualquer override via spawn-env (`@neetru/cli` injeta\r\n * `NEXT_PUBLIC_NEETRU_ENV` assim desde a v2.26.2). `neetru build` lê este\r\n * arquivo depois do build e recusa empacotar o tarball se o valor não\r\n * bater com o ambiente esperado do deploy.\r\n *\r\n * Uso, em `next.config.mjs`:\r\n * ```js\r\n * import { withNeetruBuildGuard } from '@neetru/sdk/build-guard';\r\n * export default withNeetruBuildGuard({ ...nextConfig });\r\n * ```\r\n *\r\n * Subpath dedicado (`@neetru/sdk/build-guard`, não o barrel `.`) de\r\n * propósito: este arquivo é o único no SDK que importa `node:fs`/`node:path`\r\n * estaticamente. O entry `.` promete rodar em browser/Node/Edge sem Node\r\n * builtins — reexportar o guard ali vazaria essa dependência pro bundle\r\n * universal (achado real da revisão: `esbuild --platform=browser` falha com\r\n * \"Could not resolve fs/path\" se o guard entra no barrel principal).\r\n *\r\n * IMPORTANTE — isto NÃO é uma checagem universal de \"o env está certo\": o\r\n * guard grava literalmente as 3 env vars `NEXT_PUBLIC_NEETRU_ENV` /\r\n * `_PRODUCT_ID` / `_OIDC_CLIENT_ID`, que são a convenção recomendada de\r\n * bootstrap Next.js do ecossistema Neetru (o que `neetru-pdvagiliza` e\r\n * outros produtos usam em `src/lib/neetru/client.ts` pra montar\r\n * `createNeetruClient({env, productId, oidcClientId})`) — **não** é o mesmo\r\n * que o fallback interno `NEETRU_ENV`/`globalThis.NEETRU_ENV` que\r\n * `resolveEnv()` (`src/auth.ts`) usa quando NENHUM `env` é passado\r\n * explicitamente pro client. Se o seu produto bootstrap o client de outra\r\n * forma (nome de env var diferente, ou sem prefixo `NEXT_PUBLIC_`, ou\r\n * hardcoded), este guard não detecta drift pra esse caso — ele só confirma\r\n * que a convenção documentada acima foi respeitada.\r\n *\r\n * Composável com qualquer wrapper que já use a chave `webpack` do config\r\n * (ex: `next-pwa`) — a função `webpack` anterior, se houver, é chamada\r\n * primeiro e o retorno dela é repassado adiante sem alteração.\r\n */\r\nimport * as fs from 'node:fs';\r\nimport * as path from 'node:path';\r\n\r\n/** Formato do `.next/neetru-build-manifest.json`. */\r\nexport interface NeetruBuildManifest {\r\n /** `NEXT_PUBLIC_NEETRU_ENV` resolvido no momento do build (`dev|workspace|prod`), ou `null` se ausente. */\r\n builtEnv: string | null;\r\n /** `NEXT_PUBLIC_NEETRU_PRODUCT_ID` resolvido, ou `null`. */\r\n productId: string | null;\r\n /** `NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` resolvido, ou `null`. */\r\n oidcClientId: string | null;\r\n /** ISO timestamp de quando o manifesto foi gravado. */\r\n builtAt: string;\r\n}\r\n\r\nexport interface WithNeetruBuildGuardOptions {\r\n /** Diretório de saída do Next (default `.next`, relativo ao cwd do build). */\r\n outputDir?: string;\r\n}\r\n\r\n/**\r\n * Formato mínimo de `next.config.mjs` que o wrapper precisa entender — só\r\n * o suficiente pra compor a chave `webpack` sem acoplar o SDK ao pacote\r\n * `next` como dependência de tipos.\r\n */\r\ntype MinimalWebpackContext = {\r\n buildId: string;\r\n dev: boolean;\r\n isServer: boolean;\r\n [key: string]: unknown;\r\n};\r\ntype MinimalNextConfig = Record<string, unknown> & {\r\n webpack?: (config: unknown, context: MinimalWebpackContext) => unknown;\r\n};\r\n\r\nexport function withNeetruBuildGuard<T extends MinimalNextConfig>(\r\n nextConfig: T,\r\n options: WithNeetruBuildGuardOptions = {},\r\n): T {\r\n const prevWebpack = nextConfig.webpack;\r\n\r\n return {\r\n ...nextConfig,\r\n webpack(config: unknown, context: MinimalWebpackContext): unknown {\r\n const result = typeof prevWebpack === 'function' ? prevWebpack(config, context) : config;\r\n\r\n // Grava só na passada server (roda 1x por build, diferente da passada\r\n // client que pode rodar múltiplas vezes) e só fora de `next dev` —\r\n // `next build` é sempre produção de artefato de deploy.\r\n if (!context.dev && context.isServer) {\r\n try {\r\n const outDir = path.resolve(process.cwd(), options.outputDir ?? '.next');\r\n fs.mkdirSync(outDir, { recursive: true });\r\n const manifest: NeetruBuildManifest = {\r\n builtEnv: process.env.NEXT_PUBLIC_NEETRU_ENV ?? null,\r\n productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID ?? null,\r\n oidcClientId: process.env.NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID ?? null,\r\n builtAt: new Date().toISOString(),\r\n };\r\n fs.writeFileSync(\r\n path.join(outDir, 'neetru-build-manifest.json'),\r\n JSON.stringify(manifest, null, 2),\r\n 'utf8',\r\n );\r\n } catch (err) {\r\n // Best-effort — o guard nunca pode quebrar o build do produto,\r\n // mas uma falha de escrita silenciosa aqui esconderia o problema\r\n // (achado da revisão: sem log, ninguém percebe que o manifesto não\r\n // foi gravado e `neetru build` trata \"manifesto ausente\" como\r\n // aviso, não bloqueio — visibilidade aqui é a única forma de notar).\r\n if (typeof console !== 'undefined') {\r\n console.warn(\r\n '[neetru-sdk] withNeetruBuildGuard: falha ao gravar neetru-build-manifest.json (ignorado, build segue):',\r\n err,\r\n );\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n },\r\n } as T;\r\n}\r\n"]}
package/dist/index.cjs CHANGED
@@ -7122,7 +7122,7 @@ function createNeetruClient(config = {}) {
7122
7122
  init_errors();
7123
7123
  init_db_errors();
7124
7124
  init_http();
7125
- var VERSION = "3.1.13";
7125
+ var VERSION = "3.2.0";
7126
7126
 
7127
7127
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
7128
7128
  exports.DEV_FIXTURE_USER = DEV_FIXTURE_USER;