@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 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[];