@mseep/affine-mcp-server 2.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 +22 -0
- package/README.md +270 -0
- package/bin/affine-mcp +5 -0
- package/dist/auth.js +61 -0
- package/dist/cli.js +726 -0
- package/dist/config.js +178 -0
- package/dist/edgeless/layout.js +222 -0
- package/dist/graphqlClient.js +116 -0
- package/dist/httpAuth.js +147 -0
- package/dist/httpDiagnostics.js +38 -0
- package/dist/index.js +209 -0
- package/dist/markdown/parse.js +559 -0
- package/dist/markdown/render.js +227 -0
- package/dist/markdown/types.js +1 -0
- package/dist/oauth.js +154 -0
- package/dist/sse.js +261 -0
- package/dist/toolSurface.js +349 -0
- package/dist/tools/accessTokens.js +45 -0
- package/dist/tools/auth.js +18 -0
- package/dist/tools/blobStorage.js +136 -0
- package/dist/tools/comments.js +104 -0
- package/dist/tools/docs.js +7478 -0
- package/dist/tools/history.js +22 -0
- package/dist/tools/icons.js +125 -0
- package/dist/tools/notifications.js +79 -0
- package/dist/tools/organize.js +1145 -0
- package/dist/tools/properties.js +426 -0
- package/dist/tools/user.js +13 -0
- package/dist/tools/userCRUD.js +77 -0
- package/dist/tools/workspaces.js +322 -0
- package/dist/util/explorerIcon.js +95 -0
- package/dist/util/mcp.js +28 -0
- package/dist/ws.js +113 -0
- package/docs/assets/edgeless-canvas-demo-advanced-dark.png +0 -0
- package/docs/assets/edgeless-canvas-demo-advanced-light.png +0 -0
- package/docs/client-setup.md +174 -0
- package/docs/configuration-and-deployment.md +265 -0
- package/docs/edgeless-canvas-cookbook.md +226 -0
- package/docs/getting-started.md +229 -0
- package/docs/tool-reference.md +200 -0
- package/docs/workflow-recipes.md +147 -0
- package/package.json +118 -0
- package/tool-manifest.json +99 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 The AFFiNE MCP Server Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# AFFiNE MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for AFFiNE. It exposes AFFiNE workspaces and documents to AI assistants over stdio (default) or HTTP (`/mcp`) and supports both AFFiNE Cloud and self-hosted deployments.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/dawncr0w/affine-mcp-server/releases)
|
|
6
|
+
[](https://github.com/modelcontextprotocol/typescript-sdk)
|
|
7
|
+
[](https://github.com/dawncr0w/affine-mcp-server/actions/workflows/ci.yml)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
<a href="https://glama.ai/mcp/servers/@DAWNCR0W/affine-mcp-server">
|
|
11
|
+
<img width="380" height="200" src="https://glama.ai/mcp/servers/@DAWNCR0W/affine-mcp-server/badge" alt="AFFiNE Server MCP server" />
|
|
12
|
+
</a>
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Overview](#overview)
|
|
17
|
+
- [Choose Your Path](#choose-your-path)
|
|
18
|
+
- [Quick Start](#quick-start)
|
|
19
|
+
- [Compatibility Matrix](#compatibility-matrix)
|
|
20
|
+
- [Tool Surface](#tool-surface)
|
|
21
|
+
- [Documentation Map](#documentation-map)
|
|
22
|
+
- [Verify Your Setup](#verify-your-setup)
|
|
23
|
+
- [Security and Scope](#security-and-scope)
|
|
24
|
+
- [Development](#development)
|
|
25
|
+
- [Release Notes](#release-notes)
|
|
26
|
+
- [License](#license)
|
|
27
|
+
- [Support](#support)
|
|
28
|
+
|
|
29
|
+
## Overview
|
|
30
|
+
|
|
31
|
+
AFFiNE MCP Server is designed for three common scenarios:
|
|
32
|
+
- Run a local stdio MCP server for Claude Code, Codex CLI, Cursor, or Claude Desktop
|
|
33
|
+
- Expose a remote HTTP MCP endpoint for hosted or browser-connected clients
|
|
34
|
+
- Automate AFFiNE workspace, document, database, organization, and comment workflows through a stable MCP tool surface
|
|
35
|
+
|
|
36
|
+
Highlights:
|
|
37
|
+
|
|
38
|
+
- Supports AFFiNE Cloud and self-hosted AFFiNE instances
|
|
39
|
+
- Supports stdio and HTTP transports
|
|
40
|
+
- Supports token, cookie, and email/password authentication
|
|
41
|
+
- Exposes 94 canonical MCP tools backed by AFFiNE GraphQL and WebSocket APIs
|
|
42
|
+
- Includes semantic page composition, native template instantiation, database intent composition, capability and fidelity reporting, and workspace blueprint helpers
|
|
43
|
+
- Includes Docker images, health probes, and end-to-end test coverage
|
|
44
|
+
|
|
45
|
+
Scope boundaries:
|
|
46
|
+
|
|
47
|
+
- This server can access only server-backed AFFiNE workspaces
|
|
48
|
+
- Browser-local workspaces stored only in local storage are not available through AFFiNE server APIs
|
|
49
|
+
- AFFiNE Cloud requires API-token-based access for MCP usage; programmatic email/password sign-in is blocked by Cloudflare
|
|
50
|
+
|
|
51
|
+
> New in v2.3.0: Added document and folder sidebar icon tools, plus richer hierarchy detection for inline LinkedPage references and synced-doc embeds.
|
|
52
|
+
|
|
53
|
+
## Choose Your Path
|
|
54
|
+
| Goal | Start here |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| Set up a local stdio server with the least friction | [docs/getting-started.md](docs/getting-started.md) |
|
|
57
|
+
| Run the server in Docker or another OCI runtime | [docs/getting-started.md#path-c-run-from-the-docker-image](docs/getting-started.md#path-c-run-from-the-docker-image) |
|
|
58
|
+
| Configure Claude Code, Claude Desktop, Codex CLI, or Cursor | [docs/client-setup.md](docs/client-setup.md) |
|
|
59
|
+
| Run the server remotely over HTTP or behind OAuth | [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md) |
|
|
60
|
+
| Lock down tool exposure for least-privilege deployments | [docs/configuration-and-deployment.md#least-privilege-tool-exposure](docs/configuration-and-deployment.md#least-privilege-tool-exposure) |
|
|
61
|
+
| Learn common AFFiNE workflows and tool sequences | [docs/workflow-recipes.md](docs/workflow-recipes.md) |
|
|
62
|
+
| Browse the tool catalog by domain | [docs/tool-reference.md](docs/tool-reference.md) |
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
### 1. Install the CLI
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm i -g affine-mcp-server
|
|
70
|
+
affine-mcp --version
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
You can also run the package ad hoc:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx -y -p affine-mcp-server affine-mcp -- --version
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Or run the server in Docker
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
docker run -d \
|
|
83
|
+
-p 3000:3000 \
|
|
84
|
+
-e MCP_TRANSPORT=http \
|
|
85
|
+
-e AFFINE_BASE_URL=https://your-affine-instance.com \
|
|
86
|
+
-e AFFINE_API_TOKEN=ut_your_token \
|
|
87
|
+
-e AFFINE_MCP_AUTH_MODE=bearer \
|
|
88
|
+
-e AFFINE_MCP_HTTP_TOKEN=your-strong-secret \
|
|
89
|
+
ghcr.io/dawncr0w/affine-mcp-server:latest
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then point your client at:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"affine": {
|
|
98
|
+
"type": "http",
|
|
99
|
+
"url": "http://localhost:3000/mcp",
|
|
100
|
+
"headers": {
|
|
101
|
+
"Authorization": "Bearer your-strong-secret"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For Docker, health checks, and remote deployment details, see [docs/configuration-and-deployment.md#docker](docs/configuration-and-deployment.md#docker).
|
|
109
|
+
|
|
110
|
+
### 3. Save credentials with interactive login
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
affine-mcp login
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This stores credentials in `~/.config/affine-mcp/config` with mode `600`.
|
|
117
|
+
|
|
118
|
+
- For AFFiNE Cloud, use an API token from `Settings -> Integrations -> MCP Server`
|
|
119
|
+
- For self-hosted AFFiNE, you can use either an API token or email/password
|
|
120
|
+
|
|
121
|
+
### 4. Register the server with your client
|
|
122
|
+
|
|
123
|
+
Claude Code project config:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"mcpServers": {
|
|
128
|
+
"affine": {
|
|
129
|
+
"command": "affine-mcp"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Codex CLI:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
codex mcp add affine -- affine-mcp
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
More client-specific setup is in [docs/client-setup.md](docs/client-setup.md).
|
|
142
|
+
|
|
143
|
+
### 5. Verify the connection
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
affine-mcp status
|
|
147
|
+
affine-mcp doctor
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If you want to expose the server remotely over HTTP instead of stdio, start with [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md).
|
|
151
|
+
|
|
152
|
+
## Compatibility Matrix
|
|
153
|
+
|
|
154
|
+
| Target | Transport | Recommended auth | Recommended path |
|
|
155
|
+
| --- | --- | --- | --- |
|
|
156
|
+
| Claude Code | stdio | Saved config or API token | [docs/client-setup.md#claude-code](docs/client-setup.md#claude-code) |
|
|
157
|
+
| Claude Desktop | stdio | Saved config or API token | [docs/client-setup.md#claude-desktop](docs/client-setup.md#claude-desktop) |
|
|
158
|
+
| Codex CLI | stdio | Saved config or API token | [docs/client-setup.md#codex-cli](docs/client-setup.md#codex-cli) |
|
|
159
|
+
| Cursor | stdio | Saved config or API token | [docs/client-setup.md#cursor](docs/client-setup.md#cursor) |
|
|
160
|
+
| Containerized remote deployment | HTTP | Bearer token or OAuth | [docs/getting-started.md#path-c-run-from-the-docker-image](docs/getting-started.md#path-c-run-from-the-docker-image) |
|
|
161
|
+
| Remote MCP clients | HTTP | Bearer token or OAuth | [docs/configuration-and-deployment.md#http-mode](docs/configuration-and-deployment.md#http-mode) |
|
|
162
|
+
| AFFiNE Cloud | stdio or HTTP | API token | [docs/configuration-and-deployment.md#auth-strategy-matrix](docs/configuration-and-deployment.md#auth-strategy-matrix) |
|
|
163
|
+
| Self-hosted AFFiNE | stdio or HTTP | API token, cookie, or email/password | [docs/configuration-and-deployment.md#auth-strategy-matrix](docs/configuration-and-deployment.md#auth-strategy-matrix) |
|
|
164
|
+
|
|
165
|
+
## Tool Surface
|
|
166
|
+
|
|
167
|
+
`tool-manifest.json` is the source of truth for canonical tool names. The MCP server exposes those tools through `tools/list` and `tools/call`.
|
|
168
|
+
|
|
169
|
+
Domains:
|
|
170
|
+
|
|
171
|
+
- Workspace: create, inspect, update, delete, and traverse workspaces
|
|
172
|
+
- Organization: collections, collection-rule sync, workspace blueprints, and experimental organize or folder helpers
|
|
173
|
+
- Documents: search, read, create, publish, move, tag, custom properties, import/export, semantic composition, template inspection and native instantiation, capability and fidelity reporting, and block-level mutation
|
|
174
|
+
- Databases: create columns, add rows, update rows, inspect schema, and compose database structures from intent
|
|
175
|
+
- Comments: list, create, update, delete, and resolve
|
|
176
|
+
- History: version history listing
|
|
177
|
+
- Users and tokens: current user, sign-in, profile/settings, personal access tokens
|
|
178
|
+
- Notifications: list and mark notifications as read
|
|
179
|
+
- Blob storage: upload, delete, and cleanup blobs
|
|
180
|
+
|
|
181
|
+
Use `AFFINE_TOOL_PROFILE=read_only`, `core`, or `authoring` when a deployment should expose a smaller surface than the complete `full` default. You can also combine profiles with `AFFINE_DISABLED_GROUPS` such as `docs.database`, `destructive`, or `admin` for finer control.
|
|
182
|
+
|
|
183
|
+
For the grouped catalog, notes, and operational caveats, see [docs/tool-reference.md](docs/tool-reference.md).
|
|
184
|
+
|
|
185
|
+
## Documentation Map
|
|
186
|
+
|
|
187
|
+
| Document | Purpose |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| [docs/getting-started.md](docs/getting-started.md) | First-run setup paths and verification |
|
|
190
|
+
| [docs/client-setup.md](docs/client-setup.md) | Client-specific configuration snippets and tips |
|
|
191
|
+
| [docs/configuration-and-deployment.md](docs/configuration-and-deployment.md) | Environment variables, auth modes, Docker, HTTP mode, and deployment guidance |
|
|
192
|
+
| [docs/workflow-recipes.md](docs/workflow-recipes.md) | End-to-end workflows and example tool sequences |
|
|
193
|
+
| [docs/tool-reference.md](docs/tool-reference.md) | Tool catalog grouped by domain |
|
|
194
|
+
| [docs/edgeless-canvas-cookbook.md](docs/edgeless-canvas-cookbook.md) | Edgeless canvas layout helpers and surface elements, worked end-to-end |
|
|
195
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | Contributor workflow |
|
|
196
|
+
| [SECURITY.md](SECURITY.md) | Security reporting |
|
|
197
|
+
|
|
198
|
+
## Verify Your Setup
|
|
199
|
+
|
|
200
|
+
Useful CLI commands:
|
|
201
|
+
|
|
202
|
+
- `affine-mcp status` - test the effective configuration
|
|
203
|
+
- `affine-mcp status --json` - machine-readable status output
|
|
204
|
+
- `affine-mcp doctor` - diagnose config and connectivity issues
|
|
205
|
+
- `affine-mcp show-config` - print the effective config with secrets redacted
|
|
206
|
+
- `affine-mcp config-path` - print the config file path
|
|
207
|
+
- `affine-mcp snippet <claude|cursor|codex|all> [--env]` - generate ready-to-paste client config
|
|
208
|
+
- `affine-mcp logout` - remove stored credentials
|
|
209
|
+
|
|
210
|
+
For common failures, see:
|
|
211
|
+
|
|
212
|
+
- [docs/getting-started.md#common-first-run-failures](docs/getting-started.md#common-first-run-failures)
|
|
213
|
+
- [docs/configuration-and-deployment.md#deployment-checklist](docs/configuration-and-deployment.md#deployment-checklist)
|
|
214
|
+
|
|
215
|
+
## Security and Scope
|
|
216
|
+
|
|
217
|
+
- Never commit secrets or long-lived tokens
|
|
218
|
+
- Prefer API tokens over cookies or passwords in production
|
|
219
|
+
- Use HTTPS for non-local deployments
|
|
220
|
+
- Rotate access tokens regularly
|
|
221
|
+
- Restrict exposed tools with `AFFINE_DISABLED_GROUPS` and `AFFINE_DISABLED_TOOLS` for least-privilege setups
|
|
222
|
+
- Use `/healthz` and `/readyz` when running the HTTP server behind a container platform or load balancer
|
|
223
|
+
|
|
224
|
+
## Development
|
|
225
|
+
|
|
226
|
+
Run the main quality gates before opening a PR:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
npm run build
|
|
230
|
+
npm run test:tool-manifest
|
|
231
|
+
npm run pack:check
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Additional validation:
|
|
235
|
+
|
|
236
|
+
- `npm run test:comprehensive` boots a local Docker AFFiNE stack and validates the tool surface
|
|
237
|
+
- `npm run test:e2e` runs Docker, MCP, and Playwright together
|
|
238
|
+
- `npm run test:playwright` runs the Playwright suite only
|
|
239
|
+
- Focused runners for the new high-level tool surface include `npm run test:create-placement`, `npm run test:capabilities-fidelity`, `npm run test:native-template`, `node tests/test-database-intent.mjs`, `node tests/test-semantic-page-composer.mjs`, `node tests/test-structured-receipts.mjs`, `node tests/test-organize-tools.mjs`, and `node tests/test-supporting-tools.mjs`
|
|
240
|
+
|
|
241
|
+
Local clone flow:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
git clone https://github.com/dawncr0w/affine-mcp-server.git
|
|
245
|
+
cd affine-mcp-server
|
|
246
|
+
npm install
|
|
247
|
+
npm run build
|
|
248
|
+
node dist/index.js
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Release Notes
|
|
252
|
+
|
|
253
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
|
254
|
+
- [RELEASE_NOTES.md](RELEASE_NOTES.md)
|
|
255
|
+
- [GitHub Releases](https://github.com/dawncr0w/affine-mcp-server/releases)
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT License - see [LICENSE](LICENSE).
|
|
260
|
+
|
|
261
|
+
## Support
|
|
262
|
+
|
|
263
|
+
- Open an issue on [GitHub](https://github.com/dawncr0w/affine-mcp-server/issues)
|
|
264
|
+
- Review AFFiNE product documentation at [docs.affine.pro](https://docs.affine.pro)
|
|
265
|
+
|
|
266
|
+
## Acknowledgments
|
|
267
|
+
|
|
268
|
+
- Built for the [AFFiNE](https://affine.pro) knowledge base platform
|
|
269
|
+
- Uses the [Model Context Protocol](https://modelcontextprotocol.io) specification
|
|
270
|
+
- Powered by [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)
|
package/bin/affine-mcp
ADDED
package/dist/auth.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
2
|
+
const AUTH_FETCH_TIMEOUT_MS = 30_000;
|
|
3
|
+
function extractCookiePairs(setCookies) {
|
|
4
|
+
const pairs = [];
|
|
5
|
+
for (const sc of setCookies) {
|
|
6
|
+
const first = sc.split(";")[0];
|
|
7
|
+
if (first)
|
|
8
|
+
pairs.push(first.trim());
|
|
9
|
+
}
|
|
10
|
+
return pairs.join("; ");
|
|
11
|
+
}
|
|
12
|
+
/** Reject cookie values containing CR/LF to prevent header injection. */
|
|
13
|
+
function assertNoCRLF(value, label) {
|
|
14
|
+
if (/[\r\n]/.test(value)) {
|
|
15
|
+
throw new Error(`${label} contains illegal CR/LF characters`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function loginWithPassword(baseUrl, email, password) {
|
|
19
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/auth/sign-in`;
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const timer = setTimeout(() => controller.abort(), AUTH_FETCH_TIMEOUT_MS);
|
|
22
|
+
let res;
|
|
23
|
+
try {
|
|
24
|
+
res = await fetch(url, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({ email, password }),
|
|
28
|
+
signal: controller.signal,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err.name === "AbortError")
|
|
33
|
+
throw new Error(`Sign-in request timed out after ${AUTH_FETCH_TIMEOUT_MS / 1000}s`);
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
}
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const raw = await res.text().catch(() => "");
|
|
41
|
+
const sanitized = raw.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
|
|
42
|
+
const truncated = sanitized.length > 200 ? sanitized.slice(0, 200) + "..." : sanitized;
|
|
43
|
+
throw new Error(`Sign-in failed: ${res.status} ${truncated}`);
|
|
44
|
+
}
|
|
45
|
+
const anyHeaders = res.headers;
|
|
46
|
+
let setCookies = [];
|
|
47
|
+
if (typeof anyHeaders.getSetCookie === "function") {
|
|
48
|
+
setCookies = anyHeaders.getSetCookie();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const sc = res.headers.get("set-cookie");
|
|
52
|
+
if (sc)
|
|
53
|
+
setCookies = [sc];
|
|
54
|
+
}
|
|
55
|
+
if (!setCookies.length) {
|
|
56
|
+
throw new Error("Sign-in succeeded but no Set-Cookie received");
|
|
57
|
+
}
|
|
58
|
+
const cookieHeader = extractCookiePairs(setCookies);
|
|
59
|
+
assertNoCRLF(cookieHeader, "Cookie header from sign-in");
|
|
60
|
+
return { cookieHeader };
|
|
61
|
+
}
|