@neetru/sdk 3.4.1 → 3.4.2
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 +13 -0
- package/dist/auth.cjs +37 -8
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.mjs +37 -8
- package/dist/auth.mjs.map +1 -1
- package/dist/build-guard.cjs +41 -6
- package/dist/build-guard.cjs.map +1 -1
- package/dist/build-guard.d.cts +24 -1
- package/dist/build-guard.d.ts +24 -1
- package/dist/build-guard.mjs +40 -7
- package/dist/build-guard.mjs.map +1 -1
- package/dist/db.cjs +37 -8
- package/dist/db.cjs.map +1 -1
- package/dist/db.mjs +37 -8
- package/dist/db.mjs.map +1 -1
- package/dist/index.cjs +37 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +37 -8
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +593 -31
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +96 -1
- package/dist/react.d.ts +96 -1
- package/dist/react.mjs +590 -31
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/dist/build-guard.cjs
CHANGED
|
@@ -25,6 +25,38 @@ var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
|
25
25
|
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
26
26
|
|
|
27
27
|
// src/build-guard.ts
|
|
28
|
+
var NeetruBuildGuardError = class extends Error {
|
|
29
|
+
constructor(message) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = "NeetruBuildGuardError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function isBlankEnvValue(v) {
|
|
35
|
+
return v == null || v.trim() === "";
|
|
36
|
+
}
|
|
37
|
+
function assertNeetruProdBuildEnv(resolved) {
|
|
38
|
+
if (resolved.builtEnv !== "prod") return;
|
|
39
|
+
const missing = [];
|
|
40
|
+
if (isBlankEnvValue(resolved.oidcClientId)) missing.push("NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID");
|
|
41
|
+
if (isBlankEnvValue(resolved.productId)) missing.push("NEXT_PUBLIC_NEETRU_PRODUCT_ID");
|
|
42
|
+
if (missing.length === 0) return;
|
|
43
|
+
const plural = missing.length > 1;
|
|
44
|
+
throw new NeetruBuildGuardError(
|
|
45
|
+
[
|
|
46
|
+
"[neetru-sdk] BUILD DE PRODU\xC7\xC3O ABORTADO (withNeetruBuildGuard).",
|
|
47
|
+
"",
|
|
48
|
+
`Vari\xE1ve${plural ? "is" : "l"} de ambiente obrigat\xF3ri${plural ? "as" : "a"} ausente${plural ? "s" : ""}/vazia${plural ? "s" : ""} no build de produ\xE7\xE3o (NEXT_PUBLIC_NEETRU_ENV=prod):`,
|
|
49
|
+
...missing.map((v) => ` \u2022 ${v}`),
|
|
50
|
+
"",
|
|
51
|
+
`Sem ${plural ? "essas vari\xE1veis" : "essa vari\xE1vel"} o SDK n\xE3o consegue montar o client_id do login OIDC \u2014 o bot\xE3o "Entrar com Neetru" (NeetruLoginButton) N\xC3O redireciona e o login do produto quebra EM SIL\xCANCIO em produ\xE7\xE3o (incidente gestovendas 2026-09-06: workspace recriado perdeu a env var e shippou um build de prod com o client_id vazio).`,
|
|
52
|
+
"",
|
|
53
|
+
"Como corrigir \u2014 defina no .env.production do produto (ou no ambiente do build de deploy):",
|
|
54
|
+
...missing.map((v) => ` ${v}=<valor real vindo do cat\xE1logo do produto no Core>`),
|
|
55
|
+
"",
|
|
56
|
+
"dev/workspace N\xC3O exigem essas vari\xE1veis (usam MockAuth/sandbox) \u2014 s\xF3 o build de produ\xE7\xE3o enfor\xE7a."
|
|
57
|
+
].join("\n")
|
|
58
|
+
);
|
|
59
|
+
}
|
|
28
60
|
function withNeetruBuildGuard(nextConfig, options = {}) {
|
|
29
61
|
const prevWebpack = nextConfig.webpack;
|
|
30
62
|
return {
|
|
@@ -32,15 +64,15 @@ function withNeetruBuildGuard(nextConfig, options = {}) {
|
|
|
32
64
|
webpack(config, context) {
|
|
33
65
|
const result = typeof prevWebpack === "function" ? prevWebpack(config, context) : config;
|
|
34
66
|
if (!context.dev && context.isServer) {
|
|
67
|
+
const manifest = {
|
|
68
|
+
builtEnv: process.env.NEXT_PUBLIC_NEETRU_ENV ?? null,
|
|
69
|
+
productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID ?? null,
|
|
70
|
+
oidcClientId: process.env.NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID ?? null,
|
|
71
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
72
|
+
};
|
|
35
73
|
try {
|
|
36
74
|
const outDir = path__namespace.resolve(process.cwd(), options.outputDir ?? ".next");
|
|
37
75
|
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
76
|
fs__namespace.writeFileSync(
|
|
45
77
|
path__namespace.join(outDir, "neetru-build-manifest.json"),
|
|
46
78
|
JSON.stringify(manifest, null, 2),
|
|
@@ -54,12 +86,15 @@ function withNeetruBuildGuard(nextConfig, options = {}) {
|
|
|
54
86
|
);
|
|
55
87
|
}
|
|
56
88
|
}
|
|
89
|
+
assertNeetruProdBuildEnv(manifest);
|
|
57
90
|
}
|
|
58
91
|
return result;
|
|
59
92
|
}
|
|
60
93
|
};
|
|
61
94
|
}
|
|
62
95
|
|
|
96
|
+
exports.NeetruBuildGuardError = NeetruBuildGuardError;
|
|
97
|
+
exports.assertNeetruProdBuildEnv = assertNeetruProdBuildEnv;
|
|
63
98
|
exports.withNeetruBuildGuard = withNeetruBuildGuard;
|
|
64
99
|
//# sourceMappingURL=build-guard.cjs.map
|
|
65
100
|
//# sourceMappingURL=build-guard.cjs.map
|
package/dist/build-guard.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/build-guard.ts"],"names":["path","fs"],"mappings":"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","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 * FAIL-LOUD em produção (incidente gestovendas 2026-09-06): além de gravar o\r\n * manifesto, quando o env-alvo resolvido é **prod** o guard FALHA o build\r\n * (`throw NeetruBuildGuardError` → `next build` sai com código ≠ 0) se\r\n * `NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` ou `NEXT_PUBLIC_NEETRU_PRODUCT_ID`\r\n * estiver ausente/vazio. Sem esse par o SDK não monta o `client_id` do login\r\n * OIDC — o `NeetruLoginButton` não redireciona e o login quebra EM SILÊNCIO\r\n * em produção (foi exatamente o que aconteceu quando a recriação do workspace\r\n * do gestovendas perdeu a env var e shippou um build de prod com o client_id\r\n * vazio). `dev`/`workspace` NÃO exigem o par (usam MockAuth/sandbox) — só prod\r\n * enforça, pra nunca quebrar build local. A checagem de valor pura vive em\r\n * `assertNeetruProdBuildEnv` (exportada, testável sem rodar webpack).\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 * Erro lançado pelo guard quando um build de PRODUÇÃO está sem uma env var\r\n * obrigatória (OIDC client_id / productId). Classe dedicada pra o caller\r\n * poder distinguir \"guard reprovou de propósito\" de uma falha genérica de\r\n * build. Lançar dentro da função `webpack` do `next.config` faz o\r\n * `next build` sair com código ≠ 0 (fail-loud) — que é justamente o objetivo.\r\n */\r\nexport class NeetruBuildGuardError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = 'NeetruBuildGuardError';\r\n }\r\n}\r\n\r\n/** `''`/whitespace/`null`/`undefined` contam como \"ausente\". */\r\nfunction isBlankEnvValue(v: string | null | undefined): boolean {\r\n return v == null || v.trim() === '';\r\n}\r\n\r\n/**\r\n * Checagem pura (sem I/O, sem webpack) que decide se um build DEVE falhar por\r\n * env var obrigatória faltando. Exportada pra ser testável isoladamente e pra\r\n * o CLI poder reusar a mesma regra se quiser.\r\n *\r\n * Regra: SÓ o env-alvo `prod` exige o par OIDC completo\r\n * (`NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` + `NEXT_PUBLIC_NEETRU_PRODUCT_ID`).\r\n * `dev`/`workspace`/qualquer outro valor (ou ausência) NÃO enforçam — usam\r\n * MockAuth/sandbox e não montam o client OIDC real, então não podem quebrar\r\n * build local. Lança `NeetruBuildGuardError` com mensagem acionável quando\r\n * reprova; retorna `void` quando passa.\r\n */\r\nexport function assertNeetruProdBuildEnv(\r\n resolved: Pick<NeetruBuildManifest, 'builtEnv' | 'productId' | 'oidcClientId'>,\r\n): void {\r\n // Só produção monta o client_id OIDC real. dev/workspace seguem permissivos.\r\n if (resolved.builtEnv !== 'prod') return;\r\n\r\n const missing: string[] = [];\r\n if (isBlankEnvValue(resolved.oidcClientId)) missing.push('NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID');\r\n if (isBlankEnvValue(resolved.productId)) missing.push('NEXT_PUBLIC_NEETRU_PRODUCT_ID');\r\n if (missing.length === 0) return;\r\n\r\n const plural = missing.length > 1;\r\n throw new NeetruBuildGuardError(\r\n [\r\n '[neetru-sdk] BUILD DE PRODUÇÃO ABORTADO (withNeetruBuildGuard).',\r\n '',\r\n `Variáve${plural ? 'is' : 'l'} de ambiente obrigatóri${plural ? 'as' : 'a'} ausente${plural ? 's' : ''}/vazia${plural ? 's' : ''} no build de produção (NEXT_PUBLIC_NEETRU_ENV=prod):`,\r\n ...missing.map((v) => ` • ${v}`),\r\n '',\r\n `Sem ${plural ? 'essas variáveis' : 'essa variável'} o SDK não consegue montar o client_id do login OIDC — o botão \"Entrar com Neetru\" (NeetruLoginButton) NÃO redireciona e o login do produto quebra EM SILÊNCIO em produção (incidente gestovendas 2026-09-06: workspace recriado perdeu a env var e shippou um build de prod com o client_id vazio).`,\r\n '',\r\n 'Como corrigir — defina no .env.production do produto (ou no ambiente do build de deploy):',\r\n ...missing.map((v) => ` ${v}=<valor real vindo do catálogo do produto no Core>`),\r\n '',\r\n 'dev/workspace NÃO exigem essas variáveis (usam MockAuth/sandbox) — só o build de produção enforça.',\r\n ].join('\\n'),\r\n );\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 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\r\n try {\r\n const outDir = path.resolve(process.cwd(), options.outputDir ?? '.next');\r\n fs.mkdirSync(outDir, { recursive: true });\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 — a FALHA DE ESCRITA do manifesto nunca pode quebrar o\r\n // build do produto, mas uma falha silenciosa aqui esconderia o\r\n // problema (achado da revisão: sem log, ninguém percebe que o\r\n // manifesto não foi gravado e `neetru build` trata \"manifesto\r\n // ausente\" como aviso, não bloqueio — visibilidade aqui é a única\r\n // 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 // FAIL-LOUD (incidente gestovendas 2026-09-06): DEPOIS de gravar o\r\n // manifesto (best-effort) e FORA do try/catch acima — este throw é\r\n // DE PROPÓSITO e DEVE escapar pra falhar o `next build` (exit ≠ 0)\r\n // quando um build de produção está sem o client_id/productId do OIDC.\r\n // Só prod enforça; dev/workspace passam direto (ver\r\n // assertNeetruProdBuildEnv).\r\n assertNeetruProdBuildEnv(manifest);\r\n }\r\n\r\n return result;\r\n },\r\n } as T;\r\n}\r\n"]}
|
package/dist/build-guard.d.cts
CHANGED
|
@@ -13,6 +13,29 @@ interface WithNeetruBuildGuardOptions {
|
|
|
13
13
|
/** Diretório de saída do Next (default `.next`, relativo ao cwd do build). */
|
|
14
14
|
outputDir?: string;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Erro lançado pelo guard quando um build de PRODUÇÃO está sem uma env var
|
|
18
|
+
* obrigatória (OIDC client_id / productId). Classe dedicada pra o caller
|
|
19
|
+
* poder distinguir "guard reprovou de propósito" de uma falha genérica de
|
|
20
|
+
* build. Lançar dentro da função `webpack` do `next.config` faz o
|
|
21
|
+
* `next build` sair com código ≠ 0 (fail-loud) — que é justamente o objetivo.
|
|
22
|
+
*/
|
|
23
|
+
declare class NeetruBuildGuardError extends Error {
|
|
24
|
+
constructor(message: string);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Checagem pura (sem I/O, sem webpack) que decide se um build DEVE falhar por
|
|
28
|
+
* env var obrigatória faltando. Exportada pra ser testável isoladamente e pra
|
|
29
|
+
* o CLI poder reusar a mesma regra se quiser.
|
|
30
|
+
*
|
|
31
|
+
* Regra: SÓ o env-alvo `prod` exige o par OIDC completo
|
|
32
|
+
* (`NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` + `NEXT_PUBLIC_NEETRU_PRODUCT_ID`).
|
|
33
|
+
* `dev`/`workspace`/qualquer outro valor (ou ausência) NÃO enforçam — usam
|
|
34
|
+
* MockAuth/sandbox e não montam o client OIDC real, então não podem quebrar
|
|
35
|
+
* build local. Lança `NeetruBuildGuardError` com mensagem acionável quando
|
|
36
|
+
* reprova; retorna `void` quando passa.
|
|
37
|
+
*/
|
|
38
|
+
declare function assertNeetruProdBuildEnv(resolved: Pick<NeetruBuildManifest, 'builtEnv' | 'productId' | 'oidcClientId'>): void;
|
|
16
39
|
/**
|
|
17
40
|
* Formato mínimo de `next.config.mjs` que o wrapper precisa entender — só
|
|
18
41
|
* o suficiente pra compor a chave `webpack` sem acoplar o SDK ao pacote
|
|
@@ -29,4 +52,4 @@ type MinimalNextConfig = Record<string, unknown> & {
|
|
|
29
52
|
};
|
|
30
53
|
declare function withNeetruBuildGuard<T extends MinimalNextConfig>(nextConfig: T, options?: WithNeetruBuildGuardOptions): T;
|
|
31
54
|
|
|
32
|
-
export { type NeetruBuildManifest, type WithNeetruBuildGuardOptions, withNeetruBuildGuard };
|
|
55
|
+
export { NeetruBuildGuardError, type NeetruBuildManifest, type WithNeetruBuildGuardOptions, assertNeetruProdBuildEnv, withNeetruBuildGuard };
|
package/dist/build-guard.d.ts
CHANGED
|
@@ -13,6 +13,29 @@ interface WithNeetruBuildGuardOptions {
|
|
|
13
13
|
/** Diretório de saída do Next (default `.next`, relativo ao cwd do build). */
|
|
14
14
|
outputDir?: string;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Erro lançado pelo guard quando um build de PRODUÇÃO está sem uma env var
|
|
18
|
+
* obrigatória (OIDC client_id / productId). Classe dedicada pra o caller
|
|
19
|
+
* poder distinguir "guard reprovou de propósito" de uma falha genérica de
|
|
20
|
+
* build. Lançar dentro da função `webpack` do `next.config` faz o
|
|
21
|
+
* `next build` sair com código ≠ 0 (fail-loud) — que é justamente o objetivo.
|
|
22
|
+
*/
|
|
23
|
+
declare class NeetruBuildGuardError extends Error {
|
|
24
|
+
constructor(message: string);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Checagem pura (sem I/O, sem webpack) que decide se um build DEVE falhar por
|
|
28
|
+
* env var obrigatória faltando. Exportada pra ser testável isoladamente e pra
|
|
29
|
+
* o CLI poder reusar a mesma regra se quiser.
|
|
30
|
+
*
|
|
31
|
+
* Regra: SÓ o env-alvo `prod` exige o par OIDC completo
|
|
32
|
+
* (`NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` + `NEXT_PUBLIC_NEETRU_PRODUCT_ID`).
|
|
33
|
+
* `dev`/`workspace`/qualquer outro valor (ou ausência) NÃO enforçam — usam
|
|
34
|
+
* MockAuth/sandbox e não montam o client OIDC real, então não podem quebrar
|
|
35
|
+
* build local. Lança `NeetruBuildGuardError` com mensagem acionável quando
|
|
36
|
+
* reprova; retorna `void` quando passa.
|
|
37
|
+
*/
|
|
38
|
+
declare function assertNeetruProdBuildEnv(resolved: Pick<NeetruBuildManifest, 'builtEnv' | 'productId' | 'oidcClientId'>): void;
|
|
16
39
|
/**
|
|
17
40
|
* Formato mínimo de `next.config.mjs` que o wrapper precisa entender — só
|
|
18
41
|
* o suficiente pra compor a chave `webpack` sem acoplar o SDK ao pacote
|
|
@@ -29,4 +52,4 @@ type MinimalNextConfig = Record<string, unknown> & {
|
|
|
29
52
|
};
|
|
30
53
|
declare function withNeetruBuildGuard<T extends MinimalNextConfig>(nextConfig: T, options?: WithNeetruBuildGuardOptions): T;
|
|
31
54
|
|
|
32
|
-
export { type NeetruBuildManifest, type WithNeetruBuildGuardOptions, withNeetruBuildGuard };
|
|
55
|
+
export { NeetruBuildGuardError, type NeetruBuildManifest, type WithNeetruBuildGuardOptions, assertNeetruProdBuildEnv, withNeetruBuildGuard };
|
package/dist/build-guard.mjs
CHANGED
|
@@ -2,6 +2,38 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
4
|
// src/build-guard.ts
|
|
5
|
+
var NeetruBuildGuardError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "NeetruBuildGuardError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function isBlankEnvValue(v) {
|
|
12
|
+
return v == null || v.trim() === "";
|
|
13
|
+
}
|
|
14
|
+
function assertNeetruProdBuildEnv(resolved) {
|
|
15
|
+
if (resolved.builtEnv !== "prod") return;
|
|
16
|
+
const missing = [];
|
|
17
|
+
if (isBlankEnvValue(resolved.oidcClientId)) missing.push("NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID");
|
|
18
|
+
if (isBlankEnvValue(resolved.productId)) missing.push("NEXT_PUBLIC_NEETRU_PRODUCT_ID");
|
|
19
|
+
if (missing.length === 0) return;
|
|
20
|
+
const plural = missing.length > 1;
|
|
21
|
+
throw new NeetruBuildGuardError(
|
|
22
|
+
[
|
|
23
|
+
"[neetru-sdk] BUILD DE PRODU\xC7\xC3O ABORTADO (withNeetruBuildGuard).",
|
|
24
|
+
"",
|
|
25
|
+
`Vari\xE1ve${plural ? "is" : "l"} de ambiente obrigat\xF3ri${plural ? "as" : "a"} ausente${plural ? "s" : ""}/vazia${plural ? "s" : ""} no build de produ\xE7\xE3o (NEXT_PUBLIC_NEETRU_ENV=prod):`,
|
|
26
|
+
...missing.map((v) => ` \u2022 ${v}`),
|
|
27
|
+
"",
|
|
28
|
+
`Sem ${plural ? "essas vari\xE1veis" : "essa vari\xE1vel"} o SDK n\xE3o consegue montar o client_id do login OIDC \u2014 o bot\xE3o "Entrar com Neetru" (NeetruLoginButton) N\xC3O redireciona e o login do produto quebra EM SIL\xCANCIO em produ\xE7\xE3o (incidente gestovendas 2026-09-06: workspace recriado perdeu a env var e shippou um build de prod com o client_id vazio).`,
|
|
29
|
+
"",
|
|
30
|
+
"Como corrigir \u2014 defina no .env.production do produto (ou no ambiente do build de deploy):",
|
|
31
|
+
...missing.map((v) => ` ${v}=<valor real vindo do cat\xE1logo do produto no Core>`),
|
|
32
|
+
"",
|
|
33
|
+
"dev/workspace N\xC3O exigem essas vari\xE1veis (usam MockAuth/sandbox) \u2014 s\xF3 o build de produ\xE7\xE3o enfor\xE7a."
|
|
34
|
+
].join("\n")
|
|
35
|
+
);
|
|
36
|
+
}
|
|
5
37
|
function withNeetruBuildGuard(nextConfig, options = {}) {
|
|
6
38
|
const prevWebpack = nextConfig.webpack;
|
|
7
39
|
return {
|
|
@@ -9,15 +41,15 @@ function withNeetruBuildGuard(nextConfig, options = {}) {
|
|
|
9
41
|
webpack(config, context) {
|
|
10
42
|
const result = typeof prevWebpack === "function" ? prevWebpack(config, context) : config;
|
|
11
43
|
if (!context.dev && context.isServer) {
|
|
44
|
+
const manifest = {
|
|
45
|
+
builtEnv: process.env.NEXT_PUBLIC_NEETRU_ENV ?? null,
|
|
46
|
+
productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID ?? null,
|
|
47
|
+
oidcClientId: process.env.NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID ?? null,
|
|
48
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49
|
+
};
|
|
12
50
|
try {
|
|
13
51
|
const outDir = path.resolve(process.cwd(), options.outputDir ?? ".next");
|
|
14
52
|
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
53
|
fs.writeFileSync(
|
|
22
54
|
path.join(outDir, "neetru-build-manifest.json"),
|
|
23
55
|
JSON.stringify(manifest, null, 2),
|
|
@@ -31,12 +63,13 @@ function withNeetruBuildGuard(nextConfig, options = {}) {
|
|
|
31
63
|
);
|
|
32
64
|
}
|
|
33
65
|
}
|
|
66
|
+
assertNeetruProdBuildEnv(manifest);
|
|
34
67
|
}
|
|
35
68
|
return result;
|
|
36
69
|
}
|
|
37
70
|
};
|
|
38
71
|
}
|
|
39
72
|
|
|
40
|
-
export { withNeetruBuildGuard };
|
|
73
|
+
export { NeetruBuildGuardError, assertNeetruProdBuildEnv, withNeetruBuildGuard };
|
|
41
74
|
//# sourceMappingURL=build-guard.mjs.map
|
|
42
75
|
//# sourceMappingURL=build-guard.mjs.map
|
package/dist/build-guard.mjs.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/build-guard.ts"],"names":[],"mappings":"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","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 * FAIL-LOUD em produção (incidente gestovendas 2026-09-06): além de gravar o\r\n * manifesto, quando o env-alvo resolvido é **prod** o guard FALHA o build\r\n * (`throw NeetruBuildGuardError` → `next build` sai com código ≠ 0) se\r\n * `NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` ou `NEXT_PUBLIC_NEETRU_PRODUCT_ID`\r\n * estiver ausente/vazio. Sem esse par o SDK não monta o `client_id` do login\r\n * OIDC — o `NeetruLoginButton` não redireciona e o login quebra EM SILÊNCIO\r\n * em produção (foi exatamente o que aconteceu quando a recriação do workspace\r\n * do gestovendas perdeu a env var e shippou um build de prod com o client_id\r\n * vazio). `dev`/`workspace` NÃO exigem o par (usam MockAuth/sandbox) — só prod\r\n * enforça, pra nunca quebrar build local. A checagem de valor pura vive em\r\n * `assertNeetruProdBuildEnv` (exportada, testável sem rodar webpack).\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 * Erro lançado pelo guard quando um build de PRODUÇÃO está sem uma env var\r\n * obrigatória (OIDC client_id / productId). Classe dedicada pra o caller\r\n * poder distinguir \"guard reprovou de propósito\" de uma falha genérica de\r\n * build. Lançar dentro da função `webpack` do `next.config` faz o\r\n * `next build` sair com código ≠ 0 (fail-loud) — que é justamente o objetivo.\r\n */\r\nexport class NeetruBuildGuardError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = 'NeetruBuildGuardError';\r\n }\r\n}\r\n\r\n/** `''`/whitespace/`null`/`undefined` contam como \"ausente\". */\r\nfunction isBlankEnvValue(v: string | null | undefined): boolean {\r\n return v == null || v.trim() === '';\r\n}\r\n\r\n/**\r\n * Checagem pura (sem I/O, sem webpack) que decide se um build DEVE falhar por\r\n * env var obrigatória faltando. Exportada pra ser testável isoladamente e pra\r\n * o CLI poder reusar a mesma regra se quiser.\r\n *\r\n * Regra: SÓ o env-alvo `prod` exige o par OIDC completo\r\n * (`NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID` + `NEXT_PUBLIC_NEETRU_PRODUCT_ID`).\r\n * `dev`/`workspace`/qualquer outro valor (ou ausência) NÃO enforçam — usam\r\n * MockAuth/sandbox e não montam o client OIDC real, então não podem quebrar\r\n * build local. Lança `NeetruBuildGuardError` com mensagem acionável quando\r\n * reprova; retorna `void` quando passa.\r\n */\r\nexport function assertNeetruProdBuildEnv(\r\n resolved: Pick<NeetruBuildManifest, 'builtEnv' | 'productId' | 'oidcClientId'>,\r\n): void {\r\n // Só produção monta o client_id OIDC real. dev/workspace seguem permissivos.\r\n if (resolved.builtEnv !== 'prod') return;\r\n\r\n const missing: string[] = [];\r\n if (isBlankEnvValue(resolved.oidcClientId)) missing.push('NEXT_PUBLIC_NEETRU_OIDC_CLIENT_ID');\r\n if (isBlankEnvValue(resolved.productId)) missing.push('NEXT_PUBLIC_NEETRU_PRODUCT_ID');\r\n if (missing.length === 0) return;\r\n\r\n const plural = missing.length > 1;\r\n throw new NeetruBuildGuardError(\r\n [\r\n '[neetru-sdk] BUILD DE PRODUÇÃO ABORTADO (withNeetruBuildGuard).',\r\n '',\r\n `Variáve${plural ? 'is' : 'l'} de ambiente obrigatóri${plural ? 'as' : 'a'} ausente${plural ? 's' : ''}/vazia${plural ? 's' : ''} no build de produção (NEXT_PUBLIC_NEETRU_ENV=prod):`,\r\n ...missing.map((v) => ` • ${v}`),\r\n '',\r\n `Sem ${plural ? 'essas variáveis' : 'essa variável'} o SDK não consegue montar o client_id do login OIDC — o botão \"Entrar com Neetru\" (NeetruLoginButton) NÃO redireciona e o login do produto quebra EM SILÊNCIO em produção (incidente gestovendas 2026-09-06: workspace recriado perdeu a env var e shippou um build de prod com o client_id vazio).`,\r\n '',\r\n 'Como corrigir — defina no .env.production do produto (ou no ambiente do build de deploy):',\r\n ...missing.map((v) => ` ${v}=<valor real vindo do catálogo do produto no Core>`),\r\n '',\r\n 'dev/workspace NÃO exigem essas variáveis (usam MockAuth/sandbox) — só o build de produção enforça.',\r\n ].join('\\n'),\r\n );\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 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\r\n try {\r\n const outDir = path.resolve(process.cwd(), options.outputDir ?? '.next');\r\n fs.mkdirSync(outDir, { recursive: true });\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 — a FALHA DE ESCRITA do manifesto nunca pode quebrar o\r\n // build do produto, mas uma falha silenciosa aqui esconderia o\r\n // problema (achado da revisão: sem log, ninguém percebe que o\r\n // manifesto não foi gravado e `neetru build` trata \"manifesto\r\n // ausente\" como aviso, não bloqueio — visibilidade aqui é a única\r\n // 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 // FAIL-LOUD (incidente gestovendas 2026-09-06): DEPOIS de gravar o\r\n // manifesto (best-effort) e FORA do try/catch acima — este throw é\r\n // DE PROPÓSITO e DEVE escapar pra falhar o `next build` (exit ≠ 0)\r\n // quando um build de produção está sem o client_id/productId do OIDC.\r\n // Só prod enforça; dev/workspace passam direto (ver\r\n // assertNeetruProdBuildEnv).\r\n assertNeetruProdBuildEnv(manifest);\r\n }\r\n\r\n return result;\r\n },\r\n } as T;\r\n}\r\n"]}
|
package/dist/db.cjs
CHANGED
|
@@ -683,13 +683,17 @@ var init_lease = __esm({
|
|
|
683
683
|
// src/db/sql/sql-client.ts
|
|
684
684
|
var sql_client_exports = {};
|
|
685
685
|
__export(sql_client_exports, {
|
|
686
|
+
DEFAULT_POOL_CONNECT_TIMEOUT_MS: () => DEFAULT_POOL_CONNECT_TIMEOUT_MS,
|
|
687
|
+
DEFAULT_POOL_IDLE_MS: () => DEFAULT_POOL_IDLE_MS,
|
|
688
|
+
DEFAULT_POOL_MAX: () => DEFAULT_POOL_MAX,
|
|
686
689
|
LEASE_ENDPOINT: () => LEASE_ENDPOINT,
|
|
687
690
|
LEASE_RENEW_ENDPOINT: () => LEASE_RENEW_ENDPOINT,
|
|
688
691
|
createDrizzleHandle: () => createDrizzleHandle,
|
|
689
692
|
createHttpLeaseFetcher: () => createHttpLeaseFetcher,
|
|
690
693
|
createPgPool: () => createPgPool,
|
|
691
694
|
createSqlClientFromConfig: () => createSqlClientFromConfig,
|
|
692
|
-
createSqlClientWithDeps: () => createSqlClientWithDeps
|
|
695
|
+
createSqlClientWithDeps: () => createSqlClientWithDeps,
|
|
696
|
+
resolvePoolTuning: () => resolvePoolTuning
|
|
693
697
|
});
|
|
694
698
|
function mapEnvToCore(sdkEnv) {
|
|
695
699
|
if (sdkEnv === "workspace") return "staging";
|
|
@@ -778,6 +782,30 @@ function parseLease(raw) {
|
|
|
778
782
|
expiresAt: req("expiresAt", "string")
|
|
779
783
|
};
|
|
780
784
|
}
|
|
785
|
+
function readEnvInt(name, def, min, max) {
|
|
786
|
+
const raw = typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
787
|
+
if (raw === void 0 || raw.trim() === "") return def;
|
|
788
|
+
const n = Number.parseInt(raw.trim(), 10);
|
|
789
|
+
if (!Number.isFinite(n) || n < min || n > max) return def;
|
|
790
|
+
return n;
|
|
791
|
+
}
|
|
792
|
+
function resolvePoolTuning() {
|
|
793
|
+
return {
|
|
794
|
+
// Teto 100: bem abaixo do `max_client_conn = 500` do PgBouncer, com folga
|
|
795
|
+
// para múltiplas instâncias do app dividirem o mesmo front-door.
|
|
796
|
+
max: readEnvInt("NEETRU_DB_POOL_MAX", DEFAULT_POOL_MAX, 1, 100),
|
|
797
|
+
// Teto 15min = janela do cert; piso 1s.
|
|
798
|
+
idleTimeoutMillis: readEnvInt("NEETRU_DB_POOL_IDLE_MS", DEFAULT_POOL_IDLE_MS, 1e3, 9e5),
|
|
799
|
+
connectionTimeoutMillis: readEnvInt(
|
|
800
|
+
"NEETRU_DB_POOL_CONNECT_TIMEOUT_MS",
|
|
801
|
+
DEFAULT_POOL_CONNECT_TIMEOUT_MS,
|
|
802
|
+
1e3,
|
|
803
|
+
12e4
|
|
804
|
+
),
|
|
805
|
+
keepAlive: true,
|
|
806
|
+
keepAliveInitialDelayMillis: 1e4
|
|
807
|
+
};
|
|
808
|
+
}
|
|
781
809
|
async function createPgPool(lease) {
|
|
782
810
|
const { Pool } = await import(
|
|
783
811
|
/* webpackIgnore: true */
|
|
@@ -796,9 +824,7 @@ async function createPgPool(lease) {
|
|
|
796
824
|
user: lease.user,
|
|
797
825
|
password: lease.password,
|
|
798
826
|
ssl,
|
|
799
|
-
|
|
800
|
-
idleTimeoutMillis: 3e4,
|
|
801
|
-
connectionTimeoutMillis: 1e4
|
|
827
|
+
...resolvePoolTuning()
|
|
802
828
|
});
|
|
803
829
|
}
|
|
804
830
|
async function createDrizzleHandle(pool, schema) {
|
|
@@ -933,9 +959,9 @@ async function createSqlClientFromConfig(config, schema, options) {
|
|
|
933
959
|
user: lease.user,
|
|
934
960
|
password: lease.password,
|
|
935
961
|
ssl,
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
962
|
+
// Tuning centralizado (env-tunável) — ver resolvePoolTuning + auditoria
|
|
963
|
+
// docs/_review/AUDITORIA_DB_CONN_PERF_2026-09-06.md no Core.
|
|
964
|
+
...resolvePoolTuning()
|
|
939
965
|
});
|
|
940
966
|
};
|
|
941
967
|
const createDrizzle = (pool, s) => {
|
|
@@ -958,7 +984,7 @@ async function createSqlClientFromConfig(config, schema, options) {
|
|
|
958
984
|
options
|
|
959
985
|
);
|
|
960
986
|
}
|
|
961
|
-
var LEASE_ENDPOINT, LEASE_RENEW_ENDPOINT;
|
|
987
|
+
var LEASE_ENDPOINT, LEASE_RENEW_ENDPOINT, DEFAULT_POOL_MAX, DEFAULT_POOL_IDLE_MS, DEFAULT_POOL_CONNECT_TIMEOUT_MS;
|
|
962
988
|
var init_sql_client = __esm({
|
|
963
989
|
"src/db/sql/sql-client.ts"() {
|
|
964
990
|
init_db_errors();
|
|
@@ -967,6 +993,9 @@ var init_sql_client = __esm({
|
|
|
967
993
|
init_resilient_pool();
|
|
968
994
|
LEASE_ENDPOINT = "/api/sdk/v1/db/lease";
|
|
969
995
|
LEASE_RENEW_ENDPOINT = "/api/sdk/v1/db/lease/renew";
|
|
996
|
+
DEFAULT_POOL_MAX = 10;
|
|
997
|
+
DEFAULT_POOL_IDLE_MS = 6e4;
|
|
998
|
+
DEFAULT_POOL_CONNECT_TIMEOUT_MS = 1e4;
|
|
970
999
|
}
|
|
971
1000
|
});
|
|
972
1001
|
|