@neetru/sdk 3.4.2 → 3.4.4

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 (58) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/ai.d.cts +1 -1
  3. package/dist/ai.d.ts +1 -1
  4. package/dist/auth.cjs +17 -7
  5. package/dist/auth.cjs.map +1 -1
  6. package/dist/auth.d.cts +1 -1
  7. package/dist/auth.d.ts +1 -1
  8. package/dist/auth.mjs +17 -7
  9. package/dist/auth.mjs.map +1 -1
  10. package/dist/build-guard.cjs +98 -0
  11. package/dist/build-guard.cjs.map +1 -1
  12. package/dist/build-guard.d.cts +63 -1
  13. package/dist/build-guard.d.ts +63 -1
  14. package/dist/build-guard.mjs +93 -1
  15. package/dist/build-guard.mjs.map +1 -1
  16. package/dist/catalog.d.cts +1 -1
  17. package/dist/catalog.d.ts +1 -1
  18. package/dist/checkout.cjs +17 -7
  19. package/dist/checkout.cjs.map +1 -1
  20. package/dist/checkout.d.cts +1 -1
  21. package/dist/checkout.d.ts +1 -1
  22. package/dist/checkout.mjs +17 -7
  23. package/dist/checkout.mjs.map +1 -1
  24. package/dist/db-react.d.cts +1 -1
  25. package/dist/db-react.d.ts +1 -1
  26. package/dist/db.d.cts +1 -1
  27. package/dist/db.d.ts +1 -1
  28. package/dist/entitlements.d.cts +1 -1
  29. package/dist/entitlements.d.ts +1 -1
  30. package/dist/firestore-compat.d.cts +1 -1
  31. package/dist/firestore-compat.d.ts +1 -1
  32. package/dist/index.cjs +18 -8
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +2 -2
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.mjs +18 -8
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/mocks.d.cts +1 -1
  39. package/dist/mocks.d.ts +1 -1
  40. package/dist/notifications.d.cts +1 -1
  41. package/dist/notifications.d.ts +1 -1
  42. package/dist/react.cjs +62 -3
  43. package/dist/react.cjs.map +1 -1
  44. package/dist/react.d.cts +38 -7
  45. package/dist/react.d.ts +38 -7
  46. package/dist/react.mjs +62 -3
  47. package/dist/react.mjs.map +1 -1
  48. package/dist/support.d.cts +1 -1
  49. package/dist/support.d.ts +1 -1
  50. package/dist/telemetry.d.cts +1 -1
  51. package/dist/telemetry.d.ts +1 -1
  52. package/dist/{types-BlOLDMR0.d.ts → types-Bzxb5rlF.d.ts} +26 -8
  53. package/dist/{types-D-QdG502.d.cts → types-Dw0fqhHm.d.cts} +26 -8
  54. package/dist/usage.d.cts +1 -1
  55. package/dist/usage.d.ts +1 -1
  56. package/dist/webhooks.d.cts +1 -1
  57. package/dist/webhooks.d.ts +1 -1
  58. package/package.json +1 -1
@@ -57,10 +57,102 @@ function assertNeetruProdBuildEnv(resolved) {
57
57
  ].join("\n")
58
58
  );
59
59
  }
60
+ var NEETRU_SQL_SERVER_EXTERNAL_PACKAGES = ["pg", "drizzle-orm"];
61
+ var NEETRU_SQL_TRACE_INCLUDE_KEY = "*";
62
+ function toStringArray(value) {
63
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
64
+ }
65
+ function findPackageJsonPath(pkgName, parentDir, fromDir) {
66
+ const bases = [];
67
+ let dir = parentDir;
68
+ for (; ; ) {
69
+ bases.push(dir);
70
+ const parent = path__namespace.dirname(dir);
71
+ if (parent === dir) break;
72
+ dir = parent;
73
+ }
74
+ if (!bases.includes(fromDir)) bases.push(fromDir);
75
+ for (const base of bases) {
76
+ const candidate = path__namespace.join(base, "node_modules", pkgName, "package.json");
77
+ try {
78
+ if (fs__namespace.existsSync(candidate)) return candidate;
79
+ } catch {
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+ function resolveSqlRuntimeClosure(fromDir) {
85
+ if (typeof fromDir !== "string" || fromDir.trim() === "") return [];
86
+ const visited = /* @__PURE__ */ new Set();
87
+ const closure = /* @__PURE__ */ new Set();
88
+ const walk = (pkgName, parentDir) => {
89
+ if (visited.has(pkgName)) return;
90
+ visited.add(pkgName);
91
+ const pkgJsonPath = findPackageJsonPath(pkgName, parentDir, fromDir);
92
+ if (!pkgJsonPath) return;
93
+ closure.add(pkgName);
94
+ let manifest;
95
+ try {
96
+ manifest = JSON.parse(fs__namespace.readFileSync(pkgJsonPath, "utf8"));
97
+ } catch {
98
+ return;
99
+ }
100
+ const deps = {
101
+ ...manifest.dependencies ?? {},
102
+ ...manifest.optionalDependencies ?? {}
103
+ };
104
+ const pkgDir = path__namespace.dirname(pkgJsonPath);
105
+ for (const dep of Object.keys(deps)) walk(dep, pkgDir);
106
+ };
107
+ try {
108
+ for (const seed of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) walk(seed, fromDir);
109
+ } catch {
110
+ return [];
111
+ }
112
+ return [...closure].sort();
113
+ }
114
+ function sqlClosureToTraceGlobs(closure) {
115
+ return closure.map((pkg) => `./node_modules/${pkg}/**`);
116
+ }
117
+ function mergeServerExternalPackages(existing) {
118
+ const out = toStringArray(existing);
119
+ for (const pkg of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) {
120
+ if (!out.includes(pkg)) out.push(pkg);
121
+ }
122
+ return out;
123
+ }
124
+ function mergeOutputFileTracingIncludes(existing, globs) {
125
+ const merged = {};
126
+ if (existing && typeof existing === "object" && !Array.isArray(existing)) {
127
+ for (const [key, value] of Object.entries(existing)) {
128
+ merged[key] = toStringArray(value);
129
+ }
130
+ }
131
+ if (globs.length === 0) return merged;
132
+ const combined = [...merged[NEETRU_SQL_TRACE_INCLUDE_KEY] ?? []];
133
+ for (const glob of globs) {
134
+ if (!combined.includes(glob)) combined.push(glob);
135
+ }
136
+ merged[NEETRU_SQL_TRACE_INCLUDE_KEY] = combined;
137
+ return merged;
138
+ }
60
139
  function withNeetruBuildGuard(nextConfig, options = {}) {
61
140
  const prevWebpack = nextConfig.webpack;
141
+ let sqlTraceGlobs = [];
142
+ try {
143
+ sqlTraceGlobs = sqlClosureToTraceGlobs(resolveSqlRuntimeClosure(process.cwd()));
144
+ } catch {
145
+ sqlTraceGlobs = [];
146
+ }
62
147
  return {
63
148
  ...nextConfig,
149
+ // `serverExternalPackages` é reforço (não o conserto do caso webpackIgnore);
150
+ // o `outputFileTracingIncludes` computado é o que realmente inclui o closure.
151
+ serverExternalPackages: mergeServerExternalPackages(nextConfig.serverExternalPackages),
152
+ outputFileTracingIncludes: mergeOutputFileTracingIncludes(
153
+ nextConfig.outputFileTracingIncludes,
154
+ sqlTraceGlobs
155
+ ),
64
156
  webpack(config, context) {
65
157
  const result = typeof prevWebpack === "function" ? prevWebpack(config, context) : config;
66
158
  if (!context.dev && context.isServer) {
@@ -93,8 +185,14 @@ function withNeetruBuildGuard(nextConfig, options = {}) {
93
185
  };
94
186
  }
95
187
 
188
+ exports.NEETRU_SQL_SERVER_EXTERNAL_PACKAGES = NEETRU_SQL_SERVER_EXTERNAL_PACKAGES;
189
+ exports.NEETRU_SQL_TRACE_INCLUDE_KEY = NEETRU_SQL_TRACE_INCLUDE_KEY;
96
190
  exports.NeetruBuildGuardError = NeetruBuildGuardError;
97
191
  exports.assertNeetruProdBuildEnv = assertNeetruProdBuildEnv;
192
+ exports.mergeOutputFileTracingIncludes = mergeOutputFileTracingIncludes;
193
+ exports.mergeServerExternalPackages = mergeServerExternalPackages;
194
+ exports.resolveSqlRuntimeClosure = resolveSqlRuntimeClosure;
195
+ exports.sqlClosureToTraceGlobs = sqlClosureToTraceGlobs;
98
196
  exports.withNeetruBuildGuard = withNeetruBuildGuard;
99
197
  //# sourceMappingURL=build-guard.cjs.map
100
198
  //# sourceMappingURL=build-guard.cjs.map
@@ -1 +1 @@
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"]}
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 * TRACE DO STANDALONE — pg + drizzle-orm (Fase 2, causa-2 do outage PDV\r\n * 2026-09-07): `client.db.sql` carrega `pg` e `drizzle-orm/node-postgres` por\r\n * `import()` DINÂMICO com o magic comment `webpackIgnore`\r\n * (`src/db/sql/sql-client.ts`) — de propósito, pra `pg` (server-only, puxa\r\n * `fs`/`tls`/`net`) nunca entrar no bundle do browser. O efeito colateral: o\r\n * trace estático do Next (`@vercel/nft`) não enxerga esses `import()` e, num\r\n * `output: 'standalone'` enxuto, NÃO copia `pg`/`drizzle-orm` pra\r\n * `.next/standalone/node_modules` → runtime quebra com `Cannot find package\r\n * 'pg'` (o bundle gordo antigo os incluía por acidente; o enxuto, correto,\r\n * não). O guard conserta isso de forma NATIVA do Next, pra TODO produto que\r\n * já adota `withNeetruBuildGuard`, injetando duas chaves no config:\r\n * - `outputFileTracingIncludes: { '*': [...closure] }` (O CONSERTO REAL) —\r\n * FORÇA o nft a copiar o FECHO TRANSITIVO de runtime de `pg` + `drizzle-orm`\r\n * pro standalone, computado PROGRAMATICAMENTE do node_modules instalado do\r\n * produto (`resolveSqlRuntimeClosure`): `pg → pg-pool/pg-protocol/pg-types →\r\n * postgres-array/postgres-bytea/… → …`. Um glob `./node_modules/pkg/**` por\r\n * pacote do closure. A chave `'*'` casa todas as rotas (Next casa a chave\r\n * com `picomatch(..., { contains: true })`).\r\n * - `serverExternalPackages: [...existentes, 'pg', 'drizzle-orm']` (REFORÇO) —\r\n * inofensivo e ajuda caminhos de import VISÍVEIS ao nft, MAS não conserta o\r\n * caso `webpackIgnore`: `serverExternalPackages` só traça imports que o nft\r\n * ENXERGA (por isso funciona pro `mongodb`/`firebase-admin` do Core, que são\r\n * `require()` visíveis) — o `import()` webpackIgnore'd do SDK é invisível, e\r\n * `serverExternalPackages:['pg']` sozinho NÃO copia `pg` nem seu closure.\r\n * POR QUE PROGRAMÁTICO (não glob fixo): o glob `./node_modules/pg/**` cobre só o\r\n * DIR do `pg`, não os transitivos HOISTADOS na raiz (npm 7+) — foi o buraco que\r\n * deixou `Cannot find module 'postgres-array'` quebrar o deploy de 2026-09-07\r\n * mesmo com as duas chaves setadas. Só o disco instalado sabe o closure real →\r\n * à prova de whack-a-mole. O walker lê `package.json` DIRETO do filesystem (não\r\n * `require.resolve`, que dá `ERR_PACKAGE_PATH_NOT_EXPORTED` em pacotes com\r\n * `exports` fechando `./package.json` — drizzle-orm/pg-protocol/pg-pool fazem\r\n * isso, e era outro jeito de perder pacotes do closure).\r\n * Ambas as chaves MESCLAM (concat + dedupe) com o que o produto já tiver — nunca\r\n * sobrescrevem. `pglite`/`pg-mem` (MOCK/teste, `src/mocks.ts`) NÃO entram — só o\r\n * caminho real (`pg` + `drizzle-orm/node-postgres`). Produto SEM SQL:\r\n * `pg`/`drizzle-orm` não resolvem → closure `[]` → nenhuma chave adicionada →\r\n * peso morto ZERO. Chaves canônicas do Next 15 (Core, pdv-agiliza 15.5,\r\n * gestovendas 15.5) — top-level, não `experimental.*`.\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 serverExternalPackages?: string[];\r\n outputFileTracingIncludes?: Record<string, string[]>;\r\n};\r\n\r\n// ─── Trace do standalone: closure de runtime SQL (causa-2 outage PDV 2026-09-07) ─\r\n\r\n/**\r\n * Pacotes-SEMENTE do caminho SQL de produção do SDK: `client.db.sql` carrega\r\n * `pg` e `drizzle-orm/node-postgres` por `import()` dinâmico com `webpackIgnore`\r\n * (`src/db/sql/sql-client.ts`). `pglite`/`pg-mem` são mock/teste (`src/mocks.ts`)\r\n * e NÃO entram aqui. A partir destas sementes o guard computa o FECHO\r\n * TRANSITIVO de runtime (ver `resolveSqlRuntimeClosure`).\r\n */\r\nexport const NEETRU_SQL_SERVER_EXTERNAL_PACKAGES: readonly string[] = ['pg', 'drizzle-orm'];\r\n\r\n/**\r\n * Chave do `outputFileTracingIncludes`: `'*'` casa TODAS as rotas — o Next\r\n * casa a chave contra o path da rota com `picomatch(key, { contains: true })`,\r\n * então `'*'` (qualquer sequência) bate em qualquer rota.\r\n */\r\nexport const NEETRU_SQL_TRACE_INCLUDE_KEY = '*';\r\n\r\n/** Coage um valor desconhecido de config pra `string[]` (descarta não-strings). */\r\nfunction toStringArray(value: unknown): string[] {\r\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string') : [];\r\n}\r\n\r\n/**\r\n * Acha o `package.json` de um pacote LENDO O FILESYSTEM (não `require.resolve`):\r\n * caminha os `node_modules` a partir de `parentDir` subindo até a raiz, e cai no\r\n * `node_modules` de `fromDir` por último. Resolve tanto dep HOISTADA (raiz) quanto\r\n * NESTED (`.../node_modules/pai/node_modules/dep`).\r\n *\r\n * Por que filesystem e não `require.resolve(dep + '/package.json')`: pacotes\r\n * modernos com campo `exports` (ex: `drizzle-orm`, `pg-protocol`, `pg-pool`)\r\n * BLOQUEIAM o subpath `./package.json` no `exports` → `require.resolve` lança\r\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` e o walker perderia esses pacotes (foi\r\n * exatamente o buraco que deixou `postgres-array` fora). Ler o arquivo direto do\r\n * disco ignora o `exports` e é à prova disso.\r\n */\r\nfunction findPackageJsonPath(\r\n pkgName: string,\r\n parentDir: string,\r\n fromDir: string,\r\n): string | null {\r\n const bases: string[] = [];\r\n let dir = parentDir;\r\n // Sobe a árvore a partir do dir do pacote-pai (cobre nested + hoisted).\r\n for (;;) {\r\n bases.push(dir);\r\n const parent = path.dirname(dir);\r\n if (parent === dir) break;\r\n dir = parent;\r\n }\r\n if (!bases.includes(fromDir)) bases.push(fromDir);\r\n\r\n for (const base of bases) {\r\n // `path.join` lida com nome escopado (`@scope/name`) e separador de SO.\r\n const candidate = path.join(base, 'node_modules', pkgName, 'package.json');\r\n try {\r\n if (fs.existsSync(candidate)) return candidate;\r\n } catch {\r\n // Filesystem hostil (permissão/EBUSY) — ignora este candidato.\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Computa o FECHO TRANSITIVO de runtime das sementes SQL (`pg`, `drizzle-orm`) a\r\n * partir da raiz do produto (`fromDir`), retornando o conjunto ORDENADO de nomes\r\n * de pacote a forçar no trace do standalone.\r\n *\r\n * Algoritmo: a partir de cada semente, lê o `package.json`, coleta `dependencies`\r\n * + `optionalDependencies` (NÃO devDeps, NÃO peerDeps — não são o closure de\r\n * runtime) e recursa. Ciclos são cortados por um `visited` set; dep não-instalada\r\n * (optional já podada, ou peer não presente) é simplesmente pulada — nunca lança.\r\n *\r\n * PROGRAMÁTICO (não glob fixo) DE PROPÓSITO: `pg → pg-types → postgres-array →\r\n * …` é uma cadeia real que um glob `./node_modules/pg/**` NÃO cobre quando os\r\n * transitivos estão HOISTADOS na raiz (npm 7+). Só o disco instalado sabe o\r\n * closure real → à prova de whack-a-mole (o `Cannot find module 'postgres-array'`\r\n * do deploy de 2026-09-07 foi exatamente esse buraco). Se `pg`/`drizzle-orm` NÃO\r\n * resolvem (produto sem SQL), o closure é `[]` → nenhum glob, zero peso morto.\r\n *\r\n * PURO e testável: só toca filesystem (leitura), sem efeitos. `fromDir` inválido\r\n * ou erro inesperado → `[]` (o caller nunca pode quebrar o `next.config`).\r\n */\r\nexport function resolveSqlRuntimeClosure(fromDir: string): string[] {\r\n if (typeof fromDir !== 'string' || fromDir.trim() === '') return [];\r\n\r\n const visited = new Set<string>();\r\n const closure = new Set<string>();\r\n\r\n const walk = (pkgName: string, parentDir: string): void => {\r\n if (visited.has(pkgName)) return;\r\n visited.add(pkgName);\r\n\r\n const pkgJsonPath = findPackageJsonPath(pkgName, parentDir, fromDir);\r\n if (!pkgJsonPath) return; // não instalado — pula sem quebrar\r\n\r\n closure.add(pkgName);\r\n\r\n let manifest: { dependencies?: Record<string, string>; optionalDependencies?: Record<string, string> };\r\n try {\r\n manifest = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));\r\n } catch {\r\n return; // package.json ilegível/corrompido — inclui o próprio, para aqui\r\n }\r\n\r\n const deps = {\r\n ...(manifest.dependencies ?? {}),\r\n ...(manifest.optionalDependencies ?? {}),\r\n };\r\n const pkgDir = path.dirname(pkgJsonPath);\r\n for (const dep of Object.keys(deps)) walk(dep, pkgDir);\r\n };\r\n\r\n try {\r\n for (const seed of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) walk(seed, fromDir);\r\n } catch {\r\n return []; // qualquer imprevisto → sem globs (build do produto nunca quebra)\r\n }\r\n\r\n return [...closure].sort();\r\n}\r\n\r\n/**\r\n * Converte o closure em globs de `outputFileTracingIncludes` (um por pacote).\r\n * `./node_modules/${pkg}/**` cobre: (a) o pacote hoistado na raiz; (b) qualquer\r\n * cópia NESTED via o `**` do pacote-pai (o glob-raiz de um pacote só-nested vira\r\n * no-op inofensivo). Nome escopado (`@scope/name`) já vem correto no path.\r\n */\r\nexport function sqlClosureToTraceGlobs(closure: readonly string[]): string[] {\r\n return closure.map((pkg) => `./node_modules/${pkg}/**`);\r\n}\r\n\r\n/**\r\n * Mescla `serverExternalPackages` do produto com `pg`/`drizzle-orm` (concat +\r\n * dedupe, preservando a ordem: existentes primeiro). NUNCA sobrescreve o que o\r\n * produto já declarou (ex: o Core lista `mongodb`, `firebase-admin`, …).\r\n *\r\n * Mantido como reforço (inofensivo, ajuda caminhos de import VISÍVEIS ao nft),\r\n * mas NÃO é o que conserta o caso `webpackIgnore` — `serverExternalPackages` só\r\n * traça imports que o nft ENXERGA; o `import()` webpackIgnore'd é invisível, por\r\n * isso o `outputFileTracingIncludes` computado é o que realmente resolve.\r\n */\r\nexport function mergeServerExternalPackages(existing: unknown): string[] {\r\n const out = toStringArray(existing);\r\n for (const pkg of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) {\r\n if (!out.includes(pkg)) out.push(pkg);\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Mescla `outputFileTracingIncludes` do produto com os `globs` do closure SQL\r\n * sob a chave `'*'`. Preserva todas as chaves/globs existentes; na chave `'*'`\r\n * concatena + dedupe (existentes primeiro). `globs` vazio (produto sem SQL) →\r\n * nenhuma chave `'*'` criada além do que o produto já tinha.\r\n */\r\nexport function mergeOutputFileTracingIncludes(\r\n existing: unknown,\r\n globs: readonly string[],\r\n): Record<string, string[]> {\r\n const merged: Record<string, string[]> = {};\r\n if (existing && typeof existing === 'object' && !Array.isArray(existing)) {\r\n for (const [key, value] of Object.entries(existing as Record<string, unknown>)) {\r\n merged[key] = toStringArray(value);\r\n }\r\n }\r\n if (globs.length === 0) return merged; // sem closure → não mexe (zero peso morto)\r\n\r\n const combined = [...(merged[NEETRU_SQL_TRACE_INCLUDE_KEY] ?? [])];\r\n for (const glob of globs) {\r\n if (!combined.includes(glob)) combined.push(glob);\r\n }\r\n merged[NEETRU_SQL_TRACE_INCLUDE_KEY] = combined;\r\n return merged;\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 // Trace do standalone (causa-2 outage PDV 2026-09-07): computa o FECHO\r\n // TRANSITIVO de `pg` + `drizzle-orm` (carregados por import() dinâmico\r\n // webpackIgnore no `client.db.sql`, logo INVISÍVEIS ao nft) a partir do\r\n // node_modules REAL do produto, e força cada pacote do closure no\r\n // `outputFileTracingIncludes`. Produto sem SQL → closure vazio → zero peso\r\n // morto. `resolveSqlRuntimeClosure` é graceful (nunca lança); ainda assim\r\n // envolvo em try/catch como cinto-e-suspensório — o guard JAMAIS pode quebrar\r\n // o next.config de um produto. Roda no BUILD do produto, não em runtime.\r\n let sqlTraceGlobs: string[] = [];\r\n try {\r\n sqlTraceGlobs = sqlClosureToTraceGlobs(resolveSqlRuntimeClosure(process.cwd()));\r\n } catch {\r\n sqlTraceGlobs = [];\r\n }\r\n\r\n return {\r\n ...nextConfig,\r\n // `serverExternalPackages` é reforço (não o conserto do caso webpackIgnore);\r\n // o `outputFileTracingIncludes` computado é o que realmente inclui o closure.\r\n serverExternalPackages: mergeServerExternalPackages(nextConfig.serverExternalPackages),\r\n outputFileTracingIncludes: mergeOutputFileTracingIncludes(\r\n nextConfig.outputFileTracingIncludes,\r\n sqlTraceGlobs,\r\n ),\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"]}
@@ -49,7 +49,69 @@ type MinimalWebpackContext = {
49
49
  };
50
50
  type MinimalNextConfig = Record<string, unknown> & {
51
51
  webpack?: (config: unknown, context: MinimalWebpackContext) => unknown;
52
+ serverExternalPackages?: string[];
53
+ outputFileTracingIncludes?: Record<string, string[]>;
52
54
  };
55
+ /**
56
+ * Pacotes-SEMENTE do caminho SQL de produção do SDK: `client.db.sql` carrega
57
+ * `pg` e `drizzle-orm/node-postgres` por `import()` dinâmico com `webpackIgnore`
58
+ * (`src/db/sql/sql-client.ts`). `pglite`/`pg-mem` são mock/teste (`src/mocks.ts`)
59
+ * e NÃO entram aqui. A partir destas sementes o guard computa o FECHO
60
+ * TRANSITIVO de runtime (ver `resolveSqlRuntimeClosure`).
61
+ */
62
+ declare const NEETRU_SQL_SERVER_EXTERNAL_PACKAGES: readonly string[];
63
+ /**
64
+ * Chave do `outputFileTracingIncludes`: `'*'` casa TODAS as rotas — o Next
65
+ * casa a chave contra o path da rota com `picomatch(key, { contains: true })`,
66
+ * então `'*'` (qualquer sequência) bate em qualquer rota.
67
+ */
68
+ declare const NEETRU_SQL_TRACE_INCLUDE_KEY = "*";
69
+ /**
70
+ * Computa o FECHO TRANSITIVO de runtime das sementes SQL (`pg`, `drizzle-orm`) a
71
+ * partir da raiz do produto (`fromDir`), retornando o conjunto ORDENADO de nomes
72
+ * de pacote a forçar no trace do standalone.
73
+ *
74
+ * Algoritmo: a partir de cada semente, lê o `package.json`, coleta `dependencies`
75
+ * + `optionalDependencies` (NÃO devDeps, NÃO peerDeps — não são o closure de
76
+ * runtime) e recursa. Ciclos são cortados por um `visited` set; dep não-instalada
77
+ * (optional já podada, ou peer não presente) é simplesmente pulada — nunca lança.
78
+ *
79
+ * PROGRAMÁTICO (não glob fixo) DE PROPÓSITO: `pg → pg-types → postgres-array →
80
+ * …` é uma cadeia real que um glob `./node_modules/pg/**` NÃO cobre quando os
81
+ * transitivos estão HOISTADOS na raiz (npm 7+). Só o disco instalado sabe o
82
+ * closure real → à prova de whack-a-mole (o `Cannot find module 'postgres-array'`
83
+ * do deploy de 2026-09-07 foi exatamente esse buraco). Se `pg`/`drizzle-orm` NÃO
84
+ * resolvem (produto sem SQL), o closure é `[]` → nenhum glob, zero peso morto.
85
+ *
86
+ * PURO e testável: só toca filesystem (leitura), sem efeitos. `fromDir` inválido
87
+ * ou erro inesperado → `[]` (o caller nunca pode quebrar o `next.config`).
88
+ */
89
+ declare function resolveSqlRuntimeClosure(fromDir: string): string[];
90
+ /**
91
+ * Converte o closure em globs de `outputFileTracingIncludes` (um por pacote).
92
+ * `./node_modules/${pkg}/**` cobre: (a) o pacote hoistado na raiz; (b) qualquer
93
+ * cópia NESTED via o `**` do pacote-pai (o glob-raiz de um pacote só-nested vira
94
+ * no-op inofensivo). Nome escopado (`@scope/name`) já vem correto no path.
95
+ */
96
+ declare function sqlClosureToTraceGlobs(closure: readonly string[]): string[];
97
+ /**
98
+ * Mescla `serverExternalPackages` do produto com `pg`/`drizzle-orm` (concat +
99
+ * dedupe, preservando a ordem: existentes primeiro). NUNCA sobrescreve o que o
100
+ * produto já declarou (ex: o Core lista `mongodb`, `firebase-admin`, …).
101
+ *
102
+ * Mantido como reforço (inofensivo, ajuda caminhos de import VISÍVEIS ao nft),
103
+ * mas NÃO é o que conserta o caso `webpackIgnore` — `serverExternalPackages` só
104
+ * traça imports que o nft ENXERGA; o `import()` webpackIgnore'd é invisível, por
105
+ * isso o `outputFileTracingIncludes` computado é o que realmente resolve.
106
+ */
107
+ declare function mergeServerExternalPackages(existing: unknown): string[];
108
+ /**
109
+ * Mescla `outputFileTracingIncludes` do produto com os `globs` do closure SQL
110
+ * sob a chave `'*'`. Preserva todas as chaves/globs existentes; na chave `'*'`
111
+ * concatena + dedupe (existentes primeiro). `globs` vazio (produto sem SQL) →
112
+ * nenhuma chave `'*'` criada além do que o produto já tinha.
113
+ */
114
+ declare function mergeOutputFileTracingIncludes(existing: unknown, globs: readonly string[]): Record<string, string[]>;
53
115
  declare function withNeetruBuildGuard<T extends MinimalNextConfig>(nextConfig: T, options?: WithNeetruBuildGuardOptions): T;
54
116
 
55
- export { NeetruBuildGuardError, type NeetruBuildManifest, type WithNeetruBuildGuardOptions, assertNeetruProdBuildEnv, withNeetruBuildGuard };
117
+ export { NEETRU_SQL_SERVER_EXTERNAL_PACKAGES, NEETRU_SQL_TRACE_INCLUDE_KEY, NeetruBuildGuardError, type NeetruBuildManifest, type WithNeetruBuildGuardOptions, assertNeetruProdBuildEnv, mergeOutputFileTracingIncludes, mergeServerExternalPackages, resolveSqlRuntimeClosure, sqlClosureToTraceGlobs, withNeetruBuildGuard };
@@ -49,7 +49,69 @@ type MinimalWebpackContext = {
49
49
  };
50
50
  type MinimalNextConfig = Record<string, unknown> & {
51
51
  webpack?: (config: unknown, context: MinimalWebpackContext) => unknown;
52
+ serverExternalPackages?: string[];
53
+ outputFileTracingIncludes?: Record<string, string[]>;
52
54
  };
55
+ /**
56
+ * Pacotes-SEMENTE do caminho SQL de produção do SDK: `client.db.sql` carrega
57
+ * `pg` e `drizzle-orm/node-postgres` por `import()` dinâmico com `webpackIgnore`
58
+ * (`src/db/sql/sql-client.ts`). `pglite`/`pg-mem` são mock/teste (`src/mocks.ts`)
59
+ * e NÃO entram aqui. A partir destas sementes o guard computa o FECHO
60
+ * TRANSITIVO de runtime (ver `resolveSqlRuntimeClosure`).
61
+ */
62
+ declare const NEETRU_SQL_SERVER_EXTERNAL_PACKAGES: readonly string[];
63
+ /**
64
+ * Chave do `outputFileTracingIncludes`: `'*'` casa TODAS as rotas — o Next
65
+ * casa a chave contra o path da rota com `picomatch(key, { contains: true })`,
66
+ * então `'*'` (qualquer sequência) bate em qualquer rota.
67
+ */
68
+ declare const NEETRU_SQL_TRACE_INCLUDE_KEY = "*";
69
+ /**
70
+ * Computa o FECHO TRANSITIVO de runtime das sementes SQL (`pg`, `drizzle-orm`) a
71
+ * partir da raiz do produto (`fromDir`), retornando o conjunto ORDENADO de nomes
72
+ * de pacote a forçar no trace do standalone.
73
+ *
74
+ * Algoritmo: a partir de cada semente, lê o `package.json`, coleta `dependencies`
75
+ * + `optionalDependencies` (NÃO devDeps, NÃO peerDeps — não são o closure de
76
+ * runtime) e recursa. Ciclos são cortados por um `visited` set; dep não-instalada
77
+ * (optional já podada, ou peer não presente) é simplesmente pulada — nunca lança.
78
+ *
79
+ * PROGRAMÁTICO (não glob fixo) DE PROPÓSITO: `pg → pg-types → postgres-array →
80
+ * …` é uma cadeia real que um glob `./node_modules/pg/**` NÃO cobre quando os
81
+ * transitivos estão HOISTADOS na raiz (npm 7+). Só o disco instalado sabe o
82
+ * closure real → à prova de whack-a-mole (o `Cannot find module 'postgres-array'`
83
+ * do deploy de 2026-09-07 foi exatamente esse buraco). Se `pg`/`drizzle-orm` NÃO
84
+ * resolvem (produto sem SQL), o closure é `[]` → nenhum glob, zero peso morto.
85
+ *
86
+ * PURO e testável: só toca filesystem (leitura), sem efeitos. `fromDir` inválido
87
+ * ou erro inesperado → `[]` (o caller nunca pode quebrar o `next.config`).
88
+ */
89
+ declare function resolveSqlRuntimeClosure(fromDir: string): string[];
90
+ /**
91
+ * Converte o closure em globs de `outputFileTracingIncludes` (um por pacote).
92
+ * `./node_modules/${pkg}/**` cobre: (a) o pacote hoistado na raiz; (b) qualquer
93
+ * cópia NESTED via o `**` do pacote-pai (o glob-raiz de um pacote só-nested vira
94
+ * no-op inofensivo). Nome escopado (`@scope/name`) já vem correto no path.
95
+ */
96
+ declare function sqlClosureToTraceGlobs(closure: readonly string[]): string[];
97
+ /**
98
+ * Mescla `serverExternalPackages` do produto com `pg`/`drizzle-orm` (concat +
99
+ * dedupe, preservando a ordem: existentes primeiro). NUNCA sobrescreve o que o
100
+ * produto já declarou (ex: o Core lista `mongodb`, `firebase-admin`, …).
101
+ *
102
+ * Mantido como reforço (inofensivo, ajuda caminhos de import VISÍVEIS ao nft),
103
+ * mas NÃO é o que conserta o caso `webpackIgnore` — `serverExternalPackages` só
104
+ * traça imports que o nft ENXERGA; o `import()` webpackIgnore'd é invisível, por
105
+ * isso o `outputFileTracingIncludes` computado é o que realmente resolve.
106
+ */
107
+ declare function mergeServerExternalPackages(existing: unknown): string[];
108
+ /**
109
+ * Mescla `outputFileTracingIncludes` do produto com os `globs` do closure SQL
110
+ * sob a chave `'*'`. Preserva todas as chaves/globs existentes; na chave `'*'`
111
+ * concatena + dedupe (existentes primeiro). `globs` vazio (produto sem SQL) →
112
+ * nenhuma chave `'*'` criada além do que o produto já tinha.
113
+ */
114
+ declare function mergeOutputFileTracingIncludes(existing: unknown, globs: readonly string[]): Record<string, string[]>;
53
115
  declare function withNeetruBuildGuard<T extends MinimalNextConfig>(nextConfig: T, options?: WithNeetruBuildGuardOptions): T;
54
116
 
55
- export { NeetruBuildGuardError, type NeetruBuildManifest, type WithNeetruBuildGuardOptions, assertNeetruProdBuildEnv, withNeetruBuildGuard };
117
+ export { NEETRU_SQL_SERVER_EXTERNAL_PACKAGES, NEETRU_SQL_TRACE_INCLUDE_KEY, NeetruBuildGuardError, type NeetruBuildManifest, type WithNeetruBuildGuardOptions, assertNeetruProdBuildEnv, mergeOutputFileTracingIncludes, mergeServerExternalPackages, resolveSqlRuntimeClosure, sqlClosureToTraceGlobs, withNeetruBuildGuard };
@@ -34,10 +34,102 @@ function assertNeetruProdBuildEnv(resolved) {
34
34
  ].join("\n")
35
35
  );
36
36
  }
37
+ var NEETRU_SQL_SERVER_EXTERNAL_PACKAGES = ["pg", "drizzle-orm"];
38
+ var NEETRU_SQL_TRACE_INCLUDE_KEY = "*";
39
+ function toStringArray(value) {
40
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
41
+ }
42
+ function findPackageJsonPath(pkgName, parentDir, fromDir) {
43
+ const bases = [];
44
+ let dir = parentDir;
45
+ for (; ; ) {
46
+ bases.push(dir);
47
+ const parent = path.dirname(dir);
48
+ if (parent === dir) break;
49
+ dir = parent;
50
+ }
51
+ if (!bases.includes(fromDir)) bases.push(fromDir);
52
+ for (const base of bases) {
53
+ const candidate = path.join(base, "node_modules", pkgName, "package.json");
54
+ try {
55
+ if (fs.existsSync(candidate)) return candidate;
56
+ } catch {
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ function resolveSqlRuntimeClosure(fromDir) {
62
+ if (typeof fromDir !== "string" || fromDir.trim() === "") return [];
63
+ const visited = /* @__PURE__ */ new Set();
64
+ const closure = /* @__PURE__ */ new Set();
65
+ const walk = (pkgName, parentDir) => {
66
+ if (visited.has(pkgName)) return;
67
+ visited.add(pkgName);
68
+ const pkgJsonPath = findPackageJsonPath(pkgName, parentDir, fromDir);
69
+ if (!pkgJsonPath) return;
70
+ closure.add(pkgName);
71
+ let manifest;
72
+ try {
73
+ manifest = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
74
+ } catch {
75
+ return;
76
+ }
77
+ const deps = {
78
+ ...manifest.dependencies ?? {},
79
+ ...manifest.optionalDependencies ?? {}
80
+ };
81
+ const pkgDir = path.dirname(pkgJsonPath);
82
+ for (const dep of Object.keys(deps)) walk(dep, pkgDir);
83
+ };
84
+ try {
85
+ for (const seed of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) walk(seed, fromDir);
86
+ } catch {
87
+ return [];
88
+ }
89
+ return [...closure].sort();
90
+ }
91
+ function sqlClosureToTraceGlobs(closure) {
92
+ return closure.map((pkg) => `./node_modules/${pkg}/**`);
93
+ }
94
+ function mergeServerExternalPackages(existing) {
95
+ const out = toStringArray(existing);
96
+ for (const pkg of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) {
97
+ if (!out.includes(pkg)) out.push(pkg);
98
+ }
99
+ return out;
100
+ }
101
+ function mergeOutputFileTracingIncludes(existing, globs) {
102
+ const merged = {};
103
+ if (existing && typeof existing === "object" && !Array.isArray(existing)) {
104
+ for (const [key, value] of Object.entries(existing)) {
105
+ merged[key] = toStringArray(value);
106
+ }
107
+ }
108
+ if (globs.length === 0) return merged;
109
+ const combined = [...merged[NEETRU_SQL_TRACE_INCLUDE_KEY] ?? []];
110
+ for (const glob of globs) {
111
+ if (!combined.includes(glob)) combined.push(glob);
112
+ }
113
+ merged[NEETRU_SQL_TRACE_INCLUDE_KEY] = combined;
114
+ return merged;
115
+ }
37
116
  function withNeetruBuildGuard(nextConfig, options = {}) {
38
117
  const prevWebpack = nextConfig.webpack;
118
+ let sqlTraceGlobs = [];
119
+ try {
120
+ sqlTraceGlobs = sqlClosureToTraceGlobs(resolveSqlRuntimeClosure(process.cwd()));
121
+ } catch {
122
+ sqlTraceGlobs = [];
123
+ }
39
124
  return {
40
125
  ...nextConfig,
126
+ // `serverExternalPackages` é reforço (não o conserto do caso webpackIgnore);
127
+ // o `outputFileTracingIncludes` computado é o que realmente inclui o closure.
128
+ serverExternalPackages: mergeServerExternalPackages(nextConfig.serverExternalPackages),
129
+ outputFileTracingIncludes: mergeOutputFileTracingIncludes(
130
+ nextConfig.outputFileTracingIncludes,
131
+ sqlTraceGlobs
132
+ ),
41
133
  webpack(config, context) {
42
134
  const result = typeof prevWebpack === "function" ? prevWebpack(config, context) : config;
43
135
  if (!context.dev && context.isServer) {
@@ -70,6 +162,6 @@ function withNeetruBuildGuard(nextConfig, options = {}) {
70
162
  };
71
163
  }
72
164
 
73
- export { NeetruBuildGuardError, assertNeetruProdBuildEnv, withNeetruBuildGuard };
165
+ export { NEETRU_SQL_SERVER_EXTERNAL_PACKAGES, NEETRU_SQL_TRACE_INCLUDE_KEY, NeetruBuildGuardError, assertNeetruProdBuildEnv, mergeOutputFileTracingIncludes, mergeServerExternalPackages, resolveSqlRuntimeClosure, sqlClosureToTraceGlobs, withNeetruBuildGuard };
74
166
  //# sourceMappingURL=build-guard.mjs.map
75
167
  //# sourceMappingURL=build-guard.mjs.map
@@ -1 +1 @@
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"]}
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 * TRACE DO STANDALONE — pg + drizzle-orm (Fase 2, causa-2 do outage PDV\r\n * 2026-09-07): `client.db.sql` carrega `pg` e `drizzle-orm/node-postgres` por\r\n * `import()` DINÂMICO com o magic comment `webpackIgnore`\r\n * (`src/db/sql/sql-client.ts`) — de propósito, pra `pg` (server-only, puxa\r\n * `fs`/`tls`/`net`) nunca entrar no bundle do browser. O efeito colateral: o\r\n * trace estático do Next (`@vercel/nft`) não enxerga esses `import()` e, num\r\n * `output: 'standalone'` enxuto, NÃO copia `pg`/`drizzle-orm` pra\r\n * `.next/standalone/node_modules` → runtime quebra com `Cannot find package\r\n * 'pg'` (o bundle gordo antigo os incluía por acidente; o enxuto, correto,\r\n * não). O guard conserta isso de forma NATIVA do Next, pra TODO produto que\r\n * já adota `withNeetruBuildGuard`, injetando duas chaves no config:\r\n * - `outputFileTracingIncludes: { '*': [...closure] }` (O CONSERTO REAL) —\r\n * FORÇA o nft a copiar o FECHO TRANSITIVO de runtime de `pg` + `drizzle-orm`\r\n * pro standalone, computado PROGRAMATICAMENTE do node_modules instalado do\r\n * produto (`resolveSqlRuntimeClosure`): `pg → pg-pool/pg-protocol/pg-types →\r\n * postgres-array/postgres-bytea/… → …`. Um glob `./node_modules/pkg/**` por\r\n * pacote do closure. A chave `'*'` casa todas as rotas (Next casa a chave\r\n * com `picomatch(..., { contains: true })`).\r\n * - `serverExternalPackages: [...existentes, 'pg', 'drizzle-orm']` (REFORÇO) —\r\n * inofensivo e ajuda caminhos de import VISÍVEIS ao nft, MAS não conserta o\r\n * caso `webpackIgnore`: `serverExternalPackages` só traça imports que o nft\r\n * ENXERGA (por isso funciona pro `mongodb`/`firebase-admin` do Core, que são\r\n * `require()` visíveis) — o `import()` webpackIgnore'd do SDK é invisível, e\r\n * `serverExternalPackages:['pg']` sozinho NÃO copia `pg` nem seu closure.\r\n * POR QUE PROGRAMÁTICO (não glob fixo): o glob `./node_modules/pg/**` cobre só o\r\n * DIR do `pg`, não os transitivos HOISTADOS na raiz (npm 7+) — foi o buraco que\r\n * deixou `Cannot find module 'postgres-array'` quebrar o deploy de 2026-09-07\r\n * mesmo com as duas chaves setadas. Só o disco instalado sabe o closure real →\r\n * à prova de whack-a-mole. O walker lê `package.json` DIRETO do filesystem (não\r\n * `require.resolve`, que dá `ERR_PACKAGE_PATH_NOT_EXPORTED` em pacotes com\r\n * `exports` fechando `./package.json` — drizzle-orm/pg-protocol/pg-pool fazem\r\n * isso, e era outro jeito de perder pacotes do closure).\r\n * Ambas as chaves MESCLAM (concat + dedupe) com o que o produto já tiver — nunca\r\n * sobrescrevem. `pglite`/`pg-mem` (MOCK/teste, `src/mocks.ts`) NÃO entram — só o\r\n * caminho real (`pg` + `drizzle-orm/node-postgres`). Produto SEM SQL:\r\n * `pg`/`drizzle-orm` não resolvem → closure `[]` → nenhuma chave adicionada →\r\n * peso morto ZERO. Chaves canônicas do Next 15 (Core, pdv-agiliza 15.5,\r\n * gestovendas 15.5) — top-level, não `experimental.*`.\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 serverExternalPackages?: string[];\r\n outputFileTracingIncludes?: Record<string, string[]>;\r\n};\r\n\r\n// ─── Trace do standalone: closure de runtime SQL (causa-2 outage PDV 2026-09-07) ─\r\n\r\n/**\r\n * Pacotes-SEMENTE do caminho SQL de produção do SDK: `client.db.sql` carrega\r\n * `pg` e `drizzle-orm/node-postgres` por `import()` dinâmico com `webpackIgnore`\r\n * (`src/db/sql/sql-client.ts`). `pglite`/`pg-mem` são mock/teste (`src/mocks.ts`)\r\n * e NÃO entram aqui. A partir destas sementes o guard computa o FECHO\r\n * TRANSITIVO de runtime (ver `resolveSqlRuntimeClosure`).\r\n */\r\nexport const NEETRU_SQL_SERVER_EXTERNAL_PACKAGES: readonly string[] = ['pg', 'drizzle-orm'];\r\n\r\n/**\r\n * Chave do `outputFileTracingIncludes`: `'*'` casa TODAS as rotas — o Next\r\n * casa a chave contra o path da rota com `picomatch(key, { contains: true })`,\r\n * então `'*'` (qualquer sequência) bate em qualquer rota.\r\n */\r\nexport const NEETRU_SQL_TRACE_INCLUDE_KEY = '*';\r\n\r\n/** Coage um valor desconhecido de config pra `string[]` (descarta não-strings). */\r\nfunction toStringArray(value: unknown): string[] {\r\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string') : [];\r\n}\r\n\r\n/**\r\n * Acha o `package.json` de um pacote LENDO O FILESYSTEM (não `require.resolve`):\r\n * caminha os `node_modules` a partir de `parentDir` subindo até a raiz, e cai no\r\n * `node_modules` de `fromDir` por último. Resolve tanto dep HOISTADA (raiz) quanto\r\n * NESTED (`.../node_modules/pai/node_modules/dep`).\r\n *\r\n * Por que filesystem e não `require.resolve(dep + '/package.json')`: pacotes\r\n * modernos com campo `exports` (ex: `drizzle-orm`, `pg-protocol`, `pg-pool`)\r\n * BLOQUEIAM o subpath `./package.json` no `exports` → `require.resolve` lança\r\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` e o walker perderia esses pacotes (foi\r\n * exatamente o buraco que deixou `postgres-array` fora). Ler o arquivo direto do\r\n * disco ignora o `exports` e é à prova disso.\r\n */\r\nfunction findPackageJsonPath(\r\n pkgName: string,\r\n parentDir: string,\r\n fromDir: string,\r\n): string | null {\r\n const bases: string[] = [];\r\n let dir = parentDir;\r\n // Sobe a árvore a partir do dir do pacote-pai (cobre nested + hoisted).\r\n for (;;) {\r\n bases.push(dir);\r\n const parent = path.dirname(dir);\r\n if (parent === dir) break;\r\n dir = parent;\r\n }\r\n if (!bases.includes(fromDir)) bases.push(fromDir);\r\n\r\n for (const base of bases) {\r\n // `path.join` lida com nome escopado (`@scope/name`) e separador de SO.\r\n const candidate = path.join(base, 'node_modules', pkgName, 'package.json');\r\n try {\r\n if (fs.existsSync(candidate)) return candidate;\r\n } catch {\r\n // Filesystem hostil (permissão/EBUSY) — ignora este candidato.\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Computa o FECHO TRANSITIVO de runtime das sementes SQL (`pg`, `drizzle-orm`) a\r\n * partir da raiz do produto (`fromDir`), retornando o conjunto ORDENADO de nomes\r\n * de pacote a forçar no trace do standalone.\r\n *\r\n * Algoritmo: a partir de cada semente, lê o `package.json`, coleta `dependencies`\r\n * + `optionalDependencies` (NÃO devDeps, NÃO peerDeps — não são o closure de\r\n * runtime) e recursa. Ciclos são cortados por um `visited` set; dep não-instalada\r\n * (optional já podada, ou peer não presente) é simplesmente pulada — nunca lança.\r\n *\r\n * PROGRAMÁTICO (não glob fixo) DE PROPÓSITO: `pg → pg-types → postgres-array →\r\n * …` é uma cadeia real que um glob `./node_modules/pg/**` NÃO cobre quando os\r\n * transitivos estão HOISTADOS na raiz (npm 7+). Só o disco instalado sabe o\r\n * closure real → à prova de whack-a-mole (o `Cannot find module 'postgres-array'`\r\n * do deploy de 2026-09-07 foi exatamente esse buraco). Se `pg`/`drizzle-orm` NÃO\r\n * resolvem (produto sem SQL), o closure é `[]` → nenhum glob, zero peso morto.\r\n *\r\n * PURO e testável: só toca filesystem (leitura), sem efeitos. `fromDir` inválido\r\n * ou erro inesperado → `[]` (o caller nunca pode quebrar o `next.config`).\r\n */\r\nexport function resolveSqlRuntimeClosure(fromDir: string): string[] {\r\n if (typeof fromDir !== 'string' || fromDir.trim() === '') return [];\r\n\r\n const visited = new Set<string>();\r\n const closure = new Set<string>();\r\n\r\n const walk = (pkgName: string, parentDir: string): void => {\r\n if (visited.has(pkgName)) return;\r\n visited.add(pkgName);\r\n\r\n const pkgJsonPath = findPackageJsonPath(pkgName, parentDir, fromDir);\r\n if (!pkgJsonPath) return; // não instalado — pula sem quebrar\r\n\r\n closure.add(pkgName);\r\n\r\n let manifest: { dependencies?: Record<string, string>; optionalDependencies?: Record<string, string> };\r\n try {\r\n manifest = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));\r\n } catch {\r\n return; // package.json ilegível/corrompido — inclui o próprio, para aqui\r\n }\r\n\r\n const deps = {\r\n ...(manifest.dependencies ?? {}),\r\n ...(manifest.optionalDependencies ?? {}),\r\n };\r\n const pkgDir = path.dirname(pkgJsonPath);\r\n for (const dep of Object.keys(deps)) walk(dep, pkgDir);\r\n };\r\n\r\n try {\r\n for (const seed of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) walk(seed, fromDir);\r\n } catch {\r\n return []; // qualquer imprevisto → sem globs (build do produto nunca quebra)\r\n }\r\n\r\n return [...closure].sort();\r\n}\r\n\r\n/**\r\n * Converte o closure em globs de `outputFileTracingIncludes` (um por pacote).\r\n * `./node_modules/${pkg}/**` cobre: (a) o pacote hoistado na raiz; (b) qualquer\r\n * cópia NESTED via o `**` do pacote-pai (o glob-raiz de um pacote só-nested vira\r\n * no-op inofensivo). Nome escopado (`@scope/name`) já vem correto no path.\r\n */\r\nexport function sqlClosureToTraceGlobs(closure: readonly string[]): string[] {\r\n return closure.map((pkg) => `./node_modules/${pkg}/**`);\r\n}\r\n\r\n/**\r\n * Mescla `serverExternalPackages` do produto com `pg`/`drizzle-orm` (concat +\r\n * dedupe, preservando a ordem: existentes primeiro). NUNCA sobrescreve o que o\r\n * produto já declarou (ex: o Core lista `mongodb`, `firebase-admin`, …).\r\n *\r\n * Mantido como reforço (inofensivo, ajuda caminhos de import VISÍVEIS ao nft),\r\n * mas NÃO é o que conserta o caso `webpackIgnore` — `serverExternalPackages` só\r\n * traça imports que o nft ENXERGA; o `import()` webpackIgnore'd é invisível, por\r\n * isso o `outputFileTracingIncludes` computado é o que realmente resolve.\r\n */\r\nexport function mergeServerExternalPackages(existing: unknown): string[] {\r\n const out = toStringArray(existing);\r\n for (const pkg of NEETRU_SQL_SERVER_EXTERNAL_PACKAGES) {\r\n if (!out.includes(pkg)) out.push(pkg);\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Mescla `outputFileTracingIncludes` do produto com os `globs` do closure SQL\r\n * sob a chave `'*'`. Preserva todas as chaves/globs existentes; na chave `'*'`\r\n * concatena + dedupe (existentes primeiro). `globs` vazio (produto sem SQL) →\r\n * nenhuma chave `'*'` criada além do que o produto já tinha.\r\n */\r\nexport function mergeOutputFileTracingIncludes(\r\n existing: unknown,\r\n globs: readonly string[],\r\n): Record<string, string[]> {\r\n const merged: Record<string, string[]> = {};\r\n if (existing && typeof existing === 'object' && !Array.isArray(existing)) {\r\n for (const [key, value] of Object.entries(existing as Record<string, unknown>)) {\r\n merged[key] = toStringArray(value);\r\n }\r\n }\r\n if (globs.length === 0) return merged; // sem closure → não mexe (zero peso morto)\r\n\r\n const combined = [...(merged[NEETRU_SQL_TRACE_INCLUDE_KEY] ?? [])];\r\n for (const glob of globs) {\r\n if (!combined.includes(glob)) combined.push(glob);\r\n }\r\n merged[NEETRU_SQL_TRACE_INCLUDE_KEY] = combined;\r\n return merged;\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 // Trace do standalone (causa-2 outage PDV 2026-09-07): computa o FECHO\r\n // TRANSITIVO de `pg` + `drizzle-orm` (carregados por import() dinâmico\r\n // webpackIgnore no `client.db.sql`, logo INVISÍVEIS ao nft) a partir do\r\n // node_modules REAL do produto, e força cada pacote do closure no\r\n // `outputFileTracingIncludes`. Produto sem SQL → closure vazio → zero peso\r\n // morto. `resolveSqlRuntimeClosure` é graceful (nunca lança); ainda assim\r\n // envolvo em try/catch como cinto-e-suspensório — o guard JAMAIS pode quebrar\r\n // o next.config de um produto. Roda no BUILD do produto, não em runtime.\r\n let sqlTraceGlobs: string[] = [];\r\n try {\r\n sqlTraceGlobs = sqlClosureToTraceGlobs(resolveSqlRuntimeClosure(process.cwd()));\r\n } catch {\r\n sqlTraceGlobs = [];\r\n }\r\n\r\n return {\r\n ...nextConfig,\r\n // `serverExternalPackages` é reforço (não o conserto do caso webpackIgnore);\r\n // o `outputFileTracingIncludes` computado é o que realmente inclui o closure.\r\n serverExternalPackages: mergeServerExternalPackages(nextConfig.serverExternalPackages),\r\n outputFileTracingIncludes: mergeOutputFileTracingIncludes(\r\n nextConfig.outputFileTracingIncludes,\r\n sqlTraceGlobs,\r\n ),\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"]}
@@ -1,4 +1,4 @@
1
- import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-D-QdG502.cjs';
1
+ import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-Dw0fqhHm.cjs';
2
2
  import 'drizzle-orm/node-postgres';
3
3
  import './errors.cjs';
4
4
 
package/dist/catalog.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-BlOLDMR0.js';
1
+ import { ag as ResolvedConfig, C as CatalogListOptions, n as CatalogListResponse, a8 as Product } from './types-Bzxb5rlF.js';
2
2
  import 'drizzle-orm/node-postgres';
3
3
  import './errors.js';
4
4