whisp-js 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +739 -2
- package/dist/cjs/client.d.ts +213 -0
- package/dist/cjs/client.js +570 -0
- package/dist/cjs/constants.d.ts +43 -0
- package/dist/cjs/constants.js +60 -0
- package/dist/cjs/errors.d.ts +96 -0
- package/dist/cjs/errors.js +115 -0
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/listener.d.ts +93 -0
- package/dist/cjs/listener.js +457 -0
- package/dist/cjs/media.d.ts +40 -0
- package/dist/cjs/media.js +181 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/refs.d.ts +4 -0
- package/dist/cjs/refs.js +38 -0
- package/dist/cjs/runtime.d.ts +24 -0
- package/dist/cjs/runtime.js +93 -0
- package/dist/cjs/sse.d.ts +32 -0
- package/dist/cjs/sse.js +105 -0
- package/dist/cjs/transport.d.ts +61 -0
- package/dist/cjs/transport.js +181 -0
- package/dist/cjs/types.d.ts +583 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/version.d.ts +2 -0
- package/dist/cjs/version.js +5 -0
- package/dist/cjs/webhook.d.ts +27 -0
- package/dist/cjs/webhook.js +128 -0
- package/dist/esm/client.d.ts +213 -0
- package/dist/esm/client.js +566 -0
- package/dist/esm/constants.d.ts +43 -0
- package/dist/esm/constants.js +57 -0
- package/dist/esm/errors.d.ts +96 -0
- package/dist/esm/errors.js +107 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/listener.d.ts +93 -0
- package/dist/esm/listener.js +452 -0
- package/dist/esm/media.d.ts +40 -0
- package/dist/esm/media.js +175 -0
- package/dist/esm/refs.d.ts +4 -0
- package/dist/esm/refs.js +33 -0
- package/dist/esm/runtime.d.ts +24 -0
- package/dist/esm/runtime.js +84 -0
- package/dist/esm/sse.d.ts +32 -0
- package/dist/esm/sse.js +100 -0
- package/dist/esm/transport.d.ts +61 -0
- package/dist/esm/transport.js +172 -0
- package/dist/esm/types.d.ts +583 -0
- package/dist/esm/types.js +13 -0
- package/dist/esm/version.d.ts +2 -0
- package/dist/esm/version.js +2 -0
- package/dist/esm/webhook.d.ts +27 -0
- package/dist/esm/webhook.js +123 -0
- package/package.json +37 -5
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PUBLIC_EVENT_TYPES = exports.UPLOAD_MAX_BYTES = exports.DAILY_CAP_MAX = exports.DAILY_CAP_MIN = exports.SEND_STRATEGIES = exports.WAIT_DEFAULT_MS = exports.FROM_MAX = exports.NUMBER_ID_PATTERN = exports.WEBHOOK_SECRET_PATTERN = exports.TOKEN_PATTERN = exports.SIGNATURE_TOLERANCE_SECONDS = exports.EVENT_CURSOR_PREFIX = exports.WEBHOOK_SECRET_PREFIX = exports.TOKEN_PREFIX = exports.SIGNATURE_HEADER = exports.USER_AGENT_PREFIX = exports.DEFAULT_BASE_URL = exports.API_URL_ENV = exports.API_TOKEN_ENV = exports.SDK_PACKAGE_NAME = void 0;
|
|
4
|
+
/** The npm name (16.2 decision 1: `@whisphq/sdk` if the `@whisp` scope isn't the owner's). */
|
|
5
|
+
exports.SDK_PACKAGE_NAME = 'whisp-js';
|
|
6
|
+
/** Where the token is read from when none is passed, on the first call. */
|
|
7
|
+
exports.API_TOKEN_ENV = 'WHISP_API_TOKEN';
|
|
8
|
+
/** Where a different base URL is read from (agent stacks, local tests). */
|
|
9
|
+
exports.API_URL_ENV = 'WHISP_API_URL';
|
|
10
|
+
exports.DEFAULT_BASE_URL = 'https://api.whisp.lue.my';
|
|
11
|
+
/** `whisp-sdk/<version> (<runtime>/<version>)`. */
|
|
12
|
+
exports.USER_AGENT_PREFIX = 'whisp-sdk';
|
|
13
|
+
/** The webhook signature header, lowercase as HTTP header names compare. */
|
|
14
|
+
exports.SIGNATURE_HEADER = 'x-whisp-signature';
|
|
15
|
+
/** Every project token starts with it. */
|
|
16
|
+
exports.TOKEN_PREFIX = 'wsp_live_';
|
|
17
|
+
/** Every webhook signing secret starts with it. */
|
|
18
|
+
exports.WEBHOOK_SECRET_PREFIX = 'wsp_whs_';
|
|
19
|
+
/** An event cursor (the SSE `id:`) starts with it; the rest is opaque. */
|
|
20
|
+
exports.EVENT_CURSOR_PREFIX = 'c_';
|
|
21
|
+
/** How old a webhook signature's timestamp may be, by default (seconds). */
|
|
22
|
+
exports.SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
23
|
+
/** A project token's whole shape: the prefix, 32 base62 and a 6-character checksum (core's `TOKEN_PATTERN`). */
|
|
24
|
+
exports.TOKEN_PATTERN = /^wsp_live_[0-9A-Za-z]{38}$/;
|
|
25
|
+
/** A webhook signing secret's whole shape (core's `WEBHOOK_SECRET_PATTERN`). */
|
|
26
|
+
exports.WEBHOOK_SECRET_PATTERN = /^wsp_whs_[0-9A-Za-z]{38}$/;
|
|
27
|
+
/** A number id: `num_` and 8 characters of core's base58 id alphabet (no 0, O, I or l). */
|
|
28
|
+
exports.NUMBER_ID_PATTERN = /^num_[1-9A-HJ-NP-Za-km-z]{8}$/;
|
|
29
|
+
/** At most this many numbers in one pool (`from`, PUBLIC_LIMITS.fromMax). */
|
|
30
|
+
exports.FROM_MAX = 20;
|
|
31
|
+
/** `wait: true` waits this long on the server (PUBLIC_LIMITS.waitDefaultMs). */
|
|
32
|
+
exports.WAIT_DEFAULT_MS = 15_000;
|
|
33
|
+
/** How a pool picks its number (core SEND_STRATEGIES); the first, `healthiest`, is the server's default. */
|
|
34
|
+
exports.SEND_STRATEGIES = ['healthiest', 'random', 'round-robin'];
|
|
35
|
+
/** A send's own `dailyCap`: a whole number in this range (core SEND_DAILY_CAP). */
|
|
36
|
+
exports.DAILY_CAP_MIN = 1;
|
|
37
|
+
exports.DAILY_CAP_MAX = 10_000;
|
|
38
|
+
/** The upload cap of each kind (core's `uploadMaxBytes`); a stream is never read past it. */
|
|
39
|
+
exports.UPLOAD_MAX_BYTES = {
|
|
40
|
+
image: 16 * 1024 * 1024,
|
|
41
|
+
video: 64 * 1024 * 1024,
|
|
42
|
+
voice: 16 * 1024 * 1024,
|
|
43
|
+
document: 100 * 1024 * 1024,
|
|
44
|
+
};
|
|
45
|
+
/** PLAN's twelve, then `message.edited` (0.3.0), in the order the API lists them. */
|
|
46
|
+
exports.PUBLIC_EVENT_TYPES = [
|
|
47
|
+
'message.received',
|
|
48
|
+
'message.sent',
|
|
49
|
+
'message.status',
|
|
50
|
+
'message.reaction',
|
|
51
|
+
'message.deleted',
|
|
52
|
+
'chat.updated',
|
|
53
|
+
'presence.updated',
|
|
54
|
+
'group.updated',
|
|
55
|
+
'group.participants',
|
|
56
|
+
'number.status',
|
|
57
|
+
'number.risk',
|
|
58
|
+
'call.received',
|
|
59
|
+
'message.edited',
|
|
60
|
+
];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK's errors (phase2-design 12.3, 12.5). Every one is a `WhispError`,
|
|
3
|
+
* so one `instanceof` check catches them all and `code` says what happened:
|
|
4
|
+
*
|
|
5
|
+
* - `WhispError`: the API answered with an error (`status` is the HTTP
|
|
6
|
+
* status, `code` the API's stable code), or a local check failed before
|
|
7
|
+
* anything was sent (`status` is null).
|
|
8
|
+
* - `WhispConnectionError`: no HTTP answer after every retry (network error
|
|
9
|
+
* or timeout).
|
|
10
|
+
* - `WhispSignatureError`: `verifyWebhook` refused a request.
|
|
11
|
+
*
|
|
12
|
+
* Messages are English and safe to log: they never contain the token or a
|
|
13
|
+
* webhook secret.
|
|
14
|
+
*/
|
|
15
|
+
/** Codes the SDK makes itself. Everything else comes from the API as it sent it. */
|
|
16
|
+
export declare const SDK_ERROR_CODES: {
|
|
17
|
+
/** No `token` was passed and WHISP_API_TOKEN is not set. Nothing was sent. */
|
|
18
|
+
readonly TOKEN_MISSING: 'TOKEN_MISSING';
|
|
19
|
+
/** The token isn't shaped like a project token (`wsp_live_…`). Nothing was sent. The API uses the same code. */
|
|
20
|
+
readonly TOKEN_INVALID: 'TOKEN_INVALID';
|
|
21
|
+
/** The SDK was created in a browser without `dangerouslyAllowBrowser`. The API uses the same code. */
|
|
22
|
+
readonly TOKEN_IN_BROWSER: 'TOKEN_IN_BROWSER';
|
|
23
|
+
/** A `phone` (or `number(...)`) that is neither a phone in international form nor a `num_` id. */
|
|
24
|
+
readonly PHONE_INVALID: 'PHONE_INVALID';
|
|
25
|
+
/** A read that needs one number (`chats`, `chat`, `chatMessages`, `check`) on a pool or whole-project instance. */
|
|
26
|
+
readonly NUMBER_REQUIRED: 'NUMBER_REQUIRED';
|
|
27
|
+
/** An option or argument the SDK can tell is wrong without asking the API (`baseUrl`, `timeoutMs`, ...). */
|
|
28
|
+
readonly OPTIONS_INVALID: 'OPTIONS_INVALID';
|
|
29
|
+
/** A file to send that can't be uploaded as given (no bytes, no type, too big). Nothing was sent. */
|
|
30
|
+
readonly MEDIA_INVALID: 'MEDIA_INVALID';
|
|
31
|
+
/** The presigned upload (the PUT to storage) failed. */
|
|
32
|
+
readonly UPLOAD_FAILED: 'UPLOAD_FAILED';
|
|
33
|
+
/** The signed file link answered an error (`downloadMedia`). */
|
|
34
|
+
readonly MEDIA_DOWNLOAD_FAILED: 'MEDIA_DOWNLOAD_FAILED';
|
|
35
|
+
/** A 2xx whose body wasn't what the API documents, or a redirect the SDK refused to follow. */
|
|
36
|
+
readonly INVALID_RESPONSE: 'INVALID_RESPONSE';
|
|
37
|
+
/** No HTTP answer: DNS, connection refused or reset, TLS. */
|
|
38
|
+
readonly NETWORK_ERROR: 'NETWORK_ERROR';
|
|
39
|
+
/** An attempt ran past `timeoutMs`, or the event stream sent nothing for 45 s. */
|
|
40
|
+
readonly TIMEOUT: 'TIMEOUT';
|
|
41
|
+
/** `verifyWebhook` got no secret (an unset env variable, usually). */
|
|
42
|
+
readonly SECRET_MISSING: 'SECRET_MISSING';
|
|
43
|
+
/** `verifyWebhook` got a secret that isn't a webhook signing secret (`wsp_whs_…`). */
|
|
44
|
+
readonly SECRET_INVALID: 'SECRET_INVALID';
|
|
45
|
+
/** `verifyWebhook` refused the request; `reason` says why. */
|
|
46
|
+
readonly SIGNATURE_INVALID: 'SIGNATURE_INVALID';
|
|
47
|
+
/** This runtime has no Web Crypto (`crypto.subtle`), which `verifyWebhook` needs. */
|
|
48
|
+
readonly CRYPTO_UNAVAILABLE: 'CRYPTO_UNAVAILABLE';
|
|
49
|
+
};
|
|
50
|
+
export type SdkErrorCode = (typeof SDK_ERROR_CODES)[keyof typeof SDK_ERROR_CODES];
|
|
51
|
+
export type ErrorParams = Record<string, string | number>;
|
|
52
|
+
export interface WhispErrorInit {
|
|
53
|
+
code: string;
|
|
54
|
+
message: string;
|
|
55
|
+
/** The HTTP status when the API answered; null for local checks and connection failures. */
|
|
56
|
+
status?: number | null;
|
|
57
|
+
params?: ErrorParams | null;
|
|
58
|
+
/** The API's `X-Request-Id`, for support. */
|
|
59
|
+
requestId?: string | null;
|
|
60
|
+
/** Seconds from `Retry-After`, when the API sent one. */
|
|
61
|
+
retryAfter?: number | null;
|
|
62
|
+
cause?: unknown;
|
|
63
|
+
}
|
|
64
|
+
export declare class WhispError extends Error {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
/** Stable: `NUMBER_NOT_IN_PROJECT`, `RATE_LIMITED`, `TOKEN_MISSING`, ... */
|
|
67
|
+
readonly code: string;
|
|
68
|
+
/** The HTTP status when the API answered; null when nothing was answered. */
|
|
69
|
+
readonly status: number | null;
|
|
70
|
+
/** The values the code names (`number`, `total`, `reason`, ...), or null. */
|
|
71
|
+
readonly params: ErrorParams | null;
|
|
72
|
+
readonly requestId: string | null;
|
|
73
|
+
/** Seconds the API asked to wait (`Retry-After`), or null. */
|
|
74
|
+
readonly retryAfter: number | null;
|
|
75
|
+
constructor(init: WhispErrorInit);
|
|
76
|
+
}
|
|
77
|
+
/** No HTTP answer after every retry. `cause` is the runtime's own error. */
|
|
78
|
+
export declare class WhispConnectionError extends WhispError {
|
|
79
|
+
readonly name: string;
|
|
80
|
+
constructor(code: 'NETWORK_ERROR' | 'TIMEOUT', message: string, cause?: unknown);
|
|
81
|
+
}
|
|
82
|
+
/** Why `verifyWebhook` refused a request (12.5). */
|
|
83
|
+
export type SignatureFailureReason = 'missing' | 'malformed' | 'timestamp' | 'mismatch';
|
|
84
|
+
export declare class WhispSignatureError extends WhispError {
|
|
85
|
+
readonly name: string;
|
|
86
|
+
readonly reason: SignatureFailureReason;
|
|
87
|
+
constructor(reason: SignatureFailureReason, message?: string);
|
|
88
|
+
}
|
|
89
|
+
/** `{ code, message, params? }` out of an API body (5.12), or null when it isn't one. */
|
|
90
|
+
export declare const readApiError: (body: unknown) => {
|
|
91
|
+
code: string;
|
|
92
|
+
message: string;
|
|
93
|
+
params: ErrorParams | null;
|
|
94
|
+
} | null;
|
|
95
|
+
/** A local check failed; nothing was sent. */
|
|
96
|
+
export declare const localError: (code: SdkErrorCode, message: string, params?: ErrorParams) => WhispError;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The SDK's errors (phase2-design 12.3, 12.5). Every one is a `WhispError`,
|
|
4
|
+
* so one `instanceof` check catches them all and `code` says what happened:
|
|
5
|
+
*
|
|
6
|
+
* - `WhispError`: the API answered with an error (`status` is the HTTP
|
|
7
|
+
* status, `code` the API's stable code), or a local check failed before
|
|
8
|
+
* anything was sent (`status` is null).
|
|
9
|
+
* - `WhispConnectionError`: no HTTP answer after every retry (network error
|
|
10
|
+
* or timeout).
|
|
11
|
+
* - `WhispSignatureError`: `verifyWebhook` refused a request.
|
|
12
|
+
*
|
|
13
|
+
* Messages are English and safe to log: they never contain the token or a
|
|
14
|
+
* webhook secret.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.localError = exports.readApiError = exports.WhispSignatureError = exports.WhispConnectionError = exports.WhispError = exports.SDK_ERROR_CODES = void 0;
|
|
18
|
+
/** Codes the SDK makes itself. Everything else comes from the API as it sent it. */
|
|
19
|
+
exports.SDK_ERROR_CODES = {
|
|
20
|
+
/** No `token` was passed and WHISP_API_TOKEN is not set. Nothing was sent. */
|
|
21
|
+
TOKEN_MISSING: 'TOKEN_MISSING',
|
|
22
|
+
/** The token isn't shaped like a project token (`wsp_live_…`). Nothing was sent. The API uses the same code. */
|
|
23
|
+
TOKEN_INVALID: 'TOKEN_INVALID',
|
|
24
|
+
/** The SDK was created in a browser without `dangerouslyAllowBrowser`. The API uses the same code. */
|
|
25
|
+
TOKEN_IN_BROWSER: 'TOKEN_IN_BROWSER',
|
|
26
|
+
/** A `phone` (or `number(...)`) that is neither a phone in international form nor a `num_` id. */
|
|
27
|
+
PHONE_INVALID: 'PHONE_INVALID',
|
|
28
|
+
/** A read that needs one number (`chats`, `chat`, `chatMessages`, `check`) on a pool or whole-project instance. */
|
|
29
|
+
NUMBER_REQUIRED: 'NUMBER_REQUIRED',
|
|
30
|
+
/** An option or argument the SDK can tell is wrong without asking the API (`baseUrl`, `timeoutMs`, ...). */
|
|
31
|
+
OPTIONS_INVALID: 'OPTIONS_INVALID',
|
|
32
|
+
/** A file to send that can't be uploaded as given (no bytes, no type, too big). Nothing was sent. */
|
|
33
|
+
MEDIA_INVALID: 'MEDIA_INVALID',
|
|
34
|
+
/** The presigned upload (the PUT to storage) failed. */
|
|
35
|
+
UPLOAD_FAILED: 'UPLOAD_FAILED',
|
|
36
|
+
/** The signed file link answered an error (`downloadMedia`). */
|
|
37
|
+
MEDIA_DOWNLOAD_FAILED: 'MEDIA_DOWNLOAD_FAILED',
|
|
38
|
+
/** A 2xx whose body wasn't what the API documents, or a redirect the SDK refused to follow. */
|
|
39
|
+
INVALID_RESPONSE: 'INVALID_RESPONSE',
|
|
40
|
+
/** No HTTP answer: DNS, connection refused or reset, TLS. */
|
|
41
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
42
|
+
/** An attempt ran past `timeoutMs`, or the event stream sent nothing for 45 s. */
|
|
43
|
+
TIMEOUT: 'TIMEOUT',
|
|
44
|
+
/** `verifyWebhook` got no secret (an unset env variable, usually). */
|
|
45
|
+
SECRET_MISSING: 'SECRET_MISSING',
|
|
46
|
+
/** `verifyWebhook` got a secret that isn't a webhook signing secret (`wsp_whs_…`). */
|
|
47
|
+
SECRET_INVALID: 'SECRET_INVALID',
|
|
48
|
+
/** `verifyWebhook` refused the request; `reason` says why. */
|
|
49
|
+
SIGNATURE_INVALID: 'SIGNATURE_INVALID',
|
|
50
|
+
/** This runtime has no Web Crypto (`crypto.subtle`), which `verifyWebhook` needs. */
|
|
51
|
+
CRYPTO_UNAVAILABLE: 'CRYPTO_UNAVAILABLE',
|
|
52
|
+
};
|
|
53
|
+
class WhispError extends Error {
|
|
54
|
+
name = 'WhispError';
|
|
55
|
+
/** Stable: `NUMBER_NOT_IN_PROJECT`, `RATE_LIMITED`, `TOKEN_MISSING`, ... */
|
|
56
|
+
code;
|
|
57
|
+
/** The HTTP status when the API answered; null when nothing was answered. */
|
|
58
|
+
status;
|
|
59
|
+
/** The values the code names (`number`, `total`, `reason`, ...), or null. */
|
|
60
|
+
params;
|
|
61
|
+
requestId;
|
|
62
|
+
/** Seconds the API asked to wait (`Retry-After`), or null. */
|
|
63
|
+
retryAfter;
|
|
64
|
+
constructor(init) {
|
|
65
|
+
super(init.message, init.cause === undefined ? undefined : { cause: init.cause });
|
|
66
|
+
this.code = init.code;
|
|
67
|
+
this.status = init.status ?? null;
|
|
68
|
+
this.params = init.params ?? null;
|
|
69
|
+
this.requestId = init.requestId ?? null;
|
|
70
|
+
this.retryAfter = init.retryAfter ?? null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.WhispError = WhispError;
|
|
74
|
+
/** No HTTP answer after every retry. `cause` is the runtime's own error. */
|
|
75
|
+
class WhispConnectionError extends WhispError {
|
|
76
|
+
name = 'WhispConnectionError';
|
|
77
|
+
constructor(code, message, cause) {
|
|
78
|
+
super({ code, message, cause });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.WhispConnectionError = WhispConnectionError;
|
|
82
|
+
const SIGNATURE_MESSAGES = {
|
|
83
|
+
missing: 'The request has no X-Whisp-Signature header',
|
|
84
|
+
malformed: 'The X-Whisp-Signature header or the body is not in the expected format',
|
|
85
|
+
timestamp: "The signature's timestamp is outside the tolerance (an old or replayed request, or a wrong clock)",
|
|
86
|
+
mismatch: 'No signature matches the body with this secret (a changed body, another secret, or a parsed body)',
|
|
87
|
+
};
|
|
88
|
+
class WhispSignatureError extends WhispError {
|
|
89
|
+
name = 'WhispSignatureError';
|
|
90
|
+
reason;
|
|
91
|
+
constructor(reason, message = SIGNATURE_MESSAGES[reason]) {
|
|
92
|
+
super({ code: exports.SDK_ERROR_CODES.SIGNATURE_INVALID, message, params: { reason } });
|
|
93
|
+
this.reason = reason;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.WhispSignatureError = WhispSignatureError;
|
|
97
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
98
|
+
/** `{ code, message, params? }` out of an API body (5.12), or null when it isn't one. */
|
|
99
|
+
const readApiError = (body) => {
|
|
100
|
+
if (!isRecord(body) || typeof body.code !== 'string' || typeof body.message !== 'string')
|
|
101
|
+
return null;
|
|
102
|
+
let params = null;
|
|
103
|
+
if (isRecord(body.params)) {
|
|
104
|
+
params = {};
|
|
105
|
+
for (const [key, value] of Object.entries(body.params)) {
|
|
106
|
+
if (typeof value === 'string' || typeof value === 'number')
|
|
107
|
+
params[key] = value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { code: body.code, message: body.message, params };
|
|
111
|
+
};
|
|
112
|
+
exports.readApiError = readApiError;
|
|
113
|
+
/** A local check failed; nothing was sent. */
|
|
114
|
+
const localError = (code, message, params) => new WhispError({ code, message, params: params ?? null });
|
|
115
|
+
exports.localError = localError;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Whisp SDK (phase2-design 12): send WhatsApp messages from your
|
|
3
|
+
* project's numbers, read their chats, listen to their events, and check
|
|
4
|
+
* webhook signatures, from Node 22.12+ or Bun. Zero runtime dependencies:
|
|
5
|
+
* `fetch`, Web Streams and Web Crypto only.
|
|
6
|
+
*
|
|
7
|
+
* import { Whisp } from 'whisp-js'
|
|
8
|
+
* const whisp = new Whisp({ phone: '972501234567' }) // WHISP_API_TOKEN from the environment
|
|
9
|
+
* await whisp.send('972521112233', 'Hello from Whisp')
|
|
10
|
+
* whisp.on('message', msg => console.log(msg.from, msg.text))
|
|
11
|
+
*/
|
|
12
|
+
export { DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, type DownloadOptions, type EditOptions, type MessagesQuery, type Page, type PageResult, type SendFileOptions, type SendOptions, type SendVoiceOptions, type VerifyResult, Whisp, type WhispOptions, } from './client.js';
|
|
13
|
+
export { API_TOKEN_ENV, API_URL_ENV, DEFAULT_BASE_URL, EVENT_CURSOR_PREFIX, PUBLIC_EVENT_TYPES, SDK_PACKAGE_NAME, SIGNATURE_HEADER, SIGNATURE_TOLERANCE_SECONDS, TOKEN_PREFIX, USER_AGENT_PREFIX, WEBHOOK_SECRET_PREFIX, } from './constants.js';
|
|
14
|
+
export { type ErrorParams, SDK_ERROR_CODES, type SdkErrorCode, type SignatureFailureReason, WhispConnectionError, WhispError, WhispSignatureError, } from './errors.js';
|
|
15
|
+
export type { AnyListenerEvent, EventHandlers, EventMeta, ListenerEvent, ListenerName, StreamClose, StreamGap, } from './listener.js';
|
|
16
|
+
export type { MediaFile, MediaSource, MediaUrl } from './media.js';
|
|
17
|
+
export type * from './types.js';
|
|
18
|
+
export { SDK_VERSION } from './version.js';
|
|
19
|
+
export { type VerifyWebhookInput, verifyWebhook } from './webhook.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyWebhook = exports.SDK_VERSION = exports.WhispSignatureError = exports.WhispError = exports.WhispConnectionError = exports.SDK_ERROR_CODES = exports.WEBHOOK_SECRET_PREFIX = exports.USER_AGENT_PREFIX = exports.TOKEN_PREFIX = exports.SIGNATURE_TOLERANCE_SECONDS = exports.SIGNATURE_HEADER = exports.SDK_PACKAGE_NAME = exports.PUBLIC_EVENT_TYPES = exports.EVENT_CURSOR_PREFIX = exports.DEFAULT_BASE_URL = exports.API_URL_ENV = exports.API_TOKEN_ENV = exports.Whisp = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_MAX_RETRIES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The Whisp SDK (phase2-design 12): send WhatsApp messages from your
|
|
6
|
+
* project's numbers, read their chats, listen to their events, and check
|
|
7
|
+
* webhook signatures, from Node 22.12+ or Bun. Zero runtime dependencies:
|
|
8
|
+
* `fetch`, Web Streams and Web Crypto only.
|
|
9
|
+
*
|
|
10
|
+
* import { Whisp } from 'whisp-js'
|
|
11
|
+
* const whisp = new Whisp({ phone: '972501234567' }) // WHISP_API_TOKEN from the environment
|
|
12
|
+
* await whisp.send('972521112233', 'Hello from Whisp')
|
|
13
|
+
* whisp.on('message', msg => console.log(msg.from, msg.text))
|
|
14
|
+
*/
|
|
15
|
+
var client_ts_1 = require("./client.js");
|
|
16
|
+
Object.defineProperty(exports, "DEFAULT_MAX_RETRIES", { enumerable: true, get: function () { return client_ts_1.DEFAULT_MAX_RETRIES; } });
|
|
17
|
+
Object.defineProperty(exports, "DEFAULT_TIMEOUT_MS", { enumerable: true, get: function () { return client_ts_1.DEFAULT_TIMEOUT_MS; } });
|
|
18
|
+
Object.defineProperty(exports, "Whisp", { enumerable: true, get: function () { return client_ts_1.Whisp; } });
|
|
19
|
+
var constants_ts_1 = require("./constants.js");
|
|
20
|
+
Object.defineProperty(exports, "API_TOKEN_ENV", { enumerable: true, get: function () { return constants_ts_1.API_TOKEN_ENV; } });
|
|
21
|
+
Object.defineProperty(exports, "API_URL_ENV", { enumerable: true, get: function () { return constants_ts_1.API_URL_ENV; } });
|
|
22
|
+
Object.defineProperty(exports, "DEFAULT_BASE_URL", { enumerable: true, get: function () { return constants_ts_1.DEFAULT_BASE_URL; } });
|
|
23
|
+
Object.defineProperty(exports, "EVENT_CURSOR_PREFIX", { enumerable: true, get: function () { return constants_ts_1.EVENT_CURSOR_PREFIX; } });
|
|
24
|
+
Object.defineProperty(exports, "PUBLIC_EVENT_TYPES", { enumerable: true, get: function () { return constants_ts_1.PUBLIC_EVENT_TYPES; } });
|
|
25
|
+
Object.defineProperty(exports, "SDK_PACKAGE_NAME", { enumerable: true, get: function () { return constants_ts_1.SDK_PACKAGE_NAME; } });
|
|
26
|
+
Object.defineProperty(exports, "SIGNATURE_HEADER", { enumerable: true, get: function () { return constants_ts_1.SIGNATURE_HEADER; } });
|
|
27
|
+
Object.defineProperty(exports, "SIGNATURE_TOLERANCE_SECONDS", { enumerable: true, get: function () { return constants_ts_1.SIGNATURE_TOLERANCE_SECONDS; } });
|
|
28
|
+
Object.defineProperty(exports, "TOKEN_PREFIX", { enumerable: true, get: function () { return constants_ts_1.TOKEN_PREFIX; } });
|
|
29
|
+
Object.defineProperty(exports, "USER_AGENT_PREFIX", { enumerable: true, get: function () { return constants_ts_1.USER_AGENT_PREFIX; } });
|
|
30
|
+
Object.defineProperty(exports, "WEBHOOK_SECRET_PREFIX", { enumerable: true, get: function () { return constants_ts_1.WEBHOOK_SECRET_PREFIX; } });
|
|
31
|
+
var errors_ts_1 = require("./errors.js");
|
|
32
|
+
Object.defineProperty(exports, "SDK_ERROR_CODES", { enumerable: true, get: function () { return errors_ts_1.SDK_ERROR_CODES; } });
|
|
33
|
+
Object.defineProperty(exports, "WhispConnectionError", { enumerable: true, get: function () { return errors_ts_1.WhispConnectionError; } });
|
|
34
|
+
Object.defineProperty(exports, "WhispError", { enumerable: true, get: function () { return errors_ts_1.WhispError; } });
|
|
35
|
+
Object.defineProperty(exports, "WhispSignatureError", { enumerable: true, get: function () { return errors_ts_1.WhispSignatureError; } });
|
|
36
|
+
var version_ts_1 = require("./version.js");
|
|
37
|
+
Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return version_ts_1.SDK_VERSION; } });
|
|
38
|
+
var webhook_ts_1 = require("./webhook.js");
|
|
39
|
+
Object.defineProperty(exports, "verifyWebhook", { enumerable: true, get: function () { return webhook_ts_1.verifyWebhook; } });
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type Timers } from './runtime.js';
|
|
2
|
+
import { type FetchLike } from './transport.js';
|
|
3
|
+
import type { CursorRejection, EventData, PublicEvent, PublicEventType, StreamReady } from './types.js';
|
|
4
|
+
/** Wait before reconnect attempt N (0-based); the last value repeats. */
|
|
5
|
+
export declare const RECONNECT_DELAYS_MS: readonly [1000, 2000, 4000, 8000, 15000];
|
|
6
|
+
/** No byte for this long and the connection is dropped (the server pings every 20 s). */
|
|
7
|
+
export declare const WATCHDOG_MS = 45000;
|
|
8
|
+
/** The wait after a 429 (TOO_MANY_STREAMS or RATE_LIMITED), unless `Retry-After` asks for longer (up to a minute). */
|
|
9
|
+
export declare const RATE_LIMITED_WAIT_MS = 30000;
|
|
10
|
+
/** Where the event came from, for logging and dedupe: `id` is the dedupe key, `cursor` the resume point. */
|
|
11
|
+
export interface EventMeta<T extends PublicEventType = PublicEventType> {
|
|
12
|
+
/** The event id (`evt_…`): the same event can arrive twice across a restart, never with another id. */
|
|
13
|
+
id: string;
|
|
14
|
+
type: T;
|
|
15
|
+
/** When the change committed. */
|
|
16
|
+
createdAt: string;
|
|
17
|
+
/** Save it and pass it as `cursor` to resume after this event. */
|
|
18
|
+
cursor: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What a handler gets (12.4): the event's `data` with the envelope folded in,
|
|
22
|
+
* so `msg.from`, `msg.text` and `msg.number` read directly, and
|
|
23
|
+
* `e.number`, `e.status` for `number.status`. `number` is the Whisp number's
|
|
24
|
+
* phone, `numberId` its id.
|
|
25
|
+
*/
|
|
26
|
+
export type ListenerEvent<T extends PublicEventType> = Omit<EventData[T], 'number' | 'numberId' | 'event'> & {
|
|
27
|
+
number: string;
|
|
28
|
+
numberId: string;
|
|
29
|
+
event: EventMeta<T>;
|
|
30
|
+
};
|
|
31
|
+
/** Any of the public types; `event.event.type` says which. */
|
|
32
|
+
export type AnyListenerEvent = {
|
|
33
|
+
[T in PublicEventType]: ListenerEvent<T>;
|
|
34
|
+
}[PublicEventType];
|
|
35
|
+
/** `'gap'`: a resume was asked and the server couldn't honour it; reconcile with reads. */
|
|
36
|
+
export interface StreamGap {
|
|
37
|
+
reason: CursorRejection;
|
|
38
|
+
/** The cursor that was sent. */
|
|
39
|
+
cursor: string;
|
|
40
|
+
}
|
|
41
|
+
/** `'close'`: a connection ended. `reconnect` is false once the listener has stopped. */
|
|
42
|
+
export interface StreamClose {
|
|
43
|
+
reconnect: boolean;
|
|
44
|
+
/** Why it ended, when it wasn't `close()`. */
|
|
45
|
+
error?: Error;
|
|
46
|
+
}
|
|
47
|
+
export type Handler<A> = (arg: A) => unknown;
|
|
48
|
+
/** Every name `on()` takes and what its handler receives. */
|
|
49
|
+
export type EventHandlers = {
|
|
50
|
+
[T in PublicEventType]: Handler<ListenerEvent<T>>;
|
|
51
|
+
} & {
|
|
52
|
+
/** `message.received`. */
|
|
53
|
+
message: Handler<ListenerEvent<'message.received'>>;
|
|
54
|
+
/** Every one of the public types. */
|
|
55
|
+
'*': Handler<AnyListenerEvent>;
|
|
56
|
+
/** A connection reached `ready`. */
|
|
57
|
+
open: Handler<StreamReady>;
|
|
58
|
+
close: Handler<StreamClose>;
|
|
59
|
+
gap: Handler<StreamGap>;
|
|
60
|
+
/** A handler threw (with the event it was given), the server sent `error`, or the listener stopped for good. */
|
|
61
|
+
error: (error: Error, event?: AnyListenerEvent) => unknown;
|
|
62
|
+
};
|
|
63
|
+
export type ListenerName = keyof EventHandlers;
|
|
64
|
+
export interface StreamTarget {
|
|
65
|
+
baseUrl: string;
|
|
66
|
+
token: string;
|
|
67
|
+
fetch: FetchLike;
|
|
68
|
+
userAgent: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ListenerConfig {
|
|
71
|
+
/** The connection's target, resolved per connection; throws a WhispError (TOKEN_MISSING) to stop. */
|
|
72
|
+
resolve: () => StreamTarget;
|
|
73
|
+
/** The instance's numbers (`numbers=`), or null for the whole project. */
|
|
74
|
+
numbers: readonly string[] | null;
|
|
75
|
+
cursor: string | null;
|
|
76
|
+
/** How long to wait for the answer's headers. */
|
|
77
|
+
connectTimeoutMs: number;
|
|
78
|
+
timers: Timers;
|
|
79
|
+
/** Where an error goes when nobody listens to `'error'`. */
|
|
80
|
+
report: (error: Error) => void;
|
|
81
|
+
}
|
|
82
|
+
/** The handler's argument: `data` plus the envelope (12.4). */
|
|
83
|
+
export declare const toListenerEvent: (envelope: PublicEvent, cursor: string) => AnyListenerEvent;
|
|
84
|
+
export declare class Listener {
|
|
85
|
+
#private;
|
|
86
|
+
constructor(config: ListenerConfig);
|
|
87
|
+
get cursor(): string | null;
|
|
88
|
+
get running(): boolean;
|
|
89
|
+
add(name: string, handler: Handler<never>): void;
|
|
90
|
+
remove(name: string, handler: Handler<never>): void;
|
|
91
|
+
/** Ends the stream and its reconnects, and forgets every handler; a later `on()` starts again from the cursor. */
|
|
92
|
+
close(): void;
|
|
93
|
+
}
|