@atproto/lex-server 0.0.11 → 0.0.13
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 +27 -0
- package/README.md +38 -21
- package/dist/errors.d.ts +28 -58
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +72 -72
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/dist/{lex-server.d.ts → lex-router.d.ts} +55 -21
- package/dist/lex-router.d.ts.map +1 -0
- package/dist/{lex-server.js → lex-router.js} +169 -73
- package/dist/lex-router.js.map +1 -0
- package/dist/lib/drain-websocket.d.ts +7 -0
- package/dist/lib/drain-websocket.d.ts.map +1 -1
- package/dist/lib/drain-websocket.js +11 -0
- package/dist/lib/drain-websocket.js.map +1 -1
- package/dist/lib/www-authenticate.d.ts +4 -3
- package/dist/lib/www-authenticate.d.ts.map +1 -1
- package/dist/lib/www-authenticate.js +29 -16
- package/dist/lib/www-authenticate.js.map +1 -1
- package/dist/nodejs.d.ts +1 -1
- package/dist/nodejs.d.ts.map +1 -1
- package/dist/nodejs.js +1 -1
- package/dist/nodejs.js.map +1 -1
- package/dist/service-auth.d.ts +1 -1
- package/dist/service-auth.d.ts.map +1 -1
- package/dist/service-auth.js.map +1 -1
- package/package.json +9 -8
- package/src/errors.test.ts +262 -0
- package/src/errors.ts +103 -78
- package/src/index.ts +1 -7
- package/src/{lex-server.test.ts → lex-router.test.ts} +591 -24
- package/src/{lex-server.ts → lex-router.ts} +275 -119
- package/src/lib/drain-websocket.ts +11 -0
- package/src/lib/www-authenticate.test.ts +134 -0
- package/src/lib/www-authenticate.ts +36 -17
- package/src/nodejs.ts +2 -2
- package/src/service-auth.ts +1 -1
- package/dist/lex-server.d.ts.map +0 -1
- package/dist/lex-server.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @atproto/lex-server
|
|
2
2
|
|
|
3
|
+
## 0.0.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`5a2f884`](https://github.com/bluesky-social/atproto/commit/5a2f8847efd91252971fa243d21bd52ada7aa8f4), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`112b159`](https://github.com/bluesky-social/atproto/commit/112b159ec293a5c3fff41237474a3788fc47f9ca), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe), [`3dc3791`](https://github.com/bluesky-social/atproto/commit/3dc37915436dec7e18c7dc9dcf01b72cad53fdbe)]:
|
|
8
|
+
- @atproto/lex-client@0.0.16
|
|
9
|
+
- @atproto/lex-schema@0.0.15
|
|
10
|
+
|
|
11
|
+
## 0.0.12
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#4688](https://github.com/bluesky-social/atproto/pull/4688) [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add support for `/xrpc/_health` requests
|
|
16
|
+
|
|
17
|
+
- [#4688](https://github.com/bluesky-social/atproto/pull/4688) [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update error management to be more aligned with the way errors work in `@atproto/xrpc` and `@atproto/xrpc-server`
|
|
18
|
+
|
|
19
|
+
- [#4688](https://github.com/bluesky-social/atproto/pull/4688) [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `LexServerError` error class to encode errors destined to be returned as XRPC responses by an XRPC server
|
|
20
|
+
|
|
21
|
+
- [#4688](https://github.com/bluesky-social/atproto/pull/4688) [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Allow defining a custom `fallback` handler for non XRPC HTTP calls
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [[`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`f7c2610`](https://github.com/bluesky-social/atproto/commit/f7c26103a6d4e24e5bedbb6fd908be140420e0dd), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7), [`52834ab`](https://github.com/bluesky-social/atproto/commit/52834aba182da8df3611fd9dff924e6c6a3973a7)]:
|
|
24
|
+
- @atproto/lex-schema@0.0.14
|
|
25
|
+
- @atproto/lex-data@0.0.13
|
|
26
|
+
- @atproto/lex-client@0.0.15
|
|
27
|
+
- @atproto/lex-cbor@0.0.14
|
|
28
|
+
- @atproto/lex-json@0.0.13
|
|
29
|
+
|
|
3
30
|
## 0.0.11
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ await serve(router, { port: 3000 })
|
|
|
64
64
|
- [Custom Authentication](#custom-authentication)
|
|
65
65
|
- [WWW-Authenticate Headers](#www-authenticate-headers)
|
|
66
66
|
- [Error Handling](#error-handling)
|
|
67
|
-
- [
|
|
67
|
+
- [LexServerError](#lexservererror)
|
|
68
68
|
- [LexServerAuthError](#lexserverautherror)
|
|
69
69
|
- [Error Handler Callback](#error-handler-callback)
|
|
70
70
|
- [Node.js Server](#nodejs-server)
|
|
@@ -100,7 +100,7 @@ lex build
|
|
|
100
100
|
**3. Create a router and add handlers**
|
|
101
101
|
|
|
102
102
|
```typescript
|
|
103
|
-
import { LexRouter,
|
|
103
|
+
import { LexRouter, LexServerError } from '@atproto/lex-server'
|
|
104
104
|
import { serve, upgradeWebSocket } from '@atproto/lex-server/nodejs'
|
|
105
105
|
import * as app from './lexicons/app.js'
|
|
106
106
|
|
|
@@ -110,7 +110,10 @@ const router = new LexRouter({ upgradeWebSocket })
|
|
|
110
110
|
router.add(app.bsky.actor.getProfile, async ({ params }) => {
|
|
111
111
|
const profile = await db.getProfile(params.actor)
|
|
112
112
|
if (!profile) {
|
|
113
|
-
throw new
|
|
113
|
+
throw new LexServerError(404, {
|
|
114
|
+
error: 'NotFound',
|
|
115
|
+
message: 'Profile not found',
|
|
116
|
+
})
|
|
114
117
|
}
|
|
115
118
|
return { body: profile }
|
|
116
119
|
})
|
|
@@ -134,7 +137,7 @@ const router = new LexRouter({
|
|
|
134
137
|
// Required for WebSocket subscriptions (Node.js)
|
|
135
138
|
upgradeWebSocket,
|
|
136
139
|
|
|
137
|
-
// Optional:
|
|
140
|
+
// Optional: Add logging or error reporting for unexpected errors in handlers
|
|
138
141
|
onHandlerError: ({ error, request, method }) => {
|
|
139
142
|
console.error(`Error in ${method.nsid}:`, error)
|
|
140
143
|
},
|
|
@@ -359,7 +362,7 @@ The auth function:
|
|
|
359
362
|
|
|
360
363
|
1. Is called **before** parsing the request body
|
|
361
364
|
2. Receives `params`, `request`, and `connection` info
|
|
362
|
-
3. Should throw `
|
|
365
|
+
3. Should throw `LexServerError` (or `LexServerAuthError`) on failure
|
|
363
366
|
4. Returns credentials that are passed to the handler
|
|
364
367
|
|
|
365
368
|
### WWW-Authenticate Headers
|
|
@@ -391,19 +394,30 @@ throw new LexServerAuthError('AuthenticationRequired', 'Auth required', {
|
|
|
391
394
|
|
|
392
395
|
## Error Handling
|
|
393
396
|
|
|
394
|
-
###
|
|
397
|
+
### LexServerError
|
|
395
398
|
|
|
396
|
-
Throw `
|
|
399
|
+
Throw `LexServerError` to return structured XRPC error responses:
|
|
397
400
|
|
|
398
401
|
```typescript
|
|
399
|
-
import {
|
|
402
|
+
import { LexServerError } from '@atproto/lex-server'
|
|
400
403
|
|
|
401
404
|
router.add(app.bsky.actor.getProfile, async ({ params }) => {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
+
try {
|
|
406
|
+
const profile = await db.getProfile(params.actor)
|
|
407
|
+
return { body: profile }
|
|
408
|
+
} catch (cause) {
|
|
409
|
+
throw new LexServerError(
|
|
410
|
+
404,
|
|
411
|
+
{
|
|
412
|
+
error: 'NotFound',
|
|
413
|
+
message: 'Profile not found',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
'cache-control': 'max-age=600',
|
|
417
|
+
},
|
|
418
|
+
{ cause },
|
|
419
|
+
)
|
|
405
420
|
}
|
|
406
|
-
return { body: profile }
|
|
407
421
|
})
|
|
408
422
|
```
|
|
409
423
|
|
|
@@ -418,7 +432,7 @@ Error responses follow the XRPC format:
|
|
|
418
432
|
|
|
419
433
|
### LexServerAuthError
|
|
420
434
|
|
|
421
|
-
`LexServerAuthError` extends `
|
|
435
|
+
`LexServerAuthError` extends `LexServerError` with `WWW-Authenticate` header support:
|
|
422
436
|
|
|
423
437
|
```typescript
|
|
424
438
|
import { LexServerAuthError } from '@atproto/lex-server'
|
|
@@ -510,7 +524,7 @@ import { toRequestListener } from '@atproto/lex-server/nodejs'
|
|
|
510
524
|
const app = express()
|
|
511
525
|
|
|
512
526
|
// Mount the XRPC router
|
|
513
|
-
app.use('/xrpc', toRequestListener(router.
|
|
527
|
+
app.use('/xrpc', toRequestListener(router.fetch))
|
|
514
528
|
|
|
515
529
|
app.listen(3000)
|
|
516
530
|
```
|
|
@@ -530,7 +544,8 @@ const router = new LexRouter({ upgradeWebSocket })
|
|
|
530
544
|
|
|
531
545
|
### Custom Response Objects
|
|
532
546
|
|
|
533
|
-
Return a `Response` object for full control over the response
|
|
547
|
+
Return a `Response` object for full control over the response (disables any kind
|
|
548
|
+
of validation of the body):
|
|
534
549
|
|
|
535
550
|
```typescript
|
|
536
551
|
router.add(schema, async ({ params }) => {
|
|
@@ -538,13 +553,15 @@ router.add(schema, async ({ params }) => {
|
|
|
538
553
|
return Response.redirect('https://example.com', 302)
|
|
539
554
|
}
|
|
540
555
|
|
|
541
|
-
return
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
556
|
+
return Response.json(
|
|
557
|
+
{ custom: true },
|
|
558
|
+
{
|
|
559
|
+
status: 201,
|
|
560
|
+
headers: {
|
|
561
|
+
'X-Custom-Header': 'value',
|
|
562
|
+
},
|
|
546
563
|
},
|
|
547
|
-
|
|
564
|
+
)
|
|
548
565
|
})
|
|
549
566
|
```
|
|
550
567
|
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
import { LexError, LexErrorCode } from '@atproto/lex-data';
|
|
1
|
+
import { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data';
|
|
2
2
|
import { WWWAuthenticate } from './lib/www-authenticate.js';
|
|
3
|
-
export
|
|
3
|
+
export { LexError };
|
|
4
|
+
export type { LexErrorCode, LexErrorData, WWWAuthenticate };
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for representing errors that should be converted to XRPC
|
|
7
|
+
* error responses.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LexServerError<N extends LexErrorCode = LexErrorCode> extends LexError<N> {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly body: LexErrorData<N>;
|
|
12
|
+
name: string;
|
|
13
|
+
readonly headers?: Headers;
|
|
14
|
+
constructor(status: number, body: LexErrorData<N>, headers?: HeadersInit, options?: ErrorOptions);
|
|
15
|
+
toJSON(): LexErrorData<N>;
|
|
16
|
+
toResponse(): Response;
|
|
17
|
+
static from(cause: unknown): LexServerError;
|
|
18
|
+
}
|
|
4
19
|
/**
|
|
5
20
|
* Error class for authentication failures in XRPC server handlers.
|
|
6
21
|
*
|
|
@@ -21,22 +36,8 @@ export type { WWWAuthenticate };
|
|
|
21
36
|
* { Bearer: { error: 'InvalidToken', realm: 'api.example.com' } }
|
|
22
37
|
* )
|
|
23
38
|
* ```
|
|
24
|
-
*
|
|
25
|
-
* @example Converting from a LexError
|
|
26
|
-
* ```typescript
|
|
27
|
-
* try {
|
|
28
|
-
* await validateToken(token)
|
|
29
|
-
* } catch (error) {
|
|
30
|
-
* if (error instanceof LexError) {
|
|
31
|
-
* throw LexServerAuthError.from(error, {
|
|
32
|
-
* Bearer: { error: 'InvalidToken' }
|
|
33
|
-
* })
|
|
34
|
-
* }
|
|
35
|
-
* throw error
|
|
36
|
-
* }
|
|
37
|
-
* ```
|
|
38
39
|
*/
|
|
39
|
-
export declare class LexServerAuthError<N extends LexErrorCode = LexErrorCode> extends
|
|
40
|
+
export declare class LexServerAuthError<N extends LexErrorCode = LexErrorCode> extends LexServerError<N> {
|
|
40
41
|
readonly wwwAuthenticate: WWWAuthenticate;
|
|
41
42
|
name: string;
|
|
42
43
|
/**
|
|
@@ -48,42 +49,6 @@ export declare class LexServerAuthError<N extends LexErrorCode = LexErrorCode> e
|
|
|
48
49
|
* @param options - Standard Error options including `cause`
|
|
49
50
|
*/
|
|
50
51
|
constructor(error: N, message: string, wwwAuthenticate?: WWWAuthenticate, options?: ErrorOptions);
|
|
51
|
-
/**
|
|
52
|
-
* Gets the formatted WWW-Authenticate header value.
|
|
53
|
-
*
|
|
54
|
-
* @returns The formatted header string for the 401 response
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```typescript
|
|
58
|
-
* const error = new LexServerAuthError('AuthenticationRequired', 'Token required', {
|
|
59
|
-
* Bearer: { realm: 'api.example.com', error: 'MissingToken' }
|
|
60
|
-
* })
|
|
61
|
-
* console.log(error.wwwAuthenticateHeader)
|
|
62
|
-
* // Output: 'Bearer realm="api.example.com", error="MissingToken"'
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
get wwwAuthenticateHeader(): string;
|
|
66
|
-
/**
|
|
67
|
-
* Converts the error to a JSON representation suitable for response bodies.
|
|
68
|
-
*
|
|
69
|
-
* If the error was created from another LexError (via `from()`), returns
|
|
70
|
-
* the original error's JSON representation.
|
|
71
|
-
*
|
|
72
|
-
* @returns JSON object with error code and message
|
|
73
|
-
*/
|
|
74
|
-
toJSON(): import("@atproto/lex-data").LexErrorData<any>;
|
|
75
|
-
/**
|
|
76
|
-
* Converts the error to an HTTP 401 Response with WWW-Authenticate header.
|
|
77
|
-
*
|
|
78
|
-
* The response includes:
|
|
79
|
-
* - Status code 401 (Unauthorized)
|
|
80
|
-
* - WWW-Authenticate header (if parameters were provided)
|
|
81
|
-
* - Access-Control-Expose-Headers for CORS compatibility
|
|
82
|
-
* - JSON body with error details
|
|
83
|
-
*
|
|
84
|
-
* @returns HTTP Response object ready to be sent to the client
|
|
85
|
-
*/
|
|
86
|
-
toResponse(): Response;
|
|
87
52
|
/**
|
|
88
53
|
* Creates a LexServerAuthError from an existing LexError.
|
|
89
54
|
*
|
|
@@ -96,12 +61,17 @@ export declare class LexServerAuthError<N extends LexErrorCode = LexErrorCode> e
|
|
|
96
61
|
*
|
|
97
62
|
* @example
|
|
98
63
|
* ```typescript
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
64
|
+
* function authenticate(token: string): Promise<User> {
|
|
65
|
+
* try {
|
|
66
|
+
* return await validateToken(token)
|
|
67
|
+
* } catch (cause) {
|
|
68
|
+
* throw LexServerAuthError.from(cause, {
|
|
69
|
+
* Bearer: { error: 'InvalidToken' }
|
|
70
|
+
* })
|
|
71
|
+
* }
|
|
72
|
+
* }
|
|
103
73
|
* ```
|
|
104
74
|
*/
|
|
105
|
-
static from(cause:
|
|
75
|
+
static from(cause: unknown, wwwAuthenticate?: WWWAuthenticate): LexServerAuthError;
|
|
106
76
|
}
|
|
107
77
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAExE,OAAO,EACL,eAAe,EAEhB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,QAAQ,EAAE,CAAA;AACnB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,CAAA;AAE3D;;;GAGG;AACH,qBAAa,cAAc,CACzB,CAAC,SAAS,YAAY,GAAG,YAAY,CACrC,SAAQ,QAAQ,CAAC,CAAC,CAAC;IAMjB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;IANhC,IAAI,SAAmB;IAEvB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;gBAGf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,YAAY;IAMf,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC;IAI3B,UAAU,IAAI,QAAQ;IAM7B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc;CAgC5C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,kBAAkB,CAC7B,CAAC,SAAS,YAAY,GAAG,YAAY,CACrC,SAAQ,cAAc,CAAC,CAAC,CAAC;IAcvB,QAAQ,CAAC,eAAe,EAAE,eAAe;IAb3C,IAAI,SAAuB;IAE3B;;;;;;;OAOG;gBAED,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,MAAM,EACN,eAAe,GAAE,eAAoB,EAC9C,OAAO,CAAC,EAAE,YAAY;IAWxB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAC,IAAI,CACT,KAAK,EAAE,OAAO,EACd,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB;CAqBtB"}
|
package/dist/errors.js
CHANGED
|
@@ -1,8 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LexServerAuthError = void 0;
|
|
3
|
+
exports.LexServerAuthError = exports.LexServerError = exports.LexError = void 0;
|
|
4
|
+
const lex_client_1 = require("@atproto/lex-client");
|
|
4
5
|
const lex_data_1 = require("@atproto/lex-data");
|
|
6
|
+
Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return lex_data_1.LexError; } });
|
|
7
|
+
const lex_schema_1 = require("@atproto/lex-schema");
|
|
5
8
|
const www_authenticate_js_1 = require("./lib/www-authenticate.js");
|
|
9
|
+
/**
|
|
10
|
+
* Base error class for representing errors that should be converted to XRPC
|
|
11
|
+
* error responses.
|
|
12
|
+
*/
|
|
13
|
+
class LexServerError extends lex_data_1.LexError {
|
|
14
|
+
status;
|
|
15
|
+
body;
|
|
16
|
+
name = 'LexServerError';
|
|
17
|
+
headers;
|
|
18
|
+
constructor(status, body, headers, options) {
|
|
19
|
+
super(body.error, body.message, options);
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.body = body;
|
|
22
|
+
this.headers = headers ? new Headers(headers) : undefined;
|
|
23
|
+
}
|
|
24
|
+
toJSON() {
|
|
25
|
+
return this.body;
|
|
26
|
+
}
|
|
27
|
+
toResponse() {
|
|
28
|
+
const { status, headers } = this;
|
|
29
|
+
// @NOTE using this.toJSON() instead of this.body to allow overrides in subclasses
|
|
30
|
+
return Response.json(this.toJSON(), { status, headers });
|
|
31
|
+
}
|
|
32
|
+
static from(cause) {
|
|
33
|
+
if (cause instanceof LexServerError) {
|
|
34
|
+
return cause;
|
|
35
|
+
}
|
|
36
|
+
// Convert @atproto/lex-client errors to downstream LexServerError
|
|
37
|
+
if (cause instanceof lex_client_1.XrpcError) {
|
|
38
|
+
const { status, body, headers } = cause.toDownstreamError();
|
|
39
|
+
return new LexServerError(status, body, headers, { cause });
|
|
40
|
+
}
|
|
41
|
+
// Convert @atproto/lex-schema validation errors to 400 Bad Request
|
|
42
|
+
if (cause instanceof lex_schema_1.LexValidationError) {
|
|
43
|
+
return new LexServerError(400, cause.toJSON(), undefined, {
|
|
44
|
+
cause,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Any other error is treated as a generic 500 Internal Server Error
|
|
48
|
+
if (cause instanceof lex_data_1.LexError) {
|
|
49
|
+
return new LexServerError(500, cause.toJSON(), undefined, {
|
|
50
|
+
cause,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return new LexServerError(500, { error: 'InternalServerError', message: 'An internal error occurred' }, undefined, { cause });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.LexServerError = LexServerError;
|
|
6
57
|
/**
|
|
7
58
|
* Error class for authentication failures in XRPC server handlers.
|
|
8
59
|
*
|
|
@@ -23,22 +74,8 @@ const www_authenticate_js_1 = require("./lib/www-authenticate.js");
|
|
|
23
74
|
* { Bearer: { error: 'InvalidToken', realm: 'api.example.com' } }
|
|
24
75
|
* )
|
|
25
76
|
* ```
|
|
26
|
-
*
|
|
27
|
-
* @example Converting from a LexError
|
|
28
|
-
* ```typescript
|
|
29
|
-
* try {
|
|
30
|
-
* await validateToken(token)
|
|
31
|
-
* } catch (error) {
|
|
32
|
-
* if (error instanceof LexError) {
|
|
33
|
-
* throw LexServerAuthError.from(error, {
|
|
34
|
-
* Bearer: { error: 'InvalidToken' }
|
|
35
|
-
* })
|
|
36
|
-
* }
|
|
37
|
-
* throw error
|
|
38
|
-
* }
|
|
39
|
-
* ```
|
|
40
77
|
*/
|
|
41
|
-
class LexServerAuthError extends
|
|
78
|
+
class LexServerAuthError extends LexServerError {
|
|
42
79
|
wwwAuthenticate;
|
|
43
80
|
name = 'LexServerAuthError';
|
|
44
81
|
/**
|
|
@@ -50,58 +87,14 @@ class LexServerAuthError extends lex_data_1.LexError {
|
|
|
50
87
|
* @param options - Standard Error options including `cause`
|
|
51
88
|
*/
|
|
52
89
|
constructor(error, message, wwwAuthenticate = {}, options) {
|
|
53
|
-
|
|
54
|
-
this.wwwAuthenticate = wwwAuthenticate;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Gets the formatted WWW-Authenticate header value.
|
|
58
|
-
*
|
|
59
|
-
* @returns The formatted header string for the 401 response
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* ```typescript
|
|
63
|
-
* const error = new LexServerAuthError('AuthenticationRequired', 'Token required', {
|
|
64
|
-
* Bearer: { realm: 'api.example.com', error: 'MissingToken' }
|
|
65
|
-
* })
|
|
66
|
-
* console.log(error.wwwAuthenticateHeader)
|
|
67
|
-
* // Output: 'Bearer realm="api.example.com", error="MissingToken"'
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
get wwwAuthenticateHeader() {
|
|
71
|
-
return (0, www_authenticate_js_1.formatWWWAuthenticateHeader)(this.wwwAuthenticate);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Converts the error to a JSON representation suitable for response bodies.
|
|
75
|
-
*
|
|
76
|
-
* If the error was created from another LexError (via `from()`), returns
|
|
77
|
-
* the original error's JSON representation.
|
|
78
|
-
*
|
|
79
|
-
* @returns JSON object with error code and message
|
|
80
|
-
*/
|
|
81
|
-
toJSON() {
|
|
82
|
-
const { cause } = this;
|
|
83
|
-
return cause instanceof lex_data_1.LexError ? cause.toJSON() : super.toJSON();
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Converts the error to an HTTP 401 Response with WWW-Authenticate header.
|
|
87
|
-
*
|
|
88
|
-
* The response includes:
|
|
89
|
-
* - Status code 401 (Unauthorized)
|
|
90
|
-
* - WWW-Authenticate header (if parameters were provided)
|
|
91
|
-
* - Access-Control-Expose-Headers for CORS compatibility
|
|
92
|
-
* - JSON body with error details
|
|
93
|
-
*
|
|
94
|
-
* @returns HTTP Response object ready to be sent to the client
|
|
95
|
-
*/
|
|
96
|
-
toResponse() {
|
|
97
|
-
const { wwwAuthenticateHeader } = this;
|
|
98
|
-
const headers = wwwAuthenticateHeader
|
|
90
|
+
const headers = Object.keys(wwwAuthenticate).length
|
|
99
91
|
? new Headers({
|
|
100
|
-
'WWW-Authenticate':
|
|
92
|
+
'WWW-Authenticate': (0, www_authenticate_js_1.formatWWWAuthenticateHeader)(wwwAuthenticate),
|
|
101
93
|
'Access-Control-Expose-Headers': 'WWW-Authenticate', // CORS
|
|
102
94
|
})
|
|
103
95
|
: undefined;
|
|
104
|
-
|
|
96
|
+
super(401, { error, message }, headers, options);
|
|
97
|
+
this.wwwAuthenticate = wwwAuthenticate;
|
|
105
98
|
}
|
|
106
99
|
/**
|
|
107
100
|
* Creates a LexServerAuthError from an existing LexError.
|
|
@@ -115,18 +108,25 @@ class LexServerAuthError extends lex_data_1.LexError {
|
|
|
115
108
|
*
|
|
116
109
|
* @example
|
|
117
110
|
* ```typescript
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
111
|
+
* function authenticate(token: string): Promise<User> {
|
|
112
|
+
* try {
|
|
113
|
+
* return await validateToken(token)
|
|
114
|
+
* } catch (cause) {
|
|
115
|
+
* throw LexServerAuthError.from(cause, {
|
|
116
|
+
* Bearer: { error: 'InvalidToken' }
|
|
117
|
+
* })
|
|
118
|
+
* }
|
|
119
|
+
* }
|
|
122
120
|
* ```
|
|
123
121
|
*/
|
|
124
122
|
static from(cause, wwwAuthenticate) {
|
|
125
|
-
if (cause instanceof LexServerAuthError)
|
|
123
|
+
if (cause instanceof LexServerAuthError) {
|
|
126
124
|
return cause;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
}
|
|
126
|
+
if (cause instanceof lex_data_1.LexError) {
|
|
127
|
+
return new LexServerAuthError(cause.error, cause.message, wwwAuthenticate, { cause });
|
|
128
|
+
}
|
|
129
|
+
return new LexServerAuthError('AuthenticationRequired', 'Authentication failed', wwwAuthenticate, { cause });
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
exports.LexServerAuthError = LexServerAuthError;
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["import { XrpcError } from '@atproto/lex-client'\nimport { LexError, LexErrorCode, LexErrorData } from '@atproto/lex-data'\nimport { LexValidationError } from '@atproto/lex-schema'\nimport {\n WWWAuthenticate,\n formatWWWAuthenticateHeader,\n} from './lib/www-authenticate.js'\n\nexport { LexError }\nexport type { LexErrorCode, LexErrorData, WWWAuthenticate }\n\n/**\n * Base error class for representing errors that should be converted to XRPC\n * error responses.\n */\nexport class LexServerError<\n N extends LexErrorCode = LexErrorCode,\n> extends LexError<N> {\n name = 'LexServerError'\n\n readonly headers?: Headers\n\n constructor(\n readonly status: number,\n readonly body: LexErrorData<N>,\n headers?: HeadersInit,\n options?: ErrorOptions,\n ) {\n super(body.error, body.message, options)\n this.headers = headers ? new Headers(headers) : undefined\n }\n\n override toJSON(): LexErrorData<N> {\n return this.body\n }\n\n public toResponse(): Response {\n const { status, headers } = this\n // @NOTE using this.toJSON() instead of this.body to allow overrides in subclasses\n return Response.json(this.toJSON(), { status, headers })\n }\n\n static from(cause: unknown): LexServerError {\n if (cause instanceof LexServerError) {\n return cause\n }\n\n // Convert @atproto/lex-client errors to downstream LexServerError\n if (cause instanceof XrpcError) {\n const { status, body, headers } = cause.toDownstreamError()\n return new LexServerError(status, body, headers, { cause })\n }\n\n // Convert @atproto/lex-schema validation errors to 400 Bad Request\n if (cause instanceof LexValidationError) {\n return new LexServerError(400, cause.toJSON(), undefined, {\n cause,\n })\n }\n\n // Any other error is treated as a generic 500 Internal Server Error\n if (cause instanceof LexError) {\n return new LexServerError(500, cause.toJSON(), undefined, {\n cause,\n })\n }\n\n return new LexServerError(\n 500,\n { error: 'InternalServerError', message: 'An internal error occurred' },\n undefined,\n { cause },\n )\n }\n}\n\n/**\n * Error class for authentication failures in XRPC server handlers.\n *\n * Extends {@link LexError} to include WWW-Authenticate header support,\n * which is required by HTTP authentication standards (RFC 7235).\n * The error automatically generates the appropriate 401 response with\n * the WWW-Authenticate header when converted to a Response.\n *\n * @typeParam N - The Lexicon error code type\n *\n * @example Throwing an auth error\n * ```typescript\n * import { LexServerAuthError } from '@atproto/lex-server'\n *\n * throw new LexServerAuthError(\n * 'AuthenticationRequired',\n * 'Invalid or expired token',\n * { Bearer: { error: 'InvalidToken', realm: 'api.example.com' } }\n * )\n * ```\n */\nexport class LexServerAuthError<\n N extends LexErrorCode = LexErrorCode,\n> extends LexServerError<N> {\n name = 'LexServerAuthError'\n\n /**\n * Creates a new authentication error.\n *\n * @param error - The Lexicon error code (e.g., 'AuthenticationRequired')\n * @param message - Human-readable error message\n * @param wwwAuthenticate - WWW-Authenticate header parameters\n * @param options - Standard Error options including `cause`\n */\n constructor(\n error: N,\n message: string,\n readonly wwwAuthenticate: WWWAuthenticate = {},\n options?: ErrorOptions,\n ) {\n const headers = Object.keys(wwwAuthenticate).length\n ? new Headers({\n 'WWW-Authenticate': formatWWWAuthenticateHeader(wwwAuthenticate),\n 'Access-Control-Expose-Headers': 'WWW-Authenticate', // CORS\n })\n : undefined\n super(401, { error, message }, headers, options)\n }\n\n /**\n * Creates a LexServerAuthError from an existing LexError.\n *\n * If the input is already a LexServerAuthError, returns it unchanged.\n * Otherwise, wraps the error with the provided WWW-Authenticate parameters.\n *\n * @param cause - The original LexError to wrap\n * @param wwwAuthenticate - WWW-Authenticate header parameters\n * @returns A LexServerAuthError instance\n *\n * @example\n * ```typescript\n * function authenticate(token: string): Promise<User> {\n * try {\n * return await validateToken(token)\n * } catch (cause) {\n * throw LexServerAuthError.from(cause, {\n * Bearer: { error: 'InvalidToken' }\n * })\n * }\n * }\n * ```\n */\n static from(\n cause: unknown,\n wwwAuthenticate?: WWWAuthenticate,\n ): LexServerAuthError {\n if (cause instanceof LexServerAuthError) {\n return cause\n }\n\n if (cause instanceof LexError) {\n return new LexServerAuthError(\n cause.error,\n cause.message,\n wwwAuthenticate,\n { cause },\n )\n }\n\n return new LexServerAuthError(\n 'AuthenticationRequired',\n 'Authentication failed',\n wwwAuthenticate,\n { cause },\n )\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LexError = void 0;
|
|
4
3
|
const tslib_1 = require("tslib");
|
|
5
|
-
var lex_data_1 = require("@atproto/lex-data");
|
|
6
|
-
Object.defineProperty(exports, "LexError", { enumerable: true, get: function () { return lex_data_1.LexError; } });
|
|
7
4
|
tslib_1.__exportStar(require("./errors.js"), exports);
|
|
8
|
-
tslib_1.__exportStar(require("./lex-
|
|
5
|
+
tslib_1.__exportStar(require("./lex-router.js"), exports);
|
|
9
6
|
tslib_1.__exportStar(require("./service-auth.js"), exports);
|
|
10
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,sDAA2B;AAC3B,0DAA+B;AAC/B,4DAAiC","sourcesContent":["export * from './errors.js'\nexport * from './lex-router.js'\nexport * from './service-auth.js'\n"]}
|