@isnap/sdk 0.1.0 → 1.1.0-next.49
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 +15 -0
- package/README.md +173 -167
- package/dist/client.d.ts +48 -50
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +258 -89
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +43 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +53 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +100 -0
- package/dist/errors.js.map +1 -0
- package/dist/generated/openapi.d.ts +10924 -0
- package/dist/idempotency.d.ts +2 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +4 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/index.d.ts +26 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -6
- package/dist/index.js.map +1 -1
- package/dist/internal/sleep.d.ts +10 -0
- package/dist/internal/sleep.d.ts.map +1 -0
- package/dist/internal/sleep.js +37 -0
- package/dist/internal/sleep.js.map +1 -0
- package/dist/pagination.d.ts +10 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +16 -0
- package/dist/pagination.js.map +1 -0
- package/dist/resources/attachments.d.ts +45 -0
- package/dist/resources/attachments.d.ts.map +1 -0
- package/dist/resources/attachments.js +120 -0
- package/dist/resources/attachments.js.map +1 -0
- package/dist/resources/base.d.ts +6 -0
- package/dist/resources/base.d.ts.map +1 -0
- package/dist/resources/base.js +7 -0
- package/dist/resources/base.js.map +1 -0
- package/dist/resources/byod.d.ts +15 -0
- package/dist/resources/byod.d.ts.map +1 -0
- package/dist/resources/byod.js +13 -0
- package/dist/resources/byod.js.map +1 -0
- package/dist/resources/chats.d.ts +29 -0
- package/dist/resources/chats.d.ts.map +1 -0
- package/dist/resources/chats.js +28 -0
- package/dist/resources/chats.js.map +1 -0
- package/dist/resources/lines.d.ts +64 -27
- package/dist/resources/lines.d.ts.map +1 -1
- package/dist/resources/lines.js +77 -37
- package/dist/resources/lines.js.map +1 -1
- package/dist/resources/lookup.d.ts +15 -0
- package/dist/resources/lookup.d.ts.map +1 -0
- package/dist/resources/lookup.js +13 -0
- package/dist/resources/lookup.js.map +1 -0
- package/dist/resources/messages.d.ts +36 -67
- package/dist/resources/messages.d.ts.map +1 -1
- package/dist/resources/messages.js +37 -94
- package/dist/resources/messages.js.map +1 -1
- package/dist/resources/trial.d.ts +28 -0
- package/dist/resources/trial.d.ts.map +1 -0
- package/dist/resources/trial.js +31 -0
- package/dist/resources/trial.js.map +1 -0
- package/dist/resources/webhooks.d.ts +49 -39
- package/dist/resources/webhooks.d.ts.map +1 -1
- package/dist/resources/webhooks.js +52 -39
- package/dist/resources/webhooks.js.map +1 -1
- package/dist/retry.d.ts +14 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +47 -0
- package/dist/retry.js.map +1 -0
- package/dist/types.d.ts +30 -196
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -28
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +20 -0
- package/dist/version.js.map +1 -0
- package/dist/webhooks/parse.d.ts +15 -0
- package/dist/webhooks/parse.d.ts.map +1 -0
- package/dist/webhooks/parse.js +47 -0
- package/dist/webhooks/parse.js.map +1 -0
- package/dist/webhooks/types.d.ts +93 -0
- package/dist/webhooks/types.d.ts.map +1 -0
- package/dist/webhooks/types.js +20 -0
- package/dist/webhooks/types.js.map +1 -0
- package/dist/webhooks/verify.d.ts +41 -0
- package/dist/webhooks/verify.d.ts.map +1 -0
- package/dist/webhooks/verify.js +110 -0
- package/dist/webhooks/verify.js.map +1 -0
- package/package.json +45 -30
- package/dist/paginate.d.ts +0 -33
- package/dist/paginate.d.ts.map +0 -1
- package/dist/paginate.js +0 -40
- package/dist/paginate.js.map +0 -1
- package/dist/webhook-events.d.ts +0 -61
- package/dist/webhook-events.d.ts.map +0 -1
- package/dist/webhook-events.js +0 -44
- package/dist/webhook-events.js.map +0 -1
- package/dist/webhook-verify.d.ts +0 -27
- package/dist/webhook-verify.d.ts.map +0 -1
- package/dist/webhook-verify.js +0 -42
- package/dist/webhook-verify.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@isnap/sdk` will be documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-1.0 versions may introduce breaking changes between 0.x → 0.(x+1) minor bumps.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Initial public surface targeting iSnap API contract `v1.0.0`.
|
|
9
|
+
- `ISnapClient` with retries (jittered exponential backoff on 429/5xx), per-attempt timeout, auto-`Idempotency-Key` for mutating verbs, header injection, abortable requests.
|
|
10
|
+
- Resource classes: `messages`, `chats`, `lines`, `lookup`, `attachments`, `webhooks`, `byod`, `trial`.
|
|
11
|
+
- `attachments.upload()` — transparent 3-step (POST register → presigned R2 PUT → poll until `ready`) with 5 GB cap and four typed failure modes.
|
|
12
|
+
- Cursor pagination as `AsyncIterable` with optional `AbortSignal` on `messages.list`, `webhooks.deliveries`, `webhooks.events`.
|
|
13
|
+
- Webhook helpers: `verifyWebhook` (HMAC-SHA256 + 5min replay window + multi-secret rotation), `parseWebhookEvent`, prefix-based type guards (`isMessageEvent`, `isReactionEvent`, `isLineEvent`, `isTypingEvent`, `isTrialEvent`).
|
|
14
|
+
- Error hierarchy mirroring contract §6: `APIError` + 13 subclasses, all carrying `status`, `code`, `traceId`, `requestId`. Connection failures surface as `APIConnectionError` with native `Error.cause`.
|
|
15
|
+
- Generated TypeScript types (`src/generated/openapi.d.ts`) compiled from the backend's frozen `openapi.json`.
|
package/README.md
CHANGED
|
@@ -1,236 +1,242 @@
|
|
|
1
1
|
# @isnap/sdk
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
[](https://www.npmjs.com/package/@isnap/sdk)
|
|
5
|
-
[](LICENSE)
|
|
3
|
+
Official TypeScript SDK for the [iSnap](https://isnap.ai) P2P telephony API — real iMessage / SMS / RCS lines from real handsets, exposed as a single typed client.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
> Pre-1.0. Targets contract `docs/api-contract.md` v1.0.0. Public surface is stable across the v0.x line; breaking changes will bump 0.x → 0.(x+1) until 1.0.0.
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## Install
|
|
10
8
|
|
|
11
9
|
```bash
|
|
10
|
+
bun add @isnap/sdk
|
|
11
|
+
# or
|
|
12
12
|
npm install @isnap/sdk
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Requires Node 20+ (or Bun 1.2+). The SDK uses native `fetch`, `node:crypto`, and ES2022 `Error.cause` — no polyfills.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
16
18
|
|
|
17
19
|
```ts
|
|
18
|
-
import {
|
|
20
|
+
import { ISnapClient } from '@isnap/sdk'
|
|
19
21
|
|
|
20
|
-
const client =
|
|
22
|
+
const client = new ISnapClient({ apiKey: process.env.ISNAP_API_KEY! })
|
|
21
23
|
|
|
22
|
-
const
|
|
23
|
-
line_id: '
|
|
24
|
+
const message = await client.messages.send({
|
|
25
|
+
line_id: 'line_01HX...',
|
|
24
26
|
to: '+15551234567',
|
|
25
|
-
body: 'Hello from iSnap
|
|
26
|
-
})
|
|
27
|
+
body: 'Hello from iSnap'
|
|
28
|
+
})
|
|
27
29
|
|
|
28
|
-
console.log(
|
|
30
|
+
console.log(message.id, message.status)
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
##
|
|
33
|
+
## Configuration
|
|
32
34
|
|
|
33
35
|
```ts
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
// Get a message by ID
|
|
45
|
-
const message = await client.messages.get('msg-abc123');
|
|
46
|
-
|
|
47
|
-
// List messages with filters
|
|
48
|
-
const { messages, total } = await client.messages.list({
|
|
49
|
-
status: 'delivered',
|
|
50
|
-
direction: 'outbound',
|
|
51
|
-
page: 1,
|
|
52
|
-
limit: 50,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// List conversations
|
|
56
|
-
const { conversations } = await client.messages.listConversations({
|
|
57
|
-
line_id: 'ln-1',
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Get a full conversation thread
|
|
61
|
-
const thread = await client.messages.getConversation('conv-abc123', {
|
|
62
|
-
page: 1,
|
|
63
|
-
limit: 25,
|
|
64
|
-
});
|
|
65
|
-
console.log(thread.conversation.contact_number);
|
|
66
|
-
console.log(thread.messages);
|
|
36
|
+
new ISnapClient({
|
|
37
|
+
apiKey: 'sk_live_...', // required
|
|
38
|
+
baseUrl: 'https://api.isnap.ai', // default
|
|
39
|
+
maxRetries: 3, // default 3 — retries on 429/5xx with jittered backoff
|
|
40
|
+
timeoutMs: 30_000, // per-attempt timeout
|
|
41
|
+
idempotencyKey: 'auto', // 'auto' generates UUIDs for POST/PATCH/PUT/DELETE; 'manual' = pass via opts
|
|
42
|
+
fetch: undefined, // override (e.g. an instrumented fetch)
|
|
43
|
+
onRetry: (info) => console.warn('retry', info)
|
|
44
|
+
})
|
|
67
45
|
```
|
|
68
46
|
|
|
69
|
-
|
|
47
|
+
Every resource method accepts an optional `RequestOptions` final argument: `{ signal, idempotencyKey, headers, query, maxRetries }`.
|
|
48
|
+
|
|
49
|
+
## Resources
|
|
50
|
+
|
|
51
|
+
| Resource | What it does |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `client.messages` | Send / get / cancel / list messages, add reactions |
|
|
54
|
+
| `client.chats` | Mark a chat read, send typing indicators, share a contact card |
|
|
55
|
+
| `client.lines` | List, configure, and inspect rented + BYOD lines, plus quota and queue snapshots |
|
|
56
|
+
| `client.lookup` | Test if a number is iMessage-capable before sending |
|
|
57
|
+
| `client.attachments` | Upload images / video / files (transparent 3-step flow) |
|
|
58
|
+
| `client.webhooks` | CRUD subscriptions, rotate secrets, list deliveries, replay events |
|
|
59
|
+
| `client.byod` | Pair a customer-owned Mac+iPhone or Android device |
|
|
60
|
+
| `client.trial` | Start, check, and convert trial sessions |
|
|
61
|
+
|
|
62
|
+
## Sending a message
|
|
70
63
|
|
|
71
64
|
```ts
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Get a line by ID
|
|
79
|
-
const line = await client.lines.get('ln-abc123');
|
|
80
|
-
console.log(line.phone_number, line.status);
|
|
65
|
+
const msg = await client.messages.send({
|
|
66
|
+
line_id: 'line_01HX...',
|
|
67
|
+
to: '+15551234567',
|
|
68
|
+
body: 'Order #1242 is on its way',
|
|
69
|
+
service: 'iMessage' // optional — falls back to SMS/RCS via the line's degradation chain
|
|
70
|
+
})
|
|
81
71
|
```
|
|
82
72
|
|
|
83
|
-
|
|
73
|
+
`messages.send` is **idempotent by default** — the SDK auto-generates an `Idempotency-Key` for every POST. Retrying the same call with the same key returns the original message without sending a duplicate.
|
|
84
74
|
|
|
85
|
-
|
|
75
|
+
## Listing with cursor pagination
|
|
76
|
+
|
|
77
|
+
`list()` returns an `AsyncIterable` that threads cursors automatically:
|
|
86
78
|
|
|
87
79
|
```ts
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
events: ['message.sent', 'message.delivered', 'message.received'],
|
|
92
|
-
});
|
|
93
|
-
// Save wh.secret for signature verification
|
|
94
|
-
|
|
95
|
-
// List webhooks
|
|
96
|
-
const { webhooks } = await client.webhooks.list();
|
|
97
|
-
|
|
98
|
-
// Delete a webhook
|
|
99
|
-
await client.webhooks.delete('wh-abc123');
|
|
80
|
+
for await (const msg of client.messages.list({ line_id: 'line_01HX...', limit: 100 })) {
|
|
81
|
+
console.log(msg.id, msg.created_at)
|
|
82
|
+
}
|
|
100
83
|
```
|
|
101
84
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Use `parseWebhook` to verify signatures and get typed events in one call:
|
|
85
|
+
To stop early, pass an `AbortSignal`. Aborting surfaces as a thrown `APIConnectionError` on the next page boundary, so wrap the loop:
|
|
105
86
|
|
|
106
87
|
```ts
|
|
107
|
-
import {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const { verified, event } = parseWebhook({
|
|
112
|
-
secret: process.env.WEBHOOK_SECRET!,
|
|
113
|
-
signature: req.headers['x-isnap-signature'] as string,
|
|
114
|
-
timestamp: req.headers['x-isnap-timestamp'] as string,
|
|
115
|
-
body: req.body,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
if (!verified) return res.status(401).send('Invalid signature');
|
|
119
|
-
|
|
120
|
-
switch (event.event) {
|
|
121
|
-
case 'message.sent':
|
|
122
|
-
console.log('Sent:', event.data.id);
|
|
123
|
-
break;
|
|
124
|
-
case 'message.delivered':
|
|
125
|
-
console.log('Delivered:', event.data.id);
|
|
126
|
-
break;
|
|
127
|
-
case 'message.received':
|
|
128
|
-
console.log('Inbound:', event.data.from_number, event.data.body);
|
|
129
|
-
break;
|
|
130
|
-
case 'message.read':
|
|
131
|
-
console.log('Read:', event.data.id);
|
|
132
|
-
break;
|
|
133
|
-
case 'message.failed':
|
|
134
|
-
console.log('Failed:', event.data.id, event.data.error_message);
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
88
|
+
import { APIConnectionError } from '@isnap/sdk'
|
|
89
|
+
|
|
90
|
+
const ac = new AbortController()
|
|
91
|
+
setTimeout(() => ac.abort(), 5_000)
|
|
137
92
|
|
|
138
|
-
|
|
139
|
-
})
|
|
93
|
+
try {
|
|
94
|
+
for await (const msg of client.messages.list({}, { signal: ac.signal })) {
|
|
95
|
+
// ...
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (!(err instanceof APIConnectionError)) throw err
|
|
99
|
+
// expected — signal aborted the iterator
|
|
100
|
+
}
|
|
140
101
|
```
|
|
141
102
|
|
|
142
|
-
|
|
103
|
+
## Uploading an attachment
|
|
104
|
+
|
|
105
|
+
`attachments.upload()` wraps the 3-step contract (register → presigned PUT → poll) into one call:
|
|
143
106
|
|
|
144
107
|
```ts
|
|
145
|
-
import {
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
108
|
+
import { readFile } from 'node:fs/promises'
|
|
109
|
+
|
|
110
|
+
const file = await readFile('./photo.jpg')
|
|
111
|
+
|
|
112
|
+
const attachment = await client.attachments.upload(file, {
|
|
113
|
+
filename: 'photo.jpg',
|
|
114
|
+
contentType: 'image/jpeg'
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
await client.messages.send({
|
|
118
|
+
line_id: 'line_01HX...',
|
|
119
|
+
to: '+15551234567',
|
|
120
|
+
body: 'Here you go',
|
|
121
|
+
attachment_ids: [attachment.id]
|
|
122
|
+
})
|
|
153
123
|
```
|
|
154
124
|
|
|
155
|
-
|
|
125
|
+
5 GB cap. Four documented failure modes — note that the R2 PUT and the server-side `failed` transition share the same `upload_failed` code:
|
|
126
|
+
|
|
127
|
+
| Failure | `code` |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `size_bytes` exceeds the 5 GB cap | `attachment_exceeds_storage_limit` |
|
|
130
|
+
| R2 presigned PUT returns non-2xx | `upload_failed` |
|
|
131
|
+
| Server transitions to `status: failed` | `upload_failed` |
|
|
132
|
+
| Polling exhausted before `status: ready` | `upload_timeout` |
|
|
156
133
|
|
|
157
|
-
|
|
134
|
+
## Verifying webhooks
|
|
158
135
|
|
|
159
136
|
```ts
|
|
160
|
-
import {
|
|
137
|
+
import { verifyWebhook, isMessageEvent } from '@isnap/sdk'
|
|
161
138
|
|
|
162
|
-
|
|
139
|
+
// Express / Hono / native handler — give us the RAW body bytes (string).
|
|
140
|
+
async function handle(rawBody: string, headers: Record<string, string>) {
|
|
141
|
+
const event = verifyWebhook(rawBody, headers, process.env.ISNAP_WEBHOOK_SECRET!)
|
|
163
142
|
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
(p) => client.messages.list(p),
|
|
167
|
-
{ status: 'delivered' },
|
|
168
|
-
'messages',
|
|
169
|
-
)) {
|
|
170
|
-
console.log(msg.id, msg.body);
|
|
171
|
-
}
|
|
143
|
+
// Dedupe by event_id — delivery is at-least-once.
|
|
144
|
+
if (await alreadyProcessed(event.event_id)) return
|
|
172
145
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{ line_id: 'ln-1' },
|
|
177
|
-
'conversations',
|
|
178
|
-
)) {
|
|
179
|
-
console.log(conv.contact_number, conv.message_count);
|
|
146
|
+
if (isMessageEvent(event)) {
|
|
147
|
+
console.log(event.event_type, event.data.message.id)
|
|
148
|
+
}
|
|
180
149
|
}
|
|
181
150
|
```
|
|
182
151
|
|
|
183
|
-
|
|
152
|
+
During a `rotate-secret` grace window pass both secrets:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
verifyWebhook(rawBody, headers, [newSecret, oldSecret])
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Throws `WebhookSignatureError` on missing headers, expired timestamp (5min tolerance), or signature mismatch.
|
|
159
|
+
|
|
160
|
+
If verification happens behind a trusted gateway, use `parseWebhookEvent(rawBody)` to skip HMAC and just type the envelope.
|
|
161
|
+
|
|
162
|
+
## Errors
|
|
163
|
+
|
|
164
|
+
All API errors extend `APIError` and carry `status`, `code`, `message`, `traceId`, `requestId`. Subclasses match the §6 contract codes:
|
|
184
165
|
|
|
185
166
|
```ts
|
|
186
|
-
import {
|
|
167
|
+
import {
|
|
168
|
+
APIError,
|
|
169
|
+
APIConnectionError,
|
|
170
|
+
AuthenticationError,
|
|
171
|
+
BadRequestError,
|
|
172
|
+
ConflictError,
|
|
173
|
+
GoneError,
|
|
174
|
+
InternalServerError,
|
|
175
|
+
NotFoundError,
|
|
176
|
+
PaymentRequiredError,
|
|
177
|
+
PermissionDeniedError,
|
|
178
|
+
QuotaExceededError,
|
|
179
|
+
RateLimitError,
|
|
180
|
+
ServiceUnavailableError,
|
|
181
|
+
UnprocessableEntityError,
|
|
182
|
+
WebhookSignatureError
|
|
183
|
+
} from '@isnap/sdk'
|
|
187
184
|
|
|
188
185
|
try {
|
|
189
|
-
await client.messages.send({
|
|
186
|
+
await client.messages.send({
|
|
187
|
+
line_id: 'line_01HX...',
|
|
188
|
+
to: '+15551234567',
|
|
189
|
+
body: 'hello'
|
|
190
|
+
})
|
|
190
191
|
} catch (err) {
|
|
191
192
|
if (err instanceof RateLimitError) {
|
|
192
|
-
|
|
193
|
-
} else if (err instanceof
|
|
194
|
-
|
|
193
|
+
// retry-after handled automatically by the retry loop, but visible here too
|
|
194
|
+
} else if (err instanceof QuotaExceededError) {
|
|
195
|
+
// pause sending until next quota window
|
|
196
|
+
} else if (err instanceof APIError) {
|
|
197
|
+
console.error(err.status, err.code, err.traceId)
|
|
198
|
+
} else if (err instanceof APIConnectionError) {
|
|
199
|
+
// network issue / timeout — already retried per `maxRetries`
|
|
200
|
+
} else {
|
|
201
|
+
throw err
|
|
195
202
|
}
|
|
196
203
|
}
|
|
197
204
|
```
|
|
198
205
|
|
|
199
|
-
|
|
206
|
+
Retries: 429 + 5xx are retried automatically with jittered exponential backoff up to `maxRetries`. The retry budget is per-call.
|
|
200
207
|
|
|
201
|
-
|
|
208
|
+
## Examples
|
|
202
209
|
|
|
203
|
-
|
|
204
|
-
const client = init({
|
|
205
|
-
apiKey: 'isnap_your_key',
|
|
206
|
-
maxRetries: 5, // default: 3
|
|
207
|
-
retryDelayMs: 2000, // default: 5000 (fallback when no Retry-After header)
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
+
Runnable scripts in [`examples/`](https://github.com/WhatSnap/iSnap-App/tree/main/packages/sdk/examples) (browse on GitHub — they live in the repo, not the published tarball):
|
|
210
211
|
|
|
211
|
-
|
|
212
|
+
- `send-message.ts` — minimal send
|
|
213
|
+
- `paginate-messages.ts` — async iterator + abort
|
|
214
|
+
- `upload-attachment.ts` — file upload + send with attachment
|
|
215
|
+
- `handle-webhook.ts` — `node:http` server that verifies and dispatches events
|
|
212
216
|
|
|
213
|
-
|
|
217
|
+
Run them against a local backend:
|
|
214
218
|
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
} from '@isnap/sdk';
|
|
219
|
+
```bash
|
|
220
|
+
ISNAP_API_KEY=sk_test_... bun run examples/send-message.ts
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Development
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
bun install
|
|
227
|
+
bun run --filter '@isnap/sdk' test
|
|
228
|
+
bun run --filter '@isnap/sdk' typecheck
|
|
229
|
+
bun run --filter '@isnap/sdk' lint
|
|
230
|
+
bun run --filter '@isnap/sdk' build
|
|
228
231
|
```
|
|
229
232
|
|
|
230
|
-
|
|
233
|
+
To regenerate types from the backend's frozen OpenAPI spec:
|
|
231
234
|
|
|
232
|
-
|
|
235
|
+
```bash
|
|
236
|
+
bun run --filter '@isnap/backend' dump-openapi # writes packages/backend/openapi.json
|
|
237
|
+
bun run --filter '@isnap/sdk' generate # regenerates src/generated/openapi.d.ts
|
|
238
|
+
```
|
|
233
239
|
|
|
234
240
|
## License
|
|
235
241
|
|
|
236
|
-
|
|
242
|
+
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -1,61 +1,59 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { type ClientConfig } from './config.js';
|
|
2
|
+
import { AttachmentsResource } from './resources/attachments.js';
|
|
3
|
+
import { ByodResource } from './resources/byod.js';
|
|
4
|
+
import { ChatsResource } from './resources/chats.js';
|
|
3
5
|
import { LinesResource } from './resources/lines.js';
|
|
6
|
+
import { LookupResource } from './resources/lookup.js';
|
|
7
|
+
import { MessagesResource } from './resources/messages.js';
|
|
8
|
+
import { TrialResource } from './resources/trial.js';
|
|
4
9
|
import { WebhooksResource } from './resources/webhooks.js';
|
|
10
|
+
import type { HttpMethod, RequestOptions } from './types.js';
|
|
5
11
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { init } from '@isnap/sdk';
|
|
12
|
+
* Test-only escape hatch to clear the per-process dedup set between test
|
|
13
|
+
* cases. Not part of the public stability contract — flagged @internal so
|
|
14
|
+
* IDE auto-import doesn't surface it to consumers.
|
|
11
15
|
*
|
|
12
|
-
*
|
|
13
|
-
* const msg = await client.messages.send({ line_id: 'ln-1', to: '+15551234567', body: 'Hello' });
|
|
14
|
-
* ```
|
|
16
|
+
* @internal
|
|
15
17
|
*/
|
|
16
|
-
export declare
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
webhooks: WebhooksResource;
|
|
27
|
-
constructor(config:
|
|
18
|
+
export declare function _resetDeprecationWarnings(): void;
|
|
19
|
+
export declare class ISnapClient {
|
|
20
|
+
private readonly config;
|
|
21
|
+
readonly attachments: AttachmentsResource;
|
|
22
|
+
readonly byod: ByodResource;
|
|
23
|
+
readonly chats: ChatsResource;
|
|
24
|
+
readonly lines: LinesResource;
|
|
25
|
+
readonly lookup: LookupResource;
|
|
26
|
+
readonly messages: MessagesResource;
|
|
27
|
+
readonly trial: TrialResource;
|
|
28
|
+
readonly webhooks: WebhooksResource;
|
|
29
|
+
constructor(config: ClientConfig);
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
31
|
+
* Direct fetch with no SDK auth, no envelope parsing, no retry — used
|
|
32
|
+
* internally by resource methods that must call URLs OUTSIDE the iSnap
|
|
33
|
+
* API surface (e.g. presigned R2 PUTs during `attachments.upload`).
|
|
30
34
|
*
|
|
31
|
-
*
|
|
35
|
+
* The leading underscore signals "internal but unavoidably public": it
|
|
36
|
+
* lives on the public class because resource classes cannot reach the
|
|
37
|
+
* private `config.fetch` otherwise. SDK consumers should use the typed
|
|
38
|
+
* resource methods or `request()` for everything in `/v1/*` — calling
|
|
39
|
+
* this directly bypasses the SDK contract.
|
|
32
40
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* @
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
* Applies the same `config.timeoutMs` ceiling as `request()` so a
|
|
42
|
+
* stalled R2 PUT cannot hang the upload indefinitely.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
_externalFetch(url: string, init?: RequestInit): Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* Low-level request method. Resource classes wrap this with typed
|
|
49
|
+
* camelCase signatures; advanced users can call it directly for
|
|
50
|
+
* endpoints not yet covered by a resource.
|
|
40
51
|
*/
|
|
41
|
-
request<T>(method:
|
|
52
|
+
request<T>(method: HttpMethod, path: string, body?: unknown, opts?: RequestOptions): Promise<T>;
|
|
53
|
+
private dispatch;
|
|
54
|
+
private buildUrl;
|
|
55
|
+
private buildHeaders;
|
|
56
|
+
private warnIfDeprecated;
|
|
57
|
+
private connectionErrorMessage;
|
|
42
58
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Create a new iSnap SDK client.
|
|
45
|
-
*
|
|
46
|
-
* @param config - API key and optional settings
|
|
47
|
-
* @returns A configured {@link iSnapClient} instance
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* ```ts
|
|
51
|
-
* import { init } from '@isnap/sdk';
|
|
52
|
-
*
|
|
53
|
-
* const client = init({
|
|
54
|
-
* apiKey: 'isnap_your_key',
|
|
55
|
-
* maxRetries: 5,
|
|
56
|
-
* retryDelayMs: 2000,
|
|
57
|
-
* });
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export declare function init(config: iSnapConfig): iSnapClient;
|
|
61
59
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,aAAa,CAAA;AAQpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAA;AAqB9E;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;gBAEvB,MAAM,EAAE,YAAY;IAYhC;;;;;;;;;;;;;;;OAeG;IACG,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAoB5E;;;;OAIG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,CAAC,CAAC;YAwBC,QAAQ;IA8GtB,OAAO,CAAC,QAAQ;IAehB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,sBAAsB;CAO/B"}
|