seam 0.47.0 → 0.49.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.
Files changed (2) hide show
  1. package/README.md +420 -15
  2. package/package.json +5 -2
package/README.md CHANGED
@@ -5,10 +5,6 @@
5
5
 
6
6
  JavaScript SDK for the Seam API written in TypeScript.
7
7
 
8
- _This repository hosts the next major version of the Seam JavaScript SDK.
9
- This SDK is available for early preview.
10
- It will eventually replace the [seamapi](https://github.com/seamapi/javascript/) package._
11
-
12
8
  ## Description
13
9
 
14
10
  [Seam] makes it easy to integrate IoT devices with your applications.
@@ -19,12 +15,7 @@ The SDK is fully tree-shakeable
19
15
  and optimized for use in both client and server applications.
20
16
 
21
17
  The repository does not contain the SDK code.
22
- Instead, it re-exports from a core set of Seam modules.
23
-
24
- _While this SDK is still in preview,
25
- please refer to the individual README files in these repositories for
26
- additional usage documentation not yet available in the primary Seam documentation.
27
- See [this issue for a draft migration guide](https://github.com/seamapi/javascript-next/issues/1) from the seamapi package._
18
+ Instead, it re-exports from a core set of Seam modules:
28
19
 
29
20
  - [@seamapi/http]: JavaScript HTTP client for the Seam API written in TypeScript.
30
21
  - [@seamapi/webhook]: Webhook SDK for the Seam API written in TypeScript.
@@ -36,6 +27,47 @@ See [this issue for a draft migration guide](https://github.com/seamapi/javascri
36
27
  [@seamapi/http]: https://github.com/seamapi/javascript-http
37
28
  [@seamapi/webhook]: https://github.com/seamapi/javascript-webhook
38
29
 
30
+ ## Contents
31
+
32
+ <!-- toc -->
33
+
34
+ - [Installation](#installation)
35
+ - [Usage](#usage)
36
+ - [Examples](#examples)
37
+ - [List devices](#list-devices)
38
+ - [Unlock a door](#unlock-a-door)
39
+ - [Authentication Methods](#authentication-methods)
40
+ - [API Key](#api-key)
41
+ - [Client Session Token](#client-session-token)
42
+ - [Publishable Key](#publishable-key)
43
+ - [Personal Access Token](#personal-access-token)
44
+ - [Console Session Token](#console-session-token)
45
+ - [Action Attempts](#action-attempts)
46
+ - [Interacting with Multiple Workspaces](#interacting-with-multiple-workspaces)
47
+ - [Personal Access Token](#personal-access-token-1)
48
+ - [Console Session Token](#console-session-token-1)
49
+ - [Advanced Usage](#advanced-usage)
50
+ - [Additional Options](#additional-options)
51
+ - [Setting the endpoint](#setting-the-endpoint)
52
+ - [Configuring the Axios Client](#configuring-the-axios-client)
53
+ - [Using the Axios Client](#using-the-axios-client)
54
+ - [Overriding the Client](#overriding-the-client)
55
+ - [Inspecting the Request](#inspecting-the-request)
56
+ - [Receiving Webhooks](#receiving-webhooks)
57
+ - [Development and Testing](#development-and-testing)
58
+ - [Quickstart](#quickstart)
59
+ - [Source code](#source-code)
60
+ - [Requirements](#requirements)
61
+ - [Publishing](#publishing)
62
+ - [Automatic](#automatic)
63
+ - [Manual](#manual)
64
+ - [GitHub Actions](#github-actions)
65
+ - [Contributing](#contributing)
66
+ - [License](#license)
67
+ - [Warranty](#warranty)
68
+
69
+ <!-- tocstop -->
70
+
39
71
  ## Installation
40
72
 
41
73
  Add this as a dependency to your project using [npm] with
@@ -46,7 +78,20 @@ $ npm install seam
46
78
 
47
79
  [npm]: https://www.npmjs.com/
48
80
 
49
- ### Usage
81
+ ## Usage
82
+
83
+ ### Examples
84
+
85
+ _These examples assume `SEAM_API_KEY` is set in your environment._
86
+
87
+ #### List devices
88
+
89
+ ```ts
90
+ import { Seam } from 'seam'
91
+
92
+ const seam = new Seam()
93
+ const devices = await seam.devices.list()
94
+ ```
50
95
 
51
96
  #### Unlock a door
52
97
 
@@ -58,15 +103,375 @@ const lock = await seam.locks.get({ name: 'Front Door' })
58
103
  await seam.locks.unlockDoor({ device_id: lock.device_id })
59
104
  ```
60
105
 
61
- #### Parse and validate a webhook
106
+ ### Authentication Methods
107
+
108
+ The SDK supports several authentication mechanisms.
109
+ Authentication may be configured by passing the corresponding
110
+ options directly to the `Seam` constructor,
111
+ or with the more ergonomic static factory methods.
112
+
113
+ > Publishable Key authentication is not supported by the constructor
114
+ > and must be configured using `Seam.fromPublishableKey`.
115
+
116
+ #### API Key
117
+
118
+ An API key is scoped to a single workspace and should only be used on the server.
119
+ Obtain one from the Seam Console.
62
120
 
63
121
  ```ts
64
- import { SeamWebhook } from 'seam'
122
+ // Set the `SEAM_API_KEY` environment variable
123
+ const seam = new Seam()
124
+
125
+ // Pass as the first argument to the constructor
126
+ const seam = new Seam('your-api-key')
127
+
128
+ // Pass as an option the constructor
129
+ const seam = new Seam({ apiKey: 'your-api-key' })
130
+
131
+ // Use the factory method
132
+ const seam = Seam.fromApiKey('your-api-key')
133
+ ```
134
+
135
+ #### Client Session Token
136
+
137
+ A Client Session Token is scoped to a client session and should only be used on the client.
138
+
139
+ ```ts
140
+ // Pass as an option the constructor
141
+ const seam = new Seam({ clientSessionToken: 'some-client-session-token' })
142
+
143
+ // Use the factory method
144
+ const seam = Seam.fromClientSessionToken('some-client-session-token')
145
+ ```
146
+
147
+ The client session token may be updated using
148
+
149
+ ```ts
150
+ const seam = Seam.fromClientSessionToken('some-client-session-token')
151
+
152
+ await seam.updateClientSessionToken('some-new-client-session-token')
153
+ ```
154
+
155
+ #### Publishable Key
156
+
157
+ A Publishable Key is used by the client to acquire Client Session Token for a workspace.
158
+ Obtain one from the Seam Console.
159
+
160
+ Use the async factory method to return a client authenticated with a client session token:
161
+
162
+ ```ts
163
+ const seam = await Seam.fromPublishableKey(
164
+ 'your-publishable-key',
165
+ 'some-user-identifier-key',
166
+ )
167
+ ```
168
+
169
+ This will get an existing client session matching the user identifier key,
170
+ or create a new empty client session.
171
+
172
+ #### Personal Access Token
173
+
174
+ A Personal Access Token is scoped to a Seam Console user.
175
+ Obtain one from the Seam Console.
176
+ A workspace id must be provided when using this method
177
+ and all requests will be scoped to that workspace.
178
+
179
+ ```ts
180
+ // Pass as an option the constructor
181
+
182
+ const seam = new Seam({
183
+ personalAccessToken: 'your-personal-access-token',
184
+ workspaceId: 'your-workspace-id',
185
+ })
186
+
187
+ // Use the factory method
188
+ const seam = Seam.fromPersonalAccessToken(
189
+ 'some-console-session-token',
190
+ 'your-workspace-id',
191
+ )
192
+ ```
193
+
194
+ #### Console Session Token
195
+
196
+ A Console Session Token is used by the Seam Console.
197
+ This authentication method is only used by internal Seam applications.
198
+ A workspace id must be provided when using this method
199
+ and all requests will be scoped to that workspace.
200
+
201
+ ```ts
202
+ // Pass as an option the constructor
203
+ const seam = new Seam({
204
+ consoleSessionToken: 'some-console-session-token',
205
+ workspaceId: 'your-workspace-id',
206
+ })
207
+
208
+ // Use the factory method
209
+ const seam = Seam.fromConsoleSessionToken(
210
+ 'some-console-session-token',
211
+ 'your-workspace-id',
212
+ )
213
+ ```
214
+
215
+ ### Action Attempts
216
+
217
+ Some asynchronous operations, e.g., unlocking a door, return an [action attempt].
218
+ Seam tracks the progress of requested operation and updates the action attempt.
219
+
220
+ To make working with action attempts more convenient for applications,
221
+ this library provides the `waitForActionAttempt` option.
222
+
223
+ Pass the option per-request,
224
+
225
+ ```ts
226
+ await seam.locks.unlockDoor(
227
+ { device_id },
228
+ {
229
+ waitForActionAttempt: true,
230
+ },
231
+ )
232
+ ```
233
+
234
+ or set the default option for the client:
235
+
236
+ ```ts
237
+ const seam = new Seam({
238
+ apiKey: 'your-api-key',
239
+ waitForActionAttempt: true,
240
+ })
241
+
242
+ await seam.locks.unlockDoor({ device_id })
243
+ ```
65
244
 
66
- const webhook = new SeamWebhook('webhook-secret')
67
- const data = webhook.verify(payload, headers)
245
+ If you have already have an action attempt id
246
+ and want to wait for it to resolve, simply use
247
+
248
+ ```ts
249
+ await seam.actionAttempts.get(
250
+ { action_attempt_id },
251
+ {
252
+ waitForActionAttempt: true,
253
+ },
254
+ )
255
+ ```
256
+
257
+ Using the `waitForActionAttempt` option:
258
+
259
+ - Polls the action attempt up to the `timeout`
260
+ at the `pollingInterval` (both in milliseconds).
261
+ - Resolves with a fresh copy of the successful action attempt.
262
+ - Rejects with a `SeamActionAttemptFailedError` if the action attempt is unsuccessful.
263
+ - Rejects with a `SeamActionAttemptTimeoutError` if the action attempt is still pending when the `timeout` is reached.
264
+ - Both errors expose an `actionAttempt` property.
265
+
266
+ ```ts
267
+ import {
268
+ Seam,
269
+ isSeamActionAttemptFailedError,
270
+ isSeamActionAttemptTimeoutError,
271
+ } from 'seam'
272
+
273
+ const seam = new Seam('your-api-key')
274
+
275
+ const [lock] = await seam.locks.list()
276
+
277
+ if (lock == null) throw new Error('No locks in this workspace')
278
+
279
+ try {
280
+ await seam.locks.unlockDoor(
281
+ { device_id: lock.device_id },
282
+ {
283
+ waitForActionAttempt: {
284
+ pollingInterval: 1000,
285
+ timeout: 5000,
286
+ },
287
+ },
288
+ )
289
+ console.log('Door unlocked')
290
+ } catch (err: unknown) {
291
+ if (isSeamActionAttemptFailedError(err)) {
292
+ console.log('Could not unlock the door')
293
+ return
294
+ }
295
+
296
+ if (isSeamActionAttemptTimeoutError(err)) {
297
+ console.log('Door took too long to unlock')
298
+ return
299
+ }
300
+
301
+ throw err
302
+ }
303
+ ```
304
+
305
+ [action attempt]: https://docs.seam.co/latest/core-concepts/action-attempts
306
+
307
+ ### Interacting with Multiple Workspaces
308
+
309
+ Some Seam API endpoints interact with multiple workspaces.
310
+ The `SeamMultiWorkspace` client is not bound to a specific workspace
311
+ and may use those endpoints with an appropriate authentication method.
312
+
313
+ #### Personal Access Token
314
+
315
+ A Personal Access Token is scoped to a Seam Console user.
316
+ Obtain one from the Seam Console.
317
+
318
+ ```ts
319
+ // Pass as an option the constructor
320
+ const seam = new SeamMultiWorkspace({
321
+ personalAccessToken: 'your-personal-access-token',
322
+ })
323
+
324
+ // Use the factory method
325
+ const seam = SeamMultiWorkspace.fromPersonalAccessToken(
326
+ 'some-console-session-token',
327
+ )
328
+
329
+ // List workspaces authorized for this Personal Access Token
330
+ const workspaces = await seam.workspaces.list()
331
+ ```
332
+
333
+ #### Console Session Token
334
+
335
+ A Console Session Token is used by the Seam Console.
336
+ This authentication method is only used by internal Seam applications.
337
+
338
+ ```ts
339
+ // Pass as an option the constructor
340
+ const seam = new SeamMultiWorkspace({
341
+ consoleSessionToken: 'some-console-session-token',
342
+ })
343
+
344
+ // Use the factory method
345
+ const seam = SeamMultiWorkspace.fromConsoleSessionToken(
346
+ 'some-console-session-token',
347
+ )
348
+
349
+ // List workspaces authorized for this Seam Console user
350
+ const workspaces = await seam.workspaces.list()
351
+ ```
352
+
353
+ ### Advanced Usage
354
+
355
+ #### Additional Options
356
+
357
+ In addition the various authentication options,
358
+ the constructor takes some advanced options that affect behavior.
359
+
360
+ ```ts
361
+ const seam = new Seam({
362
+ apiKey: 'your-api-key',
363
+ endpoint: 'https://example.com',
364
+ axiosOptions: {},
365
+ axiosRetryOptions: {},
366
+ })
367
+ ```
368
+
369
+ When using the static factory methods,
370
+ these options may be passed in as the last argument.
371
+
372
+ ```ts
373
+ const seam = Seam.fromApiKey('some-api-key', {
374
+ endpoint: 'https://example.com',
375
+ axiosOptions: {},
376
+ axiosRetryOptions: {},
377
+ })
378
+ ```
379
+
380
+ #### Setting the endpoint
381
+
382
+ Some contexts may need to override the API endpoint,
383
+ e.g., testing or proxy setups.
384
+ This option corresponds to the Axios `baseURL` setting.
385
+
386
+ Either pass the `endpoint` option, or set the `SEAM_ENDPOINT` environment variable.
387
+
388
+ #### Configuring the Axios Client
389
+
390
+ The Axios client and retry behavior may be configured with custom initiation options
391
+ via [`axiosOptions`][axiosOptions] and [`axiosRetryOptions`][axiosRetryOptions].
392
+ Options are deep merged with the default options.
393
+
394
+ [axiosOptions]: https://axios-http.com/docs/config_defaults
395
+ [axiosRetryOptions]: https://github.com/softonic/axios-retry
396
+
397
+ #### Using the Axios Client
398
+
399
+ The Axios client is exposed and may be used or configured directly:
400
+
401
+ ```ts
402
+ import { Seam, DevicesListResponse } from 'seam'
403
+
404
+ const seam = new Seam()
405
+
406
+ seam.client.interceptors.response.use((response) => {
407
+ console.log(response)
408
+ return response
409
+ })
410
+
411
+ const devices = await seam.client.get<DevicesListResponse>('/devices/list')
68
412
  ```
69
413
 
414
+ #### Overriding the Client
415
+
416
+ An Axios compatible client may be provided to create a `Seam` instance.
417
+ This API is used internally and is not directly supported.
418
+
419
+ #### Inspecting the Request
420
+
421
+ All client methods return an instance of `SeamRequest`.
422
+ Inspect the request before it is sent to the server by intentionally not awaiting the `SeamRequest`:
423
+
424
+ ```ts
425
+ const seam = new Seam('your-api-key')
426
+
427
+ const request = seam.devices.list()
428
+
429
+ console.log(`${request.method} ${request.url}`, JSON.stringify(request.body))
430
+
431
+ const devices = await request.execute()
432
+ ```
433
+
434
+ ### Receiving Webhooks
435
+
436
+ First, create a webhook using the Seam API or Seam Console
437
+ and obtain a Seam webhook secret.
438
+
439
+ _This example is for [Express], see the [Svix docs for more examples in specific frameworks](https://docs.svix.com/receiving/verifying-payloads/how)._
440
+
441
+ ```js
442
+ import { SeamWebhook } from 'seam'
443
+ import express from 'express'
444
+ import bodyParser from 'body-parser'
445
+
446
+ import { storeEvent } from './store-event.js'
447
+
448
+ const app = express()
449
+
450
+ const webhook = new SeamWebhook(process.env.SEAM_WEBHOOK_SECRET)
451
+
452
+ app.post(
453
+ '/webhook',
454
+ bodyParser.raw({ type: 'application/json' }),
455
+ (req, res) => {
456
+ let data
457
+ try {
458
+ data = webhook.verify(payload, headers)
459
+ } catch {
460
+ return res.status(400).send()
461
+ }
462
+
463
+ storeEvent(data, (err) => {
464
+ if (err != null) {
465
+ return res.status(500).send()
466
+ }
467
+ res.status(204).send()
468
+ })
469
+ },
470
+ )
471
+ ```
472
+
473
+ [Express]: https://expressjs.com/
474
+
70
475
  ## Development and Testing
71
476
 
72
477
  ### Quickstart
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seam",
3
- "version": "0.47.0",
3
+ "version": "0.49.0",
4
4
  "description": "JavaScript SDK for the Seam API written in TypeScript.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -52,6 +52,8 @@
52
52
  "lint": "eslint --ignore-path .gitignore .",
53
53
  "prelint": "prettier --check --ignore-path .gitignore .",
54
54
  "postversion": "git push --follow-tags",
55
+ "generate": "tsx generate-readme.ts",
56
+ "postgenerate": "markdown-toc -i README.md --bullets '-'",
55
57
  "example": "tsx examples",
56
58
  "example:inspect": "tsx --inspect examples",
57
59
  "format": "eslint --ignore-path .gitignore --fix .",
@@ -63,7 +65,7 @@
63
65
  },
64
66
  "dependencies": {
65
67
  "@seamapi/http": "0.25.2",
66
- "@seamapi/types": "1.146.0",
68
+ "@seamapi/types": "1.147.1",
67
69
  "@seamapi/webhook": "1.0.0-rc.0",
68
70
  "seamapi-types": "1.42.0"
69
71
  },
@@ -77,6 +79,7 @@
77
79
  "eslint-plugin-simple-import-sort": "^12.0.0",
78
80
  "eslint-plugin-unused-imports": "^3.0.0",
79
81
  "landlubber": "^2.0.0",
82
+ "markdown-toc": "^1.2.0",
80
83
  "prettier": "^3.0.0",
81
84
  "tsc-alias": "^1.8.2",
82
85
  "tsup": "^8.0.1",