@verixo/sdk 0.1.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/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # @verixo/sdk
2
+
3
+ Official Node.js SDK for the [Verixo](https://verifiedcore.com) OTP-as-a-Service API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @verixo/sdk
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```ts
14
+ import Verixo from "@verixo/sdk";
15
+
16
+ const vc = new Verixo(process.env.VC_API_KEY);
17
+
18
+ const { numbers } = await vc.numbers.search({
19
+ serviceSlug: "whatsapp",
20
+ countryCode: "NG",
21
+ minScore: 80,
22
+ limit: 5,
23
+ });
24
+
25
+ const session = await vc.numbers.purchase({
26
+ serviceSlug: "whatsapp",
27
+ countryCode: "NG",
28
+ });
29
+
30
+ vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {
31
+ console.log(`OTP: ${otp} in ${latencyMs}ms`);
32
+ });
33
+ ```
34
+
35
+ Note: `purchase()` takes `serviceSlug` + `countryCode`, not a specific
36
+ `numberId` -- the server picks the best available number by Health Score at
37
+ purchase time, since a candidate returned by `search()` may already be gone
38
+ by the time you'd reference it back.
39
+
40
+ ## Polling instead of WebSocket push
41
+
42
+ `subscribe()` requires a persistent connection, which isn't always practical
43
+ (e.g. serverless functions). Use `sessions.waitForOtp()` instead:
44
+
45
+ ```ts
46
+ const result = await vc.sessions.waitForOtp(session.sessionToken);
47
+ if (result.status === "DELIVERED") console.log(result.otpCode);
48
+ ```
49
+
50
+ ## Errors
51
+
52
+ All non-2xx responses throw a `VerixoError` with `status`, and usually
53
+ `code`/`detail` from the API's error body:
54
+
55
+ ```ts
56
+ import { VerixoError } from "@verixo/sdk";
57
+
58
+ try {
59
+ await vc.numbers.purchase({ serviceSlug: "whatsapp", countryCode: "NG" });
60
+ } catch (err) {
61
+ if (err instanceof VerixoError && err.status === 402) {
62
+ console.log("Insufficient wallet balance");
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Requirements
68
+
69
+ Node.js 18+ for `fetch`. `subscribe()` additionally requires a global
70
+ `WebSocket` implementation, built into Node 22+ and all browsers; on Node
71
+ 18-21, polyfill `globalThis.WebSocket` (e.g. with the `ws` package) before
72
+ calling `subscribe()`.
73
+
74
+ ## Development
75
+
76
+ ```bash
77
+ npm install
78
+ npm run build # tsup -> dist/ (ESM + CJS + .d.ts)
79
+ npm run typecheck
80
+ node examples/quickstart.mjs # requires VC_API_KEY env var, see file header
81
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ VerixoError: () => VerixoError,
24
+ default: () => Verixo
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/errors.ts
29
+ var VerixoError = class extends Error {
30
+ status;
31
+ code;
32
+ detail;
33
+ raw;
34
+ constructor(message, options) {
35
+ super(message);
36
+ this.name = "VerixoError";
37
+ this.status = options.status;
38
+ this.code = options.code;
39
+ this.detail = options.detail;
40
+ this.raw = options.raw;
41
+ }
42
+ };
43
+ var STATUS_FALLBACKS = {
44
+ 401: "Missing or invalid API key.",
45
+ 402: "Insufficient wallet balance.",
46
+ 403: "You don't have access to this resource.",
47
+ 404: "Not found.",
48
+ 408: "Request timed out.",
49
+ 422: "The request did not meet a required condition (e.g. minScore).",
50
+ 429: "Rate limit exceeded."
51
+ };
52
+ async function errorFromResponse(res) {
53
+ let body;
54
+ try {
55
+ body = await res.json();
56
+ } catch {
57
+ body = void 0;
58
+ }
59
+ const obj = body && typeof body === "object" ? body : {};
60
+ const detail = obj.detail ?? obj.error ?? obj.message;
61
+ const code = obj.grpcCode ?? obj.code;
62
+ const message = detail ?? STATUS_FALLBACKS[res.status] ?? `Request failed with status ${res.status}`;
63
+ return new VerixoError(message, { status: res.status, code, detail, raw: body });
64
+ }
65
+
66
+ // src/client.ts
67
+ var DEFAULT_BASE_URL = "https://api.verixo.com";
68
+ var HttpClient = class {
69
+ apiKey;
70
+ baseUrl;
71
+ constructor(apiKey, options = {}) {
72
+ if (!apiKey) {
73
+ throw new Error("Verixo: an API key (or JWT) is required, e.g. new Verixo('vc_test_...')");
74
+ }
75
+ this.apiKey = apiKey;
76
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
77
+ }
78
+ async request(path, init = {}) {
79
+ const isApiKey = this.apiKey.startsWith("vc_test_") || this.apiKey.startsWith("vc_live_");
80
+ const authorization = isApiKey ? this.apiKey : `Bearer ${this.apiKey}`;
81
+ const res = await fetch(`${this.baseUrl}${path}`, {
82
+ ...init,
83
+ headers: {
84
+ Authorization: authorization,
85
+ ...init.body ? { "Content-Type": "application/json" } : {},
86
+ ...init.headers
87
+ }
88
+ });
89
+ if (!res.ok) {
90
+ throw await errorFromResponse(res);
91
+ }
92
+ if (res.status === 204) return void 0;
93
+ return await res.json();
94
+ }
95
+ get(path) {
96
+ return this.request(path, { method: "GET" });
97
+ }
98
+ post(path, body) {
99
+ return this.request(path, { method: "POST", body: body ? JSON.stringify(body) : void 0 });
100
+ }
101
+ /** ws:// or wss:// origin derived from baseUrl, for the OTP push subscription. */
102
+ get wsOrigin() {
103
+ return this.baseUrl.replace(/^http/, "ws");
104
+ }
105
+ };
106
+
107
+ // src/resources/analytics.ts
108
+ var Analytics = class {
109
+ constructor(http) {
110
+ this.http = http;
111
+ }
112
+ http;
113
+ /** Public delivery-rate stats by country for a service. No auth required by the API itself. */
114
+ rates(serviceSlug = "whatsapp") {
115
+ return this.http.get(`/api/v1/analytics/rates?serviceSlug=${encodeURIComponent(serviceSlug)}`);
116
+ }
117
+ };
118
+
119
+ // src/resources/numbers.ts
120
+ var Numbers = class {
121
+ constructor(http) {
122
+ this.http = http;
123
+ }
124
+ http;
125
+ /** Browse available numbers ranked by Health Score (TM). No purchase is made. */
126
+ search(params) {
127
+ const q = new URLSearchParams({ serviceSlug: params.serviceSlug });
128
+ if (params.countryCode) q.set("countryCode", params.countryCode);
129
+ if (params.minScore != null) q.set("minScore", String(params.minScore));
130
+ if (params.limit != null) q.set("limit", String(params.limit));
131
+ return this.http.get(`/api/v1/numbers/search?${q}`);
132
+ }
133
+ /**
134
+ * Buy a number for a service + country. The server picks the best available
135
+ * candidate by Health Score automatically -- there's no numberId parameter,
136
+ * since by the time you'd pass one back the number may already be gone.
137
+ */
138
+ purchase(params) {
139
+ return this.http.post("/api/v1/numbers/purchase", params);
140
+ }
141
+ };
142
+
143
+ // src/resources/sessions.ts
144
+ var Sessions = class {
145
+ constructor(http) {
146
+ this.http = http;
147
+ }
148
+ http;
149
+ /** Poll a session's current delivery status. */
150
+ get(sessionToken) {
151
+ return this.http.get(`/api/v1/numbers/session/${encodeURIComponent(sessionToken)}`);
152
+ }
153
+ /**
154
+ * Poll until the session reaches a terminal state (DELIVERED, REFUNDED, EXPIRED, FAILED)
155
+ * or the timeout elapses. Prefer `vc.subscribe()` for real-time push -- this is a fallback
156
+ * for environments where a persistent WebSocket isn't practical (e.g. serverless functions).
157
+ */
158
+ async waitForOtp(sessionToken, timeoutMs = 9e4, intervalMs = 2e3) {
159
+ const deadline = Date.now() + timeoutMs;
160
+ for (; ; ) {
161
+ const session = await this.get(sessionToken);
162
+ if (session.status !== "PENDING") return session;
163
+ if (Date.now() >= deadline) return session;
164
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
165
+ }
166
+ }
167
+ };
168
+
169
+ // src/subscribe.ts
170
+ var import_stompjs = require("@stomp/stompjs");
171
+ function subscribe(http, sessionToken, onOtp) {
172
+ if (typeof WebSocket === "undefined") {
173
+ throw new Error(
174
+ "Verixo.subscribe() requires a global WebSocket implementation. This is built into browsers and Node 22+; on older Node, polyfill globalThis.WebSocket (e.g. with the 'ws' package) before calling subscribe()."
175
+ );
176
+ }
177
+ const subscribedAt = Date.now();
178
+ const client = new import_stompjs.Client({
179
+ brokerURL: `${http.wsOrigin}/ws`,
180
+ reconnectDelay: 5e3,
181
+ onConnect: () => {
182
+ client.subscribe(`/topic/otp/${sessionToken}`, (message) => {
183
+ try {
184
+ const payload = JSON.parse(message.body);
185
+ onOtp({ ...payload, latencyMs: Date.now() - subscribedAt });
186
+ } catch {
187
+ }
188
+ });
189
+ }
190
+ });
191
+ client.activate();
192
+ return () => {
193
+ void client.deactivate();
194
+ };
195
+ }
196
+
197
+ // src/index.ts
198
+ var Verixo = class {
199
+ numbers;
200
+ sessions;
201
+ analytics;
202
+ http;
203
+ constructor(apiKey, options = {}) {
204
+ this.http = new HttpClient(apiKey, options);
205
+ this.numbers = new Numbers(this.http);
206
+ this.sessions = new Sessions(this.http);
207
+ this.analytics = new Analytics(this.http);
208
+ }
209
+ /**
210
+ * Get pushed the OTP for a session in real time, instead of polling
211
+ * `sessions.get()`. Returns an unsubscribe function.
212
+ *
213
+ * @example
214
+ * const session = await vc.numbers.purchase({ serviceSlug: 'whatsapp', countryCode: 'NG' })
215
+ * vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {
216
+ * console.log(`OTP: ${otp} in ${latencyMs}ms`)
217
+ * })
218
+ */
219
+ subscribe(sessionToken, onOtp) {
220
+ return subscribe(this.http, sessionToken, onOtp);
221
+ }
222
+ };
223
+ // Annotate the CommonJS export names for ESM import in node:
224
+ 0 && (module.exports = {
225
+ VerixoError
226
+ });
227
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/resources/analytics.ts","../src/resources/numbers.ts","../src/resources/sessions.ts","../src/subscribe.ts"],"sourcesContent":["import { HttpClient } from \"./client.js\";\nimport { Analytics } from \"./resources/analytics.js\";\nimport { Numbers } from \"./resources/numbers.js\";\nimport { Sessions } from \"./resources/sessions.js\";\nimport { subscribe } from \"./subscribe.js\";\nimport type { OtpPush, VerixoOptions } from \"./types.js\";\n\nexport { VerixoError } from \"./errors.js\";\nexport type {\n DeliveryRate,\n OtpPush,\n PurchaseNumberParams,\n PurchaseResult,\n SearchNumbersParams,\n SearchNumbersResult,\n SessionState,\n SessionStatus,\n VerixoOptions,\n VirtualNumber,\n} from \"./types.js\";\n\nexport default class Verixo {\n readonly numbers: Numbers;\n readonly sessions: Sessions;\n readonly analytics: Analytics;\n private readonly http: HttpClient;\n\n constructor(apiKey: string, options: VerixoOptions = {}) {\n this.http = new HttpClient(apiKey, options);\n this.numbers = new Numbers(this.http);\n this.sessions = new Sessions(this.http);\n this.analytics = new Analytics(this.http);\n }\n\n /**\n * Get pushed the OTP for a session in real time, instead of polling\n * `sessions.get()`. Returns an unsubscribe function.\n *\n * @example\n * const session = await vc.numbers.purchase({ serviceSlug: 'whatsapp', countryCode: 'NG' })\n * vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {\n * console.log(`OTP: ${otp} in ${latencyMs}ms`)\n * })\n */\n subscribe(sessionToken: string, onOtp: (push: OtpPush) => void): () => void {\n return subscribe(this.http, sessionToken, onOtp);\n }\n}\n","export interface VerixoErrorOptions {\n status: number;\n code?: string;\n detail?: string;\n raw?: unknown;\n}\n\n/**\n * Thrown for any non-2xx response from the Verixo API. `status` is always\n * present; `code`/`detail` are populated when the gateway returns a JSON\n * error body (most do) -- a small number of paths (e.g. an invalid API key\n * rejected before reaching a controller) return an empty body, in which case\n * only `status` and a generic `message` are available.\n */\nexport class VerixoError extends Error {\n readonly status: number;\n readonly code?: string;\n readonly detail?: string;\n readonly raw?: unknown;\n\n constructor(message: string, options: VerixoErrorOptions) {\n super(message);\n this.name = \"VerixoError\";\n this.status = options.status;\n this.code = options.code;\n this.detail = options.detail;\n this.raw = options.raw;\n }\n}\n\nconst STATUS_FALLBACKS: Record<number, string> = {\n 401: \"Missing or invalid API key.\",\n 402: \"Insufficient wallet balance.\",\n 403: \"You don't have access to this resource.\",\n 404: \"Not found.\",\n 408: \"Request timed out.\",\n 422: \"The request did not meet a required condition (e.g. minScore).\",\n 429: \"Rate limit exceeded.\",\n};\n\nexport async function errorFromResponse(res: Response): Promise<VerixoError> {\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n body = undefined;\n }\n\n const obj = (body && typeof body === \"object\" ? body : {}) as Record<string, unknown>;\n const detail = (obj.detail ?? obj.error ?? obj.message) as string | undefined;\n const code = (obj.grpcCode ?? obj.code) as string | undefined;\n const message = detail ?? STATUS_FALLBACKS[res.status] ?? `Request failed with status ${res.status}`;\n\n return new VerixoError(message, { status: res.status, code, detail, raw: body });\n}\n","import { errorFromResponse } from \"./errors.js\";\nimport type { VerixoOptions } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.verixo.com\";\n\nexport class HttpClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n\n constructor(apiKey: string, options: VerixoOptions = {}) {\n if (!apiKey) {\n throw new Error(\"Verixo: an API key (or JWT) is required, e.g. new Verixo('vc_test_...')\");\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n }\n\n async request<T>(path: string, init: RequestInit = {}): Promise<T> {\n // The gateway classifies the credential by sniffing the raw Authorization header for a\n // vc_test_/vc_live_ prefix -- a \"Bearer \" prefix on an API key would make it misclassify\n // the request as a (malformed) JWT and reject it, so API keys go out unprefixed.\n const isApiKey = this.apiKey.startsWith(\"vc_test_\") || this.apiKey.startsWith(\"vc_live_\");\n const authorization = isApiKey ? this.apiKey : `Bearer ${this.apiKey}`;\n\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: {\n Authorization: authorization,\n ...(init.body ? { \"Content-Type\": \"application/json\" } : {}),\n ...init.headers,\n },\n });\n\n if (!res.ok) {\n throw await errorFromResponse(res);\n }\n\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n get<T>(path: string): Promise<T> {\n return this.request<T>(path, { method: \"GET\" });\n }\n\n post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>(path, { method: \"POST\", body: body ? JSON.stringify(body) : undefined });\n }\n\n /** ws:// or wss:// origin derived from baseUrl, for the OTP push subscription. */\n get wsOrigin(): string {\n return this.baseUrl.replace(/^http/, \"ws\");\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { DeliveryRate } from \"../types.js\";\n\nexport class Analytics {\n constructor(private readonly http: HttpClient) {}\n\n /** Public delivery-rate stats by country for a service. No auth required by the API itself. */\n rates(serviceSlug = \"whatsapp\"): Promise<{ rates: DeliveryRate[]; updatedAt: string }> {\n return this.http.get(`/api/v1/analytics/rates?serviceSlug=${encodeURIComponent(serviceSlug)}`);\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { PurchaseNumberParams, PurchaseResult, SearchNumbersParams, SearchNumbersResult } from \"../types.js\";\n\nexport class Numbers {\n constructor(private readonly http: HttpClient) {}\n\n /** Browse available numbers ranked by Health Score (TM). No purchase is made. */\n search(params: SearchNumbersParams): Promise<SearchNumbersResult> {\n const q = new URLSearchParams({ serviceSlug: params.serviceSlug });\n if (params.countryCode) q.set(\"countryCode\", params.countryCode);\n if (params.minScore != null) q.set(\"minScore\", String(params.minScore));\n if (params.limit != null) q.set(\"limit\", String(params.limit));\n return this.http.get<SearchNumbersResult>(`/api/v1/numbers/search?${q}`);\n }\n\n /**\n * Buy a number for a service + country. The server picks the best available\n * candidate by Health Score automatically -- there's no numberId parameter,\n * since by the time you'd pass one back the number may already be gone.\n */\n purchase(params: PurchaseNumberParams): Promise<PurchaseResult> {\n return this.http.post<PurchaseResult>(\"/api/v1/numbers/purchase\", params);\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { SessionStatus } from \"../types.js\";\n\nexport class Sessions {\n constructor(private readonly http: HttpClient) {}\n\n /** Poll a session's current delivery status. */\n get(sessionToken: string): Promise<SessionStatus> {\n return this.http.get<SessionStatus>(`/api/v1/numbers/session/${encodeURIComponent(sessionToken)}`);\n }\n\n /**\n * Poll until the session reaches a terminal state (DELIVERED, REFUNDED, EXPIRED, FAILED)\n * or the timeout elapses. Prefer `vc.subscribe()` for real-time push -- this is a fallback\n * for environments where a persistent WebSocket isn't practical (e.g. serverless functions).\n */\n async waitForOtp(sessionToken: string, timeoutMs = 90_000, intervalMs = 2_000): Promise<SessionStatus> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const session = await this.get(sessionToken);\n if (session.status !== \"PENDING\") return session;\n if (Date.now() >= deadline) return session;\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n }\n}\n","import { Client } from \"@stomp/stompjs\";\nimport type { HttpClient } from \"./client.js\";\nimport type { OtpPush } from \"./types.js\";\n\n/**\n * Subscribes to real-time OTP push for a session over the gateway's WebSocket\n * proxy (wss://.../ws -> delivery-service's STOMP broker, topic\n * /topic/otp/{sessionToken}). Requires a global WebSocket implementation --\n * present natively in browsers and Node 22+; on older Node, pass a `ws`\n * instance via globalThis.WebSocket before calling subscribe().\n *\n * Returns an unsubscribe function. The callback fires at most once per\n * session (a session only ever delivers one OTP).\n */\nexport function subscribe(http: HttpClient, sessionToken: string, onOtp: (push: OtpPush) => void): () => void {\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n \"Verixo.subscribe() requires a global WebSocket implementation. \" +\n \"This is built into browsers and Node 22+; on older Node, polyfill \" +\n \"globalThis.WebSocket (e.g. with the 'ws' package) before calling subscribe().\"\n );\n }\n\n const subscribedAt = Date.now();\n const client = new Client({\n brokerURL: `${http.wsOrigin}/ws`,\n reconnectDelay: 5_000,\n onConnect: () => {\n client.subscribe(`/topic/otp/${sessionToken}`, (message) => {\n try {\n const payload = JSON.parse(message.body) as Omit<OtpPush, \"latencyMs\">;\n onOtp({ ...payload, latencyMs: Date.now() - subscribedAt });\n } catch {\n // Ignore malformed frames rather than crashing the caller's process.\n }\n });\n },\n });\n\n client.activate();\n return () => {\n void client.deactivate();\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,177 @@
1
+ interface VerixoOptions {
2
+ /** Override the API base URL. Defaults to the production gateway. */
3
+ baseUrl?: string;
4
+ }
5
+ interface VirtualNumber {
6
+ numberId: string;
7
+ number: string;
8
+ e164Number: string;
9
+ healthScore: number;
10
+ numberType: string;
11
+ countryCode: string;
12
+ countryName: string;
13
+ countryFlag: string;
14
+ priceUsd: number;
15
+ deliveryRate: number;
16
+ avgLatencyMs: number | null;
17
+ available: boolean;
18
+ scoreColor: string;
19
+ carrierName: string;
20
+ }
21
+ interface SearchNumbersParams {
22
+ serviceSlug: string;
23
+ countryCode?: string;
24
+ minScore?: number;
25
+ limit?: number;
26
+ }
27
+ interface SearchNumbersResult {
28
+ numbers: VirtualNumber[];
29
+ totalAvailable: number;
30
+ countryCode: string;
31
+ serviceSlug: string;
32
+ }
33
+ interface PurchaseNumberParams {
34
+ serviceSlug: string;
35
+ countryCode: string;
36
+ /** Strip PII from delivery logs server-side. Default false. */
37
+ privacyMode?: boolean;
38
+ /** Auto-deliver a synthetic OTP immediately instead of waiting on a real carrier. Default false. */
39
+ testMode?: boolean;
40
+ channel?: "SMS" | "WHATSAPP" | "VOICE" | "EMAIL" | "VIBER";
41
+ }
42
+ interface PurchaseResult {
43
+ sessionToken: string;
44
+ purchaseId: string;
45
+ webSocketTopic: string;
46
+ e164Number: string;
47
+ healthScoreAtPurchase: number;
48
+ scoreColor: string;
49
+ carrierType: string;
50
+ aggregatorType: string;
51
+ priceUsd: number;
52
+ slaRemainingSeconds: number;
53
+ slaExpiresAt: string;
54
+ privacyMode: boolean;
55
+ confidenceLabel: string;
56
+ channel: string;
57
+ }
58
+ type SessionState = "PENDING" | "DELIVERED" | "REFUNDED" | "EXPIRED" | "FAILED";
59
+ interface SessionStatus {
60
+ sessionToken: string;
61
+ status: SessionState;
62
+ otpCode: string;
63
+ slaRemainingSeconds: number;
64
+ failureReason: string;
65
+ aggregatorType: string;
66
+ healthScore: number;
67
+ privacyMode: boolean;
68
+ channel: string;
69
+ }
70
+ interface DeliveryRate {
71
+ countryCode: string;
72
+ countryName: string;
73
+ countryFlag: string;
74
+ rate: number;
75
+ latency: string;
76
+ totalDeliveries: number;
77
+ updatedAt: string;
78
+ }
79
+ interface OtpPush {
80
+ sessionToken: string;
81
+ otp: string;
82
+ e164Number: string;
83
+ serviceSlug: string;
84
+ privacyMode: boolean;
85
+ deliveredAt: string;
86
+ /** Milliseconds between calling subscribe() and this push arriving -- not from the server. */
87
+ latencyMs: number;
88
+ }
89
+
90
+ declare class HttpClient {
91
+ readonly apiKey: string;
92
+ readonly baseUrl: string;
93
+ constructor(apiKey: string, options?: VerixoOptions);
94
+ request<T>(path: string, init?: RequestInit): Promise<T>;
95
+ get<T>(path: string): Promise<T>;
96
+ post<T>(path: string, body?: unknown): Promise<T>;
97
+ /** ws:// or wss:// origin derived from baseUrl, for the OTP push subscription. */
98
+ get wsOrigin(): string;
99
+ }
100
+
101
+ declare class Analytics {
102
+ private readonly http;
103
+ constructor(http: HttpClient);
104
+ /** Public delivery-rate stats by country for a service. No auth required by the API itself. */
105
+ rates(serviceSlug?: string): Promise<{
106
+ rates: DeliveryRate[];
107
+ updatedAt: string;
108
+ }>;
109
+ }
110
+
111
+ declare class Numbers {
112
+ private readonly http;
113
+ constructor(http: HttpClient);
114
+ /** Browse available numbers ranked by Health Score (TM). No purchase is made. */
115
+ search(params: SearchNumbersParams): Promise<SearchNumbersResult>;
116
+ /**
117
+ * Buy a number for a service + country. The server picks the best available
118
+ * candidate by Health Score automatically -- there's no numberId parameter,
119
+ * since by the time you'd pass one back the number may already be gone.
120
+ */
121
+ purchase(params: PurchaseNumberParams): Promise<PurchaseResult>;
122
+ }
123
+
124
+ declare class Sessions {
125
+ private readonly http;
126
+ constructor(http: HttpClient);
127
+ /** Poll a session's current delivery status. */
128
+ get(sessionToken: string): Promise<SessionStatus>;
129
+ /**
130
+ * Poll until the session reaches a terminal state (DELIVERED, REFUNDED, EXPIRED, FAILED)
131
+ * or the timeout elapses. Prefer `vc.subscribe()` for real-time push -- this is a fallback
132
+ * for environments where a persistent WebSocket isn't practical (e.g. serverless functions).
133
+ */
134
+ waitForOtp(sessionToken: string, timeoutMs?: number, intervalMs?: number): Promise<SessionStatus>;
135
+ }
136
+
137
+ interface VerixoErrorOptions {
138
+ status: number;
139
+ code?: string;
140
+ detail?: string;
141
+ raw?: unknown;
142
+ }
143
+ /**
144
+ * Thrown for any non-2xx response from the Verixo API. `status` is always
145
+ * present; `code`/`detail` are populated when the gateway returns a JSON
146
+ * error body (most do) -- a small number of paths (e.g. an invalid API key
147
+ * rejected before reaching a controller) return an empty body, in which case
148
+ * only `status` and a generic `message` are available.
149
+ */
150
+ declare class VerixoError extends Error {
151
+ readonly status: number;
152
+ readonly code?: string;
153
+ readonly detail?: string;
154
+ readonly raw?: unknown;
155
+ constructor(message: string, options: VerixoErrorOptions);
156
+ }
157
+
158
+ declare class Verixo {
159
+ readonly numbers: Numbers;
160
+ readonly sessions: Sessions;
161
+ readonly analytics: Analytics;
162
+ private readonly http;
163
+ constructor(apiKey: string, options?: VerixoOptions);
164
+ /**
165
+ * Get pushed the OTP for a session in real time, instead of polling
166
+ * `sessions.get()`. Returns an unsubscribe function.
167
+ *
168
+ * @example
169
+ * const session = await vc.numbers.purchase({ serviceSlug: 'whatsapp', countryCode: 'NG' })
170
+ * vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {
171
+ * console.log(`OTP: ${otp} in ${latencyMs}ms`)
172
+ * })
173
+ */
174
+ subscribe(sessionToken: string, onOtp: (push: OtpPush) => void): () => void;
175
+ }
176
+
177
+ export { type DeliveryRate, type OtpPush, type PurchaseNumberParams, type PurchaseResult, type SearchNumbersParams, type SearchNumbersResult, type SessionState, type SessionStatus, VerixoError, type VerixoOptions, type VirtualNumber, Verixo as default };
@@ -0,0 +1,177 @@
1
+ interface VerixoOptions {
2
+ /** Override the API base URL. Defaults to the production gateway. */
3
+ baseUrl?: string;
4
+ }
5
+ interface VirtualNumber {
6
+ numberId: string;
7
+ number: string;
8
+ e164Number: string;
9
+ healthScore: number;
10
+ numberType: string;
11
+ countryCode: string;
12
+ countryName: string;
13
+ countryFlag: string;
14
+ priceUsd: number;
15
+ deliveryRate: number;
16
+ avgLatencyMs: number | null;
17
+ available: boolean;
18
+ scoreColor: string;
19
+ carrierName: string;
20
+ }
21
+ interface SearchNumbersParams {
22
+ serviceSlug: string;
23
+ countryCode?: string;
24
+ minScore?: number;
25
+ limit?: number;
26
+ }
27
+ interface SearchNumbersResult {
28
+ numbers: VirtualNumber[];
29
+ totalAvailable: number;
30
+ countryCode: string;
31
+ serviceSlug: string;
32
+ }
33
+ interface PurchaseNumberParams {
34
+ serviceSlug: string;
35
+ countryCode: string;
36
+ /** Strip PII from delivery logs server-side. Default false. */
37
+ privacyMode?: boolean;
38
+ /** Auto-deliver a synthetic OTP immediately instead of waiting on a real carrier. Default false. */
39
+ testMode?: boolean;
40
+ channel?: "SMS" | "WHATSAPP" | "VOICE" | "EMAIL" | "VIBER";
41
+ }
42
+ interface PurchaseResult {
43
+ sessionToken: string;
44
+ purchaseId: string;
45
+ webSocketTopic: string;
46
+ e164Number: string;
47
+ healthScoreAtPurchase: number;
48
+ scoreColor: string;
49
+ carrierType: string;
50
+ aggregatorType: string;
51
+ priceUsd: number;
52
+ slaRemainingSeconds: number;
53
+ slaExpiresAt: string;
54
+ privacyMode: boolean;
55
+ confidenceLabel: string;
56
+ channel: string;
57
+ }
58
+ type SessionState = "PENDING" | "DELIVERED" | "REFUNDED" | "EXPIRED" | "FAILED";
59
+ interface SessionStatus {
60
+ sessionToken: string;
61
+ status: SessionState;
62
+ otpCode: string;
63
+ slaRemainingSeconds: number;
64
+ failureReason: string;
65
+ aggregatorType: string;
66
+ healthScore: number;
67
+ privacyMode: boolean;
68
+ channel: string;
69
+ }
70
+ interface DeliveryRate {
71
+ countryCode: string;
72
+ countryName: string;
73
+ countryFlag: string;
74
+ rate: number;
75
+ latency: string;
76
+ totalDeliveries: number;
77
+ updatedAt: string;
78
+ }
79
+ interface OtpPush {
80
+ sessionToken: string;
81
+ otp: string;
82
+ e164Number: string;
83
+ serviceSlug: string;
84
+ privacyMode: boolean;
85
+ deliveredAt: string;
86
+ /** Milliseconds between calling subscribe() and this push arriving -- not from the server. */
87
+ latencyMs: number;
88
+ }
89
+
90
+ declare class HttpClient {
91
+ readonly apiKey: string;
92
+ readonly baseUrl: string;
93
+ constructor(apiKey: string, options?: VerixoOptions);
94
+ request<T>(path: string, init?: RequestInit): Promise<T>;
95
+ get<T>(path: string): Promise<T>;
96
+ post<T>(path: string, body?: unknown): Promise<T>;
97
+ /** ws:// or wss:// origin derived from baseUrl, for the OTP push subscription. */
98
+ get wsOrigin(): string;
99
+ }
100
+
101
+ declare class Analytics {
102
+ private readonly http;
103
+ constructor(http: HttpClient);
104
+ /** Public delivery-rate stats by country for a service. No auth required by the API itself. */
105
+ rates(serviceSlug?: string): Promise<{
106
+ rates: DeliveryRate[];
107
+ updatedAt: string;
108
+ }>;
109
+ }
110
+
111
+ declare class Numbers {
112
+ private readonly http;
113
+ constructor(http: HttpClient);
114
+ /** Browse available numbers ranked by Health Score (TM). No purchase is made. */
115
+ search(params: SearchNumbersParams): Promise<SearchNumbersResult>;
116
+ /**
117
+ * Buy a number for a service + country. The server picks the best available
118
+ * candidate by Health Score automatically -- there's no numberId parameter,
119
+ * since by the time you'd pass one back the number may already be gone.
120
+ */
121
+ purchase(params: PurchaseNumberParams): Promise<PurchaseResult>;
122
+ }
123
+
124
+ declare class Sessions {
125
+ private readonly http;
126
+ constructor(http: HttpClient);
127
+ /** Poll a session's current delivery status. */
128
+ get(sessionToken: string): Promise<SessionStatus>;
129
+ /**
130
+ * Poll until the session reaches a terminal state (DELIVERED, REFUNDED, EXPIRED, FAILED)
131
+ * or the timeout elapses. Prefer `vc.subscribe()` for real-time push -- this is a fallback
132
+ * for environments where a persistent WebSocket isn't practical (e.g. serverless functions).
133
+ */
134
+ waitForOtp(sessionToken: string, timeoutMs?: number, intervalMs?: number): Promise<SessionStatus>;
135
+ }
136
+
137
+ interface VerixoErrorOptions {
138
+ status: number;
139
+ code?: string;
140
+ detail?: string;
141
+ raw?: unknown;
142
+ }
143
+ /**
144
+ * Thrown for any non-2xx response from the Verixo API. `status` is always
145
+ * present; `code`/`detail` are populated when the gateway returns a JSON
146
+ * error body (most do) -- a small number of paths (e.g. an invalid API key
147
+ * rejected before reaching a controller) return an empty body, in which case
148
+ * only `status` and a generic `message` are available.
149
+ */
150
+ declare class VerixoError extends Error {
151
+ readonly status: number;
152
+ readonly code?: string;
153
+ readonly detail?: string;
154
+ readonly raw?: unknown;
155
+ constructor(message: string, options: VerixoErrorOptions);
156
+ }
157
+
158
+ declare class Verixo {
159
+ readonly numbers: Numbers;
160
+ readonly sessions: Sessions;
161
+ readonly analytics: Analytics;
162
+ private readonly http;
163
+ constructor(apiKey: string, options?: VerixoOptions);
164
+ /**
165
+ * Get pushed the OTP for a session in real time, instead of polling
166
+ * `sessions.get()`. Returns an unsubscribe function.
167
+ *
168
+ * @example
169
+ * const session = await vc.numbers.purchase({ serviceSlug: 'whatsapp', countryCode: 'NG' })
170
+ * vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {
171
+ * console.log(`OTP: ${otp} in ${latencyMs}ms`)
172
+ * })
173
+ */
174
+ subscribe(sessionToken: string, onOtp: (push: OtpPush) => void): () => void;
175
+ }
176
+
177
+ export { type DeliveryRate, type OtpPush, type PurchaseNumberParams, type PurchaseResult, type SearchNumbersParams, type SearchNumbersResult, type SessionState, type SessionStatus, VerixoError, type VerixoOptions, type VirtualNumber, Verixo as default };
package/dist/index.js ADDED
@@ -0,0 +1,200 @@
1
+ // src/errors.ts
2
+ var VerixoError = class extends Error {
3
+ status;
4
+ code;
5
+ detail;
6
+ raw;
7
+ constructor(message, options) {
8
+ super(message);
9
+ this.name = "VerixoError";
10
+ this.status = options.status;
11
+ this.code = options.code;
12
+ this.detail = options.detail;
13
+ this.raw = options.raw;
14
+ }
15
+ };
16
+ var STATUS_FALLBACKS = {
17
+ 401: "Missing or invalid API key.",
18
+ 402: "Insufficient wallet balance.",
19
+ 403: "You don't have access to this resource.",
20
+ 404: "Not found.",
21
+ 408: "Request timed out.",
22
+ 422: "The request did not meet a required condition (e.g. minScore).",
23
+ 429: "Rate limit exceeded."
24
+ };
25
+ async function errorFromResponse(res) {
26
+ let body;
27
+ try {
28
+ body = await res.json();
29
+ } catch {
30
+ body = void 0;
31
+ }
32
+ const obj = body && typeof body === "object" ? body : {};
33
+ const detail = obj.detail ?? obj.error ?? obj.message;
34
+ const code = obj.grpcCode ?? obj.code;
35
+ const message = detail ?? STATUS_FALLBACKS[res.status] ?? `Request failed with status ${res.status}`;
36
+ return new VerixoError(message, { status: res.status, code, detail, raw: body });
37
+ }
38
+
39
+ // src/client.ts
40
+ var DEFAULT_BASE_URL = "https://api.verixo.com";
41
+ var HttpClient = class {
42
+ apiKey;
43
+ baseUrl;
44
+ constructor(apiKey, options = {}) {
45
+ if (!apiKey) {
46
+ throw new Error("Verixo: an API key (or JWT) is required, e.g. new Verixo('vc_test_...')");
47
+ }
48
+ this.apiKey = apiKey;
49
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
50
+ }
51
+ async request(path, init = {}) {
52
+ const isApiKey = this.apiKey.startsWith("vc_test_") || this.apiKey.startsWith("vc_live_");
53
+ const authorization = isApiKey ? this.apiKey : `Bearer ${this.apiKey}`;
54
+ const res = await fetch(`${this.baseUrl}${path}`, {
55
+ ...init,
56
+ headers: {
57
+ Authorization: authorization,
58
+ ...init.body ? { "Content-Type": "application/json" } : {},
59
+ ...init.headers
60
+ }
61
+ });
62
+ if (!res.ok) {
63
+ throw await errorFromResponse(res);
64
+ }
65
+ if (res.status === 204) return void 0;
66
+ return await res.json();
67
+ }
68
+ get(path) {
69
+ return this.request(path, { method: "GET" });
70
+ }
71
+ post(path, body) {
72
+ return this.request(path, { method: "POST", body: body ? JSON.stringify(body) : void 0 });
73
+ }
74
+ /** ws:// or wss:// origin derived from baseUrl, for the OTP push subscription. */
75
+ get wsOrigin() {
76
+ return this.baseUrl.replace(/^http/, "ws");
77
+ }
78
+ };
79
+
80
+ // src/resources/analytics.ts
81
+ var Analytics = class {
82
+ constructor(http) {
83
+ this.http = http;
84
+ }
85
+ http;
86
+ /** Public delivery-rate stats by country for a service. No auth required by the API itself. */
87
+ rates(serviceSlug = "whatsapp") {
88
+ return this.http.get(`/api/v1/analytics/rates?serviceSlug=${encodeURIComponent(serviceSlug)}`);
89
+ }
90
+ };
91
+
92
+ // src/resources/numbers.ts
93
+ var Numbers = class {
94
+ constructor(http) {
95
+ this.http = http;
96
+ }
97
+ http;
98
+ /** Browse available numbers ranked by Health Score (TM). No purchase is made. */
99
+ search(params) {
100
+ const q = new URLSearchParams({ serviceSlug: params.serviceSlug });
101
+ if (params.countryCode) q.set("countryCode", params.countryCode);
102
+ if (params.minScore != null) q.set("minScore", String(params.minScore));
103
+ if (params.limit != null) q.set("limit", String(params.limit));
104
+ return this.http.get(`/api/v1/numbers/search?${q}`);
105
+ }
106
+ /**
107
+ * Buy a number for a service + country. The server picks the best available
108
+ * candidate by Health Score automatically -- there's no numberId parameter,
109
+ * since by the time you'd pass one back the number may already be gone.
110
+ */
111
+ purchase(params) {
112
+ return this.http.post("/api/v1/numbers/purchase", params);
113
+ }
114
+ };
115
+
116
+ // src/resources/sessions.ts
117
+ var Sessions = class {
118
+ constructor(http) {
119
+ this.http = http;
120
+ }
121
+ http;
122
+ /** Poll a session's current delivery status. */
123
+ get(sessionToken) {
124
+ return this.http.get(`/api/v1/numbers/session/${encodeURIComponent(sessionToken)}`);
125
+ }
126
+ /**
127
+ * Poll until the session reaches a terminal state (DELIVERED, REFUNDED, EXPIRED, FAILED)
128
+ * or the timeout elapses. Prefer `vc.subscribe()` for real-time push -- this is a fallback
129
+ * for environments where a persistent WebSocket isn't practical (e.g. serverless functions).
130
+ */
131
+ async waitForOtp(sessionToken, timeoutMs = 9e4, intervalMs = 2e3) {
132
+ const deadline = Date.now() + timeoutMs;
133
+ for (; ; ) {
134
+ const session = await this.get(sessionToken);
135
+ if (session.status !== "PENDING") return session;
136
+ if (Date.now() >= deadline) return session;
137
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
138
+ }
139
+ }
140
+ };
141
+
142
+ // src/subscribe.ts
143
+ import { Client } from "@stomp/stompjs";
144
+ function subscribe(http, sessionToken, onOtp) {
145
+ if (typeof WebSocket === "undefined") {
146
+ throw new Error(
147
+ "Verixo.subscribe() requires a global WebSocket implementation. This is built into browsers and Node 22+; on older Node, polyfill globalThis.WebSocket (e.g. with the 'ws' package) before calling subscribe()."
148
+ );
149
+ }
150
+ const subscribedAt = Date.now();
151
+ const client = new Client({
152
+ brokerURL: `${http.wsOrigin}/ws`,
153
+ reconnectDelay: 5e3,
154
+ onConnect: () => {
155
+ client.subscribe(`/topic/otp/${sessionToken}`, (message) => {
156
+ try {
157
+ const payload = JSON.parse(message.body);
158
+ onOtp({ ...payload, latencyMs: Date.now() - subscribedAt });
159
+ } catch {
160
+ }
161
+ });
162
+ }
163
+ });
164
+ client.activate();
165
+ return () => {
166
+ void client.deactivate();
167
+ };
168
+ }
169
+
170
+ // src/index.ts
171
+ var Verixo = class {
172
+ numbers;
173
+ sessions;
174
+ analytics;
175
+ http;
176
+ constructor(apiKey, options = {}) {
177
+ this.http = new HttpClient(apiKey, options);
178
+ this.numbers = new Numbers(this.http);
179
+ this.sessions = new Sessions(this.http);
180
+ this.analytics = new Analytics(this.http);
181
+ }
182
+ /**
183
+ * Get pushed the OTP for a session in real time, instead of polling
184
+ * `sessions.get()`. Returns an unsubscribe function.
185
+ *
186
+ * @example
187
+ * const session = await vc.numbers.purchase({ serviceSlug: 'whatsapp', countryCode: 'NG' })
188
+ * vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {
189
+ * console.log(`OTP: ${otp} in ${latencyMs}ms`)
190
+ * })
191
+ */
192
+ subscribe(sessionToken, onOtp) {
193
+ return subscribe(this.http, sessionToken, onOtp);
194
+ }
195
+ };
196
+ export {
197
+ VerixoError,
198
+ Verixo as default
199
+ };
200
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/resources/analytics.ts","../src/resources/numbers.ts","../src/resources/sessions.ts","../src/subscribe.ts","../src/index.ts"],"sourcesContent":["export interface VerixoErrorOptions {\n status: number;\n code?: string;\n detail?: string;\n raw?: unknown;\n}\n\n/**\n * Thrown for any non-2xx response from the Verixo API. `status` is always\n * present; `code`/`detail` are populated when the gateway returns a JSON\n * error body (most do) -- a small number of paths (e.g. an invalid API key\n * rejected before reaching a controller) return an empty body, in which case\n * only `status` and a generic `message` are available.\n */\nexport class VerixoError extends Error {\n readonly status: number;\n readonly code?: string;\n readonly detail?: string;\n readonly raw?: unknown;\n\n constructor(message: string, options: VerixoErrorOptions) {\n super(message);\n this.name = \"VerixoError\";\n this.status = options.status;\n this.code = options.code;\n this.detail = options.detail;\n this.raw = options.raw;\n }\n}\n\nconst STATUS_FALLBACKS: Record<number, string> = {\n 401: \"Missing or invalid API key.\",\n 402: \"Insufficient wallet balance.\",\n 403: \"You don't have access to this resource.\",\n 404: \"Not found.\",\n 408: \"Request timed out.\",\n 422: \"The request did not meet a required condition (e.g. minScore).\",\n 429: \"Rate limit exceeded.\",\n};\n\nexport async function errorFromResponse(res: Response): Promise<VerixoError> {\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n body = undefined;\n }\n\n const obj = (body && typeof body === \"object\" ? body : {}) as Record<string, unknown>;\n const detail = (obj.detail ?? obj.error ?? obj.message) as string | undefined;\n const code = (obj.grpcCode ?? obj.code) as string | undefined;\n const message = detail ?? STATUS_FALLBACKS[res.status] ?? `Request failed with status ${res.status}`;\n\n return new VerixoError(message, { status: res.status, code, detail, raw: body });\n}\n","import { errorFromResponse } from \"./errors.js\";\nimport type { VerixoOptions } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.verixo.com\";\n\nexport class HttpClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n\n constructor(apiKey: string, options: VerixoOptions = {}) {\n if (!apiKey) {\n throw new Error(\"Verixo: an API key (or JWT) is required, e.g. new Verixo('vc_test_...')\");\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n }\n\n async request<T>(path: string, init: RequestInit = {}): Promise<T> {\n // The gateway classifies the credential by sniffing the raw Authorization header for a\n // vc_test_/vc_live_ prefix -- a \"Bearer \" prefix on an API key would make it misclassify\n // the request as a (malformed) JWT and reject it, so API keys go out unprefixed.\n const isApiKey = this.apiKey.startsWith(\"vc_test_\") || this.apiKey.startsWith(\"vc_live_\");\n const authorization = isApiKey ? this.apiKey : `Bearer ${this.apiKey}`;\n\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: {\n Authorization: authorization,\n ...(init.body ? { \"Content-Type\": \"application/json\" } : {}),\n ...init.headers,\n },\n });\n\n if (!res.ok) {\n throw await errorFromResponse(res);\n }\n\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n get<T>(path: string): Promise<T> {\n return this.request<T>(path, { method: \"GET\" });\n }\n\n post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>(path, { method: \"POST\", body: body ? JSON.stringify(body) : undefined });\n }\n\n /** ws:// or wss:// origin derived from baseUrl, for the OTP push subscription. */\n get wsOrigin(): string {\n return this.baseUrl.replace(/^http/, \"ws\");\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { DeliveryRate } from \"../types.js\";\n\nexport class Analytics {\n constructor(private readonly http: HttpClient) {}\n\n /** Public delivery-rate stats by country for a service. No auth required by the API itself. */\n rates(serviceSlug = \"whatsapp\"): Promise<{ rates: DeliveryRate[]; updatedAt: string }> {\n return this.http.get(`/api/v1/analytics/rates?serviceSlug=${encodeURIComponent(serviceSlug)}`);\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { PurchaseNumberParams, PurchaseResult, SearchNumbersParams, SearchNumbersResult } from \"../types.js\";\n\nexport class Numbers {\n constructor(private readonly http: HttpClient) {}\n\n /** Browse available numbers ranked by Health Score (TM). No purchase is made. */\n search(params: SearchNumbersParams): Promise<SearchNumbersResult> {\n const q = new URLSearchParams({ serviceSlug: params.serviceSlug });\n if (params.countryCode) q.set(\"countryCode\", params.countryCode);\n if (params.minScore != null) q.set(\"minScore\", String(params.minScore));\n if (params.limit != null) q.set(\"limit\", String(params.limit));\n return this.http.get<SearchNumbersResult>(`/api/v1/numbers/search?${q}`);\n }\n\n /**\n * Buy a number for a service + country. The server picks the best available\n * candidate by Health Score automatically -- there's no numberId parameter,\n * since by the time you'd pass one back the number may already be gone.\n */\n purchase(params: PurchaseNumberParams): Promise<PurchaseResult> {\n return this.http.post<PurchaseResult>(\"/api/v1/numbers/purchase\", params);\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { SessionStatus } from \"../types.js\";\n\nexport class Sessions {\n constructor(private readonly http: HttpClient) {}\n\n /** Poll a session's current delivery status. */\n get(sessionToken: string): Promise<SessionStatus> {\n return this.http.get<SessionStatus>(`/api/v1/numbers/session/${encodeURIComponent(sessionToken)}`);\n }\n\n /**\n * Poll until the session reaches a terminal state (DELIVERED, REFUNDED, EXPIRED, FAILED)\n * or the timeout elapses. Prefer `vc.subscribe()` for real-time push -- this is a fallback\n * for environments where a persistent WebSocket isn't practical (e.g. serverless functions).\n */\n async waitForOtp(sessionToken: string, timeoutMs = 90_000, intervalMs = 2_000): Promise<SessionStatus> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const session = await this.get(sessionToken);\n if (session.status !== \"PENDING\") return session;\n if (Date.now() >= deadline) return session;\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n }\n}\n","import { Client } from \"@stomp/stompjs\";\nimport type { HttpClient } from \"./client.js\";\nimport type { OtpPush } from \"./types.js\";\n\n/**\n * Subscribes to real-time OTP push for a session over the gateway's WebSocket\n * proxy (wss://.../ws -> delivery-service's STOMP broker, topic\n * /topic/otp/{sessionToken}). Requires a global WebSocket implementation --\n * present natively in browsers and Node 22+; on older Node, pass a `ws`\n * instance via globalThis.WebSocket before calling subscribe().\n *\n * Returns an unsubscribe function. The callback fires at most once per\n * session (a session only ever delivers one OTP).\n */\nexport function subscribe(http: HttpClient, sessionToken: string, onOtp: (push: OtpPush) => void): () => void {\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n \"Verixo.subscribe() requires a global WebSocket implementation. \" +\n \"This is built into browsers and Node 22+; on older Node, polyfill \" +\n \"globalThis.WebSocket (e.g. with the 'ws' package) before calling subscribe().\"\n );\n }\n\n const subscribedAt = Date.now();\n const client = new Client({\n brokerURL: `${http.wsOrigin}/ws`,\n reconnectDelay: 5_000,\n onConnect: () => {\n client.subscribe(`/topic/otp/${sessionToken}`, (message) => {\n try {\n const payload = JSON.parse(message.body) as Omit<OtpPush, \"latencyMs\">;\n onOtp({ ...payload, latencyMs: Date.now() - subscribedAt });\n } catch {\n // Ignore malformed frames rather than crashing the caller's process.\n }\n });\n },\n });\n\n client.activate();\n return () => {\n void client.deactivate();\n };\n}\n","import { HttpClient } from \"./client.js\";\nimport { Analytics } from \"./resources/analytics.js\";\nimport { Numbers } from \"./resources/numbers.js\";\nimport { Sessions } from \"./resources/sessions.js\";\nimport { subscribe } from \"./subscribe.js\";\nimport type { OtpPush, VerixoOptions } from \"./types.js\";\n\nexport { VerixoError } from \"./errors.js\";\nexport type {\n DeliveryRate,\n OtpPush,\n PurchaseNumberParams,\n PurchaseResult,\n SearchNumbersParams,\n SearchNumbersResult,\n SessionState,\n SessionStatus,\n VerixoOptions,\n VirtualNumber,\n} from \"./types.js\";\n\nexport default class Verixo {\n readonly numbers: Numbers;\n readonly sessions: Sessions;\n readonly analytics: Analytics;\n private readonly http: HttpClient;\n\n constructor(apiKey: string, options: VerixoOptions = {}) {\n this.http = new HttpClient(apiKey, options);\n this.numbers = new Numbers(this.http);\n this.sessions = new Sessions(this.http);\n this.analytics = new Analytics(this.http);\n }\n\n /**\n * Get pushed the OTP for a session in real time, instead of polling\n * `sessions.get()`. Returns an unsubscribe function.\n *\n * @example\n * const session = await vc.numbers.purchase({ serviceSlug: 'whatsapp', countryCode: 'NG' })\n * vc.subscribe(session.sessionToken, ({ otp, latencyMs }) => {\n * console.log(`OTP: ${otp} in ${latencyMs}ms`)\n * })\n */\n subscribe(sessionToken: string, onOtp: (push: OtpPush) => void): () => void {\n return subscribe(this.http, sessionToken, onOtp);\n }\n}\n"],"mappings":";AAcO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,SAA6B;AACxD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,QAAQ;AACtB,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,QAAQ;AACtB,SAAK,MAAM,QAAQ;AAAA,EACrB;AACF;AAEA,IAAM,mBAA2C;AAAA,EAC/C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEA,eAAsB,kBAAkB,KAAqC;AAC3E,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,MAAO,QAAQ,OAAO,SAAS,WAAW,OAAO,CAAC;AACxD,QAAM,SAAU,IAAI,UAAU,IAAI,SAAS,IAAI;AAC/C,QAAM,OAAQ,IAAI,YAAY,IAAI;AAClC,QAAM,UAAU,UAAU,iBAAiB,IAAI,MAAM,KAAK,8BAA8B,IAAI,MAAM;AAElG,SAAO,IAAI,YAAY,SAAS,EAAE,QAAQ,IAAI,QAAQ,MAAM,QAAQ,KAAK,KAAK,CAAC;AACjF;;;ACnDA,IAAM,mBAAmB;AAElB,IAAM,aAAN,MAAiB;AAAA,EACb;AAAA,EACA;AAAA,EAET,YAAY,QAAgB,UAAyB,CAAC,GAAG;AACvD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,yEAAyE;AAAA,IAC3F;AACA,SAAK,SAAS;AACd,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,OAAO,EAAE;AAAA,EACxE;AAAA,EAEA,MAAM,QAAW,MAAc,OAAoB,CAAC,GAAe;AAIjE,UAAM,WAAW,KAAK,OAAO,WAAW,UAAU,KAAK,KAAK,OAAO,WAAW,UAAU;AACxF,UAAM,gBAAgB,WAAW,KAAK,SAAS,UAAU,KAAK,MAAM;AAEpE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAChD,GAAG;AAAA,MACH,SAAS;AAAA,QACP,eAAe;AAAA,QACf,GAAI,KAAK,OAAO,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,QAC1D,GAAG,KAAK;AAAA,MACV;AAAA,IACF,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,kBAAkB,GAAG;AAAA,IACnC;AAEA,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAAA,EAEA,IAAO,MAA0B;AAC/B,WAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,EAChD;AAAA,EAEA,KAAQ,MAAc,MAA4B;AAChD,WAAO,KAAK,QAAW,MAAM,EAAE,QAAQ,QAAQ,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI,OAAU,CAAC;AAAA,EAChG;AAAA;AAAA,EAGA,IAAI,WAAmB;AACrB,WAAO,KAAK,QAAQ,QAAQ,SAAS,IAAI;AAAA,EAC3C;AACF;;;AClDO,IAAM,YAAN,MAAgB;AAAA,EACrB,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,MAAM,cAAc,YAAmE;AACrF,WAAO,KAAK,KAAK,IAAI,uCAAuC,mBAAmB,WAAW,CAAC,EAAE;AAAA,EAC/F;AACF;;;ACPO,IAAM,UAAN,MAAc;AAAA,EACnB,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,OAAO,QAA2D;AAChE,UAAM,IAAI,IAAI,gBAAgB,EAAE,aAAa,OAAO,YAAY,CAAC;AACjE,QAAI,OAAO,YAAa,GAAE,IAAI,eAAe,OAAO,WAAW;AAC/D,QAAI,OAAO,YAAY,KAAM,GAAE,IAAI,YAAY,OAAO,OAAO,QAAQ,CAAC;AACtE,QAAI,OAAO,SAAS,KAAM,GAAE,IAAI,SAAS,OAAO,OAAO,KAAK,CAAC;AAC7D,WAAO,KAAK,KAAK,IAAyB,0BAA0B,CAAC,EAAE;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,QAAuD;AAC9D,WAAO,KAAK,KAAK,KAAqB,4BAA4B,MAAM;AAAA,EAC1E;AACF;;;ACpBO,IAAM,WAAN,MAAe;AAAA,EACpB,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,IAAI,cAA8C;AAChD,WAAO,KAAK,KAAK,IAAmB,2BAA2B,mBAAmB,YAAY,CAAC,EAAE;AAAA,EACnG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,cAAsB,YAAY,KAAQ,aAAa,KAA+B;AACrG,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,eAAS;AACP,YAAM,UAAU,MAAM,KAAK,IAAI,YAAY;AAC3C,UAAI,QAAQ,WAAW,UAAW,QAAO;AACzC,UAAI,KAAK,IAAI,KAAK,SAAU,QAAO;AACnC,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAAA,IAChE;AAAA,EACF;AACF;;;ACzBA,SAAS,cAAc;AAchB,SAAS,UAAU,MAAkB,cAAsB,OAA4C;AAC5G,MAAI,OAAO,cAAc,aAAa;AACpC,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AAEA,QAAM,eAAe,KAAK,IAAI;AAC9B,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,WAAW,GAAG,KAAK,QAAQ;AAAA,IAC3B,gBAAgB;AAAA,IAChB,WAAW,MAAM;AACf,aAAO,UAAU,cAAc,YAAY,IAAI,CAAC,YAAY;AAC1D,YAAI;AACF,gBAAM,UAAU,KAAK,MAAM,QAAQ,IAAI;AACvC,gBAAM,EAAE,GAAG,SAAS,WAAW,KAAK,IAAI,IAAI,aAAa,CAAC;AAAA,QAC5D,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO,SAAS;AAChB,SAAO,MAAM;AACX,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;;;ACtBA,IAAqB,SAArB,MAA4B;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACQ;AAAA,EAEjB,YAAY,QAAgB,UAAyB,CAAC,GAAG;AACvD,SAAK,OAAO,IAAI,WAAW,QAAQ,OAAO;AAC1C,SAAK,UAAU,IAAI,QAAQ,KAAK,IAAI;AACpC,SAAK,WAAW,IAAI,SAAS,KAAK,IAAI;AACtC,SAAK,YAAY,IAAI,UAAU,KAAK,IAAI;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,UAAU,cAAsB,OAA4C;AAC1E,WAAO,UAAU,KAAK,MAAM,cAAc,KAAK;AAAA,EACjD;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@verixo/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official Node.js SDK for the Verixo OTP-as-a-Service API",
5
+ "license": "MIT",
6
+ "homepage": "https://verifiedcore.com",
7
+ "bugs": {
8
+ "url": "https://github.com/titicodes/verixo/issues"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.cts",
22
+ "default": "./dist/index.cjs"
23
+ }
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup",
34
+ "dev": "tsup --watch",
35
+ "typecheck": "tsc --noEmit"
36
+ },
37
+ "dependencies": {
38
+ "@stomp/stompjs": "^7.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "tsup": "^8.3.5",
42
+ "typescript": "^5.7.2"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/titicodes/verixo.git",
47
+ "directory": "sdk/node"
48
+ },
49
+ "keywords": [
50
+ "otp",
51
+ "sms-verification",
52
+ "verixo",
53
+ "phone-verification"
54
+ ]
55
+ }