@neetru/sdk 1.1.1 → 1.2.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 (83) hide show
  1. package/CHANGELOG.md +33 -3
  2. package/README.md +135 -159
  3. package/dist/auth.cjs +482 -42
  4. package/dist/auth.cjs.map +1 -1
  5. package/dist/auth.d.cts +1 -1
  6. package/dist/auth.d.ts +1 -1
  7. package/dist/auth.mjs +482 -42
  8. package/dist/auth.mjs.map +1 -1
  9. package/dist/catalog.cjs +63 -24
  10. package/dist/catalog.cjs.map +1 -1
  11. package/dist/catalog.d.cts +2 -2
  12. package/dist/catalog.d.ts +2 -2
  13. package/dist/catalog.mjs +63 -24
  14. package/dist/catalog.mjs.map +1 -1
  15. package/dist/checkout.cjs +60 -18
  16. package/dist/checkout.cjs.map +1 -1
  17. package/dist/checkout.d.cts +1 -1
  18. package/dist/checkout.d.ts +1 -1
  19. package/dist/checkout.mjs +60 -18
  20. package/dist/checkout.mjs.map +1 -1
  21. package/dist/db.cjs +66 -25
  22. package/dist/db.cjs.map +1 -1
  23. package/dist/db.d.cts +1 -1
  24. package/dist/db.d.ts +1 -1
  25. package/dist/db.mjs +66 -25
  26. package/dist/db.mjs.map +1 -1
  27. package/dist/entitlements.cjs +101 -24
  28. package/dist/entitlements.cjs.map +1 -1
  29. package/dist/entitlements.d.cts +11 -5
  30. package/dist/entitlements.d.ts +11 -5
  31. package/dist/entitlements.mjs +101 -24
  32. package/dist/entitlements.mjs.map +1 -1
  33. package/dist/index.cjs +487 -43
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +3 -3
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.mjs +484 -44
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/mocks.cjs +3 -2
  40. package/dist/mocks.cjs.map +1 -1
  41. package/dist/mocks.d.cts +4 -2
  42. package/dist/mocks.d.ts +4 -2
  43. package/dist/mocks.mjs +3 -2
  44. package/dist/mocks.mjs.map +1 -1
  45. package/dist/notifications.cjs +296 -0
  46. package/dist/notifications.cjs.map +1 -0
  47. package/dist/notifications.d.cts +1 -0
  48. package/dist/notifications.d.ts +1 -0
  49. package/dist/notifications.mjs +293 -0
  50. package/dist/notifications.mjs.map +1 -0
  51. package/dist/react.cjs +7 -3
  52. package/dist/react.cjs.map +1 -1
  53. package/dist/react.d.cts +1 -1
  54. package/dist/react.d.ts +1 -1
  55. package/dist/react.mjs +7 -3
  56. package/dist/react.mjs.map +1 -1
  57. package/dist/support.cjs +60 -18
  58. package/dist/support.cjs.map +1 -1
  59. package/dist/support.d.cts +1 -1
  60. package/dist/support.d.ts +1 -1
  61. package/dist/support.mjs +60 -18
  62. package/dist/support.mjs.map +1 -1
  63. package/dist/telemetry.cjs +130 -19
  64. package/dist/telemetry.cjs.map +1 -1
  65. package/dist/telemetry.d.cts +17 -1
  66. package/dist/telemetry.d.ts +17 -1
  67. package/dist/telemetry.mjs +130 -19
  68. package/dist/telemetry.mjs.map +1 -1
  69. package/dist/{types-BA53dd8S.d.cts → types-CQAfwqUS.d.cts} +172 -8
  70. package/dist/{types-BA53dd8S.d.ts → types-CQAfwqUS.d.ts} +172 -8
  71. package/dist/usage.cjs +60 -18
  72. package/dist/usage.cjs.map +1 -1
  73. package/dist/usage.d.cts +1 -1
  74. package/dist/usage.d.ts +1 -1
  75. package/dist/usage.mjs +60 -18
  76. package/dist/usage.mjs.map +1 -1
  77. package/dist/webhooks.cjs +316 -0
  78. package/dist/webhooks.cjs.map +1 -0
  79. package/dist/webhooks.d.cts +1 -0
  80. package/dist/webhooks.d.ts +1 -0
  81. package/dist/webhooks.mjs +312 -0
  82. package/dist/webhooks.mjs.map +1 -0
  83. package/package.json +12 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts"],"names":["result","allowed"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\n *\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\n *\n * Exports:\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\n * (suporte a `mode='readonly'` pro decisão CEO §5).\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\n *\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\n * puxam React mesmo via transitive.\n */\nimport * as React from 'react';\nimport type { NeetruClient } from './types';\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\n\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\n\nexport interface CheckoutLinkProps\n extends Omit<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n 'href' | 'onClick' | 'onError'\n > {\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\n client: NeetruClient;\n /** Slug do produto (ex: `neetru-pulse`). */\n productId: string;\n /** Plan id (ex: `pro_monthly`). */\n planId: string;\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\n callbackUrl: string;\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\n tenantType?: 'pf' | 'pj';\n tenantId?: string;\n /** Children — texto/JSX do CTA. */\n children: React.ReactNode;\n /** Callback quando intent foi criada (antes de redirect). */\n onIntentCreated?: (result: CheckoutStartResult) => void;\n /** Callback de erro. */\n onError?: (err: Error) => void;\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\n noAutoRedirect?: boolean;\n /** Override de href (default: `#`). */\n href?: string;\n}\n\n/**\n * Wrapper de `<a>` que dispara `client.checkout.start()` no click.\n *\n * Uso típico:\n * ```tsx\n * <CheckoutLink\n * client={client}\n * productId=\"meu-saas\"\n * planId=\"pro_monthly\"\n * callbackUrl=\"https://meu-saas.com/billing/success\"\n * >\n * Assinar Pro\n * </CheckoutLink>\n * ```\n *\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\n * de URL no hover (pode ser sobrescrito via prop spread).\n */\nexport function CheckoutLink({\n client,\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n children,\n onIntentCreated,\n onError,\n noAutoRedirect,\n ...rest\n}: CheckoutLinkProps): React.ReactElement {\n const [submitting, setSubmitting] = React.useState(false);\n\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } }).checkout;\n\n const handleClick = React.useCallback(\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n if (submitting) return;\n if (!checkout || typeof checkout.start !== 'function') {\n onError?.(new Error('SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+.'));\n return;\n }\n setSubmitting(true);\n try {\n const result = await checkout.start({\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n autoRedirect: !noAutoRedirect,\n });\n onIntentCreated?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setSubmitting(false);\n }\n },\n [submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\n );\n\n return React.createElement(\n 'a',\n {\n ...rest,\n href: rest.href ?? '#',\n onClick: handleClick,\n 'aria-busy': submitting || undefined,\n 'data-neetru-checkout-link': 'true',\n },\n children,\n );\n}\n\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\n\n/** Modo de gate quando entitlement não é permitido. */\nexport type EntitlementGateMode = 'block' | 'readonly';\n\ninterface EntitlementContextValue {\n /** True quando feature está em modo read-only (limit exceeded). */\n readonly: boolean;\n /** Reason code da última check. */\n reason?: string;\n /** Limite atual (free tier). */\n limit?: number;\n /** Uso atual. */\n currentUsage?: number;\n}\n\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\n readonly: false,\n});\n\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\n * desabilitar inputs/botões de escrita. */\nexport function useEntitlementContext(): EntitlementContextValue {\n return React.useContext(EntitlementContext);\n}\n\nexport interface EntitlementGateProps {\n /** Cliente SDK Neetru. */\n client: NeetruClient;\n /** Feature key a checar (ex: `export.csv`). */\n feature: string;\n /** Slug do produto. Default: lê do client.config se ausente. */\n productSlug?: string;\n /** Modo:\n * - `block` (default): renderiza fallback quando não permitido.\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\n mode?: EntitlementGateMode;\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\n fallback?: React.ReactNode;\n /** Loading UI enquanto check resolve (Suspense-like). */\n loading?: React.ReactNode;\n children: React.ReactNode;\n}\n\ninterface EntitlementCheckResult {\n allowed: boolean;\n reason?: string;\n limit?: number;\n currentUsage?: number;\n behavior?: 'readonly' | null;\n}\n\n/**\n * Wrapper que gateia children por entitlement check.\n *\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\n * - Children renderizam normalmente.\n * - Context injeta `{readonly: true}`.\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\n * escritas (botões, forms, inputs).\n *\n * Quando `mode='block'` + check retorna `allowed=false`:\n * - `fallback` é renderizado (ex: paywall, upsell modal).\n *\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\n */\nexport function EntitlementGate({\n client,\n feature,\n productSlug,\n mode = 'block',\n fallback,\n loading,\n children,\n}: EntitlementGateProps): React.ReactElement {\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\n status: 'loading',\n });\n\n // Resolve productSlug from prop or client.config\n const resolvedSlug =\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\n\n React.useEffect(() => {\n let cancelled = false;\n const run = async () => {\n try {\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\n let result: EntitlementCheckResult;\n const usage = (client as { usage?: { check: (resource: string, opts?: { productId?: string }) => Promise<EntitlementCheckResult> } }).usage;\n if (usage && typeof usage.check === 'function') {\n const res = await usage.check(feature, { productId: resolvedSlug });\n result = {\n allowed: res.allowed === true,\n reason: res.reason,\n limit: res.limit,\n currentUsage: res.currentUsage,\n behavior: res.behavior,\n };\n } else {\n const allowed = await client.entitlements.check(resolvedSlug, feature);\n result = { allowed };\n }\n if (!cancelled) setState({ status: 'ready', result });\n } catch (err) {\n // Fail open in dev/prod — caller pode decidir via onError pattern futuro.\n if (!cancelled) {\n setState({ status: 'ready', result: { allowed: false, reason: 'check_failed' } });\n }\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed', err);\n }\n };\n run();\n return () => {\n cancelled = true;\n };\n }, [client, feature, resolvedSlug]);\n\n if (state.status === 'loading') {\n return React.createElement(React.Fragment, null, loading ?? null);\n }\n\n const result = state.result;\n const allowed = result?.allowed === true;\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\n\n if (!allowed && mode === 'block' && !isReadonly) {\n return React.createElement(React.Fragment, null, fallback ?? null);\n }\n\n return React.createElement(\n EntitlementContext.Provider,\n {\n value: {\n readonly: isReadonly,\n reason: result?.reason,\n limit: result?.limit,\n currentUsage: result?.currentUsage,\n },\n },\n children,\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/react.ts"],"names":["result","allowed"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\n *\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\n *\n * Exports:\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\n * (suporte a `mode='readonly'` pro decisão CEO §5).\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\n *\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\n * puxam React mesmo via transitive.\n */\nimport * as React from 'react';\nimport type { NeetruClient } from './types';\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\n\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\n\nexport interface CheckoutLinkProps\n extends Omit<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n 'href' | 'onClick' | 'onError'\n > {\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\n client: NeetruClient;\n /** Slug do produto (ex: `neetru-pulse`). */\n productId: string;\n /** Plan id (ex: `pro_monthly`). */\n planId: string;\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\n callbackUrl: string;\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\n tenantType?: 'pf' | 'pj';\n tenantId?: string;\n /** Children — texto/JSX do CTA. */\n children: React.ReactNode;\n /** Callback quando intent foi criada (antes de redirect). */\n onIntentCreated?: (result: CheckoutStartResult) => void;\n /** Callback de erro. */\n onError?: (err: Error) => void;\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\n noAutoRedirect?: boolean;\n /** Override de href (default: `#`). */\n href?: string;\n}\n\n/**\n * Wrapper de `<a>` que dispara `client.checkout.start()` no click.\n *\n * Uso típico:\n * ```tsx\n * <CheckoutLink\n * client={client}\n * productId=\"meu-saas\"\n * planId=\"pro_monthly\"\n * callbackUrl=\"https://meu-saas.com/billing/success\"\n * >\n * Assinar Pro\n * </CheckoutLink>\n * ```\n *\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\n * de URL no hover (pode ser sobrescrito via prop spread).\n */\nexport function CheckoutLink({\n client,\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n children,\n onIntentCreated,\n onError,\n noAutoRedirect,\n ...rest\n}: CheckoutLinkProps): React.ReactElement {\n const [submitting, setSubmitting] = React.useState(false);\n\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } }).checkout;\n\n const handleClick = React.useCallback(\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n if (submitting) return;\n if (!checkout || typeof checkout.start !== 'function') {\n onError?.(new Error('SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+.'));\n return;\n }\n setSubmitting(true);\n try {\n const result = await checkout.start({\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n autoRedirect: !noAutoRedirect,\n });\n onIntentCreated?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setSubmitting(false);\n }\n },\n [submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\n );\n\n return React.createElement(\n 'a',\n {\n ...rest,\n href: rest.href ?? '#',\n onClick: handleClick,\n 'aria-busy': submitting || undefined,\n 'data-neetru-checkout-link': 'true',\n },\n children,\n );\n}\n\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\n\n/** Modo de gate quando entitlement não é permitido. */\nexport type EntitlementGateMode = 'block' | 'readonly';\n\ninterface EntitlementContextValue {\n /** True quando feature está em modo read-only (limit exceeded). */\n readonly: boolean;\n /** Reason code da última check. */\n reason?: string;\n /** Limite atual (free tier). */\n limit?: number;\n /** Uso atual. */\n currentUsage?: number;\n}\n\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\n readonly: false,\n});\n\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\n * desabilitar inputs/botões de escrita. */\nexport function useEntitlementContext(): EntitlementContextValue {\n return React.useContext(EntitlementContext);\n}\n\nexport interface EntitlementGateProps {\n /** Cliente SDK Neetru. */\n client: NeetruClient;\n /** Feature key a checar (ex: `export.csv`). */\n feature: string;\n /** Slug do produto. Default: lê do client.config se ausente. */\n productSlug?: string;\n /** Modo:\n * - `block` (default): renderiza fallback quando não permitido.\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\n mode?: EntitlementGateMode;\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\n fallback?: React.ReactNode;\n /** Loading UI enquanto check resolve (Suspense-like). */\n loading?: React.ReactNode;\n children: React.ReactNode;\n}\n\ninterface EntitlementCheckResult {\n allowed: boolean;\n reason?: string;\n limit?: number;\n currentUsage?: number;\n behavior?: 'readonly' | null;\n}\n\n/**\n * Wrapper que gateia children por entitlement check.\n *\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\n * - Children renderizam normalmente.\n * - Context injeta `{readonly: true}`.\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\n * escritas (botões, forms, inputs).\n *\n * Quando `mode='block'` + check retorna `allowed=false`:\n * - `fallback` é renderizado (ex: paywall, upsell modal).\n *\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\n */\nexport function EntitlementGate({\n client,\n feature,\n productSlug,\n mode = 'block',\n fallback,\n loading,\n children,\n}: EntitlementGateProps): React.ReactElement {\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\n status: 'loading',\n });\n\n // Resolve productSlug from prop or client.config\n const resolvedSlug =\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\n\n // tenantId fallback: client.config.tenantId (default no createNeetruClient).\n // usage.check valida ambos productId + tenantId — sem isso, lança\n // validation_failed e o Gate fica trancado em allowed:false.\n const resolvedTenantId = (client.config as { tenantId?: string }).tenantId;\n\n React.useEffect(() => {\n let cancelled = false;\n const run = async () => {\n try {\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\n let result: EntitlementCheckResult;\n const usage = (client as {\n usage?: {\n check: (\n resource: string,\n opts?: { productId?: string; tenantId?: string },\n ) => Promise<EntitlementCheckResult>;\n };\n }).usage;\n if (usage && typeof usage.check === 'function' && resolvedTenantId) {\n const res = await usage.check(feature, {\n productId: resolvedSlug,\n tenantId: resolvedTenantId,\n });\n result = {\n allowed: res.allowed === true,\n reason: res.reason,\n limit: res.limit,\n currentUsage: res.currentUsage,\n behavior: res.behavior,\n };\n } else {\n // Sem tenantId, cai pra entitlements.check (não exige tenant) em vez\n // de chamar usage.check e tomar validation_failed silencioso.\n const allowed = await client.entitlements.check(resolvedSlug, feature);\n result = { allowed };\n }\n if (!cancelled) setState({ status: 'ready', result });\n } catch (err) {\n // Fail open in dev/prod — caller pode decidir via onError pattern futuro.\n if (!cancelled) {\n setState({ status: 'ready', result: { allowed: false, reason: 'check_failed' } });\n }\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed', err);\n }\n };\n run();\n return () => {\n cancelled = true;\n };\n }, [client, feature, resolvedSlug, resolvedTenantId]);\n\n if (state.status === 'loading') {\n return React.createElement(React.Fragment, null, loading ?? null);\n }\n\n const result = state.result;\n const allowed = result?.allowed === true;\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\n\n if (!allowed && mode === 'block' && !isReadonly) {\n return React.createElement(React.Fragment, null, fallback ?? null);\n }\n\n return React.createElement(\n EntitlementContext.Provider,\n {\n value: {\n readonly: isReadonly,\n reason: result?.reason,\n limit: result?.limit,\n currentUsage: result?.currentUsage,\n },\n },\n children,\n );\n}\n"]}
package/dist/support.cjs CHANGED
@@ -16,6 +16,31 @@ var NeetruError = class _NeetruError extends Error {
16
16
  };
17
17
 
18
18
  // src/http.ts
19
+ var DEFAULT_RETRIES = 2;
20
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set([
21
+ "rate_limited",
22
+ "server_error",
23
+ "network_error"
24
+ ]);
25
+ function backoffMs(attempt) {
26
+ const base = 200 * Math.pow(4, attempt);
27
+ const jitter = base * 0.2 * (Math.random() * 2 - 1);
28
+ return Math.max(50, Math.round(base + jitter));
29
+ }
30
+ function parseRetryAfter(value) {
31
+ if (!value) return null;
32
+ const secs = Number(value);
33
+ if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
34
+ const dateMs = Date.parse(value);
35
+ if (Number.isFinite(dateMs)) {
36
+ const delta = dateMs - Date.now();
37
+ if (delta > 0) return delta;
38
+ }
39
+ return null;
40
+ }
41
+ function sleep(ms) {
42
+ return new Promise((resolve) => setTimeout(resolve, ms));
43
+ }
19
44
  function statusToCode(status) {
20
45
  if (status === 401) return "unauthorized";
21
46
  if (status === 403) return "forbidden";
@@ -49,6 +74,7 @@ async function safeJson(res) {
49
74
  async function httpRequest(config, opts) {
50
75
  const method = opts.method ?? "GET";
51
76
  const url = buildUrl(config.baseUrl, opts.path, opts.query);
77
+ const maxRetries = opts.retries ?? DEFAULT_RETRIES;
52
78
  const headers = {
53
79
  accept: "application/json",
54
80
  ...opts.headers
@@ -62,24 +88,32 @@ async function httpRequest(config, opts) {
62
88
  }
63
89
  headers.authorization = `Bearer ${config.apiKey}`;
64
90
  }
65
- const init = { method, headers };
66
- if (opts.body !== void 0 && method !== "GET" && method !== "DELETE") {
91
+ const bodyString = opts.body !== void 0 && method !== "GET" && method !== "DELETE" ? JSON.stringify(opts.body) : void 0;
92
+ if (bodyString !== void 0) {
67
93
  headers["content-type"] = "application/json";
68
- init.body = JSON.stringify(opts.body);
69
94
  }
70
- init.signal = AbortSignal.timeout(3e4);
71
- let res;
72
- try {
73
- res = await config.fetch(url, init);
74
- } catch (err) {
75
- if (err instanceof DOMException && err.name === "TimeoutError") {
76
- throw new NeetruError("network_error", "Network error: timeout after 30s");
95
+ let lastError = null;
96
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
97
+ const init = { method, headers };
98
+ if (bodyString !== void 0) init.body = bodyString;
99
+ init.signal = AbortSignal.timeout(3e4);
100
+ let res;
101
+ try {
102
+ res = await config.fetch(url, init);
103
+ } catch (err2) {
104
+ const message2 = err2 instanceof DOMException && err2.name === "TimeoutError" ? "Network error: timeout after 30s" : `Network error: ${err2 instanceof Error ? err2.message : "fetch failed"}`;
105
+ lastError = new NeetruError("network_error", message2);
106
+ if (attempt < maxRetries) {
107
+ await sleep(backoffMs(attempt));
108
+ continue;
109
+ }
110
+ throw lastError;
111
+ }
112
+ const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
113
+ if (res.ok) {
114
+ const parsed = await safeJson(res);
115
+ return parsed;
77
116
  }
78
- const message = err instanceof Error ? err.message : "fetch failed";
79
- throw new NeetruError("network_error", `Network error: ${message}`);
80
- }
81
- const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
82
- if (!res.ok) {
83
117
  const body = await safeJson(res);
84
118
  let code = statusToCode(res.status);
85
119
  let message = `HTTP ${res.status}`;
@@ -92,10 +126,18 @@ async function httpRequest(config, opts) {
92
126
  if (typeof errField.message === "string") message = errField.message;
93
127
  }
94
128
  }
95
- throw new NeetruError(code, message, res.status, requestId);
129
+ const err = new NeetruError(code, message, res.status, requestId);
130
+ lastError = err;
131
+ const isRetryable = RETRYABLE_CODES.has(code);
132
+ if (isRetryable && attempt < maxRetries) {
133
+ const retryAfter = parseRetryAfter(res.headers.get("retry-after"));
134
+ const delay = retryAfter ?? backoffMs(attempt);
135
+ await sleep(delay);
136
+ continue;
137
+ }
138
+ throw err;
96
139
  }
97
- const parsed = await safeJson(res);
98
- return parsed;
140
+ throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
99
141
  }
100
142
 
101
143
  // src/support.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/support.ts"],"names":[],"mappings":"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","file":"support.cjs","sourcesContent":["/**\n * Erros tipados do SDK.\n *\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\n * por `.code` (string estável) sem parsing de message.\n */\n\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\nexport type NeetruErrorCode =\n | 'invalid_config'\n | 'missing_api_key'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'rate_limited'\n | 'validation_failed'\n | 'network_error'\n | 'invalid_response'\n | 'server_error'\n | 'unknown';\n\n/**\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\n *\n * @example\n * ```ts\n * try { await client.catalog.list(); }\n * catch (e) {\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\n * }\n * ```\n */\nexport class NeetruError extends Error {\n public readonly code: NeetruErrorCode | string;\n public readonly status?: number;\n public readonly requestId?: string;\n\n constructor(\n code: NeetruErrorCode | string,\n message: string,\n status?: number,\n requestId?: string,\n ) {\n super(message);\n this.name = 'NeetruError';\n this.code = code;\n this.status = status;\n this.requestId = requestId;\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\n Object.setPrototypeOf(this, NeetruError.prototype);\n }\n}\n","/**\n * HTTP transport interno do SDK.\n *\n * Responsabilidades:\n * - Construir URL absoluta a partir de `baseUrl` + path\n * - Injetar Bearer token quando `requireAuth=true`\n * - Mapear status HTTP → `NeetruError` com `code` estável\n * - Parse defensivo de JSON (não lança em body vazio em 204)\n *\n * Não faz retry/backoff em v0.1 (carry-over Sprint 3+).\n */\nimport { NeetruError, type NeetruErrorCode } from './errors';\nimport type { ResolvedConfig } from './types';\n\n/** Opções da request HTTP. */\nexport interface HttpRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\n path: string;\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\n query?: Record<string, string | number | boolean | undefined>;\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\n body?: unknown;\n /**\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\n * se config.apiKey ausente. Default false.\n */\n requireAuth?: boolean;\n /** Cabeçalhos extras. */\n headers?: Record<string, string>;\n}\n\n/** Mapeamento status → code estável do NeetruError. */\nfunction statusToCode(status: number): NeetruErrorCode {\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 422 || status === 400) return 'validation_failed';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'server_error';\n return 'unknown';\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\n // Trim trailing slash em base e leading em path pra evitar `//`.\n const base = baseUrl.replace(/\\/+$/, '');\n const p = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(`${base}${p}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\nasync function safeJson(res: Response): Promise<unknown> {\n const text = await res.text();\n if (!text) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\n * `NeetruError` com `code` derivado do status.\n */\nexport async function httpRequest<T>(\n config: ResolvedConfig,\n opts: HttpRequestOptions,\n): Promise<T> {\n const method = opts.method ?? 'GET';\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\n\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...opts.headers,\n };\n\n if (opts.requireAuth) {\n if (!config.apiKey) {\n throw new NeetruError(\n 'missing_api_key',\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\n );\n }\n headers.authorization = `Bearer ${config.apiKey}`;\n }\n\n const init: RequestInit = { method, headers };\n if (opts.body !== undefined && method !== 'GET' && method !== 'DELETE') {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(opts.body);\n }\n\n // BUG-020 fix (2026-05-13): timeout default 30s. Sem isso, em SSR/ISR do\n // Next.js o request pode travar a request inteira; em build estático\n // bloqueia o pipeline. AbortSignal.timeout requer Node 18+ ou browsers\n // recentes — alvo do SDK é Node 18+ e Chrome/Firefox/Safari modernos.\n init.signal = AbortSignal.timeout(30_000);\n\n let res: Response;\n try {\n res = await config.fetch(url, init);\n } catch (err) {\n if (err instanceof DOMException && err.name === 'TimeoutError') {\n throw new NeetruError('network_error', 'Network error: timeout after 30s');\n }\n const message = err instanceof Error ? err.message : 'fetch failed';\n throw new NeetruError('network_error', `Network error: ${message}`);\n }\n\n const requestId = res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\n\n if (!res.ok) {\n const body = (await safeJson(res)) as { error?: { code?: string; message?: string } | string } | undefined;\n let code: string = statusToCode(res.status);\n let message = `HTTP ${res.status}`;\n if (body && typeof body === 'object' && 'error' in body) {\n const errField = body.error;\n if (typeof errField === 'string') {\n message = errField;\n } else if (errField && typeof errField === 'object') {\n if (typeof errField.code === 'string') code = errField.code;\n if (typeof errField.message === 'string') message = errField.message;\n }\n }\n throw new NeetruError(code, message, res.status, requestId);\n }\n\n const parsed = await safeJson(res);\n // Caller é responsável por validar shape; SDK assume backend correto.\n return parsed as T;\n}\n","/**\n * Support namespace — criar e listar tickets do customer (v0.2).\n *\n * Endpoints (em prod):\n * - `POST /api/v1/products/{slug}/tickets` — criar ticket\n * - `GET /api/v1/products/{slug}/tickets` — listar meus tickets do produto\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n *\n * Decisão: severity default é `normal`. `low` indica feature requests; `urgent`\n * pra outages production (paginating ops imediatamente em prod).\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport type {\n CreateTicketInput,\n ResolvedConfig,\n SupportNamespace,\n SupportSeverity,\n SupportStatus,\n SupportTicket,\n} from './types';\n\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\n\ninterface RawTicket {\n id?: string;\n subject?: string;\n message?: string;\n severity?: string;\n status?: string;\n createdAt?: string;\n updatedAt?: string;\n productSlug?: string;\n}\n\nfunction toTicket(raw: unknown): SupportTicket {\n if (!raw || typeof raw !== 'object') {\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\n }\n const r = raw as RawTicket;\n if (typeof r.id !== 'string') {\n throw new NeetruError('invalid_response', 'Ticket missing id');\n }\n return {\n id: r.id,\n subject: typeof r.subject === 'string' ? r.subject : '',\n message: typeof r.message === 'string' ? r.message : '',\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\n ? (r.severity as SupportSeverity)\n : 'normal',\n status: VALID_STATUSES.includes(r.status as SupportStatus)\n ? (r.status as SupportStatus)\n : 'open',\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\n productSlug: typeof r.productSlug === 'string' ? r.productSlug : undefined,\n };\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. Se `productSlug` não é\n * passado, o backend infere do escopo do token.\n */\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input || typeof input !== 'object') {\n throw new NeetruError('validation_failed', 'input is required');\n }\n if (!input.subject || typeof input.subject !== 'string') {\n throw new NeetruError('validation_failed', 'subject is required');\n }\n if (input.subject.length > 200) {\n throw new NeetruError('validation_failed', 'subject max 200 chars');\n }\n if (!input.message || typeof input.message !== 'string') {\n throw new NeetruError('validation_failed', 'message is required');\n }\n if (input.message.length > 10_000) {\n throw new NeetruError('validation_failed', 'message max 10000 chars');\n }\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\n throw new NeetruError('validation_failed', `severity must be one of ${VALID_SEVERITIES.join(', ')}`);\n }\n\n const slug = input.productSlug ?? '_default';\n const body: Record<string, unknown> = {\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: `/api/v1/products/${encodeURIComponent(slug)}/tickets`,\n body,\n requireAuth: true,\n });\n\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\n const candidate =\n raw && typeof raw === 'object' && 'ticket' in raw\n ? (raw as { ticket?: RawTicket }).ticket\n : raw;\n return toTicket(candidate);\n },\n\n /**\n * Lista tickets do customer no produto atual (escopo do token).\n */\n async listMyTickets(): Promise<SupportTicket[]> {\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/v1/products/_default/tickets',\n requireAuth: true,\n });\n const list = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && 'tickets' in raw ? (raw as { tickets?: unknown[] }).tickets ?? [] : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/support.ts"],"names":["err","message"],"mappings":"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","file":"support.cjs","sourcesContent":["/**\n * Erros tipados do SDK.\n *\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\n * por `.code` (string estável) sem parsing de message.\n */\n\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\nexport type NeetruErrorCode =\n | 'invalid_config'\n | 'missing_api_key'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'rate_limited'\n | 'validation_failed'\n | 'network_error'\n | 'invalid_response'\n | 'server_error'\n | 'unknown';\n\n/**\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\n *\n * @example\n * ```ts\n * try { await client.catalog.list(); }\n * catch (e) {\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\n * }\n * ```\n */\nexport class NeetruError extends Error {\n public readonly code: NeetruErrorCode | string;\n public readonly status?: number;\n public readonly requestId?: string;\n\n constructor(\n code: NeetruErrorCode | string,\n message: string,\n status?: number,\n requestId?: string,\n ) {\n super(message);\n this.name = 'NeetruError';\n this.code = code;\n this.status = status;\n this.requestId = requestId;\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\n Object.setPrototypeOf(this, NeetruError.prototype);\n }\n}\n","/**\n * HTTP transport interno do SDK.\n *\n * Responsabilidades:\n * - Construir URL absoluta a partir de `baseUrl` + path\n * - Injetar Bearer token quando `requireAuth=true`\n * - Mapear status HTTP → `NeetruError` com `code` estável\n * - Parse defensivo de JSON (não lança em body vazio em 204)\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\n * presente. Default 2 retries (3 tentativas no total). Caller opta-out\n * com `retries: 0`.\n */\nimport { NeetruError, type NeetruErrorCode } from './errors';\nimport type { ResolvedConfig } from './types';\n\n/** Opções da request HTTP. */\nexport interface HttpRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\n path: string;\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\n query?: Record<string, string | number | boolean | undefined>;\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\n body?: unknown;\n /**\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\n * se config.apiKey ausente. Default false.\n */\n requireAuth?: boolean;\n /** Cabeçalhos extras. */\n headers?: Record<string, string>;\n /**\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\n * `network_error`). Default 2 (= 3 tentativas total). Caller passa `0` pra\n * desativar (útil em operações não-idempotentes específicas).\n */\n retries?: number;\n}\n\nconst DEFAULT_RETRIES = 2;\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\n 'rate_limited',\n 'server_error',\n 'network_error',\n]);\n\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\nfunction backoffMs(attempt: number): number {\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\n return Math.max(50, Math.round(base + jitter));\n}\n\n/**\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\n * inválido. RFC 9110 §10.2.3.\n */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const secs = Number(value);\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\n const dateMs = Date.parse(value);\n if (Number.isFinite(dateMs)) {\n const delta = dateMs - Date.now();\n if (delta > 0) return delta;\n }\n return null;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Mapeamento status → code estável do NeetruError. */\nfunction statusToCode(status: number): NeetruErrorCode {\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 422 || status === 400) return 'validation_failed';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'server_error';\n return 'unknown';\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\n // Trim trailing slash em base e leading em path pra evitar `//`.\n const base = baseUrl.replace(/\\/+$/, '');\n const p = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(`${base}${p}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\nasync function safeJson(res: Response): Promise<unknown> {\n const text = await res.text();\n if (!text) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\n * automático em códigos transientes (rate_limited/server_error/network_error)\n * conforme `opts.retries` (default 2 = 3 tentativas).\n */\nexport async function httpRequest<T>(\n config: ResolvedConfig,\n opts: HttpRequestOptions,\n): Promise<T> {\n const method = opts.method ?? 'GET';\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\n const maxRetries = opts.retries ?? DEFAULT_RETRIES;\n\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...opts.headers,\n };\n\n if (opts.requireAuth) {\n if (!config.apiKey) {\n throw new NeetruError(\n 'missing_api_key',\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\n );\n }\n headers.authorization = `Bearer ${config.apiKey}`;\n }\n\n // Body só é serializado uma vez — reusado entre tentativas.\n const bodyString =\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\n ? JSON.stringify(opts.body)\n : undefined;\n if (bodyString !== undefined) {\n headers['content-type'] = 'application/json';\n }\n\n let lastError: NeetruError | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const init: RequestInit = { method, headers };\n if (bodyString !== undefined) init.body = bodyString;\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\n // requer Node 18+ ou browsers recentes.\n init.signal = AbortSignal.timeout(30_000);\n\n let res: Response;\n try {\n res = await config.fetch(url, init);\n } catch (err) {\n const message =\n err instanceof DOMException && err.name === 'TimeoutError'\n ? 'Network error: timeout after 30s'\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\n lastError = new NeetruError('network_error', message);\n if (attempt < maxRetries) {\n await sleep(backoffMs(attempt));\n continue;\n }\n throw lastError;\n }\n\n const requestId =\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\n\n if (res.ok) {\n const parsed = await safeJson(res);\n return parsed as T;\n }\n\n const body = (await safeJson(res)) as\n | { error?: { code?: string; message?: string } | string }\n | undefined;\n let code: string = statusToCode(res.status);\n let message = `HTTP ${res.status}`;\n if (body && typeof body === 'object' && 'error' in body) {\n const errField = body.error;\n if (typeof errField === 'string') {\n message = errField;\n } else if (errField && typeof errField === 'object') {\n if (typeof errField.code === 'string') code = errField.code;\n if (typeof errField.message === 'string') message = errField.message;\n }\n }\n const err = new NeetruError(code, message, res.status, requestId);\n lastError = err;\n\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\n if (isRetryable && attempt < maxRetries) {\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const delay = retryAfter ?? backoffMs(attempt);\n await sleep(delay);\n continue;\n }\n throw err;\n }\n\n // Unreachable — loop sempre retorna ou lança. Safety net.\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\n}\n","/**\n * Support namespace — criar e listar tickets do customer (v0.2).\n *\n * Endpoints (em prod):\n * - `POST /api/v1/products/{slug}/tickets` — criar ticket\n * - `GET /api/v1/products/{slug}/tickets` — listar meus tickets do produto\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n *\n * Decisão: severity default é `normal`. `low` indica feature requests; `urgent`\n * pra outages production (paginating ops imediatamente em prod).\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport type {\n CreateTicketInput,\n ResolvedConfig,\n SupportNamespace,\n SupportSeverity,\n SupportStatus,\n SupportTicket,\n} from './types';\n\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\n\ninterface RawTicket {\n id?: string;\n subject?: string;\n message?: string;\n severity?: string;\n status?: string;\n createdAt?: string;\n updatedAt?: string;\n productSlug?: string;\n}\n\nfunction toTicket(raw: unknown): SupportTicket {\n if (!raw || typeof raw !== 'object') {\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\n }\n const r = raw as RawTicket;\n if (typeof r.id !== 'string') {\n throw new NeetruError('invalid_response', 'Ticket missing id');\n }\n return {\n id: r.id,\n subject: typeof r.subject === 'string' ? r.subject : '',\n message: typeof r.message === 'string' ? r.message : '',\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\n ? (r.severity as SupportSeverity)\n : 'normal',\n status: VALID_STATUSES.includes(r.status as SupportStatus)\n ? (r.status as SupportStatus)\n : 'open',\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\n productSlug: typeof r.productSlug === 'string' ? r.productSlug : undefined,\n };\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. Se `productSlug` não é\n * passado, o backend infere do escopo do token.\n */\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input || typeof input !== 'object') {\n throw new NeetruError('validation_failed', 'input is required');\n }\n if (!input.subject || typeof input.subject !== 'string') {\n throw new NeetruError('validation_failed', 'subject is required');\n }\n if (input.subject.length > 200) {\n throw new NeetruError('validation_failed', 'subject max 200 chars');\n }\n if (!input.message || typeof input.message !== 'string') {\n throw new NeetruError('validation_failed', 'message is required');\n }\n if (input.message.length > 10_000) {\n throw new NeetruError('validation_failed', 'message max 10000 chars');\n }\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\n throw new NeetruError('validation_failed', `severity must be one of ${VALID_SEVERITIES.join(', ')}`);\n }\n\n const slug = input.productSlug ?? '_default';\n const body: Record<string, unknown> = {\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: `/api/v1/products/${encodeURIComponent(slug)}/tickets`,\n body,\n requireAuth: true,\n });\n\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\n const candidate =\n raw && typeof raw === 'object' && 'ticket' in raw\n ? (raw as { ticket?: RawTicket }).ticket\n : raw;\n return toTicket(candidate);\n },\n\n /**\n * Lista tickets do customer no produto atual (escopo do token).\n */\n async listMyTickets(): Promise<SupportTicket[]> {\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/v1/products/_default/tickets',\n requireAuth: true,\n });\n const list = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && 'tickets' in raw ? (raw as { tickets?: unknown[] }).tickets ?? [] : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { R as ResolvedConfig, s as SupportNamespace } from './types-BA53dd8S.cjs';
1
+ import { x as ResolvedConfig, z as SupportNamespace } from './types-CQAfwqUS.cjs';
2
2
 
3
3
  declare function createSupportNamespace(config: ResolvedConfig): SupportNamespace;
4
4
 
package/dist/support.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { R as ResolvedConfig, s as SupportNamespace } from './types-BA53dd8S.js';
1
+ import { x as ResolvedConfig, z as SupportNamespace } from './types-CQAfwqUS.js';
2
2
 
3
3
  declare function createSupportNamespace(config: ResolvedConfig): SupportNamespace;
4
4
 
package/dist/support.mjs CHANGED
@@ -14,6 +14,31 @@ var NeetruError = class _NeetruError extends Error {
14
14
  };
15
15
 
16
16
  // src/http.ts
17
+ var DEFAULT_RETRIES = 2;
18
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set([
19
+ "rate_limited",
20
+ "server_error",
21
+ "network_error"
22
+ ]);
23
+ function backoffMs(attempt) {
24
+ const base = 200 * Math.pow(4, attempt);
25
+ const jitter = base * 0.2 * (Math.random() * 2 - 1);
26
+ return Math.max(50, Math.round(base + jitter));
27
+ }
28
+ function parseRetryAfter(value) {
29
+ if (!value) return null;
30
+ const secs = Number(value);
31
+ if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1e3);
32
+ const dateMs = Date.parse(value);
33
+ if (Number.isFinite(dateMs)) {
34
+ const delta = dateMs - Date.now();
35
+ if (delta > 0) return delta;
36
+ }
37
+ return null;
38
+ }
39
+ function sleep(ms) {
40
+ return new Promise((resolve) => setTimeout(resolve, ms));
41
+ }
17
42
  function statusToCode(status) {
18
43
  if (status === 401) return "unauthorized";
19
44
  if (status === 403) return "forbidden";
@@ -47,6 +72,7 @@ async function safeJson(res) {
47
72
  async function httpRequest(config, opts) {
48
73
  const method = opts.method ?? "GET";
49
74
  const url = buildUrl(config.baseUrl, opts.path, opts.query);
75
+ const maxRetries = opts.retries ?? DEFAULT_RETRIES;
50
76
  const headers = {
51
77
  accept: "application/json",
52
78
  ...opts.headers
@@ -60,24 +86,32 @@ async function httpRequest(config, opts) {
60
86
  }
61
87
  headers.authorization = `Bearer ${config.apiKey}`;
62
88
  }
63
- const init = { method, headers };
64
- if (opts.body !== void 0 && method !== "GET" && method !== "DELETE") {
89
+ const bodyString = opts.body !== void 0 && method !== "GET" && method !== "DELETE" ? JSON.stringify(opts.body) : void 0;
90
+ if (bodyString !== void 0) {
65
91
  headers["content-type"] = "application/json";
66
- init.body = JSON.stringify(opts.body);
67
92
  }
68
- init.signal = AbortSignal.timeout(3e4);
69
- let res;
70
- try {
71
- res = await config.fetch(url, init);
72
- } catch (err) {
73
- if (err instanceof DOMException && err.name === "TimeoutError") {
74
- throw new NeetruError("network_error", "Network error: timeout after 30s");
93
+ let lastError = null;
94
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
95
+ const init = { method, headers };
96
+ if (bodyString !== void 0) init.body = bodyString;
97
+ init.signal = AbortSignal.timeout(3e4);
98
+ let res;
99
+ try {
100
+ res = await config.fetch(url, init);
101
+ } catch (err2) {
102
+ const message2 = err2 instanceof DOMException && err2.name === "TimeoutError" ? "Network error: timeout after 30s" : `Network error: ${err2 instanceof Error ? err2.message : "fetch failed"}`;
103
+ lastError = new NeetruError("network_error", message2);
104
+ if (attempt < maxRetries) {
105
+ await sleep(backoffMs(attempt));
106
+ continue;
107
+ }
108
+ throw lastError;
109
+ }
110
+ const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
111
+ if (res.ok) {
112
+ const parsed = await safeJson(res);
113
+ return parsed;
75
114
  }
76
- const message = err instanceof Error ? err.message : "fetch failed";
77
- throw new NeetruError("network_error", `Network error: ${message}`);
78
- }
79
- const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
80
- if (!res.ok) {
81
115
  const body = await safeJson(res);
82
116
  let code = statusToCode(res.status);
83
117
  let message = `HTTP ${res.status}`;
@@ -90,10 +124,18 @@ async function httpRequest(config, opts) {
90
124
  if (typeof errField.message === "string") message = errField.message;
91
125
  }
92
126
  }
93
- throw new NeetruError(code, message, res.status, requestId);
127
+ const err = new NeetruError(code, message, res.status, requestId);
128
+ lastError = err;
129
+ const isRetryable = RETRYABLE_CODES.has(code);
130
+ if (isRetryable && attempt < maxRetries) {
131
+ const retryAfter = parseRetryAfter(res.headers.get("retry-after"));
132
+ const delay = retryAfter ?? backoffMs(attempt);
133
+ await sleep(delay);
134
+ continue;
135
+ }
136
+ throw err;
94
137
  }
95
- const parsed = await safeJson(res);
96
- return parsed;
138
+ throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
97
139
  }
98
140
 
99
141
  // src/support.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/support.ts"],"names":[],"mappings":"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","file":"support.mjs","sourcesContent":["/**\n * Erros tipados do SDK.\n *\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\n * por `.code` (string estável) sem parsing de message.\n */\n\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\nexport type NeetruErrorCode =\n | 'invalid_config'\n | 'missing_api_key'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'rate_limited'\n | 'validation_failed'\n | 'network_error'\n | 'invalid_response'\n | 'server_error'\n | 'unknown';\n\n/**\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\n *\n * @example\n * ```ts\n * try { await client.catalog.list(); }\n * catch (e) {\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\n * }\n * ```\n */\nexport class NeetruError extends Error {\n public readonly code: NeetruErrorCode | string;\n public readonly status?: number;\n public readonly requestId?: string;\n\n constructor(\n code: NeetruErrorCode | string,\n message: string,\n status?: number,\n requestId?: string,\n ) {\n super(message);\n this.name = 'NeetruError';\n this.code = code;\n this.status = status;\n this.requestId = requestId;\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\n Object.setPrototypeOf(this, NeetruError.prototype);\n }\n}\n","/**\n * HTTP transport interno do SDK.\n *\n * Responsabilidades:\n * - Construir URL absoluta a partir de `baseUrl` + path\n * - Injetar Bearer token quando `requireAuth=true`\n * - Mapear status HTTP → `NeetruError` com `code` estável\n * - Parse defensivo de JSON (não lança em body vazio em 204)\n *\n * Não faz retry/backoff em v0.1 (carry-over Sprint 3+).\n */\nimport { NeetruError, type NeetruErrorCode } from './errors';\nimport type { ResolvedConfig } from './types';\n\n/** Opções da request HTTP. */\nexport interface HttpRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\n path: string;\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\n query?: Record<string, string | number | boolean | undefined>;\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\n body?: unknown;\n /**\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\n * se config.apiKey ausente. Default false.\n */\n requireAuth?: boolean;\n /** Cabeçalhos extras. */\n headers?: Record<string, string>;\n}\n\n/** Mapeamento status → code estável do NeetruError. */\nfunction statusToCode(status: number): NeetruErrorCode {\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 422 || status === 400) return 'validation_failed';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'server_error';\n return 'unknown';\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\n // Trim trailing slash em base e leading em path pra evitar `//`.\n const base = baseUrl.replace(/\\/+$/, '');\n const p = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(`${base}${p}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\nasync function safeJson(res: Response): Promise<unknown> {\n const text = await res.text();\n if (!text) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\n * `NeetruError` com `code` derivado do status.\n */\nexport async function httpRequest<T>(\n config: ResolvedConfig,\n opts: HttpRequestOptions,\n): Promise<T> {\n const method = opts.method ?? 'GET';\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\n\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...opts.headers,\n };\n\n if (opts.requireAuth) {\n if (!config.apiKey) {\n throw new NeetruError(\n 'missing_api_key',\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\n );\n }\n headers.authorization = `Bearer ${config.apiKey}`;\n }\n\n const init: RequestInit = { method, headers };\n if (opts.body !== undefined && method !== 'GET' && method !== 'DELETE') {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(opts.body);\n }\n\n // BUG-020 fix (2026-05-13): timeout default 30s. Sem isso, em SSR/ISR do\n // Next.js o request pode travar a request inteira; em build estático\n // bloqueia o pipeline. AbortSignal.timeout requer Node 18+ ou browsers\n // recentes — alvo do SDK é Node 18+ e Chrome/Firefox/Safari modernos.\n init.signal = AbortSignal.timeout(30_000);\n\n let res: Response;\n try {\n res = await config.fetch(url, init);\n } catch (err) {\n if (err instanceof DOMException && err.name === 'TimeoutError') {\n throw new NeetruError('network_error', 'Network error: timeout after 30s');\n }\n const message = err instanceof Error ? err.message : 'fetch failed';\n throw new NeetruError('network_error', `Network error: ${message}`);\n }\n\n const requestId = res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\n\n if (!res.ok) {\n const body = (await safeJson(res)) as { error?: { code?: string; message?: string } | string } | undefined;\n let code: string = statusToCode(res.status);\n let message = `HTTP ${res.status}`;\n if (body && typeof body === 'object' && 'error' in body) {\n const errField = body.error;\n if (typeof errField === 'string') {\n message = errField;\n } else if (errField && typeof errField === 'object') {\n if (typeof errField.code === 'string') code = errField.code;\n if (typeof errField.message === 'string') message = errField.message;\n }\n }\n throw new NeetruError(code, message, res.status, requestId);\n }\n\n const parsed = await safeJson(res);\n // Caller é responsável por validar shape; SDK assume backend correto.\n return parsed as T;\n}\n","/**\n * Support namespace — criar e listar tickets do customer (v0.2).\n *\n * Endpoints (em prod):\n * - `POST /api/v1/products/{slug}/tickets` — criar ticket\n * - `GET /api/v1/products/{slug}/tickets` — listar meus tickets do produto\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n *\n * Decisão: severity default é `normal`. `low` indica feature requests; `urgent`\n * pra outages production (paginating ops imediatamente em prod).\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport type {\n CreateTicketInput,\n ResolvedConfig,\n SupportNamespace,\n SupportSeverity,\n SupportStatus,\n SupportTicket,\n} from './types';\n\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\n\ninterface RawTicket {\n id?: string;\n subject?: string;\n message?: string;\n severity?: string;\n status?: string;\n createdAt?: string;\n updatedAt?: string;\n productSlug?: string;\n}\n\nfunction toTicket(raw: unknown): SupportTicket {\n if (!raw || typeof raw !== 'object') {\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\n }\n const r = raw as RawTicket;\n if (typeof r.id !== 'string') {\n throw new NeetruError('invalid_response', 'Ticket missing id');\n }\n return {\n id: r.id,\n subject: typeof r.subject === 'string' ? r.subject : '',\n message: typeof r.message === 'string' ? r.message : '',\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\n ? (r.severity as SupportSeverity)\n : 'normal',\n status: VALID_STATUSES.includes(r.status as SupportStatus)\n ? (r.status as SupportStatus)\n : 'open',\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\n productSlug: typeof r.productSlug === 'string' ? r.productSlug : undefined,\n };\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. Se `productSlug` não é\n * passado, o backend infere do escopo do token.\n */\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input || typeof input !== 'object') {\n throw new NeetruError('validation_failed', 'input is required');\n }\n if (!input.subject || typeof input.subject !== 'string') {\n throw new NeetruError('validation_failed', 'subject is required');\n }\n if (input.subject.length > 200) {\n throw new NeetruError('validation_failed', 'subject max 200 chars');\n }\n if (!input.message || typeof input.message !== 'string') {\n throw new NeetruError('validation_failed', 'message is required');\n }\n if (input.message.length > 10_000) {\n throw new NeetruError('validation_failed', 'message max 10000 chars');\n }\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\n throw new NeetruError('validation_failed', `severity must be one of ${VALID_SEVERITIES.join(', ')}`);\n }\n\n const slug = input.productSlug ?? '_default';\n const body: Record<string, unknown> = {\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: `/api/v1/products/${encodeURIComponent(slug)}/tickets`,\n body,\n requireAuth: true,\n });\n\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\n const candidate =\n raw && typeof raw === 'object' && 'ticket' in raw\n ? (raw as { ticket?: RawTicket }).ticket\n : raw;\n return toTicket(candidate);\n },\n\n /**\n * Lista tickets do customer no produto atual (escopo do token).\n */\n async listMyTickets(): Promise<SupportTicket[]> {\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/v1/products/_default/tickets',\n requireAuth: true,\n });\n const list = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && 'tickets' in raw ? (raw as { tickets?: unknown[] }).tickets ?? [] : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/support.ts"],"names":["err","message"],"mappings":"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","file":"support.mjs","sourcesContent":["/**\n * Erros tipados do SDK.\n *\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\n * por `.code` (string estável) sem parsing de message.\n */\n\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\nexport type NeetruErrorCode =\n | 'invalid_config'\n | 'missing_api_key'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'rate_limited'\n | 'validation_failed'\n | 'network_error'\n | 'invalid_response'\n | 'server_error'\n | 'unknown';\n\n/**\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\n *\n * @example\n * ```ts\n * try { await client.catalog.list(); }\n * catch (e) {\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\n * }\n * ```\n */\nexport class NeetruError extends Error {\n public readonly code: NeetruErrorCode | string;\n public readonly status?: number;\n public readonly requestId?: string;\n\n constructor(\n code: NeetruErrorCode | string,\n message: string,\n status?: number,\n requestId?: string,\n ) {\n super(message);\n this.name = 'NeetruError';\n this.code = code;\n this.status = status;\n this.requestId = requestId;\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\n Object.setPrototypeOf(this, NeetruError.prototype);\n }\n}\n","/**\n * HTTP transport interno do SDK.\n *\n * Responsabilidades:\n * - Construir URL absoluta a partir de `baseUrl` + path\n * - Injetar Bearer token quando `requireAuth=true`\n * - Mapear status HTTP → `NeetruError` com `code` estável\n * - Parse defensivo de JSON (não lança em body vazio em 204)\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\n * presente. Default 2 retries (3 tentativas no total). Caller opta-out\n * com `retries: 0`.\n */\nimport { NeetruError, type NeetruErrorCode } from './errors';\nimport type { ResolvedConfig } from './types';\n\n/** Opções da request HTTP. */\nexport interface HttpRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\n path: string;\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\n query?: Record<string, string | number | boolean | undefined>;\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\n body?: unknown;\n /**\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\n * se config.apiKey ausente. Default false.\n */\n requireAuth?: boolean;\n /** Cabeçalhos extras. */\n headers?: Record<string, string>;\n /**\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\n * `network_error`). Default 2 (= 3 tentativas total). Caller passa `0` pra\n * desativar (útil em operações não-idempotentes específicas).\n */\n retries?: number;\n}\n\nconst DEFAULT_RETRIES = 2;\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\n 'rate_limited',\n 'server_error',\n 'network_error',\n]);\n\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\nfunction backoffMs(attempt: number): number {\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\n return Math.max(50, Math.round(base + jitter));\n}\n\n/**\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\n * inválido. RFC 9110 §10.2.3.\n */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const secs = Number(value);\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\n const dateMs = Date.parse(value);\n if (Number.isFinite(dateMs)) {\n const delta = dateMs - Date.now();\n if (delta > 0) return delta;\n }\n return null;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Mapeamento status → code estável do NeetruError. */\nfunction statusToCode(status: number): NeetruErrorCode {\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not_found';\n if (status === 422 || status === 400) return 'validation_failed';\n if (status === 429) return 'rate_limited';\n if (status >= 500) return 'server_error';\n return 'unknown';\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\n // Trim trailing slash em base e leading em path pra evitar `//`.\n const base = baseUrl.replace(/\\/+$/, '');\n const p = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(`${base}${p}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\nasync function safeJson(res: Response): Promise<unknown> {\n const text = await res.text();\n if (!text) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\n * automático em códigos transientes (rate_limited/server_error/network_error)\n * conforme `opts.retries` (default 2 = 3 tentativas).\n */\nexport async function httpRequest<T>(\n config: ResolvedConfig,\n opts: HttpRequestOptions,\n): Promise<T> {\n const method = opts.method ?? 'GET';\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\n const maxRetries = opts.retries ?? DEFAULT_RETRIES;\n\n const headers: Record<string, string> = {\n accept: 'application/json',\n ...opts.headers,\n };\n\n if (opts.requireAuth) {\n if (!config.apiKey) {\n throw new NeetruError(\n 'missing_api_key',\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\n );\n }\n headers.authorization = `Bearer ${config.apiKey}`;\n }\n\n // Body só é serializado uma vez — reusado entre tentativas.\n const bodyString =\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\n ? JSON.stringify(opts.body)\n : undefined;\n if (bodyString !== undefined) {\n headers['content-type'] = 'application/json';\n }\n\n let lastError: NeetruError | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const init: RequestInit = { method, headers };\n if (bodyString !== undefined) init.body = bodyString;\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\n // requer Node 18+ ou browsers recentes.\n init.signal = AbortSignal.timeout(30_000);\n\n let res: Response;\n try {\n res = await config.fetch(url, init);\n } catch (err) {\n const message =\n err instanceof DOMException && err.name === 'TimeoutError'\n ? 'Network error: timeout after 30s'\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\n lastError = new NeetruError('network_error', message);\n if (attempt < maxRetries) {\n await sleep(backoffMs(attempt));\n continue;\n }\n throw lastError;\n }\n\n const requestId =\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\n\n if (res.ok) {\n const parsed = await safeJson(res);\n return parsed as T;\n }\n\n const body = (await safeJson(res)) as\n | { error?: { code?: string; message?: string } | string }\n | undefined;\n let code: string = statusToCode(res.status);\n let message = `HTTP ${res.status}`;\n if (body && typeof body === 'object' && 'error' in body) {\n const errField = body.error;\n if (typeof errField === 'string') {\n message = errField;\n } else if (errField && typeof errField === 'object') {\n if (typeof errField.code === 'string') code = errField.code;\n if (typeof errField.message === 'string') message = errField.message;\n }\n }\n const err = new NeetruError(code, message, res.status, requestId);\n lastError = err;\n\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\n if (isRetryable && attempt < maxRetries) {\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const delay = retryAfter ?? backoffMs(attempt);\n await sleep(delay);\n continue;\n }\n throw err;\n }\n\n // Unreachable — loop sempre retorna ou lança. Safety net.\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\n}\n","/**\n * Support namespace — criar e listar tickets do customer (v0.2).\n *\n * Endpoints (em prod):\n * - `POST /api/v1/products/{slug}/tickets` — criar ticket\n * - `GET /api/v1/products/{slug}/tickets` — listar meus tickets do produto\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n *\n * Decisão: severity default é `normal`. `low` indica feature requests; `urgent`\n * pra outages production (paginating ops imediatamente em prod).\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport type {\n CreateTicketInput,\n ResolvedConfig,\n SupportNamespace,\n SupportSeverity,\n SupportStatus,\n SupportTicket,\n} from './types';\n\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\n\ninterface RawTicket {\n id?: string;\n subject?: string;\n message?: string;\n severity?: string;\n status?: string;\n createdAt?: string;\n updatedAt?: string;\n productSlug?: string;\n}\n\nfunction toTicket(raw: unknown): SupportTicket {\n if (!raw || typeof raw !== 'object') {\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\n }\n const r = raw as RawTicket;\n if (typeof r.id !== 'string') {\n throw new NeetruError('invalid_response', 'Ticket missing id');\n }\n return {\n id: r.id,\n subject: typeof r.subject === 'string' ? r.subject : '',\n message: typeof r.message === 'string' ? r.message : '',\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\n ? (r.severity as SupportSeverity)\n : 'normal',\n status: VALID_STATUSES.includes(r.status as SupportStatus)\n ? (r.status as SupportStatus)\n : 'open',\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\n productSlug: typeof r.productSlug === 'string' ? r.productSlug : undefined,\n };\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. Se `productSlug` não é\n * passado, o backend infere do escopo do token.\n */\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input || typeof input !== 'object') {\n throw new NeetruError('validation_failed', 'input is required');\n }\n if (!input.subject || typeof input.subject !== 'string') {\n throw new NeetruError('validation_failed', 'subject is required');\n }\n if (input.subject.length > 200) {\n throw new NeetruError('validation_failed', 'subject max 200 chars');\n }\n if (!input.message || typeof input.message !== 'string') {\n throw new NeetruError('validation_failed', 'message is required');\n }\n if (input.message.length > 10_000) {\n throw new NeetruError('validation_failed', 'message max 10000 chars');\n }\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\n throw new NeetruError('validation_failed', `severity must be one of ${VALID_SEVERITIES.join(', ')}`);\n }\n\n const slug = input.productSlug ?? '_default';\n const body: Record<string, unknown> = {\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: `/api/v1/products/${encodeURIComponent(slug)}/tickets`,\n body,\n requireAuth: true,\n });\n\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\n const candidate =\n raw && typeof raw === 'object' && 'ticket' in raw\n ? (raw as { ticket?: RawTicket }).ticket\n : raw;\n return toTicket(candidate);\n },\n\n /**\n * Lista tickets do customer no produto atual (escopo do token).\n */\n async listMyTickets(): Promise<SupportTicket[]> {\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/v1/products/_default/tickets',\n requireAuth: true,\n });\n const list = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && 'tickets' in raw ? (raw as { tickets?: unknown[] }).tickets ?? [] : [];\n return list.map(toTicket);\n },\n };\n}\n"]}