@neetru/sdk 3.4.4 → 3.4.6

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 (76) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/ai.cjs +1 -1
  3. package/dist/ai.cjs.map +1 -1
  4. package/dist/ai.d.cts +1 -1
  5. package/dist/ai.d.ts +1 -1
  6. package/dist/ai.mjs +1 -1
  7. package/dist/ai.mjs.map +1 -1
  8. package/dist/auth.cjs +47 -74
  9. package/dist/auth.cjs.map +1 -1
  10. package/dist/auth.d.cts +53 -2
  11. package/dist/auth.d.ts +53 -2
  12. package/dist/auth.mjs +46 -75
  13. package/dist/auth.mjs.map +1 -1
  14. package/dist/catalog.d.cts +1 -1
  15. package/dist/catalog.d.ts +1 -1
  16. package/dist/checkout.d.cts +1 -1
  17. package/dist/checkout.d.ts +1 -1
  18. package/dist/db-react.d.cts +1 -1
  19. package/dist/db-react.d.ts +1 -1
  20. package/dist/db.d.cts +1 -1
  21. package/dist/db.d.ts +1 -1
  22. package/dist/entitlements.cjs +16 -263
  23. package/dist/entitlements.cjs.map +1 -1
  24. package/dist/entitlements.d.cts +12 -10
  25. package/dist/entitlements.d.ts +12 -10
  26. package/dist/entitlements.mjs +16 -263
  27. package/dist/entitlements.mjs.map +1 -1
  28. package/dist/firestore-compat.d.cts +1 -1
  29. package/dist/firestore-compat.d.ts +1 -1
  30. package/dist/index.cjs +44 -75
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +3 -3
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.mjs +44 -76
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/mocks.d.cts +1 -1
  37. package/dist/mocks.d.ts +1 -1
  38. package/dist/notifications.cjs +1 -1
  39. package/dist/notifications.cjs.map +1 -1
  40. package/dist/notifications.d.cts +1 -1
  41. package/dist/notifications.d.ts +1 -1
  42. package/dist/notifications.mjs +1 -1
  43. package/dist/notifications.mjs.map +1 -1
  44. package/dist/react.cjs +6 -2
  45. package/dist/react.cjs.map +1 -1
  46. package/dist/react.d.cts +1 -1
  47. package/dist/react.d.ts +1 -1
  48. package/dist/react.mjs +6 -2
  49. package/dist/react.mjs.map +1 -1
  50. package/dist/support.cjs +1 -1
  51. package/dist/support.cjs.map +1 -1
  52. package/dist/support.d.cts +1 -1
  53. package/dist/support.d.ts +1 -1
  54. package/dist/support.mjs +1 -1
  55. package/dist/support.mjs.map +1 -1
  56. package/dist/telemetry.cjs +16 -4
  57. package/dist/telemetry.cjs.map +1 -1
  58. package/dist/telemetry.d.cts +1 -1
  59. package/dist/telemetry.d.ts +1 -1
  60. package/dist/telemetry.mjs +16 -4
  61. package/dist/telemetry.mjs.map +1 -1
  62. package/dist/{types-Dw0fqhHm.d.cts → types-DIVZl7nO.d.cts} +3 -0
  63. package/dist/{types-Bzxb5rlF.d.ts → types-nnztVk7G.d.ts} +3 -0
  64. package/dist/usage.cjs +1 -1
  65. package/dist/usage.cjs.map +1 -1
  66. package/dist/usage.d.cts +1 -1
  67. package/dist/usage.d.ts +1 -1
  68. package/dist/usage.mjs +1 -1
  69. package/dist/usage.mjs.map +1 -1
  70. package/dist/webhooks.cjs +1 -1
  71. package/dist/webhooks.cjs.map +1 -1
  72. package/dist/webhooks.d.cts +1 -1
  73. package/dist/webhooks.d.ts +1 -1
  74. package/dist/webhooks.mjs +1 -1
  75. package/dist/webhooks.mjs.map +1 -1
  76. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/account-bar.ts","../src/firestore-compat.ts","../src/react.ts"],"names":["React","result","allowed"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\r\n * Tipos públicos do SDK Neetru.\r\n *\r\n * Vendor-neutral: nada aqui referencia `firebase/*`, `stripe`, ou qualquer\r\n * provider interno. Caller só vê superfície neutra (Product, Entitlement, ...).\r\n */\r\n\r\n/** Default base URL pra API pública Neetru. */\r\nexport const DEFAULT_BASE_URL = 'https://api.neetru.com';\r\n\r\n/**\r\n * Base URL do Core local no emulador da Dev Suite (`neetru dev up`). Usada como\r\n * default quando `env: 'dev-local'` e nenhum `baseUrl` explícito é passado.\r\n * `dev-local` chama HTTP real contra o Core local — não é mock.\r\n */\r\nexport const DEV_LOCAL_BASE_URL = 'http://localhost:9003';\r\n\r\n/**\r\n * Configuração passada pra `createNeetruClient`.\r\n */\r\nexport interface NeetruClientConfig {\r\n /**\r\n * Bearer token Neetru (formato `nrt_<keyId>_<secret>`). Opcional —\r\n * se ausente, o SDK tenta `process.env.NEETRU_API_KEY` em Node.\r\n *\r\n * Endpoints públicos (catalog) funcionam sem apiKey. Endpoints autenticados\r\n * (entitlements, telemetry) lançam `missing_api_key` se ausente.\r\n */\r\n apiKey?: string;\r\n /**\r\n * 3.1.3 — `client_id` do client OIDC confidencial do produto, registrado em\r\n * `oidc_clients/{id}` no Core (par id + segredo, modelo Firebase/Facebook).\r\n *\r\n * Usado como `client_id` no fluxo `auth.signIn()` (Authorization Code + PKCE).\r\n * É **distinto** do `keyId` embutido na `apiKey` (`nrt_<keyId>_<secret>`), que\r\n * identifica a API key de máquina — não um client OIDC. Se ausente, `signIn`\r\n * cai no `keyId` por compat, mas o `/authorize` do Core só reconhece um\r\n * `oidc_clients` válido, então registre e passe `oidcClientId` em produção.\r\n */\r\n oidcClientId?: string;\r\n /**\r\n * URL base da API Neetru. Default: `https://api.neetru.com`.\r\n * Útil em testes apontando pra staging ou mock server.\r\n */\r\n baseUrl?: string;\r\n /**\r\n * Implementação de fetch customizada. Default: `globalThis.fetch`.\r\n * Necessário em runtimes que não exponham fetch global por default.\r\n */\r\n fetch?: typeof globalThis.fetch;\r\n /**\r\n * Modo de runtime. `dev` ativa mocks (auth retorna user fixture, usage só\r\n * loga, support retorna lista fake). `workspace` e `prod` chamam HTTP real.\r\n * `dev-local` chama HTTP real contra o Core local do emulador da Dev Suite\r\n * (`neetru dev up`): sem `baseUrl` explícito, aponta pra `http://localhost:9003`.\r\n *\r\n * Default: lê `process.env.NEETRU_ENV` (Node) ou `globalThis.NEETRU_ENV`\r\n * (browser); fallback `prod`.\r\n */\r\n env?: 'dev' | 'workspace' | 'prod' | 'dev-local';\r\n /**\r\n * Override de mocks — útil em tests do consumer pra injetar fixtures\r\n * determinísticos (sem depender de NEETRU_ENV=dev).\r\n */\r\n mocks?: {\r\n auth?: AuthNamespace;\r\n usage?: UsageNamespace;\r\n support?: SupportNamespace;\r\n entitlements?: NeetruClient['entitlements'];\r\n /** v2.0: aceita NeetruDb (inclui MockDb). DbNamespace legado não é mais aceito. */\r\n db?: import('./db/client-db').NeetruDb;\r\n webhooks?: import('./webhooks').WebhooksNamespace;\r\n notifications?: import('./notifications').NotificationsNamespace;\r\n ai?: import('./ai').AiNamespace;\r\n };\r\n /**\r\n * v0.3 — productId default usado por `usage.report()` / `usage.check()` /\r\n * `db.collection()` quando não passado explicitamente nas options.\r\n */\r\n productId?: string;\r\n /**\r\n * v0.3 — tenantId default usado por `usage.report()` / `usage.check()` /\r\n * `db.collection()` quando não passado explicitamente nas options.\r\n */\r\n tenantId?: string;\r\n /**\r\n * v2.0 — Configuração do banco lógico: transport, dbId, coleções.\r\n * Gerado pelo scaffold `neetru db init`. Opcional — padrão é transport='rest'.\r\n *\r\n * Use `transport` (v2.3+) em vez do campo `engine` depreciado.\r\n * Valores: `'rest'` | `'firestore'` | `'nosql-vm'`.\r\n */\r\n db?: import('./db/client-db').NeetruDbOptions;\r\n /**\r\n * 3.0 — hosts adicionais permitidos para `baseUrl`. Por default, SDK aceita\r\n * `*.neetru.com` HTTPS + localhost. Para apontar pra staging/proxy custom em\r\n * `https://meu-proxy.empresa.com`, adicione `['empresa.com']`.\r\n *\r\n * Bypass total: `NEETRU_ALLOW_INSECURE_BASEURL=1` no env.\r\n */\r\n allowedHosts?: string[];\r\n}\r\n\r\n/**\r\n * Status do produto no catálogo público.\r\n */\r\nexport type ProductStatus = 'live' | 'soon' | 'beta';\r\n\r\n/**\r\n * Produto SaaS publicado pelo Neetru Core.\r\n *\r\n * Schema neutro — alinhado com `public_products/{slug}` no backend mas\r\n * nunca expõe campos internos (Firestore Timestamps, draft state, etc).\r\n */\r\nexport interface Product {\r\n /** Identificador estável do produto, ex: `neetru-pulse`. */\r\n slug: string;\r\n /** Nome de exibição, ex: `Neetru Pulse`. */\r\n name: string;\r\n /** Subtítulo curto, ex: `Gestão de operações`. */\r\n tagline?: string;\r\n /** Descrição longa em prosa. */\r\n description?: string;\r\n /** Status público do produto. */\r\n status?: ProductStatus;\r\n /** Hint de ícone (catálogo de keys interno do Core). */\r\n iconKey?: string;\r\n /** Link override do CTA principal (default: página do produto). */\r\n ctaHref?: string;\r\n /** Label override do CTA. */\r\n ctaLabel?: string;\r\n /** Lista opcional de planos cobrados. Pode ser preenchida v0.2+. */\r\n plans?: ProductPlan[];\r\n}\r\n\r\n/** Plano cobrado de um produto (placeholder v0.1 — schema final v0.2). */\r\nexport interface ProductPlan {\r\n id: string;\r\n name: string;\r\n /** Preço mensal em centavos (BRL). Pode ser undefined em planos custom. */\r\n amountCents?: number;\r\n features?: string[];\r\n}\r\n\r\n/**\r\n * Resposta do `client.catalog.list()`.\r\n */\r\nexport interface CatalogListResponse {\r\n products: Product[];\r\n fetchedAt: string;\r\n}\r\n\r\n/**\r\n * Resposta do `client.entitlements.check(productSlug, feature)`.\r\n *\r\n * `allowed` é o sinal forte; `reason` ajuda a debugar (não exibir pro user\r\n * final).\r\n */\r\nexport interface EntitlementCheck {\r\n allowed: boolean;\r\n productSlug: string;\r\n feature: string;\r\n /** Reason code estável: `granted` | `not_subscribed` | `feature_not_in_plan` | `expired`. */\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Payload pra `client.telemetry.event()`.\r\n */\r\nexport interface TelemetryEventInput {\r\n /** Nome do evento, ex: `dashboard_opened`, `report_exported`. */\r\n name: string;\r\n /** Atributos adicionais (chave → valor primitivo). */\r\n properties?: Record<string, string | number | boolean | null>;\r\n /** Timestamp ISO opcional; default = server time. */\r\n timestamp?: string;\r\n}\r\n\r\n/**\r\n * Confirmação de aceitação do evento de telemetria.\r\n */\r\nexport interface TelemetryEventAck {\r\n ok: true;\r\n /** ID gerado pelo backend pra evento (`usage_events/{id}`). */\r\n eventId: string;\r\n}\r\n\r\n// ─── Sprint 6 — Telemetry log per-product ──────────────────────────────────\r\n\r\nexport type TelemetryLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\r\n\r\nexport interface TelemetryLogInput {\r\n level: TelemetryLogLevel;\r\n message: string;\r\n metadata?: Record<string, unknown>;\r\n /** Override correlationId (default: lê dos headers/env automaticamente). */\r\n correlationId?: string;\r\n /** Slug do produto. Se ausente, o SDK usa `config.productId` como default\r\n * (o backend NÃO infere — exige productSlug no body, fail-closed). */\r\n productSlug?: string;\r\n}\r\n\r\nexport interface TelemetryLogAck {\r\n ok: true;\r\n /** ID do log gravado (`logs/{productId}/{yyyymmdd}/{logId}`) — opcional em mocks. */\r\n logId?: string;\r\n /** Indicador de modo (mock vs http). */\r\n mode: 'mock' | 'http';\r\n}\r\n\r\n/**\r\n * Handle imutável retornado por `createNeetruClient`. Carrega namespaces\r\n * `auth`, `catalog`, `entitlements`, `telemetry`, `usage`, `support`.\r\n *\r\n * Cada namespace é exposto como objeto chamável — espelha API Firebase\r\n * (`auth`, `firestore`) ou Stripe (`paymentIntents`, `customers`).\r\n */\r\nexport interface NeetruClient {\r\n /** Configuração resolvida (apiKey, baseUrl, fetch). Read-only. */\r\n readonly config: ResolvedConfig;\r\n /** Namespace auth (v0.2 — OIDC + mocks dev). */\r\n readonly auth: AuthNamespace;\r\n /** Namespace catálogo público de produtos. */\r\n readonly catalog: {\r\n /** Lista produtos publicados. */\r\n list(opts?: CatalogListOptions): Promise<CatalogListResponse>;\r\n /** Busca produto único por slug. */\r\n get(slug: string): Promise<Product>;\r\n };\r\n /** Namespace entitlements (v1.2: cache LRU 60s TTL). */\r\n readonly entitlements: {\r\n /**\r\n * Verifica se o portador da apiKey pode usar `feature` do produto `slug`.\r\n * Cacheado 60s. `{ cacheBust: true }` força ida ao servidor.\r\n */\r\n check(\r\n productSlug: string,\r\n feature: string,\r\n opts?: { cacheBust?: boolean },\r\n ): Promise<boolean>;\r\n /** Variante que retorna o objeto completo com `reason`. */\r\n checkDetailed(\r\n productSlug: string,\r\n feature: string,\r\n opts?: { cacheBust?: boolean },\r\n ): Promise<EntitlementCheck>;\r\n };\r\n /** Namespace telemetria (v1.2: track fire-and-forget + flush). */\r\n readonly telemetry: {\r\n /** Persiste um evento single-shot. await retorna { ok, eventId }. */\r\n event(input: TelemetryEventInput): Promise<TelemetryEventAck>;\r\n /** Fire-and-forget: enqueue + flush 500ms debounce. Sem retorno. */\r\n track(input: TelemetryEventInput): void;\r\n /** Força flush da queue do track() (chamar antes de unload). */\r\n flush(): Promise<void>;\r\n /**\r\n * Registra um log estruturado per-product (Sprint 6).\r\n *\r\n * - Em `NEETRU_ENV=dev`: apenas console.{level} (sem network).\r\n * - Em `workspace`/`prod`: POST `/api/sdk/v1/telemetry/log` com Bearer + correlationId.\r\n */\r\n log(input: TelemetryLogInput): Promise<TelemetryLogAck>;\r\n };\r\n /** Namespace usage (v0.2 — track + quota; v0.3 — report + check). */\r\n readonly usage: UsageNamespace;\r\n /** Namespace support (v0.2 — tickets). */\r\n readonly support: SupportNamespace;\r\n /**\r\n * Namespace db (v2.0 — Mundo Documentos offline-first + Mundo SQL).\r\n *\r\n * Breaking change v2.0: `client.db` agora é `NeetruDb` em vez de `DbNamespace`.\r\n * - `db.collection(name)` → `DbCollectionRef` (offline-first, com `onSnapshot`,\r\n * `batch`, `doc(id)`, `onDoc`; `list()` retorna `DbListResult` com cursor).\r\n * - `db.sql(schema)` → `Promise<NeetruSqlClient>` (Mundo SQL).\r\n * - `db.syncState` / `db.flush()` / `db.clearCache()` (camada offline).\r\n *\r\n * O antigo `DbNamespace` (list retornava `T[]`) foi removido em v2.0.\r\n */\r\n readonly db: import('./db/client-db').NeetruDb;\r\n /** Namespace checkout (v1.1 — start/get/cancel intent + auto-redirect). */\r\n readonly checkout: import('./checkout').CheckoutNamespace;\r\n /** Namespace webhooks (v1.2 — register/list/unregister/test). */\r\n readonly webhooks: import('./webhooks').WebhooksNamespace;\r\n /** Namespace notifications (v1.2 — send/list/markRead/dismiss). */\r\n readonly notifications: import('./notifications').NotificationsNamespace;\r\n /** Namespace ai (3.1.10 — AI Gateway: chat non-stream + stream SSE). */\r\n readonly ai: import('./ai').AiNamespace;\r\n}\r\n\r\n// ─── v0.2 — Auth namespace ──────────────────────────────────────────────────\r\n\r\n/**\r\n * User retornado pelo OIDC ID token. Schema neutro — não vaza Firebase\r\n * decoded token shape. Subset estável do RFC 7519 + custom claims Neetru.\r\n */\r\nexport interface NeetruUser {\r\n /** Subject — uid estável. */\r\n uid: string;\r\n email: string;\r\n emailVerified?: boolean;\r\n displayName?: string;\r\n photoURL?: string;\r\n /** Custom claim Neetru — true quando staff. */\r\n isStaff?: boolean;\r\n /** Custom claim Neetru — true quando customer enrolled. */\r\n isCustomer?: boolean;\r\n /** Tenant assigned (multi-tenant deployments). */\r\n tenantId?: string;\r\n /** Outros claims OIDC (aud, iss, iat, exp). */\r\n [extra: string]: unknown;\r\n}\r\n\r\nexport interface SignInOptions {\r\n /** OIDC redirect_uri override. Default = window.location origin. */\r\n redirectUri?: string;\r\n /** OIDC scope. Default `openid profile email`. */\r\n scope?: string;\r\n /** Onde mandar após login completo. Default = window.location.href. */\r\n postLoginRedirect?: string;\r\n}\r\n\r\nexport type AuthStateListener = (user: NeetruUser | null) => void;\r\n\r\n/**\r\n * 3.1.3 — Resultado de `auth.handleRedirectCallback()`: o material que o **backend\r\n * do produto** (confidential client) usa pra trocar o code por tokens no\r\n * endpoint `/api/v1/oauth/token` (POST x-www-form-urlencoded, `grant_type=\r\n * authorization_code`, autenticação `Basic base64(clientId:clientSecret)`).\r\n *\r\n * 3.1.12: para SPAs (public client, OAuth 2.1) prefira `completeSignIn()` —\r\n * ele faz a troca via PKCE puro (sem client_secret) e grava o `id_token`.\r\n * `handleRedirectCallback` continua disponível pra quem troca o code no\r\n * backend (confidential client, `Basic base64(clientId:clientSecret)`).\r\n */\r\nexport interface RedirectCallbackResult {\r\n /** `authorization_code` emitido pelo IdP — single-use, TTL curto. */\r\n code: string;\r\n /** PKCE `code_verifier` (RFC 7636) — enviar junto do code no `/token`. */\r\n codeVerifier: string;\r\n /**\r\n * `redirect_uri` exato usado no `/authorize`. O `/token` exige reenvio\r\n * idêntico (exact match) — passe este valor sem alterar.\r\n */\r\n redirectUri: string;\r\n /**\r\n * `nonce` enviado no `/authorize`. O backend deve validar que o claim\r\n * `nonce` do `id_token` retornado bate com este valor (anti-replay OIDC).\r\n */\r\n nonce: string;\r\n /** `state` validado (CSRF). Exposto pra logging/correlação. */\r\n state: string;\r\n /**\r\n * Destino pós-login que o app passou em `signIn({ postLoginRedirect })`.\r\n * `undefined` se não informado.\r\n */\r\n postLoginRedirect?: string;\r\n}\r\n\r\n/**\r\n * 3.1.12 — Resultado de `auth.completeSignIn()`: o login SPA PKCE está\r\n * COMPLETO — o `id_token` já foi validado (nonce), gravado em\r\n * `localStorage['neetru_id_token']` e os listeners de `onAuthStateChanged`\r\n * já foram notificados.\r\n */\r\nexport interface CompleteSignInResult {\r\n /** Usuário logado — mesmo shape retornado por `getUser()`. */\r\n user: NeetruUser;\r\n /**\r\n * Destino pós-login que o app passou em `signIn({ postLoginRedirect })`.\r\n * `undefined` se não informado. O SDK NÃO navega — o app decide.\r\n */\r\n postLoginRedirect?: string;\r\n}\r\n\r\nexport interface AuthNamespace {\r\n /**\r\n * Inicia fluxo de login OIDC (Authorization Code + PKCE). Em dev\r\n * (`NEETRU_ENV=dev`) retorna mock user direto sem redirect. Em browser\r\n * gera `state` (CSRF), `nonce` e o par PKCE `code_verifier`/`code_challenge`\r\n * (S256), persiste a transação em `sessionStorage` e redireciona pro\r\n * `/authorize`. Complete o fluxo no retorno com `handleRedirectCallback()`.\r\n */\r\n signIn(options?: SignInOptions): Promise<NeetruUser | void>;\r\n /**\r\n * 3.1.3 — Processa o retorno do IdP após `signIn`. Lê `code` + `state` da URL\r\n * (default `location.href`; passe `currentUrl` pra override), valida o\r\n * `state` contra a transação salva em `signIn` (CSRF), consome a transação\r\n * (single-use), limpa os params da URL e devolve o material pro backend\r\n * trocar por tokens. Retorna `null` se a URL não é um callback (sem `code`).\r\n *\r\n * @throws {NeetruError} `oidc_state_mismatch` se o `state` não bate ou não há\r\n * transação pendente; ou um erro com `.code` = o `error` do IdP (ex:\r\n * `access_denied`) quando a URL traz `?error=`.\r\n */\r\n handleRedirectCallback(currentUrl?: string): Promise<RedirectCallbackResult | null>;\r\n /**\r\n * 3.1.12 — Completa o login SPA PKCE (public client, OAuth 2.1): chama\r\n * `handleRedirectCallback(currentUrl)` e, se houver transação, troca o\r\n * `authorization_code` por tokens no `/api/v1/oauth/token` (POST\r\n * x-www-form-urlencoded com `code_verifier`, SEM client_secret), valida o\r\n * `nonce` do `id_token`, grava o token em `localStorage['neetru_id_token']`\r\n * e notifica os listeners de `onAuthStateChanged`.\r\n *\r\n * Seguro pra chamar em todo page load: retorna `null` quando a URL não é um\r\n * callback OIDC (sem `?code=`). Em dev (`MockAuth`) sempre retorna `null`.\r\n *\r\n * Fecha o gap descoberto no incidente pdv-agiliza 2026-07-10:\r\n * `handleRedirectCallback` parseava a volta do IdP mas NADA trocava o code\r\n * por tokens — o id_token nunca chegava ao storage que `getUser()` lê.\r\n *\r\n * @throws {NeetruError} `oidc_state_mismatch` (via `handleRedirectCallback`),\r\n * `token_exchange_failed` se o `/token` falhar ou vier sem `id_token`,\r\n * `oidc_nonce_mismatch` se o nonce do `id_token` não bater (token descartado).\r\n */\r\n completeSignIn(currentUrl?: string): Promise<CompleteSignInResult | null>;\r\n /**\r\n * 3.1.3 — Retorna o `id_token` cru (string JWT) que o app armazenou em\r\n * `localStorage['neetru_id_token']`, ou `null` se ausente/expirado. Útil pra\r\n * mandar como `Authorization: Bearer` ao BFF do produto. Para os claims\r\n * decodificados use `getUser()`; para validar assinatura use `verifyToken()`.\r\n */\r\n getIdToken(): string | null;\r\n /** Limpa session local + revoga refresh token no servidor. */\r\n signOut(): Promise<void>;\r\n /** Retorna o user atual (do id_token cached) ou `null` se não logado. */\r\n getUser(): NeetruUser | null;\r\n /**\r\n * Subscreve a mudanças de estado de auth. Listener é invocado imediatamente\r\n * com user atual. Retorna função de unsubscribe.\r\n */\r\n onAuthStateChanged(listener: AuthStateListener): () => void;\r\n /**\r\n * Valida um id_token JWT emitido por `auth.neetru.com` server-side.\r\n *\r\n * Usa JWKS local cacheado (1h TTL, configurável via `jwksCacheTtlMs`).\r\n * Ideal para Next.js route handlers, middleware e qualquer ambiente Node/Edge\r\n * server-side onde verificação rápida sem round-trip é necessária.\r\n *\r\n * @param token - JWT id_token obtido do cookie ou header Authorization.\r\n * @param options - Opções opcionais de verificação.\r\n * @returns `NeetruUser` com claims validados.\r\n *\r\n * @throws {NeetruError} com `.code`:\r\n * - `token_expired` — token expirado (`exp` no passado)\r\n * - `token_invalid_signature` — assinatura não verificada pela JWKS\r\n * - `token_invalid_audience` — `aud` não bate com o `clientId` configurado\r\n * - `token_invalid_issuer` — `iss` não é `https://auth.neetru.com`\r\n *\r\n * @example\r\n * ```ts\r\n * // Next.js route handler\r\n * import { createNeetruClient } from '@neetru/sdk';\r\n * const client = createNeetruClient({ apiKey: 'nrt_...' });\r\n *\r\n * export async function POST(req: Request) {\r\n * const token = req.cookies.get('neetru-session')?.value;\r\n * try {\r\n * const user = await client.auth.verifyToken(token);\r\n * // user.uid, user.email, user.isStaff etc disponíveis\r\n * } catch (err) {\r\n * return new Response('Unauthorized', { status: 401 });\r\n * }\r\n * }\r\n * ```\r\n */\r\n verifyToken(token: string, options?: VerifyTokenOptions): Promise<NeetruUser>;\r\n}\r\n\r\n/** Opções para `auth.verifyToken()`. */\r\nexport interface VerifyTokenOptions {\r\n /**\r\n * TTL do cache de JWKS em milissegundos. Default: 3600000 (1h).\r\n * Valores menores aumentam latência (mais fetches); maiores aumentam\r\n * o tempo de janela após rotação de chaves.\r\n */\r\n jwksCacheTtlMs?: number;\r\n /**\r\n * Audience (`aud`) esperada no token. Se ausente, o SDK tenta derivar do\r\n * `apiKey` configurado no client (campo `keyId`). Pelo menos uma das duas\r\n * fontes é **obrigatória** — `verifyToken` lança `invalid_config` se nenhuma\r\n * audience puder ser resolvida, pois verificar um token sem `aud` permitiria\r\n * tokens emitidos para outros clients passarem sem detecção.\r\n */\r\n audience?: string;\r\n}\r\n\r\n// ─── v0.2 — Usage namespace ─────────────────────────────────────────────────\r\n\r\n/**\r\n * 3.0 BREAKING: `UsageEventInput` mantido só como tipo para tests/legacy\r\n * imports — não é mais retorno de método. `usage.track`/`usage.getQuota`\r\n * **REMOVIDOS** em 3.0. Use `usage.report(resource, qty, options)`.\r\n */\r\nexport interface UsageEventInput {\r\n event: string;\r\n properties?: Record<string, string | number | boolean | null>;\r\n quantity?: number;\r\n}\r\n\r\n/**\r\n * 3.0 BREAKING: `UsageQuota` mantido só para compat de imports. Não é mais\r\n * retorno de método ativo. `usage.report()` devolve `{value, limit, remaining}`\r\n * inline; `usage.check()` devolve detalhes do entitlement.\r\n */\r\nexport interface UsageQuota {\r\n metric: string;\r\n used: number;\r\n limit: number;\r\n resetsAt?: string;\r\n plan?: string;\r\n}\r\n\r\n/**\r\n * 3.0 — superfície limpa: `report` (POST metered) + `check` (GET entitlement).\r\n *\r\n * Métodos removidos vs 2.x:\r\n * - `track(event, props)` — vaporware contra Core. Use `report()`.\r\n * - `getQuota(metric)` — endpoint nunca existiu. `report()` devolve quota.\r\n */\r\nexport interface UsageNamespace {\r\n /**\r\n * Reporta consumo metered. Incrementa `usage_counters` no Core atomicamente.\r\n * Idempotency-Key gerado por chamada (defesa anti-dup em retry transient).\r\n *\r\n * `productId`/`tenantId` lidos do contexto do client se ausentes nas options.\r\n */\r\n report(\r\n resource: string,\r\n qty?: number,\r\n options?: { productId?: string; tenantId?: string },\r\n ): Promise<{\r\n ok: true;\r\n counterId?: string;\r\n value?: number;\r\n limit?: number;\r\n remaining?: number;\r\n status?: string;\r\n }>;\r\n /**\r\n * Verifica entitlement de um resource/feature. GET cacheável (HTTP 60s).\r\n * Resposta inclui `allowed`, `reason`, `remaining`, `limit`, `planId`,\r\n * `planFeatures` e `behavior` ('readonly' quando limit_exceeded).\r\n */\r\n check(\r\n resource: string,\r\n options?: { productId?: string; tenantId?: string },\r\n ): Promise<{\r\n allowed: boolean;\r\n reason?: string;\r\n remaining?: number;\r\n limit?: number;\r\n planId?: string | null;\r\n planFeatures?: string[];\r\n behavior?: 'readonly' | null;\r\n }>;\r\n}\r\n\r\n// ─── v0.3 — Datastore namespace ────────────────────────────────────────────\r\n\r\n/**\r\n * Namespace `db` (v0.3) — wrapper minimalista para CRUD em coleções tenant-scoped\r\n * via Core REST. Em dev retorna fixtures in-memory por collection.\r\n *\r\n * O Core injeta automaticamente o tenantId no path do Firestore:\r\n * `tenant_{tid}_{name}/{docId}`.\r\n */\r\n/** Filtro `where` simples — alinhado com endpoint REST `field:op:value`. */\r\nexport interface DbWhereFilter {\r\n field: string;\r\n op: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in';\r\n value: string | number | boolean | null | Array<string | number | boolean>;\r\n}\r\n\r\nexport interface DbListOptions {\r\n limit?: number;\r\n /** Lista de filtros — máximo 5 (alinhado ao backend). */\r\n where?: DbWhereFilter[];\r\n}\r\n\r\nexport interface DbCollectionRef {\r\n /** Lista documentos com filtros opcionais. */\r\n list<T = Record<string, unknown>>(opts?: DbListOptions): Promise<T[]>;\r\n /** Lê um documento por id. Retorna `null` se não existe. */\r\n get<T = Record<string, unknown>>(id: string): Promise<T | null>;\r\n /** Cria/upsert um documento com id explícito. */\r\n set(id: string, data: Record<string, unknown>): Promise<{ ok: true }>;\r\n /**\r\n * v0.3.1 — Adiciona doc com id auto-gerado pelo backend. Retorna `{id}` do\r\n * doc criado. Diferente de `set(id, data)` que requer caller fornecer id.\r\n */\r\n add(data: Record<string, unknown>): Promise<{ ok: true; id: string }>;\r\n /** Atualiza doc com merge — só campos passados em `data`. */\r\n update(id: string, data: Record<string, unknown>): Promise<{ ok: true }>;\r\n /** Deleta um documento. */\r\n remove(id: string): Promise<{ ok: true }>;\r\n}\r\n\r\nexport interface DbNamespace {\r\n collection(name: string): DbCollectionRef;\r\n}\r\n\r\n// ─── v0.2 — Support namespace ───────────────────────────────────────────────\r\n\r\nexport type SupportSeverity = 'low' | 'normal' | 'high' | 'urgent';\r\nexport type SupportStatus = 'open' | 'pending' | 'resolved' | 'closed';\r\n\r\nexport interface SupportTicket {\r\n id: string;\r\n subject: string;\r\n message: string;\r\n severity: SupportSeverity;\r\n status: SupportStatus;\r\n createdAt: string;\r\n updatedAt?: string;\r\n /** Slug do produto referenciado. */\r\n productSlug?: string;\r\n /**\r\n * 3.4: URL da página onde o feedback/problema foi originado (input de\r\n * `createTicket`). `null` quando não informado.\r\n */\r\n pageUrl: string | null;\r\n /**\r\n * 3.4: URL do primeiro anexo (screenshot) do ticket, quando enviado via\r\n * `createTicket({ screenshotBase64 })`. `null` se nenhum anexo.\r\n */\r\n screenshotUrl: string | null;\r\n /**\r\n * 3.4: ISO — setado pelo Core na transição do ticket pra `resolved`.\r\n * `null` enquanto não resolvido.\r\n */\r\n resolvedAt: string | null;\r\n /**\r\n * 3.4: ISO — setado por `markResolvedSeen` (idempotente, só na 1ª vez que\r\n * o cliente vê a notificação de resolução). `null` até então.\r\n */\r\n resolvedNotifiedAt: string | null;\r\n /**\r\n * 3.4: quantas vezes o modal de satisfação foi exibido\r\n * (`recordSatisfactionAttempt`). Default `0`.\r\n */\r\n satisfactionAttempts: number;\r\n /** 3.4: ISO da última tentativa de exibição do modal de satisfação. */\r\n lastSatisfactionAttemptAt: string | null;\r\n /**\r\n * 3.4: ISO — setado por `submitSatisfaction` (avaliação OU skip). Marca o\r\n * ciclo de satisfação como concluído (terminal, até uma eventual\r\n * `contestTicket`).\r\n */\r\n satisfiedAt: string | null;\r\n /** 3.4: nota CSAT 1-5, ou `null` se ainda não avaliado / pulado. */\r\n rating: number | null;\r\n /** 3.4: comentário opcional da avaliação de satisfação. */\r\n satisfactionComment: string | null;\r\n}\r\n\r\nexport interface CreateTicketInput {\r\n subject: string;\r\n message: string;\r\n severity?: SupportSeverity;\r\n productSlug?: string;\r\n /**\r\n * **L-05 fix (Opus review 2026-05-27):** metadata opcional (Core já aceita\r\n * via `metadata: z.record(...)` no Zod). Útil pra anexar contexto como\r\n * `{ orderId, userAgent, route, sessionId }` sem inflar subject/message.\r\n *\r\n * Chaves limitadas a 64 chars; valores primitive (string/number/boolean/null).\r\n */\r\n metadata?: Record<string, string | number | boolean | null>;\r\n /**\r\n * 3.4: URL da página onde o problema/feedback ocorreu. Propagado como\r\n * `pageUrl` no ticket.\r\n */\r\n pageUrl?: string;\r\n /**\r\n * 3.4: screenshot em data URL base64 (cap ~700KB — mesmo limite do\r\n * `report-problem.ts` do Core). O Core converte em `attachments`/\r\n * `screenshotAttached`.\r\n */\r\n screenshotBase64?: string;\r\n /** 3.4: logs de console relevantes capturados no momento do report. */\r\n consoleLogs?: string;\r\n}\r\n\r\nexport interface SupportNamespace {\r\n /**\r\n * Cria um novo ticket. **3.0**: POST `/api/sdk/v1/support/tickets`\r\n * (canonical, requer `productId` no body — default `config.productId`).\r\n * Mock em dev.\r\n */\r\n createTicket(input: CreateTicketInput): Promise<SupportTicket>;\r\n /**\r\n * Lista meus tickets abertos. **3.0**: GET `/api/sdk/v1/support/tickets?productId=...`\r\n * (canonical). `options.productSlug` override do `config.productId` default.\r\n */\r\n listMyTickets(options?: { productSlug?: string }): Promise<SupportTicket[]>;\r\n /**\r\n * 3.4: marca que o usuário viu a notificação de resolução do ticket\r\n * (`resolvedNotifiedAt`, idempotente — só seta se ainda `null` no Core).\r\n * POST `/api/sdk/v1/support/tickets/{id}/resolved-seen`. Requer Bearer +\r\n * ownership + ticket em `resolved`.\r\n */\r\n markResolvedSeen(ticketId: string): Promise<void>;\r\n /**\r\n * 3.4: registra uma tentativa de exibição do modal de satisfação\r\n * (`satisfactionAttempts += 1`, `lastSatisfactionAttemptAt = now`).\r\n * **Chamar no SHOW do modal, não na resposta** — exibir já conta como\r\n * tentativa, evita reprompt infinito. POST\r\n * `/api/sdk/v1/support/tickets/{id}/satisfaction-attempt`. Requer Bearer +\r\n * ownership + ticket em `resolved`.\r\n */\r\n recordSatisfactionAttempt(ticketId: string): Promise<void>;\r\n /**\r\n * 3.4: envia (ou pula) a avaliação de satisfação. `rating` é CSAT 1-5 ou\r\n * `null` (junto de `skipped: true`). Terminal — seta `satisfiedAt`. POST\r\n * `/api/sdk/v1/support/tickets/{id}/satisfaction`. Requer Bearer +\r\n * ownership.\r\n */\r\n submitSatisfaction(input: {\r\n ticketId: string;\r\n rating: number | null;\r\n comment?: string | null;\r\n skipped: boolean;\r\n }): Promise<void>;\r\n /**\r\n * 3.4: contesta um ticket resolvido — reabre (`status='open'`) e zera o\r\n * ciclo de satisfação/notificação (`resolvedAt`, `resolvedNotifiedAt`,\r\n * `satisfactionAttempts`, `lastSatisfactionAttemptAt`, `satisfiedAt`,\r\n * `rating`, `satisfactionComment`). POST\r\n * `/api/sdk/v1/support/tickets/{id}/contest`. Requer Bearer + ownership +\r\n * ticket em `resolved`.\r\n */\r\n contestTicket(ticketId: string, opts?: { comment?: string }): Promise<void>;\r\n}\r\n\r\n// ─── v0.2 — Env detection ───────────────────────────────────────────────────\r\n\r\n/**\r\n * Modo do SDK. `dev` ativa mocks automáticos (úteis pra testes e\r\n * desenvolvimento sem precisar de backend rodando). `dev-local` chama HTTP\r\n * real contra o Core local do emulador (`neetru dev up`) — não é mock.\r\n */\r\nexport type NeetruEnv = 'dev' | 'workspace' | 'prod' | 'dev-local';\r\n\r\n/**\r\n * Configuração resolvida (defaults aplicados, fetch garantido).\r\n */\r\nexport interface ResolvedConfig {\r\n readonly apiKey?: string;\r\n /** 3.1.3 — client_id OIDC confidencial do produto (vide NeetruClientConfig). */\r\n readonly oidcClientId?: string;\r\n readonly baseUrl: string;\r\n readonly fetch: typeof globalThis.fetch;\r\n /** Resolved env — `dev` | `workspace` | `prod`. Default `prod`. */\r\n readonly env: NeetruEnv;\r\n /** Default productId (v0.3). */\r\n readonly productId?: string;\r\n /** Default tenantId (v0.3). */\r\n readonly tenantId?: string;\r\n}\r\n\r\n/** Opções pra `catalog.list()`. */\r\nexport interface CatalogListOptions {\r\n /** Inclui rascunhos (apenas com Bearer staff). Default false. */\r\n includeDrafts?: boolean;\r\n}\r\n","/**\r\n * Barra de conta / app-switcher padrão da Neetru — subpath `@neetru/sdk/react`.\r\n *\r\n * Um componente DROP-IN que TODO produto SaaS Neetru coloca no header:\r\n * - **Avatar** (foto ou iniciais) → menu de conta: nome/e-mail + \"Minha conta\r\n * Neetru\" (minhaconta.neetru.com) + \"Configurações da conta\" (deste produto)\r\n * + \"Sair\".\r\n * - **Waffle** (grid 9-pontos) → grid dos OUTROS produtos SaaS Neetru que o\r\n * cliente tem acesso, pra pular de um pro outro. O produto atual é destacado.\r\n *\r\n * Fonte dos dados: `GET /api/sdk/v1/account/bar` no Neetru Core, autenticado\r\n * com o **id_token** do end-user (o SDK já o guarda; `client.auth.getIdToken()`).\r\n * O componente NÃO faz fetch cru cross-repo — a busca passa por\r\n * `fetchNeetruAccountBar`, que é parte do próprio SDK (o conduíte canônico).\r\n *\r\n * Estilo: BMW (rounded-none, sem sombra/glassmorphism), inline (roda em\r\n * qualquer app com ou sem Tailwind). Superfície de CLIENTE → sempre CLARO\r\n * (não adere a dark mode; dark é staff-only). Acessível: teclado (setas, Tab\r\n * trap, Esc), aria, foco devolvido ao gatilho. i18n PT-BR (override via `labels`).\r\n *\r\n * Autorado com `React.createElement` (sem JSX) igual ao resto de `react.ts` —\r\n * mantém o build `.ts` sem depender de config JSX no consumidor.\r\n */\r\nimport * as React from 'react';\r\nimport { DEFAULT_BASE_URL } from './types';\r\nimport type { NeetruClient } from './types';\r\n\r\nconst h = React.createElement;\r\n\r\n// ─── Tipos do contrato ───────────────────────────────────────────────────────\r\n\r\nexport interface AccountBarAccount {\r\n displayName: string;\r\n email: string;\r\n photoUrl?: string | null;\r\n}\r\n\r\nexport interface AccountBarProductItem {\r\n slug: string;\r\n name: string;\r\n iconUrl?: string;\r\n url: string;\r\n}\r\n\r\nexport interface AccountBarData {\r\n account: AccountBarAccount;\r\n accountUrl: string;\r\n products: AccountBarProductItem[];\r\n}\r\n\r\n/** Textos da UI — default PT-BR, override parcial via prop `labels`. */\r\nexport interface AccountBarLabels {\r\n /** aria-label do botão de conta. */\r\n accountButton: string;\r\n /** aria-label do botão waffle. */\r\n appsButton: string;\r\n /** Título da seção de produtos no popover waffle. */\r\n appsTitle: string;\r\n /** Item \"Minha conta Neetru\". */\r\n myAccount: string;\r\n /** Item \"Configurações da conta\". */\r\n accountSettings: string;\r\n /** Item \"Sair\". */\r\n signOut: string;\r\n /** Badge no produto atual. */\r\n currentBadge: string;\r\n /** Estado vazio (sem outros produtos). */\r\n emptyProducts: string;\r\n}\r\n\r\nconst DEFAULT_LABELS: AccountBarLabels = {\r\n accountButton: 'Abrir menu da conta',\r\n appsButton: 'Seus produtos Neetru',\r\n appsTitle: 'Trocar de produto',\r\n myAccount: 'Minha conta Neetru',\r\n accountSettings: 'Configurações da conta',\r\n signOut: 'Sair',\r\n currentBadge: 'Atual',\r\n emptyProducts: 'Nenhum outro produto disponível.',\r\n};\r\n\r\n// ─── Fetch (conduíte SDK) ────────────────────────────────────────────────────\r\n\r\n/**\r\n * Busca os dados da barra de conta no Core usando o id_token do end-user.\r\n *\r\n * Retorna `null` quando não há usuário logado (sem id_token) ou a resposta não\r\n * é OK — o componente trata como \"não renderizar\" / usar identidade local.\r\n *\r\n * Exportado à parte pra ser reusável fora de React (ex.: pré-carregar no BFF do\r\n * produto) e testável isoladamente.\r\n */\r\nexport async function fetchNeetruAccountBar(\r\n client: NeetruClient,\r\n): Promise<AccountBarData | null> {\r\n const auth = client.auth as { getIdToken?: () => string | null } | undefined;\r\n const idToken = typeof auth?.getIdToken === 'function' ? auth.getIdToken() : null;\r\n if (!idToken) return null;\r\n\r\n const config = client.config as { baseUrl?: string; fetch?: typeof fetch };\r\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\r\n const fetchFn = config.fetch ?? globalThis.fetch;\r\n const url = `${baseUrl}/api/sdk/v1/account/bar`;\r\n\r\n try {\r\n const res = await fetchFn(url, {\r\n headers: { authorization: `Bearer ${idToken}`, accept: 'application/json' },\r\n });\r\n if (!res.ok) return null;\r\n const body = (await res.json()) as AccountBarData;\r\n if (!body || typeof body !== 'object' || !body.account) return null;\r\n return {\r\n account: {\r\n displayName: String(body.account.displayName ?? ''),\r\n email: String(body.account.email ?? ''),\r\n photoUrl: body.account.photoUrl ?? null,\r\n },\r\n accountUrl: String(body.accountUrl ?? 'https://minhaconta.neetru.com'),\r\n products: Array.isArray(body.products)\r\n ? body.products.filter(\r\n (p): p is AccountBarProductItem =>\r\n !!p && typeof p.slug === 'string' && typeof p.url === 'string',\r\n )\r\n : [],\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ─── Helpers de UI ───────────────────────────────────────────────────────────\r\n\r\n/** Iniciais (até 2) a partir do nome/e-mail pro fallback do avatar/monograma. */\r\nexport function initialsFrom(nameOrEmail: string): string {\r\n const s = (nameOrEmail ?? '').trim();\r\n if (!s) return '?';\r\n const local = s.includes('@') ? s.split('@')[0] : s;\r\n const parts = local.split(/[\\s._-]+/).filter(Boolean);\r\n if (parts.length === 0) return local.slice(0, 1).toUpperCase();\r\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\r\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\r\n}\r\n\r\n/** Paleta BMW clara — superfície de cliente força claro. */\r\nconst C = {\r\n bg: '#ffffff',\r\n bgHover: '#f5f5f5',\r\n fg: '#0a0a0a',\r\n muted: '#666666',\r\n border: '#0a0a0a',\r\n borderSoft: '#e5e5e5',\r\n};\r\n\r\nconst SQUARE = 0; // rounded-none\r\n\r\nfunction WaffleIcon({ size = 18, color = C.fg }: { size?: number; color?: string }): React.ReactElement {\r\n const dots = [0, 1, 2].flatMap((r) => [0, 1, 2].map((c) => ({ r, c })));\r\n const gap = size / 3;\r\n const dot = Math.max(2, size / 7);\r\n return h(\r\n 'svg',\r\n { width: size, height: size, viewBox: `0 0 ${size} ${size}`, 'aria-hidden': true, style: { display: 'block' } },\r\n dots.map(({ r, c }) =>\r\n h('rect', {\r\n key: `${r}-${c}`,\r\n x: c * gap + (gap - dot) / 2,\r\n y: r * gap + (gap - dot) / 2,\r\n width: dot,\r\n height: dot,\r\n fill: color,\r\n }),\r\n ),\r\n );\r\n}\r\n\r\nfunction focusables(container: HTMLElement | null): HTMLElement[] {\r\n if (!container) return [];\r\n return Array.from(\r\n container.querySelectorAll<HTMLElement>(\r\n 'a[href], button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n ),\r\n ).filter((el) => el.offsetParent !== null || el === document.activeElement);\r\n}\r\n\r\n// ─── Componente ──────────────────────────────────────────────────────────────\r\n\r\nexport interface NeetruAccountBarProps {\r\n /** Cliente SDK Neetru — fornece `config.baseUrl` + `auth.getIdToken()`. */\r\n client: NeetruClient;\r\n /** Slug do produto atual — destacado no grid waffle. */\r\n currentProductSlug?: string;\r\n /** Link \"Configurações da conta\" DESTE produto (rota interna do app). Omite o item se ausente. */\r\n settingsUrl?: string;\r\n /** Chamado após `signOut` concluir (ex.: redirecionar pro login). */\r\n onSignOut?: () => void;\r\n /** Override do fetcher — default `fetchNeetruAccountBar(client)`. Útil em teste/SSR. */\r\n fetchBar?: (client: NeetruClient) => Promise<AccountBarData | null>;\r\n /** Override parcial dos textos (default PT-BR). */\r\n labels?: Partial<AccountBarLabels>;\r\n /** Classe extra no container (o resto do estilo é inline). */\r\n className?: string;\r\n}\r\n\r\ntype OpenState = 'account' | 'apps' | null;\r\n\r\n/**\r\n * `<NeetruAccountBar client={client} currentProductSlug=\"pdv-agiliza\" settingsUrl=\"/config\" />`\r\n *\r\n * Renderiza waffle + avatar. Identidade aparece instantânea (do id_token via\r\n * `client.auth.getUser()`); produtos chegam do Core em seguida.\r\n */\r\nexport function NeetruAccountBar({\r\n client,\r\n currentProductSlug,\r\n settingsUrl,\r\n onSignOut,\r\n fetchBar,\r\n labels,\r\n className,\r\n}: NeetruAccountBarProps): React.ReactElement | null {\r\n const L: AccountBarLabels = { ...DEFAULT_LABELS, ...labels };\r\n\r\n // Identidade instantânea a partir do token já em mãos (sem esperar o fetch).\r\n const seedAccount = React.useMemo<AccountBarAccount | null>(() => {\r\n try {\r\n const u = client.auth.getUser();\r\n if (!u) return null;\r\n return {\r\n displayName: (u.displayName as string) || u.email || '',\r\n email: u.email || '',\r\n photoUrl: (u.photoURL as string) ?? null,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n }, [client]);\r\n\r\n const [data, setData] = React.useState<AccountBarData | null>(null);\r\n const [open, setOpen] = React.useState<OpenState>(null);\r\n const [signingOut, setSigningOut] = React.useState(false);\r\n\r\n const containerRef = React.useRef<HTMLDivElement | null>(null);\r\n const accountBtnRef = React.useRef<HTMLButtonElement | null>(null);\r\n const appsBtnRef = React.useRef<HTMLButtonElement | null>(null);\r\n const popoverRef = React.useRef<HTMLDivElement | null>(null);\r\n\r\n // Busca os dados no Core (produtos + identidade autoritativa).\r\n React.useEffect(() => {\r\n let cancelled = false;\r\n const run = fetchBar ?? fetchNeetruAccountBar;\r\n Promise.resolve(run(client))\r\n .then((res) => {\r\n if (!cancelled && res) setData(res);\r\n })\r\n .catch(() => {\r\n /* fail-soft: mantém identidade seed, sem produtos */\r\n });\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [client, fetchBar]);\r\n\r\n const account: AccountBarAccount | null = data?.account ?? seedAccount;\r\n const accountUrl = data?.accountUrl ?? 'https://minhaconta.neetru.com';\r\n const products = data?.products ?? [];\r\n\r\n const closeAndRefocus = React.useCallback((which: OpenState) => {\r\n setOpen(null);\r\n const trigger = which === 'apps' ? appsBtnRef.current : accountBtnRef.current;\r\n trigger?.focus();\r\n }, []);\r\n\r\n // Foco inicial no popover + fechar ao clicar fora.\r\n React.useEffect(() => {\r\n if (!open) return;\r\n const first = focusables(popoverRef.current)[0];\r\n first?.focus();\r\n\r\n const onDocMouseDown = (e: MouseEvent) => {\r\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\r\n setOpen(null);\r\n }\r\n };\r\n document.addEventListener('mousedown', onDocMouseDown);\r\n return () => document.removeEventListener('mousedown', onDocMouseDown);\r\n }, [open]);\r\n\r\n const onPopoverKeyDown = React.useCallback(\r\n (e: React.KeyboardEvent<HTMLDivElement>) => {\r\n if (e.key === 'Escape') {\r\n e.stopPropagation();\r\n closeAndRefocus(open);\r\n return;\r\n }\r\n const items = focusables(popoverRef.current);\r\n if (items.length === 0) return;\r\n const idx = items.indexOf(document.activeElement as HTMLElement);\r\n if (e.key === 'Tab') {\r\n // Focus trap.\r\n if (e.shiftKey && idx <= 0) {\r\n e.preventDefault();\r\n items[items.length - 1].focus();\r\n } else if (!e.shiftKey && idx === items.length - 1) {\r\n e.preventDefault();\r\n items[0].focus();\r\n }\r\n } else if (e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n items[(idx + 1) % items.length].focus();\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n items[(idx - 1 + items.length) % items.length].focus();\r\n }\r\n },\r\n [open, closeAndRefocus],\r\n );\r\n\r\n const handleSignOut = React.useCallback(async () => {\r\n if (signingOut) return;\r\n setSigningOut(true);\r\n try {\r\n await client.auth.signOut();\r\n } catch {\r\n /* fail-soft — segue com o callback do produto */\r\n } finally {\r\n setSigningOut(false);\r\n setOpen(null);\r\n onSignOut?.();\r\n }\r\n }, [client, signingOut, onSignOut]);\r\n\r\n // Sem identidade nenhuma → não renderiza (produto trata logged-out).\r\n if (!account) return null;\r\n\r\n const initials = initialsFrom(account.displayName || account.email);\r\n\r\n // ── Avatar ─────────────────────────────────────────────────────────────────\r\n const avatarInner = account.photoUrl\r\n ? h('img', {\r\n src: account.photoUrl,\r\n alt: '',\r\n width: 32,\r\n height: 32,\r\n style: { width: 32, height: 32, objectFit: 'cover', display: 'block' },\r\n })\r\n : h(\r\n 'span',\r\n {\r\n 'aria-hidden': true,\r\n style: {\r\n width: 32,\r\n height: 32,\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: C.fg,\r\n color: '#ffffff',\r\n fontSize: 13,\r\n fontWeight: 700,\r\n letterSpacing: '0.02em',\r\n },\r\n },\r\n initials,\r\n );\r\n\r\n const iconBtnStyle = (active: boolean): React.CSSProperties => ({\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 36,\r\n height: 36,\r\n padding: 0,\r\n borderRadius: SQUARE,\r\n border: `1px solid ${active ? C.border : C.borderSoft}`,\r\n backgroundColor: active ? C.bgHover : C.bg,\r\n color: C.fg,\r\n cursor: 'pointer',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n overflow: 'hidden',\r\n });\r\n\r\n const appsButton = h(\r\n 'button',\r\n {\r\n type: 'button',\r\n ref: appsBtnRef,\r\n 'aria-label': L.appsButton,\r\n 'aria-haspopup': 'menu',\r\n 'aria-expanded': open === 'apps',\r\n 'data-neetru-appswitcher-button': 'true',\r\n onClick: () => setOpen((o) => (o === 'apps' ? null : 'apps')),\r\n style: iconBtnStyle(open === 'apps'),\r\n },\r\n h(WaffleIcon, {}),\r\n );\r\n\r\n const accountButton = h(\r\n 'button',\r\n {\r\n type: 'button',\r\n ref: accountBtnRef,\r\n 'aria-label': `${L.accountButton}${account.displayName ? ` (${account.displayName})` : ''}`,\r\n 'aria-haspopup': 'menu',\r\n 'aria-expanded': open === 'account',\r\n 'data-neetru-account-button': 'true',\r\n onClick: () => setOpen((o) => (o === 'account' ? null : 'account')),\r\n style: { ...iconBtnStyle(open === 'account'), width: 36, height: 36 },\r\n },\r\n avatarInner,\r\n );\r\n\r\n // ── Popover base ─────────────────────────────────────────────────────────────\r\n const popoverStyle: React.CSSProperties = {\r\n position: 'absolute',\r\n top: 'calc(100% + 6px)',\r\n right: 0,\r\n minWidth: 260,\r\n maxWidth: 320,\r\n backgroundColor: C.bg,\r\n border: `1px solid ${C.border}`,\r\n borderRadius: SQUARE,\r\n zIndex: 1000,\r\n color: C.fg,\r\n fontFamily: 'inherit',\r\n // BMW: sem sombra/glassmorphism — borda sólida define o recorte.\r\n };\r\n\r\n const menuItemStyle: React.CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 10,\r\n width: '100%',\r\n padding: '10px 14px',\r\n background: 'transparent',\r\n border: 'none',\r\n borderTop: `1px solid ${C.borderSoft}`,\r\n color: C.fg,\r\n fontSize: 14,\r\n fontWeight: 400,\r\n textAlign: 'left',\r\n textDecoration: 'none',\r\n cursor: 'pointer',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n };\r\n\r\n const hoverHandlers = {\r\n onMouseEnter: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = C.bgHover;\r\n },\r\n onMouseLeave: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = 'transparent';\r\n },\r\n };\r\n\r\n // ── Popover: conta ───────────────────────────────────────────────────────────\r\n const accountPopover =\r\n open === 'account'\r\n ? h(\r\n 'div',\r\n {\r\n ref: popoverRef,\r\n role: 'menu',\r\n 'aria-label': L.accountButton,\r\n 'data-neetru-account-menu': 'true',\r\n onKeyDown: onPopoverKeyDown,\r\n style: popoverStyle,\r\n },\r\n // Header identidade\r\n h(\r\n 'div',\r\n { style: { display: 'flex', gap: 12, alignItems: 'center', padding: 14 } },\r\n h(\r\n 'span',\r\n {\r\n 'aria-hidden': true,\r\n style: {\r\n width: 40,\r\n height: 40,\r\n flexShrink: 0,\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n overflow: 'hidden',\r\n backgroundColor: account.photoUrl ? 'transparent' : C.fg,\r\n color: '#ffffff',\r\n fontSize: 15,\r\n fontWeight: 700,\r\n },\r\n },\r\n account.photoUrl\r\n ? h('img', {\r\n src: account.photoUrl,\r\n alt: '',\r\n width: 40,\r\n height: 40,\r\n style: { width: 40, height: 40, objectFit: 'cover', display: 'block' },\r\n })\r\n : initials,\r\n ),\r\n h(\r\n 'span',\r\n { style: { display: 'flex', flexDirection: 'column', minWidth: 0 } },\r\n h(\r\n 'span',\r\n {\r\n style: {\r\n fontWeight: 700,\r\n fontSize: 14,\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n },\r\n },\r\n account.displayName || account.email,\r\n ),\r\n account.email\r\n ? h(\r\n 'span',\r\n {\r\n style: {\r\n color: C.muted,\r\n fontSize: 12,\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n },\r\n },\r\n account.email,\r\n )\r\n : null,\r\n ),\r\n ),\r\n // Minha conta Neetru\r\n h(\r\n 'a',\r\n {\r\n role: 'menuitem',\r\n href: accountUrl,\r\n target: '_blank',\r\n rel: 'noopener noreferrer',\r\n style: menuItemStyle,\r\n ...hoverHandlers,\r\n },\r\n L.myAccount,\r\n ),\r\n // Configurações da conta (deste produto)\r\n settingsUrl\r\n ? h(\r\n 'a',\r\n { role: 'menuitem', href: settingsUrl, style: menuItemStyle, ...hoverHandlers },\r\n L.accountSettings,\r\n )\r\n : null,\r\n // Sair\r\n h(\r\n 'button',\r\n {\r\n type: 'button',\r\n role: 'menuitem',\r\n onClick: handleSignOut,\r\n disabled: signingOut,\r\n 'data-neetru-signout': 'true',\r\n style: { ...menuItemStyle, cursor: signingOut ? 'not-allowed' : 'pointer' },\r\n ...hoverHandlers,\r\n },\r\n signingOut ? `${L.signOut}…` : L.signOut,\r\n ),\r\n )\r\n : null;\r\n\r\n // ── Popover: produtos (waffle) ───────────────────────────────────────────────\r\n const appsPopover =\r\n open === 'apps'\r\n ? h(\r\n 'div',\r\n {\r\n ref: popoverRef,\r\n role: 'menu',\r\n 'aria-label': L.appsTitle,\r\n 'data-neetru-appswitcher-menu': 'true',\r\n onKeyDown: onPopoverKeyDown,\r\n style: { ...popoverStyle, minWidth: 280 },\r\n },\r\n h(\r\n 'div',\r\n {\r\n style: {\r\n padding: '12px 14px 8px',\r\n fontSize: 11,\r\n fontWeight: 700,\r\n letterSpacing: '0.08em',\r\n textTransform: 'uppercase',\r\n color: C.muted,\r\n },\r\n },\r\n L.appsTitle,\r\n ),\r\n products.length === 0\r\n ? h(\r\n 'div',\r\n { style: { padding: '4px 14px 16px', fontSize: 13, color: C.muted } },\r\n L.emptyProducts,\r\n )\r\n : h(\r\n 'div',\r\n {\r\n style: {\r\n display: 'grid',\r\n gridTemplateColumns: '1fr 1fr',\r\n gap: 1,\r\n padding: 1,\r\n backgroundColor: C.borderSoft,\r\n },\r\n },\r\n products.map((p) => {\r\n const isCurrent = !!currentProductSlug && p.slug === currentProductSlug;\r\n return h(\r\n 'a',\r\n {\r\n key: p.slug,\r\n role: 'menuitem',\r\n href: p.url,\r\n 'aria-current': isCurrent ? 'true' : undefined,\r\n 'data-neetru-product': p.slug,\r\n 'data-current': isCurrent ? 'true' : undefined,\r\n title: p.name,\r\n style: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n gap: 6,\r\n padding: '14px 8px',\r\n textDecoration: 'none',\r\n color: C.fg,\r\n backgroundColor: isCurrent ? C.bgHover : C.bg,\r\n cursor: 'pointer',\r\n boxSizing: 'border-box',\r\n },\r\n onMouseEnter: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = C.bgHover;\r\n },\r\n onMouseLeave: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = isCurrent\r\n ? C.bgHover\r\n : C.bg;\r\n },\r\n },\r\n // Tile/ícone\r\n p.iconUrl\r\n ? h('img', {\r\n src: p.iconUrl,\r\n alt: '',\r\n width: 32,\r\n height: 32,\r\n style: { width: 32, height: 32, objectFit: 'cover', display: 'block' },\r\n })\r\n : h(\r\n 'span',\r\n {\r\n 'aria-hidden': true,\r\n style: {\r\n width: 32,\r\n height: 32,\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: C.fg,\r\n color: '#ffffff',\r\n fontSize: 13,\r\n fontWeight: 700,\r\n },\r\n },\r\n initialsFrom(p.name),\r\n ),\r\n h(\r\n 'span',\r\n {\r\n style: {\r\n fontSize: 12,\r\n fontWeight: isCurrent ? 700 : 400,\r\n textAlign: 'center',\r\n lineHeight: 1.2,\r\n maxWidth: '100%',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n width: '100%',\r\n },\r\n },\r\n p.name,\r\n ),\r\n isCurrent\r\n ? h(\r\n 'span',\r\n {\r\n style: {\r\n fontSize: 9,\r\n fontWeight: 700,\r\n letterSpacing: '0.08em',\r\n textTransform: 'uppercase',\r\n color: C.muted,\r\n },\r\n },\r\n L.currentBadge,\r\n )\r\n : null,\r\n );\r\n }),\r\n ),\r\n )\r\n : null;\r\n\r\n return h(\r\n 'div',\r\n {\r\n ref: containerRef,\r\n className,\r\n 'data-neetru-account-bar': 'true',\r\n style: {\r\n position: 'relative',\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n gap: 8,\r\n // Superfície de cliente = sempre claro (dark é staff-only).\r\n colorScheme: 'light',\r\n fontFamily: 'inherit',\r\n },\r\n },\r\n appsButton,\r\n accountButton,\r\n accountPopover,\r\n appsPopover,\r\n );\r\n}\r\n","/**\r\n * @neetru/sdk/firestore-compat — camada de COMPATIBILIDADE (transicional).\r\n *\r\n * ⚠️ **STATUS: compat / transitional.** Este módulo espelha a superfície\r\n * modular do Firebase Firestore (`doc`, `collection`, `query`, `where`,\r\n * `getDocs`, `setDoc`, `onSnapshot`, `writeBatch`, `runTransaction`,\r\n * `serverTimestamp`, `Timestamp`, `FieldValue`, …) para que produtos que\r\n * migram do Firebase **não precisem reescrever as chamadas à mão**.\r\n *\r\n * Ele NÃO é a API nativa do SDK e NÃO compete com `client.db` (NeetruDb).\r\n * É um auxílio de migração: troque os imports de `firebase/firestore` por\r\n * `@neetru/sdk/firestore-compat`, valide, e depois migre incrementalmente\r\n * para a superfície nativa `client.db.collection(...)` quando fizer sentido.\r\n *\r\n * Esta implementação foi desenhada a partir dos shims reais que os produtos\r\n * `neetru-gestovendas` (`src/neetru/firestore.ts`) e `neetru-pdvagiliza`\r\n * (`src/lib/neetru/firestore-compat.ts`) escreveram à mão — espelha\r\n * exatamente a superfície que eles precisam para poder **deletar os shims**.\r\n *\r\n * ### Backing\r\n * Tudo flui por `client.db` (`NeetruDb`). As funções recebem ou um `NeetruDb`\r\n * (o `client.db`) ou o `NeetruClient` inteiro como primeiro argumento — ambos\r\n * são aceitos por `getFirestore(...)`/`collection(...)`/`doc(...)`.\r\n *\r\n * ### Diferenças deliberadas vs Firebase\r\n * - `serverTimestamp()` e `increment()`/`arrayUnion()`/`arrayRemove()` retornam\r\n * sentinels do SDK (`@neetru/sdk` `serverTimestamp`/`increment`) — resolvidos\r\n * SERVER-SIDE pelo Core (não pelo relógio spoofável do cliente).\r\n * - `Timestamp` é uma classe leve (sem proto de Firebase) com `toDate()`,\r\n * `toMillis()`, `seconds`, `nanoseconds` e `Timestamp.now()/fromDate()/fromMillis()`.\r\n * - `onSnapshot` usa o realtime nativo do `client.db` (`onSnapshot`/`doc().onSnapshot`)\r\n * quando o transporte oferece; caso contrário entrega o snapshot inicial e\r\n * reage às mudanças locais (offline-first do SDK).\r\n *\r\n * @module\r\n */\r\n\r\nimport type {\r\n NeetruDb,\r\n DbCollectionRef,\r\n DbQuery,\r\n DbWhereFilter,\r\n DbListResult,\r\n DbGetResult,\r\n} from './db/client-db';\r\nimport {\r\n serverTimestamp as sdkServerTimestamp,\r\n increment as sdkIncrement,\r\n type FieldSentinel,\r\n} from './db/field-value';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Resolução do handle de banco\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Aceita tanto o `NeetruDb` (`client.db`) quanto o `NeetruClient` inteiro.\r\n * Produtos legados chamam `getFirestore(client)` ou `getFirestore(client.db)`;\r\n * ambos resolvem para o `NeetruDb` subjacente.\r\n */\r\nexport type FirestoreLike = NeetruDb | { db: NeetruDb };\r\n\r\n/** Marker brand reconhecível em runtime — `getFirestore()` devolve isto. */\r\nexport interface Firestore {\r\n readonly __neetruFirestore: true;\r\n readonly db: NeetruDb;\r\n}\r\n\r\nfunction isClientWrapper(v: FirestoreLike | Firestore): v is { db: NeetruDb } {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n 'db' in v &&\r\n typeof (v as { db?: unknown }).db === 'object' &&\r\n (v as { db?: { collection?: unknown } }).db?.collection !== undefined\r\n );\r\n}\r\n\r\nfunction resolveDb(handle: FirestoreLike | Firestore): NeetruDb {\r\n if ((handle as Firestore).__neetruFirestore) return (handle as Firestore).db;\r\n if (isClientWrapper(handle)) return handle.db;\r\n // É o próprio NeetruDb (tem `.collection`).\r\n return handle as NeetruDb;\r\n}\r\n\r\n/**\r\n * Equivalente compat de `getFirestore(app)`. Aceita o `NeetruClient` ou o\r\n * `client.db` (`NeetruDb`) e devolve um handle `Firestore` opaco.\r\n *\r\n * @example\r\n * ```ts\r\n * import { getFirestore } from '@neetru/sdk/firestore-compat';\r\n * const db = getFirestore(client); // ou getFirestore(client.db)\r\n * ```\r\n */\r\nexport function getFirestore(handle: FirestoreLike): Firestore {\r\n return { __neetruFirestore: true, db: resolveDb(handle) };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Refs — Collection & Document\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Operadores `where` suportados (subset do Firestore + ops do SDK). */\r\nexport type WhereFilterOp =\r\n | '=='\r\n | '!='\r\n | '<'\r\n | '<='\r\n | '>'\r\n | '>='\r\n | 'in'\r\n | 'not-in'\r\n | 'array-contains'\r\n | 'array-contains-any';\r\n\r\nexport interface CollectionReference<T = Record<string, unknown>> {\r\n readonly type: 'collection';\r\n readonly __kind: 'collection';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly db: NeetruDb;\r\n /** Handle nativo do SDK — atalho para a superfície `client.db`. */\r\n readonly ref: DbCollectionRef<T>;\r\n}\r\n\r\nexport interface DocumentReference<T = Record<string, unknown>> {\r\n readonly type: 'document';\r\n readonly __kind: 'document';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n /** Marcador de tipo (phantom) — preserva `T` para inferência do caller. */\r\n readonly __doc?: T;\r\n}\r\n\r\n/** Aceita tanto `Firestore` quanto `NeetruDb`/`NeetruClient` direto. */\r\ntype DbArg = Firestore | FirestoreLike;\r\n\r\n/**\r\n * `collection(db, name)` — referência a uma coleção.\r\n *\r\n * Aceita `db` como `Firestore` (de `getFirestore`), `NeetruClient` ou `client.db`.\r\n */\r\nexport function collection<T = Record<string, unknown>>(\r\n db: DbArg,\r\n name: string,\r\n): CollectionReference<T> {\r\n const resolved = resolveDb(db);\r\n return {\r\n type: 'collection',\r\n __kind: 'collection',\r\n id: name,\r\n path: name,\r\n db: resolved,\r\n ref: resolved.collection<T>(name),\r\n };\r\n}\r\n\r\n/**\r\n * `doc(...)` — referência a um documento. Suporta as 3 assinaturas legadas:\r\n * doc(db, 'collection', 'id') → coleção + id\r\n * doc(collectionRef, 'id') → id dentro de uma CollectionReference\r\n * doc(db, 'collection/id') → path com barra (id auto-gerado se id ausente)\r\n *\r\n * Quando o `id` é omitido, gera um id v4 (espelha `doc(collectionRef)` do Firebase\r\n * que cria uma ref com id automático antes do `setDoc`).\r\n */\r\nexport function doc<T = Record<string, unknown>>(\r\n dbOrCollection: DbArg | CollectionReference<T>,\r\n ...path: string[]\r\n): DocumentReference<T> {\r\n // Forma doc(collectionRef, id?)\r\n if (\r\n typeof dbOrCollection === 'object' &&\r\n dbOrCollection !== null &&\r\n (dbOrCollection as CollectionReference<T>).__kind === 'collection'\r\n ) {\r\n const col = dbOrCollection as CollectionReference<T>;\r\n const id = path[0] ?? generateId();\r\n return makeDocRef<T>(col.db, col.id, id);\r\n }\r\n\r\n const resolved = resolveDb(dbOrCollection as DbArg);\r\n\r\n if (path.length >= 2) {\r\n return makeDocRef<T>(resolved, path[0], path.slice(1).join('/'));\r\n }\r\n if (path[0]?.includes('/')) {\r\n const parts = path[0].split('/');\r\n return makeDocRef<T>(resolved, parts[0], parts.slice(1).join('/') || generateId());\r\n }\r\n // doc(db, 'collection') — id auto-gerado\r\n return makeDocRef<T>(resolved, path[0] ?? '', generateId());\r\n}\r\n\r\nfunction makeDocRef<T>(db: NeetruDb, collectionName: string, id: string): DocumentReference<T> {\r\n return {\r\n type: 'document',\r\n __kind: 'document',\r\n id,\r\n collection: collectionName,\r\n path: `${collectionName}/${id}`,\r\n db,\r\n };\r\n}\r\n\r\n/**\r\n * Gera um id estilo Firestore (20 chars alfanuméricos) com aleatoriedade\r\n * CRIPTOGRÁFICA.\r\n *\r\n * Antes usava `Math.random()` — previsível e colisível (PRNG não-criptográfico,\r\n * estado recuperável). IDs de documento previsíveis permitem enumeração/forja.\r\n * Agora usa `globalThis.crypto.getRandomValues` (WebCrypto), disponível tanto em\r\n * Node ≥20 quanto em browsers — sem importar `node:crypto` (que quebraria o\r\n * bundle browser). Rejeita bytes ≥ 248 (4×62) para eliminar o viés de módulo\r\n * sobre o alfabeto de 62 caracteres.\r\n */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 62\r\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!cryptoObj?.getRandomValues) {\r\n throw new Error(\r\n '[Neetru SDK / firestore-compat] WebCrypto indisponível: generateId() exige ' +\r\n 'globalThis.crypto.getRandomValues (Node ≥20 ou ambiente browser).',\r\n );\r\n }\r\n let out = '';\r\n while (out.length < 20) {\r\n const buf = new Uint8Array(20 - out.length);\r\n cryptoObj.getRandomValues(buf);\r\n for (const byte of buf) {\r\n if (byte < 248) {\r\n out += CHARS[byte % 62];\r\n if (out.length === 20) break;\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUERY — acumula constraints e gera DbQuery quando consumido\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type QueryConstraintType = 'where' | 'orderBy' | 'limit';\r\n\r\nexport interface QueryConstraint {\r\n readonly __constraint: QueryConstraintType;\r\n}\r\n\r\ninterface WhereConstraint extends QueryConstraint {\r\n readonly __constraint: 'where';\r\n readonly field: string;\r\n readonly op: WhereFilterOp;\r\n readonly value: unknown;\r\n}\r\n\r\ninterface OrderByConstraint extends QueryConstraint {\r\n readonly __constraint: 'orderBy';\r\n readonly field: string;\r\n readonly direction: 'asc' | 'desc';\r\n}\r\n\r\ninterface LimitConstraint extends QueryConstraint {\r\n readonly __constraint: 'limit';\r\n readonly value: number;\r\n}\r\n\r\nexport interface Query<T = Record<string, unknown>> {\r\n readonly type: 'query';\r\n readonly __kind: 'query';\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n readonly ref: DbCollectionRef<T>;\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * `query(collectionRef, ...constraints)` — combina `where`/`orderBy`/`limit`.\r\n * Aceita também uma `Query` como fonte (encadeamento de constraints).\r\n */\r\nexport function query<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const base: Query<T> =\r\n source.__kind === 'query'\r\n ? source\r\n : {\r\n type: 'query',\r\n __kind: 'query',\r\n path: source.path,\r\n collection: source.id,\r\n db: source.db,\r\n ref: source.ref,\r\n constraints: [],\r\n };\r\n return { ...base, constraints: [...base.constraints, ...constraints] };\r\n}\r\n\r\nexport function where(field: string, op: WhereFilterOp, value: unknown): WhereConstraint {\r\n return { __constraint: 'where', field, op, value };\r\n}\r\n\r\nexport function orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): OrderByConstraint {\r\n return { __constraint: 'orderBy', field, direction };\r\n}\r\n\r\nexport function limit(value: number): LimitConstraint {\r\n return { __constraint: 'limit', value };\r\n}\r\n\r\n/**\r\n * Converte os constraints de uma `Query` compat para o `DbQuery` nativo do SDK.\r\n *\r\n * Exportado para que os hooks React (`@neetru/sdk/react`) possam reusar a mesma\r\n * tradução — fonte única de verdade da conversão de constraints.\r\n */\r\nexport function toDbQuery(constraints: QueryConstraint[]): DbQuery {\r\n const whereFilters: DbWhereFilter[] = [];\r\n let orderByClause: DbQuery['orderBy'];\r\n let limitValue: number | undefined;\r\n\r\n for (const c of constraints) {\r\n if (c.__constraint === 'where') {\r\n const w = c as WhereConstraint;\r\n whereFilters.push({ field: w.field, op: normalizeOp(w.op), value: w.value });\r\n } else if (c.__constraint === 'orderBy') {\r\n const o = c as OrderByConstraint;\r\n orderByClause = { field: o.field, direction: o.direction };\r\n } else if (c.__constraint === 'limit') {\r\n limitValue = (c as LimitConstraint).value;\r\n }\r\n }\r\n\r\n const out: DbQuery = {};\r\n if (whereFilters.length > 0) out.where = whereFilters;\r\n if (orderByClause) out.orderBy = orderByClause;\r\n if (limitValue !== undefined) out.limit = limitValue;\r\n return out;\r\n}\r\n\r\n/**\r\n * O `DbWhereFilter` do SDK não declara `array-contains-any`/`not-in` no union.\r\n * Passamos o op como-está; o Core valida server-side e retorna erro explícito\r\n * se não suportado.\r\n *\r\n * Bug corrigido: remapeava 'array-contains-any' → 'array-contains' silenciosamente,\r\n * o que tem semântica completamente diferente (checa literal do array, não interseção)\r\n * e nunca retornava resultados para queries reais.\r\n */\r\nfunction normalizeOp(op: WhereFilterOp): DbWhereFilter['op'] {\r\n // Todas as ops passam direto; o Core valida server-side.\r\n return op as DbWhereFilter['op'];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SNAPSHOTS — emulam a API antiga do Firebase\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Remove o campo `id` dos dados (o Firestore expõe id via `.id`, não em `data()`). */\r\nfunction stripId<T>(raw: Record<string, unknown>): T {\r\n if (raw && typeof raw === 'object' && 'id' in raw) {\r\n const { id: _omit, ...rest } = raw;\r\n return rest as T;\r\n }\r\n return raw as T;\r\n}\r\n\r\nexport class DocumentSnapshot<T = Record<string, unknown>> {\r\n constructor(\r\n public readonly id: string,\r\n private readonly _data: T | null,\r\n public readonly ref?: DocumentReference<T>,\r\n ) {}\r\n\r\n exists(): boolean {\r\n return this._data != null;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._data ?? undefined;\r\n }\r\n\r\n /** Compat: `snap.get('field')` lê um campo top-level. */\r\n get(field: string): unknown {\r\n return this._data ? (this._data as Record<string, unknown>)[field] : undefined;\r\n }\r\n}\r\n\r\nexport interface QueryDocumentSnapshot<T = Record<string, unknown>> {\r\n readonly id: string;\r\n data(): T;\r\n exists(): boolean;\r\n get(field: string): unknown;\r\n readonly ref: DocumentReference<T>;\r\n}\r\n\r\nexport class QuerySnapshot<T = Record<string, unknown>> {\r\n public readonly docs: Array<QueryDocumentSnapshot<T>>;\r\n public readonly empty: boolean;\r\n public readonly size: number;\r\n\r\n constructor(\r\n rows: Array<{ id: string; data: Record<string, unknown> }>,\r\n db?: NeetruDb,\r\n collectionName?: string,\r\n ) {\r\n this.docs = rows.map(({ id, data }) => {\r\n const stripped = stripId<T>(data);\r\n const ref: DocumentReference<T> | undefined =\r\n db && collectionName ? makeDocRef<T>(db, collectionName, id) : undefined;\r\n return {\r\n id,\r\n data: () => stripped,\r\n exists: () => true,\r\n get: (field: string) => (stripped as Record<string, unknown>)?.[field],\r\n // ref sempre presente quando db+collection conhecidos (caso comum).\r\n ref: ref as DocumentReference<T>,\r\n };\r\n });\r\n this.empty = this.docs.length === 0;\r\n this.size = this.docs.length;\r\n }\r\n\r\n forEach(cb: (doc: QueryDocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(cb);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// READS\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Extrai os dados crus do primeiro doc de um `DbGetResult`. */\r\nfunction firstDocData(result: DbGetResult<Record<string, unknown>> | null): {\r\n data: Record<string, unknown> | null;\r\n id: string | null;\r\n} {\r\n const first = result?.docs[0];\r\n return { data: first ? (first.data as Record<string, unknown>) : null, id: first?.id ?? null };\r\n}\r\n\r\nexport async function getDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n): Promise<DocumentSnapshot<T>> {\r\n const result = (await ref.db.collection(ref.collection).get(ref.id)) as DbGetResult<\r\n Record<string, unknown>\r\n > | null;\r\n const { data } = firstDocData(result);\r\n return new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? (stripId<T>({ id: ref.id, ...data })) : null,\r\n ref,\r\n );\r\n}\r\n\r\n/** Mapeia um `DbListResult` para linhas `{ id, data }` (id garantido). */\r\nfunction listResultToRows(\r\n result: DbListResult<Record<string, unknown>>,\r\n): Array<{ id: string; data: Record<string, unknown> }> {\r\n return result.docs.map((d) => ({ id: d.id, data: { ...(d.data as Record<string, unknown>) } }));\r\n}\r\n\r\nexport async function getDocs<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n): Promise<QuerySnapshot<T>> {\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const collectionName = source.__kind === 'query' ? source.collection : source.id;\r\n const result = (await source.db\r\n .collection(collectionName)\r\n .list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n return new QuerySnapshot<T>(listResultToRows(result), source.db, collectionName);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITES\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SetOptions {\r\n merge?: boolean;\r\n mergeFields?: string[];\r\n}\r\n\r\nexport async function setDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options: SetOptions = {},\r\n): Promise<void> {\r\n const col = ref.db.collection(ref.collection);\r\n const payload = data as Record<string, unknown>;\r\n if (options.merge || (options.mergeFields && options.mergeFields.length > 0)) {\r\n // bug_0273e4dd: ANTES o check de existência usava o cache LOCAL (col.get é\r\n // offline-first). Doc que existia só no SERVIDOR dava docExists=false → caía\r\n // em col.set() (replace) e APAGAVA todos os campos ausentes do payload →\r\n // perda de dados silenciosa. Agora a decisão é DO SERVIDOR via merge-upsert:\r\n // update({upsert:true}) cria-se-ausente OU mescla-se-presente, sem nunca\r\n // apagar campos não enviados — exatamente o setDoc({merge:true}) do Firebase.\r\n // Contrato Core acordado (deploy0632): PATCH com upsert=true não 404a em\r\n // ausente e faz ref.set(data,{merge:true}).\r\n await col.update(ref.id, payload, { upsert: true });\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n}\r\n\r\nexport async function addDoc<T = Record<string, unknown>>(\r\n source: CollectionReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<DocumentReference<T>> {\r\n const result = await source.db.collection(source.id).add(data as Record<string, unknown>);\r\n return makeDocRef<T>(source.db, source.id, result.id);\r\n}\r\n\r\nexport async function updateDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<void> {\r\n await ref.db.collection(ref.collection).update(ref.id, data as Record<string, unknown>);\r\n}\r\n\r\nexport async function deleteDoc(ref: DocumentReference): Promise<void> {\r\n await ref.db.collection(ref.collection).remove(ref.id);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITE BATCH\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\ninterface PendingBatchOp {\r\n collection: string;\r\n op:\r\n | { kind: 'set'; id: string; data: Record<string, unknown> }\r\n | { kind: 'update'; id: string; data: Record<string, unknown>; upsert?: boolean }\r\n | { kind: 'remove'; id: string };\r\n}\r\n\r\nexport class WriteBatch {\r\n private ops: PendingBatchOp[] = [];\r\n\r\n constructor(private readonly db: NeetruDb) {}\r\n\r\n set<T>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options?: SetOptions,\r\n ): this {\r\n // bug_0273e4dd: SetOptions.merge era DESCARTADO → batch.set(ref,data,{merge:true})\r\n // virava replace e APAGAVA campos ausentes (perda de dados). Agora merge vira\r\n // update com upsert (merge-server-side, cria-se-ausente), igual ao setDoc.\r\n const merge = !!(options?.merge || (options?.mergeFields && options.mergeFields.length > 0));\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: merge\r\n ? { kind: 'update', id: ref.id, data: data as Record<string, unknown>, upsert: true }\r\n : { kind: 'set', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'update', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n delete(ref: DocumentReference): this {\r\n this.ops.push({ collection: ref.collection, op: { kind: 'remove', id: ref.id } });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n // Agrupa por coleção — o `DbCollectionRef.batch` opera numa coleção por vez.\r\n const byCol = new Map<string, PendingBatchOp['op'][]>();\r\n for (const { collection: name, op } of this.ops) {\r\n const list = byCol.get(name) ?? [];\r\n list.push(op);\r\n byCol.set(name, list);\r\n }\r\n for (const [name, ops] of byCol.entries()) {\r\n // O union nativo `DbBatchOp` casa 1:1 com nossas variantes (set/update/remove).\r\n await this.db.collection(name).batch(ops as never);\r\n }\r\n this.ops = [];\r\n }\r\n}\r\n\r\nexport function writeBatch(db: DbArg): WriteBatch {\r\n return new WriteBatch(resolveDb(db));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// runTransaction — emulação otimista (read → write) sobre client.db\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Handle de transação compat. O SDK não expõe transações multi-doc atômicas\r\n * com lock server-side, então emulamos a forma do Firebase: `tx.get` lê do\r\n * `client.db`, e `tx.set/update/delete` enfileiram escritas que são aplicadas\r\n * no fim do callback.\r\n *\r\n * ⚠️ **LIMITAÇÃO CONHECIDA — sem isolamento server-side (lost-update silencioso):**\r\n *\r\n * Diferente do Firestore nativo (que usa serializable snapshot isolation com\r\n * contention detection), este shim executa reads e writes em rounds separados\r\n * sem garantia de atomicidade:\r\n *\r\n * 1. `tx.get(ref)` faz um read imediato via `client.db`.\r\n * 2. As escritas (`tx.set/update/delete`) são **enfileiradas** — não bloqueiam\r\n * outros writers.\r\n * 3. No fim do callback, `batch.commit()` aplica as escritas. Se outro processo\r\n * tiver modificado o mesmo documento entre o read e o commit, a escrita\r\n * será um **lost-update silencioso** (sem erro, sem retry automático).\r\n *\r\n * **Quando isso importa:** counters, saldos, estoques — qualquer dado\r\n * que precise de leitura-modificação-escrita atômica.\r\n *\r\n * **Alternativas seguras:**\r\n * - Use **Server Actions no Core** (`src/lib/actions/`) que rodam dentro\r\n * de transações Firestore Admin SDK reais (com locks server-side).\r\n * - Use `increment()` sentinel para contadores numéricos — server-side atomic.\r\n * - Use `arrayUnion()`/`arrayRemove()` para sets (também server-side atomic).\r\n *\r\n * Esta emulação é aceitável para migrações transitórias e para operações\r\n * onde a probabilidade de concorrência é baixa (ex: dados de um único usuário\r\n * sem write concorrente). **Não use para dados financeiros ou de inventário.**\r\n */\r\nexport interface Transaction {\r\n get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n set<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>, options?: SetOptions): Transaction;\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): Transaction;\r\n delete(ref: DocumentReference): Transaction;\r\n}\r\n\r\n/** Flag para emitir o aviso de limitação uma só vez por processo (não spammar). */\r\nlet _runTransactionWarnEmitted = false;\r\n\r\nexport async function runTransaction<R>(\r\n db: DbArg,\r\n updateFunction: (transaction: Transaction) => Promise<R>,\r\n): Promise<R> {\r\n // Aviso em desenvolvimento/staging: sem isolamento server-side.\r\n if (!_runTransactionWarnEmitted) {\r\n _runTransactionWarnEmitted = true;\r\n console.warn(\r\n '[Neetru SDK / firestore-compat] runTransaction(): esta emulação NÃO oferece' +\r\n ' isolamento server-side. Reads e writes são executados em rounds separados;' +\r\n ' um write concorrente entre o get() e o commit() resultará em lost-update' +\r\n ' silencioso. Para atomicidade real, use Server Actions no Core ou sentinels' +\r\n ' increment()/arrayUnion(). Veja a doc em src/firestore-compat.ts § runTransaction.',\r\n );\r\n }\r\n\r\n const resolved = resolveDb(db);\r\n const batch = new WriteBatch(resolved);\r\n\r\n const tx: Transaction = {\r\n async get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n return getDoc(ref);\r\n },\r\n set(ref, data, options) {\r\n batch.set(ref, data, options);\r\n return tx;\r\n },\r\n update(ref, data) {\r\n batch.update(ref, data);\r\n return tx;\r\n },\r\n delete(ref) {\r\n batch.delete(ref);\r\n return tx;\r\n },\r\n };\r\n\r\n const result = await updateFunction(tx);\r\n await batch.commit();\r\n return result;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// onSnapshot — realtime via client.db (com fallback inicial)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type Unsubscribe = () => void;\r\n\r\ntype DocOnNext<T> = (snap: DocumentSnapshot<T>) => void;\r\ntype QueryOnNext<T> = (snap: QuerySnapshot<T>) => void;\r\ntype OnError = (err: Error) => void;\r\n\r\nexport function onSnapshot<T>(\r\n ref: DocumentReference<T>,\r\n onNext: DocOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: Query<T> | CollectionReference<T>,\r\n onNext: QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNext: DocOnNext<T> | QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe {\r\n // Documento único\r\n if (source.__kind === 'document') {\r\n const ref = source as DocumentReference<T>;\r\n const docRef = ref.db.collection(ref.collection).doc(ref.id);\r\n try {\r\n return docRef.onSnapshot((snap) => {\r\n const data = snap?.docs[0]?.data as Record<string, unknown> | undefined;\r\n (onNext as DocOnNext<T>)(\r\n new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? stripId<T>({ id: ref.id, ...data }) : null,\r\n ref,\r\n ),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n }\r\n\r\n // Coleção / Query\r\n const collectionName = source.__kind === 'query' ? source.collection : (source as CollectionReference<T>).id;\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const col = source.db.collection(collectionName);\r\n try {\r\n return col.onSnapshot(dbQuery, (snap: DbListResult<Record<string, unknown>>) => {\r\n (onNext as QueryOnNext<T>)(\r\n new QuerySnapshot<T>(listResultToRows(snap), source.db, collectionName),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Timestamp — classe leve compat\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Reimplementação compat de `Timestamp` do Firestore. Sem proto do Firebase.\r\n * Cobre `seconds`, `nanoseconds`, `toDate()`, `toMillis()`, `isEqual()`,\r\n * `valueOf()`, `toJSON()` + estáticos `now()`, `fromDate()`, `fromMillis()`.\r\n */\r\nexport class Timestamp {\r\n constructor(\r\n public readonly seconds: number,\r\n public readonly nanoseconds: number,\r\n ) {}\r\n\r\n static now(): Timestamp {\r\n return Timestamp.fromMillis(Date.now());\r\n }\r\n\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n static fromMillis(millis: number): Timestamp {\r\n const seconds = Math.floor(millis / 1000);\r\n const nanoseconds = (millis - seconds * 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n valueOf(): string {\r\n // Estável para ordenação por string (espelha Firebase).\r\n return `${this.seconds.toString().padStart(12, '0')}.${this.nanoseconds\r\n .toString()\r\n .padStart(9, '0')}`;\r\n }\r\n\r\n toJSON(): { seconds: number; nanoseconds: number } {\r\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\r\n }\r\n\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// FieldValue + sentinels\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sentinel de `arrayUnion`/`arrayRemove`. O SDK ainda não tem sentinel de array\r\n * server-side, então emitimos um marker namespaceado que o Core reconhece\r\n * (mesma família de `__neetru__`). Se o backing não suportar, o produto deve\r\n * preferir read-modify-write via Server Action. Documentado como compat.\r\n */\r\nexport interface ArrayUnionSentinel {\r\n readonly __neetru__: 'arrayUnion';\r\n readonly elements: unknown[];\r\n}\r\nexport interface ArrayRemoveSentinel {\r\n readonly __neetru__: 'arrayRemove';\r\n readonly elements: unknown[];\r\n}\r\n\r\n/**\r\n * Sentinel de campo a deletar (compat `deleteField()`).\r\n */\r\nexport interface DeleteFieldSentinel {\r\n readonly __neetru__: 'deleteField';\r\n}\r\n\r\n/**\r\n * `serverTimestamp()` — delega ao sentinel do SDK (resolvido SERVER-SIDE).\r\n * NÃO retorna mais ISO/Date do cliente (que era spoofável).\r\n */\r\nexport function serverTimestamp(): FieldSentinel {\r\n return sdkServerTimestamp();\r\n}\r\n\r\n/**\r\n * `increment(n)` — incremento atômico server-side (delega ao sentinel do SDK).\r\n */\r\nexport function increment(n: number): FieldSentinel {\r\n return sdkIncrement(n);\r\n}\r\n\r\n/** `arrayUnion(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayUnion(...elements: unknown[]): ArrayUnionSentinel {\r\n return { __neetru__: 'arrayUnion', elements };\r\n}\r\n\r\n/** `arrayRemove(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayRemove(...elements: unknown[]): ArrayRemoveSentinel {\r\n return { __neetru__: 'arrayRemove', elements };\r\n}\r\n\r\n/** `deleteField()` — sentinel compat para remover um campo. */\r\nexport function deleteField(): DeleteFieldSentinel {\r\n return { __neetru__: 'deleteField' };\r\n}\r\n\r\n/**\r\n * Namespace estático `FieldValue` — espelha `firebase/firestore`'s `FieldValue`.\r\n * Os métodos delegam às funções modulares acima.\r\n *\r\n * @example\r\n * ```ts\r\n * import { FieldValue } from '@neetru/sdk/firestore-compat';\r\n * await updateDoc(ref, {\r\n * tags: FieldValue.arrayUnion('novo'),\r\n * counter: FieldValue.increment(1),\r\n * updatedAt: FieldValue.serverTimestamp(),\r\n * });\r\n * ```\r\n */\r\nexport const FieldValue = {\r\n serverTimestamp,\r\n increment,\r\n arrayUnion,\r\n arrayRemove,\r\n delete: deleteField,\r\n} as const;\r\n","/**\r\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\r\n *\r\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\r\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\r\n *\r\n * Exports:\r\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\r\n * - `<NeetruLoginButton>` — botão padronizado \"Entrar com Neetru\" que chama\r\n * `client.auth.signIn()` no click (mesmo visual BMW do botão preto\r\n * \"Entrar com Google\" de `auth.neetru.com/oauth/login`, com o ícone\r\n * Neetru no lugar do ícone do provider).\r\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\r\n * (suporte a `mode='readonly'` pro decisão CEO §5).\r\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\r\n *\r\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\r\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\r\n * puxam React mesmo via transitive.\r\n */\r\nimport * as React from 'react';\r\nimport type { NeetruClient, NeetruUser, AuthNamespace, SignInOptions } from './types';\r\n\r\n// ─── Barra de conta / app-switcher (drop-in padrão Neetru) ───────────────────\r\n// Autorado em `./account-bar` pra manter este arquivo enxuto; reexportado aqui\r\n// pra ficar sob o subpath canônico `@neetru/sdk/react`.\r\nexport {\r\n NeetruAccountBar,\r\n fetchNeetruAccountBar,\r\n initialsFrom,\r\n type NeetruAccountBarProps,\r\n type AccountBarData,\r\n type AccountBarAccount,\r\n type AccountBarProductItem,\r\n type AccountBarLabels,\r\n} from './account-bar';\r\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\r\nimport type {\r\n CollectionReference,\r\n Query,\r\n DocumentReference,\r\n} from './firestore-compat';\r\nimport { toDbQuery } from './firestore-compat';\r\nimport type { DbListResult, DbGetResult } from './db/client-db';\r\n\r\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\r\n\r\nexport interface CheckoutLinkProps\r\n extends Omit<\r\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\r\n 'href' | 'onClick' | 'onError'\r\n > {\r\n /**\r\n * Cliente SDK Neetru. Passado explicitamente pra evitar context provider.\r\n *\r\n * **Opcional** quando `startPath` é usado (padrão BFF/cookie recomendado): o\r\n * browser não tem token nem client, o start acontece no route BFF do app.\r\n * Obrigatório no modo legado (client-side start), quando o browser tem o token.\r\n */\r\n client?: NeetruClient;\r\n /**\r\n * **BFF/cookie (padrão recomendado):** path do route do PRÓPRIO app que inicia\r\n * o checkout server-side (ex: `/api/checkout/start`). Quando presente, o clique\r\n * faz `POST` nesse path (same-origin, `credentials: 'include'` → manda o cookie\r\n * httpOnly de sessão), o route lê o id_token do cookie e chama o Core\r\n * server-side, devolvendo `{ redirectUrl }`. Assim o **browser nunca precisa do\r\n * access_token** — que no padrão BFF é descartado no servidor.\r\n *\r\n * A resposta esperada do route BFF é `{ redirectUrl, intentId?, status?,\r\n * expiresAt?, requiresKyc? }` (ou `{ intent: { redirectUrl, ... } }`). Sem\r\n * `startPath`, o componente cai no modo legado `client.checkout.start()`.\r\n */\r\n startPath?: string;\r\n /** Slug do produto (ex: `neetru-pulse`). */\r\n productId: string;\r\n /** Plan id (ex: `pro_monthly`). */\r\n planId: string;\r\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\r\n callbackUrl: string;\r\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\r\n tenantType?: 'pf' | 'pj';\r\n tenantId?: string;\r\n /** Children — texto/JSX do CTA. */\r\n children: React.ReactNode;\r\n /** Callback quando intent foi criada (antes de redirect). */\r\n onIntentCreated?: (result: CheckoutStartResult) => void;\r\n /** Callback de erro. */\r\n onError?: (err: Error) => void;\r\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\r\n noAutoRedirect?: boolean;\r\n /** Override de href (default: `#`). */\r\n href?: string;\r\n}\r\n\r\n/**\r\n * Faz o start de checkout via route BFF do próprio app (padrão recomendado).\r\n * `POST` same-origin com `credentials: 'include'` — o cookie httpOnly de sessão\r\n * vai junto, e o route BFF lê o id_token dele e chama o Core server-side. O\r\n * browser nunca vê token. Espera `{ redirectUrl, ... }` (ou `{ intent: {...} }`).\r\n */\r\nasync function startCheckoutViaBff(\r\n startPath: string,\r\n input: {\r\n productId: string;\r\n planId: string;\r\n callbackUrl: string;\r\n tenantType?: 'pf' | 'pj';\r\n tenantId?: string;\r\n },\r\n): Promise<CheckoutStartResult> {\r\n const res = await fetch(startPath, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json', accept: 'application/json' },\r\n credentials: 'include',\r\n body: JSON.stringify(input),\r\n });\r\n let payload: Record<string, unknown> | null = null;\r\n try {\r\n payload = (await res.json()) as Record<string, unknown>;\r\n } catch {\r\n payload = null;\r\n }\r\n if (!res.ok) {\r\n const p = payload as\r\n | { error?: { message?: string } | string; message?: string }\r\n | null;\r\n const msg =\r\n (p &&\r\n ((typeof p.error === 'object' && p.error?.message) ||\r\n (typeof p.error === 'string' && p.error) ||\r\n (typeof p.message === 'string' && p.message))) ||\r\n `Checkout BFF respondeu HTTP ${res.status}`;\r\n throw new Error(typeof msg === 'string' ? msg : `Checkout BFF respondeu HTTP ${res.status}`);\r\n }\r\n const intent = ((payload?.intent as Record<string, unknown> | undefined) ?? payload) ?? {};\r\n const redirectUrl = (payload?.redirectUrl ?? intent.redirectUrl) as string | undefined;\r\n if (!redirectUrl || typeof redirectUrl !== 'string') {\r\n throw new Error('Resposta do route BFF de checkout sem `redirectUrl`.');\r\n }\r\n return {\r\n intentId: String(payload?.intentId ?? intent.intentId ?? ''),\r\n redirectUrl,\r\n status: (payload?.status ?? intent.status ?? 'pending') as CheckoutStartResult['status'],\r\n expiresAt:\r\n typeof payload?.expiresAt === 'string'\r\n ? (payload.expiresAt as string)\r\n : typeof intent.expiresAt === 'string'\r\n ? (intent.expiresAt as string)\r\n : '',\r\n requiresKyc: payload?.requiresKyc === true || intent.requiresKyc === true,\r\n };\r\n}\r\n\r\n/** Redireciona o browser pra `url`, best-effort (no-op fora do browser). */\r\nfunction checkoutRedirect(url: string): void {\r\n const loc = (globalThis as { location?: { assign?: (u: string) => void } }).location;\r\n if (loc && typeof loc.assign === 'function') loc.assign(url);\r\n}\r\n\r\n/**\r\n * Wrapper de `<a>` que inicia o checkout no click.\r\n *\r\n * Dois modos:\r\n *\r\n * **1. BFF/cookie (recomendado)** — passe `startPath`. O browser NÃO precisa de\r\n * token; o clique faz `POST` no route do seu app, que inicia o checkout\r\n * server-side com o id_token do cookie httpOnly:\r\n * ```tsx\r\n * <CheckoutLink\r\n * startPath=\"/api/checkout/start\"\r\n * productId=\"meu-saas\"\r\n * planId=\"pro_monthly\"\r\n * callbackUrl=\"https://meu-saas.com/billing/success\"\r\n * >\r\n * Assinar Pro\r\n * </CheckoutLink>\r\n * ```\r\n *\r\n * **2. Legado (client-side)** — passe `client`. Só funciona se o browser tiver o\r\n * token (SPA não-BFF que guarda o access_token no browser). Chama\r\n * `client.checkout.start()` direto:\r\n * ```tsx\r\n * <CheckoutLink client={client} productId=\"meu-saas\" planId=\"pro_monthly\"\r\n * callbackUrl=\"https://meu-saas.com/billing/success\">Assinar Pro</CheckoutLink>\r\n * ```\r\n *\r\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\r\n * de URL no hover (pode ser sobrescrito via prop spread).\r\n */\r\nexport function CheckoutLink({\r\n client,\r\n startPath,\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n children,\r\n onIntentCreated,\r\n onError,\r\n noAutoRedirect,\r\n ...rest\r\n}: CheckoutLinkProps): React.ReactElement {\r\n const [submitting, setSubmitting] = React.useState(false);\r\n\r\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\r\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\r\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } } | undefined)?.checkout;\r\n\r\n const handleClick = React.useCallback(\r\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\r\n event.preventDefault();\r\n if (submitting) return;\r\n\r\n // Modo BFF/cookie (recomendado): sem token no browser. POST no route do app.\r\n if (startPath) {\r\n setSubmitting(true);\r\n try {\r\n const result = await startCheckoutViaBff(startPath, {\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n });\r\n onIntentCreated?.(result);\r\n if (!noAutoRedirect) checkoutRedirect(result.redirectUrl);\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n return;\r\n }\r\n\r\n // Modo legado (client-side start): o browser precisa ter o token.\r\n if (!checkout || typeof checkout.start !== 'function') {\r\n onError?.(\r\n new Error(\r\n 'CheckoutLink: passe `startPath` (padrão BFF/cookie recomendado) OU um `client` com o namespace `checkout` (SDK 1.1+).',\r\n ),\r\n );\r\n return;\r\n }\r\n setSubmitting(true);\r\n try {\r\n const result = await checkout.start({\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n autoRedirect: !noAutoRedirect,\r\n });\r\n onIntentCreated?.(result);\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n },\r\n [submitting, checkout, startPath, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\r\n );\r\n\r\n return React.createElement(\r\n 'a',\r\n {\r\n ...rest,\r\n href: rest.href ?? '#',\r\n onClick: handleClick,\r\n 'aria-busy': submitting || undefined,\r\n 'data-neetru-checkout-link': 'true',\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ─── <NeetruLoginButton> ────────────────────────────────────────────────────\r\n\r\n/**\r\n * Path do ícone-chama Neetru (mesmo asset de `src/components/logo.tsx` no\r\n * Core — `variant=\"mark\"`, viewBox 527×760). Preto/branco conforme o tema do\r\n * botão, igual ao ícone colorido do Google no botão de referência.\r\n */\r\nconst NEETRU_FLAME_PATH =\r\n 'M 281 5.163 C 240.659 26.308, 211.260 52.739, 187.750 89 C 177.297 105.123, 167.942 126.107, 162.988 144.543 C 154.486 176.188, 153.303 204.118, 159.101 236.317 C 161.553 249.934, 164.601 259.065, 171.539 273.568 C 179.323 289.843, 188.311 302.619, 205.856 322.343 C 216.993 334.865, 218.719 337.460, 223.536 348.944 C 233.797 373.403, 237.419 401.984, 232.417 419.032 C 229.111 430.300, 219.859 441.975, 210.427 446.779 C 186.006 459.218, 155.612 448.383, 142.319 422.500 C 128.813 396.204, 123.953 356.753, 129.495 318.395 C 131.172 306.786, 138.276 276.222, 140.080 272.850 C 140.675 271.738, 140.944 270.610, 140.676 270.343 C 139.995 269.662, 121.004 281.262, 112 287.859 C 100.565 296.237, 95.542 300.566, 83.993 312 C 44.470 351.127, 19.275 398.763, 8.623 454.500 C 4.302 477.115, 3.308 513.551, 6.332 538.569 C 9.164 562.011, 17.855 591.852, 27.324 610.648 C 37.693 631.233, 54.130 653.987, 71.900 672.357 C 80.540 681.289, 98.477 696.992, 104.478 700.878 C 106.690 702.310, 108.725 703.766, 109 704.114 C 109.836 705.172, 125.939 715.307, 135.539 720.817 C 152.923 730.795, 169.097 737.316, 200.500 747.009 C 204.454 748.230, 222.087 751.463, 234 753.153 C 246.383 754.909, 283.484 754.921, 296 753.173 C 310.006 751.217, 321.722 749.122, 328.500 747.360 C 384.024 732.928, 433.393 701.075, 469.555 656.351 C 491.448 629.275, 505.223 601.823, 514.391 567 C 519.380 548.047, 520.999 536.377, 521.686 514.410 C 523.126 468.370, 514.560 426.479, 494.812 383 C 471.567 331.818, 436.019 282.480, 379.951 223.582 C 354.302 196.638, 349.024 190.981, 341.670 182.551 C 318.918 156.471, 301.763 128.316, 293.505 103.500 C 286.625 82.826, 285.662 76.446, 285.578 51 C 285.500 27.576, 285.510 27.460, 288.750 15.129 C 290.538 8.326, 292 2.363, 292 1.879 C 292 0.197, 288.454 1.255, 281 5.163';\r\nconst NEETRU_FLAME_VIEWBOX = '0 0 527 760';\r\nconst NEETRU_FLAME_ASPECT = 527 / 760;\r\n\r\nfunction NeetruFlameIcon({ fill, size = 16 }: { fill: string; size?: number }): React.ReactElement {\r\n return React.createElement(\r\n 'svg',\r\n {\r\n viewBox: NEETRU_FLAME_VIEWBOX,\r\n width: Math.round(size * NEETRU_FLAME_ASPECT),\r\n height: size,\r\n 'aria-hidden': true,\r\n style: { flexShrink: 0 },\r\n },\r\n React.createElement('path', { d: NEETRU_FLAME_PATH, fill }),\r\n );\r\n}\r\n\r\n/** Paleta BMW do botão — mesmos valores do \"Entrar com Google\" de `auth.neetru.com/oauth/login` (src/app/oauth/login/page.tsx). */\r\nconst NEETRU_LOGIN_BUTTON_THEME: Record<\r\n 'dark' | 'light',\r\n { bg: string; bgHover: string; fg: string; border: string }\r\n> = {\r\n dark: { bg: '#0a0a0a', bgHover: '#1a1a1a', fg: '#ffffff', border: '#0a0a0a' },\r\n light: { bg: '#ffffff', bgHover: '#f5f5f5', fg: '#0a0a0a', border: '#0a0a0a' },\r\n};\r\n\r\nexport interface NeetruLoginButtonProps\r\n extends Omit<\r\n React.ButtonHTMLAttributes<HTMLButtonElement>,\r\n 'onClick' | 'onError' | 'children' | 'style'\r\n > {\r\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\r\n client: NeetruClient;\r\n /** OIDC redirect_uri override — repassado a `client.auth.signIn`. Default: origin atual. */\r\n redirectUri?: string;\r\n /** OIDC scope — repassado a `client.auth.signIn`. Default `openid profile email`. */\r\n scope?: string;\r\n /** Pra onde mandar após login completo — repassado a `client.auth.signIn`. */\r\n postLoginRedirect?: string;\r\n /** Variante visual — `dark` (preto, default/primário) ou `light` (branco, secundário/sobre fundo escuro). */\r\n theme?: 'dark' | 'light';\r\n /** Texto do botão. Default `\"Entrar com Neetru\"`. */\r\n label?: string;\r\n /** Callback de erro — `signIn()` lançou antes do redirect (ex: sessionStorage indisponível, WebCrypto ausente, oidcClientId não configurado). Em browser real o caminho feliz NUNCA chama isto: a navegação interrompe o JS. */\r\n onError?: (err: Error) => void;\r\n /** Override de estilo inline — mesclado por cima do padrão BMW (não substitui). */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n/**\r\n * Botão padronizado \"Entrar com Neetru\" — dispara `client.auth.signIn()` no\r\n * click (redirect OIDC PKCE pra `auth.neetru.com/authorize`). Visual BMW\r\n * idêntico ao botão preto \"Entrar com Google\" que `auth.neetru.com` usa hoje\r\n * (mesma altura, tipografia, tracking, rounded-none), com o ícone-chama\r\n * Neetru no lugar do ícone do provider — é O botão padrão que qualquer\r\n * produto SaaS Neetru deveria usar na própria tela de login, em vez de cada\r\n * um inventar o próprio estilo.\r\n *\r\n * Estilos são inline (não Tailwind) de propósito: o componente roda em\r\n * QUALQUER app consumidor do SDK, com ou sem Tailwind configurado — precisa\r\n * renderizar idêntico em todo lugar, não só nos produtos que por acaso usam\r\n * o mesmo setup de CSS do Core.\r\n *\r\n * Uso típico:\r\n * ```tsx\r\n * <NeetruLoginButton client={client} postLoginRedirect=\"/dashboard\" />\r\n * ```\r\n */\r\nexport function NeetruLoginButton({\r\n client,\r\n redirectUri,\r\n scope,\r\n postLoginRedirect,\r\n theme = 'dark',\r\n label = 'Entrar com Neetru',\r\n onError,\r\n style,\r\n disabled,\r\n ...rest\r\n}: NeetruLoginButtonProps): React.ReactElement {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n const [isHover, setIsHover] = React.useState(false);\r\n const colors = NEETRU_LOGIN_BUTTON_THEME[theme];\r\n const isDisabled = Boolean(disabled) || isLoading;\r\n\r\n const handleClick = React.useCallback(async () => {\r\n if (isDisabled) return;\r\n setIsLoading(true);\r\n try {\r\n const options: SignInOptions = { redirectUri, scope, postLoginRedirect };\r\n await client.auth.signIn(options);\r\n // Bug achado por verificação end-to-end (2026-07-24): signIn() real\r\n // faz location.assign() e RETORNA em seguida (não espera a navegação\r\n // acontecer) — a suposição antiga de que a navegação \"interrompe a\r\n // execução\" era falsa, e em modo mock (NEETRU_ENV=dev) não há\r\n // navegação nenhuma. Sem o reset abaixo, o botão travava em\r\n // \"Entrando…\"/disabled para sempre depois do 1º signIn bem-sucedido.\r\n // No browser real, o reset é inofensivo: a navegação já está em voo.\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, [client, redirectUri, scope, postLoginRedirect, isDisabled, onError]);\r\n\r\n return React.createElement(\r\n 'button',\r\n {\r\n ...rest,\r\n type: 'button',\r\n onClick: handleClick,\r\n disabled: isDisabled,\r\n onMouseEnter: () => setIsHover(true),\r\n onMouseLeave: () => setIsHover(false),\r\n 'aria-label': rest['aria-label'] ?? label,\r\n 'aria-busy': isLoading || undefined,\r\n 'data-neetru-login-button': 'true',\r\n style: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: '12px',\r\n width: '100%',\r\n height: '48px',\r\n padding: '0 20px',\r\n borderRadius: 0,\r\n border: `1px solid ${colors.border}`,\r\n backgroundColor: isHover && !isDisabled ? colors.bgHover : colors.bg,\r\n color: colors.fg,\r\n fontSize: '14px',\r\n fontWeight: 700,\r\n textTransform: 'uppercase',\r\n letterSpacing: '0.08em',\r\n fontFamily: 'inherit',\r\n lineHeight: 1,\r\n cursor: isDisabled ? 'not-allowed' : 'pointer',\r\n opacity: isDisabled && !isLoading ? 0.6 : 1,\r\n transition: 'background-color 150ms ease',\r\n boxSizing: 'border-box',\r\n ...style,\r\n },\r\n },\r\n React.createElement(NeetruFlameIcon, { fill: colors.fg }),\r\n React.createElement('span', null, isLoading ? 'Entrando…' : label),\r\n );\r\n}\r\n\r\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\r\n\r\n/** Modo de gate quando entitlement não é permitido. */\r\nexport type EntitlementGateMode = 'block' | 'readonly';\r\n\r\ninterface EntitlementContextValue {\r\n /** True quando feature está em modo read-only (limit exceeded). */\r\n readonly: boolean;\r\n /** Reason code da última check. */\r\n reason?: string;\r\n /** Limite atual (free tier). */\r\n limit?: number;\r\n /** Uso atual. */\r\n currentUsage?: number;\r\n}\r\n\r\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\r\n readonly: false,\r\n});\r\n\r\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\r\n * desabilitar inputs/botões de escrita. */\r\nexport function useEntitlementContext(): EntitlementContextValue {\r\n return React.useContext(EntitlementContext);\r\n}\r\n\r\nexport interface EntitlementGateProps {\r\n /** Cliente SDK Neetru. */\r\n client: NeetruClient;\r\n /** Feature key a checar (ex: `export.csv`). */\r\n feature: string;\r\n /** Slug do produto. Default: lê do client.config se ausente. */\r\n productSlug?: string;\r\n /** Modo:\r\n * - `block` (default): renderiza fallback quando não permitido.\r\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\r\n mode?: EntitlementGateMode;\r\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\r\n fallback?: React.ReactNode;\r\n /** Loading UI enquanto check resolve (Suspense-like). */\r\n loading?: React.ReactNode;\r\n children: React.ReactNode;\r\n}\r\n\r\ninterface EntitlementCheckResult {\r\n allowed: boolean;\r\n reason?: string;\r\n limit?: number;\r\n currentUsage?: number;\r\n behavior?: 'readonly' | null;\r\n}\r\n\r\n/**\r\n * Wrapper que gateia children por entitlement check.\r\n *\r\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\r\n * - Children renderizam normalmente.\r\n * - Context injeta `{readonly: true}`.\r\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\r\n * escritas (botões, forms, inputs).\r\n *\r\n * Quando `mode='block'` + check retorna `allowed=false`:\r\n * - `fallback` é renderizado (ex: paywall, upsell modal).\r\n *\r\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\r\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\r\n */\r\nexport function EntitlementGate({\r\n client,\r\n feature,\r\n productSlug,\r\n mode = 'block',\r\n fallback,\r\n loading,\r\n children,\r\n}: EntitlementGateProps): React.ReactElement {\r\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\r\n status: 'loading',\r\n });\r\n\r\n // Resolve productSlug from prop or client.config\r\n const resolvedSlug =\r\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\r\n\r\n // tenantId fallback: client.config.tenantId (default no createNeetruClient).\r\n // usage.check valida ambos productId + tenantId — sem isso, lança\r\n // validation_failed e o Gate fica trancado em allowed:false.\r\n const resolvedTenantId = (client.config as { tenantId?: string }).tenantId;\r\n\r\n React.useEffect(() => {\r\n let cancelled = false;\r\n const run = async () => {\r\n try {\r\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\r\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\r\n let result: EntitlementCheckResult;\r\n const usage = (client as {\r\n usage?: {\r\n check: (\r\n resource: string,\r\n opts?: { productId?: string; tenantId?: string },\r\n ) => Promise<EntitlementCheckResult>;\r\n };\r\n }).usage;\r\n if (usage && typeof usage.check === 'function' && resolvedTenantId) {\r\n const res = await usage.check(feature, {\r\n productId: resolvedSlug,\r\n tenantId: resolvedTenantId,\r\n });\r\n result = {\r\n allowed: res.allowed === true,\r\n reason: res.reason,\r\n limit: res.limit,\r\n currentUsage: res.currentUsage,\r\n behavior: res.behavior,\r\n };\r\n } else {\r\n // Sem tenantId, cai pra entitlements.check (não exige tenant) em vez\r\n // de chamar usage.check e tomar validation_failed silencioso.\r\n const allowed = await client.entitlements.check(resolvedSlug, feature);\r\n result = { allowed };\r\n }\r\n if (!cancelled) setState({ status: 'ready', result });\r\n } catch (err) {\r\n // Fail open: erros de rede/timeout não bloqueiam o usuário com plano válido.\r\n // Bug corrigido: antes setava allowed:false (fail-closed) contradizendo o comentário.\r\n if (!cancelled) {\r\n setState({ status: 'ready', result: { allowed: true, reason: 'check_error_fallback' } });\r\n }\r\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed — fail-open', err);\r\n }\r\n };\r\n run();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [client, feature, resolvedSlug, resolvedTenantId]);\r\n\r\n if (state.status === 'loading') {\r\n return React.createElement(React.Fragment, null, loading ?? null);\r\n }\r\n\r\n const result = state.result;\r\n const allowed = result?.allowed === true;\r\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\r\n\r\n if (!allowed && mode === 'block' && !isReadonly) {\r\n return React.createElement(React.Fragment, null, fallback ?? null);\r\n }\r\n\r\n return React.createElement(\r\n EntitlementContext.Provider,\r\n {\r\n value: {\r\n readonly: isReadonly,\r\n reason: result?.reason,\r\n limit: result?.limit,\r\n currentUsage: result?.currentUsage,\r\n },\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ════════════════════════════════════════════════════════════════════════════\r\n// SDK-2 — Hooks de dados de 1ª classe (compat com hooks caseiros por-produto)\r\n//\r\n// ⚠️ STATUS: 1ª classe, mas desenhados para CASAR com a superfície que os\r\n// produtos (gestovendas / pdvagiliza) escreveram à mão em cima do\r\n// firestore-compat. Substituem os hooks `useCollection`/`useDoc`/`useUser`\r\n// que cada produto reescrevia (~200-400 LOC por produto).\r\n//\r\n// Estes hooks aceitam **refs do `@neetru/sdk/firestore-compat`**\r\n// (`CollectionReference` / `Query` / `DocumentReference`) e devolvem o shape\r\n// `{ data, isLoading, error, isStale, lastUpdated }` que os produtos já\r\n// consomem. Internamente usam o realtime nativo do `client.db`\r\n// (`onSnapshot`) quando disponível, com fallback de polling visibility-aware.\r\n//\r\n// Diferença vs `@neetru/sdk/db/react` (`useDocument`/`useCollection` de lá):\r\n// - Lá: API offline-first, ref = `DbCollectionRef` nativo, retorno\r\n// `{ docs, loading, fromCache, stale }`.\r\n// - Aqui: API firestore-compat, ref = ref do firestore-compat, retorno\r\n// `{ data, isLoading, error, isStale, lastUpdated }` (id já embutido em\r\n// cada item; data nunca duplica o id internamente).\r\n// ════════════════════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resultado de `useCollection` (subpath `@neetru/sdk/react`, API firestore-compat).\r\n *\r\n * Nome distinto de `UseCollectionResult` do subpath `@neetru/sdk/db/react`:\r\n * - `src/db/react.ts` → `{ docs, loading, fromCache, stale, hasPendingWrites }`\r\n * - `src/react.ts` → `{ data, isLoading, error, isStale, lastUpdated }` (este)\r\n *\r\n * Consumers que importam de ambos subpaths não terão clash de tipo.\r\n * Alias de retrocompat `UseCollectionResult` exportado abaixo.\r\n */\r\nexport interface FirestoreCompatCollectionResult<T> {\r\n /** Documentos (cada um com `id` embutido) ou `null` enquanto carrega/sem ref. */\r\n data: Array<T & { id: string }> | null;\r\n /** `true` enquanto o primeiro snapshot não chegou. */\r\n isLoading: boolean;\r\n /** Erro do último refresh (dados exibidos podem estar defasados). */\r\n error: Error | null;\r\n /** `true` quando um refresh falhou ou o snapshot veio do cache/offline. */\r\n isStale: boolean;\r\n /** epoch ms do último snapshot bem-sucedido, ou `null`. */\r\n lastUpdated: number | null;\r\n}\r\n\r\n/**\r\n * Alias de retrocompat — exportado para não quebrar produtos que importam\r\n * `UseCollectionResult` do subpath `@neetru/sdk/react`. Prefira\r\n * `FirestoreCompatCollectionResult` em código novo para evitar o clash de nome\r\n * com `UseCollectionResult` de `@neetru/sdk/db/react`.\r\n *\r\n * @deprecated Use `FirestoreCompatCollectionResult` em novo código.\r\n */\r\nexport type UseCollectionResult<T> = FirestoreCompatCollectionResult<T>;\r\n\r\n/** Resultado de `useDoc`. */\r\nexport interface UseDocResult<T> {\r\n data: (T & { id: string }) | null;\r\n isLoading: boolean;\r\n error: Error | null;\r\n isStale: boolean;\r\n lastUpdated: number | null;\r\n}\r\n\r\n/** Opções comuns dos hooks de dados. */\r\nexport interface UseDataOptions {\r\n /**\r\n * Intervalo de polling de fallback em ms (quando o transporte não oferece\r\n * realtime nativo). Default 5000. O polling pausa quando a aba está oculta.\r\n */\r\n intervalMs?: number;\r\n /**\r\n * Força polling mesmo quando o `onSnapshot` nativo está disponível. Útil para\r\n * transportes REST onde `onSnapshot` só entrega o snapshot inicial.\r\n * Default: `false` (usa realtime quando o transporte oferece).\r\n */\r\n poll?: boolean;\r\n}\r\n\r\nconst DEFAULT_INTERVAL_MS = 5000;\r\n\r\n/** `true` quando a aba está oculta (não dispara polling em background). */\r\nfunction isDocumentHidden(): boolean {\r\n return typeof document !== 'undefined' && document.hidden === true;\r\n}\r\n\r\n/**\r\n * Serializa uma fonte de coleção/query firestore-compat numa chave estável.\r\n * Garante que mudanças de VALOR (não de identidade) re-subscrevam, mas objetos\r\n * iguais não causem loop de efeito.\r\n */\r\nfunction serializeSource(\r\n source: CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n): string | null {\r\n if (!source) return null;\r\n if (source.__kind === 'collection') {\r\n return `collection:${source.id}`;\r\n }\r\n const dbQuery = toDbQuery(source.constraints);\r\n return `query:${source.collection}:${JSON.stringify(dbQuery)}`;\r\n}\r\n\r\n/** Normaliza linhas de um snapshot do client.db em `{...data, id}` (id embutido). */\r\nfunction rowsFromList<T>(snap: DbListResult<Record<string, unknown>>): Array<T & { id: string }> {\r\n return snap.docs.map((d) => {\r\n const data = d.data as Record<string, unknown>;\r\n // Garante que o id externo prevaleça (sem duplicar dentro do objeto data).\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id: d.id };\r\n });\r\n}\r\n\r\nfunction rowFromGet<T>(\r\n snap: DbGetResult<Record<string, unknown>> | null,\r\n id: string,\r\n): (T & { id: string }) | null {\r\n const first = snap?.docs[0];\r\n if (!first) return null;\r\n const data = first.data as Record<string, unknown>;\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id };\r\n}\r\n\r\n/**\r\n * Hook de tempo real para uma coleção (ou subconjunto filtrado via `query`).\r\n *\r\n * Aceita uma `CollectionReference` ou `Query` do `@neetru/sdk/firestore-compat`.\r\n * Subscreve ao `onSnapshot` nativo do `client.db`; quando o transporte é REST\r\n * (sem realtime nativo), faz polling visibility-aware no `intervalMs`.\r\n *\r\n * ```tsx\r\n * import { collection, query, where } from '@neetru/sdk/firestore-compat';\r\n * import { useCollection } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(\r\n * () => query(collection(client.db, 'orders'), where('status', '==', 'open')),\r\n * [],\r\n * );\r\n * const { data, isLoading, error, isStale } = useCollection<Order>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useCollection<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseCollectionResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const sourceKey = serializeSource(\r\n source as CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n );\r\n\r\n const [state, setState] = React.useState<UseCollectionResult<T>>({\r\n data: null,\r\n isLoading: sourceKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n // Mantém a fonte mais recente sem re-disparar o efeito por identidade.\r\n const sourceRef = React.useRef(source);\r\n sourceRef.current = source;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = sourceRef.current;\r\n\r\n if (!current || sourceKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const collectionName = current.__kind === 'query' ? current.collection : current.id;\r\n const dbQuery = current.__kind === 'query' ? toDbQuery(current.constraints) : undefined;\r\n const col = current.db.collection(collectionName);\r\n\r\n const applySnap = (snap: DbListResult<Record<string, unknown>>) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowsFromList<T>(snap),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n // Mantém o último data bom — não limpa em erro de refresh.\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n // Polling explícito (REST) ou realtime nativo.\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await col.list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = col.onSnapshot(dbQuery, applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [sourceKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * Hook de tempo real para um documento.\r\n *\r\n * Aceita uma `DocumentReference` do `@neetru/sdk/firestore-compat`.\r\n *\r\n * ```tsx\r\n * import { doc } from '@neetru/sdk/firestore-compat';\r\n * import { useDoc } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(() => doc(client.db, 'profiles', uid), [uid]);\r\n * const { data, isLoading } = useDoc<Profile>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseDocResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const refKey = ref && ref.id ? `${ref.collection}/${ref.id}` : null;\r\n\r\n const [state, setState] = React.useState<UseDocResult<T>>({\r\n data: null,\r\n isLoading: refKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n const refRef = React.useRef(ref);\r\n refRef.current = ref;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = refRef.current;\r\n\r\n if (!current || refKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const docRef = current.db.collection(current.collection).doc(current.id);\r\n const id = current.id;\r\n\r\n const applySnap = (snap: DbGetResult<Record<string, unknown>> | null) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowFromGet<T>(snap, id),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap?.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await docRef.get()) as DbGetResult<Record<string, unknown>> | null;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = docRef.onSnapshot(applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [refKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n// ─── useUser ────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Fonte de auth aceita por `useUser`: o `NeetruClient` inteiro (resolve\r\n * `client.auth`) ou o `AuthNamespace` direto.\r\n */\r\nexport type UseUserInput = NeetruClient | { auth: AuthNamespace } | AuthNamespace;\r\n\r\n/** Resultado de `useUser`. */\r\nexport interface UseUserResult {\r\n /** Usuário autenticado ou `null`. */\r\n user: NeetruUser | null;\r\n /** `true` enquanto o estado de auth inicial não foi resolvido. */\r\n isLoading: boolean;\r\n /** Erro na resolução de auth (raro — listener nunca deveria lançar). */\r\n error: Error | null;\r\n}\r\n\r\nfunction resolveAuth(input: UseUserInput): AuthNamespace {\r\n const maybe = input as { auth?: AuthNamespace };\r\n if (maybe.auth && typeof maybe.auth.onAuthStateChanged === 'function') {\r\n return maybe.auth;\r\n }\r\n return input as AuthNamespace;\r\n}\r\n\r\n/**\r\n * Hook de estado de autenticação.\r\n *\r\n * Subscreve a `client.auth.onAuthStateChanged` e devolve `{ user, isLoading,\r\n * error }`. Inclui um **timeout de seed** (default 8s): se o estado de auth não\r\n * resolver nesse prazo, `isLoading` cai para `false` com `user: null` — evita\r\n * spinners infinitos quando o provedor de auth nunca dispara o callback.\r\n *\r\n * ```tsx\r\n * import { useUser } from '@neetru/sdk/react';\r\n * const { user, isLoading } = useUser(client);\r\n * if (isLoading) return <Spinner />;\r\n * if (!user) return <SignInButton />;\r\n * return <Avatar user={user} />;\r\n * ```\r\n *\r\n * Aceita o `NeetruClient`, `{ auth }` ou o `AuthNamespace` direto.\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useUser(\r\n input: UseUserInput,\r\n options?: { timeoutMs?: number },\r\n): UseUserResult {\r\n const timeoutMs = options?.timeoutMs ?? 8000;\r\n const auth = resolveAuth(input);\r\n\r\n const [state, setState] = React.useState<UseUserResult>(() => {\r\n // Seed síncrono do estado atual (se o namespace já tem user em cache).\r\n let seeded: NeetruUser | null = null;\r\n try {\r\n seeded = auth.getUser();\r\n } catch {\r\n seeded = null;\r\n }\r\n return { user: seeded, isLoading: seeded === null, error: null };\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n let settled = false;\r\n\r\n // Timeout de seed: se onAuthStateChanged nunca disparar, destrava o loading.\r\n const timer = setTimeout(() => {\r\n if (!mountedRef.current || settled) return;\r\n setState((prev) => (prev.isLoading ? { ...prev, isLoading: false } : prev));\r\n }, timeoutMs);\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = auth.onAuthStateChanged((user) => {\r\n if (!mountedRef.current) return;\r\n settled = true;\r\n setState({ user: user ?? null, isLoading: false, error: null });\r\n });\r\n } catch (e) {\r\n if (mountedRef.current) {\r\n setState({\r\n user: null,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n }\r\n }\r\n\r\n return () => {\r\n mountedRef.current = false;\r\n clearTimeout(timer);\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [auth, timeoutMs]);\r\n\r\n return state;\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/account-bar.ts","../src/firestore-compat.ts","../src/react.ts"],"names":["React","result"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\r\n * Tipos públicos do SDK Neetru.\r\n *\r\n * Vendor-neutral: nada aqui referencia `firebase/*`, `stripe`, ou qualquer\r\n * provider interno. Caller só vê superfície neutra (Product, Entitlement, ...).\r\n */\r\n\r\n/** Default base URL pra API pública Neetru. */\r\nexport const DEFAULT_BASE_URL = 'https://api.neetru.com';\r\n\r\n/**\r\n * Base URL do Core local no emulador da Dev Suite (`neetru dev up`). Usada como\r\n * default quando `env: 'dev-local'` e nenhum `baseUrl` explícito é passado.\r\n * `dev-local` chama HTTP real contra o Core local — não é mock.\r\n */\r\nexport const DEV_LOCAL_BASE_URL = 'http://localhost:9003';\r\n\r\n/**\r\n * Configuração passada pra `createNeetruClient`.\r\n */\r\nexport interface NeetruClientConfig {\r\n /**\r\n * Bearer token Neetru (formato `nrt_<keyId>_<secret>`). Opcional —\r\n * se ausente, o SDK tenta `process.env.NEETRU_API_KEY` em Node.\r\n *\r\n * Endpoints públicos (catalog) funcionam sem apiKey. Endpoints autenticados\r\n * (entitlements, telemetry) lançam `missing_api_key` se ausente.\r\n */\r\n apiKey?: string;\r\n /**\r\n * 3.1.3 — `client_id` do client OIDC confidencial do produto, registrado em\r\n * `oidc_clients/{id}` no Core (par id + segredo, modelo Firebase/Facebook).\r\n *\r\n * Usado como `client_id` no fluxo `auth.signIn()` (Authorization Code + PKCE).\r\n * É **distinto** do `keyId` embutido na `apiKey` (`nrt_<keyId>_<secret>`), que\r\n * identifica a API key de máquina — não um client OIDC. Se ausente, `signIn`\r\n * cai no `keyId` por compat, mas o `/authorize` do Core só reconhece um\r\n * `oidc_clients` válido, então registre e passe `oidcClientId` em produção.\r\n */\r\n oidcClientId?: string;\r\n /**\r\n * URL base da API Neetru. Default: `https://api.neetru.com`.\r\n * Útil em testes apontando pra staging ou mock server.\r\n */\r\n baseUrl?: string;\r\n /**\r\n * Implementação de fetch customizada. Default: `globalThis.fetch`.\r\n * Necessário em runtimes que não exponham fetch global por default.\r\n */\r\n fetch?: typeof globalThis.fetch;\r\n /**\r\n * Modo de runtime. `dev` ativa mocks (auth retorna user fixture, usage só\r\n * loga, support retorna lista fake). `workspace` e `prod` chamam HTTP real.\r\n * `dev-local` chama HTTP real contra o Core local do emulador da Dev Suite\r\n * (`neetru dev up`): sem `baseUrl` explícito, aponta pra `http://localhost:9003`.\r\n *\r\n * Default: lê `process.env.NEETRU_ENV` (Node) ou `globalThis.NEETRU_ENV`\r\n * (browser); fallback `prod`.\r\n */\r\n env?: 'dev' | 'workspace' | 'prod' | 'dev-local';\r\n /**\r\n * Override de mocks — útil em tests do consumer pra injetar fixtures\r\n * determinísticos (sem depender de NEETRU_ENV=dev).\r\n */\r\n mocks?: {\r\n auth?: AuthNamespace;\r\n usage?: UsageNamespace;\r\n support?: SupportNamespace;\r\n entitlements?: NeetruClient['entitlements'];\r\n /** v2.0: aceita NeetruDb (inclui MockDb). DbNamespace legado não é mais aceito. */\r\n db?: import('./db/client-db').NeetruDb;\r\n webhooks?: import('./webhooks').WebhooksNamespace;\r\n notifications?: import('./notifications').NotificationsNamespace;\r\n ai?: import('./ai').AiNamespace;\r\n };\r\n /**\r\n * v0.3 — productId default usado por `usage.report()` / `usage.check()` /\r\n * `db.collection()` quando não passado explicitamente nas options.\r\n */\r\n productId?: string;\r\n /**\r\n * v0.3 — tenantId default usado por `usage.report()` / `usage.check()` /\r\n * `db.collection()` quando não passado explicitamente nas options.\r\n */\r\n tenantId?: string;\r\n /**\r\n * v2.0 — Configuração do banco lógico: transport, dbId, coleções.\r\n * Gerado pelo scaffold `neetru db init`. Opcional — padrão é transport='rest'.\r\n *\r\n * Use `transport` (v2.3+) em vez do campo `engine` depreciado.\r\n * Valores: `'rest'` | `'firestore'` | `'nosql-vm'`.\r\n */\r\n db?: import('./db/client-db').NeetruDbOptions;\r\n /**\r\n * 3.0 — hosts adicionais permitidos para `baseUrl`. Por default, SDK aceita\r\n * `*.neetru.com` HTTPS + localhost. Para apontar pra staging/proxy custom em\r\n * `https://meu-proxy.empresa.com`, adicione `['empresa.com']`.\r\n *\r\n * Bypass total: `NEETRU_ALLOW_INSECURE_BASEURL=1` no env.\r\n */\r\n allowedHosts?: string[];\r\n}\r\n\r\n/**\r\n * Status do produto no catálogo público.\r\n */\r\nexport type ProductStatus = 'live' | 'soon' | 'beta';\r\n\r\n/**\r\n * Produto SaaS publicado pelo Neetru Core.\r\n *\r\n * Schema neutro — alinhado com `public_products/{slug}` no backend mas\r\n * nunca expõe campos internos (Firestore Timestamps, draft state, etc).\r\n */\r\nexport interface Product {\r\n /** Identificador estável do produto, ex: `neetru-pulse`. */\r\n slug: string;\r\n /** Nome de exibição, ex: `Neetru Pulse`. */\r\n name: string;\r\n /** Subtítulo curto, ex: `Gestão de operações`. */\r\n tagline?: string;\r\n /** Descrição longa em prosa. */\r\n description?: string;\r\n /** Status público do produto. */\r\n status?: ProductStatus;\r\n /** Hint de ícone (catálogo de keys interno do Core). */\r\n iconKey?: string;\r\n /** Link override do CTA principal (default: página do produto). */\r\n ctaHref?: string;\r\n /** Label override do CTA. */\r\n ctaLabel?: string;\r\n /** Lista opcional de planos cobrados. Pode ser preenchida v0.2+. */\r\n plans?: ProductPlan[];\r\n}\r\n\r\n/** Plano cobrado de um produto (placeholder v0.1 — schema final v0.2). */\r\nexport interface ProductPlan {\r\n id: string;\r\n name: string;\r\n /** Preço mensal em centavos (BRL). Pode ser undefined em planos custom. */\r\n amountCents?: number;\r\n features?: string[];\r\n}\r\n\r\n/**\r\n * Resposta do `client.catalog.list()`.\r\n */\r\nexport interface CatalogListResponse {\r\n products: Product[];\r\n fetchedAt: string;\r\n}\r\n\r\n/**\r\n * Resposta do `client.entitlements.check(productSlug, feature)`.\r\n *\r\n * `allowed` é o sinal forte; `reason` ajuda a debugar (não exibir pro user\r\n * final).\r\n */\r\nexport interface EntitlementCheck {\r\n allowed: boolean;\r\n productSlug: string;\r\n feature: string;\r\n /** Reason code estável: `granted` | `not_subscribed` | `feature_not_in_plan` | `expired`. */\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Payload pra `client.telemetry.event()`.\r\n */\r\nexport interface TelemetryEventInput {\r\n /** Nome do evento, ex: `dashboard_opened`, `report_exported`. */\r\n name: string;\r\n /** Atributos adicionais (chave → valor primitivo). */\r\n properties?: Record<string, string | number | boolean | null>;\r\n /** Timestamp ISO opcional; default = server time. */\r\n timestamp?: string;\r\n /** Slug do produto. Se ausente, o SDK usa `config.productId` como default\r\n * (o backend NÃO infere — exige productSlug no body, fail-closed). */\r\n productSlug?: string;\r\n}\r\n\r\n/**\r\n * Confirmação de aceitação do evento de telemetria.\r\n */\r\nexport interface TelemetryEventAck {\r\n ok: true;\r\n /** ID gerado pelo backend pra evento (`usage_events/{id}`). */\r\n eventId: string;\r\n}\r\n\r\n// ─── Sprint 6 — Telemetry log per-product ──────────────────────────────────\r\n\r\nexport type TelemetryLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\r\n\r\nexport interface TelemetryLogInput {\r\n level: TelemetryLogLevel;\r\n message: string;\r\n metadata?: Record<string, unknown>;\r\n /** Override correlationId (default: lê dos headers/env automaticamente). */\r\n correlationId?: string;\r\n /** Slug do produto. Se ausente, o SDK usa `config.productId` como default\r\n * (o backend NÃO infere — exige productSlug no body, fail-closed). */\r\n productSlug?: string;\r\n}\r\n\r\nexport interface TelemetryLogAck {\r\n ok: true;\r\n /** ID do log gravado (`logs/{productId}/{yyyymmdd}/{logId}`) — opcional em mocks. */\r\n logId?: string;\r\n /** Indicador de modo (mock vs http). */\r\n mode: 'mock' | 'http';\r\n}\r\n\r\n/**\r\n * Handle imutável retornado por `createNeetruClient`. Carrega namespaces\r\n * `auth`, `catalog`, `entitlements`, `telemetry`, `usage`, `support`.\r\n *\r\n * Cada namespace é exposto como objeto chamável — espelha API Firebase\r\n * (`auth`, `firestore`) ou Stripe (`paymentIntents`, `customers`).\r\n */\r\nexport interface NeetruClient {\r\n /** Configuração resolvida (apiKey, baseUrl, fetch). Read-only. */\r\n readonly config: ResolvedConfig;\r\n /** Namespace auth (v0.2 — OIDC + mocks dev). */\r\n readonly auth: AuthNamespace;\r\n /** Namespace catálogo público de produtos. */\r\n readonly catalog: {\r\n /** Lista produtos publicados. */\r\n list(opts?: CatalogListOptions): Promise<CatalogListResponse>;\r\n /** Busca produto único por slug. */\r\n get(slug: string): Promise<Product>;\r\n };\r\n /** Namespace entitlements (v1.2: cache LRU 60s TTL). */\r\n readonly entitlements: {\r\n /**\r\n * Verifica se o portador da apiKey pode usar `feature` do produto `slug`.\r\n * Cacheado 60s. `{ cacheBust: true }` força ida ao servidor.\r\n */\r\n check(\r\n productSlug: string,\r\n feature: string,\r\n opts?: { cacheBust?: boolean },\r\n ): Promise<boolean>;\r\n /** Variante que retorna o objeto completo com `reason`. */\r\n checkDetailed(\r\n productSlug: string,\r\n feature: string,\r\n opts?: { cacheBust?: boolean },\r\n ): Promise<EntitlementCheck>;\r\n };\r\n /** Namespace telemetria (v1.2: track fire-and-forget + flush). */\r\n readonly telemetry: {\r\n /** Persiste um evento single-shot. await retorna { ok, eventId }. */\r\n event(input: TelemetryEventInput): Promise<TelemetryEventAck>;\r\n /** Fire-and-forget: enqueue + flush 500ms debounce. Sem retorno. */\r\n track(input: TelemetryEventInput): void;\r\n /** Força flush da queue do track() (chamar antes de unload). */\r\n flush(): Promise<void>;\r\n /**\r\n * Registra um log estruturado per-product (Sprint 6).\r\n *\r\n * - Em `NEETRU_ENV=dev`: apenas console.{level} (sem network).\r\n * - Em `workspace`/`prod`: POST `/api/sdk/v1/telemetry/log` com Bearer + correlationId.\r\n */\r\n log(input: TelemetryLogInput): Promise<TelemetryLogAck>;\r\n };\r\n /** Namespace usage (v0.2 — track + quota; v0.3 — report + check). */\r\n readonly usage: UsageNamespace;\r\n /** Namespace support (v0.2 — tickets). */\r\n readonly support: SupportNamespace;\r\n /**\r\n * Namespace db (v2.0 — Mundo Documentos offline-first + Mundo SQL).\r\n *\r\n * Breaking change v2.0: `client.db` agora é `NeetruDb` em vez de `DbNamespace`.\r\n * - `db.collection(name)` → `DbCollectionRef` (offline-first, com `onSnapshot`,\r\n * `batch`, `doc(id)`, `onDoc`; `list()` retorna `DbListResult` com cursor).\r\n * - `db.sql(schema)` → `Promise<NeetruSqlClient>` (Mundo SQL).\r\n * - `db.syncState` / `db.flush()` / `db.clearCache()` (camada offline).\r\n *\r\n * O antigo `DbNamespace` (list retornava `T[]`) foi removido em v2.0.\r\n */\r\n readonly db: import('./db/client-db').NeetruDb;\r\n /** Namespace checkout (v1.1 — start/get/cancel intent + auto-redirect). */\r\n readonly checkout: import('./checkout').CheckoutNamespace;\r\n /** Namespace webhooks (v1.2 — register/list/unregister/test). */\r\n readonly webhooks: import('./webhooks').WebhooksNamespace;\r\n /** Namespace notifications (v1.2 — send/list/markRead/dismiss). */\r\n readonly notifications: import('./notifications').NotificationsNamespace;\r\n /** Namespace ai (3.1.10 — AI Gateway: chat non-stream + stream SSE). */\r\n readonly ai: import('./ai').AiNamespace;\r\n}\r\n\r\n// ─── v0.2 — Auth namespace ──────────────────────────────────────────────────\r\n\r\n/**\r\n * User retornado pelo OIDC ID token. Schema neutro — não vaza Firebase\r\n * decoded token shape. Subset estável do RFC 7519 + custom claims Neetru.\r\n */\r\nexport interface NeetruUser {\r\n /** Subject — uid estável. */\r\n uid: string;\r\n email: string;\r\n emailVerified?: boolean;\r\n displayName?: string;\r\n photoURL?: string;\r\n /** Custom claim Neetru — true quando staff. */\r\n isStaff?: boolean;\r\n /** Custom claim Neetru — true quando customer enrolled. */\r\n isCustomer?: boolean;\r\n /** Tenant assigned (multi-tenant deployments). */\r\n tenantId?: string;\r\n /** Outros claims OIDC (aud, iss, iat, exp). */\r\n [extra: string]: unknown;\r\n}\r\n\r\nexport interface SignInOptions {\r\n /** OIDC redirect_uri override. Default = window.location origin. */\r\n redirectUri?: string;\r\n /** OIDC scope. Default `openid profile email`. */\r\n scope?: string;\r\n /** Onde mandar após login completo. Default = window.location.href. */\r\n postLoginRedirect?: string;\r\n}\r\n\r\nexport type AuthStateListener = (user: NeetruUser | null) => void;\r\n\r\n/**\r\n * 3.1.3 — Resultado de `auth.handleRedirectCallback()`: o material que o **backend\r\n * do produto** (confidential client) usa pra trocar o code por tokens no\r\n * endpoint `/api/v1/oauth/token` (POST x-www-form-urlencoded, `grant_type=\r\n * authorization_code`, autenticação `Basic base64(clientId:clientSecret)`).\r\n *\r\n * 3.1.12: para SPAs (public client, OAuth 2.1) prefira `completeSignIn()` —\r\n * ele faz a troca via PKCE puro (sem client_secret) e grava o `id_token`.\r\n * `handleRedirectCallback` continua disponível pra quem troca o code no\r\n * backend (confidential client, `Basic base64(clientId:clientSecret)`).\r\n */\r\nexport interface RedirectCallbackResult {\r\n /** `authorization_code` emitido pelo IdP — single-use, TTL curto. */\r\n code: string;\r\n /** PKCE `code_verifier` (RFC 7636) — enviar junto do code no `/token`. */\r\n codeVerifier: string;\r\n /**\r\n * `redirect_uri` exato usado no `/authorize`. O `/token` exige reenvio\r\n * idêntico (exact match) — passe este valor sem alterar.\r\n */\r\n redirectUri: string;\r\n /**\r\n * `nonce` enviado no `/authorize`. O backend deve validar que o claim\r\n * `nonce` do `id_token` retornado bate com este valor (anti-replay OIDC).\r\n */\r\n nonce: string;\r\n /** `state` validado (CSRF). Exposto pra logging/correlação. */\r\n state: string;\r\n /**\r\n * Destino pós-login que o app passou em `signIn({ postLoginRedirect })`.\r\n * `undefined` se não informado.\r\n */\r\n postLoginRedirect?: string;\r\n}\r\n\r\n/**\r\n * 3.1.12 — Resultado de `auth.completeSignIn()`: o login SPA PKCE está\r\n * COMPLETO — o `id_token` já foi validado (nonce), gravado em\r\n * `localStorage['neetru_id_token']` e os listeners de `onAuthStateChanged`\r\n * já foram notificados.\r\n */\r\nexport interface CompleteSignInResult {\r\n /** Usuário logado — mesmo shape retornado por `getUser()`. */\r\n user: NeetruUser;\r\n /**\r\n * Destino pós-login que o app passou em `signIn({ postLoginRedirect })`.\r\n * `undefined` se não informado. O SDK NÃO navega — o app decide.\r\n */\r\n postLoginRedirect?: string;\r\n}\r\n\r\nexport interface AuthNamespace {\r\n /**\r\n * Inicia fluxo de login OIDC (Authorization Code + PKCE). Em dev\r\n * (`NEETRU_ENV=dev`) retorna mock user direto sem redirect. Em browser\r\n * gera `state` (CSRF), `nonce` e o par PKCE `code_verifier`/`code_challenge`\r\n * (S256), persiste a transação em `sessionStorage` e redireciona pro\r\n * `/authorize`. Complete o fluxo no retorno com `handleRedirectCallback()`.\r\n */\r\n signIn(options?: SignInOptions): Promise<NeetruUser | void>;\r\n /**\r\n * 3.1.3 — Processa o retorno do IdP após `signIn`. Lê `code` + `state` da URL\r\n * (default `location.href`; passe `currentUrl` pra override), valida o\r\n * `state` contra a transação salva em `signIn` (CSRF), consome a transação\r\n * (single-use), limpa os params da URL e devolve o material pro backend\r\n * trocar por tokens. Retorna `null` se a URL não é um callback (sem `code`).\r\n *\r\n * @throws {NeetruError} `oidc_state_mismatch` se o `state` não bate ou não há\r\n * transação pendente; ou um erro com `.code` = o `error` do IdP (ex:\r\n * `access_denied`) quando a URL traz `?error=`.\r\n */\r\n handleRedirectCallback(currentUrl?: string): Promise<RedirectCallbackResult | null>;\r\n /**\r\n * 3.1.12 — Completa o login SPA PKCE (public client, OAuth 2.1): chama\r\n * `handleRedirectCallback(currentUrl)` e, se houver transação, troca o\r\n * `authorization_code` por tokens no `/api/v1/oauth/token` (POST\r\n * x-www-form-urlencoded com `code_verifier`, SEM client_secret), valida o\r\n * `nonce` do `id_token`, grava o token em `localStorage['neetru_id_token']`\r\n * e notifica os listeners de `onAuthStateChanged`.\r\n *\r\n * Seguro pra chamar em todo page load: retorna `null` quando a URL não é um\r\n * callback OIDC (sem `?code=`). Em dev (`MockAuth`) sempre retorna `null`.\r\n *\r\n * Fecha o gap descoberto no incidente pdv-agiliza 2026-07-10:\r\n * `handleRedirectCallback` parseava a volta do IdP mas NADA trocava o code\r\n * por tokens — o id_token nunca chegava ao storage que `getUser()` lê.\r\n *\r\n * @throws {NeetruError} `oidc_state_mismatch` (via `handleRedirectCallback`),\r\n * `token_exchange_failed` se o `/token` falhar ou vier sem `id_token`,\r\n * `oidc_nonce_mismatch` se o nonce do `id_token` não bater (token descartado).\r\n */\r\n completeSignIn(currentUrl?: string): Promise<CompleteSignInResult | null>;\r\n /**\r\n * 3.1.3 — Retorna o `id_token` cru (string JWT) que o app armazenou em\r\n * `localStorage['neetru_id_token']`, ou `null` se ausente/expirado. Útil pra\r\n * mandar como `Authorization: Bearer` ao BFF do produto. Para os claims\r\n * decodificados use `getUser()`; para validar assinatura use `verifyToken()`.\r\n */\r\n getIdToken(): string | null;\r\n /** Limpa session local + revoga refresh token no servidor. */\r\n signOut(): Promise<void>;\r\n /** Retorna o user atual (do id_token cached) ou `null` se não logado. */\r\n getUser(): NeetruUser | null;\r\n /**\r\n * Subscreve a mudanças de estado de auth. Listener é invocado imediatamente\r\n * com user atual. Retorna função de unsubscribe.\r\n */\r\n onAuthStateChanged(listener: AuthStateListener): () => void;\r\n /**\r\n * Valida um id_token JWT emitido por `auth.neetru.com` server-side.\r\n *\r\n * Usa JWKS local cacheado (1h TTL, configurável via `jwksCacheTtlMs`).\r\n * Ideal para Next.js route handlers, middleware e qualquer ambiente Node/Edge\r\n * server-side onde verificação rápida sem round-trip é necessária.\r\n *\r\n * @param token - JWT id_token obtido do cookie ou header Authorization.\r\n * @param options - Opções opcionais de verificação.\r\n * @returns `NeetruUser` com claims validados.\r\n *\r\n * @throws {NeetruError} com `.code`:\r\n * - `token_expired` — token expirado (`exp` no passado)\r\n * - `token_invalid_signature` — assinatura não verificada pela JWKS\r\n * - `token_invalid_audience` — `aud` não bate com o `clientId` configurado\r\n * - `token_invalid_issuer` — `iss` não é `https://auth.neetru.com`\r\n *\r\n * @example\r\n * ```ts\r\n * // Next.js route handler\r\n * import { createNeetruClient } from '@neetru/sdk';\r\n * const client = createNeetruClient({ apiKey: 'nrt_...' });\r\n *\r\n * export async function POST(req: Request) {\r\n * const token = req.cookies.get('neetru-session')?.value;\r\n * try {\r\n * const user = await client.auth.verifyToken(token);\r\n * // user.uid, user.email, user.isStaff etc disponíveis\r\n * } catch (err) {\r\n * return new Response('Unauthorized', { status: 401 });\r\n * }\r\n * }\r\n * ```\r\n */\r\n verifyToken(token: string, options?: VerifyTokenOptions): Promise<NeetruUser>;\r\n}\r\n\r\n/** Opções para `auth.verifyToken()`. */\r\nexport interface VerifyTokenOptions {\r\n /**\r\n * TTL do cache de JWKS em milissegundos. Default: 3600000 (1h).\r\n * Valores menores aumentam latência (mais fetches); maiores aumentam\r\n * o tempo de janela após rotação de chaves.\r\n */\r\n jwksCacheTtlMs?: number;\r\n /**\r\n * Audience (`aud`) esperada no token. Se ausente, o SDK tenta derivar do\r\n * `apiKey` configurado no client (campo `keyId`). Pelo menos uma das duas\r\n * fontes é **obrigatória** — `verifyToken` lança `invalid_config` se nenhuma\r\n * audience puder ser resolvida, pois verificar um token sem `aud` permitiria\r\n * tokens emitidos para outros clients passarem sem detecção.\r\n */\r\n audience?: string;\r\n}\r\n\r\n// ─── v0.2 — Usage namespace ─────────────────────────────────────────────────\r\n\r\n/**\r\n * 3.0 BREAKING: `UsageEventInput` mantido só como tipo para tests/legacy\r\n * imports — não é mais retorno de método. `usage.track`/`usage.getQuota`\r\n * **REMOVIDOS** em 3.0. Use `usage.report(resource, qty, options)`.\r\n */\r\nexport interface UsageEventInput {\r\n event: string;\r\n properties?: Record<string, string | number | boolean | null>;\r\n quantity?: number;\r\n}\r\n\r\n/**\r\n * 3.0 BREAKING: `UsageQuota` mantido só para compat de imports. Não é mais\r\n * retorno de método ativo. `usage.report()` devolve `{value, limit, remaining}`\r\n * inline; `usage.check()` devolve detalhes do entitlement.\r\n */\r\nexport interface UsageQuota {\r\n metric: string;\r\n used: number;\r\n limit: number;\r\n resetsAt?: string;\r\n plan?: string;\r\n}\r\n\r\n/**\r\n * 3.0 — superfície limpa: `report` (POST metered) + `check` (GET entitlement).\r\n *\r\n * Métodos removidos vs 2.x:\r\n * - `track(event, props)` — vaporware contra Core. Use `report()`.\r\n * - `getQuota(metric)` — endpoint nunca existiu. `report()` devolve quota.\r\n */\r\nexport interface UsageNamespace {\r\n /**\r\n * Reporta consumo metered. Incrementa `usage_counters` no Core atomicamente.\r\n * Idempotency-Key gerado por chamada (defesa anti-dup em retry transient).\r\n *\r\n * `productId`/`tenantId` lidos do contexto do client se ausentes nas options.\r\n */\r\n report(\r\n resource: string,\r\n qty?: number,\r\n options?: { productId?: string; tenantId?: string },\r\n ): Promise<{\r\n ok: true;\r\n counterId?: string;\r\n value?: number;\r\n limit?: number;\r\n remaining?: number;\r\n status?: string;\r\n }>;\r\n /**\r\n * Verifica entitlement de um resource/feature. GET cacheável (HTTP 60s).\r\n * Resposta inclui `allowed`, `reason`, `remaining`, `limit`, `planId`,\r\n * `planFeatures` e `behavior` ('readonly' quando limit_exceeded).\r\n */\r\n check(\r\n resource: string,\r\n options?: { productId?: string; tenantId?: string },\r\n ): Promise<{\r\n allowed: boolean;\r\n reason?: string;\r\n remaining?: number;\r\n limit?: number;\r\n planId?: string | null;\r\n planFeatures?: string[];\r\n behavior?: 'readonly' | null;\r\n }>;\r\n}\r\n\r\n// ─── v0.3 — Datastore namespace ────────────────────────────────────────────\r\n\r\n/**\r\n * Namespace `db` (v0.3) — wrapper minimalista para CRUD em coleções tenant-scoped\r\n * via Core REST. Em dev retorna fixtures in-memory por collection.\r\n *\r\n * O Core injeta automaticamente o tenantId no path do Firestore:\r\n * `tenant_{tid}_{name}/{docId}`.\r\n */\r\n/** Filtro `where` simples — alinhado com endpoint REST `field:op:value`. */\r\nexport interface DbWhereFilter {\r\n field: string;\r\n op: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in';\r\n value: string | number | boolean | null | Array<string | number | boolean>;\r\n}\r\n\r\nexport interface DbListOptions {\r\n limit?: number;\r\n /** Lista de filtros — máximo 5 (alinhado ao backend). */\r\n where?: DbWhereFilter[];\r\n}\r\n\r\nexport interface DbCollectionRef {\r\n /** Lista documentos com filtros opcionais. */\r\n list<T = Record<string, unknown>>(opts?: DbListOptions): Promise<T[]>;\r\n /** Lê um documento por id. Retorna `null` se não existe. */\r\n get<T = Record<string, unknown>>(id: string): Promise<T | null>;\r\n /** Cria/upsert um documento com id explícito. */\r\n set(id: string, data: Record<string, unknown>): Promise<{ ok: true }>;\r\n /**\r\n * v0.3.1 — Adiciona doc com id auto-gerado pelo backend. Retorna `{id}` do\r\n * doc criado. Diferente de `set(id, data)` que requer caller fornecer id.\r\n */\r\n add(data: Record<string, unknown>): Promise<{ ok: true; id: string }>;\r\n /** Atualiza doc com merge — só campos passados em `data`. */\r\n update(id: string, data: Record<string, unknown>): Promise<{ ok: true }>;\r\n /** Deleta um documento. */\r\n remove(id: string): Promise<{ ok: true }>;\r\n}\r\n\r\nexport interface DbNamespace {\r\n collection(name: string): DbCollectionRef;\r\n}\r\n\r\n// ─── v0.2 — Support namespace ───────────────────────────────────────────────\r\n\r\nexport type SupportSeverity = 'low' | 'normal' | 'high' | 'urgent';\r\nexport type SupportStatus = 'open' | 'pending' | 'resolved' | 'closed';\r\n\r\nexport interface SupportTicket {\r\n id: string;\r\n subject: string;\r\n message: string;\r\n severity: SupportSeverity;\r\n status: SupportStatus;\r\n createdAt: string;\r\n updatedAt?: string;\r\n /** Slug do produto referenciado. */\r\n productSlug?: string;\r\n /**\r\n * 3.4: URL da página onde o feedback/problema foi originado (input de\r\n * `createTicket`). `null` quando não informado.\r\n */\r\n pageUrl: string | null;\r\n /**\r\n * 3.4: URL do primeiro anexo (screenshot) do ticket, quando enviado via\r\n * `createTicket({ screenshotBase64 })`. `null` se nenhum anexo.\r\n */\r\n screenshotUrl: string | null;\r\n /**\r\n * 3.4: ISO — setado pelo Core na transição do ticket pra `resolved`.\r\n * `null` enquanto não resolvido.\r\n */\r\n resolvedAt: string | null;\r\n /**\r\n * 3.4: ISO — setado por `markResolvedSeen` (idempotente, só na 1ª vez que\r\n * o cliente vê a notificação de resolução). `null` até então.\r\n */\r\n resolvedNotifiedAt: string | null;\r\n /**\r\n * 3.4: quantas vezes o modal de satisfação foi exibido\r\n * (`recordSatisfactionAttempt`). Default `0`.\r\n */\r\n satisfactionAttempts: number;\r\n /** 3.4: ISO da última tentativa de exibição do modal de satisfação. */\r\n lastSatisfactionAttemptAt: string | null;\r\n /**\r\n * 3.4: ISO — setado por `submitSatisfaction` (avaliação OU skip). Marca o\r\n * ciclo de satisfação como concluído (terminal, até uma eventual\r\n * `contestTicket`).\r\n */\r\n satisfiedAt: string | null;\r\n /** 3.4: nota CSAT 1-5, ou `null` se ainda não avaliado / pulado. */\r\n rating: number | null;\r\n /** 3.4: comentário opcional da avaliação de satisfação. */\r\n satisfactionComment: string | null;\r\n}\r\n\r\nexport interface CreateTicketInput {\r\n subject: string;\r\n message: string;\r\n severity?: SupportSeverity;\r\n productSlug?: string;\r\n /**\r\n * **L-05 fix (Opus review 2026-05-27):** metadata opcional (Core já aceita\r\n * via `metadata: z.record(...)` no Zod). Útil pra anexar contexto como\r\n * `{ orderId, userAgent, route, sessionId }` sem inflar subject/message.\r\n *\r\n * Chaves limitadas a 64 chars; valores primitive (string/number/boolean/null).\r\n */\r\n metadata?: Record<string, string | number | boolean | null>;\r\n /**\r\n * 3.4: URL da página onde o problema/feedback ocorreu. Propagado como\r\n * `pageUrl` no ticket.\r\n */\r\n pageUrl?: string;\r\n /**\r\n * 3.4: screenshot em data URL base64 (cap ~700KB — mesmo limite do\r\n * `report-problem.ts` do Core). O Core converte em `attachments`/\r\n * `screenshotAttached`.\r\n */\r\n screenshotBase64?: string;\r\n /** 3.4: logs de console relevantes capturados no momento do report. */\r\n consoleLogs?: string;\r\n}\r\n\r\nexport interface SupportNamespace {\r\n /**\r\n * Cria um novo ticket. **3.0**: POST `/api/sdk/v1/support/tickets`\r\n * (canonical, requer `productId` no body — default `config.productId`).\r\n * Mock em dev.\r\n */\r\n createTicket(input: CreateTicketInput): Promise<SupportTicket>;\r\n /**\r\n * Lista meus tickets abertos. **3.0**: GET `/api/sdk/v1/support/tickets?productId=...`\r\n * (canonical). `options.productSlug` override do `config.productId` default.\r\n */\r\n listMyTickets(options?: { productSlug?: string }): Promise<SupportTicket[]>;\r\n /**\r\n * 3.4: marca que o usuário viu a notificação de resolução do ticket\r\n * (`resolvedNotifiedAt`, idempotente — só seta se ainda `null` no Core).\r\n * POST `/api/sdk/v1/support/tickets/{id}/resolved-seen`. Requer Bearer +\r\n * ownership + ticket em `resolved`.\r\n */\r\n markResolvedSeen(ticketId: string): Promise<void>;\r\n /**\r\n * 3.4: registra uma tentativa de exibição do modal de satisfação\r\n * (`satisfactionAttempts += 1`, `lastSatisfactionAttemptAt = now`).\r\n * **Chamar no SHOW do modal, não na resposta** — exibir já conta como\r\n * tentativa, evita reprompt infinito. POST\r\n * `/api/sdk/v1/support/tickets/{id}/satisfaction-attempt`. Requer Bearer +\r\n * ownership + ticket em `resolved`.\r\n */\r\n recordSatisfactionAttempt(ticketId: string): Promise<void>;\r\n /**\r\n * 3.4: envia (ou pula) a avaliação de satisfação. `rating` é CSAT 1-5 ou\r\n * `null` (junto de `skipped: true`). Terminal — seta `satisfiedAt`. POST\r\n * `/api/sdk/v1/support/tickets/{id}/satisfaction`. Requer Bearer +\r\n * ownership.\r\n */\r\n submitSatisfaction(input: {\r\n ticketId: string;\r\n rating: number | null;\r\n comment?: string | null;\r\n skipped: boolean;\r\n }): Promise<void>;\r\n /**\r\n * 3.4: contesta um ticket resolvido — reabre (`status='open'`) e zera o\r\n * ciclo de satisfação/notificação (`resolvedAt`, `resolvedNotifiedAt`,\r\n * `satisfactionAttempts`, `lastSatisfactionAttemptAt`, `satisfiedAt`,\r\n * `rating`, `satisfactionComment`). POST\r\n * `/api/sdk/v1/support/tickets/{id}/contest`. Requer Bearer + ownership +\r\n * ticket em `resolved`.\r\n */\r\n contestTicket(ticketId: string, opts?: { comment?: string }): Promise<void>;\r\n}\r\n\r\n// ─── v0.2 — Env detection ───────────────────────────────────────────────────\r\n\r\n/**\r\n * Modo do SDK. `dev` ativa mocks automáticos (úteis pra testes e\r\n * desenvolvimento sem precisar de backend rodando). `dev-local` chama HTTP\r\n * real contra o Core local do emulador (`neetru dev up`) — não é mock.\r\n */\r\nexport type NeetruEnv = 'dev' | 'workspace' | 'prod' | 'dev-local';\r\n\r\n/**\r\n * Configuração resolvida (defaults aplicados, fetch garantido).\r\n */\r\nexport interface ResolvedConfig {\r\n readonly apiKey?: string;\r\n /** 3.1.3 — client_id OIDC confidencial do produto (vide NeetruClientConfig). */\r\n readonly oidcClientId?: string;\r\n readonly baseUrl: string;\r\n readonly fetch: typeof globalThis.fetch;\r\n /** Resolved env — `dev` | `workspace` | `prod`. Default `prod`. */\r\n readonly env: NeetruEnv;\r\n /** Default productId (v0.3). */\r\n readonly productId?: string;\r\n /** Default tenantId (v0.3). */\r\n readonly tenantId?: string;\r\n}\r\n\r\n/** Opções pra `catalog.list()`. */\r\nexport interface CatalogListOptions {\r\n /** Inclui rascunhos (apenas com Bearer staff). Default false. */\r\n includeDrafts?: boolean;\r\n}\r\n","/**\r\n * Barra de conta / app-switcher padrão da Neetru — subpath `@neetru/sdk/react`.\r\n *\r\n * Um componente DROP-IN que TODO produto SaaS Neetru coloca no header:\r\n * - **Avatar** (foto ou iniciais) → menu de conta: nome/e-mail + \"Minha conta\r\n * Neetru\" (minhaconta.neetru.com) + \"Configurações da conta\" (deste produto)\r\n * + \"Sair\".\r\n * - **Waffle** (grid 9-pontos) → grid dos OUTROS produtos SaaS Neetru que o\r\n * cliente tem acesso, pra pular de um pro outro. O produto atual é destacado.\r\n *\r\n * Fonte dos dados: `GET /api/sdk/v1/account/bar` no Neetru Core, autenticado\r\n * com o **id_token** do end-user (o SDK já o guarda; `client.auth.getIdToken()`).\r\n * O componente NÃO faz fetch cru cross-repo — a busca passa por\r\n * `fetchNeetruAccountBar`, que é parte do próprio SDK (o conduíte canônico).\r\n *\r\n * Estilo: BMW (rounded-none, sem sombra/glassmorphism), inline (roda em\r\n * qualquer app com ou sem Tailwind). Superfície de CLIENTE → sempre CLARO\r\n * (não adere a dark mode; dark é staff-only). Acessível: teclado (setas, Tab\r\n * trap, Esc), aria, foco devolvido ao gatilho. i18n PT-BR (override via `labels`).\r\n *\r\n * Autorado com `React.createElement` (sem JSX) igual ao resto de `react.ts` —\r\n * mantém o build `.ts` sem depender de config JSX no consumidor.\r\n */\r\nimport * as React from 'react';\r\nimport { DEFAULT_BASE_URL } from './types';\r\nimport type { NeetruClient } from './types';\r\n\r\nconst h = React.createElement;\r\n\r\n// ─── Tipos do contrato ───────────────────────────────────────────────────────\r\n\r\nexport interface AccountBarAccount {\r\n displayName: string;\r\n email: string;\r\n photoUrl?: string | null;\r\n}\r\n\r\nexport interface AccountBarProductItem {\r\n slug: string;\r\n name: string;\r\n iconUrl?: string;\r\n url: string;\r\n}\r\n\r\nexport interface AccountBarData {\r\n account: AccountBarAccount;\r\n accountUrl: string;\r\n products: AccountBarProductItem[];\r\n}\r\n\r\n/** Textos da UI — default PT-BR, override parcial via prop `labels`. */\r\nexport interface AccountBarLabels {\r\n /** aria-label do botão de conta. */\r\n accountButton: string;\r\n /** aria-label do botão waffle. */\r\n appsButton: string;\r\n /** Título da seção de produtos no popover waffle. */\r\n appsTitle: string;\r\n /** Item \"Minha conta Neetru\". */\r\n myAccount: string;\r\n /** Item \"Configurações da conta\". */\r\n accountSettings: string;\r\n /** Item \"Sair\". */\r\n signOut: string;\r\n /** Badge no produto atual. */\r\n currentBadge: string;\r\n /** Estado vazio (sem outros produtos). */\r\n emptyProducts: string;\r\n}\r\n\r\nconst DEFAULT_LABELS: AccountBarLabels = {\r\n accountButton: 'Abrir menu da conta',\r\n appsButton: 'Seus produtos Neetru',\r\n appsTitle: 'Trocar de produto',\r\n myAccount: 'Minha conta Neetru',\r\n accountSettings: 'Configurações da conta',\r\n signOut: 'Sair',\r\n currentBadge: 'Atual',\r\n emptyProducts: 'Nenhum outro produto disponível.',\r\n};\r\n\r\n// ─── Fetch (conduíte SDK) ────────────────────────────────────────────────────\r\n\r\n/**\r\n * Busca os dados da barra de conta no Core usando o id_token do end-user.\r\n *\r\n * Retorna `null` quando não há usuário logado (sem id_token) ou a resposta não\r\n * é OK — o componente trata como \"não renderizar\" / usar identidade local.\r\n *\r\n * Exportado à parte pra ser reusável fora de React (ex.: pré-carregar no BFF do\r\n * produto) e testável isoladamente.\r\n */\r\nexport async function fetchNeetruAccountBar(\r\n client: NeetruClient,\r\n): Promise<AccountBarData | null> {\r\n const auth = client.auth as { getIdToken?: () => string | null } | undefined;\r\n const idToken = typeof auth?.getIdToken === 'function' ? auth.getIdToken() : null;\r\n if (!idToken) return null;\r\n\r\n const config = client.config as { baseUrl?: string; fetch?: typeof fetch };\r\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\r\n const fetchFn = config.fetch ?? globalThis.fetch;\r\n const url = `${baseUrl}/api/sdk/v1/account/bar`;\r\n\r\n try {\r\n const res = await fetchFn(url, {\r\n headers: { authorization: `Bearer ${idToken}`, accept: 'application/json' },\r\n });\r\n if (!res.ok) return null;\r\n const body = (await res.json()) as AccountBarData;\r\n if (!body || typeof body !== 'object' || !body.account) return null;\r\n return {\r\n account: {\r\n displayName: String(body.account.displayName ?? ''),\r\n email: String(body.account.email ?? ''),\r\n photoUrl: body.account.photoUrl ?? null,\r\n },\r\n accountUrl: String(body.accountUrl ?? 'https://minhaconta.neetru.com'),\r\n products: Array.isArray(body.products)\r\n ? body.products.filter(\r\n (p): p is AccountBarProductItem =>\r\n !!p && typeof p.slug === 'string' && typeof p.url === 'string',\r\n )\r\n : [],\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ─── Helpers de UI ───────────────────────────────────────────────────────────\r\n\r\n/** Iniciais (até 2) a partir do nome/e-mail pro fallback do avatar/monograma. */\r\nexport function initialsFrom(nameOrEmail: string): string {\r\n const s = (nameOrEmail ?? '').trim();\r\n if (!s) return '?';\r\n const local = s.includes('@') ? s.split('@')[0] : s;\r\n const parts = local.split(/[\\s._-]+/).filter(Boolean);\r\n if (parts.length === 0) return local.slice(0, 1).toUpperCase();\r\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\r\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\r\n}\r\n\r\n/** Paleta BMW clara — superfície de cliente força claro. */\r\nconst C = {\r\n bg: '#ffffff',\r\n bgHover: '#f5f5f5',\r\n fg: '#0a0a0a',\r\n muted: '#666666',\r\n border: '#0a0a0a',\r\n borderSoft: '#e5e5e5',\r\n};\r\n\r\nconst SQUARE = 0; // rounded-none\r\n\r\nfunction WaffleIcon({ size = 18, color = C.fg }: { size?: number; color?: string }): React.ReactElement {\r\n const dots = [0, 1, 2].flatMap((r) => [0, 1, 2].map((c) => ({ r, c })));\r\n const gap = size / 3;\r\n const dot = Math.max(2, size / 7);\r\n return h(\r\n 'svg',\r\n { width: size, height: size, viewBox: `0 0 ${size} ${size}`, 'aria-hidden': true, style: { display: 'block' } },\r\n dots.map(({ r, c }) =>\r\n h('rect', {\r\n key: `${r}-${c}`,\r\n x: c * gap + (gap - dot) / 2,\r\n y: r * gap + (gap - dot) / 2,\r\n width: dot,\r\n height: dot,\r\n fill: color,\r\n }),\r\n ),\r\n );\r\n}\r\n\r\nfunction focusables(container: HTMLElement | null): HTMLElement[] {\r\n if (!container) return [];\r\n return Array.from(\r\n container.querySelectorAll<HTMLElement>(\r\n 'a[href], button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n ),\r\n ).filter((el) => el.offsetParent !== null || el === document.activeElement);\r\n}\r\n\r\n// ─── Componente ──────────────────────────────────────────────────────────────\r\n\r\nexport interface NeetruAccountBarProps {\r\n /** Cliente SDK Neetru — fornece `config.baseUrl` + `auth.getIdToken()`. */\r\n client: NeetruClient;\r\n /** Slug do produto atual — destacado no grid waffle. */\r\n currentProductSlug?: string;\r\n /** Link \"Configurações da conta\" DESTE produto (rota interna do app). Omite o item se ausente. */\r\n settingsUrl?: string;\r\n /** Chamado após `signOut` concluir (ex.: redirecionar pro login). */\r\n onSignOut?: () => void;\r\n /** Override do fetcher — default `fetchNeetruAccountBar(client)`. Útil em teste/SSR. */\r\n fetchBar?: (client: NeetruClient) => Promise<AccountBarData | null>;\r\n /** Override parcial dos textos (default PT-BR). */\r\n labels?: Partial<AccountBarLabels>;\r\n /** Classe extra no container (o resto do estilo é inline). */\r\n className?: string;\r\n}\r\n\r\ntype OpenState = 'account' | 'apps' | null;\r\n\r\n/**\r\n * `<NeetruAccountBar client={client} currentProductSlug=\"pdv-agiliza\" settingsUrl=\"/config\" />`\r\n *\r\n * Renderiza waffle + avatar. Identidade aparece instantânea (do id_token via\r\n * `client.auth.getUser()`); produtos chegam do Core em seguida.\r\n */\r\nexport function NeetruAccountBar({\r\n client,\r\n currentProductSlug,\r\n settingsUrl,\r\n onSignOut,\r\n fetchBar,\r\n labels,\r\n className,\r\n}: NeetruAccountBarProps): React.ReactElement | null {\r\n const L: AccountBarLabels = { ...DEFAULT_LABELS, ...labels };\r\n\r\n // Identidade instantânea a partir do token já em mãos (sem esperar o fetch).\r\n const seedAccount = React.useMemo<AccountBarAccount | null>(() => {\r\n try {\r\n const u = client.auth.getUser();\r\n if (!u) return null;\r\n return {\r\n displayName: (u.displayName as string) || u.email || '',\r\n email: u.email || '',\r\n photoUrl: (u.photoURL as string) ?? null,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n }, [client]);\r\n\r\n const [data, setData] = React.useState<AccountBarData | null>(null);\r\n const [open, setOpen] = React.useState<OpenState>(null);\r\n const [signingOut, setSigningOut] = React.useState(false);\r\n\r\n const containerRef = React.useRef<HTMLDivElement | null>(null);\r\n const accountBtnRef = React.useRef<HTMLButtonElement | null>(null);\r\n const appsBtnRef = React.useRef<HTMLButtonElement | null>(null);\r\n const popoverRef = React.useRef<HTMLDivElement | null>(null);\r\n\r\n // Busca os dados no Core (produtos + identidade autoritativa).\r\n React.useEffect(() => {\r\n let cancelled = false;\r\n const run = fetchBar ?? fetchNeetruAccountBar;\r\n Promise.resolve(run(client))\r\n .then((res) => {\r\n if (!cancelled && res) setData(res);\r\n })\r\n .catch(() => {\r\n /* fail-soft: mantém identidade seed, sem produtos */\r\n });\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [client, fetchBar]);\r\n\r\n const account: AccountBarAccount | null = data?.account ?? seedAccount;\r\n const accountUrl = data?.accountUrl ?? 'https://minhaconta.neetru.com';\r\n const products = data?.products ?? [];\r\n\r\n const closeAndRefocus = React.useCallback((which: OpenState) => {\r\n setOpen(null);\r\n const trigger = which === 'apps' ? appsBtnRef.current : accountBtnRef.current;\r\n trigger?.focus();\r\n }, []);\r\n\r\n // Foco inicial no popover + fechar ao clicar fora.\r\n React.useEffect(() => {\r\n if (!open) return;\r\n const first = focusables(popoverRef.current)[0];\r\n first?.focus();\r\n\r\n const onDocMouseDown = (e: MouseEvent) => {\r\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\r\n setOpen(null);\r\n }\r\n };\r\n document.addEventListener('mousedown', onDocMouseDown);\r\n return () => document.removeEventListener('mousedown', onDocMouseDown);\r\n }, [open]);\r\n\r\n const onPopoverKeyDown = React.useCallback(\r\n (e: React.KeyboardEvent<HTMLDivElement>) => {\r\n if (e.key === 'Escape') {\r\n e.stopPropagation();\r\n closeAndRefocus(open);\r\n return;\r\n }\r\n const items = focusables(popoverRef.current);\r\n if (items.length === 0) return;\r\n const idx = items.indexOf(document.activeElement as HTMLElement);\r\n if (e.key === 'Tab') {\r\n // Focus trap.\r\n if (e.shiftKey && idx <= 0) {\r\n e.preventDefault();\r\n items[items.length - 1].focus();\r\n } else if (!e.shiftKey && idx === items.length - 1) {\r\n e.preventDefault();\r\n items[0].focus();\r\n }\r\n } else if (e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n items[(idx + 1) % items.length].focus();\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n items[(idx - 1 + items.length) % items.length].focus();\r\n }\r\n },\r\n [open, closeAndRefocus],\r\n );\r\n\r\n const handleSignOut = React.useCallback(async () => {\r\n if (signingOut) return;\r\n setSigningOut(true);\r\n try {\r\n await client.auth.signOut();\r\n } catch {\r\n /* fail-soft — segue com o callback do produto */\r\n } finally {\r\n setSigningOut(false);\r\n setOpen(null);\r\n onSignOut?.();\r\n }\r\n }, [client, signingOut, onSignOut]);\r\n\r\n // Sem identidade nenhuma → não renderiza (produto trata logged-out).\r\n if (!account) return null;\r\n\r\n const initials = initialsFrom(account.displayName || account.email);\r\n\r\n // ── Avatar ─────────────────────────────────────────────────────────────────\r\n const avatarInner = account.photoUrl\r\n ? h('img', {\r\n src: account.photoUrl,\r\n alt: '',\r\n width: 32,\r\n height: 32,\r\n style: { width: 32, height: 32, objectFit: 'cover', display: 'block' },\r\n })\r\n : h(\r\n 'span',\r\n {\r\n 'aria-hidden': true,\r\n style: {\r\n width: 32,\r\n height: 32,\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: C.fg,\r\n color: '#ffffff',\r\n fontSize: 13,\r\n fontWeight: 700,\r\n letterSpacing: '0.02em',\r\n },\r\n },\r\n initials,\r\n );\r\n\r\n const iconBtnStyle = (active: boolean): React.CSSProperties => ({\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 36,\r\n height: 36,\r\n padding: 0,\r\n borderRadius: SQUARE,\r\n border: `1px solid ${active ? C.border : C.borderSoft}`,\r\n backgroundColor: active ? C.bgHover : C.bg,\r\n color: C.fg,\r\n cursor: 'pointer',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n overflow: 'hidden',\r\n });\r\n\r\n const appsButton = h(\r\n 'button',\r\n {\r\n type: 'button',\r\n ref: appsBtnRef,\r\n 'aria-label': L.appsButton,\r\n 'aria-haspopup': 'menu',\r\n 'aria-expanded': open === 'apps',\r\n 'data-neetru-appswitcher-button': 'true',\r\n onClick: () => setOpen((o) => (o === 'apps' ? null : 'apps')),\r\n style: iconBtnStyle(open === 'apps'),\r\n },\r\n h(WaffleIcon, {}),\r\n );\r\n\r\n const accountButton = h(\r\n 'button',\r\n {\r\n type: 'button',\r\n ref: accountBtnRef,\r\n 'aria-label': `${L.accountButton}${account.displayName ? ` (${account.displayName})` : ''}`,\r\n 'aria-haspopup': 'menu',\r\n 'aria-expanded': open === 'account',\r\n 'data-neetru-account-button': 'true',\r\n onClick: () => setOpen((o) => (o === 'account' ? null : 'account')),\r\n style: { ...iconBtnStyle(open === 'account'), width: 36, height: 36 },\r\n },\r\n avatarInner,\r\n );\r\n\r\n // ── Popover base ─────────────────────────────────────────────────────────────\r\n const popoverStyle: React.CSSProperties = {\r\n position: 'absolute',\r\n top: 'calc(100% + 6px)',\r\n right: 0,\r\n minWidth: 260,\r\n maxWidth: 320,\r\n backgroundColor: C.bg,\r\n border: `1px solid ${C.border}`,\r\n borderRadius: SQUARE,\r\n zIndex: 1000,\r\n color: C.fg,\r\n fontFamily: 'inherit',\r\n // BMW: sem sombra/glassmorphism — borda sólida define o recorte.\r\n };\r\n\r\n const menuItemStyle: React.CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 10,\r\n width: '100%',\r\n padding: '10px 14px',\r\n background: 'transparent',\r\n border: 'none',\r\n borderTop: `1px solid ${C.borderSoft}`,\r\n color: C.fg,\r\n fontSize: 14,\r\n fontWeight: 400,\r\n textAlign: 'left',\r\n textDecoration: 'none',\r\n cursor: 'pointer',\r\n boxSizing: 'border-box',\r\n fontFamily: 'inherit',\r\n };\r\n\r\n const hoverHandlers = {\r\n onMouseEnter: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = C.bgHover;\r\n },\r\n onMouseLeave: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = 'transparent';\r\n },\r\n };\r\n\r\n // ── Popover: conta ───────────────────────────────────────────────────────────\r\n const accountPopover =\r\n open === 'account'\r\n ? h(\r\n 'div',\r\n {\r\n ref: popoverRef,\r\n role: 'menu',\r\n 'aria-label': L.accountButton,\r\n 'data-neetru-account-menu': 'true',\r\n onKeyDown: onPopoverKeyDown,\r\n style: popoverStyle,\r\n },\r\n // Header identidade\r\n h(\r\n 'div',\r\n { style: { display: 'flex', gap: 12, alignItems: 'center', padding: 14 } },\r\n h(\r\n 'span',\r\n {\r\n 'aria-hidden': true,\r\n style: {\r\n width: 40,\r\n height: 40,\r\n flexShrink: 0,\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n overflow: 'hidden',\r\n backgroundColor: account.photoUrl ? 'transparent' : C.fg,\r\n color: '#ffffff',\r\n fontSize: 15,\r\n fontWeight: 700,\r\n },\r\n },\r\n account.photoUrl\r\n ? h('img', {\r\n src: account.photoUrl,\r\n alt: '',\r\n width: 40,\r\n height: 40,\r\n style: { width: 40, height: 40, objectFit: 'cover', display: 'block' },\r\n })\r\n : initials,\r\n ),\r\n h(\r\n 'span',\r\n { style: { display: 'flex', flexDirection: 'column', minWidth: 0 } },\r\n h(\r\n 'span',\r\n {\r\n style: {\r\n fontWeight: 700,\r\n fontSize: 14,\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n },\r\n },\r\n account.displayName || account.email,\r\n ),\r\n account.email\r\n ? h(\r\n 'span',\r\n {\r\n style: {\r\n color: C.muted,\r\n fontSize: 12,\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n },\r\n },\r\n account.email,\r\n )\r\n : null,\r\n ),\r\n ),\r\n // Minha conta Neetru\r\n h(\r\n 'a',\r\n {\r\n role: 'menuitem',\r\n href: accountUrl,\r\n target: '_blank',\r\n rel: 'noopener noreferrer',\r\n style: menuItemStyle,\r\n ...hoverHandlers,\r\n },\r\n L.myAccount,\r\n ),\r\n // Configurações da conta (deste produto)\r\n settingsUrl\r\n ? h(\r\n 'a',\r\n { role: 'menuitem', href: settingsUrl, style: menuItemStyle, ...hoverHandlers },\r\n L.accountSettings,\r\n )\r\n : null,\r\n // Sair\r\n h(\r\n 'button',\r\n {\r\n type: 'button',\r\n role: 'menuitem',\r\n onClick: handleSignOut,\r\n disabled: signingOut,\r\n 'data-neetru-signout': 'true',\r\n style: { ...menuItemStyle, cursor: signingOut ? 'not-allowed' : 'pointer' },\r\n ...hoverHandlers,\r\n },\r\n signingOut ? `${L.signOut}…` : L.signOut,\r\n ),\r\n )\r\n : null;\r\n\r\n // ── Popover: produtos (waffle) ───────────────────────────────────────────────\r\n const appsPopover =\r\n open === 'apps'\r\n ? h(\r\n 'div',\r\n {\r\n ref: popoverRef,\r\n role: 'menu',\r\n 'aria-label': L.appsTitle,\r\n 'data-neetru-appswitcher-menu': 'true',\r\n onKeyDown: onPopoverKeyDown,\r\n style: { ...popoverStyle, minWidth: 280 },\r\n },\r\n h(\r\n 'div',\r\n {\r\n style: {\r\n padding: '12px 14px 8px',\r\n fontSize: 11,\r\n fontWeight: 700,\r\n letterSpacing: '0.08em',\r\n textTransform: 'uppercase',\r\n color: C.muted,\r\n },\r\n },\r\n L.appsTitle,\r\n ),\r\n products.length === 0\r\n ? h(\r\n 'div',\r\n { style: { padding: '4px 14px 16px', fontSize: 13, color: C.muted } },\r\n L.emptyProducts,\r\n )\r\n : h(\r\n 'div',\r\n {\r\n style: {\r\n display: 'grid',\r\n gridTemplateColumns: '1fr 1fr',\r\n gap: 1,\r\n padding: 1,\r\n backgroundColor: C.borderSoft,\r\n },\r\n },\r\n products.map((p) => {\r\n const isCurrent = !!currentProductSlug && p.slug === currentProductSlug;\r\n return h(\r\n 'a',\r\n {\r\n key: p.slug,\r\n role: 'menuitem',\r\n href: p.url,\r\n 'aria-current': isCurrent ? 'true' : undefined,\r\n 'data-neetru-product': p.slug,\r\n 'data-current': isCurrent ? 'true' : undefined,\r\n title: p.name,\r\n style: {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n gap: 6,\r\n padding: '14px 8px',\r\n textDecoration: 'none',\r\n color: C.fg,\r\n backgroundColor: isCurrent ? C.bgHover : C.bg,\r\n cursor: 'pointer',\r\n boxSizing: 'border-box',\r\n },\r\n onMouseEnter: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = C.bgHover;\r\n },\r\n onMouseLeave: (e: React.MouseEvent<HTMLElement>) => {\r\n (e.currentTarget as HTMLElement).style.backgroundColor = isCurrent\r\n ? C.bgHover\r\n : C.bg;\r\n },\r\n },\r\n // Tile/ícone\r\n p.iconUrl\r\n ? h('img', {\r\n src: p.iconUrl,\r\n alt: '',\r\n width: 32,\r\n height: 32,\r\n style: { width: 32, height: 32, objectFit: 'cover', display: 'block' },\r\n })\r\n : h(\r\n 'span',\r\n {\r\n 'aria-hidden': true,\r\n style: {\r\n width: 32,\r\n height: 32,\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: C.fg,\r\n color: '#ffffff',\r\n fontSize: 13,\r\n fontWeight: 700,\r\n },\r\n },\r\n initialsFrom(p.name),\r\n ),\r\n h(\r\n 'span',\r\n {\r\n style: {\r\n fontSize: 12,\r\n fontWeight: isCurrent ? 700 : 400,\r\n textAlign: 'center',\r\n lineHeight: 1.2,\r\n maxWidth: '100%',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n whiteSpace: 'nowrap',\r\n width: '100%',\r\n },\r\n },\r\n p.name,\r\n ),\r\n isCurrent\r\n ? h(\r\n 'span',\r\n {\r\n style: {\r\n fontSize: 9,\r\n fontWeight: 700,\r\n letterSpacing: '0.08em',\r\n textTransform: 'uppercase',\r\n color: C.muted,\r\n },\r\n },\r\n L.currentBadge,\r\n )\r\n : null,\r\n );\r\n }),\r\n ),\r\n )\r\n : null;\r\n\r\n return h(\r\n 'div',\r\n {\r\n ref: containerRef,\r\n className,\r\n 'data-neetru-account-bar': 'true',\r\n style: {\r\n position: 'relative',\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n gap: 8,\r\n // Superfície de cliente = sempre claro (dark é staff-only).\r\n colorScheme: 'light',\r\n fontFamily: 'inherit',\r\n },\r\n },\r\n appsButton,\r\n accountButton,\r\n accountPopover,\r\n appsPopover,\r\n );\r\n}\r\n","/**\r\n * @neetru/sdk/firestore-compat — camada de COMPATIBILIDADE (transicional).\r\n *\r\n * ⚠️ **STATUS: compat / transitional.** Este módulo espelha a superfície\r\n * modular do Firebase Firestore (`doc`, `collection`, `query`, `where`,\r\n * `getDocs`, `setDoc`, `onSnapshot`, `writeBatch`, `runTransaction`,\r\n * `serverTimestamp`, `Timestamp`, `FieldValue`, …) para que produtos que\r\n * migram do Firebase **não precisem reescrever as chamadas à mão**.\r\n *\r\n * Ele NÃO é a API nativa do SDK e NÃO compete com `client.db` (NeetruDb).\r\n * É um auxílio de migração: troque os imports de `firebase/firestore` por\r\n * `@neetru/sdk/firestore-compat`, valide, e depois migre incrementalmente\r\n * para a superfície nativa `client.db.collection(...)` quando fizer sentido.\r\n *\r\n * Esta implementação foi desenhada a partir dos shims reais que os produtos\r\n * `neetru-gestovendas` (`src/neetru/firestore.ts`) e `neetru-pdvagiliza`\r\n * (`src/lib/neetru/firestore-compat.ts`) escreveram à mão — espelha\r\n * exatamente a superfície que eles precisam para poder **deletar os shims**.\r\n *\r\n * ### Backing\r\n * Tudo flui por `client.db` (`NeetruDb`). As funções recebem ou um `NeetruDb`\r\n * (o `client.db`) ou o `NeetruClient` inteiro como primeiro argumento — ambos\r\n * são aceitos por `getFirestore(...)`/`collection(...)`/`doc(...)`.\r\n *\r\n * ### Diferenças deliberadas vs Firebase\r\n * - `serverTimestamp()` e `increment()`/`arrayUnion()`/`arrayRemove()` retornam\r\n * sentinels do SDK (`@neetru/sdk` `serverTimestamp`/`increment`) — resolvidos\r\n * SERVER-SIDE pelo Core (não pelo relógio spoofável do cliente).\r\n * - `Timestamp` é uma classe leve (sem proto de Firebase) com `toDate()`,\r\n * `toMillis()`, `seconds`, `nanoseconds` e `Timestamp.now()/fromDate()/fromMillis()`.\r\n * - `onSnapshot` usa o realtime nativo do `client.db` (`onSnapshot`/`doc().onSnapshot`)\r\n * quando o transporte oferece; caso contrário entrega o snapshot inicial e\r\n * reage às mudanças locais (offline-first do SDK).\r\n *\r\n * @module\r\n */\r\n\r\nimport type {\r\n NeetruDb,\r\n DbCollectionRef,\r\n DbQuery,\r\n DbWhereFilter,\r\n DbListResult,\r\n DbGetResult,\r\n} from './db/client-db';\r\nimport {\r\n serverTimestamp as sdkServerTimestamp,\r\n increment as sdkIncrement,\r\n type FieldSentinel,\r\n} from './db/field-value';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Resolução do handle de banco\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Aceita tanto o `NeetruDb` (`client.db`) quanto o `NeetruClient` inteiro.\r\n * Produtos legados chamam `getFirestore(client)` ou `getFirestore(client.db)`;\r\n * ambos resolvem para o `NeetruDb` subjacente.\r\n */\r\nexport type FirestoreLike = NeetruDb | { db: NeetruDb };\r\n\r\n/** Marker brand reconhecível em runtime — `getFirestore()` devolve isto. */\r\nexport interface Firestore {\r\n readonly __neetruFirestore: true;\r\n readonly db: NeetruDb;\r\n}\r\n\r\nfunction isClientWrapper(v: FirestoreLike | Firestore): v is { db: NeetruDb } {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n 'db' in v &&\r\n typeof (v as { db?: unknown }).db === 'object' &&\r\n (v as { db?: { collection?: unknown } }).db?.collection !== undefined\r\n );\r\n}\r\n\r\nfunction resolveDb(handle: FirestoreLike | Firestore): NeetruDb {\r\n if ((handle as Firestore).__neetruFirestore) return (handle as Firestore).db;\r\n if (isClientWrapper(handle)) return handle.db;\r\n // É o próprio NeetruDb (tem `.collection`).\r\n return handle as NeetruDb;\r\n}\r\n\r\n/**\r\n * Equivalente compat de `getFirestore(app)`. Aceita o `NeetruClient` ou o\r\n * `client.db` (`NeetruDb`) e devolve um handle `Firestore` opaco.\r\n *\r\n * @example\r\n * ```ts\r\n * import { getFirestore } from '@neetru/sdk/firestore-compat';\r\n * const db = getFirestore(client); // ou getFirestore(client.db)\r\n * ```\r\n */\r\nexport function getFirestore(handle: FirestoreLike): Firestore {\r\n return { __neetruFirestore: true, db: resolveDb(handle) };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Refs — Collection & Document\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Operadores `where` suportados (subset do Firestore + ops do SDK). */\r\nexport type WhereFilterOp =\r\n | '=='\r\n | '!='\r\n | '<'\r\n | '<='\r\n | '>'\r\n | '>='\r\n | 'in'\r\n | 'not-in'\r\n | 'array-contains'\r\n | 'array-contains-any';\r\n\r\nexport interface CollectionReference<T = Record<string, unknown>> {\r\n readonly type: 'collection';\r\n readonly __kind: 'collection';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly db: NeetruDb;\r\n /** Handle nativo do SDK — atalho para a superfície `client.db`. */\r\n readonly ref: DbCollectionRef<T>;\r\n}\r\n\r\nexport interface DocumentReference<T = Record<string, unknown>> {\r\n readonly type: 'document';\r\n readonly __kind: 'document';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n /** Marcador de tipo (phantom) — preserva `T` para inferência do caller. */\r\n readonly __doc?: T;\r\n}\r\n\r\n/** Aceita tanto `Firestore` quanto `NeetruDb`/`NeetruClient` direto. */\r\ntype DbArg = Firestore | FirestoreLike;\r\n\r\n/**\r\n * `collection(db, name)` — referência a uma coleção.\r\n *\r\n * Aceita `db` como `Firestore` (de `getFirestore`), `NeetruClient` ou `client.db`.\r\n */\r\nexport function collection<T = Record<string, unknown>>(\r\n db: DbArg,\r\n name: string,\r\n): CollectionReference<T> {\r\n const resolved = resolveDb(db);\r\n return {\r\n type: 'collection',\r\n __kind: 'collection',\r\n id: name,\r\n path: name,\r\n db: resolved,\r\n ref: resolved.collection<T>(name),\r\n };\r\n}\r\n\r\n/**\r\n * `doc(...)` — referência a um documento. Suporta as 3 assinaturas legadas:\r\n * doc(db, 'collection', 'id') → coleção + id\r\n * doc(collectionRef, 'id') → id dentro de uma CollectionReference\r\n * doc(db, 'collection/id') → path com barra (id auto-gerado se id ausente)\r\n *\r\n * Quando o `id` é omitido, gera um id v4 (espelha `doc(collectionRef)` do Firebase\r\n * que cria uma ref com id automático antes do `setDoc`).\r\n */\r\nexport function doc<T = Record<string, unknown>>(\r\n dbOrCollection: DbArg | CollectionReference<T>,\r\n ...path: string[]\r\n): DocumentReference<T> {\r\n // Forma doc(collectionRef, id?)\r\n if (\r\n typeof dbOrCollection === 'object' &&\r\n dbOrCollection !== null &&\r\n (dbOrCollection as CollectionReference<T>).__kind === 'collection'\r\n ) {\r\n const col = dbOrCollection as CollectionReference<T>;\r\n const id = path[0] ?? generateId();\r\n return makeDocRef<T>(col.db, col.id, id);\r\n }\r\n\r\n const resolved = resolveDb(dbOrCollection as DbArg);\r\n\r\n if (path.length >= 2) {\r\n return makeDocRef<T>(resolved, path[0], path.slice(1).join('/'));\r\n }\r\n if (path[0]?.includes('/')) {\r\n const parts = path[0].split('/');\r\n return makeDocRef<T>(resolved, parts[0], parts.slice(1).join('/') || generateId());\r\n }\r\n // doc(db, 'collection') — id auto-gerado\r\n return makeDocRef<T>(resolved, path[0] ?? '', generateId());\r\n}\r\n\r\nfunction makeDocRef<T>(db: NeetruDb, collectionName: string, id: string): DocumentReference<T> {\r\n return {\r\n type: 'document',\r\n __kind: 'document',\r\n id,\r\n collection: collectionName,\r\n path: `${collectionName}/${id}`,\r\n db,\r\n };\r\n}\r\n\r\n/**\r\n * Gera um id estilo Firestore (20 chars alfanuméricos) com aleatoriedade\r\n * CRIPTOGRÁFICA.\r\n *\r\n * Antes usava `Math.random()` — previsível e colisível (PRNG não-criptográfico,\r\n * estado recuperável). IDs de documento previsíveis permitem enumeração/forja.\r\n * Agora usa `globalThis.crypto.getRandomValues` (WebCrypto), disponível tanto em\r\n * Node ≥20 quanto em browsers — sem importar `node:crypto` (que quebraria o\r\n * bundle browser). Rejeita bytes ≥ 248 (4×62) para eliminar o viés de módulo\r\n * sobre o alfabeto de 62 caracteres.\r\n */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 62\r\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!cryptoObj?.getRandomValues) {\r\n throw new Error(\r\n '[Neetru SDK / firestore-compat] WebCrypto indisponível: generateId() exige ' +\r\n 'globalThis.crypto.getRandomValues (Node ≥20 ou ambiente browser).',\r\n );\r\n }\r\n let out = '';\r\n while (out.length < 20) {\r\n const buf = new Uint8Array(20 - out.length);\r\n cryptoObj.getRandomValues(buf);\r\n for (const byte of buf) {\r\n if (byte < 248) {\r\n out += CHARS[byte % 62];\r\n if (out.length === 20) break;\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUERY — acumula constraints e gera DbQuery quando consumido\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type QueryConstraintType = 'where' | 'orderBy' | 'limit';\r\n\r\nexport interface QueryConstraint {\r\n readonly __constraint: QueryConstraintType;\r\n}\r\n\r\ninterface WhereConstraint extends QueryConstraint {\r\n readonly __constraint: 'where';\r\n readonly field: string;\r\n readonly op: WhereFilterOp;\r\n readonly value: unknown;\r\n}\r\n\r\ninterface OrderByConstraint extends QueryConstraint {\r\n readonly __constraint: 'orderBy';\r\n readonly field: string;\r\n readonly direction: 'asc' | 'desc';\r\n}\r\n\r\ninterface LimitConstraint extends QueryConstraint {\r\n readonly __constraint: 'limit';\r\n readonly value: number;\r\n}\r\n\r\nexport interface Query<T = Record<string, unknown>> {\r\n readonly type: 'query';\r\n readonly __kind: 'query';\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n readonly ref: DbCollectionRef<T>;\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * `query(collectionRef, ...constraints)` — combina `where`/`orderBy`/`limit`.\r\n * Aceita também uma `Query` como fonte (encadeamento de constraints).\r\n */\r\nexport function query<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const base: Query<T> =\r\n source.__kind === 'query'\r\n ? source\r\n : {\r\n type: 'query',\r\n __kind: 'query',\r\n path: source.path,\r\n collection: source.id,\r\n db: source.db,\r\n ref: source.ref,\r\n constraints: [],\r\n };\r\n return { ...base, constraints: [...base.constraints, ...constraints] };\r\n}\r\n\r\nexport function where(field: string, op: WhereFilterOp, value: unknown): WhereConstraint {\r\n return { __constraint: 'where', field, op, value };\r\n}\r\n\r\nexport function orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): OrderByConstraint {\r\n return { __constraint: 'orderBy', field, direction };\r\n}\r\n\r\nexport function limit(value: number): LimitConstraint {\r\n return { __constraint: 'limit', value };\r\n}\r\n\r\n/**\r\n * Converte os constraints de uma `Query` compat para o `DbQuery` nativo do SDK.\r\n *\r\n * Exportado para que os hooks React (`@neetru/sdk/react`) possam reusar a mesma\r\n * tradução — fonte única de verdade da conversão de constraints.\r\n */\r\nexport function toDbQuery(constraints: QueryConstraint[]): DbQuery {\r\n const whereFilters: DbWhereFilter[] = [];\r\n let orderByClause: DbQuery['orderBy'];\r\n let limitValue: number | undefined;\r\n\r\n for (const c of constraints) {\r\n if (c.__constraint === 'where') {\r\n const w = c as WhereConstraint;\r\n whereFilters.push({ field: w.field, op: normalizeOp(w.op), value: w.value });\r\n } else if (c.__constraint === 'orderBy') {\r\n const o = c as OrderByConstraint;\r\n orderByClause = { field: o.field, direction: o.direction };\r\n } else if (c.__constraint === 'limit') {\r\n limitValue = (c as LimitConstraint).value;\r\n }\r\n }\r\n\r\n const out: DbQuery = {};\r\n if (whereFilters.length > 0) out.where = whereFilters;\r\n if (orderByClause) out.orderBy = orderByClause;\r\n if (limitValue !== undefined) out.limit = limitValue;\r\n return out;\r\n}\r\n\r\n/**\r\n * O `DbWhereFilter` do SDK não declara `array-contains-any`/`not-in` no union.\r\n * Passamos o op como-está; o Core valida server-side e retorna erro explícito\r\n * se não suportado.\r\n *\r\n * Bug corrigido: remapeava 'array-contains-any' → 'array-contains' silenciosamente,\r\n * o que tem semântica completamente diferente (checa literal do array, não interseção)\r\n * e nunca retornava resultados para queries reais.\r\n */\r\nfunction normalizeOp(op: WhereFilterOp): DbWhereFilter['op'] {\r\n // Todas as ops passam direto; o Core valida server-side.\r\n return op as DbWhereFilter['op'];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SNAPSHOTS — emulam a API antiga do Firebase\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Remove o campo `id` dos dados (o Firestore expõe id via `.id`, não em `data()`). */\r\nfunction stripId<T>(raw: Record<string, unknown>): T {\r\n if (raw && typeof raw === 'object' && 'id' in raw) {\r\n const { id: _omit, ...rest } = raw;\r\n return rest as T;\r\n }\r\n return raw as T;\r\n}\r\n\r\nexport class DocumentSnapshot<T = Record<string, unknown>> {\r\n constructor(\r\n public readonly id: string,\r\n private readonly _data: T | null,\r\n public readonly ref?: DocumentReference<T>,\r\n ) {}\r\n\r\n exists(): boolean {\r\n return this._data != null;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._data ?? undefined;\r\n }\r\n\r\n /** Compat: `snap.get('field')` lê um campo top-level. */\r\n get(field: string): unknown {\r\n return this._data ? (this._data as Record<string, unknown>)[field] : undefined;\r\n }\r\n}\r\n\r\nexport interface QueryDocumentSnapshot<T = Record<string, unknown>> {\r\n readonly id: string;\r\n data(): T;\r\n exists(): boolean;\r\n get(field: string): unknown;\r\n readonly ref: DocumentReference<T>;\r\n}\r\n\r\nexport class QuerySnapshot<T = Record<string, unknown>> {\r\n public readonly docs: Array<QueryDocumentSnapshot<T>>;\r\n public readonly empty: boolean;\r\n public readonly size: number;\r\n\r\n constructor(\r\n rows: Array<{ id: string; data: Record<string, unknown> }>,\r\n db?: NeetruDb,\r\n collectionName?: string,\r\n ) {\r\n this.docs = rows.map(({ id, data }) => {\r\n const stripped = stripId<T>(data);\r\n const ref: DocumentReference<T> | undefined =\r\n db && collectionName ? makeDocRef<T>(db, collectionName, id) : undefined;\r\n return {\r\n id,\r\n data: () => stripped,\r\n exists: () => true,\r\n get: (field: string) => (stripped as Record<string, unknown>)?.[field],\r\n // ref sempre presente quando db+collection conhecidos (caso comum).\r\n ref: ref as DocumentReference<T>,\r\n };\r\n });\r\n this.empty = this.docs.length === 0;\r\n this.size = this.docs.length;\r\n }\r\n\r\n forEach(cb: (doc: QueryDocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(cb);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// READS\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Extrai os dados crus do primeiro doc de um `DbGetResult`. */\r\nfunction firstDocData(result: DbGetResult<Record<string, unknown>> | null): {\r\n data: Record<string, unknown> | null;\r\n id: string | null;\r\n} {\r\n const first = result?.docs[0];\r\n return { data: first ? (first.data as Record<string, unknown>) : null, id: first?.id ?? null };\r\n}\r\n\r\nexport async function getDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n): Promise<DocumentSnapshot<T>> {\r\n const result = (await ref.db.collection(ref.collection).get(ref.id)) as DbGetResult<\r\n Record<string, unknown>\r\n > | null;\r\n const { data } = firstDocData(result);\r\n return new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? (stripId<T>({ id: ref.id, ...data })) : null,\r\n ref,\r\n );\r\n}\r\n\r\n/** Mapeia um `DbListResult` para linhas `{ id, data }` (id garantido). */\r\nfunction listResultToRows(\r\n result: DbListResult<Record<string, unknown>>,\r\n): Array<{ id: string; data: Record<string, unknown> }> {\r\n return result.docs.map((d) => ({ id: d.id, data: { ...(d.data as Record<string, unknown>) } }));\r\n}\r\n\r\nexport async function getDocs<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n): Promise<QuerySnapshot<T>> {\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const collectionName = source.__kind === 'query' ? source.collection : source.id;\r\n const result = (await source.db\r\n .collection(collectionName)\r\n .list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n return new QuerySnapshot<T>(listResultToRows(result), source.db, collectionName);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITES\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SetOptions {\r\n merge?: boolean;\r\n mergeFields?: string[];\r\n}\r\n\r\nexport async function setDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options: SetOptions = {},\r\n): Promise<void> {\r\n const col = ref.db.collection(ref.collection);\r\n const payload = data as Record<string, unknown>;\r\n if (options.merge || (options.mergeFields && options.mergeFields.length > 0)) {\r\n // bug_0273e4dd: ANTES o check de existência usava o cache LOCAL (col.get é\r\n // offline-first). Doc que existia só no SERVIDOR dava docExists=false → caía\r\n // em col.set() (replace) e APAGAVA todos os campos ausentes do payload →\r\n // perda de dados silenciosa. Agora a decisão é DO SERVIDOR via merge-upsert:\r\n // update({upsert:true}) cria-se-ausente OU mescla-se-presente, sem nunca\r\n // apagar campos não enviados — exatamente o setDoc({merge:true}) do Firebase.\r\n // Contrato Core acordado (deploy0632): PATCH com upsert=true não 404a em\r\n // ausente e faz ref.set(data,{merge:true}).\r\n await col.update(ref.id, payload, { upsert: true });\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n}\r\n\r\nexport async function addDoc<T = Record<string, unknown>>(\r\n source: CollectionReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<DocumentReference<T>> {\r\n const result = await source.db.collection(source.id).add(data as Record<string, unknown>);\r\n return makeDocRef<T>(source.db, source.id, result.id);\r\n}\r\n\r\nexport async function updateDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<void> {\r\n await ref.db.collection(ref.collection).update(ref.id, data as Record<string, unknown>);\r\n}\r\n\r\nexport async function deleteDoc(ref: DocumentReference): Promise<void> {\r\n await ref.db.collection(ref.collection).remove(ref.id);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITE BATCH\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\ninterface PendingBatchOp {\r\n collection: string;\r\n op:\r\n | { kind: 'set'; id: string; data: Record<string, unknown> }\r\n | { kind: 'update'; id: string; data: Record<string, unknown>; upsert?: boolean }\r\n | { kind: 'remove'; id: string };\r\n}\r\n\r\nexport class WriteBatch {\r\n private ops: PendingBatchOp[] = [];\r\n\r\n constructor(private readonly db: NeetruDb) {}\r\n\r\n set<T>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options?: SetOptions,\r\n ): this {\r\n // bug_0273e4dd: SetOptions.merge era DESCARTADO → batch.set(ref,data,{merge:true})\r\n // virava replace e APAGAVA campos ausentes (perda de dados). Agora merge vira\r\n // update com upsert (merge-server-side, cria-se-ausente), igual ao setDoc.\r\n const merge = !!(options?.merge || (options?.mergeFields && options.mergeFields.length > 0));\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: merge\r\n ? { kind: 'update', id: ref.id, data: data as Record<string, unknown>, upsert: true }\r\n : { kind: 'set', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'update', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n delete(ref: DocumentReference): this {\r\n this.ops.push({ collection: ref.collection, op: { kind: 'remove', id: ref.id } });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n // Agrupa por coleção — o `DbCollectionRef.batch` opera numa coleção por vez.\r\n const byCol = new Map<string, PendingBatchOp['op'][]>();\r\n for (const { collection: name, op } of this.ops) {\r\n const list = byCol.get(name) ?? [];\r\n list.push(op);\r\n byCol.set(name, list);\r\n }\r\n for (const [name, ops] of byCol.entries()) {\r\n // O union nativo `DbBatchOp` casa 1:1 com nossas variantes (set/update/remove).\r\n await this.db.collection(name).batch(ops as never);\r\n }\r\n this.ops = [];\r\n }\r\n}\r\n\r\nexport function writeBatch(db: DbArg): WriteBatch {\r\n return new WriteBatch(resolveDb(db));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// runTransaction — emulação otimista (read → write) sobre client.db\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Handle de transação compat. O SDK não expõe transações multi-doc atômicas\r\n * com lock server-side, então emulamos a forma do Firebase: `tx.get` lê do\r\n * `client.db`, e `tx.set/update/delete` enfileiram escritas que são aplicadas\r\n * no fim do callback.\r\n *\r\n * ⚠️ **LIMITAÇÃO CONHECIDA — sem isolamento server-side (lost-update silencioso):**\r\n *\r\n * Diferente do Firestore nativo (que usa serializable snapshot isolation com\r\n * contention detection), este shim executa reads e writes em rounds separados\r\n * sem garantia de atomicidade:\r\n *\r\n * 1. `tx.get(ref)` faz um read imediato via `client.db`.\r\n * 2. As escritas (`tx.set/update/delete`) são **enfileiradas** — não bloqueiam\r\n * outros writers.\r\n * 3. No fim do callback, `batch.commit()` aplica as escritas. Se outro processo\r\n * tiver modificado o mesmo documento entre o read e o commit, a escrita\r\n * será um **lost-update silencioso** (sem erro, sem retry automático).\r\n *\r\n * **Quando isso importa:** counters, saldos, estoques — qualquer dado\r\n * que precise de leitura-modificação-escrita atômica.\r\n *\r\n * **Alternativas seguras:**\r\n * - Use **Server Actions no Core** (`src/lib/actions/`) que rodam dentro\r\n * de transações Firestore Admin SDK reais (com locks server-side).\r\n * - Use `increment()` sentinel para contadores numéricos — server-side atomic.\r\n * - Use `arrayUnion()`/`arrayRemove()` para sets (também server-side atomic).\r\n *\r\n * Esta emulação é aceitável para migrações transitórias e para operações\r\n * onde a probabilidade de concorrência é baixa (ex: dados de um único usuário\r\n * sem write concorrente). **Não use para dados financeiros ou de inventário.**\r\n */\r\nexport interface Transaction {\r\n get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n set<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>, options?: SetOptions): Transaction;\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): Transaction;\r\n delete(ref: DocumentReference): Transaction;\r\n}\r\n\r\n/** Flag para emitir o aviso de limitação uma só vez por processo (não spammar). */\r\nlet _runTransactionWarnEmitted = false;\r\n\r\nexport async function runTransaction<R>(\r\n db: DbArg,\r\n updateFunction: (transaction: Transaction) => Promise<R>,\r\n): Promise<R> {\r\n // Aviso em desenvolvimento/staging: sem isolamento server-side.\r\n if (!_runTransactionWarnEmitted) {\r\n _runTransactionWarnEmitted = true;\r\n console.warn(\r\n '[Neetru SDK / firestore-compat] runTransaction(): esta emulação NÃO oferece' +\r\n ' isolamento server-side. Reads e writes são executados em rounds separados;' +\r\n ' um write concorrente entre o get() e o commit() resultará em lost-update' +\r\n ' silencioso. Para atomicidade real, use Server Actions no Core ou sentinels' +\r\n ' increment()/arrayUnion(). Veja a doc em src/firestore-compat.ts § runTransaction.',\r\n );\r\n }\r\n\r\n const resolved = resolveDb(db);\r\n const batch = new WriteBatch(resolved);\r\n\r\n const tx: Transaction = {\r\n async get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n return getDoc(ref);\r\n },\r\n set(ref, data, options) {\r\n batch.set(ref, data, options);\r\n return tx;\r\n },\r\n update(ref, data) {\r\n batch.update(ref, data);\r\n return tx;\r\n },\r\n delete(ref) {\r\n batch.delete(ref);\r\n return tx;\r\n },\r\n };\r\n\r\n const result = await updateFunction(tx);\r\n await batch.commit();\r\n return result;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// onSnapshot — realtime via client.db (com fallback inicial)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type Unsubscribe = () => void;\r\n\r\ntype DocOnNext<T> = (snap: DocumentSnapshot<T>) => void;\r\ntype QueryOnNext<T> = (snap: QuerySnapshot<T>) => void;\r\ntype OnError = (err: Error) => void;\r\n\r\nexport function onSnapshot<T>(\r\n ref: DocumentReference<T>,\r\n onNext: DocOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: Query<T> | CollectionReference<T>,\r\n onNext: QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNext: DocOnNext<T> | QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe {\r\n // Documento único\r\n if (source.__kind === 'document') {\r\n const ref = source as DocumentReference<T>;\r\n const docRef = ref.db.collection(ref.collection).doc(ref.id);\r\n try {\r\n return docRef.onSnapshot((snap) => {\r\n const data = snap?.docs[0]?.data as Record<string, unknown> | undefined;\r\n (onNext as DocOnNext<T>)(\r\n new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? stripId<T>({ id: ref.id, ...data }) : null,\r\n ref,\r\n ),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n }\r\n\r\n // Coleção / Query\r\n const collectionName = source.__kind === 'query' ? source.collection : (source as CollectionReference<T>).id;\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const col = source.db.collection(collectionName);\r\n try {\r\n return col.onSnapshot(dbQuery, (snap: DbListResult<Record<string, unknown>>) => {\r\n (onNext as QueryOnNext<T>)(\r\n new QuerySnapshot<T>(listResultToRows(snap), source.db, collectionName),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Timestamp — classe leve compat\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Reimplementação compat de `Timestamp` do Firestore. Sem proto do Firebase.\r\n * Cobre `seconds`, `nanoseconds`, `toDate()`, `toMillis()`, `isEqual()`,\r\n * `valueOf()`, `toJSON()` + estáticos `now()`, `fromDate()`, `fromMillis()`.\r\n */\r\nexport class Timestamp {\r\n constructor(\r\n public readonly seconds: number,\r\n public readonly nanoseconds: number,\r\n ) {}\r\n\r\n static now(): Timestamp {\r\n return Timestamp.fromMillis(Date.now());\r\n }\r\n\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n static fromMillis(millis: number): Timestamp {\r\n const seconds = Math.floor(millis / 1000);\r\n const nanoseconds = (millis - seconds * 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n valueOf(): string {\r\n // Estável para ordenação por string (espelha Firebase).\r\n return `${this.seconds.toString().padStart(12, '0')}.${this.nanoseconds\r\n .toString()\r\n .padStart(9, '0')}`;\r\n }\r\n\r\n toJSON(): { seconds: number; nanoseconds: number } {\r\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\r\n }\r\n\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// FieldValue + sentinels\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sentinel de `arrayUnion`/`arrayRemove`. O SDK ainda não tem sentinel de array\r\n * server-side, então emitimos um marker namespaceado que o Core reconhece\r\n * (mesma família de `__neetru__`). Se o backing não suportar, o produto deve\r\n * preferir read-modify-write via Server Action. Documentado como compat.\r\n */\r\nexport interface ArrayUnionSentinel {\r\n readonly __neetru__: 'arrayUnion';\r\n readonly elements: unknown[];\r\n}\r\nexport interface ArrayRemoveSentinel {\r\n readonly __neetru__: 'arrayRemove';\r\n readonly elements: unknown[];\r\n}\r\n\r\n/**\r\n * Sentinel de campo a deletar (compat `deleteField()`).\r\n */\r\nexport interface DeleteFieldSentinel {\r\n readonly __neetru__: 'deleteField';\r\n}\r\n\r\n/**\r\n * `serverTimestamp()` — delega ao sentinel do SDK (resolvido SERVER-SIDE).\r\n * NÃO retorna mais ISO/Date do cliente (que era spoofável).\r\n */\r\nexport function serverTimestamp(): FieldSentinel {\r\n return sdkServerTimestamp();\r\n}\r\n\r\n/**\r\n * `increment(n)` — incremento atômico server-side (delega ao sentinel do SDK).\r\n */\r\nexport function increment(n: number): FieldSentinel {\r\n return sdkIncrement(n);\r\n}\r\n\r\n/** `arrayUnion(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayUnion(...elements: unknown[]): ArrayUnionSentinel {\r\n return { __neetru__: 'arrayUnion', elements };\r\n}\r\n\r\n/** `arrayRemove(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayRemove(...elements: unknown[]): ArrayRemoveSentinel {\r\n return { __neetru__: 'arrayRemove', elements };\r\n}\r\n\r\n/** `deleteField()` — sentinel compat para remover um campo. */\r\nexport function deleteField(): DeleteFieldSentinel {\r\n return { __neetru__: 'deleteField' };\r\n}\r\n\r\n/**\r\n * Namespace estático `FieldValue` — espelha `firebase/firestore`'s `FieldValue`.\r\n * Os métodos delegam às funções modulares acima.\r\n *\r\n * @example\r\n * ```ts\r\n * import { FieldValue } from '@neetru/sdk/firestore-compat';\r\n * await updateDoc(ref, {\r\n * tags: FieldValue.arrayUnion('novo'),\r\n * counter: FieldValue.increment(1),\r\n * updatedAt: FieldValue.serverTimestamp(),\r\n * });\r\n * ```\r\n */\r\nexport const FieldValue = {\r\n serverTimestamp,\r\n increment,\r\n arrayUnion,\r\n arrayRemove,\r\n delete: deleteField,\r\n} as const;\r\n","/**\r\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\r\n *\r\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\r\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\r\n *\r\n * Exports:\r\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\r\n * - `<NeetruLoginButton>` — botão padronizado \"Entrar com Neetru\" que chama\r\n * `client.auth.signIn()` no click (mesmo visual BMW do botão preto\r\n * \"Entrar com Google\" de `auth.neetru.com/oauth/login`, com o ícone\r\n * Neetru no lugar do ícone do provider).\r\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\r\n * (suporte a `mode='readonly'` pro decisão CEO §5).\r\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\r\n *\r\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\r\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\r\n * puxam React mesmo via transitive.\r\n */\r\nimport * as React from 'react';\r\nimport type { NeetruClient, NeetruUser, AuthNamespace, SignInOptions } from './types';\r\n\r\n// ─── Barra de conta / app-switcher (drop-in padrão Neetru) ───────────────────\r\n// Autorado em `./account-bar` pra manter este arquivo enxuto; reexportado aqui\r\n// pra ficar sob o subpath canônico `@neetru/sdk/react`.\r\nexport {\r\n NeetruAccountBar,\r\n fetchNeetruAccountBar,\r\n initialsFrom,\r\n type NeetruAccountBarProps,\r\n type AccountBarData,\r\n type AccountBarAccount,\r\n type AccountBarProductItem,\r\n type AccountBarLabels,\r\n} from './account-bar';\r\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\r\nimport type {\r\n CollectionReference,\r\n Query,\r\n DocumentReference,\r\n} from './firestore-compat';\r\nimport { toDbQuery } from './firestore-compat';\r\nimport type { DbListResult, DbGetResult } from './db/client-db';\r\n\r\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\r\n\r\nexport interface CheckoutLinkProps\r\n extends Omit<\r\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\r\n 'href' | 'onClick' | 'onError'\r\n > {\r\n /**\r\n * Cliente SDK Neetru. Passado explicitamente pra evitar context provider.\r\n *\r\n * **Opcional** quando `startPath` é usado (padrão BFF/cookie recomendado): o\r\n * browser não tem token nem client, o start acontece no route BFF do app.\r\n * Obrigatório no modo legado (client-side start), quando o browser tem o token.\r\n */\r\n client?: NeetruClient;\r\n /**\r\n * **BFF/cookie (padrão recomendado):** path do route do PRÓPRIO app que inicia\r\n * o checkout server-side (ex: `/api/checkout/start`). Quando presente, o clique\r\n * faz `POST` nesse path (same-origin, `credentials: 'include'` → manda o cookie\r\n * httpOnly de sessão), o route lê o id_token do cookie e chama o Core\r\n * server-side, devolvendo `{ redirectUrl }`. Assim o **browser nunca precisa do\r\n * access_token** — que no padrão BFF é descartado no servidor.\r\n *\r\n * A resposta esperada do route BFF é `{ redirectUrl, intentId?, status?,\r\n * expiresAt?, requiresKyc? }` (ou `{ intent: { redirectUrl, ... } }`). Sem\r\n * `startPath`, o componente cai no modo legado `client.checkout.start()`.\r\n */\r\n startPath?: string;\r\n /** Slug do produto (ex: `neetru-pulse`). */\r\n productId: string;\r\n /** Plan id (ex: `pro_monthly`). */\r\n planId: string;\r\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\r\n callbackUrl: string;\r\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\r\n tenantType?: 'pf' | 'pj';\r\n tenantId?: string;\r\n /** Children — texto/JSX do CTA. */\r\n children: React.ReactNode;\r\n /** Callback quando intent foi criada (antes de redirect). */\r\n onIntentCreated?: (result: CheckoutStartResult) => void;\r\n /** Callback de erro. */\r\n onError?: (err: Error) => void;\r\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\r\n noAutoRedirect?: boolean;\r\n /** Override de href (default: `#`). */\r\n href?: string;\r\n}\r\n\r\n/**\r\n * Faz o start de checkout via route BFF do próprio app (padrão recomendado).\r\n * `POST` same-origin com `credentials: 'include'` — o cookie httpOnly de sessão\r\n * vai junto, e o route BFF lê o id_token dele e chama o Core server-side. O\r\n * browser nunca vê token. Espera `{ redirectUrl, ... }` (ou `{ intent: {...} }`).\r\n */\r\nasync function startCheckoutViaBff(\r\n startPath: string,\r\n input: {\r\n productId: string;\r\n planId: string;\r\n callbackUrl: string;\r\n tenantType?: 'pf' | 'pj';\r\n tenantId?: string;\r\n },\r\n): Promise<CheckoutStartResult> {\r\n const res = await fetch(startPath, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json', accept: 'application/json' },\r\n credentials: 'include',\r\n body: JSON.stringify(input),\r\n });\r\n let payload: Record<string, unknown> | null = null;\r\n try {\r\n payload = (await res.json()) as Record<string, unknown>;\r\n } catch {\r\n payload = null;\r\n }\r\n if (!res.ok) {\r\n const p = payload as\r\n | { error?: { message?: string } | string; message?: string }\r\n | null;\r\n const msg =\r\n (p &&\r\n ((typeof p.error === 'object' && p.error?.message) ||\r\n (typeof p.error === 'string' && p.error) ||\r\n (typeof p.message === 'string' && p.message))) ||\r\n `Checkout BFF respondeu HTTP ${res.status}`;\r\n throw new Error(typeof msg === 'string' ? msg : `Checkout BFF respondeu HTTP ${res.status}`);\r\n }\r\n const intent = ((payload?.intent as Record<string, unknown> | undefined) ?? payload) ?? {};\r\n const redirectUrl = (payload?.redirectUrl ?? intent.redirectUrl) as string | undefined;\r\n if (!redirectUrl || typeof redirectUrl !== 'string') {\r\n throw new Error('Resposta do route BFF de checkout sem `redirectUrl`.');\r\n }\r\n return {\r\n intentId: String(payload?.intentId ?? intent.intentId ?? ''),\r\n redirectUrl,\r\n status: (payload?.status ?? intent.status ?? 'pending') as CheckoutStartResult['status'],\r\n expiresAt:\r\n typeof payload?.expiresAt === 'string'\r\n ? (payload.expiresAt as string)\r\n : typeof intent.expiresAt === 'string'\r\n ? (intent.expiresAt as string)\r\n : '',\r\n requiresKyc: payload?.requiresKyc === true || intent.requiresKyc === true,\r\n };\r\n}\r\n\r\n/** Redireciona o browser pra `url`, best-effort (no-op fora do browser). */\r\nfunction checkoutRedirect(url: string): void {\r\n const loc = (globalThis as { location?: { assign?: (u: string) => void } }).location;\r\n if (loc && typeof loc.assign === 'function') loc.assign(url);\r\n}\r\n\r\n/**\r\n * Wrapper de `<a>` que inicia o checkout no click.\r\n *\r\n * Dois modos:\r\n *\r\n * **1. BFF/cookie (recomendado)** — passe `startPath`. O browser NÃO precisa de\r\n * token; o clique faz `POST` no route do seu app, que inicia o checkout\r\n * server-side com o id_token do cookie httpOnly:\r\n * ```tsx\r\n * <CheckoutLink\r\n * startPath=\"/api/checkout/start\"\r\n * productId=\"meu-saas\"\r\n * planId=\"pro_monthly\"\r\n * callbackUrl=\"https://meu-saas.com/billing/success\"\r\n * >\r\n * Assinar Pro\r\n * </CheckoutLink>\r\n * ```\r\n *\r\n * **2. Legado (client-side)** — passe `client`. Só funciona se o browser tiver o\r\n * token (SPA não-BFF que guarda o access_token no browser). Chama\r\n * `client.checkout.start()` direto:\r\n * ```tsx\r\n * <CheckoutLink client={client} productId=\"meu-saas\" planId=\"pro_monthly\"\r\n * callbackUrl=\"https://meu-saas.com/billing/success\">Assinar Pro</CheckoutLink>\r\n * ```\r\n *\r\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\r\n * de URL no hover (pode ser sobrescrito via prop spread).\r\n */\r\nexport function CheckoutLink({\r\n client,\r\n startPath,\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n children,\r\n onIntentCreated,\r\n onError,\r\n noAutoRedirect,\r\n ...rest\r\n}: CheckoutLinkProps): React.ReactElement {\r\n const [submitting, setSubmitting] = React.useState(false);\r\n\r\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\r\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\r\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } } | undefined)?.checkout;\r\n\r\n const handleClick = React.useCallback(\r\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\r\n event.preventDefault();\r\n if (submitting) return;\r\n\r\n // Modo BFF/cookie (recomendado): sem token no browser. POST no route do app.\r\n if (startPath) {\r\n setSubmitting(true);\r\n try {\r\n const result = await startCheckoutViaBff(startPath, {\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n });\r\n onIntentCreated?.(result);\r\n if (!noAutoRedirect) checkoutRedirect(result.redirectUrl);\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n return;\r\n }\r\n\r\n // Modo legado (client-side start): o browser precisa ter o token.\r\n if (!checkout || typeof checkout.start !== 'function') {\r\n onError?.(\r\n new Error(\r\n 'CheckoutLink: passe `startPath` (padrão BFF/cookie recomendado) OU um `client` com o namespace `checkout` (SDK 1.1+).',\r\n ),\r\n );\r\n return;\r\n }\r\n setSubmitting(true);\r\n try {\r\n const result = await checkout.start({\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n autoRedirect: !noAutoRedirect,\r\n });\r\n onIntentCreated?.(result);\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n },\r\n [submitting, checkout, startPath, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\r\n );\r\n\r\n return React.createElement(\r\n 'a',\r\n {\r\n ...rest,\r\n href: rest.href ?? '#',\r\n onClick: handleClick,\r\n 'aria-busy': submitting || undefined,\r\n 'data-neetru-checkout-link': 'true',\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ─── <NeetruLoginButton> ────────────────────────────────────────────────────\r\n\r\n/**\r\n * Path do ícone-chama Neetru (mesmo asset de `src/components/logo.tsx` no\r\n * Core — `variant=\"mark\"`, viewBox 527×760). Preto/branco conforme o tema do\r\n * botão, igual ao ícone colorido do Google no botão de referência.\r\n */\r\nconst NEETRU_FLAME_PATH =\r\n 'M 281 5.163 C 240.659 26.308, 211.260 52.739, 187.750 89 C 177.297 105.123, 167.942 126.107, 162.988 144.543 C 154.486 176.188, 153.303 204.118, 159.101 236.317 C 161.553 249.934, 164.601 259.065, 171.539 273.568 C 179.323 289.843, 188.311 302.619, 205.856 322.343 C 216.993 334.865, 218.719 337.460, 223.536 348.944 C 233.797 373.403, 237.419 401.984, 232.417 419.032 C 229.111 430.300, 219.859 441.975, 210.427 446.779 C 186.006 459.218, 155.612 448.383, 142.319 422.500 C 128.813 396.204, 123.953 356.753, 129.495 318.395 C 131.172 306.786, 138.276 276.222, 140.080 272.850 C 140.675 271.738, 140.944 270.610, 140.676 270.343 C 139.995 269.662, 121.004 281.262, 112 287.859 C 100.565 296.237, 95.542 300.566, 83.993 312 C 44.470 351.127, 19.275 398.763, 8.623 454.500 C 4.302 477.115, 3.308 513.551, 6.332 538.569 C 9.164 562.011, 17.855 591.852, 27.324 610.648 C 37.693 631.233, 54.130 653.987, 71.900 672.357 C 80.540 681.289, 98.477 696.992, 104.478 700.878 C 106.690 702.310, 108.725 703.766, 109 704.114 C 109.836 705.172, 125.939 715.307, 135.539 720.817 C 152.923 730.795, 169.097 737.316, 200.500 747.009 C 204.454 748.230, 222.087 751.463, 234 753.153 C 246.383 754.909, 283.484 754.921, 296 753.173 C 310.006 751.217, 321.722 749.122, 328.500 747.360 C 384.024 732.928, 433.393 701.075, 469.555 656.351 C 491.448 629.275, 505.223 601.823, 514.391 567 C 519.380 548.047, 520.999 536.377, 521.686 514.410 C 523.126 468.370, 514.560 426.479, 494.812 383 C 471.567 331.818, 436.019 282.480, 379.951 223.582 C 354.302 196.638, 349.024 190.981, 341.670 182.551 C 318.918 156.471, 301.763 128.316, 293.505 103.500 C 286.625 82.826, 285.662 76.446, 285.578 51 C 285.500 27.576, 285.510 27.460, 288.750 15.129 C 290.538 8.326, 292 2.363, 292 1.879 C 292 0.197, 288.454 1.255, 281 5.163';\r\nconst NEETRU_FLAME_VIEWBOX = '0 0 527 760';\r\nconst NEETRU_FLAME_ASPECT = 527 / 760;\r\n\r\nfunction NeetruFlameIcon({ fill, size = 16 }: { fill: string; size?: number }): React.ReactElement {\r\n return React.createElement(\r\n 'svg',\r\n {\r\n viewBox: NEETRU_FLAME_VIEWBOX,\r\n width: Math.round(size * NEETRU_FLAME_ASPECT),\r\n height: size,\r\n 'aria-hidden': true,\r\n style: { flexShrink: 0 },\r\n },\r\n React.createElement('path', { d: NEETRU_FLAME_PATH, fill }),\r\n );\r\n}\r\n\r\n/** Paleta BMW do botão — mesmos valores do \"Entrar com Google\" de `auth.neetru.com/oauth/login` (src/app/oauth/login/page.tsx). */\r\nconst NEETRU_LOGIN_BUTTON_THEME: Record<\r\n 'dark' | 'light',\r\n { bg: string; bgHover: string; fg: string; border: string }\r\n> = {\r\n dark: { bg: '#0a0a0a', bgHover: '#1a1a1a', fg: '#ffffff', border: '#0a0a0a' },\r\n light: { bg: '#ffffff', bgHover: '#f5f5f5', fg: '#0a0a0a', border: '#0a0a0a' },\r\n};\r\n\r\nexport interface NeetruLoginButtonProps\r\n extends Omit<\r\n React.ButtonHTMLAttributes<HTMLButtonElement>,\r\n 'onClick' | 'onError' | 'children' | 'style'\r\n > {\r\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\r\n client: NeetruClient;\r\n /** OIDC redirect_uri override — repassado a `client.auth.signIn`. Default: origin atual. */\r\n redirectUri?: string;\r\n /** OIDC scope — repassado a `client.auth.signIn`. Default `openid profile email`. */\r\n scope?: string;\r\n /** Pra onde mandar após login completo — repassado a `client.auth.signIn`. */\r\n postLoginRedirect?: string;\r\n /** Variante visual — `dark` (preto, default/primário) ou `light` (branco, secundário/sobre fundo escuro). */\r\n theme?: 'dark' | 'light';\r\n /** Texto do botão. Default `\"Entrar com Neetru\"`. */\r\n label?: string;\r\n /** Callback de erro — `signIn()` lançou antes do redirect (ex: sessionStorage indisponível, WebCrypto ausente, oidcClientId não configurado). Em browser real o caminho feliz NUNCA chama isto: a navegação interrompe o JS. */\r\n onError?: (err: Error) => void;\r\n /** Override de estilo inline — mesclado por cima do padrão BMW (não substitui). */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n/**\r\n * Botão padronizado \"Entrar com Neetru\" — dispara `client.auth.signIn()` no\r\n * click (redirect OIDC PKCE pra `auth.neetru.com/authorize`). Visual BMW\r\n * idêntico ao botão preto \"Entrar com Google\" que `auth.neetru.com` usa hoje\r\n * (mesma altura, tipografia, tracking, rounded-none), com o ícone-chama\r\n * Neetru no lugar do ícone do provider — é O botão padrão que qualquer\r\n * produto SaaS Neetru deveria usar na própria tela de login, em vez de cada\r\n * um inventar o próprio estilo.\r\n *\r\n * Estilos são inline (não Tailwind) de propósito: o componente roda em\r\n * QUALQUER app consumidor do SDK, com ou sem Tailwind configurado — precisa\r\n * renderizar idêntico em todo lugar, não só nos produtos que por acaso usam\r\n * o mesmo setup de CSS do Core.\r\n *\r\n * Uso típico:\r\n * ```tsx\r\n * <NeetruLoginButton client={client} postLoginRedirect=\"/dashboard\" />\r\n * ```\r\n */\r\nexport function NeetruLoginButton({\r\n client,\r\n redirectUri,\r\n scope,\r\n postLoginRedirect,\r\n theme = 'dark',\r\n label = 'Entrar com Neetru',\r\n onError,\r\n style,\r\n disabled,\r\n ...rest\r\n}: NeetruLoginButtonProps): React.ReactElement {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n const [isHover, setIsHover] = React.useState(false);\r\n const colors = NEETRU_LOGIN_BUTTON_THEME[theme];\r\n const isDisabled = Boolean(disabled) || isLoading;\r\n\r\n const handleClick = React.useCallback(async () => {\r\n if (isDisabled) return;\r\n setIsLoading(true);\r\n try {\r\n const options: SignInOptions = { redirectUri, scope, postLoginRedirect };\r\n await client.auth.signIn(options);\r\n // Bug achado por verificação end-to-end (2026-07-24): signIn() real\r\n // faz location.assign() e RETORNA em seguida (não espera a navegação\r\n // acontecer) — a suposição antiga de que a navegação \"interrompe a\r\n // execução\" era falsa, e em modo mock (NEETRU_ENV=dev) não há\r\n // navegação nenhuma. Sem o reset abaixo, o botão travava em\r\n // \"Entrando…\"/disabled para sempre depois do 1º signIn bem-sucedido.\r\n // No browser real, o reset é inofensivo: a navegação já está em voo.\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, [client, redirectUri, scope, postLoginRedirect, isDisabled, onError]);\r\n\r\n return React.createElement(\r\n 'button',\r\n {\r\n ...rest,\r\n type: 'button',\r\n onClick: handleClick,\r\n disabled: isDisabled,\r\n onMouseEnter: () => setIsHover(true),\r\n onMouseLeave: () => setIsHover(false),\r\n 'aria-label': rest['aria-label'] ?? label,\r\n 'aria-busy': isLoading || undefined,\r\n 'data-neetru-login-button': 'true',\r\n style: {\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: '12px',\r\n width: '100%',\r\n height: '48px',\r\n padding: '0 20px',\r\n borderRadius: 0,\r\n border: `1px solid ${colors.border}`,\r\n backgroundColor: isHover && !isDisabled ? colors.bgHover : colors.bg,\r\n color: colors.fg,\r\n fontSize: '14px',\r\n fontWeight: 700,\r\n textTransform: 'uppercase',\r\n letterSpacing: '0.08em',\r\n fontFamily: 'inherit',\r\n lineHeight: 1,\r\n cursor: isDisabled ? 'not-allowed' : 'pointer',\r\n opacity: isDisabled && !isLoading ? 0.6 : 1,\r\n transition: 'background-color 150ms ease',\r\n boxSizing: 'border-box',\r\n ...style,\r\n },\r\n },\r\n React.createElement(NeetruFlameIcon, { fill: colors.fg }),\r\n React.createElement('span', null, isLoading ? 'Entrando…' : label),\r\n );\r\n}\r\n\r\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\r\n\r\n/** Modo de gate quando entitlement não é permitido. */\r\nexport type EntitlementGateMode = 'block' | 'readonly';\r\n\r\ninterface EntitlementContextValue {\r\n /** True quando feature está em modo read-only (limit exceeded). */\r\n readonly: boolean;\r\n /** Reason code da última check. */\r\n reason?: string;\r\n /** Limite atual (free tier). */\r\n limit?: number;\r\n /** Uso atual. */\r\n currentUsage?: number;\r\n}\r\n\r\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\r\n readonly: false,\r\n});\r\n\r\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\r\n * desabilitar inputs/botões de escrita. */\r\nexport function useEntitlementContext(): EntitlementContextValue {\r\n return React.useContext(EntitlementContext);\r\n}\r\n\r\nexport interface EntitlementGateProps {\r\n /** Cliente SDK Neetru. */\r\n client: NeetruClient;\r\n /** Feature key a checar (ex: `export.csv`). */\r\n feature: string;\r\n /** Slug do produto. Default: lê do client.config se ausente. */\r\n productSlug?: string;\r\n /** Modo:\r\n * - `block` (default): renderiza fallback quando não permitido.\r\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\r\n mode?: EntitlementGateMode;\r\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\r\n fallback?: React.ReactNode;\r\n /** Loading UI enquanto check resolve (Suspense-like). */\r\n loading?: React.ReactNode;\r\n children: React.ReactNode;\r\n}\r\n\r\ninterface EntitlementCheckResult {\r\n allowed: boolean;\r\n reason?: string;\r\n limit?: number;\r\n currentUsage?: number;\r\n behavior?: 'readonly' | null;\r\n}\r\n\r\n/**\r\n * Wrapper que gateia children por entitlement check.\r\n *\r\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\r\n * - Children renderizam normalmente.\r\n * - Context injeta `{readonly: true}`.\r\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\r\n * escritas (botões, forms, inputs).\r\n *\r\n * Quando `mode='block'` + check retorna `allowed=false`:\r\n * - `fallback` é renderizado (ex: paywall, upsell modal).\r\n *\r\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\r\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\r\n */\r\nexport function EntitlementGate({\r\n client,\r\n feature,\r\n productSlug,\r\n mode = 'block',\r\n fallback,\r\n loading,\r\n children,\r\n}: EntitlementGateProps): React.ReactElement {\r\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\r\n status: 'loading',\r\n });\r\n\r\n // Resolve productSlug from prop or client.config\r\n const resolvedSlug =\r\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\r\n\r\n // tenantId fallback: client.config.tenantId (default no createNeetruClient).\r\n // usage.check valida ambos productId + tenantId — sem isso, lança\r\n // validation_failed e o Gate fica trancado em allowed:false.\r\n const resolvedTenantId = (client.config as { tenantId?: string }).tenantId;\r\n\r\n React.useEffect(() => {\r\n let cancelled = false;\r\n const run = async () => {\r\n try {\r\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\r\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\r\n let result: EntitlementCheckResult;\r\n const usage = (client as {\r\n usage?: {\r\n check: (\r\n resource: string,\r\n opts?: { productId?: string; tenantId?: string },\r\n ) => Promise<EntitlementCheckResult>;\r\n };\r\n }).usage;\r\n if (usage && typeof usage.check === 'function' && resolvedTenantId) {\r\n const res = await usage.check(feature, {\r\n productId: resolvedSlug,\r\n tenantId: resolvedTenantId,\r\n });\r\n result = {\r\n allowed: res.allowed === true,\r\n reason: res.reason,\r\n limit: res.limit,\r\n currentUsage: res.currentUsage,\r\n behavior: res.behavior,\r\n };\r\n } else {\r\n // 2026-09: client.entitlements.check() foi DEPRECADO (apontava pra\r\n // endpoint morto no Core que sempre retornava not_subscribed — ver\r\n // src/entitlements.ts) e agora LANÇA synchronous. Sem tenantId não\r\n // há como verificar o entitlement real (usage.check exige\r\n // productId+tenantId) — fail-CLOSED explícito em vez de depender do\r\n // fallback morto (que, se chamado, cairia no catch abaixo e\r\n // fail-abriria: risco de vazar feature paga sem checar nada).\r\n // Observável idêntico ao comportamento de produção anterior — o\r\n // endpoint legado sempre devolvia not_subscribed (allowed:false)\r\n // de qualquer forma, então nenhum caller real perde acesso aqui.\r\n if (typeof console !== 'undefined') {\r\n console.warn(\r\n '[EntitlementGate] tenantId ausente em client.config — não é possível verificar entitlement ' +\r\n '(client.entitlements.check() está deprecado, ver src/entitlements.ts). Configure tenantId ' +\r\n 'no createNeetruClient() ou passe via props. Bloqueando por padrão (fail-closed).',\r\n );\r\n }\r\n result = { allowed: false, reason: 'tenant_id_required' };\r\n }\r\n if (!cancelled) setState({ status: 'ready', result });\r\n } catch (err) {\r\n // Fail open: erros de rede/timeout não bloqueiam o usuário com plano válido.\r\n // Bug corrigido: antes setava allowed:false (fail-closed) contradizendo o comentário.\r\n if (!cancelled) {\r\n setState({ status: 'ready', result: { allowed: true, reason: 'check_error_fallback' } });\r\n }\r\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed — fail-open', err);\r\n }\r\n };\r\n run();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [client, feature, resolvedSlug, resolvedTenantId]);\r\n\r\n if (state.status === 'loading') {\r\n return React.createElement(React.Fragment, null, loading ?? null);\r\n }\r\n\r\n const result = state.result;\r\n const allowed = result?.allowed === true;\r\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\r\n\r\n if (!allowed && mode === 'block' && !isReadonly) {\r\n return React.createElement(React.Fragment, null, fallback ?? null);\r\n }\r\n\r\n return React.createElement(\r\n EntitlementContext.Provider,\r\n {\r\n value: {\r\n readonly: isReadonly,\r\n reason: result?.reason,\r\n limit: result?.limit,\r\n currentUsage: result?.currentUsage,\r\n },\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ════════════════════════════════════════════════════════════════════════════\r\n// SDK-2 — Hooks de dados de 1ª classe (compat com hooks caseiros por-produto)\r\n//\r\n// ⚠️ STATUS: 1ª classe, mas desenhados para CASAR com a superfície que os\r\n// produtos (gestovendas / pdvagiliza) escreveram à mão em cima do\r\n// firestore-compat. Substituem os hooks `useCollection`/`useDoc`/`useUser`\r\n// que cada produto reescrevia (~200-400 LOC por produto).\r\n//\r\n// Estes hooks aceitam **refs do `@neetru/sdk/firestore-compat`**\r\n// (`CollectionReference` / `Query` / `DocumentReference`) e devolvem o shape\r\n// `{ data, isLoading, error, isStale, lastUpdated }` que os produtos já\r\n// consomem. Internamente usam o realtime nativo do `client.db`\r\n// (`onSnapshot`) quando disponível, com fallback de polling visibility-aware.\r\n//\r\n// Diferença vs `@neetru/sdk/db/react` (`useDocument`/`useCollection` de lá):\r\n// - Lá: API offline-first, ref = `DbCollectionRef` nativo, retorno\r\n// `{ docs, loading, fromCache, stale }`.\r\n// - Aqui: API firestore-compat, ref = ref do firestore-compat, retorno\r\n// `{ data, isLoading, error, isStale, lastUpdated }` (id já embutido em\r\n// cada item; data nunca duplica o id internamente).\r\n// ════════════════════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resultado de `useCollection` (subpath `@neetru/sdk/react`, API firestore-compat).\r\n *\r\n * Nome distinto de `UseCollectionResult` do subpath `@neetru/sdk/db/react`:\r\n * - `src/db/react.ts` → `{ docs, loading, fromCache, stale, hasPendingWrites }`\r\n * - `src/react.ts` → `{ data, isLoading, error, isStale, lastUpdated }` (este)\r\n *\r\n * Consumers que importam de ambos subpaths não terão clash de tipo.\r\n * Alias de retrocompat `UseCollectionResult` exportado abaixo.\r\n */\r\nexport interface FirestoreCompatCollectionResult<T> {\r\n /** Documentos (cada um com `id` embutido) ou `null` enquanto carrega/sem ref. */\r\n data: Array<T & { id: string }> | null;\r\n /** `true` enquanto o primeiro snapshot não chegou. */\r\n isLoading: boolean;\r\n /** Erro do último refresh (dados exibidos podem estar defasados). */\r\n error: Error | null;\r\n /** `true` quando um refresh falhou ou o snapshot veio do cache/offline. */\r\n isStale: boolean;\r\n /** epoch ms do último snapshot bem-sucedido, ou `null`. */\r\n lastUpdated: number | null;\r\n}\r\n\r\n/**\r\n * Alias de retrocompat — exportado para não quebrar produtos que importam\r\n * `UseCollectionResult` do subpath `@neetru/sdk/react`. Prefira\r\n * `FirestoreCompatCollectionResult` em código novo para evitar o clash de nome\r\n * com `UseCollectionResult` de `@neetru/sdk/db/react`.\r\n *\r\n * @deprecated Use `FirestoreCompatCollectionResult` em novo código.\r\n */\r\nexport type UseCollectionResult<T> = FirestoreCompatCollectionResult<T>;\r\n\r\n/** Resultado de `useDoc`. */\r\nexport interface UseDocResult<T> {\r\n data: (T & { id: string }) | null;\r\n isLoading: boolean;\r\n error: Error | null;\r\n isStale: boolean;\r\n lastUpdated: number | null;\r\n}\r\n\r\n/** Opções comuns dos hooks de dados. */\r\nexport interface UseDataOptions {\r\n /**\r\n * Intervalo de polling de fallback em ms (quando o transporte não oferece\r\n * realtime nativo). Default 5000. O polling pausa quando a aba está oculta.\r\n */\r\n intervalMs?: number;\r\n /**\r\n * Força polling mesmo quando o `onSnapshot` nativo está disponível. Útil para\r\n * transportes REST onde `onSnapshot` só entrega o snapshot inicial.\r\n * Default: `false` (usa realtime quando o transporte oferece).\r\n */\r\n poll?: boolean;\r\n}\r\n\r\nconst DEFAULT_INTERVAL_MS = 5000;\r\n\r\n/** `true` quando a aba está oculta (não dispara polling em background). */\r\nfunction isDocumentHidden(): boolean {\r\n return typeof document !== 'undefined' && document.hidden === true;\r\n}\r\n\r\n/**\r\n * Serializa uma fonte de coleção/query firestore-compat numa chave estável.\r\n * Garante que mudanças de VALOR (não de identidade) re-subscrevam, mas objetos\r\n * iguais não causem loop de efeito.\r\n */\r\nfunction serializeSource(\r\n source: CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n): string | null {\r\n if (!source) return null;\r\n if (source.__kind === 'collection') {\r\n return `collection:${source.id}`;\r\n }\r\n const dbQuery = toDbQuery(source.constraints);\r\n return `query:${source.collection}:${JSON.stringify(dbQuery)}`;\r\n}\r\n\r\n/** Normaliza linhas de um snapshot do client.db em `{...data, id}` (id embutido). */\r\nfunction rowsFromList<T>(snap: DbListResult<Record<string, unknown>>): Array<T & { id: string }> {\r\n return snap.docs.map((d) => {\r\n const data = d.data as Record<string, unknown>;\r\n // Garante que o id externo prevaleça (sem duplicar dentro do objeto data).\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id: d.id };\r\n });\r\n}\r\n\r\nfunction rowFromGet<T>(\r\n snap: DbGetResult<Record<string, unknown>> | null,\r\n id: string,\r\n): (T & { id: string }) | null {\r\n const first = snap?.docs[0];\r\n if (!first) return null;\r\n const data = first.data as Record<string, unknown>;\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id };\r\n}\r\n\r\n/**\r\n * Hook de tempo real para uma coleção (ou subconjunto filtrado via `query`).\r\n *\r\n * Aceita uma `CollectionReference` ou `Query` do `@neetru/sdk/firestore-compat`.\r\n * Subscreve ao `onSnapshot` nativo do `client.db`; quando o transporte é REST\r\n * (sem realtime nativo), faz polling visibility-aware no `intervalMs`.\r\n *\r\n * ```tsx\r\n * import { collection, query, where } from '@neetru/sdk/firestore-compat';\r\n * import { useCollection } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(\r\n * () => query(collection(client.db, 'orders'), where('status', '==', 'open')),\r\n * [],\r\n * );\r\n * const { data, isLoading, error, isStale } = useCollection<Order>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useCollection<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseCollectionResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const sourceKey = serializeSource(\r\n source as CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n );\r\n\r\n const [state, setState] = React.useState<UseCollectionResult<T>>({\r\n data: null,\r\n isLoading: sourceKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n // Mantém a fonte mais recente sem re-disparar o efeito por identidade.\r\n const sourceRef = React.useRef(source);\r\n sourceRef.current = source;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = sourceRef.current;\r\n\r\n if (!current || sourceKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const collectionName = current.__kind === 'query' ? current.collection : current.id;\r\n const dbQuery = current.__kind === 'query' ? toDbQuery(current.constraints) : undefined;\r\n const col = current.db.collection(collectionName);\r\n\r\n const applySnap = (snap: DbListResult<Record<string, unknown>>) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowsFromList<T>(snap),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n // Mantém o último data bom — não limpa em erro de refresh.\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n // Polling explícito (REST) ou realtime nativo.\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await col.list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = col.onSnapshot(dbQuery, applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [sourceKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * Hook de tempo real para um documento.\r\n *\r\n * Aceita uma `DocumentReference` do `@neetru/sdk/firestore-compat`.\r\n *\r\n * ```tsx\r\n * import { doc } from '@neetru/sdk/firestore-compat';\r\n * import { useDoc } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(() => doc(client.db, 'profiles', uid), [uid]);\r\n * const { data, isLoading } = useDoc<Profile>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseDocResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const refKey = ref && ref.id ? `${ref.collection}/${ref.id}` : null;\r\n\r\n const [state, setState] = React.useState<UseDocResult<T>>({\r\n data: null,\r\n isLoading: refKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n const refRef = React.useRef(ref);\r\n refRef.current = ref;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = refRef.current;\r\n\r\n if (!current || refKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const docRef = current.db.collection(current.collection).doc(current.id);\r\n const id = current.id;\r\n\r\n const applySnap = (snap: DbGetResult<Record<string, unknown>> | null) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowFromGet<T>(snap, id),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap?.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await docRef.get()) as DbGetResult<Record<string, unknown>> | null;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = docRef.onSnapshot(applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [refKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n// ─── useUser ────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Fonte de auth aceita por `useUser`: o `NeetruClient` inteiro (resolve\r\n * `client.auth`) ou o `AuthNamespace` direto.\r\n */\r\nexport type UseUserInput = NeetruClient | { auth: AuthNamespace } | AuthNamespace;\r\n\r\n/** Resultado de `useUser`. */\r\nexport interface UseUserResult {\r\n /** Usuário autenticado ou `null`. */\r\n user: NeetruUser | null;\r\n /** `true` enquanto o estado de auth inicial não foi resolvido. */\r\n isLoading: boolean;\r\n /** Erro na resolução de auth (raro — listener nunca deveria lançar). */\r\n error: Error | null;\r\n}\r\n\r\nfunction resolveAuth(input: UseUserInput): AuthNamespace {\r\n const maybe = input as { auth?: AuthNamespace };\r\n if (maybe.auth && typeof maybe.auth.onAuthStateChanged === 'function') {\r\n return maybe.auth;\r\n }\r\n return input as AuthNamespace;\r\n}\r\n\r\n/**\r\n * Hook de estado de autenticação.\r\n *\r\n * Subscreve a `client.auth.onAuthStateChanged` e devolve `{ user, isLoading,\r\n * error }`. Inclui um **timeout de seed** (default 8s): se o estado de auth não\r\n * resolver nesse prazo, `isLoading` cai para `false` com `user: null` — evita\r\n * spinners infinitos quando o provedor de auth nunca dispara o callback.\r\n *\r\n * ```tsx\r\n * import { useUser } from '@neetru/sdk/react';\r\n * const { user, isLoading } = useUser(client);\r\n * if (isLoading) return <Spinner />;\r\n * if (!user) return <SignInButton />;\r\n * return <Avatar user={user} />;\r\n * ```\r\n *\r\n * Aceita o `NeetruClient`, `{ auth }` ou o `AuthNamespace` direto.\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useUser(\r\n input: UseUserInput,\r\n options?: { timeoutMs?: number },\r\n): UseUserResult {\r\n const timeoutMs = options?.timeoutMs ?? 8000;\r\n const auth = resolveAuth(input);\r\n\r\n const [state, setState] = React.useState<UseUserResult>(() => {\r\n // Seed síncrono do estado atual (se o namespace já tem user em cache).\r\n let seeded: NeetruUser | null = null;\r\n try {\r\n seeded = auth.getUser();\r\n } catch {\r\n seeded = null;\r\n }\r\n return { user: seeded, isLoading: seeded === null, error: null };\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n let settled = false;\r\n\r\n // Timeout de seed: se onAuthStateChanged nunca disparar, destrava o loading.\r\n const timer = setTimeout(() => {\r\n if (!mountedRef.current || settled) return;\r\n setState((prev) => (prev.isLoading ? { ...prev, isLoading: false } : prev));\r\n }, timeoutMs);\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = auth.onAuthStateChanged((user) => {\r\n if (!mountedRef.current) return;\r\n settled = true;\r\n setState({ user: user ?? null, isLoading: false, error: null });\r\n });\r\n } catch (e) {\r\n if (mountedRef.current) {\r\n setState({\r\n user: null,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n }\r\n }\r\n\r\n return () => {\r\n mountedRef.current = false;\r\n clearTimeout(timer);\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [auth, timeoutMs]);\r\n\r\n return state;\r\n}\r\n"]}