@vitrina/api 15.21.0 → 15.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -76,7 +76,8 @@ var ERROR_CODES = [
76
76
  "TASADOR_ANON_DAILY_LIMIT",
77
77
  "TASADOR_CAPACITY_REACHED",
78
78
  "CLINIC_PLAN_REQUIRED",
79
- "TENANT_BILLING_PAUSED"
79
+ "TENANT_BILLING_PAUSED",
80
+ "WAITING_SUBJECT_CHANGED"
80
81
  ];
81
82
 
82
83
  // src/errors.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":["createHmac","timingSafeEqual","createOpenApiClient","randomUUID"],"mappings":"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","file":"index.cjs","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'ACCOUNT_DELETION_BLOCKED'\n | 'CONFIRMATION_REQUIRED'\n | 'VERSION_CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'MODULE_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'VEHICLE_VALUE_OUT_OF_RANGE'\n | 'PRICE_CHANGE_CONFIRMATION_REQUIRED'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'FINANZAS_SYNC_THROTTLED'\n | 'FINANZAS_CLAVE_ERROR'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'AUDIO_RENDITION_PENDING'\n | 'MEDIA_SIGNING_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'ACCOUNT_DELETION_BLOCKED',\n 'CONFIRMATION_REQUIRED',\n 'VERSION_CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'MODULE_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'VEHICLE_VALUE_OUT_OF_RANGE',\n 'PRICE_CHANGE_CONFIRMATION_REQUIRED',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'FINANZAS_SYNC_THROTTLED',\n 'FINANZAS_CLAVE_ERROR',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'AUDIO_RENDITION_PENDING',\n 'MEDIA_SIGNING_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
1
+ {"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":["createHmac","timingSafeEqual","createOpenApiClient","randomUUID"],"mappings":";;;;;;;;;;;;AA8EO,IAAM,WAAA,GAAoC;AAAA,EAC/C,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,0BAAA;AAAA,EACA,uBAAA;AAAA,EACA,kBAAA;AAAA,EACA,iBAAA;AAAA,EACA,0BAAA;AAAA,EACA,sBAAA;AAAA,EACA,oBAAA;AAAA,EACA,wBAAA;AAAA,EACA,0BAAA;AAAA,EACA,4BAAA;AAAA,EACA,oCAAA;AAAA,EACA,qBAAA;AAAA,EACA,mBAAA;AAAA,EACA,wBAAA;AAAA,EACA,gBAAA;AAAA,EACA,cAAA;AAAA,EACA,yBAAA;AAAA,EACA,sBAAA;AAAA,EACA,MAAA;AAAA,EACA,gBAAA;AAAA,EACA,kCAAA;AAAA,EACA,+BAAA;AAAA,EACA,iCAAA;AAAA,EACA,kBAAA;AAAA,EACA,yBAAA;AAAA,EACA,6BAAA;AAAA,EACA,kCAAA;AAAA,EACA,iCAAA;AAAA,EACA,wBAAA;AAAA,EACA,wBAAA;AAAA,EACA,yBAAA;AAAA,EACA,yBAAA;AAAA,EACA,2BAAA;AAAA,EACA,iCAAA;AAAA,EACA,2BAAA;AAAA,EACA,8BAAA;AAAA,EACA,sBAAA;AAAA,EACA,6BAAA;AAAA,EACA,2BAAA;AAAA,EACA,0BAAA;AAAA,EACA,2BAAA;AAAA,EACA,6BAAA;AAAA,EACA,oBAAA;AAAA,EACA,mBAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA,qBAAA;AAAA,EACA,wBAAA;AAAA,EACA,qBAAA;AAAA,EACA,mBAAA;AAAA,EACA,qBAAA;AAAA,EACA,iBAAA;AAAA,EACA,mBAAA;AAAA,EACA,iBAAA;AAAA,EACA,uBAAA;AAAA,EACA,yBAAA;AAAA,EACA,0BAAA;AAAA,EACA,0BAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA;AACF;;;AClGO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAqBzC,WAAA,CACE,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EAC5B;AACA,IAAA,KAAA,CAAM,KAAK,OAAA,IAAW,CAAA,kBAAA,EAAqB,KAAK,IAAI,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,CAAG,CAAA;AACvE,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AACpB,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,YAAA;AACxB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,gBAAgB,OAAA,CAAQ,aAAA;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,aAAA,GAAyB;AAC3B,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,cAAA,GAA0B;AAC5B,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,WAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,UAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,qBAAA,GAAiC;AACnC,IAAA,OAAO,KAAK,IAAA,KAAS,0BAAA;AAAA,EACvB;AACF;AAQO,IAAM,oBAAA,GAAN,cAAmC,eAAA,CAAgB;AAAA,EAOxD,WAAA,CACE,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EAC5B;AACA,IAAA,KAAA,CAAM,MAAA,EAAQ,MAAM,OAAO,CAAA;AAC3B,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AACZ,IAAA,MAAM,cAAA,GACJ,KAAK,OAAA,EACJ,OAAA;AACH,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,cAAA,IAAkB,EAAC;AAClD,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA,IAAQ,KAAK,OAAA,CAAQ,CAAC,GAAG,IAAA,IAAQ,EAAA;AAAA,EACpD;AAAA;AAAA,EAGA,IAAI,SAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,IAAA,KAAS,kBAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,SAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,IAAA,KAAS,kBAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,gBAAA,GAA6B;AAC/B,IAAA,OAAO,KAAK,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,EACvC;AACF;AAEA,SAAS,WAAA,CAAY,SAAkB,MAAA,EAA8B;AACnE,EAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC1C,IAAA,MAAM,aAAA,GAAgB,OAAA;AACtB,IAAA,MAAM,SAAA,GACJ,cAAc,KAAA,IAAS,OAAO,cAAc,KAAA,KAAU,QAAA,GACjD,cAAc,KAAA,GACd,OAAA;AACP,IAAA,IACE,OAAO,SAAA,CAAU,IAAA,KAAS,YAC1B,OAAO,SAAA,CAAU,YAAY,QAAA,EAC7B;AACA,MAAA,OAAO,SAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,QAAQ,MAAM,CAAA,mCAAA;AAAA,GACzB;AACF;AAGO,SAAS,UAAA,CACd,MAAA,EACA,OAAA,EACA,OAAA,GAA2B,EAAC,EACX;AACjB,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,OAAA,EAAS,MAAM,CAAA;AACxC,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,kBAAA,IAAsB,IAAA,CAAK,SAAS,kBAAA,EAAoB;AACxE,IAAA,OAAO,IAAI,oBAAA,CAAqB,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA;AAAA,EACvD;AACA,EAAA,OAAO,IAAI,eAAA,CAAgB,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA;AAClD;AAgBO,SAAS,MAAA,CACd,MAAA,EACA,OAAA,GAA2B,EAAC,EACzB;AACH,EAAA,IAAI,MAAA,CAAO,UAAU,MAAA,EAAW;AAC9B,IAAA,MAAM,WAAW,MAAA,CAAO,QAAA,CAAS,MAAA,EAAQ,MAAA,CAAO,OAAO,OAAO,CAAA;AAAA,EAChE;AACA,EAAA,IAAI,MAAA,CAAO,SAAS,MAAA,EAAW;AAC7B,IAAA,MAAM,IAAI,eAAA;AAAA,MACR,OAAO,QAAA,CAAS,MAAA;AAAA,MAChB;AAAA,QACE,IAAA,EAAM,gBAAA;AAAA,QACN,OAAA,EAAS;AAAA,OACX;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB;ACnJA,SAAS,qBACP,MAAA,EAC0C;AAC1C,EAAA,MAAM,QAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA,EAAG;AACpC,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC3B,IAAA,IAAI,OAAO,EAAA,EAAI;AACf,IAAA,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CAAE,IAAA,EAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,CAAC,EAAE,IAAA,EAAK;AAAA,EAC5D;AACA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA;AAChC,EAAA,IAAI,CAAC,OAAO,QAAA,CAAS,SAAS,KAAK,CAAC,KAAA,CAAM,IAAI,OAAO,IAAA;AACrD,EAAA,OAAO,EAAE,SAAA,EAAW,EAAA,EAAI,KAAA,CAAM,EAAA,EAAG;AACnC;AASO,SAAS,uBACd,MAAA,EAKc;AACd,EAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,MAAA,CAAO,eAAe,CAAA;AAC1D,EAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,kBAAA,EAAmB;AAE5D,EAAA,MAAM,IAAA,GACJ,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,GACtB,OAAO,OAAA,GACP,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,MAAM,CAAA;AACpC,EAAA,MAAM,QAAA,GAAWA,iBAAA,CAAW,QAAA,EAAU,MAAA,CAAO,MAAM,CAAA,CAChD,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,SAAS,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA,CACpC,OAAO,KAAK,CAAA;AAEf,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,IAAI,MAAM,CAAA;AAC3C,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU,MAAM,CAAA;AACzC,EAAA,MAAM,mBACJ,KAAA,CAAM,MAAA,KAAW,KAAK,MAAA,IAAUC,sBAAA,CAAgB,OAAO,IAAI,CAAA;AAC7D,EAAA,IAAI,CAAC,gBAAA,EAAkB,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,eAAA,EAAgB;AAEnE,EAAA,MAAM,SAAA,GAAY,OAAO,gBAAA,IAAoB,GAAA;AAC7C,EAAA,MAAM,GAAA,GAAM,OAAO,OAAA,IAAW,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AAC1D,EAAA,IAAI,KAAK,GAAA,CAAI,GAAA,GAAM,MAAA,CAAO,SAAS,IAAI,SAAA,EAAW;AAChD,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,OAAA,EAAQ;AAAA,EACtC;AACA,EAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AACpB;AAEO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAG/C,YAAY,MAAA,EAAuD;AACjE,IAAA,KAAA,CAAM,CAAA,uCAAA,EAA0C,MAAM,CAAA,CAAE,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AACF;AAQO,SAAS,uBACd,MAAA,EAK6B;AAC7B,EAAA,MAAM,MAAA,GAAS,uBAAuB,MAAM,CAAA;AAC5C,EAAA,IAAI,CAAC,MAAA,CAAO,EAAA,QAAU,IAAI,qBAAA,CAAsB,OAAO,MAAM,CAAA;AAC7D,EAAA,MAAM,IAAA,GACJ,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,GACtB,OAAO,OAAA,GACP,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,MAAM,CAAA;AACpC,EAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AACxB;;;AC7IO,IAAM,6BAAA,GAAN,cAA4C,KAAA,CAAM;AAAA,EAGvD,YAAY,KAAA,EAA6B;AACvC,IAAA,KAAA;AAAA,MACE,MAAM,YAAA,GACF,CAAA,MAAA,EAAS,KAAA,CAAM,EAAE,KAAK,KAAA,CAAM,IAAI,CAAA,+CAAA,EAA6C,KAAA,CAAM,YAAY,CAAA,CAAA,GAC/F,CAAA,MAAA,EAAS,MAAM,EAAE,CAAA,EAAA,EAAK,MAAM,IAAI,CAAA,yBAAA;AAAA,KACtC;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,+BAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AACF;AAwBO,SAAS,sBAAsB,IAAA,EAAwB;AAK5D,EAAA,eAAe,cACb,KAAA,EACoB;AACpB,IAAA,IAAI,CAAC,KAAA,CAAM,QAAA,CAAS,KAAK,MAAM,IAAI,8BAA8B,KAAK,CAAA;AACtE,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,QAAA,CAAS,GAAA,EAAK,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC1E,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,MAAM,IAAI,IAAA,EAAK;AAAA,IACxB,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,GAAO,MAAA;AAAA,IACT;AACA,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,UAAA,CAAW,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM;AAAA,QACjC,UAAA,EAAY,MAAM,QAAA,CAAS,GAAA;AAAA,QAC3B,aAAA,EAAe;AAAA,OAChB,CAAA;AAAA,IACH;AACA,IAAA,OAAQ,IAAA,EAA8B,IAAA;AAAA,EACxC;AAEA,EAAA,OAAO;AAAA;AAAA,IAEL,eAAA,EAAiB,CACf,MAAA,KAKG,sBAAA,CAAuB,MAAM,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAelC,eACE,MAAA,EAKqB;AACrB,MAAA,MAAM,QAAA,GAAW,uBAA8B,MAAM,CAAA;AACrD,MAAA,OAAO,MAAA,CAAO,OAAO,QAAA,EAAU;AAAA,QAC7B,KAAA,EAAO,MAAyB,aAAA,CAAyB,QAAQ;AAAA,OAClE,CAAA;AAAA,IACH;AAAA,GACF;AACF;;;AC5DO,SAAS,aAAa,IAAA,EAA0C;AACrE,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA,EAAQ;AAC9B,IAAA,MAAM,IAAI,UAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA;AACvC,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,GAAI,aAAa,EAAE,aAAA,EAAe,UAAU,UAAU,CAAA,CAAA,KAAO,EAAC;AAAA,IAC9D,GAAG,IAAA,CAAK;AAAA,GACV;AACA,EAAA,MAAM,SAAA,GAAY,KAAK,KAAA,IAAS,KAAA;AAEhC,EAAA,MAAM,OAAOC,oCAAA,CAA2B;AAAA,IACtC,SAAS,IAAA,CAAK,OAAA;AAAA,IACd,OAAA;AAAA,IACA,KAAA,EAAO;AAAA,GACR,CAAA;AAED,EAAA,MAAM,SAAS,qBAAA,CAAsB,EAAE,OAAA,EAAS,KAAA,EAAO,WAAW,CAAA;AAElE,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,EAAE,QAAQ,CAAA;AACvC;;;ACnCA,SAAS,aAAa,IAAA,EAAsD;AAC1E,EAAA,MAAM,MAAA,GAAS,IAAA,EAAM,UAAA,EAAY,UAAA,IAAc,MAAM,UAAA,IAAc,IAAA;AACnE,EAAA,OAAO,MAAA,IAAU,MAAA;AACnB;AAyBA,gBAAuB,SACrB,SAAA,EACkC;AAClC,EAAA,IAAI,MAAA;AACJ,EAAA,WAAS;AAEP,IAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAU,MAAM,CAAA;AACrC,IAAA,IAAI,MAAA,CAAO,UAAU,MAAA,EAAW;AAC9B,MAAA,MAAM,UAAA,CAAW,MAAA,CAAO,QAAA,CAAS,MAAA,EAAQ,OAAO,KAAK,CAAA;AAAA,IACvD;AACA,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAM,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,EAAG;AACxC,IAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,IAAA,EAAM,MAAM,IAAA;AACpC,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,IAAA,CAAK,IAAI,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAA,GAAS,IAAA;AAAA,EACX;AACF;AAKA,eAAsB,YACpB,SAAA,EACc;AACd,EAAA,MAAM,QAAa,EAAC;AAEpB,EAAA,WAAA,MAAiB,QAAQ,QAAA,CAAS,SAAS,CAAA,EAAG,KAAA,CAAM,KAAK,IAAI,CAAA;AAC7D,EAAA,OAAO,KAAA;AACT;AC9EA,IAAM,WAAA,GAAc,wBAAA;AAab,SAAS,eAAe,GAAA,EAAmC;AAChE,EAAA,IAAI,CAAC,WAAA,CAAY,IAAA,CAAK,GAAG,CAAA,EAAG;AAC1B,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,gEAAA,EAAmE,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA;AAAA,KACxF;AAAA,EACF;AACA,EAAA,OAAO,EAAE,mBAAmB,GAAA,EAAI;AAClC;AASO,SAAS,uBAAuB,MAAA,EAAyB;AAC9D,EAAA,MAAM,EAAA,GAAKC,iBAAA,EAAW,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA;AACxC,EAAA,OAAO,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAA;AACtC","file":"index.cjs","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'ACCOUNT_DELETION_BLOCKED'\n | 'CONFIRMATION_REQUIRED'\n | 'VERSION_CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'MODULE_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'VEHICLE_VALUE_OUT_OF_RANGE'\n | 'PRICE_CHANGE_CONFIRMATION_REQUIRED'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'FINANZAS_SYNC_THROTTLED'\n | 'FINANZAS_CLAVE_ERROR'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'AUDIO_RENDITION_PENDING'\n | 'MEDIA_SIGNING_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED'\n | 'WAITING_SUBJECT_CHANGED';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'ACCOUNT_DELETION_BLOCKED',\n 'CONFIRMATION_REQUIRED',\n 'VERSION_CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'MODULE_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'VEHICLE_VALUE_OUT_OF_RANGE',\n 'PRICE_CHANGE_CONFIRMATION_REQUIRED',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'FINANZAS_SYNC_THROTTLED',\n 'FINANZAS_CLAVE_ERROR',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'AUDIO_RENDITION_PENDING',\n 'MEDIA_SIGNING_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n 'WAITING_SUBJECT_CHANGED',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -141461,7 +141461,7 @@ interface paths {
141461
141461
  aiReplied?: boolean | null;
141462
141462
  fromAd?: boolean | null;
141463
141463
  fromCampaign?: boolean | null;
141464
- /** @description true: only conversations where a customer is waiting on a human («Esperando respuesta»), team-wide. Waiting = open or pending, not filtered, on a channel you can reply in, and either an AI handoff nobody on the team has answered yet (a reply from ANY teammate clears it, including WhatsApp Business / Instagram app replies; a silent «Visita acordada» notice needs no answer) or a human-handled thread whose last message is the customer’s; waits older than 7 days are left out; and what the customer wrote still asks for a reply: a thread whose newest unanswered customer message was judged, with high confidence, as the customer closing the chat («ok», «gracias», 👍 after we answered) or owing nothing, a customer who paid (a transfer receipt after our bank details) or is at the store («llegué», «voy subiendo»), or a follow-up we promised for a later day that is not due yet, is left out (any other open promise of ours always stays). `closed`: exactly those left-out threads, still live (not resolved) — `waiting_state` says why, `waiting_due_at` when a promised follow-up falls due. Each row carries `waiting_since`, `waiting_state` and `waiting_due_at`. Pair with `sort=wait` for the queue order. */
141464
+ /** @description true: only conversations where a customer is waiting on a human («Esperando respuesta»), team-wide. Waiting = open or pending, not filtered, on a channel you can reply in, and either an AI handoff nobody on the team has answered yet (a reply from ANY teammate clears it, including WhatsApp Business / Instagram app replies; a silent «Visita acordada» notice needs no answer) or a human-handled thread whose last message is the customer’s; waits older than 7 days are left out; and what the customer wrote still asks for a reply: a thread whose newest unanswered customer message was judged, with high confidence, as the customer closing the chat («ok», «gracias», 👍 after we answered) or owing nothing, a customer who paid (a transfer receipt after our bank details) or is at the store («llegué», «voy subiendo»), or a follow-up we promised for a later day that is not due yet, is left out (any other open promise of ours always stays). `closed`: exactly those left-out threads, still live (not resolved) — `waiting_state` says why, `waiting_due_at` when a promised follow-up falls due. Each row carries `waiting_since`, `waiting_state`, `waiting_due_at` and `waiting_subject_id` (the message, or handoff event, the wait is about — `POST /conversations/{id}/no-reply-needed` takes it as `subject_id`). Pair with `sort=wait` for the queue order. */
141465
141465
  waiting?: boolean | "closed" | null;
141466
141466
  /** @description `wait`: urgent (`handoff_urgent`) first, then by `waiting_since` — `order=desc` (default) is the longest wait first, `asc` the newest; conversations that are not waiting go last. */
141467
141467
  sort?: "created_at" | "updated_at" | "last_message_date" | "wait";
@@ -141606,7 +141606,8 @@ interface paths {
141606
141606
  * "handoff_urgent": false,
141607
141607
  * "waiting_since": null,
141608
141608
  * "waiting_state": null,
141609
- * "waiting_due_at": null
141609
+ * "waiting_due_at": null,
141610
+ * "waiting_subject_id": null
141610
141611
  * },
141611
141612
  * {
141612
141613
  * "id": "bbbbbbbb-0000-4000-8000-000000000002",
@@ -141746,7 +141747,8 @@ interface paths {
141746
141747
  * "handoff_urgent": false,
141747
141748
  * "waiting_since": null,
141748
141749
  * "waiting_state": null,
141749
- * "waiting_due_at": null
141750
+ * "waiting_due_at": null,
141751
+ * "waiting_subject_id": null
141750
141752
  * }
141751
141753
  * ],
141752
141754
  * "meta": {
@@ -142310,6 +142312,7 @@ interface paths {
142310
142312
  * "waiting_since": null,
142311
142313
  * "waiting_state": null,
142312
142314
  * "waiting_due_at": null,
142315
+ * "waiting_subject_id": null,
142313
142316
  * "ad_origin_nudge": null,
142314
142317
  * "handoff_resolved_suggested": false,
142315
142318
  * "message": [
@@ -147028,6 +147031,229 @@ interface paths {
147028
147031
  patch?: never;
147029
147032
  trace?: never;
147030
147033
  };
147034
+ "/conversations/{id}/no-reply-needed": {
147035
+ parameters: {
147036
+ query?: never;
147037
+ header?: never;
147038
+ path?: never;
147039
+ cookie?: never;
147040
+ };
147041
+ get?: never;
147042
+ put?: never;
147043
+ /**
147044
+ * Mark the current wait as needing no reply («No requiere respuesta»)
147045
+ * @description Takes the conversation out of «Esperando respuesta» for the wait the person saw: `subject_id` is the row’s `waiting_subject_id` (the newest customer message nobody on the team answered; with none pending, the open handoff’s event). If the customer wrote again since, the call answers 409 `WAITING_SUBJECT_CHANGED` (`details.subject_id` = the current one) and records nothing. The customer’s next message starts a new wait. Recorded as a correction of the `reply_owed` verdict on that subject (label `nothing_owed`); a click outranks any verdict on the same subject, and this workspace’s later verdicts read recent clicks as examples — never another workspace’s. Idempotent (one click per verdict, concurrent clicks included): already out of the queue → `changed: false`. Other 409s carry `details.reason`: `not_waiting`, or one the queue never hides — `urgent`, `payment_obligation` (a payment held open), `promised_human_contact` (the AI told the customer a person would contact them). `subject_kind` is `message` or `handoff`; `waiting_state` is the thread’s state after the call.
147046
+ */
147047
+ post: {
147048
+ parameters: {
147049
+ query?: never;
147050
+ header?: {
147051
+ /** @description Replay-safe retries: resending the SAME key with the SAME body returns the original response (`X-Idempotent-Replay: 1`) instead of creating a second copy — safe to send whenever a response might not have arrived. The same key with a DIFFERENT body answers `409 IDEMPOTENCY_KEY_CONFLICT`; use a fresh key per operation. */
147052
+ "Idempotency-Key"?: string;
147053
+ };
147054
+ path: {
147055
+ id: string;
147056
+ };
147057
+ cookie?: never;
147058
+ };
147059
+ requestBody?: {
147060
+ content: {
147061
+ /**
147062
+ * @example {
147063
+ * "subject_id": "01a17d4e-c0de-7a11-9d0e-5e1a7e5b0c11"
147064
+ * }
147065
+ */
147066
+ "application/json": {
147067
+ /** Format: uuid */
147068
+ subject_id: string;
147069
+ };
147070
+ };
147071
+ };
147072
+ responses: {
147073
+ /** @description `{ conversation_id, subject_id, subject_kind, waiting_state, changed }` */
147074
+ 200: {
147075
+ headers: {
147076
+ [name: string]: unknown;
147077
+ };
147078
+ content: {
147079
+ /**
147080
+ * @example {
147081
+ * "data": {
147082
+ * "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
147083
+ * "subject_id": "01a17d4e-c0de-7a11-9d0e-5e1a7e5b0c11",
147084
+ * "subject_kind": "message",
147085
+ * "waiting_state": "nothing_owed",
147086
+ * "changed": true
147087
+ * }
147088
+ * }
147089
+ */
147090
+ "application/json": {
147091
+ data?: unknown;
147092
+ meta?: {
147093
+ [key: string]: unknown;
147094
+ };
147095
+ };
147096
+ };
147097
+ };
147098
+ /** @description Validation error */
147099
+ 400: {
147100
+ headers: {
147101
+ [name: string]: unknown;
147102
+ };
147103
+ content: {
147104
+ "application/json": components["schemas"]["Error"];
147105
+ };
147106
+ };
147107
+ /** @description Unauthorized */
147108
+ 401: {
147109
+ headers: {
147110
+ [name: string]: unknown;
147111
+ };
147112
+ content: {
147113
+ "application/json": components["schemas"]["Error"];
147114
+ };
147115
+ };
147116
+ /** @description Forbidden */
147117
+ 403: {
147118
+ headers: {
147119
+ [name: string]: unknown;
147120
+ };
147121
+ content: {
147122
+ "application/json": components["schemas"]["Error"];
147123
+ };
147124
+ };
147125
+ /** @description Not found */
147126
+ 404: {
147127
+ headers: {
147128
+ [name: string]: unknown;
147129
+ };
147130
+ content: {
147131
+ "application/json": components["schemas"]["Error"];
147132
+ };
147133
+ };
147134
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
147135
+ 409: {
147136
+ headers: {
147137
+ [name: string]: unknown;
147138
+ };
147139
+ content: {
147140
+ "application/json": components["schemas"]["Error"];
147141
+ };
147142
+ };
147143
+ /** @description Rate limited */
147144
+ 429: {
147145
+ headers: {
147146
+ [name: string]: unknown;
147147
+ };
147148
+ content: {
147149
+ "application/json": components["schemas"]["Error"];
147150
+ };
147151
+ };
147152
+ };
147153
+ };
147154
+ /**
147155
+ * Undo «No requiere respuesta»
147156
+ * @description Removes the click `POST /no-reply-needed` recorded on the current wait (and the verdict row it created), so the conversation waits again as before. `subject_id` (optional) names the wait the undo is for: when the conversation now waits on another message, nothing changes. Nothing to undo → `changed: false`.
147157
+ */
147158
+ delete: {
147159
+ parameters: {
147160
+ query?: {
147161
+ subject_id?: string;
147162
+ };
147163
+ header?: never;
147164
+ path: {
147165
+ id: string;
147166
+ };
147167
+ cookie?: never;
147168
+ };
147169
+ requestBody?: never;
147170
+ responses: {
147171
+ /** @description `{ conversation_id, subject_id, subject_kind, waiting_state, changed }` */
147172
+ 200: {
147173
+ headers: {
147174
+ [name: string]: unknown;
147175
+ };
147176
+ content: {
147177
+ /**
147178
+ * @example {
147179
+ * "data": {
147180
+ * "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
147181
+ * "subject_id": "01a17d4e-c0de-7a11-9d0e-5e1a7e5b0c11",
147182
+ * "subject_kind": "message",
147183
+ * "waiting_state": "nothing_owed",
147184
+ * "changed": true
147185
+ * }
147186
+ * }
147187
+ */
147188
+ "application/json": {
147189
+ data?: unknown;
147190
+ meta?: {
147191
+ [key: string]: unknown;
147192
+ };
147193
+ };
147194
+ };
147195
+ };
147196
+ /** @description Validation error */
147197
+ 400: {
147198
+ headers: {
147199
+ [name: string]: unknown;
147200
+ };
147201
+ content: {
147202
+ "application/json": components["schemas"]["Error"];
147203
+ };
147204
+ };
147205
+ /** @description Unauthorized */
147206
+ 401: {
147207
+ headers: {
147208
+ [name: string]: unknown;
147209
+ };
147210
+ content: {
147211
+ "application/json": components["schemas"]["Error"];
147212
+ };
147213
+ };
147214
+ /** @description Forbidden */
147215
+ 403: {
147216
+ headers: {
147217
+ [name: string]: unknown;
147218
+ };
147219
+ content: {
147220
+ "application/json": components["schemas"]["Error"];
147221
+ };
147222
+ };
147223
+ /** @description Not found */
147224
+ 404: {
147225
+ headers: {
147226
+ [name: string]: unknown;
147227
+ };
147228
+ content: {
147229
+ "application/json": components["schemas"]["Error"];
147230
+ };
147231
+ };
147232
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
147233
+ 409: {
147234
+ headers: {
147235
+ [name: string]: unknown;
147236
+ };
147237
+ content: {
147238
+ "application/json": components["schemas"]["Error"];
147239
+ };
147240
+ };
147241
+ /** @description Rate limited */
147242
+ 429: {
147243
+ headers: {
147244
+ [name: string]: unknown;
147245
+ };
147246
+ content: {
147247
+ "application/json": components["schemas"]["Error"];
147248
+ };
147249
+ };
147250
+ };
147251
+ };
147252
+ options?: never;
147253
+ head?: never;
147254
+ patch?: never;
147255
+ trace?: never;
147256
+ };
147031
147257
  "/conversations/{id}/filter-sender": {
147032
147258
  parameters: {
147033
147259
  query?: never;
@@ -166653,7 +166879,7 @@ interface paths {
166653
166879
  };
166654
166880
  /**
166655
166881
  * Conversation volume and resolution
166656
- * @description Volume, resolution and handling time over the window — the top-level view the other reports break down.
166882
+ * @description Volume, resolution and handling time over the window — the top-level view the other reports break down. `waiting_clears` counts how «Esperando respuesta» was cleared in the window: `by_jev` (the `reply_owed` verdict took a wait out), `by_button` («No requiere respuesta», `POST /conversations/{id}/no-reply-needed`; undone clicks excluded) and `corrected_to_owed` (a person answered a thread Jev had taken out, so it was owed after all); `null` when it could not be read.
166657
166883
  */
166658
166884
  get: {
166659
166885
  parameters: {
@@ -166761,7 +166987,12 @@ interface paths {
166761
166987
  * ]
166762
166988
  * },
166763
166989
  * "facturables": 4,
166764
- * "solo_humano": 14
166990
+ * "solo_humano": 14,
166991
+ * "waiting_clears": {
166992
+ * "by_jev": 9,
166993
+ * "by_button": 2,
166994
+ * "corrected_to_owed": 1
166995
+ * }
166765
166996
  * },
166766
166997
  * "meta": {
166767
166998
  * "window": {
@@ -272209,7 +272440,7 @@ declare function createClient(opts: CreateClientOptions): VitrinaClient;
272209
272440
  * is regenerated — narrow on the ones you handle, never assume this
272210
272441
  * list is exhaustive at runtime.
272211
272442
  */
272212
- type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'ACCOUNT_DELETION_BLOCKED' | 'CONFIRMATION_REQUIRED' | 'VERSION_CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'MODULE_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'VEHICLE_VALUE_OUT_OF_RANGE' | 'PRICE_CHANGE_CONFIRMATION_REQUIRED' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'FINANZAS_SYNC_THROTTLED' | 'FINANZAS_CLAVE_ERROR' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'AUDIO_RENDITION_PENDING' | 'MEDIA_SIGNING_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED';
272443
+ type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'ACCOUNT_DELETION_BLOCKED' | 'CONFIRMATION_REQUIRED' | 'VERSION_CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'MODULE_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'VEHICLE_VALUE_OUT_OF_RANGE' | 'PRICE_CHANGE_CONFIRMATION_REQUIRED' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'FINANZAS_SYNC_THROTTLED' | 'FINANZAS_CLAVE_ERROR' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'AUDIO_RENDITION_PENDING' | 'MEDIA_SIGNING_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED' | 'WAITING_SUBJECT_CHANGED';
272213
272444
  declare const ERROR_CODES: readonly ErrorCode[];
272214
272445
 
272215
272446
  /**
package/dist/index.d.ts CHANGED
@@ -141461,7 +141461,7 @@ interface paths {
141461
141461
  aiReplied?: boolean | null;
141462
141462
  fromAd?: boolean | null;
141463
141463
  fromCampaign?: boolean | null;
141464
- /** @description true: only conversations where a customer is waiting on a human («Esperando respuesta»), team-wide. Waiting = open or pending, not filtered, on a channel you can reply in, and either an AI handoff nobody on the team has answered yet (a reply from ANY teammate clears it, including WhatsApp Business / Instagram app replies; a silent «Visita acordada» notice needs no answer) or a human-handled thread whose last message is the customer’s; waits older than 7 days are left out; and what the customer wrote still asks for a reply: a thread whose newest unanswered customer message was judged, with high confidence, as the customer closing the chat («ok», «gracias», 👍 after we answered) or owing nothing, a customer who paid (a transfer receipt after our bank details) or is at the store («llegué», «voy subiendo»), or a follow-up we promised for a later day that is not due yet, is left out (any other open promise of ours always stays). `closed`: exactly those left-out threads, still live (not resolved) — `waiting_state` says why, `waiting_due_at` when a promised follow-up falls due. Each row carries `waiting_since`, `waiting_state` and `waiting_due_at`. Pair with `sort=wait` for the queue order. */
141464
+ /** @description true: only conversations where a customer is waiting on a human («Esperando respuesta»), team-wide. Waiting = open or pending, not filtered, on a channel you can reply in, and either an AI handoff nobody on the team has answered yet (a reply from ANY teammate clears it, including WhatsApp Business / Instagram app replies; a silent «Visita acordada» notice needs no answer) or a human-handled thread whose last message is the customer’s; waits older than 7 days are left out; and what the customer wrote still asks for a reply: a thread whose newest unanswered customer message was judged, with high confidence, as the customer closing the chat («ok», «gracias», 👍 after we answered) or owing nothing, a customer who paid (a transfer receipt after our bank details) or is at the store («llegué», «voy subiendo»), or a follow-up we promised for a later day that is not due yet, is left out (any other open promise of ours always stays). `closed`: exactly those left-out threads, still live (not resolved) — `waiting_state` says why, `waiting_due_at` when a promised follow-up falls due. Each row carries `waiting_since`, `waiting_state`, `waiting_due_at` and `waiting_subject_id` (the message, or handoff event, the wait is about — `POST /conversations/{id}/no-reply-needed` takes it as `subject_id`). Pair with `sort=wait` for the queue order. */
141465
141465
  waiting?: boolean | "closed" | null;
141466
141466
  /** @description `wait`: urgent (`handoff_urgent`) first, then by `waiting_since` — `order=desc` (default) is the longest wait first, `asc` the newest; conversations that are not waiting go last. */
141467
141467
  sort?: "created_at" | "updated_at" | "last_message_date" | "wait";
@@ -141606,7 +141606,8 @@ interface paths {
141606
141606
  * "handoff_urgent": false,
141607
141607
  * "waiting_since": null,
141608
141608
  * "waiting_state": null,
141609
- * "waiting_due_at": null
141609
+ * "waiting_due_at": null,
141610
+ * "waiting_subject_id": null
141610
141611
  * },
141611
141612
  * {
141612
141613
  * "id": "bbbbbbbb-0000-4000-8000-000000000002",
@@ -141746,7 +141747,8 @@ interface paths {
141746
141747
  * "handoff_urgent": false,
141747
141748
  * "waiting_since": null,
141748
141749
  * "waiting_state": null,
141749
- * "waiting_due_at": null
141750
+ * "waiting_due_at": null,
141751
+ * "waiting_subject_id": null
141750
141752
  * }
141751
141753
  * ],
141752
141754
  * "meta": {
@@ -142310,6 +142312,7 @@ interface paths {
142310
142312
  * "waiting_since": null,
142311
142313
  * "waiting_state": null,
142312
142314
  * "waiting_due_at": null,
142315
+ * "waiting_subject_id": null,
142313
142316
  * "ad_origin_nudge": null,
142314
142317
  * "handoff_resolved_suggested": false,
142315
142318
  * "message": [
@@ -147028,6 +147031,229 @@ interface paths {
147028
147031
  patch?: never;
147029
147032
  trace?: never;
147030
147033
  };
147034
+ "/conversations/{id}/no-reply-needed": {
147035
+ parameters: {
147036
+ query?: never;
147037
+ header?: never;
147038
+ path?: never;
147039
+ cookie?: never;
147040
+ };
147041
+ get?: never;
147042
+ put?: never;
147043
+ /**
147044
+ * Mark the current wait as needing no reply («No requiere respuesta»)
147045
+ * @description Takes the conversation out of «Esperando respuesta» for the wait the person saw: `subject_id` is the row’s `waiting_subject_id` (the newest customer message nobody on the team answered; with none pending, the open handoff’s event). If the customer wrote again since, the call answers 409 `WAITING_SUBJECT_CHANGED` (`details.subject_id` = the current one) and records nothing. The customer’s next message starts a new wait. Recorded as a correction of the `reply_owed` verdict on that subject (label `nothing_owed`); a click outranks any verdict on the same subject, and this workspace’s later verdicts read recent clicks as examples — never another workspace’s. Idempotent (one click per verdict, concurrent clicks included): already out of the queue → `changed: false`. Other 409s carry `details.reason`: `not_waiting`, or one the queue never hides — `urgent`, `payment_obligation` (a payment held open), `promised_human_contact` (the AI told the customer a person would contact them). `subject_kind` is `message` or `handoff`; `waiting_state` is the thread’s state after the call.
147046
+ */
147047
+ post: {
147048
+ parameters: {
147049
+ query?: never;
147050
+ header?: {
147051
+ /** @description Replay-safe retries: resending the SAME key with the SAME body returns the original response (`X-Idempotent-Replay: 1`) instead of creating a second copy — safe to send whenever a response might not have arrived. The same key with a DIFFERENT body answers `409 IDEMPOTENCY_KEY_CONFLICT`; use a fresh key per operation. */
147052
+ "Idempotency-Key"?: string;
147053
+ };
147054
+ path: {
147055
+ id: string;
147056
+ };
147057
+ cookie?: never;
147058
+ };
147059
+ requestBody?: {
147060
+ content: {
147061
+ /**
147062
+ * @example {
147063
+ * "subject_id": "01a17d4e-c0de-7a11-9d0e-5e1a7e5b0c11"
147064
+ * }
147065
+ */
147066
+ "application/json": {
147067
+ /** Format: uuid */
147068
+ subject_id: string;
147069
+ };
147070
+ };
147071
+ };
147072
+ responses: {
147073
+ /** @description `{ conversation_id, subject_id, subject_kind, waiting_state, changed }` */
147074
+ 200: {
147075
+ headers: {
147076
+ [name: string]: unknown;
147077
+ };
147078
+ content: {
147079
+ /**
147080
+ * @example {
147081
+ * "data": {
147082
+ * "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
147083
+ * "subject_id": "01a17d4e-c0de-7a11-9d0e-5e1a7e5b0c11",
147084
+ * "subject_kind": "message",
147085
+ * "waiting_state": "nothing_owed",
147086
+ * "changed": true
147087
+ * }
147088
+ * }
147089
+ */
147090
+ "application/json": {
147091
+ data?: unknown;
147092
+ meta?: {
147093
+ [key: string]: unknown;
147094
+ };
147095
+ };
147096
+ };
147097
+ };
147098
+ /** @description Validation error */
147099
+ 400: {
147100
+ headers: {
147101
+ [name: string]: unknown;
147102
+ };
147103
+ content: {
147104
+ "application/json": components["schemas"]["Error"];
147105
+ };
147106
+ };
147107
+ /** @description Unauthorized */
147108
+ 401: {
147109
+ headers: {
147110
+ [name: string]: unknown;
147111
+ };
147112
+ content: {
147113
+ "application/json": components["schemas"]["Error"];
147114
+ };
147115
+ };
147116
+ /** @description Forbidden */
147117
+ 403: {
147118
+ headers: {
147119
+ [name: string]: unknown;
147120
+ };
147121
+ content: {
147122
+ "application/json": components["schemas"]["Error"];
147123
+ };
147124
+ };
147125
+ /** @description Not found */
147126
+ 404: {
147127
+ headers: {
147128
+ [name: string]: unknown;
147129
+ };
147130
+ content: {
147131
+ "application/json": components["schemas"]["Error"];
147132
+ };
147133
+ };
147134
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
147135
+ 409: {
147136
+ headers: {
147137
+ [name: string]: unknown;
147138
+ };
147139
+ content: {
147140
+ "application/json": components["schemas"]["Error"];
147141
+ };
147142
+ };
147143
+ /** @description Rate limited */
147144
+ 429: {
147145
+ headers: {
147146
+ [name: string]: unknown;
147147
+ };
147148
+ content: {
147149
+ "application/json": components["schemas"]["Error"];
147150
+ };
147151
+ };
147152
+ };
147153
+ };
147154
+ /**
147155
+ * Undo «No requiere respuesta»
147156
+ * @description Removes the click `POST /no-reply-needed` recorded on the current wait (and the verdict row it created), so the conversation waits again as before. `subject_id` (optional) names the wait the undo is for: when the conversation now waits on another message, nothing changes. Nothing to undo → `changed: false`.
147157
+ */
147158
+ delete: {
147159
+ parameters: {
147160
+ query?: {
147161
+ subject_id?: string;
147162
+ };
147163
+ header?: never;
147164
+ path: {
147165
+ id: string;
147166
+ };
147167
+ cookie?: never;
147168
+ };
147169
+ requestBody?: never;
147170
+ responses: {
147171
+ /** @description `{ conversation_id, subject_id, subject_kind, waiting_state, changed }` */
147172
+ 200: {
147173
+ headers: {
147174
+ [name: string]: unknown;
147175
+ };
147176
+ content: {
147177
+ /**
147178
+ * @example {
147179
+ * "data": {
147180
+ * "conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
147181
+ * "subject_id": "01a17d4e-c0de-7a11-9d0e-5e1a7e5b0c11",
147182
+ * "subject_kind": "message",
147183
+ * "waiting_state": "nothing_owed",
147184
+ * "changed": true
147185
+ * }
147186
+ * }
147187
+ */
147188
+ "application/json": {
147189
+ data?: unknown;
147190
+ meta?: {
147191
+ [key: string]: unknown;
147192
+ };
147193
+ };
147194
+ };
147195
+ };
147196
+ /** @description Validation error */
147197
+ 400: {
147198
+ headers: {
147199
+ [name: string]: unknown;
147200
+ };
147201
+ content: {
147202
+ "application/json": components["schemas"]["Error"];
147203
+ };
147204
+ };
147205
+ /** @description Unauthorized */
147206
+ 401: {
147207
+ headers: {
147208
+ [name: string]: unknown;
147209
+ };
147210
+ content: {
147211
+ "application/json": components["schemas"]["Error"];
147212
+ };
147213
+ };
147214
+ /** @description Forbidden */
147215
+ 403: {
147216
+ headers: {
147217
+ [name: string]: unknown;
147218
+ };
147219
+ content: {
147220
+ "application/json": components["schemas"]["Error"];
147221
+ };
147222
+ };
147223
+ /** @description Not found */
147224
+ 404: {
147225
+ headers: {
147226
+ [name: string]: unknown;
147227
+ };
147228
+ content: {
147229
+ "application/json": components["schemas"]["Error"];
147230
+ };
147231
+ };
147232
+ /** @description Conflict (incl. Idempotency-Key reuse with different body) */
147233
+ 409: {
147234
+ headers: {
147235
+ [name: string]: unknown;
147236
+ };
147237
+ content: {
147238
+ "application/json": components["schemas"]["Error"];
147239
+ };
147240
+ };
147241
+ /** @description Rate limited */
147242
+ 429: {
147243
+ headers: {
147244
+ [name: string]: unknown;
147245
+ };
147246
+ content: {
147247
+ "application/json": components["schemas"]["Error"];
147248
+ };
147249
+ };
147250
+ };
147251
+ };
147252
+ options?: never;
147253
+ head?: never;
147254
+ patch?: never;
147255
+ trace?: never;
147256
+ };
147031
147257
  "/conversations/{id}/filter-sender": {
147032
147258
  parameters: {
147033
147259
  query?: never;
@@ -166653,7 +166879,7 @@ interface paths {
166653
166879
  };
166654
166880
  /**
166655
166881
  * Conversation volume and resolution
166656
- * @description Volume, resolution and handling time over the window — the top-level view the other reports break down.
166882
+ * @description Volume, resolution and handling time over the window — the top-level view the other reports break down. `waiting_clears` counts how «Esperando respuesta» was cleared in the window: `by_jev` (the `reply_owed` verdict took a wait out), `by_button` («No requiere respuesta», `POST /conversations/{id}/no-reply-needed`; undone clicks excluded) and `corrected_to_owed` (a person answered a thread Jev had taken out, so it was owed after all); `null` when it could not be read.
166657
166883
  */
166658
166884
  get: {
166659
166885
  parameters: {
@@ -166761,7 +166987,12 @@ interface paths {
166761
166987
  * ]
166762
166988
  * },
166763
166989
  * "facturables": 4,
166764
- * "solo_humano": 14
166990
+ * "solo_humano": 14,
166991
+ * "waiting_clears": {
166992
+ * "by_jev": 9,
166993
+ * "by_button": 2,
166994
+ * "corrected_to_owed": 1
166995
+ * }
166765
166996
  * },
166766
166997
  * "meta": {
166767
166998
  * "window": {
@@ -272209,7 +272440,7 @@ declare function createClient(opts: CreateClientOptions): VitrinaClient;
272209
272440
  * is regenerated — narrow on the ones you handle, never assume this
272210
272441
  * list is exhaustive at runtime.
272211
272442
  */
272212
- type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'ACCOUNT_DELETION_BLOCKED' | 'CONFIRMATION_REQUIRED' | 'VERSION_CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'MODULE_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'VEHICLE_VALUE_OUT_OF_RANGE' | 'PRICE_CHANGE_CONFIRMATION_REQUIRED' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'FINANZAS_SYNC_THROTTLED' | 'FINANZAS_CLAVE_ERROR' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'AUDIO_RENDITION_PENDING' | 'MEDIA_SIGNING_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED';
272443
+ type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'ACCOUNT_DELETION_BLOCKED' | 'CONFIRMATION_REQUIRED' | 'VERSION_CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'MODULE_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'VEHICLE_VALUE_OUT_OF_RANGE' | 'PRICE_CHANGE_CONFIRMATION_REQUIRED' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'FINANZAS_SYNC_THROTTLED' | 'FINANZAS_CLAVE_ERROR' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'AUDIO_RENDITION_PENDING' | 'MEDIA_SIGNING_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED' | 'WAITING_SUBJECT_CHANGED';
272213
272444
  declare const ERROR_CODES: readonly ErrorCode[];
272214
272445
 
272215
272446
  /**
package/dist/index.js CHANGED
@@ -70,7 +70,8 @@ var ERROR_CODES = [
70
70
  "TASADOR_ANON_DAILY_LIMIT",
71
71
  "TASADOR_CAPACITY_REACHED",
72
72
  "CLINIC_PLAN_REQUIRED",
73
- "TENANT_BILLING_PAUSED"
73
+ "TENANT_BILLING_PAUSED",
74
+ "WAITING_SUBJECT_CHANGED"
74
75
  ];
75
76
 
76
77
  // src/errors.ts
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'ACCOUNT_DELETION_BLOCKED'\n | 'CONFIRMATION_REQUIRED'\n | 'VERSION_CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'MODULE_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'VEHICLE_VALUE_OUT_OF_RANGE'\n | 'PRICE_CHANGE_CONFIRMATION_REQUIRED'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'FINANZAS_SYNC_THROTTLED'\n | 'FINANZAS_CLAVE_ERROR'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'AUDIO_RENDITION_PENDING'\n | 'MEDIA_SIGNING_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'ACCOUNT_DELETION_BLOCKED',\n 'CONFIRMATION_REQUIRED',\n 'VERSION_CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'MODULE_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'VEHICLE_VALUE_OUT_OF_RANGE',\n 'PRICE_CHANGE_CONFIRMATION_REQUIRED',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'FINANZAS_SYNC_THROTTLED',\n 'FINANZAS_CLAVE_ERROR',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'AUDIO_RENDITION_PENDING',\n 'MEDIA_SIGNING_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
1
+ {"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'ACCOUNT_DELETION_BLOCKED'\n | 'CONFIRMATION_REQUIRED'\n | 'VERSION_CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'MODULE_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'VEHICLE_VALUE_OUT_OF_RANGE'\n | 'PRICE_CHANGE_CONFIRMATION_REQUIRED'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'FINANZAS_SYNC_THROTTLED'\n | 'FINANZAS_CLAVE_ERROR'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'AUDIO_RENDITION_PENDING'\n | 'MEDIA_SIGNING_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED'\n | 'WAITING_SUBJECT_CHANGED';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'ACCOUNT_DELETION_BLOCKED',\n 'CONFIRMATION_REQUIRED',\n 'VERSION_CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'MODULE_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'VEHICLE_VALUE_OUT_OF_RANGE',\n 'PRICE_CHANGE_CONFIRMATION_REQUIRED',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'FINANZAS_SYNC_THROTTLED',\n 'FINANZAS_CLAVE_ERROR',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STAGE_QUALIFIES_LEAD_NOT_ALLOWED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'AUDIO_RENDITION_PENDING',\n 'MEDIA_SIGNING_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n 'WAITING_SUBJECT_CHANGED',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitrina/api",
3
- "version": "15.21.0",
3
+ "version": "15.22.0",
4
4
  "description": "Typed TypeScript SDK for the Vitrina API pública — generated from the committed openapi.public.json (ADR 0106).",
5
5
  "type": "module",
6
6
  "license": "MIT",