@neetru/sdk 3.4.7 → 3.6.0

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 (94) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/ai.cjs +17 -13
  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 +17 -13
  7. package/dist/ai.mjs.map +1 -1
  8. package/dist/auth.cjs +159 -107
  9. package/dist/auth.cjs.map +1 -1
  10. package/dist/auth.d.cts +2 -2
  11. package/dist/auth.d.ts +2 -2
  12. package/dist/auth.mjs +159 -107
  13. package/dist/auth.mjs.map +1 -1
  14. package/dist/catalog.cjs +19 -4
  15. package/dist/catalog.cjs.map +1 -1
  16. package/dist/catalog.d.cts +1 -1
  17. package/dist/catalog.d.ts +1 -1
  18. package/dist/catalog.mjs +19 -4
  19. package/dist/catalog.mjs.map +1 -1
  20. package/dist/checkout.cjs +11 -3
  21. package/dist/checkout.cjs.map +1 -1
  22. package/dist/checkout.d.cts +1 -1
  23. package/dist/checkout.d.ts +1 -1
  24. package/dist/checkout.mjs +11 -3
  25. package/dist/checkout.mjs.map +1 -1
  26. package/dist/db-react.d.cts +1 -1
  27. package/dist/db-react.d.ts +1 -1
  28. package/dist/db.cjs +17 -7
  29. package/dist/db.cjs.map +1 -1
  30. package/dist/db.d.cts +1 -1
  31. package/dist/db.d.ts +1 -1
  32. package/dist/db.mjs +17 -7
  33. package/dist/db.mjs.map +1 -1
  34. package/dist/entitlements.d.cts +1 -1
  35. package/dist/entitlements.d.ts +1 -1
  36. package/dist/firestore-compat.d.cts +1 -1
  37. package/dist/firestore-compat.d.ts +1 -1
  38. package/dist/index.cjs +160 -108
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +30 -2
  41. package/dist/index.d.ts +30 -2
  42. package/dist/index.mjs +160 -108
  43. package/dist/index.mjs.map +1 -1
  44. package/dist/mobile.cjs +515 -0
  45. package/dist/mobile.cjs.map +1 -0
  46. package/dist/mobile.d.cts +89 -0
  47. package/dist/mobile.d.ts +89 -0
  48. package/dist/mobile.mjs +513 -0
  49. package/dist/mobile.mjs.map +1 -0
  50. package/dist/mocks.cjs +17 -0
  51. package/dist/mocks.cjs.map +1 -1
  52. package/dist/mocks.d.cts +5 -1
  53. package/dist/mocks.d.ts +5 -1
  54. package/dist/mocks.mjs +17 -0
  55. package/dist/mocks.mjs.map +1 -1
  56. package/dist/notifications.cjs +11 -3
  57. package/dist/notifications.cjs.map +1 -1
  58. package/dist/notifications.d.cts +1 -1
  59. package/dist/notifications.d.ts +1 -1
  60. package/dist/notifications.mjs +11 -3
  61. package/dist/notifications.mjs.map +1 -1
  62. package/dist/react.cjs +36 -2
  63. package/dist/react.cjs.map +1 -1
  64. package/dist/react.d.cts +1 -1
  65. package/dist/react.d.ts +1 -1
  66. package/dist/react.mjs +36 -2
  67. package/dist/react.mjs.map +1 -1
  68. package/dist/support.cjs +15 -4
  69. package/dist/support.cjs.map +1 -1
  70. package/dist/support.d.cts +1 -1
  71. package/dist/support.d.ts +1 -1
  72. package/dist/support.mjs +15 -4
  73. package/dist/support.mjs.map +1 -1
  74. package/dist/telemetry.cjs +11 -3
  75. package/dist/telemetry.cjs.map +1 -1
  76. package/dist/telemetry.d.cts +1 -1
  77. package/dist/telemetry.d.ts +1 -1
  78. package/dist/telemetry.mjs +11 -3
  79. package/dist/telemetry.mjs.map +1 -1
  80. package/dist/{types-nnztVk7G.d.ts → types-BPATkXYu.d.ts} +35 -7
  81. package/dist/{types-DIVZl7nO.d.cts → types-Du_iioJ7.d.cts} +35 -7
  82. package/dist/usage.cjs +11 -3
  83. package/dist/usage.cjs.map +1 -1
  84. package/dist/usage.d.cts +1 -1
  85. package/dist/usage.d.ts +1 -1
  86. package/dist/usage.mjs +11 -3
  87. package/dist/usage.mjs.map +1 -1
  88. package/dist/webhooks.cjs +17 -4
  89. package/dist/webhooks.cjs.map +1 -1
  90. package/dist/webhooks.d.cts +1 -1
  91. package/dist/webhooks.d.ts +1 -1
  92. package/dist/webhooks.mjs +17 -4
  93. package/dist/webhooks.mjs.map +1 -1
  94. package/package.json +9 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/base-url-allowlist.ts","../src/api-key.ts","../src/auth-pkce-shared.ts","../src/mobile.ts"],"names":[],"mappings":"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","file":"mobile.mjs","sourcesContent":["/**\r\n * Erros tipados do SDK.\r\n *\r\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\r\n * por `.code` (string estável) sem parsing de message.\r\n */\r\n\r\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\r\nexport type NeetruErrorCode =\r\n | 'invalid_config'\r\n | 'missing_api_key'\r\n | 'unauthorized'\r\n | 'forbidden'\r\n | 'not_found'\r\n | 'rate_limited'\r\n | 'validation_failed'\r\n | 'network_error'\r\n | 'invalid_response'\r\n | 'server_error'\r\n | 'token_expired'\r\n | 'token_invalid_signature'\r\n | 'token_invalid_audience'\r\n | 'token_invalid_issuer'\r\n /** 3.0: runtime não expõe API necessária (ex: WebCrypto ausente). */\r\n | 'runtime_unsupported'\r\n /**\r\n * 3.1.3 (bug_c1c96a00): o `state` retornado pelo IdP no callback OIDC não bate com o gerado em\r\n * `signIn` (CSRF). Também usado quando não há transação OIDC pendente.\r\n */\r\n | 'oidc_state_mismatch'\r\n /**\r\n * 3.1.12 (incidente pdv-agiliza 2026-07-10): o `nonce` do `id_token` retornado\r\n * pelo `/token` não bate com o gerado em `signIn` (possível replay). O token\r\n * é descartado sem tocar o storage.\r\n */\r\n | 'oidc_nonce_mismatch'\r\n /**\r\n * 3.1.12: o POST `/api/v1/oauth/token` (troca do authorization_code por\r\n * tokens em `completeSignIn`) falhou — HTTP não-2xx, corpo com `error`, ou\r\n * resposta sem `id_token`.\r\n */\r\n | 'token_exchange_failed'\r\n /** HTTP 409 — colisão de idempotência ou conflito de recurso. */\r\n | 'conflict'\r\n | 'unknown';\r\n\r\n/**\r\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\r\n *\r\n * @example\r\n * ```ts\r\n * try { await client.catalog.list(); }\r\n * catch (e) {\r\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\r\n * }\r\n * ```\r\n */\r\nexport class NeetruError extends Error {\r\n public readonly code: NeetruErrorCode | string;\r\n public readonly status?: number;\r\n public readonly requestId?: string;\r\n /**\r\n * Motivo semântico opcional propagado pelo Core no corpo do erro (campo\r\n * `reason` no TOPO do body, fora de `error`). Usado principalmente pelo 503\r\n * de lease de DB, onde `reason` discrimina o estado do ambiente\r\n * (`suspended` | `archived` | `revoked` | `provisioning` | `degraded` |\r\n * `no_route` | `expired`). Camadas superiores (ex.: `db/sql`) mapeiam isso\r\n * para um `NeetruDbErrorCode` mais específico (recuperável vs não).\r\n */\r\n public readonly reason?: string;\r\n\r\n constructor(\r\n code: NeetruErrorCode | string,\r\n message: string,\r\n status?: number,\r\n requestId?: string,\r\n reason?: string,\r\n ) {\r\n super(message);\r\n this.name = 'NeetruError';\r\n this.code = code;\r\n this.status = status;\r\n this.requestId = requestId;\r\n this.reason = reason;\r\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\r\n Object.setPrototypeOf(this, NeetruError.prototype);\r\n }\r\n}\r\n","/**\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 / server-side) — ÚNICA fonte de\r\n * env que pode ativar `dev`/`dev-local`. `globalThis.NEETRU_ENV` (browser) só é\r\n * honrado para `workspace`/`prod` (HTTP real), NUNCA para `dev` — é um canal\r\n * client-side não-confiável (SECURITY audit 2026-06-18). Fallback `prod`.\r\n * Além disso, mocks-por-env são IMPOSSÍVEIS em produção (`NODE_ENV=production`):\r\n * lá, só `mocks` injetado explicitamente ativa comportamento mock.\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 /**\r\n * Planos cobrados do produto. **EXPERIMENTAL / reservado (2026-09-19):** os\r\n * endpoints do catálogo serializam este campo condicionalmente, mas o Core\r\n * ainda NÃO o popula por nenhum caminho de escrita (upsert / CLI / reorder) —\r\n * na prática vem SEMPRE ausente/vazio. NÃO construa UX assumindo que existe;\r\n * trate como opcional e possivelmente inexistente até o Core passar a emitir\r\n * planos publicamente.\r\n */\r\n plans?: ProductPlan[];\r\n}\r\n\r\n/**\r\n * Plano cobrado de um produto. **EXPERIMENTAL:** o Core ainda não emite este\r\n * shape (ver `Product.plans`). Mantido para parse forward-compatible.\r\n */\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 * baseUrl allowlist (HIGH-SDK-05) — fail-closed contra phishing/MITM.\r\n *\r\n * Default allow:\r\n * - HTTPS para `*.neetru.com` (apex e qualquer subdomínio)\r\n * - HTTP **apenas** para localhost / 127.0.0.1 / [::1] / *.test / *.localhost\r\n * - HTTPS para qualquer hostname **se** estiver em `allowedHosts` configurado\r\n * pelo consumer (override explícito)\r\n *\r\n * Bypass: `NEETRU_ALLOW_INSECURE_BASEURL=1` no env (Node) ou `globalThis.NEETRU_ALLOW_INSECURE_BASEURL = '1'`\r\n * (browser/Edge). Útil em tests / staging customizado / proxies. Lança warning\r\n * no console quando ativo.\r\n *\r\n * Por que: caller pode configurar `createNeetruClient({apiKey: 'nrt_xxx', baseUrl: 'http://attacker.example/'})`.\r\n * Sem allowlist, todo Bearer vaza pro atacker. SDK 3.0 fail-closed.\r\n */\r\nimport { NeetruError } from './errors';\r\n\r\n/** Hosts considerados loopback (dev local). */\r\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\r\n\r\n/** Suffix oficial de produção/staging Neetru. */\r\nconst NEETRU_SUFFIX = '.neetru.com';\r\n\r\n/** Hostnames apex sem dot (raríssimo, mas defensivo). */\r\nconst NEETRU_APEX = 'neetru.com';\r\n\r\n/**\r\n * Detecta sub-host de dev TLD seguro (M-02 fix).\r\n *\r\n * Antes: `hostname.endsWith('.localhost')` aceitava `evil.com.localhost`\r\n * (TLD `.localhost` real). Agora regex força que `.localhost`/`.test` seja\r\n * o SUFFIX TLD e o subdomínio seja simples (sem outros pontos seguindo).\r\n *\r\n * Aceita:\r\n * - `myapp.test`\r\n * - `frontend.localhost`\r\n * - `app.localhost`\r\n * - `localhost` (cai no LOOPBACK_HOSTS, não aqui)\r\n *\r\n * Rejeita:\r\n * - `evil.com.localhost` (TLD `.localhost` malicioso registrado)\r\n * - `attacker.evil.test` (subdomínio profundo)\r\n * - `xn--neetr-rta.localhost` (IDN punycode)\r\n */\r\nconst SAFE_DEV_HOST_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?\\.(test|localhost)$/;\r\n\r\nfunction isSafeDevHost(hostname: string): boolean {\r\n return SAFE_DEV_HOST_RE.test(hostname);\r\n}\r\n\r\n/** Normaliza trailing-dot FQDN (`api.neetru.com.` → `api.neetru.com`). L-06 fix. */\r\nfunction normalizeHostname(hostname: string): string {\r\n return hostname.toLowerCase().replace(/\\.+$/, '');\r\n}\r\n\r\n/**\r\n * Valida que `baseUrl` é seguro pra enviar Bearer tokens. Retorna URL\r\n * normalizada (trim trailing slash). Lança `NeetruError('invalid_config')`\r\n * em rejeição, exceto se override estiver ativo.\r\n *\r\n * @param baseUrl URL configurada pelo caller (raw, antes de normalização).\r\n * @param allowedHosts Hostnames extras permitidos (override do consumer). Suffix match.\r\n * @returns URL validada e normalizada (sem trailing slash).\r\n *\r\n * @throws NeetruError('invalid_config') se URL inválida ou fora da allowlist.\r\n */\r\nexport function validateBaseUrl(baseUrl: string, allowedHosts: string[] = []): string {\r\n if (typeof baseUrl !== 'string' || baseUrl.length === 0) {\r\n throw new NeetruError('invalid_config', 'baseUrl is required');\r\n }\r\n\r\n let parsed: URL;\r\n try {\r\n parsed = new URL(baseUrl);\r\n } catch {\r\n throw new NeetruError('invalid_config', `baseUrl is not a valid URL: ${baseUrl}`);\r\n }\r\n\r\n const protocol = parsed.protocol;\r\n // L-06: normaliza trailing dot (FQDN absoluto). M-02: já em lowercase.\r\n const hostname = normalizeHostname(parsed.hostname);\r\n\r\n // Bypass explícito — útil em tests / staging custom proxies.\r\n if (isInsecureBypassActive()) {\r\n if (typeof console !== 'undefined') {\r\n console.warn(\r\n `[neetru-sdk] NEETRU_ALLOW_INSECURE_BASEURL=1 ativo. baseUrl=${baseUrl} aceito sem validação.`,\r\n );\r\n }\r\n return baseUrl.replace(/\\/+$/, '');\r\n }\r\n\r\n // Bloqueia file:, javascript:, etc.\r\n if (protocol !== 'https:' && protocol !== 'http:') {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n `baseUrl protocol must be http: or https:, got ${protocol}`,\r\n );\r\n }\r\n\r\n // HTTP só pra loopback / dev seguro.\r\n if (protocol === 'http:') {\r\n const isLoopback =\r\n LOOPBACK_HOSTS.has(hostname) ||\r\n isSafeDevHost(hostname);\r\n if (!isLoopback) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n `baseUrl with http: only allowed for localhost/127.0.0.1/*.test/*.localhost. Got: ${hostname}. Use https: or set NEETRU_ALLOW_INSECURE_BASEURL=1 to bypass.`,\r\n );\r\n }\r\n return baseUrl.replace(/\\/+$/, '');\r\n }\r\n\r\n // HTTPS: aceita *.neetru.com automaticamente, ou hostnames em allowedHosts.\r\n const isNeetruDomain =\r\n hostname === NEETRU_APEX || hostname.endsWith(NEETRU_SUFFIX);\r\n const isAllowedHost = allowedHosts.some((allowed) => {\r\n const norm = allowed.toLowerCase();\r\n return hostname === norm || hostname.endsWith(`.${norm}`);\r\n });\r\n // Loopback HTTPS também aceito (raro mas válido).\r\n const isLoopbackHttps =\r\n LOOPBACK_HOSTS.has(hostname) ||\r\n isSafeDevHost(hostname);\r\n\r\n if (!isNeetruDomain && !isAllowedHost && !isLoopbackHttps) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n `baseUrl ${hostname} is not in the allowlist. Allowed: *.neetru.com, localhost, or configure { allowedHosts: ['...'] }. Bypass: NEETRU_ALLOW_INSECURE_BASEURL=1.`,\r\n );\r\n }\r\n\r\n return baseUrl.replace(/\\/+$/, '');\r\n}\r\n\r\n/** Lê flag de bypass do env (Node) ou globalThis (browser/Edge). */\r\nfunction isInsecureBypassActive(): boolean {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const proc = (globalThis as any).process;\r\n const envVal = proc?.env?.NEETRU_ALLOW_INSECURE_BASEURL;\r\n if (envVal === '1' || envVal === 'true') return true;\r\n } catch {\r\n /* ignore */\r\n }\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const g = (globalThis as any).NEETRU_ALLOW_INSECURE_BASEURL;\r\n if (g === '1' || g === 'true' || g === true) return true;\r\n } catch {\r\n /* ignore */\r\n }\r\n return false;\r\n}\r\n","/**\r\n * Helper canonical pra parsing de apiKey Neetru.\r\n *\r\n * Formato: `nrt_<keyId>_<secret>` onde keyId e secret sao tokens\r\n * alfanumericos sem underscore.\r\n *\r\n * **H-02 fix (Opus review 2026-05-27):** o uso anterior `apiKey.split('_')[1]`\r\n * com fallback `?? apiKey` vazava a chave inteira como `client_id` na URL\r\n * publica de OAuth se o consumer passasse uma string malformada\r\n * (typo, ou key de outro provider colada por engano).\r\n *\r\n * Agora valida o formato e throws `NeetruError('invalid_config')` em\r\n * input malformado.\r\n */\r\nimport { NeetruError } from './errors';\r\n\r\nconst API_KEY_RE = /^nrt_([A-Za-z0-9]+)_([A-Za-z0-9]+)$/;\r\n\r\nexport interface ParsedApiKey {\r\n keyId: string;\r\n secret: string;\r\n}\r\n\r\n/**\r\n * Faz parse seguro da apiKey. Throws `NeetruError('invalid_config')`\r\n * se o formato nao bater.\r\n *\r\n * @example\r\n * ```ts\r\n * const { keyId, secret } = parseApiKey('nrt_kid123_sec456');\r\n * // -> { keyId: 'kid123', secret: 'sec456' }\r\n * ```\r\n */\r\nexport function parseApiKey(apiKey: string | undefined | null): ParsedApiKey {\r\n if (typeof apiKey !== 'string' || apiKey.length === 0) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'apiKey is required (formato nrt_<keyId>_<secret>)',\r\n );\r\n }\r\n const match = API_KEY_RE.exec(apiKey);\r\n if (!match) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'apiKey malformado - esperado formato nrt_<keyId>_<secret>',\r\n );\r\n }\r\n return { keyId: match[1], secret: match[2] };\r\n}\r\n\r\n/**\r\n * Variante que retorna `null` ao inves de throw - util pra paths\r\n * opcionais onde apiKey pode estar vazio (signOut sem auth previo).\r\n */\r\nexport function tryParseApiKey(apiKey: string | undefined | null): ParsedApiKey | null {\r\n if (typeof apiKey !== 'string' || apiKey.length === 0) return null;\r\n const match = API_KEY_RE.exec(apiKey);\r\n if (!match) return null;\r\n return { keyId: match[1], secret: match[2] };\r\n}\r\n","/**\r\n * Helpers PKCE/OIDC compartilhados entre `auth.ts` (namespace `auth` — web,\r\n * `location`/`sessionStorage`/`localStorage`) e `mobile.ts` (namespace\r\n * `createMobileAuthNamespace` — React Native/Expo, storage+navegação\r\n * injetados pelo app).\r\n *\r\n * Por que existe este módulo: a lógica de geração PKCE (RFC 7636) — WebCrypto,\r\n * `state`/`nonce` CSRF, `code_verifier`/`code_challenge` S256 — e o parsing de\r\n * claims do `id_token` são **criptográficas e de segurança**, não UI. Web e\r\n * mobile precisam produzir exatamente a mesma coisa (o Core do lado servidor\r\n * não sabe nem quer saber se quem chamou `/authorize` foi um browser ou um\r\n * app RN). Duplicar essa lógica por copy-paste arrisca as duas cópias\r\n * divergirem silenciosamente (ex: um lado corrige um bug de padding base64url\r\n * e o outro não). Este módulo é a ÚNICA fonte da verdade para essa parte;\r\n * `auth.ts` e `mobile.ts` só diferem em COMO guardam a transação e COMO\r\n * navegam pro `/authorize` — não em COMO geram os valores PKCE.\r\n *\r\n * O que **não** mora aqui: storage (sessionStorage vs `MobileAuthStorage`),\r\n * navegação (`location.assign` vs `openAuthUrl`), e a troca `code`→tokens via\r\n * fetch (cada namespace faz seu próprio POST — protocolo simples e estável\r\n * o bastante pra não justificar o acoplamento de mais uma camada aqui).\r\n */\r\nimport { NeetruError } from './errors';\r\nimport { parseApiKey } from './api-key';\r\nimport type { NeetruUser } from './types';\r\n\r\n// ─── WebCrypto ──────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Acessa WebCrypto de forma defensiva. O fluxo PKCE exige `getRandomValues` +\r\n * `subtle.digest`. Em runtimes sem WebCrypto (http inseguro, browser muito\r\n * antigo, **React Native/Hermes sem polyfill** — vide JSDoc de `mobile.ts`)\r\n * lança `runtime_unsupported` em vez de gerar PKCE fraco.\r\n */\r\nexport function getWebCrypto(): Crypto {\r\n const c = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!c || typeof c.getRandomValues !== 'function' || !c.subtle) {\r\n throw new NeetruError(\r\n 'runtime_unsupported',\r\n 'auth.signIn requer WebCrypto (crypto.subtle) — indisponível neste runtime. Use HTTPS (ou http://localhost) num browser moderno, ou instale um polyfill WebCrypto (ex: react-native-quick-crypto) em React Native.',\r\n );\r\n }\r\n return c;\r\n}\r\n\r\n/** Codifica bytes em base64url sem padding (browser/Node/RN-com-polyfill compat). */\r\nexport function base64UrlFromBytes(bytes: Uint8Array): string {\r\n let str = '';\r\n for (const b of bytes) str += String.fromCharCode(b);\r\n const b64 =\r\n typeof btoa === 'function' ? btoa(str) : Buffer.from(str, 'binary').toString('base64');\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\r\n}\r\n\r\n/** Token URL-safe aleatório de `byteLen` bytes (base64url). 32 bytes → 43 chars. */\r\nexport function randomUrlSafe(crypto: Crypto, byteLen: number): string {\r\n const bytes = new Uint8Array(byteLen);\r\n crypto.getRandomValues(bytes);\r\n return base64UrlFromBytes(bytes);\r\n}\r\n\r\n/** PKCE S256 (RFC 7636 §4.2): `base64url(sha256(code_verifier))`. */\r\nexport async function pkceChallengeS256(crypto: Crypto, verifier: string): Promise<string> {\r\n const data = new TextEncoder().encode(verifier);\r\n const digest = await crypto.subtle.digest('SHA-256', data);\r\n return base64UrlFromBytes(new Uint8Array(digest));\r\n}\r\n\r\n// ─── IdP / client_id resolution ────────────────────────────────────────────\r\n\r\n/**\r\n * Deriva o origin do IdP (`auth.neetru.com`) a partir do `baseUrl` (api.* →\r\n * auth.*). Override por `globalThis.NEETRU_AUTH_URL` (testes / staging).\r\n */\r\nexport function resolveIdpOrigin(baseUrl: string): string {\r\n const overrideAuthUrl =\r\n typeof (globalThis as { NEETRU_AUTH_URL?: string }).NEETRU_AUTH_URL === 'string'\r\n ? (globalThis as { NEETRU_AUTH_URL?: string }).NEETRU_AUTH_URL\r\n : null;\r\n return overrideAuthUrl ?? baseUrl.replace(/^https?:\\/\\/api\\./, 'https://auth.');\r\n}\r\n\r\n/**\r\n * Resolve o `client_id` OIDC: `config.oidcClientId` (oidc_clients/{id}) ??\r\n * keyId da apiKey (compat). H-02: parse seguro — nunca vaza a apiKey inteira\r\n * como client_id.\r\n */\r\nexport function resolveOidcClientId(config: { oidcClientId?: string; apiKey?: string }): string {\r\n let clientId = config.oidcClientId;\r\n if (!clientId && config.apiKey) {\r\n clientId = parseApiKey(config.apiKey).keyId;\r\n }\r\n if (!clientId) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'auth OIDC requer `oidcClientId` (ou `apiKey`) configurado para montar o client_id.',\r\n );\r\n }\r\n return clientId;\r\n}\r\n\r\n// ─── /authorize URL ─────────────────────────────────────────────────────────\r\n\r\nexport interface AuthorizeUrlParams {\r\n idpOrigin: string;\r\n clientId: string;\r\n redirectUri: string;\r\n scope: string;\r\n state: string;\r\n nonce: string;\r\n codeChallenge: string;\r\n}\r\n\r\n/** Monta a URL do `/api/v1/oauth/authorize` (Authorization Code + PKCE S256). */\r\nexport function buildAuthorizeUrl(params: AuthorizeUrlParams): URL {\r\n const url = new URL('/api/v1/oauth/authorize', params.idpOrigin);\r\n url.searchParams.set('response_type', 'code');\r\n url.searchParams.set('client_id', params.clientId);\r\n url.searchParams.set('redirect_uri', params.redirectUri);\r\n url.searchParams.set('scope', params.scope);\r\n url.searchParams.set('state', params.state);\r\n url.searchParams.set('nonce', params.nonce);\r\n url.searchParams.set('code_challenge', params.codeChallenge);\r\n url.searchParams.set('code_challenge_method', 'S256');\r\n return url;\r\n}\r\n\r\n/** Path canônico do endpoint de token (RFC 6749 §5.2), relativo ao IdP origin. */\r\nexport const OAUTH_TOKEN_PATH = '/api/v1/oauth/token';\r\n\r\n// ─── id_token decode/mapping ────────────────────────────────────────────────\r\n\r\n/** Decodifica o payload (claims) de um JWT sem validar assinatura. */\r\nexport function decodeJwtPayload(token: string): Record<string, unknown> | null {\r\n const parts = token.split('.');\r\n if (parts.length !== 3) return null;\r\n try {\r\n // base64url decode (browser/Node compat).\r\n const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\r\n const pad = '='.repeat((4 - (b64.length % 4)) % 4);\r\n const decoded =\r\n typeof atob === 'function'\r\n ? atob(b64 + pad)\r\n : Buffer.from(b64 + pad, 'base64').toString('utf-8');\r\n return JSON.parse(decoded);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Decodifica um `id_token` e mapeia pra `NeetruUser`, validando expiração.\r\n * `exp` é timestamp Unix em segundos; token expirado (ou payload sem `sub`)\r\n * → `null`, independente de qualquer cache do chamador.\r\n */\r\nexport function jwtToNeetruUser(token: string): NeetruUser | null {\r\n const payload = decodeJwtPayload(token);\r\n if (!payload || typeof payload.sub !== 'string') return null;\r\n if (typeof payload.exp === 'number' && payload.exp * 1000 < Date.now()) {\r\n return null;\r\n }\r\n return {\r\n uid: payload.sub,\r\n email: typeof payload.email === 'string' ? payload.email : '',\r\n emailVerified: typeof payload.email_verified === 'boolean' ? payload.email_verified : undefined,\r\n displayName: typeof payload.name === 'string' ? payload.name : undefined,\r\n photoURL: typeof payload.picture === 'string' ? payload.picture : undefined,\r\n isStaff: payload.is_staff === true,\r\n isCustomer: payload.is_customer === true,\r\n tenantId: typeof payload.tenant_id === 'string' ? payload.tenant_id : undefined,\r\n ...payload,\r\n };\r\n}\r\n","/**\r\n * `createMobileAuthNamespace` — namespace de auth OIDC (Authorization Code +\r\n * PKCE) para **React Native / Expo**, subpath `@neetru/sdk/mobile`.\r\n *\r\n * ## Por que um namespace PARALELO (não um patch do `auth.ts` existente)\r\n *\r\n * O namespace `auth` (web, `createNeetruClient(...).auth`) é hardcoded em\r\n * três APIs que **não existem em React Native**:\r\n * - `globalThis.location.assign(url)` pra iniciar o redirect do `signIn`.\r\n * - `sessionStorage` pra persistir a transação PKCE em voo.\r\n * - `localStorage` pra persistir `id_token`/`refresh_token`.\r\n *\r\n * RN não tem nenhum dos três, e não há seam de injeção pra eles no `auth.ts`\r\n * hoje. Em vez de forçar esses três conceitos web numa API que finge\r\n * funcionar em mobile, este módulo expõe `createMobileAuthNamespace(config)`\r\n * com contratos que fazem sentido nativamente em RN: o app injeta storage\r\n * (`config.storage`) e navegação (`config.openAuthUrl`), e passa a URL do\r\n * deep link de volta explicitamente pro SDK (`completeSignIn(callbackUrl)`) —\r\n * não há `location.href` implícito pra ler.\r\n *\r\n * A lógica **criptográfica** de PKCE (WebCrypto, geração de `state`/`nonce`/\r\n * `code_verifier`/`code_challenge` S256, parsing/validação de `id_token`) é\r\n * 100% reaproveitada de `./auth-pkce-shared` — a MESMA usada por `auth.ts` —\r\n * pra nunca divergir entre web e mobile.\r\n *\r\n * ## Requisitos do app consumidor (leia antes de integrar)\r\n *\r\n * 1. **Polyfill de WebCrypto** — Hermes/React Native não implementa\r\n * `crypto.subtle` nem `crypto.getRandomValues` nativamente. Instale e\r\n * inicialize ANTES de chamar `signIn`/`completeSignIn`:\r\n * - `react-native-get-random-values` (cobre `crypto.getRandomValues`)\r\n * - `react-native-quick-crypto` (ou equivalente) pra `crypto.subtle.digest`\r\n * O SDK **não** faz polyfill sozinho — se ausente, `signIn`/`completeSignIn`\r\n * lançam `NeetruError('runtime_unsupported', ...)` com mensagem clara,\r\n * nunca geram PKCE fraco silenciosamente.\r\n *\r\n * 2. **`crypto.randomUUID()`** — usado internamente pelo SDK em headers\r\n * `Idempotency-Key` (`./idempotency`) para mutations (`usage.report`,\r\n * `support.createTicket`, etc). Mesmo requisito de polyfill acima\r\n * (`react-native-get-random-values` cobre isso também em runtimes recentes;\r\n * confira a versão instalada).\r\n *\r\n * 3. **`config.openAuthUrl` DEVE abrir um browser externo — NUNCA uma WebView\r\n * embutida.** RFC 8252 §8.12 (OAuth 2.0 for Native Apps) proíbe explicitamente\r\n * embedded user-agents (WebView in-app) pro authorization request: o app\r\n * hospedeiro pode inspecionar/injetar script no formulário de login do IdP\r\n * (roubo de senha/cookie pelo próprio app, phishing indistinguível). Use\r\n * `expo-web-browser` (`WebBrowser.openAuthSessionAsync`, que usa\r\n * `ASWebAuthenticationSession` no iOS / Custom Tabs no Android) ou\r\n * `Linking.openURL` (abre o browser do sistema). O SDK nunca abre a URL\r\n * sozinho — só delega pra `config.openAuthUrl`, é o app quem escolhe o\r\n * mecanismo, mas a escolha SEGURA é obrigatória, não opcional.\r\n *\r\n * 4. **`config.storage` deve ser persistente e idealmente seguro** —\r\n * recomendação: `expo-secure-store` ou `react-native-keychain` (Keychain/\r\n * Keystore nativo, criptografado em repouso). `AsyncStorage` puro funciona\r\n * mas NÃO é criptografado — evite pra tokens em produção.\r\n *\r\n * 5. **`client.db` (offline-first, IndexedDB via `idb`) NÃO tem storage\r\n * persistente equivalente em React Native ainda.** Sem `IndexedDB`, o SDK\r\n * cai automaticamente no `MemoryStore` (`./db/offline/memory-store.ts`) —\r\n * 100% em memória, os dados **não sobrevivem a um restart do app**. Essa é\r\n * uma limitação CONHECIDA e ACEITA de v1 deste subpath — não é um bug, não\r\n * tente contorná-la agora. Se seu produto RN depende de `client.db`\r\n * sobrevivendo a restart, isso ainda não está pronto.\r\n *\r\n * ## Contrato assíncrono (diferente do namespace `auth` web)\r\n *\r\n * `getIdToken()` e `getUser()` aqui retornam **`Promise`** (não valores\r\n * síncronos como no `auth.ts` web) porque `config.storage` é assíncrono —\r\n * React Native não tem nenhum storage síncrono equivalente a `localStorage`.\r\n * Todo o resto do contrato segue o mesmo padrão: qualquer operação que toque\r\n * storage é `async`.\r\n */\r\nimport { NeetruError } from './errors';\r\nimport { DEFAULT_BASE_URL } from './types';\r\nimport type {\r\n AuthStateListener,\r\n CompleteSignInResult,\r\n NeetruClientConfig,\r\n NeetruUser,\r\n} from './types';\r\nimport { validateBaseUrl } from './base-url-allowlist';\r\nimport {\r\n getWebCrypto,\r\n randomUrlSafe,\r\n pkceChallengeS256,\r\n resolveIdpOrigin,\r\n resolveOidcClientId,\r\n buildAuthorizeUrl,\r\n decodeJwtPayload,\r\n jwtToNeetruUser,\r\n OAUTH_TOKEN_PATH,\r\n} from './auth-pkce-shared';\r\n\r\n// ─── Storage / navigation seams ─────────────────────────────────────────────\r\n\r\n/**\r\n * Storage assíncrono chave-valor que o app RN injeta — ex: um wrapper fino\r\n * sobre `expo-secure-store` ou `@react-native-async-storage/async-storage`.\r\n * O SDK não assume NENHUMA implementação concreta.\r\n */\r\nexport interface MobileAuthStorage {\r\n getItem(key: string): Promise<string | null>;\r\n setItem(key: string, value: string): Promise<void>;\r\n removeItem(key: string): Promise<void>;\r\n}\r\n\r\nexport interface MobileAuthConfig extends NeetruClientConfig {\r\n /** Storage persistente (idealmente seguro) — ver requisito 4 no JSDoc do módulo. */\r\n storage: MobileAuthStorage;\r\n /**\r\n * Abre a URL de authorize num browser EXTERNO (nunca WebView embutida) —\r\n * ver requisito 3 no JSDoc do módulo. Ex: `WebBrowser.openAuthSessionAsync`\r\n * (expo-web-browser) ou `Linking.openURL`.\r\n */\r\n openAuthUrl: (url: string) => Promise<void>;\r\n}\r\n\r\nexport interface MobileSignInOptions {\r\n /**\r\n * Universal Link / App Link (`https://...`) registrado como `redirect_uri`\r\n * do OIDC client mobile no Core. Obrigatório — diferente do namespace web,\r\n * não há `location.origin` implícito pra usar como default.\r\n */\r\n redirectUri: string;\r\n /** OIDC scope. Default `openid profile email`. */\r\n scope?: string;\r\n /** Destino pós-login que o app decide interpretar após `completeSignIn`. */\r\n postLoginRedirect?: string;\r\n}\r\n\r\nexport interface MobileAuthNamespace {\r\n /**\r\n * Inicia o fluxo OIDC Authorization Code + PKCE: gera `state`/`nonce`/\r\n * `code_verifier`/`code_challenge` (S256), persiste a transação via\r\n * `config.storage`, e chama `config.openAuthUrl(url)` — NUNCA navega\r\n * sozinho (sem `location` em RN).\r\n *\r\n * @throws {NeetruError} `runtime_unsupported` se `crypto.subtle` ausente.\r\n */\r\n signIn(options: MobileSignInOptions): Promise<void>;\r\n /**\r\n * Completa o login: recebe a URL COMPLETA do deep link de retorno (o app\r\n * lê via `Linking.getInitialURL()` / listener `Linking.addEventListener`)\r\n * e troca o `code` pelos tokens no mesmo `/api/v1/oauth/token` que o\r\n * namespace web usa (POST x-www-form-urlencoded, PKCE puro, sem\r\n * client_secret). Valida `state` (CSRF) e `nonce` (anti-replay), grava os\r\n * tokens em `config.storage` e notifica `onAuthStateChanged`.\r\n *\r\n * `callbackUrl` é OBRIGATÓRIO — mobile não tem `location.href` implícito.\r\n *\r\n * @throws {NeetruError} `oidc_state_mismatch`, `oidc_nonce_mismatch`,\r\n * `token_exchange_failed`, `network_error`, ou `invalid_config` se a URL\r\n * não é um callback OIDC válido (sem `?code=`/`?error=`) ou não há\r\n * transação pendente no storage.\r\n */\r\n completeSignIn(callbackUrl: string): Promise<CompleteSignInResult>;\r\n /** `id_token` cru armazenado, ou `null` se ausente/expirado. */\r\n getIdToken(): Promise<string | null>;\r\n /** Usuário atual (claims do `id_token` armazenado), ou `null`. */\r\n getUser(): Promise<NeetruUser | null>;\r\n /**\r\n * Subscreve a mudanças de estado de auth. Diferente do namespace web, o\r\n * listener inicial é disparado de forma ASSÍNCRONA (a leitura do storage\r\n * injetado é sempre `Promise`) — não confie em ordem síncrona de chamada.\r\n * Retorna função de unsubscribe (síncrona).\r\n */\r\n onAuthStateChanged(listener: AuthStateListener): () => void;\r\n /**\r\n * Renova os tokens via `grant_type=refresh_token`, se houver refresh_token\r\n * armazenado. `true` se renovou; `false` em qualquer falha (sem refresh\r\n * token, `invalid_grant`, rede) — não lança.\r\n */\r\n refreshTokens(): Promise<boolean>;\r\n /** Limpa a sessão local (storage) + melhor esforço de revoke no servidor. */\r\n signOut(): Promise<void>;\r\n}\r\n\r\n// ─── internals ───────────────────────────────────────────────────────────────\r\n\r\nconst OIDC_TX_KEY = 'neetru_oauth_tx';\r\nconst ID_TOKEN_KEY = 'neetru_id_token';\r\nconst REFRESH_TOKEN_KEY = 'neetru_refresh_token';\r\n\r\ninterface MobileOidcTransaction {\r\n state: string;\r\n nonce: string;\r\n codeVerifier: string;\r\n redirectUri: string;\r\n postLoginRedirect?: string;\r\n}\r\n\r\ninterface ResolvedMobileConfig {\r\n fetchImpl: typeof globalThis.fetch;\r\n baseUrl: string;\r\n oidcClientId?: string;\r\n apiKey?: string;\r\n storage: MobileAuthStorage;\r\n openAuthUrl: (url: string) => Promise<void>;\r\n}\r\n\r\nfunction resolveMobileConfig(config: MobileAuthConfig): ResolvedMobileConfig {\r\n if (!config.storage) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'createMobileAuthNamespace requer `storage` (MobileAuthStorage) — ver JSDoc de src/mobile.ts requisito 4.',\r\n );\r\n }\r\n if (typeof config.openAuthUrl !== 'function') {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'createMobileAuthNamespace requer `openAuthUrl` (browser externo, NUNCA WebView embutida) — ver JSDoc de src/mobile.ts requisito 3.',\r\n );\r\n }\r\n const fetchImpl = config.fetch ?? globalThis.fetch;\r\n if (typeof fetchImpl !== 'function') {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'fetch is not available in this runtime. Pass `fetch` explicitly to createMobileAuthNamespace.',\r\n );\r\n }\r\n const baseUrl = validateBaseUrl(config.baseUrl ?? DEFAULT_BASE_URL, config.allowedHosts ?? []);\r\n return {\r\n fetchImpl: fetchImpl.bind(globalThis),\r\n baseUrl,\r\n oidcClientId: config.oidcClientId,\r\n apiKey: config.apiKey,\r\n storage: config.storage,\r\n openAuthUrl: config.openAuthUrl,\r\n };\r\n}\r\n\r\n/**\r\n * POST `/api/v1/oauth/token` (RFC 6749 §5.2) — mesmo endpoint e mesmo shape\r\n * de request que `auth.ts` usa (x-www-form-urlencoded, sem `client_secret`,\r\n * PKCE puro). Reimplementado aqui (não importado de `auth.ts`) porque a\r\n * versão web está acoplada a `sessionStorage`/`localStorage` síncronos por\r\n * dentro de closures não-exportadas — não há como chamá-la isolada sem\r\n * reescrever aquele módulo. O PROTOCOLO (não a lógica crypto) é estável o\r\n * bastante (RFC) pra não justificar esse acoplamento a mais.\r\n */\r\nasync function postToken(\r\n fetchImpl: typeof globalThis.fetch,\r\n tokenUrl: string,\r\n form: URLSearchParams,\r\n): Promise<Record<string, unknown>> {\r\n let res: Response;\r\n try {\r\n res = await fetchImpl(tokenUrl, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\r\n body: form.toString(),\r\n });\r\n } catch (err) {\r\n throw new NeetruError(\r\n 'network_error',\r\n `POST ${tokenUrl} falhou (network): ${(err as Error)?.message ?? String(err)}`,\r\n );\r\n }\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\r\n if (!res.ok || typeof payload?.error === 'string') {\r\n const desc =\r\n (typeof payload?.error_description === 'string' && payload.error_description) ||\r\n (typeof payload?.error === 'string' && payload.error) ||\r\n `HTTP ${res.status}`;\r\n throw new NeetruError(\r\n 'token_exchange_failed',\r\n `Troca de tokens falhou no /token: ${desc}`,\r\n res.status,\r\n );\r\n }\r\n\r\n return payload ?? {};\r\n}\r\n\r\n/**\r\n * Cria o namespace de auth mobile. Ver JSDoc do módulo pros requisitos do\r\n * app consumidor (polyfill WebCrypto, browser externo, storage seguro).\r\n */\r\nexport function createMobileAuthNamespace(config: MobileAuthConfig): MobileAuthNamespace {\r\n const resolved = resolveMobileConfig(config);\r\n const listeners = new Set<AuthStateListener>();\r\n\r\n function tokenUrl(): string {\r\n return `${resolveIdpOrigin(resolved.baseUrl)}${OAUTH_TOKEN_PATH}`;\r\n }\r\n\r\n async function notify(): Promise<void> {\r\n const user = await getUser();\r\n for (const l of listeners) {\r\n try {\r\n l(user);\r\n } catch {\r\n /* ignore consumer errors */\r\n }\r\n }\r\n }\r\n\r\n async function signIn(options: MobileSignInOptions): Promise<void> {\r\n if (!options?.redirectUri) {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'mobile auth.signIn requer `redirectUri` (Universal Link/App Link https://... registrado no Core) — não há location.origin implícito em React Native.',\r\n );\r\n }\r\n const crypto = getWebCrypto();\r\n const scope = options.scope ?? 'openid profile email';\r\n const clientId = resolveOidcClientId(resolved);\r\n\r\n const state = randomUrlSafe(crypto, 16);\r\n const nonce = randomUrlSafe(crypto, 16);\r\n // RFC 7636 §4.1: verifier 43..128 chars. 32 bytes → 43 chars base64url.\r\n const codeVerifier = randomUrlSafe(crypto, 32);\r\n const codeChallenge = await pkceChallengeS256(crypto, codeVerifier);\r\n\r\n const tx: MobileOidcTransaction = {\r\n state,\r\n nonce,\r\n codeVerifier,\r\n redirectUri: options.redirectUri,\r\n postLoginRedirect: options.postLoginRedirect,\r\n };\r\n // Persiste ANTES de abrir o browser — sobrevive ao round-trip via deep link\r\n // (o processo do app pode até ser suspenso/recriado enquanto o browser externo\r\n // está aberto; storage persistente garante que a transação segue lá).\r\n await resolved.storage.setItem(OIDC_TX_KEY, JSON.stringify(tx));\r\n\r\n const idpOrigin = resolveIdpOrigin(resolved.baseUrl);\r\n const url = buildAuthorizeUrl({\r\n idpOrigin,\r\n clientId,\r\n redirectUri: options.redirectUri,\r\n scope,\r\n state,\r\n nonce,\r\n codeChallenge,\r\n });\r\n await resolved.openAuthUrl(url.toString());\r\n }\r\n\r\n async function completeSignIn(callbackUrl: string): Promise<CompleteSignInResult> {\r\n if (!callbackUrl || typeof callbackUrl !== 'string') {\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'mobile auth.completeSignIn requer `callbackUrl` (a URL completa do deep link de retorno) — mobile não tem location.href implícito.',\r\n );\r\n }\r\n let parsed: URL;\r\n try {\r\n parsed = new URL(callbackUrl);\r\n } catch {\r\n throw new NeetruError('invalid_config', `callbackUrl não é uma URL válida: ${callbackUrl}`);\r\n }\r\n const params = parsed.searchParams;\r\n\r\n const idpError = params.get('error');\r\n if (idpError) {\r\n const desc = params.get('error_description') ?? idpError;\r\n await resolved.storage.removeItem(OIDC_TX_KEY);\r\n throw new NeetruError(idpError, `OIDC authorize falhou: ${desc}`);\r\n }\r\n\r\n const code = params.get('code');\r\n if (!code) {\r\n // Revisão adversarial 2026-09-20: limpa a transação pendente (state/\r\n // nonce/code_verifier) igual ao branch de erro do IdP acima — sem isto,\r\n // uma callbackUrl inesperada (bug do app, deep link malformado, link\r\n // stale reaberto) deixava dados residuais no storage até o próximo\r\n // signIn() sobrescrever a mesma chave. completeSignIn é uma chamada\r\n // explícita única (não especulativa como o handleRedirectCallback do\r\n // fluxo web), então a limpeza importa aqui.\r\n await resolved.storage.removeItem(OIDC_TX_KEY);\r\n throw new NeetruError(\r\n 'invalid_config',\r\n 'callbackUrl não contém `?code=` nem `?error=` — não é um callback OIDC válido.',\r\n );\r\n }\r\n\r\n const returnedState = params.get('state');\r\n const txRaw = await resolved.storage.getItem(OIDC_TX_KEY);\r\n let tx: MobileOidcTransaction | null = null;\r\n if (txRaw) {\r\n try {\r\n tx = JSON.parse(txRaw) as MobileOidcTransaction;\r\n } catch {\r\n tx = null;\r\n }\r\n }\r\n if (!tx) {\r\n throw new NeetruError(\r\n 'oidc_state_mismatch',\r\n 'Callback OIDC sem transação pendente — `signIn` não foi iniciado (ou já consumido).',\r\n );\r\n }\r\n if (!returnedState || returnedState !== tx.state) {\r\n await resolved.storage.removeItem(OIDC_TX_KEY);\r\n throw new NeetruError(\r\n 'oidc_state_mismatch',\r\n 'state do callback OIDC não confere com o da transação (possível CSRF).',\r\n );\r\n }\r\n\r\n // Consome a transação (single-use) antes de trocar o code.\r\n await resolved.storage.removeItem(OIDC_TX_KEY);\r\n\r\n const clientId = resolveOidcClientId(resolved);\r\n const form = new URLSearchParams({\r\n grant_type: 'authorization_code',\r\n code,\r\n redirect_uri: tx.redirectUri,\r\n client_id: clientId,\r\n code_verifier: tx.codeVerifier,\r\n });\r\n const payload = await postToken(resolved.fetchImpl, tokenUrl(), form);\r\n\r\n const idToken = payload.id_token;\r\n if (typeof idToken !== 'string' || idToken.length === 0) {\r\n throw new NeetruError(\r\n 'token_exchange_failed',\r\n 'Resposta do /token sem `id_token` — o login não pode ser completado.',\r\n );\r\n }\r\n\r\n // Anti-replay OIDC: nonce do id_token TEM que bater com o da transação.\r\n const claims = decodeJwtPayload(idToken);\r\n if (!claims) {\r\n throw new NeetruError(\r\n 'token_exchange_failed',\r\n 'id_token retornado pelo /token é malformado (payload indecodificável).',\r\n );\r\n }\r\n if (claims.nonce !== tx.nonce) {\r\n throw new NeetruError(\r\n 'oidc_nonce_mismatch',\r\n 'nonce do id_token não confere com o da transação OIDC (possível replay). Token descartado.',\r\n );\r\n }\r\n\r\n const user = jwtToNeetruUser(idToken);\r\n if (!user) {\r\n throw new NeetruError(\r\n 'token_exchange_failed',\r\n 'id_token retornado pelo /token não tem `sub` ou já está expirado.',\r\n );\r\n }\r\n\r\n await resolved.storage.setItem(ID_TOKEN_KEY, idToken);\r\n const refreshToken = payload.refresh_token;\r\n if (typeof refreshToken === 'string' && refreshToken.length > 0) {\r\n await resolved.storage.setItem(REFRESH_TOKEN_KEY, refreshToken);\r\n }\r\n\r\n await notify();\r\n\r\n return { user, postLoginRedirect: tx.postLoginRedirect };\r\n }\r\n\r\n async function getIdToken(): Promise<string | null> {\r\n const token = await resolved.storage.getItem(ID_TOKEN_KEY);\r\n if (!token) return null;\r\n // Não devolve token expirado/malformado.\r\n return jwtToNeetruUser(token) ? token : null;\r\n }\r\n\r\n async function getUser(): Promise<NeetruUser | null> {\r\n const token = await resolved.storage.getItem(ID_TOKEN_KEY);\r\n if (!token) return null;\r\n return jwtToNeetruUser(token);\r\n }\r\n\r\n async function refreshTokens(): Promise<boolean> {\r\n const refreshToken = await resolved.storage.getItem(REFRESH_TOKEN_KEY);\r\n if (!refreshToken) return false;\r\n\r\n let clientId: string;\r\n try {\r\n clientId = resolveOidcClientId(resolved);\r\n } catch {\r\n return false;\r\n }\r\n\r\n const form = new URLSearchParams({\r\n grant_type: 'refresh_token',\r\n refresh_token: refreshToken,\r\n client_id: clientId,\r\n });\r\n\r\n let payload: Record<string, unknown>;\r\n try {\r\n payload = await postToken(resolved.fetchImpl, tokenUrl(), form);\r\n } catch {\r\n // Network ou protocolo — mantém os tokens atuais; caller decide se\r\n // limpa a sessão. Mesmo contrato do namespace web (`return false`).\r\n return false;\r\n }\r\n\r\n const idToken = payload.id_token;\r\n if (typeof idToken !== 'string' || idToken.length === 0) return false;\r\n const user = jwtToNeetruUser(idToken);\r\n if (!user) return false;\r\n\r\n await resolved.storage.setItem(ID_TOKEN_KEY, idToken);\r\n const rotated = payload.refresh_token;\r\n if (typeof rotated === 'string' && rotated.length > 0) {\r\n await resolved.storage.setItem(REFRESH_TOKEN_KEY, rotated);\r\n }\r\n await notify();\r\n return true;\r\n }\r\n\r\n async function signOut(): Promise<void> {\r\n const storedToken = await resolved.storage.getItem(ID_TOKEN_KEY);\r\n await resolved.storage.removeItem(ID_TOKEN_KEY);\r\n await resolved.storage.removeItem(REFRESH_TOKEN_KEY);\r\n\r\n if (storedToken) {\r\n const idpOrigin = resolveIdpOrigin(resolved.baseUrl);\r\n const revokeUrl = `${idpOrigin}/api/v1/oauth/revoke`;\r\n try {\r\n await resolved.fetchImpl(revokeUrl, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify({ token: storedToken }),\r\n });\r\n } catch (err) {\r\n if (typeof console !== 'undefined') {\r\n console.warn(\r\n '[neetru-sdk] mobile signOut: server-side revoke falhou. Refresh token pode estar stale.',\r\n err,\r\n );\r\n }\r\n }\r\n }\r\n await notify();\r\n }\r\n\r\n return {\r\n signIn,\r\n completeSignIn,\r\n getIdToken,\r\n getUser,\r\n onAuthStateChanged(listener: AuthStateListener): () => void {\r\n listeners.add(listener);\r\n // Disparo inicial é assíncrono (storage é sempre Promise) — diferente\r\n // do namespace web, que dispara sync. Ver JSDoc do módulo.\r\n void getUser().then((user) => {\r\n if (listeners.has(listener)) {\r\n try {\r\n listener(user);\r\n } catch {\r\n /* ignore */\r\n }\r\n }\r\n });\r\n return () => {\r\n listeners.delete(listener);\r\n };\r\n },\r\n refreshTokens,\r\n signOut,\r\n };\r\n}\r\n"]}
package/dist/mocks.cjs CHANGED
@@ -54,6 +54,17 @@ var NeetruDbError = class _NeetruDbError extends NeetruError {
54
54
  }
55
55
  };
56
56
 
57
+ // src/runtime-env.ts
58
+ function isProductionRuntime() {
59
+ try {
60
+ if (typeof process !== "undefined" && process.env) {
61
+ return process.env.NODE_ENV === "production";
62
+ }
63
+ } catch {
64
+ }
65
+ return false;
66
+ }
67
+
57
68
  // src/mocks.ts
58
69
  var DEV_FIXTURE_USER = Object.freeze({
59
70
  uid: "dev-fixture-uid-0001",
@@ -71,6 +82,12 @@ var MockAuth = class {
71
82
  this._user = initialUser;
72
83
  this._listeners = /* @__PURE__ */ new Set();
73
84
  this._allowUnsignedDecode = options?.allowUnsignedDecode ?? false;
85
+ if (this._allowUnsignedDecode && isProductionRuntime()) {
86
+ throw new NeetruError(
87
+ "invalid_config",
88
+ "MockAuth com allowUnsignedDecode=true \xE9 PROIBIDO em produ\xE7\xE3o (NODE_ENV=production): decodificar JWT sem verificar assinatura \xE9 bypass de autentica\xE7\xE3o. Este modo existe exclusivamente para testes."
89
+ );
90
+ }
74
91
  if (this._allowUnsignedDecode) {
75
92
  const isTestEnv = typeof process !== "undefined" && (process.env?.NODE_ENV === "test" || process.env?.VITEST === "true" || process.env?.VITEST != null);
76
93
  if (!isTestEnv && typeof console !== "undefined") {