@neetru/sdk 2.3.6 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +185 -0
- package/README.md +84 -22
- package/dist/auth.cjs +360 -140
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -2
- package/dist/auth.d.ts +1 -2
- package/dist/auth.mjs +360 -140
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.cjs +21 -2
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +1 -2
- package/dist/catalog.d.ts +1 -2
- package/dist/catalog.mjs +21 -2
- package/dist/catalog.mjs.map +1 -1
- package/dist/checkout.cjs +21 -2
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +1 -2
- package/dist/checkout.d.ts +1 -2
- package/dist/checkout.mjs +21 -2
- package/dist/checkout.mjs.map +1 -1
- package/dist/db.cjs +28 -3
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +1 -2
- package/dist/db.d.ts +1 -2
- package/dist/db.mjs +28 -3
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.cjs +21 -2
- package/dist/entitlements.cjs.map +1 -1
- package/dist/entitlements.d.cts +1 -2
- package/dist/entitlements.d.ts +1 -2
- package/dist/entitlements.mjs +21 -2
- package/dist/entitlements.mjs.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +3 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/index.cjs +408 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -20
- package/dist/index.d.ts +31 -20
- package/dist/index.mjs +408 -146
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.cjs +33 -33
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.cts +23 -20
- package/dist/mocks.d.ts +23 -20
- package/dist/mocks.mjs +33 -33
- package/dist/mocks.mjs.map +1 -1
- package/dist/notifications.cjs +80 -14
- package/dist/notifications.cjs.map +1 -1
- package/dist/notifications.d.cts +1 -2
- package/dist/notifications.d.ts +1 -2
- package/dist/notifications.mjs +80 -14
- package/dist/notifications.mjs.map +1 -1
- package/dist/react.d.cts +1 -2
- package/dist/react.d.ts +1 -2
- package/dist/support.cjs +72 -12
- package/dist/support.cjs.map +1 -1
- package/dist/support.d.cts +1 -2
- package/dist/support.d.ts +1 -2
- package/dist/support.mjs +72 -12
- package/dist/support.mjs.map +1 -1
- package/dist/telemetry.cjs +23 -4
- package/dist/telemetry.cjs.map +1 -1
- package/dist/telemetry.d.cts +1 -2
- package/dist/telemetry.d.ts +1 -2
- package/dist/telemetry.mjs +23 -4
- package/dist/telemetry.mjs.map +1 -1
- package/dist/{types-CSC-RIdS.d.ts → types-DALIhcbq.d.ts} +130 -74
- package/dist/{types-sGGN1vkg.d.cts → types-Lfd3LiAF.d.cts} +130 -74
- package/dist/usage.cjs +57 -63
- package/dist/usage.cjs.map +1 -1
- package/dist/usage.d.cts +1 -2
- package/dist/usage.d.ts +1 -2
- package/dist/usage.mjs +57 -63
- package/dist/usage.mjs.map +1 -1
- package/dist/webhooks.cjs +110 -33
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +1 -2
- package/dist/webhooks.d.ts +1 -2
- package/dist/webhooks.mjs +110 -33
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +2 -3
package/dist/support.cjs
CHANGED
|
@@ -15,8 +15,22 @@ var NeetruError = class _NeetruError extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/idempotency.ts
|
|
19
|
+
function generateIdempotencyKey() {
|
|
20
|
+
const cryptoObj = typeof globalThis !== "undefined" && "crypto" in globalThis ? globalThis.crypto : void 0;
|
|
21
|
+
if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
|
|
22
|
+
return cryptoObj.randomUUID();
|
|
23
|
+
}
|
|
24
|
+
throw new NeetruError(
|
|
25
|
+
"runtime_unsupported",
|
|
26
|
+
"crypto.randomUUID not available in this runtime. SDK 3.0 requires Node \u226520 or modern browser/Edge."
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
// src/http.ts
|
|
19
|
-
var
|
|
31
|
+
var DEFAULT_RETRIES_READ = 2;
|
|
32
|
+
var DEFAULT_RETRIES_WRITE = 0;
|
|
33
|
+
var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
20
34
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
21
35
|
"rate_limited",
|
|
22
36
|
"server_error",
|
|
@@ -74,7 +88,8 @@ async function safeJson(res) {
|
|
|
74
88
|
async function httpRequest(config, opts) {
|
|
75
89
|
const method = opts.method ?? "GET";
|
|
76
90
|
const url = buildUrl(config.baseUrl, opts.path, opts.query);
|
|
77
|
-
const
|
|
91
|
+
const defaultRetries = READ_METHODS.has(method) ? DEFAULT_RETRIES_READ : DEFAULT_RETRIES_WRITE;
|
|
92
|
+
const maxRetries = opts.retries ?? defaultRetries;
|
|
78
93
|
const headers = {
|
|
79
94
|
accept: "application/json",
|
|
80
95
|
...opts.headers
|
|
@@ -88,6 +103,10 @@ async function httpRequest(config, opts) {
|
|
|
88
103
|
}
|
|
89
104
|
headers.authorization = `Bearer ${config.apiKey}`;
|
|
90
105
|
}
|
|
106
|
+
if (opts.idempotencyKey) {
|
|
107
|
+
const key = typeof opts.idempotencyKey === "string" ? opts.idempotencyKey : generateIdempotencyKey();
|
|
108
|
+
headers["idempotency-key"] = key;
|
|
109
|
+
}
|
|
91
110
|
const bodyString = opts.body !== void 0 && method !== "GET" && method !== "DELETE" ? JSON.stringify(opts.body) : void 0;
|
|
92
111
|
if (bodyString !== void 0) {
|
|
93
112
|
headers["content-type"] = "application/json";
|
|
@@ -140,6 +159,17 @@ async function httpRequest(config, opts) {
|
|
|
140
159
|
throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
|
|
141
160
|
}
|
|
142
161
|
|
|
162
|
+
// src/validators.ts
|
|
163
|
+
var SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
164
|
+
function assertValidSdkId(field, value) {
|
|
165
|
+
if (typeof value !== "string" || !SDK_ID_RE.test(value)) {
|
|
166
|
+
throw new NeetruError(
|
|
167
|
+
"validation_failed",
|
|
168
|
+
`${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
143
173
|
// src/support.ts
|
|
144
174
|
var VALID_SEVERITIES = ["low", "normal", "high", "urgent"];
|
|
145
175
|
var VALID_STATUSES = ["open", "pending", "resolved", "closed"];
|
|
@@ -159,14 +189,32 @@ function toTicket(raw) {
|
|
|
159
189
|
status: VALID_STATUSES.includes(r.status) ? r.status : "open",
|
|
160
190
|
createdAt: typeof r.createdAt === "string" ? r.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
161
191
|
updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : void 0,
|
|
162
|
-
productSlug: typeof r.productSlug === "string" ? r.productSlug : void 0
|
|
192
|
+
productSlug: typeof r.productId === "string" ? r.productId : typeof r.productSlug === "string" ? r.productSlug : void 0
|
|
163
193
|
};
|
|
164
194
|
}
|
|
195
|
+
function resolveProductId(config, ...candidates) {
|
|
196
|
+
for (const c of candidates) {
|
|
197
|
+
if (typeof c === "string" && c.length > 0) {
|
|
198
|
+
assertValidSdkId("productSlug", c);
|
|
199
|
+
return c;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (config.productId) {
|
|
203
|
+
assertValidSdkId("productId", config.productId);
|
|
204
|
+
return config.productId;
|
|
205
|
+
}
|
|
206
|
+
throw new NeetruError(
|
|
207
|
+
"validation_failed",
|
|
208
|
+
"productId required (pass to input.productSlug or set on createNeetruClient)"
|
|
209
|
+
);
|
|
210
|
+
}
|
|
165
211
|
function createSupportNamespace(config) {
|
|
166
212
|
return {
|
|
167
213
|
/**
|
|
168
|
-
* Cria um ticket de suporte. Requer Bearer auth.
|
|
169
|
-
*
|
|
214
|
+
* Cria um ticket de suporte. Requer Bearer auth. `productSlug` no input
|
|
215
|
+
* (ou `config.productId` default) vai como `productId` no body do Core.
|
|
216
|
+
*
|
|
217
|
+
* Endpoint canonical: `POST /api/sdk/v1/support/tickets`.
|
|
170
218
|
*/
|
|
171
219
|
async createTicket(input) {
|
|
172
220
|
if (!input || typeof input !== "object") {
|
|
@@ -185,30 +233,42 @@ function createSupportNamespace(config) {
|
|
|
185
233
|
throw new NeetruError("validation_failed", "message max 10000 chars");
|
|
186
234
|
}
|
|
187
235
|
if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {
|
|
188
|
-
throw new NeetruError(
|
|
236
|
+
throw new NeetruError(
|
|
237
|
+
"validation_failed",
|
|
238
|
+
`severity must be one of ${VALID_SEVERITIES.join(", ")}`
|
|
239
|
+
);
|
|
189
240
|
}
|
|
190
|
-
const
|
|
241
|
+
const productId = resolveProductId(config, input.productSlug);
|
|
191
242
|
const body = {
|
|
243
|
+
productId,
|
|
192
244
|
subject: input.subject,
|
|
193
245
|
message: input.message,
|
|
194
246
|
severity: input.severity ?? "normal"
|
|
195
247
|
};
|
|
248
|
+
if (input.metadata && typeof input.metadata === "object") {
|
|
249
|
+
body.metadata = input.metadata;
|
|
250
|
+
}
|
|
196
251
|
const raw = await httpRequest(config, {
|
|
197
252
|
method: "POST",
|
|
198
|
-
path:
|
|
253
|
+
path: "/api/sdk/v1/support/tickets",
|
|
199
254
|
body,
|
|
200
|
-
requireAuth: true
|
|
255
|
+
requireAuth: true,
|
|
256
|
+
idempotencyKey: true
|
|
201
257
|
});
|
|
202
258
|
const candidate = raw && typeof raw === "object" && "ticket" in raw ? raw.ticket : raw;
|
|
203
259
|
return toTicket(candidate);
|
|
204
260
|
},
|
|
205
261
|
/**
|
|
206
|
-
* Lista tickets do customer no produto atual
|
|
262
|
+
* Lista tickets do customer no produto atual. `productSlug` no contexto.
|
|
263
|
+
*
|
|
264
|
+
* Endpoint canonical: `GET /api/sdk/v1/support/tickets?productId=...`.
|
|
207
265
|
*/
|
|
208
|
-
async listMyTickets() {
|
|
266
|
+
async listMyTickets(options) {
|
|
267
|
+
const productId = resolveProductId(config, options?.productSlug);
|
|
209
268
|
const raw = await httpRequest(config, {
|
|
210
269
|
method: "GET",
|
|
211
|
-
path: "/api/v1/
|
|
270
|
+
path: "/api/sdk/v1/support/tickets",
|
|
271
|
+
query: { productId },
|
|
212
272
|
requireAuth: true
|
|
213
273
|
});
|
|
214
274
|
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && "tickets" in raw ? raw.tickets ?? [] : [];
|
package/dist/support.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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":["/**\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 | '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 constructor(\r\n code: NeetruErrorCode | string,\r\n message: string,\r\n status?: number,\r\n requestId?: 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 // 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 * HTTP transport interno do SDK.\r\n *\r\n * Responsabilidades:\r\n * - Construir URL absoluta a partir de `baseUrl` + path\r\n * - Injetar Bearer token quando `requireAuth=true`\r\n * - Mapear status HTTP → `NeetruError` com `code` estável\r\n * - Parse defensivo de JSON (não lança em body vazio em 204)\r\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\r\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\r\n * presente. Default 2 retries (3 tentativas no total). Caller opta-out\r\n * com `retries: 0`.\r\n */\r\nimport { NeetruError, type NeetruErrorCode } from './errors';\r\nimport type { ResolvedConfig } from './types';\r\n\r\n/** Opções da request HTTP. */\r\nexport interface HttpRequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\r\n path: string;\r\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\r\n query?: Record<string, string | number | boolean | undefined>;\r\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\r\n body?: unknown;\r\n /**\r\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\r\n * se config.apiKey ausente. Default false.\r\n */\r\n requireAuth?: boolean;\r\n /** Cabeçalhos extras. */\r\n headers?: Record<string, string>;\r\n /**\r\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\r\n * `network_error`). Default 2 (= 3 tentativas total). Caller passa `0` pra\r\n * desativar (útil em operações não-idempotentes específicas).\r\n */\r\n retries?: number;\r\n}\r\n\r\nconst DEFAULT_RETRIES = 2;\r\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\r\n 'rate_limited',\r\n 'server_error',\r\n 'network_error',\r\n]);\r\n\r\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\r\nfunction backoffMs(attempt: number): number {\r\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\r\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\r\n return Math.max(50, Math.round(base + jitter));\r\n}\r\n\r\n/**\r\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\r\n * inválido. RFC 9110 §10.2.3.\r\n */\r\nfunction parseRetryAfter(value: string | null): number | null {\r\n if (!value) return null;\r\n const secs = Number(value);\r\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\r\n const dateMs = Date.parse(value);\r\n if (Number.isFinite(dateMs)) {\r\n const delta = dateMs - Date.now();\r\n if (delta > 0) return delta;\r\n }\r\n return null;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/** Mapeamento status → code estável do NeetruError. */\r\nfunction statusToCode(status: number): NeetruErrorCode {\r\n if (status === 401) return 'unauthorized';\r\n if (status === 403) return 'forbidden';\r\n if (status === 404) return 'not_found';\r\n if (status === 422 || status === 400) return 'validation_failed';\r\n if (status === 429) return 'rate_limited';\r\n if (status >= 500) return 'server_error';\r\n return 'unknown';\r\n}\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\r\n // Trim trailing slash em base e leading em path pra evitar `//`.\r\n const base = baseUrl.replace(/\\/+$/, '');\r\n const p = path.startsWith('/') ? path : `/${path}`;\r\n const url = new URL(`${base}${p}`);\r\n if (query) {\r\n for (const [k, v] of Object.entries(query)) {\r\n if (v === undefined) continue;\r\n url.searchParams.set(k, String(v));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\r\nasync function safeJson(res: Response): Promise<unknown> {\r\n const text = await res.text();\r\n if (!text) return undefined;\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\r\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\r\n * automático em códigos transientes (rate_limited/server_error/network_error)\r\n * conforme `opts.retries` (default 2 = 3 tentativas).\r\n */\r\nexport async function httpRequest<T>(\r\n config: ResolvedConfig,\r\n opts: HttpRequestOptions,\r\n): Promise<T> {\r\n const method = opts.method ?? 'GET';\r\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\r\n const maxRetries = opts.retries ?? DEFAULT_RETRIES;\r\n\r\n const headers: Record<string, string> = {\r\n accept: 'application/json',\r\n ...opts.headers,\r\n };\r\n\r\n if (opts.requireAuth) {\r\n if (!config.apiKey) {\r\n throw new NeetruError(\r\n 'missing_api_key',\r\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\r\n );\r\n }\r\n headers.authorization = `Bearer ${config.apiKey}`;\r\n }\r\n\r\n // Body só é serializado uma vez — reusado entre tentativas.\r\n const bodyString =\r\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\r\n ? JSON.stringify(opts.body)\r\n : undefined;\r\n if (bodyString !== undefined) {\r\n headers['content-type'] = 'application/json';\r\n }\r\n\r\n let lastError: NeetruError | null = null;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n const init: RequestInit = { method, headers };\r\n if (bodyString !== undefined) init.body = bodyString;\r\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\r\n // requer Node 18+ ou browsers recentes.\r\n init.signal = AbortSignal.timeout(30_000);\r\n\r\n let res: Response;\r\n try {\r\n res = await config.fetch(url, init);\r\n } catch (err) {\r\n const message =\r\n err instanceof DOMException && err.name === 'TimeoutError'\r\n ? 'Network error: timeout after 30s'\r\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\r\n lastError = new NeetruError('network_error', message);\r\n if (attempt < maxRetries) {\r\n await sleep(backoffMs(attempt));\r\n continue;\r\n }\r\n throw lastError;\r\n }\r\n\r\n const requestId =\r\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\r\n\r\n if (res.ok) {\r\n const parsed = await safeJson(res);\r\n return parsed as T;\r\n }\r\n\r\n const body = (await safeJson(res)) as\r\n | { error?: { code?: string; message?: string } | string }\r\n | undefined;\r\n let code: string = statusToCode(res.status);\r\n let message = `HTTP ${res.status}`;\r\n if (body && typeof body === 'object' && 'error' in body) {\r\n const errField = body.error;\r\n if (typeof errField === 'string') {\r\n message = errField;\r\n } else if (errField && typeof errField === 'object') {\r\n if (typeof errField.code === 'string') code = errField.code;\r\n if (typeof errField.message === 'string') message = errField.message;\r\n }\r\n }\r\n const err = new NeetruError(code, message, res.status, requestId);\r\n lastError = err;\r\n\r\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\r\n if (isRetryable && attempt < maxRetries) {\r\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\r\n const delay = retryAfter ?? backoffMs(attempt);\r\n await sleep(delay);\r\n continue;\r\n }\r\n throw err;\r\n }\r\n\r\n // Unreachable — loop sempre retorna ou lança. Safety net.\r\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\r\n}\r\n","/**\r\n * Support namespace — criar e listar tickets do customer (v0.2).\r\n *\r\n * Endpoints (em prod):\r\n * - `POST /api/v1/products/{slug}/tickets` — criar ticket\r\n * - `GET /api/v1/products/{slug}/tickets` — listar meus tickets do produto\r\n *\r\n * Em dev (mocks ativos via factory) tudo é in-memory.\r\n *\r\n * Decisão: severity default é `normal`. `low` indica feature requests; `urgent`\r\n * pra outages production (paginating ops imediatamente em prod).\r\n */\r\nimport { NeetruError } from './errors';\r\nimport { httpRequest } from './http';\r\nimport type {\r\n CreateTicketInput,\r\n ResolvedConfig,\r\n SupportNamespace,\r\n SupportSeverity,\r\n SupportStatus,\r\n SupportTicket,\r\n} from './types';\r\n\r\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\r\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\r\n\r\ninterface RawTicket {\r\n id?: string;\r\n subject?: string;\r\n message?: string;\r\n severity?: string;\r\n status?: string;\r\n createdAt?: string;\r\n updatedAt?: string;\r\n productSlug?: string;\r\n}\r\n\r\nfunction toTicket(raw: unknown): SupportTicket {\r\n if (!raw || typeof raw !== 'object') {\r\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\r\n }\r\n const r = raw as RawTicket;\r\n if (typeof r.id !== 'string') {\r\n throw new NeetruError('invalid_response', 'Ticket missing id');\r\n }\r\n return {\r\n id: r.id,\r\n subject: typeof r.subject === 'string' ? r.subject : '',\r\n message: typeof r.message === 'string' ? r.message : '',\r\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\r\n ? (r.severity as SupportSeverity)\r\n : 'normal',\r\n status: VALID_STATUSES.includes(r.status as SupportStatus)\r\n ? (r.status as SupportStatus)\r\n : 'open',\r\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\r\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\r\n productSlug: typeof r.productSlug === 'string' ? r.productSlug : undefined,\r\n };\r\n}\r\n\r\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\r\n return {\r\n /**\r\n * Cria um ticket de suporte. Requer Bearer auth. Se `productSlug` não é\r\n * passado, o backend infere do escopo do token.\r\n */\r\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\r\n if (!input || typeof input !== 'object') {\r\n throw new NeetruError('validation_failed', 'input is required');\r\n }\r\n if (!input.subject || typeof input.subject !== 'string') {\r\n throw new NeetruError('validation_failed', 'subject is required');\r\n }\r\n if (input.subject.length > 200) {\r\n throw new NeetruError('validation_failed', 'subject max 200 chars');\r\n }\r\n if (!input.message || typeof input.message !== 'string') {\r\n throw new NeetruError('validation_failed', 'message is required');\r\n }\r\n if (input.message.length > 10_000) {\r\n throw new NeetruError('validation_failed', 'message max 10000 chars');\r\n }\r\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\r\n throw new NeetruError('validation_failed', `severity must be one of ${VALID_SEVERITIES.join(', ')}`);\r\n }\r\n\r\n const slug = input.productSlug ?? '_default';\r\n const body: Record<string, unknown> = {\r\n subject: input.subject,\r\n message: input.message,\r\n severity: input.severity ?? 'normal',\r\n };\r\n\r\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\r\n method: 'POST',\r\n path: `/api/v1/products/${encodeURIComponent(slug)}/tickets`,\r\n body,\r\n requireAuth: true,\r\n });\r\n\r\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\r\n const candidate =\r\n raw && typeof raw === 'object' && 'ticket' in raw\r\n ? (raw as { ticket?: RawTicket }).ticket\r\n : raw;\r\n return toTicket(candidate);\r\n },\r\n\r\n /**\r\n * Lista tickets do customer no produto atual (escopo do token).\r\n */\r\n async listMyTickets(): Promise<SupportTicket[]> {\r\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\r\n method: 'GET',\r\n path: '/api/v1/products/_default/tickets',\r\n requireAuth: true,\r\n });\r\n const list = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && 'tickets' in raw ? (raw as { tickets?: unknown[] }).tickets ?? [] : [];\r\n return list.map(toTicket);\r\n },\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/idempotency.ts","../src/http.ts","../src/validators.ts","../src/support.ts"],"names":["err","message"],"mappings":";;;AAsCO,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EACrB,IAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EAEhB,WAAA,CACE,IAAA,EACA,OAAA,EACA,MAAA,EACA,SAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAEjB,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,YAAA,CAAY,SAAS,CAAA;AAAA,EACnD;AACF,CAAA;;;ACvBO,SAAS,sBAAA,GAAiC;AAG/C,EAAA,MAAM,YACJ,OAAO,UAAA,KAAe,eAAe,QAAA,IAAY,UAAA,GAC5C,WAA0D,MAAA,GAC3D,MAAA;AACN,EAAA,IAAI,SAAA,IAAa,OAAO,SAAA,CAAU,UAAA,KAAe,UAAA,EAAY;AAC3D,IAAA,OAAO,UAAU,UAAA,EAAW;AAAA,EAC9B;AACA,EAAA,MAAM,IAAI,WAAA;AAAA,IACR,qBAAA;AAAA,IACA;AAAA,GACF;AACF;;;ACiBA,IAAM,oBAAA,GAAuB,CAAA;AAC7B,IAAM,qBAAA,GAAwB,CAAA;AAC9B,IAAM,+BAAe,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAC5C,IAAM,eAAA,uBAAsB,GAAA,CAAqB;AAAA,EAC/C,cAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF,CAAC,CAAA;AAGD,SAAS,UAAU,OAAA,EAAyB;AAC1C,EAAA,MAAM,IAAA,GAAO,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,OAAO,CAAA;AACtC,EAAA,MAAM,SAAS,IAAA,GAAO,GAAA,IAAO,IAAA,CAAK,MAAA,KAAW,CAAA,GAAI,CAAA,CAAA;AACjD,EAAA,OAAO,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,KAAA,CAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAC/C;AAMA,SAAS,gBAAgB,KAAA,EAAqC;AAC5D,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,MAAM,IAAA,GAAO,OAAO,KAAK,CAAA;AACzB,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,IAAK,IAAA,IAAQ,GAAG,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,GAAI,CAAA;AACrE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC/B,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,EAAG;AAC3B,IAAA,MAAM,KAAA,GAAQ,MAAA,GAAS,IAAA,CAAK,GAAA,EAAI;AAChC,IAAA,IAAI,KAAA,GAAQ,GAAG,OAAO,KAAA;AAAA,EACxB;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAGA,SAAS,aAAa,MAAA,EAAiC;AACrD,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,cAAA;AAC3B,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,WAAA;AAC3B,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,WAAA;AAC3B,EAAA,IAAI,MAAA,KAAW,GAAA,IAAO,MAAA,KAAW,GAAA,EAAK,OAAO,mBAAA;AAC7C,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,cAAA;AAC3B,EAAA,IAAI,MAAA,IAAU,KAAK,OAAO,cAAA;AAC1B,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,QAAA,CAAS,OAAA,EAAiB,IAAA,EAAc,KAAA,EAA6C;AAE5F,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACvC,EAAA,MAAM,IAAI,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,IAAA,GAAO,IAAI,IAAI,CAAA,CAAA;AAChD,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,IAAI,CAAA,EAAG,CAAC,CAAA,CAAE,CAAA;AACjC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC1C,MAAA,IAAI,MAAM,MAAA,EAAW;AACrB,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,IACnC;AAAA,EACF;AACA,EAAA,OAAO,IAAI,QAAA,EAAS;AACtB;AAGA,eAAe,SAAS,GAAA,EAAiC;AACvD,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,EAAA,IAAI,CAAC,MAAM,OAAO,MAAA;AAClB,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAQA,eAAsB,WAAA,CACpB,QACA,IAAA,EACY;AACZ,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,IAAU,KAAA;AAC9B,EAAA,MAAM,MAAM,QAAA,CAAS,MAAA,CAAO,SAAS,IAAA,CAAK,IAAA,EAAM,KAAK,KAAK,CAAA;AAE1D,EAAA,MAAM,cAAA,GAAiB,YAAA,CAAa,GAAA,CAAI,MAAM,IAC1C,oBAAA,GACA,qBAAA;AACJ,EAAA,MAAM,UAAA,GAAa,KAAK,OAAA,IAAW,cAAA;AAEnC,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,MAAA,EAAQ,kBAAA;AAAA,IACR,GAAG,IAAA,CAAK;AAAA,GACV;AAEA,EAAA,IAAI,KAAK,WAAA,EAAa;AACpB,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,MAAM,IAAI,WAAA;AAAA,QACR,iBAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,EACjD;AAGA,EAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,IAAA,MAAM,MACJ,OAAO,IAAA,CAAK,mBAAmB,QAAA,GAC3B,IAAA,CAAK,iBACL,sBAAA,EAAuB;AAC7B,IAAA,OAAA,CAAQ,iBAAiB,CAAA,GAAI,GAAA;AAAA,EAC/B;AAGA,EAAA,MAAM,UAAA,GACJ,IAAA,CAAK,IAAA,KAAS,MAAA,IAAa,MAAA,KAAW,KAAA,IAAS,MAAA,KAAW,QAAA,GACtD,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GACxB,MAAA;AACN,EAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,IAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,kBAAA;AAAA,EAC5B;AAEA,EAAA,IAAI,SAAA,GAAgC,IAAA;AAEpC,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,UAAA,EAAY,OAAA,EAAA,EAAW;AACtD,IAAA,MAAM,IAAA,GAAoB,EAAE,MAAA,EAAQ,OAAA,EAAQ;AAC5C,IAAA,IAAI,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,IAAA,GAAO,UAAA;AAG1C,IAAA,IAAA,CAAK,MAAA,GAAS,WAAA,CAAY,OAAA,CAAQ,GAAM,CAAA;AAExC,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,GAAA,GAAM,MAAM,MAAA,CAAO,KAAA,CAAM,GAAA,EAAK,IAAI,CAAA;AAAA,IACpC,SAASA,IAAAA,EAAK;AACZ,MAAA,MAAMC,QAAAA,GACJD,IAAAA,YAAe,YAAA,IAAgBA,IAAAA,CAAI,IAAA,KAAS,cAAA,GACxC,kCAAA,GACA,CAAA,eAAA,EAAkBA,IAAAA,YAAe,KAAA,GAAQA,IAAAA,CAAI,OAAA,GAAU,cAAc,CAAA,CAAA;AAC3E,MAAA,SAAA,GAAY,IAAI,WAAA,CAAY,eAAA,EAAiBC,QAAO,CAAA;AACpD,MAAA,IAAI,UAAU,UAAA,EAAY;AACxB,QAAA,MAAM,KAAA,CAAM,SAAA,CAAU,OAAO,CAAC,CAAA;AAC9B,QAAA;AAAA,MACF;AACA,MAAA,MAAM,SAAA;AAAA,IACR;AAEA,IAAA,MAAM,SAAA,GACJ,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,cAAc,KAAK,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAA,IAAK,MAAA;AAE5E,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,GAAG,CAAA;AACjC,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,GAAG,CAAA;AAGhC,IAAA,IAAI,IAAA,GAAe,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AAC1C,IAAA,IAAI,OAAA,GAAU,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,CAAA;AAChC,IAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,WAAW,IAAA,EAAM;AACvD,MAAA,MAAM,WAAW,IAAA,CAAK,KAAA;AACtB,MAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,QAAA,OAAA,GAAU,QAAA;AAAA,MACZ,CAAA,MAAA,IAAW,QAAA,IAAY,OAAO,QAAA,KAAa,QAAA,EAAU;AACnD,QAAA,IAAI,OAAO,QAAA,CAAS,IAAA,KAAS,QAAA,SAAiB,QAAA,CAAS,IAAA;AACvD,QAAA,IAAI,OAAO,QAAA,CAAS,OAAA,KAAY,QAAA,YAAoB,QAAA,CAAS,OAAA;AAAA,MAC/D;AAAA,IACF;AACA,IAAA,MAAM,MAAM,IAAI,WAAA,CAAY,MAAM,OAAA,EAAS,GAAA,CAAI,QAAQ,SAAS,CAAA;AAChE,IAAA,SAAA,GAAY,GAAA;AAEZ,IAAA,MAAM,WAAA,GAAc,eAAA,CAAgB,GAAA,CAAI,IAAuB,CAAA;AAC/D,IAAA,IAAI,WAAA,IAAe,UAAU,UAAA,EAAY;AACvC,MAAA,MAAM,aAAa,eAAA,CAAgB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AACjE,MAAA,MAAM,KAAA,GAAQ,UAAA,IAAc,SAAA,CAAU,OAAO,CAAA;AAC7C,MAAA,MAAM,MAAM,KAAK,CAAA;AACjB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,GAAA;AAAA,EACR;AAGA,EAAA,MAAM,SAAA,IAAa,IAAI,WAAA,CAAY,SAAA,EAAW,6BAA6B,CAAA;AAC7E;;;AC1OO,IAAM,SAAA,GAAY,4BAAA;AAYlB,SAAS,gBAAA,CAAiB,OAAe,KAAA,EAAqB;AACnE,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA,EAAG;AACvD,IAAA,MAAM,IAAI,WAAA;AAAA,MACR,mBAAA;AAAA,MACA,CAAA,EAAG,KAAK,CAAA,YAAA,EAAe,SAAA,CAAU,MAAM,CAAA,8EAAA;AAAA,KACzC;AAAA,EACF;AACF;;;ACLA,IAAM,gBAAA,GAA+C,CAAC,KAAA,EAAO,QAAA,EAAU,QAAQ,QAAQ,CAAA;AACvF,IAAM,cAAA,GAA2C,CAAC,MAAA,EAAQ,SAAA,EAAW,YAAY,QAAQ,CAAA;AAczF,SAAS,SAAS,GAAA,EAA6B;AAC7C,EAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,EAAU;AACnC,IAAA,MAAM,IAAI,WAAA,CAAY,kBAAA,EAAoB,kCAAkC,CAAA;AAAA,EAC9E;AACA,EAAA,MAAM,CAAA,GAAI,GAAA;AACV,EAAA,IAAI,OAAO,CAAA,CAAE,EAAA,KAAO,QAAA,EAAU;AAC5B,IAAA,MAAM,IAAI,WAAA,CAAY,kBAAA,EAAoB,mBAAmB,CAAA;AAAA,EAC/D;AACA,EAAA,OAAO;AAAA,IACL,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,SAAS,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,GAAW,EAAE,OAAA,GAAU,EAAA;AAAA,IACrD,SAAS,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,GAAW,EAAE,OAAA,GAAU,EAAA;AAAA,IACrD,UAAU,gBAAA,CAAiB,QAAA,CAAS,EAAE,QAA2B,CAAA,GAC5D,EAAE,QAAA,GACH,QAAA;AAAA,IACJ,QAAQ,cAAA,CAAe,QAAA,CAAS,EAAE,MAAuB,CAAA,GACpD,EAAE,MAAA,GACH,MAAA;AAAA,IACJ,SAAA,EAAW,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,GAAW,EAAE,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IAClF,WAAW,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,GAAW,EAAE,SAAA,GAAY,MAAA;AAAA,IAC3D,WAAA,EACE,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,GACnB,CAAA,CAAE,SAAA,GACF,OAAO,CAAA,CAAE,WAAA,KAAgB,QAAA,GACvB,CAAA,CAAE,WAAA,GACF;AAAA,GACV;AACF;AAEA,SAAS,gBAAA,CACP,WACG,UAAA,EACK;AACR,EAAA,KAAA,MAAW,KAAK,UAAA,EAAY;AAC1B,IAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,SAAS,CAAA,EAAG;AACzC,MAAA,gBAAA,CAAiB,eAAe,CAAC,CAAA;AACjC,MAAA,OAAO,CAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,IAAI,OAAO,SAAA,EAAW;AACpB,IAAA,gBAAA,CAAiB,WAAA,EAAa,OAAO,SAAS,CAAA;AAC9C,IAAA,OAAO,MAAA,CAAO,SAAA;AAAA,EAChB;AACA,EAAA,MAAM,IAAI,WAAA;AAAA,IACR,mBAAA;AAAA,IACA;AAAA,GACF;AACF;AAEO,SAAS,uBAAuB,MAAA,EAA0C;AAC/E,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,MAAM,aAAa,KAAA,EAAkD;AACnE,MAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,mBAAmB,CAAA;AAAA,MAChE;AACA,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,OAAO,KAAA,CAAM,YAAY,QAAA,EAAU;AACvD,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,qBAAqB,CAAA;AAAA,MAClE;AACA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAA,GAAS,GAAA,EAAK;AAC9B,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,uBAAuB,CAAA;AAAA,MACpE;AACA,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,OAAO,KAAA,CAAM,YAAY,QAAA,EAAU;AACvD,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,qBAAqB,CAAA;AAAA,MAClE;AACA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAA,GAAS,GAAA,EAAQ;AACjC,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,yBAAyB,CAAA;AAAA,MACtE;AACA,MAAA,IAAI,MAAM,QAAA,IAAY,CAAC,iBAAiB,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA,EAAG;AAChE,QAAA,MAAM,IAAI,WAAA;AAAA,UACR,mBAAA;AAAA,UACA,CAAA,wBAAA,EAA2B,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACxD;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,MAAA,EAAQ,KAAA,CAAM,WAAW,CAAA;AAG5D,MAAA,MAAM,IAAA,GAAgC;AAAA,QACpC,SAAA;AAAA,QACA,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,QAAA,EAAU,MAAM,QAAA,IAAY;AAAA,OAC9B;AAGA,MAAA,IAAI,KAAA,CAAM,QAAA,IAAY,OAAO,KAAA,CAAM,aAAa,QAAA,EAAU;AACxD,QAAA,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AAAA,MACxB;AAEA,MAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAgD,MAAA,EAAQ;AAAA,QACxE,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,6BAAA;AAAA,QACN,IAAA;AAAA,QACA,WAAA,EAAa,IAAA;AAAA,QACb,cAAA,EAAgB;AAAA,OACjB,CAAA;AAGD,MAAA,MAAM,SAAA,GACJ,OAAO,OAAO,GAAA,KAAQ,YAAY,QAAA,IAAY,GAAA,GACzC,IAA+B,MAAA,GAChC,GAAA;AACN,MAAA,OAAO,SAAS,SAAS,CAAA;AAAA,IAC3B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM,cAAc,OAAA,EAA8D;AAChF,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,MAAA,EAAQ,OAAA,EAAS,WAAW,CAAA;AAC/D,MAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAiD,MAAA,EAAQ;AAAA,QACzE,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,6BAAA;AAAA,QACN,KAAA,EAAO,EAAE,SAAA,EAAU;AAAA,QACnB,WAAA,EAAa;AAAA,OACd,CAAA;AACD,MAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAC1B,MACA,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,aAAa,GAAA,GAC3C,GAAA,CAAgC,OAAA,IAAW,KAC7C,EAAC;AACP,MAAA,OAAO,IAAA,CAAK,IAAI,QAAQ,CAAA;AAAA,IAC1B;AAAA,GACF;AACF","file":"support.cjs","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 | '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 constructor(\r\n code: NeetruErrorCode | string,\r\n message: string,\r\n status?: number,\r\n requestId?: 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 // 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","/**\n * Idempotency-Key helper — gera UUID v4 cripto-seguro para identificar\n * mutations não-idempotentes (`usage.report`, `support.createTicket`,\n * `notifications.send`, `webhooks.test`).\n *\n * Estratégia 3.0:\n * - SDK gera UUID v4 por chamada via `randomUUID()` (WebCrypto.subtle não\n * necessário aqui — `crypto.randomUUID()` está em Node 14.17+ e em todos\n * os browsers/Edge runtimes modernos).\n * - Header `Idempotency-Key: <uuid>` é injetado pelo `httpRequest` quando\n * `opts.idempotencyKey === true` OU caller passa explicit `opts.idempotencyKeyValue`.\n * - Core dedup em coleção `usage_idempotency_keys/{key}` TTL 24h (vide\n * `docs/CORE_CHANGES_REQUIRED.md` §2). Se Core ainda não dedupa, o header\n * é benign (ignorado).\n *\n * Defesa em camadas: combinado com `retries: 0` em POSTs (vide http.ts), o\n * risco de double-write em transientes vai pra zero.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Gera UUID v4 criptograficamente seguro. Universal: Node 14.17+, browsers\n * modernos, Edge runtimes (Workers/Vercel Edge/Cloudflare).\n *\n * Fallback puro-JS removido em 3.0 — `crypto.randomUUID()` é ubíquo desde\n * 2022. Se runtime não tem, lança `NeetruError('runtime_unsupported')`\n * (consumer deve polyfillar).\n *\n * **L-01 fix (Opus review 2026-05-27):** antes lançava `Error` cru, que\n * não era pego por `catch (err) { if (err instanceof NeetruError) ... }`\n * dos consumers. Agora throw canonical.\n *\n * @throws NeetruError('runtime_unsupported') se `crypto.randomUUID` não disponível.\n */\nexport function generateIdempotencyKey(): string {\n // WebCrypto (browser/Edge) e Node 14.17+ expõem `crypto.randomUUID()`.\n // Node anterior precisa de `node:crypto` mas SDK 3.0 exige Node ≥20 (vide engines).\n const cryptoObj: { randomUUID?: () => string } | undefined =\n typeof globalThis !== 'undefined' && 'crypto' in globalThis\n ? (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n : undefined;\n if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {\n return cryptoObj.randomUUID();\n }\n throw new NeetruError(\n 'runtime_unsupported',\n 'crypto.randomUUID not available in this runtime. SDK 3.0 requires Node ≥20 or modern browser/Edge.',\n );\n}\n\n/** Regex UUID v4 — validação para uso interno. */\nexport const UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n","/**\r\n * HTTP transport interno do SDK.\r\n *\r\n * Responsabilidades:\r\n * - Construir URL absoluta a partir de `baseUrl` + path\r\n * - Injetar Bearer token quando `requireAuth=true`\r\n * - Mapear status HTTP → `NeetruError` com `code` estável\r\n * - Parse defensivo de JSON (não lança em body vazio em 204)\r\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\r\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\r\n * presente.\r\n *\r\n * Política de retries (mudança breaking 3.0):\r\n * - GET/HEAD: default 2 retries (3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: default **0 retries** — mutations podem duplicar\r\n * side effects (ex: `usage.report` incrementa contador). Caller pode\r\n * reativar passando `retries: N` explicitamente ou usar `Idempotency-Key`\r\n * header (gerado automaticamente se `opts.idempotencyKey=true`).\r\n *\r\n * Idempotency-Key (3.0):\r\n * - `opts.idempotencyKey === true` → SDK gera UUID v4 e injeta header.\r\n * - `opts.idempotencyKey === 'uuid-string'` → usa o valor passado.\r\n * - Caller mantém retentos client-side se quiser; Core dedup serverside\r\n * (vide `docs/CORE_CHANGES_REQUIRED.md` §2).\r\n */\r\nimport { NeetruError, type NeetruErrorCode } from './errors';\r\nimport { generateIdempotencyKey } from './idempotency';\r\nimport type { ResolvedConfig } from './types';\r\n\r\n/** Opções da request HTTP. */\r\nexport interface HttpRequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\r\n path: string;\r\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\r\n query?: Record<string, string | number | boolean | undefined>;\r\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\r\n body?: unknown;\r\n /**\r\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\r\n * se config.apiKey ausente. Default false.\r\n */\r\n requireAuth?: boolean;\r\n /** Cabeçalhos extras. */\r\n headers?: Record<string, string>;\r\n /**\r\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\r\n * `network_error`).\r\n *\r\n * Default (3.0):\r\n * - GET/HEAD: 2 (= 3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: 0 — mutations podem duplicar side effects.\r\n *\r\n * Caller pode reativar passando explicitamente: `retries: 2`.\r\n */\r\n retries?: number;\r\n /**\r\n * Injeta `Idempotency-Key` header pra dedup serverside (3.0):\r\n * - `true` → SDK gera UUID v4 cripto-seguro automaticamente.\r\n * - string → usa o valor passado (deve ser UUID v4).\r\n * - `undefined`/`false` → sem header.\r\n */\r\n idempotencyKey?: boolean | string;\r\n}\r\n\r\nconst DEFAULT_RETRIES_READ = 2;\r\nconst DEFAULT_RETRIES_WRITE = 0;\r\nconst READ_METHODS = new Set(['GET', 'HEAD']);\r\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\r\n 'rate_limited',\r\n 'server_error',\r\n 'network_error',\r\n]);\r\n\r\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\r\nfunction backoffMs(attempt: number): number {\r\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\r\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\r\n return Math.max(50, Math.round(base + jitter));\r\n}\r\n\r\n/**\r\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\r\n * inválido. RFC 9110 §10.2.3.\r\n */\r\nfunction parseRetryAfter(value: string | null): number | null {\r\n if (!value) return null;\r\n const secs = Number(value);\r\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\r\n const dateMs = Date.parse(value);\r\n if (Number.isFinite(dateMs)) {\r\n const delta = dateMs - Date.now();\r\n if (delta > 0) return delta;\r\n }\r\n return null;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/** Mapeamento status → code estável do NeetruError. */\r\nfunction statusToCode(status: number): NeetruErrorCode {\r\n if (status === 401) return 'unauthorized';\r\n if (status === 403) return 'forbidden';\r\n if (status === 404) return 'not_found';\r\n if (status === 422 || status === 400) return 'validation_failed';\r\n if (status === 429) return 'rate_limited';\r\n if (status >= 500) return 'server_error';\r\n return 'unknown';\r\n}\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\r\n // Trim trailing slash em base e leading em path pra evitar `//`.\r\n const base = baseUrl.replace(/\\/+$/, '');\r\n const p = path.startsWith('/') ? path : `/${path}`;\r\n const url = new URL(`${base}${p}`);\r\n if (query) {\r\n for (const [k, v] of Object.entries(query)) {\r\n if (v === undefined) continue;\r\n url.searchParams.set(k, String(v));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\r\nasync function safeJson(res: Response): Promise<unknown> {\r\n const text = await res.text();\r\n if (!text) return undefined;\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\r\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\r\n * automático em códigos transientes (rate_limited/server_error/network_error)\r\n * conforme `opts.retries` (default 2 = 3 tentativas).\r\n */\r\nexport async function httpRequest<T>(\r\n config: ResolvedConfig,\r\n opts: HttpRequestOptions,\r\n): Promise<T> {\r\n const method = opts.method ?? 'GET';\r\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\r\n // Default por método: reads tentam 2x, writes 0x. Caller override sempre vence.\r\n const defaultRetries = READ_METHODS.has(method)\r\n ? DEFAULT_RETRIES_READ\r\n : DEFAULT_RETRIES_WRITE;\r\n const maxRetries = opts.retries ?? defaultRetries;\r\n\r\n const headers: Record<string, string> = {\r\n accept: 'application/json',\r\n ...opts.headers,\r\n };\r\n\r\n if (opts.requireAuth) {\r\n if (!config.apiKey) {\r\n throw new NeetruError(\r\n 'missing_api_key',\r\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\r\n );\r\n }\r\n headers.authorization = `Bearer ${config.apiKey}`;\r\n }\r\n\r\n // Idempotency-Key: gerado por chamada se solicitado. UUID v4 cripto-seguro.\r\n if (opts.idempotencyKey) {\r\n const key =\r\n typeof opts.idempotencyKey === 'string'\r\n ? opts.idempotencyKey\r\n : generateIdempotencyKey();\r\n headers['idempotency-key'] = key;\r\n }\r\n\r\n // Body só é serializado uma vez — reusado entre tentativas.\r\n const bodyString =\r\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\r\n ? JSON.stringify(opts.body)\r\n : undefined;\r\n if (bodyString !== undefined) {\r\n headers['content-type'] = 'application/json';\r\n }\r\n\r\n let lastError: NeetruError | null = null;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n const init: RequestInit = { method, headers };\r\n if (bodyString !== undefined) init.body = bodyString;\r\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\r\n // requer Node 18+ ou browsers recentes.\r\n init.signal = AbortSignal.timeout(30_000);\r\n\r\n let res: Response;\r\n try {\r\n res = await config.fetch(url, init);\r\n } catch (err) {\r\n const message =\r\n err instanceof DOMException && err.name === 'TimeoutError'\r\n ? 'Network error: timeout after 30s'\r\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\r\n lastError = new NeetruError('network_error', message);\r\n if (attempt < maxRetries) {\r\n await sleep(backoffMs(attempt));\r\n continue;\r\n }\r\n throw lastError;\r\n }\r\n\r\n const requestId =\r\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\r\n\r\n if (res.ok) {\r\n const parsed = await safeJson(res);\r\n return parsed as T;\r\n }\r\n\r\n const body = (await safeJson(res)) as\r\n | { error?: { code?: string; message?: string } | string }\r\n | undefined;\r\n let code: string = statusToCode(res.status);\r\n let message = `HTTP ${res.status}`;\r\n if (body && typeof body === 'object' && 'error' in body) {\r\n const errField = body.error;\r\n if (typeof errField === 'string') {\r\n message = errField;\r\n } else if (errField && typeof errField === 'object') {\r\n if (typeof errField.code === 'string') code = errField.code;\r\n if (typeof errField.message === 'string') message = errField.message;\r\n }\r\n }\r\n const err = new NeetruError(code, message, res.status, requestId);\r\n lastError = err;\r\n\r\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\r\n if (isRetryable && attempt < maxRetries) {\r\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\r\n const delay = retryAfter ?? backoffMs(attempt);\r\n await sleep(delay);\r\n continue;\r\n }\r\n throw err;\r\n }\r\n\r\n // Unreachable — loop sempre retorna ou lança. Safety net.\r\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\r\n}\r\n","/**\n * Validators centralizados pra inputs do SDK que precisam casar com\n * regex do Core.\n *\n * **M-03 fix (Opus review 2026-05-27):** o Core rejeita productId que\n * não bate `/^[a-z0-9][a-z0-9_-]{0,62}$/` com 400 `validation_failed`.\n * O SDK não pré-validava — consumer recebia erro só em runtime de\n * produção, depois do round-trip de rede. Agora SDK throws localmente\n * antes do fetch.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Regex canonical de IDs no Neetru Core (productId, tenantId, etc).\n * Espelha `SDK_ID_RE` em `neetru_core/src/lib/schemas/sdk.ts`.\n */\nexport const SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;\n\n/**\n * Throws `NeetruError('validation_failed')` se `value` não bater no\n * formato canonical.\n *\n * @example\n * ```ts\n * assertValidSdkId('productId', 'gestovendas'); // ok\n * assertValidSdkId('productId', 'GestoVendas'); // throws\n * ```\n */\nexport function assertValidSdkId(field: string, value: string): void {\n if (typeof value !== 'string' || !SDK_ID_RE.test(value)) {\n throw new NeetruError(\n 'validation_failed',\n `${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`,\n );\n }\n}\n","/**\n * Support namespace — criar e listar tickets via SDK (3.0).\n *\n * **Breaking 3.0**:\n * - Endpoint mudou de `/api/v1/products/{slug}/tickets` (que **NUNCA existiu\n * no Core** — vaporware testado) para `/api/sdk/v1/support/tickets`\n * (canonical SDK path).\n * - `productId` agora é obrigatório no body/query (default resolve do\n * `config.productId`; throw `validation_failed` se ambos ausentes).\n * - `createTicket()` envia `Idempotency-Key` header (anti dup-create\n * em retry transient — Core dedup TTL 24h conforme CORE_CHANGES_REQUIRED §1).\n *\n * **Pré-requisito Core**: endpoint `/api/sdk/v1/support/tickets` (POST + GET)\n * precisa existir no Core antes do SDK 3.0 publish. Vide\n * `docs/CORE_CHANGES_REQUIRED.md` §1 para contrato esperado.\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport { assertValidSdkId } from './validators';\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 productId?: 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:\n typeof r.productId === 'string'\n ? r.productId\n : typeof r.productSlug === 'string'\n ? r.productSlug\n : undefined,\n };\n}\n\nfunction resolveProductId(\n config: ResolvedConfig,\n ...candidates: Array<string | undefined>\n): string {\n for (const c of candidates) {\n if (typeof c === 'string' && c.length > 0) {\n assertValidSdkId('productSlug', c); // M-03: pré-valida client-side\n return c;\n }\n }\n if (config.productId) {\n assertValidSdkId('productId', config.productId);\n return config.productId;\n }\n throw new NeetruError(\n 'validation_failed',\n 'productId required (pass to input.productSlug or set on createNeetruClient)',\n );\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. `productSlug` no input\n * (ou `config.productId` default) vai como `productId` no body do Core.\n *\n * Endpoint canonical: `POST /api/sdk/v1/support/tickets`.\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(\n 'validation_failed',\n `severity must be one of ${VALID_SEVERITIES.join(', ')}`,\n );\n }\n\n const productId = resolveProductId(config, input.productSlug);\n // M-03: validação acontece dentro de resolveProductId; aqui só usa.\n\n const body: Record<string, unknown> = {\n productId,\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n // L-05 fix: propaga metadata quando consumer enviar.\n if (input.metadata && typeof input.metadata === 'object') {\n body.metadata = input.metadata;\n }\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: '/api/sdk/v1/support/tickets',\n body,\n requireAuth: true,\n idempotencyKey: 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. `productSlug` no contexto.\n *\n * Endpoint canonical: `GET /api/sdk/v1/support/tickets?productId=...`.\n */\n async listMyTickets(options?: { productSlug?: string }): Promise<SupportTicket[]> {\n const productId = resolveProductId(config, options?.productSlug);\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/sdk/v1/support/tickets',\n query: { productId },\n requireAuth: true,\n });\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && 'tickets' in raw\n ? ((raw as { tickets?: unknown[] }).tickets ?? [])\n : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
|
package/dist/support.d.cts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as ResolvedConfig, O as SupportNamespace } from './types-Lfd3LiAF.cjs';
|
|
2
2
|
import './collection-ref-DqAAhuhX.cjs';
|
|
3
|
-
import '@neetru/realtime-protocol';
|
|
4
3
|
import 'drizzle-orm/node-postgres';
|
|
5
4
|
import './errors.cjs';
|
|
6
5
|
|
package/dist/support.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as ResolvedConfig, O as SupportNamespace } from './types-DALIhcbq.js';
|
|
2
2
|
import './collection-ref-DqAAhuhX.js';
|
|
3
|
-
import '@neetru/realtime-protocol';
|
|
4
3
|
import 'drizzle-orm/node-postgres';
|
|
5
4
|
import './errors.js';
|
|
6
5
|
|
package/dist/support.mjs
CHANGED
|
@@ -13,8 +13,22 @@ var NeetruError = class _NeetruError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
// src/idempotency.ts
|
|
17
|
+
function generateIdempotencyKey() {
|
|
18
|
+
const cryptoObj = typeof globalThis !== "undefined" && "crypto" in globalThis ? globalThis.crypto : void 0;
|
|
19
|
+
if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
|
|
20
|
+
return cryptoObj.randomUUID();
|
|
21
|
+
}
|
|
22
|
+
throw new NeetruError(
|
|
23
|
+
"runtime_unsupported",
|
|
24
|
+
"crypto.randomUUID not available in this runtime. SDK 3.0 requires Node \u226520 or modern browser/Edge."
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
// src/http.ts
|
|
17
|
-
var
|
|
29
|
+
var DEFAULT_RETRIES_READ = 2;
|
|
30
|
+
var DEFAULT_RETRIES_WRITE = 0;
|
|
31
|
+
var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
18
32
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
19
33
|
"rate_limited",
|
|
20
34
|
"server_error",
|
|
@@ -72,7 +86,8 @@ async function safeJson(res) {
|
|
|
72
86
|
async function httpRequest(config, opts) {
|
|
73
87
|
const method = opts.method ?? "GET";
|
|
74
88
|
const url = buildUrl(config.baseUrl, opts.path, opts.query);
|
|
75
|
-
const
|
|
89
|
+
const defaultRetries = READ_METHODS.has(method) ? DEFAULT_RETRIES_READ : DEFAULT_RETRIES_WRITE;
|
|
90
|
+
const maxRetries = opts.retries ?? defaultRetries;
|
|
76
91
|
const headers = {
|
|
77
92
|
accept: "application/json",
|
|
78
93
|
...opts.headers
|
|
@@ -86,6 +101,10 @@ async function httpRequest(config, opts) {
|
|
|
86
101
|
}
|
|
87
102
|
headers.authorization = `Bearer ${config.apiKey}`;
|
|
88
103
|
}
|
|
104
|
+
if (opts.idempotencyKey) {
|
|
105
|
+
const key = typeof opts.idempotencyKey === "string" ? opts.idempotencyKey : generateIdempotencyKey();
|
|
106
|
+
headers["idempotency-key"] = key;
|
|
107
|
+
}
|
|
89
108
|
const bodyString = opts.body !== void 0 && method !== "GET" && method !== "DELETE" ? JSON.stringify(opts.body) : void 0;
|
|
90
109
|
if (bodyString !== void 0) {
|
|
91
110
|
headers["content-type"] = "application/json";
|
|
@@ -138,6 +157,17 @@ async function httpRequest(config, opts) {
|
|
|
138
157
|
throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
|
|
139
158
|
}
|
|
140
159
|
|
|
160
|
+
// src/validators.ts
|
|
161
|
+
var SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
162
|
+
function assertValidSdkId(field, value) {
|
|
163
|
+
if (typeof value !== "string" || !SDK_ID_RE.test(value)) {
|
|
164
|
+
throw new NeetruError(
|
|
165
|
+
"validation_failed",
|
|
166
|
+
`${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
141
171
|
// src/support.ts
|
|
142
172
|
var VALID_SEVERITIES = ["low", "normal", "high", "urgent"];
|
|
143
173
|
var VALID_STATUSES = ["open", "pending", "resolved", "closed"];
|
|
@@ -157,14 +187,32 @@ function toTicket(raw) {
|
|
|
157
187
|
status: VALID_STATUSES.includes(r.status) ? r.status : "open",
|
|
158
188
|
createdAt: typeof r.createdAt === "string" ? r.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
159
189
|
updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : void 0,
|
|
160
|
-
productSlug: typeof r.productSlug === "string" ? r.productSlug : void 0
|
|
190
|
+
productSlug: typeof r.productId === "string" ? r.productId : typeof r.productSlug === "string" ? r.productSlug : void 0
|
|
161
191
|
};
|
|
162
192
|
}
|
|
193
|
+
function resolveProductId(config, ...candidates) {
|
|
194
|
+
for (const c of candidates) {
|
|
195
|
+
if (typeof c === "string" && c.length > 0) {
|
|
196
|
+
assertValidSdkId("productSlug", c);
|
|
197
|
+
return c;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (config.productId) {
|
|
201
|
+
assertValidSdkId("productId", config.productId);
|
|
202
|
+
return config.productId;
|
|
203
|
+
}
|
|
204
|
+
throw new NeetruError(
|
|
205
|
+
"validation_failed",
|
|
206
|
+
"productId required (pass to input.productSlug or set on createNeetruClient)"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
163
209
|
function createSupportNamespace(config) {
|
|
164
210
|
return {
|
|
165
211
|
/**
|
|
166
|
-
* Cria um ticket de suporte. Requer Bearer auth.
|
|
167
|
-
*
|
|
212
|
+
* Cria um ticket de suporte. Requer Bearer auth. `productSlug` no input
|
|
213
|
+
* (ou `config.productId` default) vai como `productId` no body do Core.
|
|
214
|
+
*
|
|
215
|
+
* Endpoint canonical: `POST /api/sdk/v1/support/tickets`.
|
|
168
216
|
*/
|
|
169
217
|
async createTicket(input) {
|
|
170
218
|
if (!input || typeof input !== "object") {
|
|
@@ -183,30 +231,42 @@ function createSupportNamespace(config) {
|
|
|
183
231
|
throw new NeetruError("validation_failed", "message max 10000 chars");
|
|
184
232
|
}
|
|
185
233
|
if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {
|
|
186
|
-
throw new NeetruError(
|
|
234
|
+
throw new NeetruError(
|
|
235
|
+
"validation_failed",
|
|
236
|
+
`severity must be one of ${VALID_SEVERITIES.join(", ")}`
|
|
237
|
+
);
|
|
187
238
|
}
|
|
188
|
-
const
|
|
239
|
+
const productId = resolveProductId(config, input.productSlug);
|
|
189
240
|
const body = {
|
|
241
|
+
productId,
|
|
190
242
|
subject: input.subject,
|
|
191
243
|
message: input.message,
|
|
192
244
|
severity: input.severity ?? "normal"
|
|
193
245
|
};
|
|
246
|
+
if (input.metadata && typeof input.metadata === "object") {
|
|
247
|
+
body.metadata = input.metadata;
|
|
248
|
+
}
|
|
194
249
|
const raw = await httpRequest(config, {
|
|
195
250
|
method: "POST",
|
|
196
|
-
path:
|
|
251
|
+
path: "/api/sdk/v1/support/tickets",
|
|
197
252
|
body,
|
|
198
|
-
requireAuth: true
|
|
253
|
+
requireAuth: true,
|
|
254
|
+
idempotencyKey: true
|
|
199
255
|
});
|
|
200
256
|
const candidate = raw && typeof raw === "object" && "ticket" in raw ? raw.ticket : raw;
|
|
201
257
|
return toTicket(candidate);
|
|
202
258
|
},
|
|
203
259
|
/**
|
|
204
|
-
* Lista tickets do customer no produto atual
|
|
260
|
+
* Lista tickets do customer no produto atual. `productSlug` no contexto.
|
|
261
|
+
*
|
|
262
|
+
* Endpoint canonical: `GET /api/sdk/v1/support/tickets?productId=...`.
|
|
205
263
|
*/
|
|
206
|
-
async listMyTickets() {
|
|
264
|
+
async listMyTickets(options) {
|
|
265
|
+
const productId = resolveProductId(config, options?.productSlug);
|
|
207
266
|
const raw = await httpRequest(config, {
|
|
208
267
|
method: "GET",
|
|
209
|
-
path: "/api/v1/
|
|
268
|
+
path: "/api/sdk/v1/support/tickets",
|
|
269
|
+
query: { productId },
|
|
210
270
|
requireAuth: true
|
|
211
271
|
});
|
|
212
272
|
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && "tickets" in raw ? raw.tickets ?? [] : [];
|
package/dist/support.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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":["/**\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 | '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 constructor(\r\n code: NeetruErrorCode | string,\r\n message: string,\r\n status?: number,\r\n requestId?: 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 // 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 * HTTP transport interno do SDK.\r\n *\r\n * Responsabilidades:\r\n * - Construir URL absoluta a partir de `baseUrl` + path\r\n * - Injetar Bearer token quando `requireAuth=true`\r\n * - Mapear status HTTP → `NeetruError` com `code` estável\r\n * - Parse defensivo de JSON (não lança em body vazio em 204)\r\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\r\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\r\n * presente. Default 2 retries (3 tentativas no total). Caller opta-out\r\n * com `retries: 0`.\r\n */\r\nimport { NeetruError, type NeetruErrorCode } from './errors';\r\nimport type { ResolvedConfig } from './types';\r\n\r\n/** Opções da request HTTP. */\r\nexport interface HttpRequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\r\n path: string;\r\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\r\n query?: Record<string, string | number | boolean | undefined>;\r\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\r\n body?: unknown;\r\n /**\r\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\r\n * se config.apiKey ausente. Default false.\r\n */\r\n requireAuth?: boolean;\r\n /** Cabeçalhos extras. */\r\n headers?: Record<string, string>;\r\n /**\r\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\r\n * `network_error`). Default 2 (= 3 tentativas total). Caller passa `0` pra\r\n * desativar (útil em operações não-idempotentes específicas).\r\n */\r\n retries?: number;\r\n}\r\n\r\nconst DEFAULT_RETRIES = 2;\r\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\r\n 'rate_limited',\r\n 'server_error',\r\n 'network_error',\r\n]);\r\n\r\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\r\nfunction backoffMs(attempt: number): number {\r\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\r\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\r\n return Math.max(50, Math.round(base + jitter));\r\n}\r\n\r\n/**\r\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\r\n * inválido. RFC 9110 §10.2.3.\r\n */\r\nfunction parseRetryAfter(value: string | null): number | null {\r\n if (!value) return null;\r\n const secs = Number(value);\r\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\r\n const dateMs = Date.parse(value);\r\n if (Number.isFinite(dateMs)) {\r\n const delta = dateMs - Date.now();\r\n if (delta > 0) return delta;\r\n }\r\n return null;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/** Mapeamento status → code estável do NeetruError. */\r\nfunction statusToCode(status: number): NeetruErrorCode {\r\n if (status === 401) return 'unauthorized';\r\n if (status === 403) return 'forbidden';\r\n if (status === 404) return 'not_found';\r\n if (status === 422 || status === 400) return 'validation_failed';\r\n if (status === 429) return 'rate_limited';\r\n if (status >= 500) return 'server_error';\r\n return 'unknown';\r\n}\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\r\n // Trim trailing slash em base e leading em path pra evitar `//`.\r\n const base = baseUrl.replace(/\\/+$/, '');\r\n const p = path.startsWith('/') ? path : `/${path}`;\r\n const url = new URL(`${base}${p}`);\r\n if (query) {\r\n for (const [k, v] of Object.entries(query)) {\r\n if (v === undefined) continue;\r\n url.searchParams.set(k, String(v));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\r\nasync function safeJson(res: Response): Promise<unknown> {\r\n const text = await res.text();\r\n if (!text) return undefined;\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\r\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\r\n * automático em códigos transientes (rate_limited/server_error/network_error)\r\n * conforme `opts.retries` (default 2 = 3 tentativas).\r\n */\r\nexport async function httpRequest<T>(\r\n config: ResolvedConfig,\r\n opts: HttpRequestOptions,\r\n): Promise<T> {\r\n const method = opts.method ?? 'GET';\r\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\r\n const maxRetries = opts.retries ?? DEFAULT_RETRIES;\r\n\r\n const headers: Record<string, string> = {\r\n accept: 'application/json',\r\n ...opts.headers,\r\n };\r\n\r\n if (opts.requireAuth) {\r\n if (!config.apiKey) {\r\n throw new NeetruError(\r\n 'missing_api_key',\r\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\r\n );\r\n }\r\n headers.authorization = `Bearer ${config.apiKey}`;\r\n }\r\n\r\n // Body só é serializado uma vez — reusado entre tentativas.\r\n const bodyString =\r\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\r\n ? JSON.stringify(opts.body)\r\n : undefined;\r\n if (bodyString !== undefined) {\r\n headers['content-type'] = 'application/json';\r\n }\r\n\r\n let lastError: NeetruError | null = null;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n const init: RequestInit = { method, headers };\r\n if (bodyString !== undefined) init.body = bodyString;\r\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\r\n // requer Node 18+ ou browsers recentes.\r\n init.signal = AbortSignal.timeout(30_000);\r\n\r\n let res: Response;\r\n try {\r\n res = await config.fetch(url, init);\r\n } catch (err) {\r\n const message =\r\n err instanceof DOMException && err.name === 'TimeoutError'\r\n ? 'Network error: timeout after 30s'\r\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\r\n lastError = new NeetruError('network_error', message);\r\n if (attempt < maxRetries) {\r\n await sleep(backoffMs(attempt));\r\n continue;\r\n }\r\n throw lastError;\r\n }\r\n\r\n const requestId =\r\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\r\n\r\n if (res.ok) {\r\n const parsed = await safeJson(res);\r\n return parsed as T;\r\n }\r\n\r\n const body = (await safeJson(res)) as\r\n | { error?: { code?: string; message?: string } | string }\r\n | undefined;\r\n let code: string = statusToCode(res.status);\r\n let message = `HTTP ${res.status}`;\r\n if (body && typeof body === 'object' && 'error' in body) {\r\n const errField = body.error;\r\n if (typeof errField === 'string') {\r\n message = errField;\r\n } else if (errField && typeof errField === 'object') {\r\n if (typeof errField.code === 'string') code = errField.code;\r\n if (typeof errField.message === 'string') message = errField.message;\r\n }\r\n }\r\n const err = new NeetruError(code, message, res.status, requestId);\r\n lastError = err;\r\n\r\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\r\n if (isRetryable && attempt < maxRetries) {\r\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\r\n const delay = retryAfter ?? backoffMs(attempt);\r\n await sleep(delay);\r\n continue;\r\n }\r\n throw err;\r\n }\r\n\r\n // Unreachable — loop sempre retorna ou lança. Safety net.\r\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\r\n}\r\n","/**\r\n * Support namespace — criar e listar tickets do customer (v0.2).\r\n *\r\n * Endpoints (em prod):\r\n * - `POST /api/v1/products/{slug}/tickets` — criar ticket\r\n * - `GET /api/v1/products/{slug}/tickets` — listar meus tickets do produto\r\n *\r\n * Em dev (mocks ativos via factory) tudo é in-memory.\r\n *\r\n * Decisão: severity default é `normal`. `low` indica feature requests; `urgent`\r\n * pra outages production (paginating ops imediatamente em prod).\r\n */\r\nimport { NeetruError } from './errors';\r\nimport { httpRequest } from './http';\r\nimport type {\r\n CreateTicketInput,\r\n ResolvedConfig,\r\n SupportNamespace,\r\n SupportSeverity,\r\n SupportStatus,\r\n SupportTicket,\r\n} from './types';\r\n\r\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\r\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\r\n\r\ninterface RawTicket {\r\n id?: string;\r\n subject?: string;\r\n message?: string;\r\n severity?: string;\r\n status?: string;\r\n createdAt?: string;\r\n updatedAt?: string;\r\n productSlug?: string;\r\n}\r\n\r\nfunction toTicket(raw: unknown): SupportTicket {\r\n if (!raw || typeof raw !== 'object') {\r\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\r\n }\r\n const r = raw as RawTicket;\r\n if (typeof r.id !== 'string') {\r\n throw new NeetruError('invalid_response', 'Ticket missing id');\r\n }\r\n return {\r\n id: r.id,\r\n subject: typeof r.subject === 'string' ? r.subject : '',\r\n message: typeof r.message === 'string' ? r.message : '',\r\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\r\n ? (r.severity as SupportSeverity)\r\n : 'normal',\r\n status: VALID_STATUSES.includes(r.status as SupportStatus)\r\n ? (r.status as SupportStatus)\r\n : 'open',\r\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\r\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\r\n productSlug: typeof r.productSlug === 'string' ? r.productSlug : undefined,\r\n };\r\n}\r\n\r\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\r\n return {\r\n /**\r\n * Cria um ticket de suporte. Requer Bearer auth. Se `productSlug` não é\r\n * passado, o backend infere do escopo do token.\r\n */\r\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\r\n if (!input || typeof input !== 'object') {\r\n throw new NeetruError('validation_failed', 'input is required');\r\n }\r\n if (!input.subject || typeof input.subject !== 'string') {\r\n throw new NeetruError('validation_failed', 'subject is required');\r\n }\r\n if (input.subject.length > 200) {\r\n throw new NeetruError('validation_failed', 'subject max 200 chars');\r\n }\r\n if (!input.message || typeof input.message !== 'string') {\r\n throw new NeetruError('validation_failed', 'message is required');\r\n }\r\n if (input.message.length > 10_000) {\r\n throw new NeetruError('validation_failed', 'message max 10000 chars');\r\n }\r\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\r\n throw new NeetruError('validation_failed', `severity must be one of ${VALID_SEVERITIES.join(', ')}`);\r\n }\r\n\r\n const slug = input.productSlug ?? '_default';\r\n const body: Record<string, unknown> = {\r\n subject: input.subject,\r\n message: input.message,\r\n severity: input.severity ?? 'normal',\r\n };\r\n\r\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\r\n method: 'POST',\r\n path: `/api/v1/products/${encodeURIComponent(slug)}/tickets`,\r\n body,\r\n requireAuth: true,\r\n });\r\n\r\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\r\n const candidate =\r\n raw && typeof raw === 'object' && 'ticket' in raw\r\n ? (raw as { ticket?: RawTicket }).ticket\r\n : raw;\r\n return toTicket(candidate);\r\n },\r\n\r\n /**\r\n * Lista tickets do customer no produto atual (escopo do token).\r\n */\r\n async listMyTickets(): Promise<SupportTicket[]> {\r\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\r\n method: 'GET',\r\n path: '/api/v1/products/_default/tickets',\r\n requireAuth: true,\r\n });\r\n const list = Array.isArray(raw) ? raw : raw && typeof raw === 'object' && 'tickets' in raw ? (raw as { tickets?: unknown[] }).tickets ?? [] : [];\r\n return list.map(toTicket);\r\n },\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/idempotency.ts","../src/http.ts","../src/validators.ts","../src/support.ts"],"names":["err","message"],"mappings":";AAsCO,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EACrB,IAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EAEhB,WAAA,CACE,IAAA,EACA,OAAA,EACA,MAAA,EACA,SAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAEjB,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,YAAA,CAAY,SAAS,CAAA;AAAA,EACnD;AACF,CAAA;;;ACvBO,SAAS,sBAAA,GAAiC;AAG/C,EAAA,MAAM,YACJ,OAAO,UAAA,KAAe,eAAe,QAAA,IAAY,UAAA,GAC5C,WAA0D,MAAA,GAC3D,MAAA;AACN,EAAA,IAAI,SAAA,IAAa,OAAO,SAAA,CAAU,UAAA,KAAe,UAAA,EAAY;AAC3D,IAAA,OAAO,UAAU,UAAA,EAAW;AAAA,EAC9B;AACA,EAAA,MAAM,IAAI,WAAA;AAAA,IACR,qBAAA;AAAA,IACA;AAAA,GACF;AACF;;;ACiBA,IAAM,oBAAA,GAAuB,CAAA;AAC7B,IAAM,qBAAA,GAAwB,CAAA;AAC9B,IAAM,+BAAe,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAC5C,IAAM,eAAA,uBAAsB,GAAA,CAAqB;AAAA,EAC/C,cAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF,CAAC,CAAA;AAGD,SAAS,UAAU,OAAA,EAAyB;AAC1C,EAAA,MAAM,IAAA,GAAO,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,OAAO,CAAA;AACtC,EAAA,MAAM,SAAS,IAAA,GAAO,GAAA,IAAO,IAAA,CAAK,MAAA,KAAW,CAAA,GAAI,CAAA,CAAA;AACjD,EAAA,OAAO,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,KAAA,CAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AAC/C;AAMA,SAAS,gBAAgB,KAAA,EAAqC;AAC5D,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,MAAM,IAAA,GAAO,OAAO,KAAK,CAAA;AACzB,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,IAAK,IAAA,IAAQ,GAAG,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,GAAI,CAAA;AACrE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC/B,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,EAAG;AAC3B,IAAA,MAAM,KAAA,GAAQ,MAAA,GAAS,IAAA,CAAK,GAAA,EAAI;AAChC,IAAA,IAAI,KAAA,GAAQ,GAAG,OAAO,KAAA;AAAA,EACxB;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAGA,SAAS,aAAa,MAAA,EAAiC;AACrD,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,cAAA;AAC3B,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,WAAA;AAC3B,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,WAAA;AAC3B,EAAA,IAAI,MAAA,KAAW,GAAA,IAAO,MAAA,KAAW,GAAA,EAAK,OAAO,mBAAA;AAC7C,EAAA,IAAI,MAAA,KAAW,KAAK,OAAO,cAAA;AAC3B,EAAA,IAAI,MAAA,IAAU,KAAK,OAAO,cAAA;AAC1B,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,QAAA,CAAS,OAAA,EAAiB,IAAA,EAAc,KAAA,EAA6C;AAE5F,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACvC,EAAA,MAAM,IAAI,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,IAAA,GAAO,IAAI,IAAI,CAAA,CAAA;AAChD,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,IAAI,CAAA,EAAG,CAAC,CAAA,CAAE,CAAA;AACjC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC1C,MAAA,IAAI,MAAM,MAAA,EAAW;AACrB,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,IACnC;AAAA,EACF;AACA,EAAA,OAAO,IAAI,QAAA,EAAS;AACtB;AAGA,eAAe,SAAS,GAAA,EAAiC;AACvD,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,EAAA,IAAI,CAAC,MAAM,OAAO,MAAA;AAClB,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAQA,eAAsB,WAAA,CACpB,QACA,IAAA,EACY;AACZ,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,IAAU,KAAA;AAC9B,EAAA,MAAM,MAAM,QAAA,CAAS,MAAA,CAAO,SAAS,IAAA,CAAK,IAAA,EAAM,KAAK,KAAK,CAAA;AAE1D,EAAA,MAAM,cAAA,GAAiB,YAAA,CAAa,GAAA,CAAI,MAAM,IAC1C,oBAAA,GACA,qBAAA;AACJ,EAAA,MAAM,UAAA,GAAa,KAAK,OAAA,IAAW,cAAA;AAEnC,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,MAAA,EAAQ,kBAAA;AAAA,IACR,GAAG,IAAA,CAAK;AAAA,GACV;AAEA,EAAA,IAAI,KAAK,WAAA,EAAa;AACpB,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,MAAM,IAAI,WAAA;AAAA,QACR,iBAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,EACjD;AAGA,EAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,IAAA,MAAM,MACJ,OAAO,IAAA,CAAK,mBAAmB,QAAA,GAC3B,IAAA,CAAK,iBACL,sBAAA,EAAuB;AAC7B,IAAA,OAAA,CAAQ,iBAAiB,CAAA,GAAI,GAAA;AAAA,EAC/B;AAGA,EAAA,MAAM,UAAA,GACJ,IAAA,CAAK,IAAA,KAAS,MAAA,IAAa,MAAA,KAAW,KAAA,IAAS,MAAA,KAAW,QAAA,GACtD,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GACxB,MAAA;AACN,EAAA,IAAI,eAAe,MAAA,EAAW;AAC5B,IAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,kBAAA;AAAA,EAC5B;AAEA,EAAA,IAAI,SAAA,GAAgC,IAAA;AAEpC,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,UAAA,EAAY,OAAA,EAAA,EAAW;AACtD,IAAA,MAAM,IAAA,GAAoB,EAAE,MAAA,EAAQ,OAAA,EAAQ;AAC5C,IAAA,IAAI,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,IAAA,GAAO,UAAA;AAG1C,IAAA,IAAA,CAAK,MAAA,GAAS,WAAA,CAAY,OAAA,CAAQ,GAAM,CAAA;AAExC,IAAA,IAAI,GAAA;AACJ,IAAA,IAAI;AACF,MAAA,GAAA,GAAM,MAAM,MAAA,CAAO,KAAA,CAAM,GAAA,EAAK,IAAI,CAAA;AAAA,IACpC,SAASA,IAAAA,EAAK;AACZ,MAAA,MAAMC,QAAAA,GACJD,IAAAA,YAAe,YAAA,IAAgBA,IAAAA,CAAI,IAAA,KAAS,cAAA,GACxC,kCAAA,GACA,CAAA,eAAA,EAAkBA,IAAAA,YAAe,KAAA,GAAQA,IAAAA,CAAI,OAAA,GAAU,cAAc,CAAA,CAAA;AAC3E,MAAA,SAAA,GAAY,IAAI,WAAA,CAAY,eAAA,EAAiBC,QAAO,CAAA;AACpD,MAAA,IAAI,UAAU,UAAA,EAAY;AACxB,QAAA,MAAM,KAAA,CAAM,SAAA,CAAU,OAAO,CAAC,CAAA;AAC9B,QAAA;AAAA,MACF;AACA,MAAA,MAAM,SAAA;AAAA,IACR;AAEA,IAAA,MAAM,SAAA,GACJ,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,cAAc,KAAK,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAA,IAAK,MAAA;AAE5E,IAAA,IAAI,IAAI,EAAA,EAAI;AACV,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,GAAG,CAAA;AACjC,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,GAAG,CAAA;AAGhC,IAAA,IAAI,IAAA,GAAe,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AAC1C,IAAA,IAAI,OAAA,GAAU,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,CAAA;AAChC,IAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,WAAW,IAAA,EAAM;AACvD,MAAA,MAAM,WAAW,IAAA,CAAK,KAAA;AACtB,MAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,QAAA,OAAA,GAAU,QAAA;AAAA,MACZ,CAAA,MAAA,IAAW,QAAA,IAAY,OAAO,QAAA,KAAa,QAAA,EAAU;AACnD,QAAA,IAAI,OAAO,QAAA,CAAS,IAAA,KAAS,QAAA,SAAiB,QAAA,CAAS,IAAA;AACvD,QAAA,IAAI,OAAO,QAAA,CAAS,OAAA,KAAY,QAAA,YAAoB,QAAA,CAAS,OAAA;AAAA,MAC/D;AAAA,IACF;AACA,IAAA,MAAM,MAAM,IAAI,WAAA,CAAY,MAAM,OAAA,EAAS,GAAA,CAAI,QAAQ,SAAS,CAAA;AAChE,IAAA,SAAA,GAAY,GAAA;AAEZ,IAAA,MAAM,WAAA,GAAc,eAAA,CAAgB,GAAA,CAAI,IAAuB,CAAA;AAC/D,IAAA,IAAI,WAAA,IAAe,UAAU,UAAA,EAAY;AACvC,MAAA,MAAM,aAAa,eAAA,CAAgB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AACjE,MAAA,MAAM,KAAA,GAAQ,UAAA,IAAc,SAAA,CAAU,OAAO,CAAA;AAC7C,MAAA,MAAM,MAAM,KAAK,CAAA;AACjB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,GAAA;AAAA,EACR;AAGA,EAAA,MAAM,SAAA,IAAa,IAAI,WAAA,CAAY,SAAA,EAAW,6BAA6B,CAAA;AAC7E;;;AC1OO,IAAM,SAAA,GAAY,4BAAA;AAYlB,SAAS,gBAAA,CAAiB,OAAe,KAAA,EAAqB;AACnE,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA,EAAG;AACvD,IAAA,MAAM,IAAI,WAAA;AAAA,MACR,mBAAA;AAAA,MACA,CAAA,EAAG,KAAK,CAAA,YAAA,EAAe,SAAA,CAAU,MAAM,CAAA,8EAAA;AAAA,KACzC;AAAA,EACF;AACF;;;ACLA,IAAM,gBAAA,GAA+C,CAAC,KAAA,EAAO,QAAA,EAAU,QAAQ,QAAQ,CAAA;AACvF,IAAM,cAAA,GAA2C,CAAC,MAAA,EAAQ,SAAA,EAAW,YAAY,QAAQ,CAAA;AAczF,SAAS,SAAS,GAAA,EAA6B;AAC7C,EAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,EAAU;AACnC,IAAA,MAAM,IAAI,WAAA,CAAY,kBAAA,EAAoB,kCAAkC,CAAA;AAAA,EAC9E;AACA,EAAA,MAAM,CAAA,GAAI,GAAA;AACV,EAAA,IAAI,OAAO,CAAA,CAAE,EAAA,KAAO,QAAA,EAAU;AAC5B,IAAA,MAAM,IAAI,WAAA,CAAY,kBAAA,EAAoB,mBAAmB,CAAA;AAAA,EAC/D;AACA,EAAA,OAAO;AAAA,IACL,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,SAAS,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,GAAW,EAAE,OAAA,GAAU,EAAA;AAAA,IACrD,SAAS,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,GAAW,EAAE,OAAA,GAAU,EAAA;AAAA,IACrD,UAAU,gBAAA,CAAiB,QAAA,CAAS,EAAE,QAA2B,CAAA,GAC5D,EAAE,QAAA,GACH,QAAA;AAAA,IACJ,QAAQ,cAAA,CAAe,QAAA,CAAS,EAAE,MAAuB,CAAA,GACpD,EAAE,MAAA,GACH,MAAA;AAAA,IACJ,SAAA,EAAW,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,GAAW,EAAE,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IAClF,WAAW,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,GAAW,EAAE,SAAA,GAAY,MAAA;AAAA,IAC3D,WAAA,EACE,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,GACnB,CAAA,CAAE,SAAA,GACF,OAAO,CAAA,CAAE,WAAA,KAAgB,QAAA,GACvB,CAAA,CAAE,WAAA,GACF;AAAA,GACV;AACF;AAEA,SAAS,gBAAA,CACP,WACG,UAAA,EACK;AACR,EAAA,KAAA,MAAW,KAAK,UAAA,EAAY;AAC1B,IAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,SAAS,CAAA,EAAG;AACzC,MAAA,gBAAA,CAAiB,eAAe,CAAC,CAAA;AACjC,MAAA,OAAO,CAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,IAAI,OAAO,SAAA,EAAW;AACpB,IAAA,gBAAA,CAAiB,WAAA,EAAa,OAAO,SAAS,CAAA;AAC9C,IAAA,OAAO,MAAA,CAAO,SAAA;AAAA,EAChB;AACA,EAAA,MAAM,IAAI,WAAA;AAAA,IACR,mBAAA;AAAA,IACA;AAAA,GACF;AACF;AAEO,SAAS,uBAAuB,MAAA,EAA0C;AAC/E,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,MAAM,aAAa,KAAA,EAAkD;AACnE,MAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,mBAAmB,CAAA;AAAA,MAChE;AACA,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,OAAO,KAAA,CAAM,YAAY,QAAA,EAAU;AACvD,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,qBAAqB,CAAA;AAAA,MAClE;AACA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAA,GAAS,GAAA,EAAK;AAC9B,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,uBAAuB,CAAA;AAAA,MACpE;AACA,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,OAAO,KAAA,CAAM,YAAY,QAAA,EAAU;AACvD,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,qBAAqB,CAAA;AAAA,MAClE;AACA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAA,GAAS,GAAA,EAAQ;AACjC,QAAA,MAAM,IAAI,WAAA,CAAY,mBAAA,EAAqB,yBAAyB,CAAA;AAAA,MACtE;AACA,MAAA,IAAI,MAAM,QAAA,IAAY,CAAC,iBAAiB,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA,EAAG;AAChE,QAAA,MAAM,IAAI,WAAA;AAAA,UACR,mBAAA;AAAA,UACA,CAAA,wBAAA,EAA2B,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACxD;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,MAAA,EAAQ,KAAA,CAAM,WAAW,CAAA;AAG5D,MAAA,MAAM,IAAA,GAAgC;AAAA,QACpC,SAAA;AAAA,QACA,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,QAAA,EAAU,MAAM,QAAA,IAAY;AAAA,OAC9B;AAGA,MAAA,IAAI,KAAA,CAAM,QAAA,IAAY,OAAO,KAAA,CAAM,aAAa,QAAA,EAAU;AACxD,QAAA,IAAA,CAAK,WAAW,KAAA,CAAM,QAAA;AAAA,MACxB;AAEA,MAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAgD,MAAA,EAAQ;AAAA,QACxE,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,6BAAA;AAAA,QACN,IAAA;AAAA,QACA,WAAA,EAAa,IAAA;AAAA,QACb,cAAA,EAAgB;AAAA,OACjB,CAAA;AAGD,MAAA,MAAM,SAAA,GACJ,OAAO,OAAO,GAAA,KAAQ,YAAY,QAAA,IAAY,GAAA,GACzC,IAA+B,MAAA,GAChC,GAAA;AACN,MAAA,OAAO,SAAS,SAAS,CAAA;AAAA,IAC3B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM,cAAc,OAAA,EAA8D;AAChF,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,MAAA,EAAQ,OAAA,EAAS,WAAW,CAAA;AAC/D,MAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAiD,MAAA,EAAQ;AAAA,QACzE,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,6BAAA;AAAA,QACN,KAAA,EAAO,EAAE,SAAA,EAAU;AAAA,QACnB,WAAA,EAAa;AAAA,OACd,CAAA;AACD,MAAA,MAAM,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAC1B,MACA,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,aAAa,GAAA,GAC3C,GAAA,CAAgC,OAAA,IAAW,KAC7C,EAAC;AACP,MAAA,OAAO,IAAA,CAAK,IAAI,QAAQ,CAAA;AAAA,IAC1B;AAAA,GACF;AACF","file":"support.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 | '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 constructor(\r\n code: NeetruErrorCode | string,\r\n message: string,\r\n status?: number,\r\n requestId?: 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 // 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","/**\n * Idempotency-Key helper — gera UUID v4 cripto-seguro para identificar\n * mutations não-idempotentes (`usage.report`, `support.createTicket`,\n * `notifications.send`, `webhooks.test`).\n *\n * Estratégia 3.0:\n * - SDK gera UUID v4 por chamada via `randomUUID()` (WebCrypto.subtle não\n * necessário aqui — `crypto.randomUUID()` está em Node 14.17+ e em todos\n * os browsers/Edge runtimes modernos).\n * - Header `Idempotency-Key: <uuid>` é injetado pelo `httpRequest` quando\n * `opts.idempotencyKey === true` OU caller passa explicit `opts.idempotencyKeyValue`.\n * - Core dedup em coleção `usage_idempotency_keys/{key}` TTL 24h (vide\n * `docs/CORE_CHANGES_REQUIRED.md` §2). Se Core ainda não dedupa, o header\n * é benign (ignorado).\n *\n * Defesa em camadas: combinado com `retries: 0` em POSTs (vide http.ts), o\n * risco de double-write em transientes vai pra zero.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Gera UUID v4 criptograficamente seguro. Universal: Node 14.17+, browsers\n * modernos, Edge runtimes (Workers/Vercel Edge/Cloudflare).\n *\n * Fallback puro-JS removido em 3.0 — `crypto.randomUUID()` é ubíquo desde\n * 2022. Se runtime não tem, lança `NeetruError('runtime_unsupported')`\n * (consumer deve polyfillar).\n *\n * **L-01 fix (Opus review 2026-05-27):** antes lançava `Error` cru, que\n * não era pego por `catch (err) { if (err instanceof NeetruError) ... }`\n * dos consumers. Agora throw canonical.\n *\n * @throws NeetruError('runtime_unsupported') se `crypto.randomUUID` não disponível.\n */\nexport function generateIdempotencyKey(): string {\n // WebCrypto (browser/Edge) e Node 14.17+ expõem `crypto.randomUUID()`.\n // Node anterior precisa de `node:crypto` mas SDK 3.0 exige Node ≥20 (vide engines).\n const cryptoObj: { randomUUID?: () => string } | undefined =\n typeof globalThis !== 'undefined' && 'crypto' in globalThis\n ? (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n : undefined;\n if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {\n return cryptoObj.randomUUID();\n }\n throw new NeetruError(\n 'runtime_unsupported',\n 'crypto.randomUUID not available in this runtime. SDK 3.0 requires Node ≥20 or modern browser/Edge.',\n );\n}\n\n/** Regex UUID v4 — validação para uso interno. */\nexport const UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n","/**\r\n * HTTP transport interno do SDK.\r\n *\r\n * Responsabilidades:\r\n * - Construir URL absoluta a partir de `baseUrl` + path\r\n * - Injetar Bearer token quando `requireAuth=true`\r\n * - Mapear status HTTP → `NeetruError` com `code` estável\r\n * - Parse defensivo de JSON (não lança em body vazio em 204)\r\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\r\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\r\n * presente.\r\n *\r\n * Política de retries (mudança breaking 3.0):\r\n * - GET/HEAD: default 2 retries (3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: default **0 retries** — mutations podem duplicar\r\n * side effects (ex: `usage.report` incrementa contador). Caller pode\r\n * reativar passando `retries: N` explicitamente ou usar `Idempotency-Key`\r\n * header (gerado automaticamente se `opts.idempotencyKey=true`).\r\n *\r\n * Idempotency-Key (3.0):\r\n * - `opts.idempotencyKey === true` → SDK gera UUID v4 e injeta header.\r\n * - `opts.idempotencyKey === 'uuid-string'` → usa o valor passado.\r\n * - Caller mantém retentos client-side se quiser; Core dedup serverside\r\n * (vide `docs/CORE_CHANGES_REQUIRED.md` §2).\r\n */\r\nimport { NeetruError, type NeetruErrorCode } from './errors';\r\nimport { generateIdempotencyKey } from './idempotency';\r\nimport type { ResolvedConfig } from './types';\r\n\r\n/** Opções da request HTTP. */\r\nexport interface HttpRequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\r\n path: string;\r\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\r\n query?: Record<string, string | number | boolean | undefined>;\r\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\r\n body?: unknown;\r\n /**\r\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\r\n * se config.apiKey ausente. Default false.\r\n */\r\n requireAuth?: boolean;\r\n /** Cabeçalhos extras. */\r\n headers?: Record<string, string>;\r\n /**\r\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\r\n * `network_error`).\r\n *\r\n * Default (3.0):\r\n * - GET/HEAD: 2 (= 3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: 0 — mutations podem duplicar side effects.\r\n *\r\n * Caller pode reativar passando explicitamente: `retries: 2`.\r\n */\r\n retries?: number;\r\n /**\r\n * Injeta `Idempotency-Key` header pra dedup serverside (3.0):\r\n * - `true` → SDK gera UUID v4 cripto-seguro automaticamente.\r\n * - string → usa o valor passado (deve ser UUID v4).\r\n * - `undefined`/`false` → sem header.\r\n */\r\n idempotencyKey?: boolean | string;\r\n}\r\n\r\nconst DEFAULT_RETRIES_READ = 2;\r\nconst DEFAULT_RETRIES_WRITE = 0;\r\nconst READ_METHODS = new Set(['GET', 'HEAD']);\r\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\r\n 'rate_limited',\r\n 'server_error',\r\n 'network_error',\r\n]);\r\n\r\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\r\nfunction backoffMs(attempt: number): number {\r\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\r\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\r\n return Math.max(50, Math.round(base + jitter));\r\n}\r\n\r\n/**\r\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\r\n * inválido. RFC 9110 §10.2.3.\r\n */\r\nfunction parseRetryAfter(value: string | null): number | null {\r\n if (!value) return null;\r\n const secs = Number(value);\r\n if (Number.isFinite(secs) && secs >= 0) return Math.round(secs * 1000);\r\n const dateMs = Date.parse(value);\r\n if (Number.isFinite(dateMs)) {\r\n const delta = dateMs - Date.now();\r\n if (delta > 0) return delta;\r\n }\r\n return null;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/** Mapeamento status → code estável do NeetruError. */\r\nfunction statusToCode(status: number): NeetruErrorCode {\r\n if (status === 401) return 'unauthorized';\r\n if (status === 403) return 'forbidden';\r\n if (status === 404) return 'not_found';\r\n if (status === 422 || status === 400) return 'validation_failed';\r\n if (status === 429) return 'rate_limited';\r\n if (status >= 500) return 'server_error';\r\n return 'unknown';\r\n}\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\r\n // Trim trailing slash em base e leading em path pra evitar `//`.\r\n const base = baseUrl.replace(/\\/+$/, '');\r\n const p = path.startsWith('/') ? path : `/${path}`;\r\n const url = new URL(`${base}${p}`);\r\n if (query) {\r\n for (const [k, v] of Object.entries(query)) {\r\n if (v === undefined) continue;\r\n url.searchParams.set(k, String(v));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\r\nasync function safeJson(res: Response): Promise<unknown> {\r\n const text = await res.text();\r\n if (!text) return undefined;\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\r\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\r\n * automático em códigos transientes (rate_limited/server_error/network_error)\r\n * conforme `opts.retries` (default 2 = 3 tentativas).\r\n */\r\nexport async function httpRequest<T>(\r\n config: ResolvedConfig,\r\n opts: HttpRequestOptions,\r\n): Promise<T> {\r\n const method = opts.method ?? 'GET';\r\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\r\n // Default por método: reads tentam 2x, writes 0x. Caller override sempre vence.\r\n const defaultRetries = READ_METHODS.has(method)\r\n ? DEFAULT_RETRIES_READ\r\n : DEFAULT_RETRIES_WRITE;\r\n const maxRetries = opts.retries ?? defaultRetries;\r\n\r\n const headers: Record<string, string> = {\r\n accept: 'application/json',\r\n ...opts.headers,\r\n };\r\n\r\n if (opts.requireAuth) {\r\n if (!config.apiKey) {\r\n throw new NeetruError(\r\n 'missing_api_key',\r\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\r\n );\r\n }\r\n headers.authorization = `Bearer ${config.apiKey}`;\r\n }\r\n\r\n // Idempotency-Key: gerado por chamada se solicitado. UUID v4 cripto-seguro.\r\n if (opts.idempotencyKey) {\r\n const key =\r\n typeof opts.idempotencyKey === 'string'\r\n ? opts.idempotencyKey\r\n : generateIdempotencyKey();\r\n headers['idempotency-key'] = key;\r\n }\r\n\r\n // Body só é serializado uma vez — reusado entre tentativas.\r\n const bodyString =\r\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\r\n ? JSON.stringify(opts.body)\r\n : undefined;\r\n if (bodyString !== undefined) {\r\n headers['content-type'] = 'application/json';\r\n }\r\n\r\n let lastError: NeetruError | null = null;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n const init: RequestInit = { method, headers };\r\n if (bodyString !== undefined) init.body = bodyString;\r\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\r\n // requer Node 18+ ou browsers recentes.\r\n init.signal = AbortSignal.timeout(30_000);\r\n\r\n let res: Response;\r\n try {\r\n res = await config.fetch(url, init);\r\n } catch (err) {\r\n const message =\r\n err instanceof DOMException && err.name === 'TimeoutError'\r\n ? 'Network error: timeout after 30s'\r\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\r\n lastError = new NeetruError('network_error', message);\r\n if (attempt < maxRetries) {\r\n await sleep(backoffMs(attempt));\r\n continue;\r\n }\r\n throw lastError;\r\n }\r\n\r\n const requestId =\r\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\r\n\r\n if (res.ok) {\r\n const parsed = await safeJson(res);\r\n return parsed as T;\r\n }\r\n\r\n const body = (await safeJson(res)) as\r\n | { error?: { code?: string; message?: string } | string }\r\n | undefined;\r\n let code: string = statusToCode(res.status);\r\n let message = `HTTP ${res.status}`;\r\n if (body && typeof body === 'object' && 'error' in body) {\r\n const errField = body.error;\r\n if (typeof errField === 'string') {\r\n message = errField;\r\n } else if (errField && typeof errField === 'object') {\r\n if (typeof errField.code === 'string') code = errField.code;\r\n if (typeof errField.message === 'string') message = errField.message;\r\n }\r\n }\r\n const err = new NeetruError(code, message, res.status, requestId);\r\n lastError = err;\r\n\r\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\r\n if (isRetryable && attempt < maxRetries) {\r\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\r\n const delay = retryAfter ?? backoffMs(attempt);\r\n await sleep(delay);\r\n continue;\r\n }\r\n throw err;\r\n }\r\n\r\n // Unreachable — loop sempre retorna ou lança. Safety net.\r\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\r\n}\r\n","/**\n * Validators centralizados pra inputs do SDK que precisam casar com\n * regex do Core.\n *\n * **M-03 fix (Opus review 2026-05-27):** o Core rejeita productId que\n * não bate `/^[a-z0-9][a-z0-9_-]{0,62}$/` com 400 `validation_failed`.\n * O SDK não pré-validava — consumer recebia erro só em runtime de\n * produção, depois do round-trip de rede. Agora SDK throws localmente\n * antes do fetch.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Regex canonical de IDs no Neetru Core (productId, tenantId, etc).\n * Espelha `SDK_ID_RE` em `neetru_core/src/lib/schemas/sdk.ts`.\n */\nexport const SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;\n\n/**\n * Throws `NeetruError('validation_failed')` se `value` não bater no\n * formato canonical.\n *\n * @example\n * ```ts\n * assertValidSdkId('productId', 'gestovendas'); // ok\n * assertValidSdkId('productId', 'GestoVendas'); // throws\n * ```\n */\nexport function assertValidSdkId(field: string, value: string): void {\n if (typeof value !== 'string' || !SDK_ID_RE.test(value)) {\n throw new NeetruError(\n 'validation_failed',\n `${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`,\n );\n }\n}\n","/**\n * Support namespace — criar e listar tickets via SDK (3.0).\n *\n * **Breaking 3.0**:\n * - Endpoint mudou de `/api/v1/products/{slug}/tickets` (que **NUNCA existiu\n * no Core** — vaporware testado) para `/api/sdk/v1/support/tickets`\n * (canonical SDK path).\n * - `productId` agora é obrigatório no body/query (default resolve do\n * `config.productId`; throw `validation_failed` se ambos ausentes).\n * - `createTicket()` envia `Idempotency-Key` header (anti dup-create\n * em retry transient — Core dedup TTL 24h conforme CORE_CHANGES_REQUIRED §1).\n *\n * **Pré-requisito Core**: endpoint `/api/sdk/v1/support/tickets` (POST + GET)\n * precisa existir no Core antes do SDK 3.0 publish. Vide\n * `docs/CORE_CHANGES_REQUIRED.md` §1 para contrato esperado.\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport { assertValidSdkId } from './validators';\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 productId?: 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:\n typeof r.productId === 'string'\n ? r.productId\n : typeof r.productSlug === 'string'\n ? r.productSlug\n : undefined,\n };\n}\n\nfunction resolveProductId(\n config: ResolvedConfig,\n ...candidates: Array<string | undefined>\n): string {\n for (const c of candidates) {\n if (typeof c === 'string' && c.length > 0) {\n assertValidSdkId('productSlug', c); // M-03: pré-valida client-side\n return c;\n }\n }\n if (config.productId) {\n assertValidSdkId('productId', config.productId);\n return config.productId;\n }\n throw new NeetruError(\n 'validation_failed',\n 'productId required (pass to input.productSlug or set on createNeetruClient)',\n );\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. `productSlug` no input\n * (ou `config.productId` default) vai como `productId` no body do Core.\n *\n * Endpoint canonical: `POST /api/sdk/v1/support/tickets`.\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(\n 'validation_failed',\n `severity must be one of ${VALID_SEVERITIES.join(', ')}`,\n );\n }\n\n const productId = resolveProductId(config, input.productSlug);\n // M-03: validação acontece dentro de resolveProductId; aqui só usa.\n\n const body: Record<string, unknown> = {\n productId,\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n // L-05 fix: propaga metadata quando consumer enviar.\n if (input.metadata && typeof input.metadata === 'object') {\n body.metadata = input.metadata;\n }\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: '/api/sdk/v1/support/tickets',\n body,\n requireAuth: true,\n idempotencyKey: 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. `productSlug` no contexto.\n *\n * Endpoint canonical: `GET /api/sdk/v1/support/tickets?productId=...`.\n */\n async listMyTickets(options?: { productSlug?: string }): Promise<SupportTicket[]> {\n const productId = resolveProductId(config, options?.productSlug);\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/sdk/v1/support/tickets',\n query: { productId },\n requireAuth: true,\n });\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && 'tickets' in raw\n ? ((raw as { tickets?: unknown[] }).tickets ?? [])\n : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
|