@atlasauth/msw 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 +126 -0
- package/dist/handlers.d.ts +29 -0
- package/dist/handlers.js +600 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/serializers.d.ts +156 -0
- package/dist/serializers.js +162 -0
- package/dist/serializers.js.map +1 -0
- package/dist/state.d.ts +242 -0
- package/dist/state.js +266 -0
- package/dist/state.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @atlasauth/msw
|
|
2
|
+
|
|
3
|
+
[Mock Service Worker](https://mswjs.io) (`msw` v2) request handlers that mock
|
|
4
|
+
Atlas's **Frontend API** (FAPI, the publishable-key browser surface) and
|
|
5
|
+
**Backend API** (BAPI, the secret-key administrative surface), so you can test
|
|
6
|
+
your Atlas integration against realistic, shape-correct responses **without a
|
|
7
|
+
live server**. The MSW peer of [`@atlasauth/testing`](../testing).
|
|
8
|
+
|
|
9
|
+
The handlers are backed by a small **in-memory mock backend** that advances
|
|
10
|
+
across calls: a sign-in attempt really transitions `prepare → attempt →
|
|
11
|
+
complete`, a minted session really authenticates `GET /v1/client/me`, and a user
|
|
12
|
+
you create over the BAPI really appears in the next list. Ids are deterministic,
|
|
13
|
+
so a test that seeds the same thing gets the same ids every run.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add -D @atlasauth/msw msw
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`msw` is a **peer dependency** — you bring the version your project already uses
|
|
22
|
+
(v2).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { setupServer } from 'msw/node';
|
|
28
|
+
import { atlasHandlers, createAtlasMock } from '@atlasauth/msw';
|
|
29
|
+
|
|
30
|
+
const mock = createAtlasMock({
|
|
31
|
+
instance: {
|
|
32
|
+
fapiBaseUrl: 'https://fapi.atlas.test', // the origin your SDK calls
|
|
33
|
+
bapiBaseUrl: 'https://api.atlas.test',
|
|
34
|
+
publishableKey: 'pk_test_…',
|
|
35
|
+
secretKey: 'sk_test_…',
|
|
36
|
+
firstFactors: ['password', 'email_code'],
|
|
37
|
+
},
|
|
38
|
+
users: [{ email: 'ada@example.com', password: 'hunter2', firstName: 'Ada' }],
|
|
39
|
+
organizations: [{ name: 'Acme', slug: 'acme' }],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const server = setupServer(...atlasHandlers(mock));
|
|
43
|
+
|
|
44
|
+
beforeAll(() => server.listen());
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
server.resetHandlers();
|
|
47
|
+
mock.reset(); // back to the original seed
|
|
48
|
+
});
|
|
49
|
+
afterAll(() => server.close());
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
In the browser (component tests, Storybook) use `setupWorker` from `msw/browser`
|
|
53
|
+
with the same `atlasHandlers(mock)`.
|
|
54
|
+
|
|
55
|
+
### Driving the mock
|
|
56
|
+
|
|
57
|
+
`createAtlasMock(seed?)` returns a mutable store you can inspect and mutate
|
|
58
|
+
directly:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
mock.addUser({ email: 'grace@example.com', password: 'hopper' });
|
|
62
|
+
mock.addOrganization({ name: 'Globex', members: [{ userId, role: 'admin' }] });
|
|
63
|
+
mock.createSession(userId); // mint a live session out of band
|
|
64
|
+
mock.reset(); // restore the original seed (ids realign)
|
|
65
|
+
|
|
66
|
+
mock.users; // the current users
|
|
67
|
+
mock.sessions; // the live sessions
|
|
68
|
+
mock.fapiBaseUrl; // the FAPI origin the handlers are mounted on
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Email-code and sign-up verification accept one fixed OTP — `instance.testOtp`,
|
|
72
|
+
default **`424242`** — so a happy-path test needs no mailbox.
|
|
73
|
+
|
|
74
|
+
`atlasHandlers(mock, { fapiBaseUrl, bapiBaseUrl })` overrides the origins the
|
|
75
|
+
handlers mount on without changing the store.
|
|
76
|
+
|
|
77
|
+
## Covered endpoints
|
|
78
|
+
|
|
79
|
+
Breadth is weighted toward the commonly-tested surface: environment bootstrap,
|
|
80
|
+
the full sign-in / sign-up happy paths, `me`, JWKS, and BAPI users / orgs /
|
|
81
|
+
sessions.
|
|
82
|
+
|
|
83
|
+
### Frontend API (publishable key — `x-publishable-key` header or `?publishable_key`)
|
|
84
|
+
|
|
85
|
+
| Method & path | What it does |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `GET /v1/client/environment` | Enabled first factors + social providers (the seeded instance config) |
|
|
88
|
+
| `GET /v1/client` | Session boot — `session: null` until signed in, otherwise the active session + user + memberships |
|
|
89
|
+
| `POST /v1/client/sign_ins` | Start a sign-in attempt from an identifier |
|
|
90
|
+
| `POST /v1/client/sign_ins/:id/prepare_first_factor` | Send an email/phone code; returns a `poll_secret` |
|
|
91
|
+
| `POST /v1/client/sign_ins/:id/attempt_first_factor` | Present `password` or `code`; on success completes + returns a `ticket` |
|
|
92
|
+
| `GET /v1/client/sign_ins/:id?poll_secret=…` | Poll an attempt (cross-device) |
|
|
93
|
+
| `POST /v1/client/sign_ups` | Start a sign-up (email + password) |
|
|
94
|
+
| `POST /v1/client/sign_ups/:id/prepare_verification` | (Re)send the verification code |
|
|
95
|
+
| `POST /v1/client/sign_ups/:id/attempt_verification` | Present the code; completes + returns a `ticket` |
|
|
96
|
+
| `POST /v1/client/tickets/exchange` | Swap a completed attempt's ticket for a session (`{ jwt, expires_in }`) |
|
|
97
|
+
| `GET /v1/client/me` | The signed-in user (`Authorization: Bearer <session jwt>`) |
|
|
98
|
+
| `GET /.well-known/jwks.json` | The JWKS the backend verifies tokens against |
|
|
99
|
+
|
|
100
|
+
### Backend API (secret key — `Authorization: Bearer sk_…`)
|
|
101
|
+
|
|
102
|
+
| Method & path | What it does |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `GET /v1/users` | Cursor-paginated list (`limit`, `starting_after`) — `{ data, has_more, next_cursor }` |
|
|
105
|
+
| `POST /v1/users` | Create a user |
|
|
106
|
+
| `GET /v1/users/:id` | Get one user (`404` unknown / soft-deleted; `?include_deleted=true` to see the tombstone) |
|
|
107
|
+
| `PATCH /v1/users/:id` | Update profile / metadata / password |
|
|
108
|
+
| `DELETE /v1/users/:id` | Soft delete (`{ object: 'user', id, deleted: true }`) |
|
|
109
|
+
| `GET /v1/organizations` | Cursor-paginated list |
|
|
110
|
+
| `POST /v1/organizations` | Create an organization |
|
|
111
|
+
| `GET /v1/organizations/:id` | Get one organization |
|
|
112
|
+
| `POST /v1/sessions` | Mint a session for a user in one call (`{ jwt, refresh_token, expires_in }`) |
|
|
113
|
+
| `GET /v1/sessions/:id` | Get one session (operator view) |
|
|
114
|
+
| `POST /v1/sessions/:id/revoke` | Revoke a session |
|
|
115
|
+
|
|
116
|
+
Object and error shapes mirror the real API: resources carry their `object`
|
|
117
|
+
tag (`user`, `organization`, `session`, `sign_in_attempt`, …), and errors use
|
|
118
|
+
the §9.1 envelope `{ errors: [{ code, message, param?, meta?, docs_url }] }`.
|
|
119
|
+
`private_metadata` is never serialised.
|
|
120
|
+
|
|
121
|
+
## Not covered
|
|
122
|
+
|
|
123
|
+
MFA / second factor, passkeys, OAuth / SSO / SAML, magic-link navigation,
|
|
124
|
+
sessions refresh-rotation + multi-account, invitations, and the rest of the BAPI
|
|
125
|
+
admin surface. Add your own `http.*` handlers alongside `atlasHandlers(mock)` for
|
|
126
|
+
anything your test needs beyond the table above.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MSW (v2) request handlers that stand in for Atlas's Frontend API (FAPI, the
|
|
3
|
+
* publishable-key browser surface) and Backend API (BAPI, the secret-key
|
|
4
|
+
* administrative surface). Each handler reads and mutates a {@link AtlasMock}
|
|
5
|
+
* store, so the mocked endpoints behave like a real instance across calls — a
|
|
6
|
+
* sign-in attempt advances, a minted session authenticates `me`, a created user
|
|
7
|
+
* appears in the next list.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { setupServer } from 'msw/node';
|
|
11
|
+
* import { atlasHandlers, createAtlasMock } from '@atlasauth/msw';
|
|
12
|
+
*
|
|
13
|
+
* const mock = createAtlasMock({ users: [{ email: 'ada@example.com', password: 'pw' }] });
|
|
14
|
+
* const server = setupServer(...atlasHandlers(mock));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { type HttpHandler } from 'msw';
|
|
18
|
+
import type { AtlasMock } from './state';
|
|
19
|
+
export interface AtlasHandlersOptions {
|
|
20
|
+
/** Override the FAPI origin the handlers mount on (defaults to `mock.fapiBaseUrl`). */
|
|
21
|
+
fapiBaseUrl?: string;
|
|
22
|
+
/** Override the BAPI origin the handlers mount on (defaults to `mock.bapiBaseUrl`). */
|
|
23
|
+
bapiBaseUrl?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the full set of FAPI + BAPI handlers for a mock store. Spread the result
|
|
27
|
+
* into `setupServer(...)` (Node) or `setupWorker(...)` (browser).
|
|
28
|
+
*/
|
|
29
|
+
export declare function atlasHandlers(mock: AtlasMock, options?: AtlasHandlersOptions): HttpHandler[];
|