@verifiquemos/sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to `@verifiquemos/sdk` will be documented here.
4
4
 
5
+ ## [0.1.3] - 2026-05-25
6
+
7
+ First published release.
8
+
9
+ ### Fixed
10
+ - Corrected the default API domain from `verifiquemos.gt` to `verifiquemos.com` (default `baseUrl`, README, support email and homepage).
11
+
12
+ ### Changed
13
+ - Removed the private repository URL from public package metadata.
14
+
5
15
  ## [0.1.1] - 2026-05-25
6
16
 
7
17
  Maintenance release — no changes to the public SDK API.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @verifiquemos/sdk
2
2
 
3
- Official TypeScript SDK for the [Verifiquemos](https://verifiquemos.gt) compliance API.
3
+ Official TypeScript SDK for the [Verifiquemos](https://verifiquemos.com) compliance API.
4
4
 
5
5
  > KYC, RENAP, OFAC, ONU, Lista Engel, PEP, CPE — for Guatemala-specific compliance, in one API call.
6
6
 
@@ -65,7 +65,7 @@ try {
65
65
  | Option | Default | Description |
66
66
  |---|---|---|
67
67
  | `apiKey` | (required) | Server-issued API key from your dashboard. Starts with `vfq_`. |
68
- | `baseUrl` | `https://api.verifiquemos.gt` | Override for staging/dev. |
68
+ | `baseUrl` | `https://api.verifiquemos.com` | Override for staging/dev. |
69
69
  | `fetch` | `globalThis.fetch` | Inject your own (useful for testing). |
70
70
  | `idempotencyKeyFactory` | ULID generator | Override the Idempotency-Key strategy. |
71
71
 
@@ -75,12 +75,12 @@ The SDK follows semver. Major version bumps mirror major API contract changes; m
75
75
 
76
76
  ## Documentation
77
77
 
78
- - API reference: <https://api.verifiquemos.gt/scalar>
79
- - Developer portal: <https://verifiquemos.gt/developers>
78
+ - API reference: <https://api.verifiquemos.com/scalar>
79
+ - Developer portal: <https://verifiquemos.com/developers>
80
80
 
81
81
  ## Support
82
82
 
83
- - Email: <soporte@verifiquemos.gt>
83
+ - Email: <soporte@verifiquemos.com>
84
84
 
85
85
  ## License
86
86
 
package/dist/index.cjs CHANGED
@@ -111,7 +111,7 @@ var VerifiquemosClient = class {
111
111
  apiKeys;
112
112
  constructor(opts) {
113
113
  this.#apiKey = opts.apiKey;
114
- this.#baseUrl = opts.baseUrl ?? "https://api.verifiquemos.gt";
114
+ this.#baseUrl = opts.baseUrl ?? "https://api.verifiquemos.com";
115
115
  this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
116
116
  this.#idempotencyKeyFactory = opts.idempotencyKeyFactory ?? generateUlid;
117
117
  this.#raw = (0, import_openapi_fetch.default)({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts"],"sourcesContent":["export { VerifiquemosClient } from \"./client\";\nexport type { VerifiquemosClientOptions } from \"./client\";\nexport { ApiError, NetworkError, RateLimitError } from \"./errors\";\nexport type * from \"./types\";\n","import createOpenapiClient from \"openapi-fetch\";\nimport type { paths } from \"./generated\";\nimport { ApiError, NetworkError, RateLimitError } from \"./errors\";\n\nconst ULID_ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n// Fix #2 — I2: Use crypto.getRandomValues() instead of Math.random() for\n// cryptographic-quality randomness in idempotency keys.\nfunction generateUlid(): string {\n // Spec: 26-char Crockford Base32. Top 10 chars are timestamp (ms since epoch).\n // Bottom 16 chars are randomness.\n const time = Date.now();\n let timePart = \"\";\n let t = time;\n for (let i = 0; i < 10; i++) {\n timePart = ULID_ALPHABET[t % 32]! + timePart;\n t = Math.floor(t / 32);\n }\n const buf = new Uint8Array(16);\n // globalThis.crypto is available in Node >= 20 and all modern browsers.\n globalThis.crypto.getRandomValues(buf);\n let randPart = \"\";\n for (let i = 0; i < 16; i++) {\n randPart += ULID_ALPHABET[buf[i]! % 32]!;\n }\n return timePart + randPart;\n}\n\nexport interface VerifiquemosClientOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof fetch;\n /** Override the default Idempotency-Key generator. */\n idempotencyKeyFactory?: () => string;\n}\n\n// Fix #1 — C1: Sub-API classes receive only the wrappedFetch closure and\n// baseUrl (both non-sensitive). The openapi-fetch raw client is also passed\n// so sub-APIs can use typed path helpers. The API key is stored only in a\n// class-private field (#apiKey) and is never exposed on the public surface.\n\n/** Internal bundle passed to sub-API classes; not part of the public API. */\ninterface SubApiDeps {\n wrappedFetch: VerifiquemosClient[\"wrappedFetch\"];\n raw: ReturnType<typeof createOpenapiClient<paths>>;\n baseUrl: string;\n}\n\nexport class VerifiquemosClient {\n // C1 fix: private class field — not reachable via (client as any)._options\n #apiKey: string;\n #baseUrl: string;\n #fetch: typeof fetch;\n #idempotencyKeyFactory: () => string;\n #raw: ReturnType<typeof createOpenapiClient<paths>>;\n\n readonly auth: AuthApi;\n readonly validations: ValidationsApi;\n readonly credits: CreditsApi;\n readonly apiKeys: ApiKeysApi;\n\n constructor(opts: VerifiquemosClientOptions) {\n this.#apiKey = opts.apiKey;\n this.#baseUrl = opts.baseUrl ?? \"https://api.verifiquemos.gt\";\n this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.#idempotencyKeyFactory = opts.idempotencyKeyFactory ?? generateUlid;\n\n this.#raw = createOpenapiClient<paths>({\n baseUrl: this.#baseUrl,\n fetch: (request: Request) => this.wrappedFetch(request),\n });\n\n const deps: SubApiDeps = {\n wrappedFetch: this.wrappedFetch.bind(this),\n raw: this.#raw,\n baseUrl: this.#baseUrl,\n };\n\n this.auth = new AuthApi(deps);\n this.validations = new ValidationsApi(deps);\n this.credits = new CreditsApi(deps);\n this.apiKeys = new ApiKeysApi(deps);\n }\n\n /**\n * Internal: every request flows through here for header injection + error normalization.\n * Accepts either a Request object (from openapi-fetch) or a URL + RequestInit (direct).\n *\n * @internal\n */\n async wrappedFetch(input: Request | string | URL, init?: RequestInit): Promise<Response> {\n const isRequest = typeof Request !== \"undefined\" && input instanceof Request;\n const url = isRequest ? input.url : typeof input === \"string\" ? input : input.toString();\n const baseInit: RequestInit = isRequest\n ? {\n method: input.method,\n body: input.body,\n headers: input.headers,\n // Pass through credentials/mode/etc when present\n credentials: input.credentials,\n cache: input.cache,\n redirect: input.redirect,\n referrer: input.referrer,\n integrity: input.integrity,\n signal: input.signal,\n }\n : { ...(init ?? {}) };\n\n const method = (baseInit.method ?? \"GET\").toUpperCase();\n const headers = new Headers(baseInit.headers ?? undefined);\n headers.set(\"x-api-key\", this.#apiKey);\n // Fix #4 — I4: Only set default accept if caller hasn't already set one\n // (e.g. validations.report() sets accept:application/pdf).\n if (!headers.has(\"accept\")) {\n headers.set(\"accept\", \"application/json\");\n }\n if ([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"].includes(method) && !headers.has(\"idempotency-key\")) {\n headers.set(\"idempotency-key\", this.#idempotencyKeyFactory());\n }\n\n const finalInit: RequestInit = { ...baseInit, headers };\n\n let response: Response;\n try {\n response = await this.#fetch(url, finalInit);\n } catch (e) {\n throw new NetworkError(\"Network request failed\", e instanceof Error ? e : undefined);\n }\n\n if (!response.ok) {\n const body = await response.clone().json().catch(() => null);\n if (response.status === 429) {\n // Fix #3 — I1: Defensive retry-after parsing — handle both numeric\n // seconds (RFC 6585 §4) and HTTP-date format (RFC 7231 §7.1.3).\n const raw = response.headers.get(\"retry-after\") ?? \"0\";\n const asNumber = Number(raw);\n let retryAfter: number;\n if (Number.isFinite(asNumber) && asNumber >= 0) {\n retryAfter = asNumber;\n } else {\n const dateMs = new Date(raw).getTime();\n retryAfter = Number.isFinite(dateMs)\n ? Math.max(0, Math.ceil((dateMs - Date.now()) / 1000))\n : 0;\n }\n throw new RateLimitError(retryAfter, body);\n }\n const detail =\n body && typeof body === \"object\" && body !== null && \"detail\" in body\n ? (body as { detail: unknown }).detail\n : null;\n const message = typeof detail === \"string\" ? detail : response.statusText;\n throw new ApiError(response.status, message, body);\n }\n\n return response;\n }\n}\n\nclass AuthApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async me() {\n const { data } = await this.deps.raw.GET(\"/api/v1/auth/me\");\n return data;\n }\n}\n\nclass ValidationsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n /**\n * Submit a new validation request.\n *\n * Note: `cui` (Guatemalan ID number) is NOT a form field — it is extracted\n * automatically from the uploaded DPI image via OCR on the server side.\n *\n * @param input.naturalezaCliente - Client nature, e.g. \"individual\" or \"juridica\".\n * @param input.paisOrigenFondos - Optional ISO-3166-1 alpha-2 country code for funds origin.\n * @param input.dpiFile - DPI image blob (front side; OCR extracts the CUI).\n */\n // Fix #5 — C1: Correct multipart field names to match the real API.\n // - Single dpi_file (not dpi_front + dpi_back)\n // - pais_origen_fondos (not pais_origen)\n // - No cui field (extracted via OCR on the server)\n async create(input: {\n naturalezaCliente: string;\n paisOrigenFondos?: string;\n dpiFile: Blob;\n }): Promise<{ id: string; status: string }> {\n const form = new FormData();\n form.append(\"naturaleza_cliente\", input.naturalezaCliente);\n if (input.paisOrigenFondos) {\n form.append(\"pais_origen_fondos\", input.paisOrigenFondos);\n }\n form.append(\"dpi_file\", input.dpiFile);\n\n // openapi-fetch's multipart support is limited; use wrappedFetch directly.\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations`,\n { method: \"POST\", body: form },\n );\n return (await response.json()) as { id: string; status: string };\n }\n\n async get(id: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/validations/{validation_id}\", {\n params: { path: { validation_id: id } },\n });\n return data;\n }\n\n // Fix #4 — I4: Set accept:application/pdf so the server streams the PDF\n // instead of attempting a JSON serialization.\n async report(id: string): Promise<Blob> {\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations/${id}/report`,\n { method: \"GET\", headers: { accept: \"application/pdf\" } },\n );\n return await response.blob();\n }\n}\n\nclass CreditsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async balance() {\n const { data } = await this.deps.raw.GET(\"/api/v1/credits/balance\");\n return data;\n }\n}\n\nclass ApiKeysApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async list(tenantId: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/tenants/{tenant_id}/api-keys\", {\n params: { path: { tenant_id: tenantId } },\n });\n return data;\n }\n}\n","/**\n * Errors thrown by the Verifiquemos SDK.\n *\n * `ApiError` is thrown for any HTTP 4xx/5xx response from the API.\n * `RateLimitError` is a specialization for 429 responses.\n * `NetworkError` wraps transport failures (DNS, timeout, connection reset).\n */\n\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isPaymentRequired(): boolean {\n return this.status === 402;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isServerError(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n}\n\nexport class RateLimitError extends ApiError {\n readonly retryAfterSeconds: number;\n\n constructor(retryAfterSeconds: number, body: unknown = null) {\n super(429, \"Rate limit exceeded\", body);\n this.name = \"RateLimitError\";\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport class NetworkError extends Error {\n override readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n"],"mappings":"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","names":["createOpenapiClient"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts"],"sourcesContent":["export { VerifiquemosClient } from \"./client\";\nexport type { VerifiquemosClientOptions } from \"./client\";\nexport { ApiError, NetworkError, RateLimitError } from \"./errors\";\nexport type * from \"./types\";\n","import createOpenapiClient from \"openapi-fetch\";\nimport type { paths } from \"./generated\";\nimport { ApiError, NetworkError, RateLimitError } from \"./errors\";\n\nconst ULID_ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n// Fix #2 — I2: Use crypto.getRandomValues() instead of Math.random() for\n// cryptographic-quality randomness in idempotency keys.\nfunction generateUlid(): string {\n // Spec: 26-char Crockford Base32. Top 10 chars are timestamp (ms since epoch).\n // Bottom 16 chars are randomness.\n const time = Date.now();\n let timePart = \"\";\n let t = time;\n for (let i = 0; i < 10; i++) {\n timePart = ULID_ALPHABET[t % 32]! + timePart;\n t = Math.floor(t / 32);\n }\n const buf = new Uint8Array(16);\n // globalThis.crypto is available in Node >= 20 and all modern browsers.\n globalThis.crypto.getRandomValues(buf);\n let randPart = \"\";\n for (let i = 0; i < 16; i++) {\n randPart += ULID_ALPHABET[buf[i]! % 32]!;\n }\n return timePart + randPart;\n}\n\nexport interface VerifiquemosClientOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof fetch;\n /** Override the default Idempotency-Key generator. */\n idempotencyKeyFactory?: () => string;\n}\n\n// Fix #1 — C1: Sub-API classes receive only the wrappedFetch closure and\n// baseUrl (both non-sensitive). The openapi-fetch raw client is also passed\n// so sub-APIs can use typed path helpers. The API key is stored only in a\n// class-private field (#apiKey) and is never exposed on the public surface.\n\n/** Internal bundle passed to sub-API classes; not part of the public API. */\ninterface SubApiDeps {\n wrappedFetch: VerifiquemosClient[\"wrappedFetch\"];\n raw: ReturnType<typeof createOpenapiClient<paths>>;\n baseUrl: string;\n}\n\nexport class VerifiquemosClient {\n // C1 fix: private class field — not reachable via (client as any)._options\n #apiKey: string;\n #baseUrl: string;\n #fetch: typeof fetch;\n #idempotencyKeyFactory: () => string;\n #raw: ReturnType<typeof createOpenapiClient<paths>>;\n\n readonly auth: AuthApi;\n readonly validations: ValidationsApi;\n readonly credits: CreditsApi;\n readonly apiKeys: ApiKeysApi;\n\n constructor(opts: VerifiquemosClientOptions) {\n this.#apiKey = opts.apiKey;\n this.#baseUrl = opts.baseUrl ?? \"https://api.verifiquemos.com\";\n this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.#idempotencyKeyFactory = opts.idempotencyKeyFactory ?? generateUlid;\n\n this.#raw = createOpenapiClient<paths>({\n baseUrl: this.#baseUrl,\n fetch: (request: Request) => this.wrappedFetch(request),\n });\n\n const deps: SubApiDeps = {\n wrappedFetch: this.wrappedFetch.bind(this),\n raw: this.#raw,\n baseUrl: this.#baseUrl,\n };\n\n this.auth = new AuthApi(deps);\n this.validations = new ValidationsApi(deps);\n this.credits = new CreditsApi(deps);\n this.apiKeys = new ApiKeysApi(deps);\n }\n\n /**\n * Internal: every request flows through here for header injection + error normalization.\n * Accepts either a Request object (from openapi-fetch) or a URL + RequestInit (direct).\n *\n * @internal\n */\n async wrappedFetch(input: Request | string | URL, init?: RequestInit): Promise<Response> {\n const isRequest = typeof Request !== \"undefined\" && input instanceof Request;\n const url = isRequest ? input.url : typeof input === \"string\" ? input : input.toString();\n const baseInit: RequestInit = isRequest\n ? {\n method: input.method,\n body: input.body,\n headers: input.headers,\n // Pass through credentials/mode/etc when present\n credentials: input.credentials,\n cache: input.cache,\n redirect: input.redirect,\n referrer: input.referrer,\n integrity: input.integrity,\n signal: input.signal,\n }\n : { ...(init ?? {}) };\n\n const method = (baseInit.method ?? \"GET\").toUpperCase();\n const headers = new Headers(baseInit.headers ?? undefined);\n headers.set(\"x-api-key\", this.#apiKey);\n // Fix #4 — I4: Only set default accept if caller hasn't already set one\n // (e.g. validations.report() sets accept:application/pdf).\n if (!headers.has(\"accept\")) {\n headers.set(\"accept\", \"application/json\");\n }\n if ([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"].includes(method) && !headers.has(\"idempotency-key\")) {\n headers.set(\"idempotency-key\", this.#idempotencyKeyFactory());\n }\n\n const finalInit: RequestInit = { ...baseInit, headers };\n\n let response: Response;\n try {\n response = await this.#fetch(url, finalInit);\n } catch (e) {\n throw new NetworkError(\"Network request failed\", e instanceof Error ? e : undefined);\n }\n\n if (!response.ok) {\n const body = await response.clone().json().catch(() => null);\n if (response.status === 429) {\n // Fix #3 — I1: Defensive retry-after parsing — handle both numeric\n // seconds (RFC 6585 §4) and HTTP-date format (RFC 7231 §7.1.3).\n const raw = response.headers.get(\"retry-after\") ?? \"0\";\n const asNumber = Number(raw);\n let retryAfter: number;\n if (Number.isFinite(asNumber) && asNumber >= 0) {\n retryAfter = asNumber;\n } else {\n const dateMs = new Date(raw).getTime();\n retryAfter = Number.isFinite(dateMs)\n ? Math.max(0, Math.ceil((dateMs - Date.now()) / 1000))\n : 0;\n }\n throw new RateLimitError(retryAfter, body);\n }\n const detail =\n body && typeof body === \"object\" && body !== null && \"detail\" in body\n ? (body as { detail: unknown }).detail\n : null;\n const message = typeof detail === \"string\" ? detail : response.statusText;\n throw new ApiError(response.status, message, body);\n }\n\n return response;\n }\n}\n\nclass AuthApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async me() {\n const { data } = await this.deps.raw.GET(\"/api/v1/auth/me\");\n return data;\n }\n}\n\nclass ValidationsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n /**\n * Submit a new validation request.\n *\n * Note: `cui` (Guatemalan ID number) is NOT a form field — it is extracted\n * automatically from the uploaded DPI image via OCR on the server side.\n *\n * @param input.naturalezaCliente - Client nature, e.g. \"individual\" or \"juridica\".\n * @param input.paisOrigenFondos - Optional ISO-3166-1 alpha-2 country code for funds origin.\n * @param input.dpiFile - DPI image blob (front side; OCR extracts the CUI).\n */\n // Fix #5 — C1: Correct multipart field names to match the real API.\n // - Single dpi_file (not dpi_front + dpi_back)\n // - pais_origen_fondos (not pais_origen)\n // - No cui field (extracted via OCR on the server)\n async create(input: {\n naturalezaCliente: string;\n paisOrigenFondos?: string;\n dpiFile: Blob;\n }): Promise<{ id: string; status: string }> {\n const form = new FormData();\n form.append(\"naturaleza_cliente\", input.naturalezaCliente);\n if (input.paisOrigenFondos) {\n form.append(\"pais_origen_fondos\", input.paisOrigenFondos);\n }\n form.append(\"dpi_file\", input.dpiFile);\n\n // openapi-fetch's multipart support is limited; use wrappedFetch directly.\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations`,\n { method: \"POST\", body: form },\n );\n return (await response.json()) as { id: string; status: string };\n }\n\n async get(id: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/validations/{validation_id}\", {\n params: { path: { validation_id: id } },\n });\n return data;\n }\n\n // Fix #4 — I4: Set accept:application/pdf so the server streams the PDF\n // instead of attempting a JSON serialization.\n async report(id: string): Promise<Blob> {\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations/${id}/report`,\n { method: \"GET\", headers: { accept: \"application/pdf\" } },\n );\n return await response.blob();\n }\n}\n\nclass CreditsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async balance() {\n const { data } = await this.deps.raw.GET(\"/api/v1/credits/balance\");\n return data;\n }\n}\n\nclass ApiKeysApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async list(tenantId: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/tenants/{tenant_id}/api-keys\", {\n params: { path: { tenant_id: tenantId } },\n });\n return data;\n }\n}\n","/**\n * Errors thrown by the Verifiquemos SDK.\n *\n * `ApiError` is thrown for any HTTP 4xx/5xx response from the API.\n * `RateLimitError` is a specialization for 429 responses.\n * `NetworkError` wraps transport failures (DNS, timeout, connection reset).\n */\n\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isPaymentRequired(): boolean {\n return this.status === 402;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isServerError(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n}\n\nexport class RateLimitError extends ApiError {\n readonly retryAfterSeconds: number;\n\n constructor(retryAfterSeconds: number, body: unknown = null) {\n super(429, \"Rate limit exceeded\", body);\n this.name = \"RateLimitError\";\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport class NetworkError extends Error {\n override readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n"],"mappings":"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","names":["createOpenapiClient"]}
package/dist/index.js CHANGED
@@ -72,7 +72,7 @@ var VerifiquemosClient = class {
72
72
  apiKeys;
73
73
  constructor(opts) {
74
74
  this.#apiKey = opts.apiKey;
75
- this.#baseUrl = opts.baseUrl ?? "https://api.verifiquemos.gt";
75
+ this.#baseUrl = opts.baseUrl ?? "https://api.verifiquemos.com";
76
76
  this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
77
77
  this.#idempotencyKeyFactory = opts.idempotencyKeyFactory ?? generateUlid;
78
78
  this.#raw = createOpenapiClient({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/errors.ts"],"sourcesContent":["import createOpenapiClient from \"openapi-fetch\";\nimport type { paths } from \"./generated\";\nimport { ApiError, NetworkError, RateLimitError } from \"./errors\";\n\nconst ULID_ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n// Fix #2 — I2: Use crypto.getRandomValues() instead of Math.random() for\n// cryptographic-quality randomness in idempotency keys.\nfunction generateUlid(): string {\n // Spec: 26-char Crockford Base32. Top 10 chars are timestamp (ms since epoch).\n // Bottom 16 chars are randomness.\n const time = Date.now();\n let timePart = \"\";\n let t = time;\n for (let i = 0; i < 10; i++) {\n timePart = ULID_ALPHABET[t % 32]! + timePart;\n t = Math.floor(t / 32);\n }\n const buf = new Uint8Array(16);\n // globalThis.crypto is available in Node >= 20 and all modern browsers.\n globalThis.crypto.getRandomValues(buf);\n let randPart = \"\";\n for (let i = 0; i < 16; i++) {\n randPart += ULID_ALPHABET[buf[i]! % 32]!;\n }\n return timePart + randPart;\n}\n\nexport interface VerifiquemosClientOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof fetch;\n /** Override the default Idempotency-Key generator. */\n idempotencyKeyFactory?: () => string;\n}\n\n// Fix #1 — C1: Sub-API classes receive only the wrappedFetch closure and\n// baseUrl (both non-sensitive). The openapi-fetch raw client is also passed\n// so sub-APIs can use typed path helpers. The API key is stored only in a\n// class-private field (#apiKey) and is never exposed on the public surface.\n\n/** Internal bundle passed to sub-API classes; not part of the public API. */\ninterface SubApiDeps {\n wrappedFetch: VerifiquemosClient[\"wrappedFetch\"];\n raw: ReturnType<typeof createOpenapiClient<paths>>;\n baseUrl: string;\n}\n\nexport class VerifiquemosClient {\n // C1 fix: private class field — not reachable via (client as any)._options\n #apiKey: string;\n #baseUrl: string;\n #fetch: typeof fetch;\n #idempotencyKeyFactory: () => string;\n #raw: ReturnType<typeof createOpenapiClient<paths>>;\n\n readonly auth: AuthApi;\n readonly validations: ValidationsApi;\n readonly credits: CreditsApi;\n readonly apiKeys: ApiKeysApi;\n\n constructor(opts: VerifiquemosClientOptions) {\n this.#apiKey = opts.apiKey;\n this.#baseUrl = opts.baseUrl ?? \"https://api.verifiquemos.gt\";\n this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.#idempotencyKeyFactory = opts.idempotencyKeyFactory ?? generateUlid;\n\n this.#raw = createOpenapiClient<paths>({\n baseUrl: this.#baseUrl,\n fetch: (request: Request) => this.wrappedFetch(request),\n });\n\n const deps: SubApiDeps = {\n wrappedFetch: this.wrappedFetch.bind(this),\n raw: this.#raw,\n baseUrl: this.#baseUrl,\n };\n\n this.auth = new AuthApi(deps);\n this.validations = new ValidationsApi(deps);\n this.credits = new CreditsApi(deps);\n this.apiKeys = new ApiKeysApi(deps);\n }\n\n /**\n * Internal: every request flows through here for header injection + error normalization.\n * Accepts either a Request object (from openapi-fetch) or a URL + RequestInit (direct).\n *\n * @internal\n */\n async wrappedFetch(input: Request | string | URL, init?: RequestInit): Promise<Response> {\n const isRequest = typeof Request !== \"undefined\" && input instanceof Request;\n const url = isRequest ? input.url : typeof input === \"string\" ? input : input.toString();\n const baseInit: RequestInit = isRequest\n ? {\n method: input.method,\n body: input.body,\n headers: input.headers,\n // Pass through credentials/mode/etc when present\n credentials: input.credentials,\n cache: input.cache,\n redirect: input.redirect,\n referrer: input.referrer,\n integrity: input.integrity,\n signal: input.signal,\n }\n : { ...(init ?? {}) };\n\n const method = (baseInit.method ?? \"GET\").toUpperCase();\n const headers = new Headers(baseInit.headers ?? undefined);\n headers.set(\"x-api-key\", this.#apiKey);\n // Fix #4 — I4: Only set default accept if caller hasn't already set one\n // (e.g. validations.report() sets accept:application/pdf).\n if (!headers.has(\"accept\")) {\n headers.set(\"accept\", \"application/json\");\n }\n if ([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"].includes(method) && !headers.has(\"idempotency-key\")) {\n headers.set(\"idempotency-key\", this.#idempotencyKeyFactory());\n }\n\n const finalInit: RequestInit = { ...baseInit, headers };\n\n let response: Response;\n try {\n response = await this.#fetch(url, finalInit);\n } catch (e) {\n throw new NetworkError(\"Network request failed\", e instanceof Error ? e : undefined);\n }\n\n if (!response.ok) {\n const body = await response.clone().json().catch(() => null);\n if (response.status === 429) {\n // Fix #3 — I1: Defensive retry-after parsing — handle both numeric\n // seconds (RFC 6585 §4) and HTTP-date format (RFC 7231 §7.1.3).\n const raw = response.headers.get(\"retry-after\") ?? \"0\";\n const asNumber = Number(raw);\n let retryAfter: number;\n if (Number.isFinite(asNumber) && asNumber >= 0) {\n retryAfter = asNumber;\n } else {\n const dateMs = new Date(raw).getTime();\n retryAfter = Number.isFinite(dateMs)\n ? Math.max(0, Math.ceil((dateMs - Date.now()) / 1000))\n : 0;\n }\n throw new RateLimitError(retryAfter, body);\n }\n const detail =\n body && typeof body === \"object\" && body !== null && \"detail\" in body\n ? (body as { detail: unknown }).detail\n : null;\n const message = typeof detail === \"string\" ? detail : response.statusText;\n throw new ApiError(response.status, message, body);\n }\n\n return response;\n }\n}\n\nclass AuthApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async me() {\n const { data } = await this.deps.raw.GET(\"/api/v1/auth/me\");\n return data;\n }\n}\n\nclass ValidationsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n /**\n * Submit a new validation request.\n *\n * Note: `cui` (Guatemalan ID number) is NOT a form field — it is extracted\n * automatically from the uploaded DPI image via OCR on the server side.\n *\n * @param input.naturalezaCliente - Client nature, e.g. \"individual\" or \"juridica\".\n * @param input.paisOrigenFondos - Optional ISO-3166-1 alpha-2 country code for funds origin.\n * @param input.dpiFile - DPI image blob (front side; OCR extracts the CUI).\n */\n // Fix #5 — C1: Correct multipart field names to match the real API.\n // - Single dpi_file (not dpi_front + dpi_back)\n // - pais_origen_fondos (not pais_origen)\n // - No cui field (extracted via OCR on the server)\n async create(input: {\n naturalezaCliente: string;\n paisOrigenFondos?: string;\n dpiFile: Blob;\n }): Promise<{ id: string; status: string }> {\n const form = new FormData();\n form.append(\"naturaleza_cliente\", input.naturalezaCliente);\n if (input.paisOrigenFondos) {\n form.append(\"pais_origen_fondos\", input.paisOrigenFondos);\n }\n form.append(\"dpi_file\", input.dpiFile);\n\n // openapi-fetch's multipart support is limited; use wrappedFetch directly.\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations`,\n { method: \"POST\", body: form },\n );\n return (await response.json()) as { id: string; status: string };\n }\n\n async get(id: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/validations/{validation_id}\", {\n params: { path: { validation_id: id } },\n });\n return data;\n }\n\n // Fix #4 — I4: Set accept:application/pdf so the server streams the PDF\n // instead of attempting a JSON serialization.\n async report(id: string): Promise<Blob> {\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations/${id}/report`,\n { method: \"GET\", headers: { accept: \"application/pdf\" } },\n );\n return await response.blob();\n }\n}\n\nclass CreditsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async balance() {\n const { data } = await this.deps.raw.GET(\"/api/v1/credits/balance\");\n return data;\n }\n}\n\nclass ApiKeysApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async list(tenantId: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/tenants/{tenant_id}/api-keys\", {\n params: { path: { tenant_id: tenantId } },\n });\n return data;\n }\n}\n","/**\n * Errors thrown by the Verifiquemos SDK.\n *\n * `ApiError` is thrown for any HTTP 4xx/5xx response from the API.\n * `RateLimitError` is a specialization for 429 responses.\n * `NetworkError` wraps transport failures (DNS, timeout, connection reset).\n */\n\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isPaymentRequired(): boolean {\n return this.status === 402;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isServerError(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n}\n\nexport class RateLimitError extends ApiError {\n readonly retryAfterSeconds: number;\n\n constructor(retryAfterSeconds: number, body: unknown = null) {\n super(429, \"Rate limit exceeded\", body);\n this.name = \"RateLimitError\";\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport class NetworkError extends Error {\n override readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/errors.ts"],"sourcesContent":["import createOpenapiClient from \"openapi-fetch\";\nimport type { paths } from \"./generated\";\nimport { ApiError, NetworkError, RateLimitError } from \"./errors\";\n\nconst ULID_ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n// Fix #2 — I2: Use crypto.getRandomValues() instead of Math.random() for\n// cryptographic-quality randomness in idempotency keys.\nfunction generateUlid(): string {\n // Spec: 26-char Crockford Base32. Top 10 chars are timestamp (ms since epoch).\n // Bottom 16 chars are randomness.\n const time = Date.now();\n let timePart = \"\";\n let t = time;\n for (let i = 0; i < 10; i++) {\n timePart = ULID_ALPHABET[t % 32]! + timePart;\n t = Math.floor(t / 32);\n }\n const buf = new Uint8Array(16);\n // globalThis.crypto is available in Node >= 20 and all modern browsers.\n globalThis.crypto.getRandomValues(buf);\n let randPart = \"\";\n for (let i = 0; i < 16; i++) {\n randPart += ULID_ALPHABET[buf[i]! % 32]!;\n }\n return timePart + randPart;\n}\n\nexport interface VerifiquemosClientOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof fetch;\n /** Override the default Idempotency-Key generator. */\n idempotencyKeyFactory?: () => string;\n}\n\n// Fix #1 — C1: Sub-API classes receive only the wrappedFetch closure and\n// baseUrl (both non-sensitive). The openapi-fetch raw client is also passed\n// so sub-APIs can use typed path helpers. The API key is stored only in a\n// class-private field (#apiKey) and is never exposed on the public surface.\n\n/** Internal bundle passed to sub-API classes; not part of the public API. */\ninterface SubApiDeps {\n wrappedFetch: VerifiquemosClient[\"wrappedFetch\"];\n raw: ReturnType<typeof createOpenapiClient<paths>>;\n baseUrl: string;\n}\n\nexport class VerifiquemosClient {\n // C1 fix: private class field — not reachable via (client as any)._options\n #apiKey: string;\n #baseUrl: string;\n #fetch: typeof fetch;\n #idempotencyKeyFactory: () => string;\n #raw: ReturnType<typeof createOpenapiClient<paths>>;\n\n readonly auth: AuthApi;\n readonly validations: ValidationsApi;\n readonly credits: CreditsApi;\n readonly apiKeys: ApiKeysApi;\n\n constructor(opts: VerifiquemosClientOptions) {\n this.#apiKey = opts.apiKey;\n this.#baseUrl = opts.baseUrl ?? \"https://api.verifiquemos.com\";\n this.#fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.#idempotencyKeyFactory = opts.idempotencyKeyFactory ?? generateUlid;\n\n this.#raw = createOpenapiClient<paths>({\n baseUrl: this.#baseUrl,\n fetch: (request: Request) => this.wrappedFetch(request),\n });\n\n const deps: SubApiDeps = {\n wrappedFetch: this.wrappedFetch.bind(this),\n raw: this.#raw,\n baseUrl: this.#baseUrl,\n };\n\n this.auth = new AuthApi(deps);\n this.validations = new ValidationsApi(deps);\n this.credits = new CreditsApi(deps);\n this.apiKeys = new ApiKeysApi(deps);\n }\n\n /**\n * Internal: every request flows through here for header injection + error normalization.\n * Accepts either a Request object (from openapi-fetch) or a URL + RequestInit (direct).\n *\n * @internal\n */\n async wrappedFetch(input: Request | string | URL, init?: RequestInit): Promise<Response> {\n const isRequest = typeof Request !== \"undefined\" && input instanceof Request;\n const url = isRequest ? input.url : typeof input === \"string\" ? input : input.toString();\n const baseInit: RequestInit = isRequest\n ? {\n method: input.method,\n body: input.body,\n headers: input.headers,\n // Pass through credentials/mode/etc when present\n credentials: input.credentials,\n cache: input.cache,\n redirect: input.redirect,\n referrer: input.referrer,\n integrity: input.integrity,\n signal: input.signal,\n }\n : { ...(init ?? {}) };\n\n const method = (baseInit.method ?? \"GET\").toUpperCase();\n const headers = new Headers(baseInit.headers ?? undefined);\n headers.set(\"x-api-key\", this.#apiKey);\n // Fix #4 — I4: Only set default accept if caller hasn't already set one\n // (e.g. validations.report() sets accept:application/pdf).\n if (!headers.has(\"accept\")) {\n headers.set(\"accept\", \"application/json\");\n }\n if ([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"].includes(method) && !headers.has(\"idempotency-key\")) {\n headers.set(\"idempotency-key\", this.#idempotencyKeyFactory());\n }\n\n const finalInit: RequestInit = { ...baseInit, headers };\n\n let response: Response;\n try {\n response = await this.#fetch(url, finalInit);\n } catch (e) {\n throw new NetworkError(\"Network request failed\", e instanceof Error ? e : undefined);\n }\n\n if (!response.ok) {\n const body = await response.clone().json().catch(() => null);\n if (response.status === 429) {\n // Fix #3 — I1: Defensive retry-after parsing — handle both numeric\n // seconds (RFC 6585 §4) and HTTP-date format (RFC 7231 §7.1.3).\n const raw = response.headers.get(\"retry-after\") ?? \"0\";\n const asNumber = Number(raw);\n let retryAfter: number;\n if (Number.isFinite(asNumber) && asNumber >= 0) {\n retryAfter = asNumber;\n } else {\n const dateMs = new Date(raw).getTime();\n retryAfter = Number.isFinite(dateMs)\n ? Math.max(0, Math.ceil((dateMs - Date.now()) / 1000))\n : 0;\n }\n throw new RateLimitError(retryAfter, body);\n }\n const detail =\n body && typeof body === \"object\" && body !== null && \"detail\" in body\n ? (body as { detail: unknown }).detail\n : null;\n const message = typeof detail === \"string\" ? detail : response.statusText;\n throw new ApiError(response.status, message, body);\n }\n\n return response;\n }\n}\n\nclass AuthApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async me() {\n const { data } = await this.deps.raw.GET(\"/api/v1/auth/me\");\n return data;\n }\n}\n\nclass ValidationsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n /**\n * Submit a new validation request.\n *\n * Note: `cui` (Guatemalan ID number) is NOT a form field — it is extracted\n * automatically from the uploaded DPI image via OCR on the server side.\n *\n * @param input.naturalezaCliente - Client nature, e.g. \"individual\" or \"juridica\".\n * @param input.paisOrigenFondos - Optional ISO-3166-1 alpha-2 country code for funds origin.\n * @param input.dpiFile - DPI image blob (front side; OCR extracts the CUI).\n */\n // Fix #5 — C1: Correct multipart field names to match the real API.\n // - Single dpi_file (not dpi_front + dpi_back)\n // - pais_origen_fondos (not pais_origen)\n // - No cui field (extracted via OCR on the server)\n async create(input: {\n naturalezaCliente: string;\n paisOrigenFondos?: string;\n dpiFile: Blob;\n }): Promise<{ id: string; status: string }> {\n const form = new FormData();\n form.append(\"naturaleza_cliente\", input.naturalezaCliente);\n if (input.paisOrigenFondos) {\n form.append(\"pais_origen_fondos\", input.paisOrigenFondos);\n }\n form.append(\"dpi_file\", input.dpiFile);\n\n // openapi-fetch's multipart support is limited; use wrappedFetch directly.\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations`,\n { method: \"POST\", body: form },\n );\n return (await response.json()) as { id: string; status: string };\n }\n\n async get(id: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/validations/{validation_id}\", {\n params: { path: { validation_id: id } },\n });\n return data;\n }\n\n // Fix #4 — I4: Set accept:application/pdf so the server streams the PDF\n // instead of attempting a JSON serialization.\n async report(id: string): Promise<Blob> {\n const response = await this.deps.wrappedFetch(\n `${this.deps.baseUrl}/api/v1/validations/${id}/report`,\n { method: \"GET\", headers: { accept: \"application/pdf\" } },\n );\n return await response.blob();\n }\n}\n\nclass CreditsApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async balance() {\n const { data } = await this.deps.raw.GET(\"/api/v1/credits/balance\");\n return data;\n }\n}\n\nclass ApiKeysApi {\n constructor(private readonly deps: SubApiDeps) {}\n\n async list(tenantId: string) {\n const { data } = await this.deps.raw.GET(\"/api/v1/tenants/{tenant_id}/api-keys\", {\n params: { path: { tenant_id: tenantId } },\n });\n return data;\n }\n}\n","/**\n * Errors thrown by the Verifiquemos SDK.\n *\n * `ApiError` is thrown for any HTTP 4xx/5xx response from the API.\n * `RateLimitError` is a specialization for 429 responses.\n * `NetworkError` wraps transport failures (DNS, timeout, connection reset).\n */\n\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n\n isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n isPaymentRequired(): boolean {\n return this.status === 402;\n }\n\n isNotFound(): boolean {\n return this.status === 404;\n }\n\n isServerError(): boolean {\n return this.status >= 500 && this.status < 600;\n }\n}\n\nexport class RateLimitError extends ApiError {\n readonly retryAfterSeconds: number;\n\n constructor(retryAfterSeconds: number, body: unknown = null) {\n super(429, \"Rate limit exceeded\", body);\n this.name = \"RateLimitError\";\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport class NetworkError extends Error {\n override readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@verifiquemos/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Official TypeScript SDK for the Verifiquemos compliance API",
5
5
  "license": "MIT",
6
6
  "author": "Verifiquemos",
7
- "homepage": "https://verifiquemos.gt/developers",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://dev.azure.com/verifiquemos/Verifiquemos/_git/verifiquemos-sdk-typescript"
11
- },
7
+ "homepage": "https://verifiquemos.com/developers",
12
8
  "main": "./dist/index.cjs",
13
9
  "module": "./dist/index.js",
14
10
  "types": "./dist/index.d.ts",