@atproto/lex-password-session 0.0.2 → 0.0.4

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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atproto/lex-password-session
2
2
 
3
+ ## 0.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4589](https://github.com/bluesky-social/atproto/pull/4589) [`369bb02`](https://github.com/bluesky-social/atproto/commit/369bb02b9f80f0e15e5242e54f09bd4e01117f3a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `PasswordSession.createAccount` static method
8
+
9
+ - [#4589](https://github.com/bluesky-social/atproto/pull/4589) [`369bb02`](https://github.com/bluesky-social/atproto/commit/369bb02b9f80f0e15e5242e54f09bd4e01117f3a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Rename `PasswordSession.create` to `PasswordSession.login`
10
+
11
+ - [#4589](https://github.com/bluesky-social/atproto/pull/4589) [`369bb02`](https://github.com/bluesky-social/atproto/commit/369bb02b9f80f0e15e5242e54f09bd4e01117f3a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Make all `PasswordSessionOptions` optional
12
+
13
+ - Updated dependencies [[`369bb02`](https://github.com/bluesky-social/atproto/commit/369bb02b9f80f0e15e5242e54f09bd4e01117f3a), [`369bb02`](https://github.com/bluesky-social/atproto/commit/369bb02b9f80f0e15e5242e54f09bd4e01117f3a), [`369bb02`](https://github.com/bluesky-social/atproto/commit/369bb02b9f80f0e15e5242e54f09bd4e01117f3a)]:
14
+ - @atproto/lex-client@0.0.11
15
+ - @atproto/lex-schema@0.0.11
16
+
17
+ ## 0.0.3
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [[`99963d0`](https://github.com/bluesky-social/atproto/commit/99963d002a9e030e79aed5fba700e0a68f31e101), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`99963d0`](https://github.com/bluesky-social/atproto/commit/99963d002a9e030e79aed5fba700e0a68f31e101), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b), [`7310b97`](https://github.com/bluesky-social/atproto/commit/7310b9704de678a3b389a741784d58bb7f79b10b)]:
22
+ - @atproto/lex-schema@0.0.10
23
+ - @atproto/lex-client@0.0.10
24
+
3
25
  ## 0.0.2
4
26
 
5
27
  ### Patch Changes
package/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  Dual MIT/Apache-2.0 License
2
2
 
3
- Copyright (c) 2022-2025 Bluesky Social PBC, and Contributors
3
+ Copyright (c) 2022-2026 Bluesky Social PBC, and Contributors
4
4
 
5
5
  Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
6
6
 
package/README.md CHANGED
@@ -139,7 +139,7 @@ if (result.success) {
139
139
  The `login()` method returns a discriminated union:
140
140
 
141
141
  - On success: `{ success: true, value: PasswordAgent }`
142
- - On expected errors: `LexRpcResponseError` with `success: false`
142
+ - On expected errors: `XrpcResponseError` with `success: false`
143
143
  - On unexpected errors: throws the error
144
144
 
145
145
  ### Two-Factor Authentication
@@ -329,7 +329,7 @@ type Session = {
329
329
 
330
330
  ## Error Handling
331
331
 
332
- Login errors are returned as `LexRpcResponseError` objects:
332
+ Login errors are returned as `XrpcResponseError` objects:
333
333
 
334
334
  ```typescript
335
335
  const result = await PasswordAgent.login({
package/dist/error.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { LexError, LexRpcResponseError } from '@atproto/lex-client';
2
- import { com } from './lexicons';
1
+ import { LexError, XrpcFailure } from '@atproto/lex-client';
3
2
  export declare class LexAuthFactorError extends LexError {
4
- readonly response: LexRpcResponseError<typeof com.atproto.server.createSession.main>;
3
+ readonly cause: XrpcFailure;
5
4
  name: string;
6
- constructor(response: LexRpcResponseError<typeof com.atproto.server.createSession.main>);
5
+ constructor(cause: XrpcFailure);
6
+ toResponse(): Response;
7
7
  }
8
8
  //# sourceMappingURL=error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;AAEhC,qBAAa,kBAAmB,SAAQ,QAAQ;IAI5C,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CACpC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAC7C;IALH,IAAI,SAAuB;gBAGhB,QAAQ,EAAE,mBAAmB,CACpC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAC7C;CAIJ"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAE3D,qBAAa,kBAAmB,SAAQ,QAAQ;IAGlC,QAAQ,CAAC,KAAK,EAAE,WAAW;IAFvC,IAAI,SAAuB;gBAEN,KAAK,EAAE,WAAW;IAI9B,UAAU,IAAI,QAAQ;CAGhC"}
package/dist/error.js CHANGED
@@ -3,11 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LexAuthFactorError = void 0;
4
4
  const lex_client_1 = require("@atproto/lex-client");
5
5
  class LexAuthFactorError extends lex_client_1.LexError {
6
- response;
6
+ cause;
7
7
  name = 'LexAuthFactorError';
8
- constructor(response) {
9
- super(response.error, response.message, { cause: response.reason });
10
- this.response = response;
8
+ constructor(cause) {
9
+ super(cause.error, cause.message ?? 'Auth factor token required', { cause });
10
+ this.cause = cause;
11
+ }
12
+ toResponse() {
13
+ return Response.json({ error: 'InternalServerError' }, { status: 500 });
11
14
  }
12
15
  }
13
16
  exports.LexAuthFactorError = LexAuthFactorError;
package/dist/error.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":";;;AAAA,oDAAmE;AAGnE,MAAa,kBAAmB,SAAQ,qBAAQ;IAInC;IAHX,IAAI,GAAG,oBAAoB,CAAA;IAE3B,YACW,QAER;QAED,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QAJ1D,aAAQ,GAAR,QAAQ,CAEhB;IAGH,CAAC;CACF;AAVD,gDAUC","sourcesContent":["import { LexError, LexRpcResponseError } from '@atproto/lex-client'\nimport { com } from './lexicons'\n\nexport class LexAuthFactorError extends LexError {\n name = 'LexAuthFactorError'\n\n constructor(\n readonly response: LexRpcResponseError<\n typeof com.atproto.server.createSession.main\n >,\n ) {\n super(response.error, response.message, { cause: response.reason })\n }\n}\n"]}
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":";;;AAAA,oDAA2D;AAE3D,MAAa,kBAAmB,SAAQ,qBAAQ;IAGzB;IAFrB,IAAI,GAAG,oBAAoB,CAAA;IAE3B,YAAqB,KAAkB;QACrC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,IAAI,4BAA4B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QADzD,UAAK,GAAL,KAAK,CAAa;IAEvC,CAAC;IAEQ,UAAU;QACjB,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACzE,CAAC;CACF;AAVD,gDAUC","sourcesContent":["import { LexError, XrpcFailure } from '@atproto/lex-client'\n\nexport class LexAuthFactorError extends LexError {\n name = 'LexAuthFactorError'\n\n constructor(readonly cause: XrpcFailure) {\n super(cause.error, cause.message ?? 'Auth factor token required', { cause })\n }\n\n override toResponse(): Response {\n return Response.json({ error: 'InternalServerError' }, { status: 500 })\n }\n}\n"]}
@@ -3,17 +3,19 @@ declare const $nsid = "com.atproto.server.createAccount";
3
3
  export { $nsid };
4
4
  /** Create an account. Implemented by PDS. */
5
5
  declare const main: l.Procedure<"com.atproto.server.createAccount", l.ParamsSchema<{}>, l.Payload<"application/json", l.ObjectSchema<{
6
- readonly email: l.OptionalSchema<string>;
6
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
7
7
  readonly handle: l.StringSchema<{
8
8
  readonly format: "handle";
9
9
  }>;
10
- readonly did: l.OptionalSchema<`did:${string}:${string}`>;
11
- readonly inviteCode: l.OptionalSchema<string>;
12
- readonly verificationCode: l.OptionalSchema<string>;
13
- readonly verificationPhone: l.OptionalSchema<string>;
14
- readonly password: l.OptionalSchema<string>;
15
- readonly recoveryKey: l.OptionalSchema<string>;
16
- readonly plcOp: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
10
+ readonly did: l.OptionalSchema<l.StringSchema<{
11
+ readonly format: "did";
12
+ }>>;
13
+ readonly inviteCode: l.OptionalSchema<l.StringSchema<{}>>;
14
+ readonly verificationCode: l.OptionalSchema<l.StringSchema<{}>>;
15
+ readonly verificationPhone: l.OptionalSchema<l.StringSchema<{}>>;
16
+ readonly password: l.OptionalSchema<l.StringSchema<{}>>;
17
+ readonly recoveryKey: l.OptionalSchema<l.StringSchema<{}>>;
18
+ readonly plcOp: l.OptionalSchema<l.UnknownObjectSchema>;
17
19
  }>>, l.Payload<"application/json", l.ObjectSchema<{
18
20
  readonly accessJwt: l.StringSchema<{}>;
19
21
  readonly refreshJwt: l.StringSchema<{}>;
@@ -23,7 +25,7 @@ declare const main: l.Procedure<"com.atproto.server.createAccount", l.ParamsSche
23
25
  readonly did: l.StringSchema<{
24
26
  readonly format: "did";
25
27
  }>;
26
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
28
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
27
29
  }>>, readonly ["InvalidHandle", "InvalidPassword", "InvalidInviteCode", "HandleNotAvailable", "UnsupportedDomain", "UnresolvableDid", "IncompatibleDidDoc"]>;
28
30
  export { main };
29
31
  export type Params = l.InferMethodParams<typeof main>;
@@ -32,17 +34,19 @@ export type InputBody = l.InferMethodInputBody<typeof main>;
32
34
  export type Output = l.InferMethodOutput<typeof main>;
33
35
  export type OutputBody = l.InferMethodOutputBody<typeof main>;
34
36
  export declare const $lxm: "com.atproto.server.createAccount", $params: l.ParamsSchema<{}>, $input: l.Payload<"application/json", l.ObjectSchema<{
35
- readonly email: l.OptionalSchema<string>;
37
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
36
38
  readonly handle: l.StringSchema<{
37
39
  readonly format: "handle";
38
40
  }>;
39
- readonly did: l.OptionalSchema<`did:${string}:${string}`>;
40
- readonly inviteCode: l.OptionalSchema<string>;
41
- readonly verificationCode: l.OptionalSchema<string>;
42
- readonly verificationPhone: l.OptionalSchema<string>;
43
- readonly password: l.OptionalSchema<string>;
44
- readonly recoveryKey: l.OptionalSchema<string>;
45
- readonly plcOp: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
41
+ readonly did: l.OptionalSchema<l.StringSchema<{
42
+ readonly format: "did";
43
+ }>>;
44
+ readonly inviteCode: l.OptionalSchema<l.StringSchema<{}>>;
45
+ readonly verificationCode: l.OptionalSchema<l.StringSchema<{}>>;
46
+ readonly verificationPhone: l.OptionalSchema<l.StringSchema<{}>>;
47
+ readonly password: l.OptionalSchema<l.StringSchema<{}>>;
48
+ readonly recoveryKey: l.OptionalSchema<l.StringSchema<{}>>;
49
+ readonly plcOp: l.OptionalSchema<l.UnknownObjectSchema>;
46
50
  }>>, $output: l.Payload<"application/json", l.ObjectSchema<{
47
51
  readonly accessJwt: l.StringSchema<{}>;
48
52
  readonly refreshJwt: l.StringSchema<{}>;
@@ -52,6 +56,6 @@ export declare const $lxm: "com.atproto.server.createAccount", $params: l.Params
52
56
  readonly did: l.StringSchema<{
53
57
  readonly format: "did";
54
58
  }>;
55
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
59
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
56
60
  }>>;
57
61
  //# sourceMappingURL=createAccount.defs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createAccount.defs.d.ts","sourceRoot":"","sources":["../../../../../src/lexicons/com/atproto/server/createAccount.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AAEvC,QAAA,MAAM,KAAK,qCAAqC,CAAA;AAEhD,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,6CAA6C;AAC7C,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;4JAgCP,CAAA;AACH,OAAO,EAAE,IAAI,EAAE,CAAA;AAEf,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,IAAI,CAAC,CAAA;AACrD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,gBAAgB,CAAC,OAAO,IAAI,CAAC,CAAA;AACnD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,oBAAoB,CAAC,OAAO,IAAI,CAAC,CAAA;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,IAAI,CAAC,CAAA;AACrD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,qBAAqB,CAAC,OAAO,IAAI,CAAC,CAAA;AAE7D,eAAO,MAAM,IAAI,oCAA0B,EACzC,OAAO,oBAAgC,EACvC,MAAM;;;;;;;;;;;;GAA2B,EACjC,OAAO;;;;;;;;;;GAA4B,CAAA"}
1
+ {"version":3,"file":"createAccount.defs.d.ts","sourceRoot":"","sources":["../../../../../src/lexicons/com/atproto/server/createAccount.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AAEvC,QAAA,MAAM,KAAK,qCAAqC,CAAA;AAEhD,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,6CAA6C;AAC7C,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;4JAgCP,CAAA;AACH,OAAO,EAAE,IAAI,EAAE,CAAA;AAEf,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,IAAI,CAAC,CAAA;AACrD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,gBAAgB,CAAC,OAAO,IAAI,CAAC,CAAA;AACnD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,oBAAoB,CAAC,OAAO,IAAI,CAAC,CAAA;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,IAAI,CAAC,CAAA;AACrD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,qBAAqB,CAAC,OAAO,IAAI,CAAC,CAAA;AAE7D,eAAO,MAAM,IAAI,oCAA0B,EACzC,OAAO,oBAAgC,EACvC,MAAM;;;;;;;;;;;;;;GAA2B,EACjC,OAAO;;;;;;;;;;GAA4B,CAAA"}
@@ -5,8 +5,8 @@ export { $nsid };
5
5
  declare const main: l.Procedure<"com.atproto.server.createSession", l.ParamsSchema<{}>, l.Payload<"application/json", l.ObjectSchema<{
6
6
  readonly identifier: l.StringSchema<{}>;
7
7
  readonly password: l.StringSchema<{}>;
8
- readonly authFactorToken: l.OptionalSchema<string>;
9
- readonly allowTakendown: l.OptionalSchema<boolean>;
8
+ readonly authFactorToken: l.OptionalSchema<l.StringSchema<{}>>;
9
+ readonly allowTakendown: l.OptionalSchema<l.BooleanSchema>;
10
10
  }>>, l.Payload<"application/json", l.ObjectSchema<{
11
11
  readonly accessJwt: l.StringSchema<{}>;
12
12
  readonly refreshJwt: l.StringSchema<{}>;
@@ -16,12 +16,12 @@ declare const main: l.Procedure<"com.atproto.server.createSession", l.ParamsSche
16
16
  readonly did: l.StringSchema<{
17
17
  readonly format: "did";
18
18
  }>;
19
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
20
- readonly email: l.OptionalSchema<string>;
21
- readonly emailConfirmed: l.OptionalSchema<boolean>;
22
- readonly emailAuthFactor: l.OptionalSchema<boolean>;
23
- readonly active: l.OptionalSchema<boolean>;
24
- readonly status: l.OptionalSchema<string>;
19
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
20
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
21
+ readonly emailConfirmed: l.OptionalSchema<l.BooleanSchema>;
22
+ readonly emailAuthFactor: l.OptionalSchema<l.BooleanSchema>;
23
+ readonly active: l.OptionalSchema<l.BooleanSchema>;
24
+ readonly status: l.OptionalSchema<l.StringSchema<{}>>;
25
25
  }>>, readonly ["AccountTakedown", "AuthFactorTokenRequired"]>;
26
26
  export { main };
27
27
  export type Params = l.InferMethodParams<typeof main>;
@@ -32,8 +32,8 @@ export type OutputBody = l.InferMethodOutputBody<typeof main>;
32
32
  export declare const $lxm: "com.atproto.server.createSession", $params: l.ParamsSchema<{}>, $input: l.Payload<"application/json", l.ObjectSchema<{
33
33
  readonly identifier: l.StringSchema<{}>;
34
34
  readonly password: l.StringSchema<{}>;
35
- readonly authFactorToken: l.OptionalSchema<string>;
36
- readonly allowTakendown: l.OptionalSchema<boolean>;
35
+ readonly authFactorToken: l.OptionalSchema<l.StringSchema<{}>>;
36
+ readonly allowTakendown: l.OptionalSchema<l.BooleanSchema>;
37
37
  }>>, $output: l.Payload<"application/json", l.ObjectSchema<{
38
38
  readonly accessJwt: l.StringSchema<{}>;
39
39
  readonly refreshJwt: l.StringSchema<{}>;
@@ -43,11 +43,11 @@ export declare const $lxm: "com.atproto.server.createSession", $params: l.Params
43
43
  readonly did: l.StringSchema<{
44
44
  readonly format: "did";
45
45
  }>;
46
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
47
- readonly email: l.OptionalSchema<string>;
48
- readonly emailConfirmed: l.OptionalSchema<boolean>;
49
- readonly emailAuthFactor: l.OptionalSchema<boolean>;
50
- readonly active: l.OptionalSchema<boolean>;
51
- readonly status: l.OptionalSchema<string>;
46
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
47
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
48
+ readonly emailConfirmed: l.OptionalSchema<l.BooleanSchema>;
49
+ readonly emailAuthFactor: l.OptionalSchema<l.BooleanSchema>;
50
+ readonly active: l.OptionalSchema<l.BooleanSchema>;
51
+ readonly status: l.OptionalSchema<l.StringSchema<{}>>;
52
52
  }>>;
53
53
  //# sourceMappingURL=createSession.defs.d.ts.map
@@ -9,12 +9,12 @@ declare const main: l.Query<"com.atproto.server.getSession", l.ParamsSchema<{}>,
9
9
  readonly did: l.StringSchema<{
10
10
  readonly format: "did";
11
11
  }>;
12
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
13
- readonly email: l.OptionalSchema<string>;
14
- readonly emailConfirmed: l.OptionalSchema<boolean>;
15
- readonly emailAuthFactor: l.OptionalSchema<boolean>;
16
- readonly active: l.OptionalSchema<boolean>;
17
- readonly status: l.OptionalSchema<string>;
12
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
13
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
14
+ readonly emailConfirmed: l.OptionalSchema<l.BooleanSchema>;
15
+ readonly emailAuthFactor: l.OptionalSchema<l.BooleanSchema>;
16
+ readonly active: l.OptionalSchema<l.BooleanSchema>;
17
+ readonly status: l.OptionalSchema<l.StringSchema<{}>>;
18
18
  }>>, undefined>;
19
19
  export { main };
20
20
  export type Params = l.InferMethodParams<typeof main>;
@@ -27,11 +27,11 @@ export declare const $lxm: "com.atproto.server.getSession", $params: l.ParamsSch
27
27
  readonly did: l.StringSchema<{
28
28
  readonly format: "did";
29
29
  }>;
30
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
31
- readonly email: l.OptionalSchema<string>;
32
- readonly emailConfirmed: l.OptionalSchema<boolean>;
33
- readonly emailAuthFactor: l.OptionalSchema<boolean>;
34
- readonly active: l.OptionalSchema<boolean>;
35
- readonly status: l.OptionalSchema<string>;
30
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
31
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
32
+ readonly emailConfirmed: l.OptionalSchema<l.BooleanSchema>;
33
+ readonly emailAuthFactor: l.OptionalSchema<l.BooleanSchema>;
34
+ readonly active: l.OptionalSchema<l.BooleanSchema>;
35
+ readonly status: l.OptionalSchema<l.StringSchema<{}>>;
36
36
  }>>;
37
37
  //# sourceMappingURL=getSession.defs.d.ts.map
@@ -11,12 +11,12 @@ declare const main: l.Procedure<"com.atproto.server.refreshSession", l.ParamsSch
11
11
  readonly did: l.StringSchema<{
12
12
  readonly format: "did";
13
13
  }>;
14
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
15
- readonly email: l.OptionalSchema<string>;
16
- readonly emailConfirmed: l.OptionalSchema<boolean>;
17
- readonly emailAuthFactor: l.OptionalSchema<boolean>;
18
- readonly active: l.OptionalSchema<boolean>;
19
- readonly status: l.OptionalSchema<string>;
14
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
15
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
16
+ readonly emailConfirmed: l.OptionalSchema<l.BooleanSchema>;
17
+ readonly emailAuthFactor: l.OptionalSchema<l.BooleanSchema>;
18
+ readonly active: l.OptionalSchema<l.BooleanSchema>;
19
+ readonly status: l.OptionalSchema<l.StringSchema<{}>>;
20
20
  }>>, readonly ["AccountTakedown", "InvalidToken", "ExpiredToken"]>;
21
21
  export { main };
22
22
  export type Params = l.InferMethodParams<typeof main>;
@@ -33,11 +33,11 @@ export declare const $lxm: "com.atproto.server.refreshSession", $params: l.Param
33
33
  readonly did: l.StringSchema<{
34
34
  readonly format: "did";
35
35
  }>;
36
- readonly didDoc: l.OptionalSchema<import("@atproto/lex-client").LexMap>;
37
- readonly email: l.OptionalSchema<string>;
38
- readonly emailConfirmed: l.OptionalSchema<boolean>;
39
- readonly emailAuthFactor: l.OptionalSchema<boolean>;
40
- readonly active: l.OptionalSchema<boolean>;
41
- readonly status: l.OptionalSchema<string>;
36
+ readonly didDoc: l.OptionalSchema<l.UnknownObjectSchema>;
37
+ readonly email: l.OptionalSchema<l.StringSchema<{}>>;
38
+ readonly emailConfirmed: l.OptionalSchema<l.BooleanSchema>;
39
+ readonly emailAuthFactor: l.OptionalSchema<l.BooleanSchema>;
40
+ readonly active: l.OptionalSchema<l.BooleanSchema>;
41
+ readonly status: l.OptionalSchema<l.StringSchema<{}>>;
42
42
  }>>;
43
43
  //# sourceMappingURL=refreshSession.defs.d.ts.map
@@ -1,7 +1,7 @@
1
- import { Agent, LexRpcFailure } from '@atproto/lex-client';
1
+ import { Agent, XrpcFailure } from '@atproto/lex-client';
2
2
  import { com } from './lexicons/index.js';
3
- export type RefreshFailure = LexRpcFailure<typeof com.atproto.server.refreshSession.main>;
4
- export type DeleteFailure = LexRpcFailure<typeof com.atproto.server.deleteSession.main>;
3
+ export type RefreshFailure = XrpcFailure<typeof com.atproto.server.refreshSession.main>;
4
+ export type DeleteFailure = XrpcFailure<typeof com.atproto.server.deleteSession.main>;
5
5
  export type SessionData = com.atproto.server.createSession.OutputBody & {
6
6
  service: string;
7
7
  };
@@ -20,7 +20,7 @@ export type PasswordSessionOptions = {
20
20
  *
21
21
  * @note this function **must** not throw
22
22
  */
23
- onUpdated: (this: PasswordSession, data: SessionData) => void | Promise<void>;
23
+ onUpdated?: (this: PasswordSession, data: SessionData) => void | Promise<void>;
24
24
  /**
25
25
  * Called whenever the session update fails due to an expected error, such as
26
26
  * a network issue or server unavailability. This function can be used to log
@@ -38,7 +38,7 @@ export type PasswordSessionOptions = {
38
38
  *
39
39
  * @note this function **must** not throw
40
40
  */
41
- onDeleted: (this: PasswordSession, data: SessionData) => void | Promise<void>;
41
+ onDeleted?: (this: PasswordSession, data: SessionData) => void | Promise<void>;
42
42
  /**
43
43
  * Called whenever a session deletion fails due to an unexpected error, such
44
44
  * as a network issue or server unavailability. This function can be used to
@@ -58,7 +58,7 @@ export type PasswordSessionOptions = {
58
58
  export declare class PasswordSession implements Agent {
59
59
  #private;
60
60
  protected readonly options: PasswordSessionOptions;
61
- constructor(sessionData: SessionData, options: PasswordSessionOptions);
61
+ constructor(sessionData: SessionData, options?: PasswordSessionOptions);
62
62
  get did(): `did:${string}:${string}`;
63
63
  get handle(): `${string}.${string}`;
64
64
  get session(): SessionData;
@@ -66,6 +66,10 @@ export declare class PasswordSession implements Agent {
66
66
  fetchHandler(path: string, init: RequestInit): Promise<Response>;
67
67
  refresh(): Promise<SessionData>;
68
68
  logout(): Promise<void>;
69
+ static createAccount(body: com.atproto.server.createAccount.InputBody, { service, headers, ...options }: PasswordSessionOptions & {
70
+ headers?: HeadersInit;
71
+ service: string | URL;
72
+ }): Promise<PasswordSession>;
69
73
  /**
70
74
  * @note It is **not** recommended to use {@link PasswordSession} with main
71
75
  * account credentials. Instead, it is strongly advised to use OAuth based
@@ -74,7 +78,7 @@ export declare class PasswordSession implements Agent {
74
78
  * use-cases.
75
79
  *
76
80
  * @throws If unable to create a session. In particular, if the server
77
- * requires a 2FA token, a {@link LexRpcResponseError} with the
81
+ * requires a 2FA token, a {@link XrpcResponseError} with the
78
82
  * `AuthFactorTokenRequired` error code will be thrown.
79
83
  *
80
84
  *
@@ -82,19 +86,19 @@ export declare class PasswordSession implements Agent {
82
86
  *
83
87
  * ```ts
84
88
  * try {
85
- * const session = await PasswordSession.create({
89
+ * const session = await PasswordSession.login({
86
90
  * service: 'https://example.com',
87
91
  * identifier: 'alice',
88
92
  * password: 'correct horse battery staple',
89
93
  * })
90
94
  * } catch (err) {
91
- * if (err instanceof LexRpcResponseError && err.error === 'AuthFactorTokenRequired') {
95
+ * if (err instanceof XrpcResponseError && err.error === 'AuthFactorTokenRequired') {
92
96
  * // Prompt user for 2FA token and re-attempt session creation
93
97
  * }
94
98
  * }
95
99
  * ```
96
100
  */
97
- static create({ service, identifier, password, allowTakendown, authFactorToken, ...options }: PasswordSessionOptions & {
101
+ static login({ service, identifier, password, allowTakendown, authFactorToken, ...options }: PasswordSessionOptions & {
98
102
  service: string | URL;
99
103
  identifier: string;
100
104
  password: string;
@@ -122,6 +126,6 @@ export declare class PasswordSession implements Agent {
122
126
  * @throws In case of unexpected error (network issue, server down, etc)
123
127
  * meaning that the session may still be valid.
124
128
  */
125
- static delete(data: SessionData, options?: Partial<PasswordSessionOptions>): Promise<void>;
129
+ static delete(data: SessionData, options?: PasswordSessionOptions): Promise<void>;
126
130
  }
127
131
  //# sourceMappingURL=password-session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"password-session.d.ts","sourceRoot":"","sources":["../src/password-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAEL,aAAa,EAGd,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AAGzC,MAAM,MAAM,cAAc,GAAG,aAAa,CACxC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAC9C,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,CACvC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAC7C,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,GAAG;IACtE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAE/B;;;;;;;;;OASG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7E;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,cAAc,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzB;;;;;;;OAOG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7E;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,aAAa,KACf,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B,CAAA;AAED,qBAAa,eAAgB,YAAW,KAAK;;IAYzC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,sBAAsB;gBADlD,WAAW,EAAE,WAAW,EACL,OAAO,EAAE,sBAAsB;IAWpD,IAAI,GAAG,8BAEN;IAED,IAAI,MAAM,0BAET;IAED,IAAI,OAAO,gBAGV;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkEhE,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC;IAwD/B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;WACU,MAAM,CAAC,EAClB,OAAO,EACP,UAAU,EACV,QAAQ,EACR,cAAc,EACd,eAAe,EACf,GAAG,OAAO,EACX,EAAE,sBAAsB,GAAG;QAC1B,OAAO,EAAE,MAAM,GAAG,GAAG,CAAA;QACrB,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,cAAc,CAAC,EAAE,OAAO,CAAA;QACxB,eAAe,CAAC,EAAE,MAAM,CAAA;KACzB,GAAG,OAAO,CAAC,eAAe,CAAC;IA6B5B;;;;;;;;;;;;OAYG;WACU,MAAM,CACjB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC;IAM3B;;;;;;OAMG;WACU,MAAM,CACjB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC;CAQjB"}
1
+ {"version":3,"file":"password-session.d.ts","sourceRoot":"","sources":["../src/password-session.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,7 @@ class PasswordSession {
14
14
  #serviceAgent;
15
15
  #sessionData;
16
16
  #sessionPromise;
17
- constructor(sessionData, options) {
17
+ constructor(sessionData, options = {}) {
18
18
  this.options = options;
19
19
  this.#serviceAgent = (0, lex_client_1.buildAgent)({
20
20
  service: sessionData.service,
@@ -32,7 +32,7 @@ class PasswordSession {
32
32
  get session() {
33
33
  if (this.#sessionData)
34
34
  return this.#sessionData;
35
- throw new lex_client_1.LexRpcError('AuthenticationRequired', 'Logged out');
35
+ throw new Error('Logged out');
36
36
  }
37
37
  get destroyed() {
38
38
  return this.#sessionData === null;
@@ -52,7 +52,7 @@ class PasswordSession {
52
52
  });
53
53
  const refreshNeeded = initialRes.status === 401 ||
54
54
  (initialRes.status === 400 &&
55
- (await (0, util_js_1.extractLexRpcErrorCode)(initialRes)) === 'ExpiredToken');
55
+ (await (0, util_js_1.extractXrpcErrorCode)(initialRes)) === 'ExpiredToken');
56
56
  if (!refreshNeeded) {
57
57
  return initialRes;
58
58
  }
@@ -93,12 +93,16 @@ class PasswordSession {
93
93
  const response = await (0, lex_client_1.xrpcSafe)(this.#serviceAgent, index_js_1.com.atproto.server.refreshSession.main, { headers: { Authorization: `Bearer ${sessionData.refreshJwt}` } });
94
94
  if (!response.success && response.matchesSchema()) {
95
95
  // Expected errors that indicate the session is no longer valid
96
- await this.options.onDeleted.call(this, sessionData);
96
+ await this.options.onDeleted?.call(this, sessionData);
97
97
  // Update the session promise to a rejected state
98
98
  this.#sessionData = null;
99
99
  throw response;
100
100
  }
101
101
  if (!response.success) {
102
+ response.error;
103
+ if (response.matchesSchema()) {
104
+ response.error;
105
+ }
102
106
  // We failed to refresh the token, assume the session might still be
103
107
  // valid by returning the existing session.
104
108
  await this.options.onUpdateFailure?.call(this, sessionData, response);
@@ -120,7 +124,7 @@ class PasswordSession {
120
124
  ...data,
121
125
  service: sessionData.service,
122
126
  };
123
- await this.options.onUpdated.call(this, newSession);
127
+ await this.options.onUpdated?.call(this, newSession);
124
128
  return (this.#sessionData = newSession);
125
129
  });
126
130
  return this.#sessionPromise;
@@ -130,10 +134,10 @@ class PasswordSession {
130
134
  this.#sessionPromise = this.#sessionPromise.then(async (sessionData) => {
131
135
  const result = await (0, lex_client_1.xrpcSafe)(this.#serviceAgent, index_js_1.com.atproto.server.deleteSession.main, { headers: { Authorization: `Bearer ${sessionData.refreshJwt}` } });
132
136
  if (result.success || result.matchesSchema()) {
133
- await this.options.onDeleted.call(this, sessionData);
137
+ await this.options.onDeleted?.call(this, sessionData);
134
138
  // Update the session promise to a rejected state
135
139
  this.#sessionData = null;
136
- throw new lex_client_1.LexRpcError('AuthenticationRequired', 'Logged out');
140
+ throw new Error('Logged out');
137
141
  }
138
142
  else {
139
143
  // Capture the reason for the failure to re-throw in the outer promise
@@ -152,6 +156,16 @@ class PasswordSession {
152
156
  // Successful logout
153
157
  });
154
158
  }
159
+ static async createAccount(body, { service, headers, ...options }) {
160
+ const response = await (0, lex_client_1.xrpc)((0, lex_client_1.buildAgent)({ service, headers, fetch: options.fetch }), index_js_1.com.atproto.server.createAccount.main, { body });
161
+ const data = {
162
+ ...response.body,
163
+ service: String(service),
164
+ };
165
+ const agent = new PasswordSession(data, options);
166
+ await options.onUpdated?.call(agent, data);
167
+ return agent;
168
+ }
155
169
  /**
156
170
  * @note It is **not** recommended to use {@link PasswordSession} with main
157
171
  * account credentials. Instead, it is strongly advised to use OAuth based
@@ -160,7 +174,7 @@ class PasswordSession {
160
174
  * use-cases.
161
175
  *
162
176
  * @throws If unable to create a session. In particular, if the server
163
- * requires a 2FA token, a {@link LexRpcResponseError} with the
177
+ * requires a 2FA token, a {@link XrpcResponseError} with the
164
178
  * `AuthFactorTokenRequired` error code will be thrown.
165
179
  *
166
180
  *
@@ -168,19 +182,19 @@ class PasswordSession {
168
182
  *
169
183
  * ```ts
170
184
  * try {
171
- * const session = await PasswordSession.create({
185
+ * const session = await PasswordSession.login({
172
186
  * service: 'https://example.com',
173
187
  * identifier: 'alice',
174
188
  * password: 'correct horse battery staple',
175
189
  * })
176
190
  * } catch (err) {
177
- * if (err instanceof LexRpcResponseError && err.error === 'AuthFactorTokenRequired') {
191
+ * if (err instanceof XrpcResponseError && err.error === 'AuthFactorTokenRequired') {
178
192
  * // Prompt user for 2FA token and re-attempt session creation
179
193
  * }
180
194
  * }
181
195
  * ```
182
196
  */
183
- static async create({ service, identifier, password, allowTakendown, authFactorToken, ...options }) {
197
+ static async login({ service, identifier, password, allowTakendown, authFactorToken, ...options }) {
184
198
  const xrpcAgent = (0, lex_client_1.buildAgent)({
185
199
  service,
186
200
  fetch: options.fetch,
@@ -197,7 +211,7 @@ class PasswordSession {
197
211
  service: String(service),
198
212
  };
199
213
  const agent = new PasswordSession(data, options);
200
- await options.onUpdated.call(agent, data);
214
+ await options.onUpdated?.call(agent, data);
201
215
  return agent;
202
216
  }
203
217
  /**
@@ -226,11 +240,7 @@ class PasswordSession {
226
240
  * meaning that the session may still be valid.
227
241
  */
228
242
  static async delete(data, options) {
229
- const agent = new PasswordSession(data, {
230
- ...options,
231
- onUpdated: options?.onUpdated ?? util_js_1.noop,
232
- onDeleted: options?.onDeleted ?? util_js_1.noop,
233
- });
243
+ const agent = new PasswordSession(data, options);
234
244
  await agent.logout();
235
245
  }
236
246
  }
@@ -1 +1 @@
1
- {"version":3,"file":"password-session.js","sourceRoot":"","sources":["../src/password-session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Agent,\n LexRpcError,\n LexRpcFailure,\n buildAgent,\n xrpcSafe,\n} from '@atproto/lex-client'\nimport { LexAuthFactorError } from './error.js'\nimport { com } from './lexicons/index.js'\nimport { extractLexRpcErrorCode, extractPdsUrl, noop } from './util.js'\n\nexport type RefreshFailure = LexRpcFailure<\n typeof com.atproto.server.refreshSession.main\n>\n\nexport type DeleteFailure = LexRpcFailure<\n typeof com.atproto.server.deleteSession.main\n>\n\nexport type SessionData = com.atproto.server.createSession.OutputBody & {\n service: string\n}\n\nexport type PasswordSessionOptions = {\n /**\n * Custom fetch implementation to use for network requests\n */\n fetch?: typeof globalThis.fetch\n\n /**\n * Called whenever the session is successfully created/refreshed, and new\n * credentials have been obtained. Use this hook to persist the updated\n * session information.\n *\n * If this callback returns a promise, this function will never be called\n * again (on the same process) until the promise resolves.\n *\n * @note this function **must** not throw\n */\n onUpdated: (this: PasswordSession, data: SessionData) => void | Promise<void>\n\n /**\n * Called whenever the session update fails due to an expected error, such as\n * a network issue or server unavailability. This function can be used to log\n * the error or notify the user, but should not assume that the session is\n * invalid.\n *\n * @note this function **must** not throw\n */\n onUpdateFailure?: (\n this: PasswordSession,\n data: SessionData,\n err: RefreshFailure,\n ) => void | Promise<void>\n\n /**\n * Called whenever the session is deleted, either due to an explicit logout or\n * because the refresh operation indicated that the session is no longer\n * valid. Use this hook to clean up any persisted session information and\n * update the application state accordingly.\n *\n * @note this function **must** not throw\n */\n onDeleted: (this: PasswordSession, data: SessionData) => void | Promise<void>\n\n /**\n * Called whenever a session deletion fails due to an unexpected error, such\n * as a network issue or server unavailability. This function can be used to\n * log the error or notify the user. When this function is called, the session\n * might still be valid on the server. It is up to the implementation to\n * decide whether to retry the deletion or keep the session active. Ignoring\n * these errors is not recommended as it can lead to orphaned sessions on the\n * server, or security issues if the user believes they have logged out when a\n * bad actor is still using the session. The implementation should consider\n * keeping track of failed deletions and retrying them later, until they\n * succeed.\n *\n * @note this function **must** not throw\n */\n onDeleteFailure?: (\n this: PasswordSession,\n data: SessionData,\n err: DeleteFailure,\n ) => void | Promise<void>\n}\n\nexport class PasswordSession implements Agent {\n /**\n * Internal {@link Agent} used for session management towards the\n * authentication service only.\n */\n #serviceAgent: Agent\n\n #sessionData: null | SessionData\n #sessionPromise: Promise<SessionData>\n\n constructor(\n sessionData: SessionData,\n protected readonly options: PasswordSessionOptions,\n ) {\n this.#serviceAgent = buildAgent({\n service: sessionData.service,\n fetch: options.fetch,\n })\n\n this.#sessionData = sessionData\n this.#sessionPromise = Promise.resolve(this.#sessionData)\n }\n\n get did() {\n return this.session.did\n }\n\n get handle() {\n return this.session.handle\n }\n\n get session() {\n if (this.#sessionData) return this.#sessionData\n throw new LexRpcError('AuthenticationRequired', 'Logged out')\n }\n\n get destroyed(): boolean {\n return this.#sessionData === null\n }\n\n async fetchHandler(path: string, init: RequestInit): Promise<Response> {\n const headers = new Headers(init.headers)\n if (headers.has('authorization')) {\n throw new TypeError(\"Unexpected 'authorization' header set\")\n }\n\n const sessionPromise = this.#sessionPromise\n const sessionData = await sessionPromise\n\n const fetch = this.options.fetch ?? globalThis.fetch\n\n headers.set('authorization', `Bearer ${sessionData.accessJwt}`)\n const initialRes = await fetch(fetchUrl(sessionData, path), {\n ...init,\n headers,\n })\n\n const refreshNeeded =\n initialRes.status === 401 ||\n (initialRes.status === 400 &&\n (await extractLexRpcErrorCode(initialRes)) === 'ExpiredToken')\n\n if (!refreshNeeded) {\n return initialRes\n }\n\n // Refresh session (unless it was already refreshed in the meantime)\n const newSessionPromise =\n this.#sessionPromise === sessionPromise\n ? this.refresh()\n : this.#sessionPromise\n\n // Error should have been propagated through hooks\n const newSessionData = await newSessionPromise.catch((_err) => null)\n if (!newSessionData) {\n return initialRes\n }\n\n // refresh silently failed, no point in retrying.\n if (newSessionData.accessJwt === sessionData.accessJwt) {\n return initialRes\n }\n\n if (init?.signal?.aborted) {\n return initialRes\n }\n\n // The stream was already consumed. We cannot retry the request. A solution\n // would be to tee() the input stream but that would bufferize the entire\n // stream in memory which can lead to memory starvation. Instead, we will\n // return the original response and let the calling code handle retries.\n if (ReadableStream && init?.body instanceof ReadableStream) {\n return initialRes\n }\n\n // Make sure the initial request is cancelled to avoid leaking resources\n // (NodeJS 👀): https://undici.nodejs.org/#/?id=garbage-collection\n if (!initialRes.bodyUsed) {\n await initialRes.body?.cancel()\n }\n\n // Finally, retry the request with the new access token\n headers.set('authorization', `Bearer ${newSessionData.accessJwt}`)\n return fetch(fetchUrl(newSessionData, path), { ...init, headers })\n }\n\n async refresh(): Promise<SessionData> {\n this.#sessionPromise = this.#sessionPromise.then(async (sessionData) => {\n const response = await xrpcSafe(\n this.#serviceAgent,\n com.atproto.server.refreshSession.main,\n { headers: { Authorization: `Bearer ${sessionData.refreshJwt}` } },\n )\n\n if (!response.success && response.matchesSchema()) {\n // Expected errors that indicate the session is no longer valid\n await this.options.onDeleted.call(this, sessionData)\n\n // Update the session promise to a rejected state\n this.#sessionData = null\n throw response\n }\n\n if (!response.success) {\n // We failed to refresh the token, assume the session might still be\n // valid by returning the existing session.\n await this.options.onUpdateFailure?.call(this, sessionData, response)\n\n return sessionData\n }\n\n const data = response.body\n\n // Historically, refreshSession did not return all the fields from\n // getSession. In particular, emailConfirmed and didDoc were missing.\n // Similarly, some servers might not return the didDoc in refreshSession.\n // We fetch them via getSession if missing, allowing to ensure that we are\n // always talking with the right PDS.\n if (data.emailConfirmed == null || data.didDoc == null) {\n const extraData = await xrpcSafe(\n this.#serviceAgent,\n com.atproto.server.getSession.main,\n { headers: { Authorization: `Bearer ${data.accessJwt}` } },\n )\n if (extraData.success && extraData.body.did === data.did) {\n Object.assign(data, extraData.body)\n }\n }\n\n const newSession: SessionData = {\n ...data,\n service: sessionData.service,\n }\n\n await this.options.onUpdated.call(this, newSession)\n\n return (this.#sessionData = newSession)\n })\n\n return this.#sessionPromise\n }\n\n async logout(): Promise<void> {\n let reason: DeleteFailure | null = null\n\n this.#sessionPromise = this.#sessionPromise.then(async (sessionData) => {\n const result = await xrpcSafe(\n this.#serviceAgent,\n com.atproto.server.deleteSession.main,\n { headers: { Authorization: `Bearer ${sessionData.refreshJwt}` } },\n )\n\n if (result.success || result.matchesSchema()) {\n await this.options.onDeleted.call(this, sessionData)\n\n // Update the session promise to a rejected state\n this.#sessionData = null\n throw new LexRpcError('AuthenticationRequired', 'Logged out')\n } else {\n // Capture the reason for the failure to re-throw in the outer promise\n reason = result\n\n // An unknown/unexpected error occurred (network, server down, etc)\n await this.options.onDeleteFailure?.call(this, sessionData, result)\n\n // Keep the session in an active state\n return sessionData\n }\n })\n\n return this.#sessionPromise.then(\n (_session) => {\n // If the promise above resolved, then logout failed. Re-throw the\n // reason captured earlier.\n throw reason!\n },\n (_err) => {\n // Successful logout\n },\n )\n }\n\n /**\n * @note It is **not** recommended to use {@link PasswordSession} with main\n * account credentials. Instead, it is strongly advised to use OAuth based\n * authentication for main username/password credentials and use\n * {@link PasswordSession} with an app-password, for bots, scripts, or similar\n * use-cases.\n *\n * @throws If unable to create a session. In particular, if the server\n * requires a 2FA token, a {@link LexRpcResponseError} with the\n * `AuthFactorTokenRequired` error code will be thrown.\n *\n *\n * @example Handling 2FA errors\n *\n * ```ts\n * try {\n * const session = await PasswordSession.create({\n * service: 'https://example.com',\n * identifier: 'alice',\n * password: 'correct horse battery staple',\n * })\n * } catch (err) {\n * if (err instanceof LexRpcResponseError && err.error === 'AuthFactorTokenRequired') {\n * // Prompt user for 2FA token and re-attempt session creation\n * }\n * }\n * ```\n */\n static async create({\n service,\n identifier,\n password,\n allowTakendown,\n authFactorToken,\n ...options\n }: PasswordSessionOptions & {\n service: string | URL\n identifier: string\n password: string\n allowTakendown?: boolean\n authFactorToken?: string\n }): Promise<PasswordSession> {\n const xrpcAgent = buildAgent({\n service,\n fetch: options.fetch,\n })\n\n const response = await xrpcSafe(\n xrpcAgent,\n com.atproto.server.createSession.main,\n { body: { identifier, password, allowTakendown, authFactorToken } },\n )\n\n if (!response.success) {\n if (response.error === 'AuthFactorTokenRequired') {\n throw new LexAuthFactorError(response)\n }\n throw response.reason\n }\n\n const data: SessionData = {\n ...response.body,\n service: String(service),\n }\n\n const agent = new PasswordSession(data, options)\n await options.onUpdated.call(agent, data)\n return agent\n }\n\n /**\n * Resume an existing session, ensuring it is still valid by refreshing it.\n * Any error thrown here indicates that the session is definitely no longer\n * valid. Network errors will be propagated through the\n * {@link PasswordSessionOptions.onUpdateFailure} hook, and not re-thrown\n * here. This means that a resolved promise does not necessarily indicate a\n * valid session, only that it's refresh did not definitively fail.\n *\n * This is the same as calling {@link PasswordSession.refresh} after\n * constructing the {@link PasswordSession} manually.\n *\n * @throws If, and only if, the session is definitely no longer valid.\n */\n static async resume(\n data: SessionData,\n options: PasswordSessionOptions,\n ): Promise<PasswordSession> {\n const agent = new PasswordSession(data, options)\n await agent.refresh()\n return agent\n }\n\n /**\n * Delete a session without having to {@link resume resume()} it first, or\n * provide hooks.\n *\n * @throws In case of unexpected error (network issue, server down, etc)\n * meaning that the session may still be valid.\n */\n static async delete(\n data: SessionData,\n options?: Partial<PasswordSessionOptions>,\n ): Promise<void> {\n const agent = new PasswordSession(data, {\n ...options,\n onUpdated: options?.onUpdated ?? noop,\n onDeleted: options?.onDeleted ?? noop,\n })\n await agent.logout()\n }\n}\n\nfunction fetchUrl(sessionData: SessionData, path: string): URL {\n const pdsUrl = extractPdsUrl(sessionData.didDoc)\n return new URL(path, pdsUrl ?? sessionData.service)\n}\n"]}
1
+ {"version":3,"file":"password-session.js","sourceRoot":"","sources":["../src/password-session.ts"],"names":[],"mappings":";;;AAAA,oDAM4B;AAC5B,yCAA+C;AAC/C,kDAAyC;AACzC,uCAA+D;AA6E/D,MAAa,eAAe;IAYL;IAXrB;;;OAGG;IACH,aAAa,CAAO;IAEpB,YAAY,CAAoB;IAChC,eAAe,CAAsB;IAErC,YACE,WAAwB,EACL,UAAkC,EAAE;QAApC,YAAO,GAAP,OAAO,CAA6B;QAEvD,IAAI,CAAC,aAAa,GAAG,IAAA,uBAAU,EAAC;YAC9B,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC,CAAA;QAEF,IAAI,CAAC,YAAY,GAAG,WAAW,CAAA;QAC/B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC3D,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAA;IACzB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;IAC5B,CAAC;IAED,IAAI,OAAO;QACT,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAA;QAC/C,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAA;IAC/B,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,YAAY,KAAK,IAAI,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,IAAY,EAAE,IAAiB;QAChD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACzC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAA;QAC9D,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAA;QAC3C,MAAM,WAAW,GAAG,MAAM,cAAc,CAAA;QAExC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAA;QAEpD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,WAAW,CAAC,SAAS,EAAE,CAAC,CAAA;QAC/D,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE;YAC1D,GAAG,IAAI;YACP,OAAO;SACR,CAAC,CAAA;QAEF,MAAM,aAAa,GACjB,UAAU,CAAC,MAAM,KAAK,GAAG;YACzB,CAAC,UAAU,CAAC,MAAM,KAAK,GAAG;gBACxB,CAAC,MAAM,IAAA,8BAAoB,EAAC,UAAU,CAAC,CAAC,KAAK,cAAc,CAAC,CAAA;QAEhE,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,oEAAoE;QACpE,MAAM,iBAAiB,GACrB,IAAI,CAAC,eAAe,KAAK,cAAc;YACrC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE;YAChB,CAAC,CAAC,IAAI,CAAC,eAAe,CAAA;QAE1B,kDAAkD;QAClD,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;QACpE,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,iDAAiD;QACjD,IAAI,cAAc,CAAC,SAAS,KAAK,WAAW,CAAC,SAAS,EAAE,CAAC;YACvD,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,IAAI,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAC1B,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,2EAA2E;QAC3E,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;QACxE,IAAI,cAAc,IAAI,IAAI,EAAE,IAAI,YAAY,cAAc,EAAE,CAAC;YAC3D,OAAO,UAAU,CAAA;QACnB,CAAC;QAED,wEAAwE;QACxE,kEAAkE;QAClE,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YACzB,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;QACjC,CAAC;QAED,uDAAuD;QACvD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,cAAc,CAAC,SAAS,EAAE,CAAC,CAAA;QAClE,OAAO,KAAK,CAAC,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACpE,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE;YACrE,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAQ,EAC7B,IAAI,CAAC,aAAa,EAClB,cAAG,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,EACtC,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,CAAC,UAAU,EAAE,EAAE,EAAE,CACnE,CAAA;YAED,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC;gBAClD,+DAA+D;gBAC/D,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;gBAErD,iDAAiD;gBACjD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;gBACxB,MAAM,QAAQ,CAAA;YAChB,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACtB,QAAQ,CAAC,KAAK,CAAA;gBACd,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC;oBAC7B,QAAQ,CAAC,KAAK,CAAA;gBAChB,CAAC;gBACD,oEAAoE;gBACpE,2CAA2C;gBAC3C,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAA;gBAErE,OAAO,WAAW,CAAA;YACpB,CAAC;YAED,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAA;YAE1B,kEAAkE;YAClE,qEAAqE;YACrE,yEAAyE;YACzE,0EAA0E;YAC1E,qCAAqC;YACrC,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;gBACvD,MAAM,SAAS,GAAG,MAAM,IAAA,qBAAQ,EAC9B,IAAI,CAAC,aAAa,EAClB,cAAG,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAClC,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,SAAS,EAAE,EAAE,EAAE,CAC3D,CAAA;gBACD,IAAI,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;oBACzD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAA;gBACrC,CAAC;YACH,CAAC;YAED,MAAM,UAAU,GAAgB;gBAC9B,GAAG,IAAI;gBACP,OAAO,EAAE,WAAW,CAAC,OAAO;aAC7B,CAAA;YAED,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;YAEpD,OAAO,CAAC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;QAEF,OAAO,IAAI,CAAC,eAAe,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,MAAM,GAAyB,IAAI,CAAA;QAEvC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE;YACrE,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAQ,EAC3B,IAAI,CAAC,aAAa,EAClB,cAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,EACrC,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,CAAC,UAAU,EAAE,EAAE,EAAE,CACnE,CAAA;YAED,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC;gBAC7C,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;gBAErD,iDAAiD;gBACjD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;gBACxB,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAA;YAC/B,CAAC;iBAAM,CAAC;gBACN,sEAAsE;gBACtE,MAAM,GAAG,MAAM,CAAA;gBAEf,mEAAmE;gBACnE,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAA;gBAEnE,sCAAsC;gBACtC,OAAO,WAAW,CAAA;YACpB,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,CAAC,QAAQ,EAAE,EAAE;YACX,kEAAkE;YAClE,2BAA2B;YAC3B,MAAM,MAAO,CAAA;QACf,CAAC,EACD,CAAC,IAAI,EAAE,EAAE;YACP,oBAAoB;QACtB,CAAC,CACF,CAAA;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,aAAa,CACxB,IAAgD,EAChD,EACE,OAAO,EACP,OAAO,EACP,GAAG,OAAO,EAIX;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,iBAAI,EACzB,IAAA,uBAAU,EAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,EACtD,cAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,EACrC,EAAE,IAAI,EAAE,CACT,CAAA;QAED,MAAM,IAAI,GAAgB;YACxB,GAAG,QAAQ,CAAC,IAAI;YAChB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC;SACzB,CAAA;QAED,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC1C,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EACjB,OAAO,EACP,UAAU,EACV,QAAQ,EACR,cAAc,EACd,eAAe,EACf,GAAG,OAAO,EAOX;QACC,MAAM,SAAS,GAAG,IAAA,uBAAU,EAAC;YAC3B,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAQ,EAC7B,SAAS,EACT,cAAG,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,EACrC,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,CACpE,CAAA;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,IAAI,QAAQ,CAAC,KAAK,KAAK,yBAAyB,EAAE,CAAC;gBACjD,MAAM,IAAI,6BAAkB,CAAC,QAAQ,CAAC,CAAA;YACxC,CAAC;YACD,MAAM,QAAQ,CAAC,MAAM,CAAA;QACvB,CAAC;QAED,MAAM,IAAI,GAAgB;YACxB,GAAG,QAAQ,CAAC,IAAI;YAChB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC;SACzB,CAAA;QAED,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC1C,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,IAAiB,EACjB,OAA+B;QAE/B,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;QACrB,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,IAAiB,EACjB,OAAgC;QAEhC,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,KAAK,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;CACF;AAnVD,0CAmVC;AAED,SAAS,QAAQ,CAAC,WAAwB,EAAE,IAAY;IACtD,MAAM,MAAM,GAAG,IAAA,uBAAa,EAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAChD,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,WAAW,CAAC,OAAO,CAAC,CAAA;AACrD,CAAC","sourcesContent":["import {\n Agent,\n XrpcFailure,\n buildAgent,\n xrpc,\n xrpcSafe,\n} from '@atproto/lex-client'\nimport { LexAuthFactorError } from './error.js'\nimport { com } from './lexicons/index.js'\nimport { extractPdsUrl, extractXrpcErrorCode } from './util.js'\n\nexport type RefreshFailure = XrpcFailure<\n typeof com.atproto.server.refreshSession.main\n>\n\nexport type DeleteFailure = XrpcFailure<\n typeof com.atproto.server.deleteSession.main\n>\n\nexport type SessionData = com.atproto.server.createSession.OutputBody & {\n service: string\n}\n\nexport type PasswordSessionOptions = {\n /**\n * Custom fetch implementation to use for network requests\n */\n fetch?: typeof globalThis.fetch\n\n /**\n * Called whenever the session is successfully created/refreshed, and new\n * credentials have been obtained. Use this hook to persist the updated\n * session information.\n *\n * If this callback returns a promise, this function will never be called\n * again (on the same process) until the promise resolves.\n *\n * @note this function **must** not throw\n */\n onUpdated?: (this: PasswordSession, data: SessionData) => void | Promise<void>\n\n /**\n * Called whenever the session update fails due to an expected error, such as\n * a network issue or server unavailability. This function can be used to log\n * the error or notify the user, but should not assume that the session is\n * invalid.\n *\n * @note this function **must** not throw\n */\n onUpdateFailure?: (\n this: PasswordSession,\n data: SessionData,\n err: RefreshFailure,\n ) => void | Promise<void>\n\n /**\n * Called whenever the session is deleted, either due to an explicit logout or\n * because the refresh operation indicated that the session is no longer\n * valid. Use this hook to clean up any persisted session information and\n * update the application state accordingly.\n *\n * @note this function **must** not throw\n */\n onDeleted?: (this: PasswordSession, data: SessionData) => void | Promise<void>\n\n /**\n * Called whenever a session deletion fails due to an unexpected error, such\n * as a network issue or server unavailability. This function can be used to\n * log the error or notify the user. When this function is called, the session\n * might still be valid on the server. It is up to the implementation to\n * decide whether to retry the deletion or keep the session active. Ignoring\n * these errors is not recommended as it can lead to orphaned sessions on the\n * server, or security issues if the user believes they have logged out when a\n * bad actor is still using the session. The implementation should consider\n * keeping track of failed deletions and retrying them later, until they\n * succeed.\n *\n * @note this function **must** not throw\n */\n onDeleteFailure?: (\n this: PasswordSession,\n data: SessionData,\n err: DeleteFailure,\n ) => void | Promise<void>\n}\n\nexport class PasswordSession implements Agent {\n /**\n * Internal {@link Agent} used for session management towards the\n * authentication service only.\n */\n #serviceAgent: Agent\n\n #sessionData: null | SessionData\n #sessionPromise: Promise<SessionData>\n\n constructor(\n sessionData: SessionData,\n protected readonly options: PasswordSessionOptions = {},\n ) {\n this.#serviceAgent = buildAgent({\n service: sessionData.service,\n fetch: options.fetch,\n })\n\n this.#sessionData = sessionData\n this.#sessionPromise = Promise.resolve(this.#sessionData)\n }\n\n get did() {\n return this.session.did\n }\n\n get handle() {\n return this.session.handle\n }\n\n get session() {\n if (this.#sessionData) return this.#sessionData\n throw new Error('Logged out')\n }\n\n get destroyed(): boolean {\n return this.#sessionData === null\n }\n\n async fetchHandler(path: string, init: RequestInit): Promise<Response> {\n const headers = new Headers(init.headers)\n if (headers.has('authorization')) {\n throw new TypeError(\"Unexpected 'authorization' header set\")\n }\n\n const sessionPromise = this.#sessionPromise\n const sessionData = await sessionPromise\n\n const fetch = this.options.fetch ?? globalThis.fetch\n\n headers.set('authorization', `Bearer ${sessionData.accessJwt}`)\n const initialRes = await fetch(fetchUrl(sessionData, path), {\n ...init,\n headers,\n })\n\n const refreshNeeded =\n initialRes.status === 401 ||\n (initialRes.status === 400 &&\n (await extractXrpcErrorCode(initialRes)) === 'ExpiredToken')\n\n if (!refreshNeeded) {\n return initialRes\n }\n\n // Refresh session (unless it was already refreshed in the meantime)\n const newSessionPromise =\n this.#sessionPromise === sessionPromise\n ? this.refresh()\n : this.#sessionPromise\n\n // Error should have been propagated through hooks\n const newSessionData = await newSessionPromise.catch((_err) => null)\n if (!newSessionData) {\n return initialRes\n }\n\n // refresh silently failed, no point in retrying.\n if (newSessionData.accessJwt === sessionData.accessJwt) {\n return initialRes\n }\n\n if (init?.signal?.aborted) {\n return initialRes\n }\n\n // The stream was already consumed. We cannot retry the request. A solution\n // would be to tee() the input stream but that would bufferize the entire\n // stream in memory which can lead to memory starvation. Instead, we will\n // return the original response and let the calling code handle retries.\n if (ReadableStream && init?.body instanceof ReadableStream) {\n return initialRes\n }\n\n // Make sure the initial request is cancelled to avoid leaking resources\n // (NodeJS 👀): https://undici.nodejs.org/#/?id=garbage-collection\n if (!initialRes.bodyUsed) {\n await initialRes.body?.cancel()\n }\n\n // Finally, retry the request with the new access token\n headers.set('authorization', `Bearer ${newSessionData.accessJwt}`)\n return fetch(fetchUrl(newSessionData, path), { ...init, headers })\n }\n\n async refresh(): Promise<SessionData> {\n this.#sessionPromise = this.#sessionPromise.then(async (sessionData) => {\n const response = await xrpcSafe(\n this.#serviceAgent,\n com.atproto.server.refreshSession.main,\n { headers: { Authorization: `Bearer ${sessionData.refreshJwt}` } },\n )\n\n if (!response.success && response.matchesSchema()) {\n // Expected errors that indicate the session is no longer valid\n await this.options.onDeleted?.call(this, sessionData)\n\n // Update the session promise to a rejected state\n this.#sessionData = null\n throw response\n }\n\n if (!response.success) {\n response.error\n if (response.matchesSchema()) {\n response.error\n }\n // We failed to refresh the token, assume the session might still be\n // valid by returning the existing session.\n await this.options.onUpdateFailure?.call(this, sessionData, response)\n\n return sessionData\n }\n\n const data = response.body\n\n // Historically, refreshSession did not return all the fields from\n // getSession. In particular, emailConfirmed and didDoc were missing.\n // Similarly, some servers might not return the didDoc in refreshSession.\n // We fetch them via getSession if missing, allowing to ensure that we are\n // always talking with the right PDS.\n if (data.emailConfirmed == null || data.didDoc == null) {\n const extraData = await xrpcSafe(\n this.#serviceAgent,\n com.atproto.server.getSession.main,\n { headers: { Authorization: `Bearer ${data.accessJwt}` } },\n )\n if (extraData.success && extraData.body.did === data.did) {\n Object.assign(data, extraData.body)\n }\n }\n\n const newSession: SessionData = {\n ...data,\n service: sessionData.service,\n }\n\n await this.options.onUpdated?.call(this, newSession)\n\n return (this.#sessionData = newSession)\n })\n\n return this.#sessionPromise\n }\n\n async logout(): Promise<void> {\n let reason: DeleteFailure | null = null\n\n this.#sessionPromise = this.#sessionPromise.then(async (sessionData) => {\n const result = await xrpcSafe(\n this.#serviceAgent,\n com.atproto.server.deleteSession.main,\n { headers: { Authorization: `Bearer ${sessionData.refreshJwt}` } },\n )\n\n if (result.success || result.matchesSchema()) {\n await this.options.onDeleted?.call(this, sessionData)\n\n // Update the session promise to a rejected state\n this.#sessionData = null\n throw new Error('Logged out')\n } else {\n // Capture the reason for the failure to re-throw in the outer promise\n reason = result\n\n // An unknown/unexpected error occurred (network, server down, etc)\n await this.options.onDeleteFailure?.call(this, sessionData, result)\n\n // Keep the session in an active state\n return sessionData\n }\n })\n\n return this.#sessionPromise.then(\n (_session) => {\n // If the promise above resolved, then logout failed. Re-throw the\n // reason captured earlier.\n throw reason!\n },\n (_err) => {\n // Successful logout\n },\n )\n }\n\n static async createAccount(\n body: com.atproto.server.createAccount.InputBody,\n {\n service,\n headers,\n ...options\n }: PasswordSessionOptions & {\n headers?: HeadersInit\n service: string | URL\n },\n ): Promise<PasswordSession> {\n const response = await xrpc(\n buildAgent({ service, headers, fetch: options.fetch }),\n com.atproto.server.createAccount.main,\n { body },\n )\n\n const data: SessionData = {\n ...response.body,\n service: String(service),\n }\n\n const agent = new PasswordSession(data, options)\n await options.onUpdated?.call(agent, data)\n return agent\n }\n\n /**\n * @note It is **not** recommended to use {@link PasswordSession} with main\n * account credentials. Instead, it is strongly advised to use OAuth based\n * authentication for main username/password credentials and use\n * {@link PasswordSession} with an app-password, for bots, scripts, or similar\n * use-cases.\n *\n * @throws If unable to create a session. In particular, if the server\n * requires a 2FA token, a {@link XrpcResponseError} with the\n * `AuthFactorTokenRequired` error code will be thrown.\n *\n *\n * @example Handling 2FA errors\n *\n * ```ts\n * try {\n * const session = await PasswordSession.login({\n * service: 'https://example.com',\n * identifier: 'alice',\n * password: 'correct horse battery staple',\n * })\n * } catch (err) {\n * if (err instanceof XrpcResponseError && err.error === 'AuthFactorTokenRequired') {\n * // Prompt user for 2FA token and re-attempt session creation\n * }\n * }\n * ```\n */\n static async login({\n service,\n identifier,\n password,\n allowTakendown,\n authFactorToken,\n ...options\n }: PasswordSessionOptions & {\n service: string | URL\n identifier: string\n password: string\n allowTakendown?: boolean\n authFactorToken?: string\n }): Promise<PasswordSession> {\n const xrpcAgent = buildAgent({\n service,\n fetch: options.fetch,\n })\n\n const response = await xrpcSafe(\n xrpcAgent,\n com.atproto.server.createSession.main,\n { body: { identifier, password, allowTakendown, authFactorToken } },\n )\n\n if (!response.success) {\n if (response.error === 'AuthFactorTokenRequired') {\n throw new LexAuthFactorError(response)\n }\n throw response.reason\n }\n\n const data: SessionData = {\n ...response.body,\n service: String(service),\n }\n\n const agent = new PasswordSession(data, options)\n await options.onUpdated?.call(agent, data)\n return agent\n }\n\n /**\n * Resume an existing session, ensuring it is still valid by refreshing it.\n * Any error thrown here indicates that the session is definitely no longer\n * valid. Network errors will be propagated through the\n * {@link PasswordSessionOptions.onUpdateFailure} hook, and not re-thrown\n * here. This means that a resolved promise does not necessarily indicate a\n * valid session, only that it's refresh did not definitively fail.\n *\n * This is the same as calling {@link PasswordSession.refresh} after\n * constructing the {@link PasswordSession} manually.\n *\n * @throws If, and only if, the session is definitely no longer valid.\n */\n static async resume(\n data: SessionData,\n options: PasswordSessionOptions,\n ): Promise<PasswordSession> {\n const agent = new PasswordSession(data, options)\n await agent.refresh()\n return agent\n }\n\n /**\n * Delete a session without having to {@link resume resume()} it first, or\n * provide hooks.\n *\n * @throws In case of unexpected error (network issue, server down, etc)\n * meaning that the session may still be valid.\n */\n static async delete(\n data: SessionData,\n options?: PasswordSessionOptions,\n ): Promise<void> {\n const agent = new PasswordSession(data, options)\n await agent.logout()\n }\n}\n\nfunction fetchUrl(sessionData: SessionData, path: string): URL {\n const pdsUrl = extractPdsUrl(sessionData.didDoc)\n return new URL(path, pdsUrl ?? sessionData.service)\n}\n"]}
package/dist/util.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { LexMap } from '@atproto/lex-client';
2
- export declare const noop: () => void;
3
- export declare function extractLexRpcErrorCode(response: Response): Promise<string | null>;
2
+ export declare function extractXrpcErrorCode(response: Response): Promise<string | null>;
4
3
  export declare function extractPdsUrl(didDoc?: LexMap): string | null;
5
4
  //# sourceMappingURL=util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,qBAAqB,CAAA;AAGtD,eAAO,MAAM,IAAI,YAAW,CAAA;AAE5B,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB;AA4BD,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5D"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,qBAAqB,CAAA;AAGtD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB;AA4BD,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5D"}
package/dist/util.js CHANGED
@@ -1,16 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noop = void 0;
4
- exports.extractLexRpcErrorCode = extractLexRpcErrorCode;
3
+ exports.extractXrpcErrorCode = extractXrpcErrorCode;
5
4
  exports.extractPdsUrl = extractPdsUrl;
6
5
  const lex_schema_1 = require("@atproto/lex-schema");
7
- const noop = () => { };
8
- exports.noop = noop;
9
- async function extractLexRpcErrorCode(response) {
6
+ async function extractXrpcErrorCode(response) {
10
7
  const json = await peekJson(response, 10 * 1024); // Avoid reading large bodies
11
8
  if (json === undefined)
12
9
  return null;
13
- if (!lex_schema_1.l.lexErrorData.matches(json))
10
+ if (!lex_schema_1.lexErrorDataSchema.matches(json))
14
11
  return null;
15
12
  return json.error;
16
13
  }
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAKA,wDAOC;AA4BD,sCAMC;AA7CD,oDAAuC;AAEhC,MAAM,IAAI,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;AAAf,QAAA,IAAI,QAAW;AAErB,KAAK,UAAU,sBAAsB,CAC1C,QAAkB;IAElB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA,CAAC,6BAA6B;IAC9E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACnC,IAAI,CAAC,cAAC,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAA;AACnB,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,QAAkB,EAClB,OAAO,GAAG,QAAQ;IAElB,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,IAAI,KAAK,kBAAkB;QAAE,OAAO,SAAS,CAAA;IACjD,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACtC,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,GAAG,OAAO;QAAE,OAAO,SAAS,CAAA;IAExD,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAsB,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EAAE,OAAO,EAAY;IAC1C,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACvC,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,OAAO,EAAY;IACxC,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;AACzE,CAAC;AAED,SAAgB,aAAa,CAAC,MAAe;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5D,QAAQ,CAAE,OAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CACzD,CAAA;IACD,MAAM,WAAW,GAAG,QAAQ,CAAE,UAAkB,EAAE,eAAe,CAAC,CAAA;IAClE,OAAO,WAAW,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA;AACtE,CAAC;AAED,MAAM,QAAQ,GAAG,CAAI,CAAI,EAAE,EAAE,CAC3B,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAIvB,CAAA;AAEjB,MAAM,OAAO,GAAG,CAAI,CAAI,EAAE,EAAE,CAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAIlB,CAAA","sourcesContent":["import { LexMap, LexValue } from '@atproto/lex-client'\nimport { l } from '@atproto/lex-schema'\n\nexport const noop = () => {}\n\nexport async function extractLexRpcErrorCode(\n response: Response,\n): Promise<string | null> {\n const json = await peekJson(response, 10 * 1024) // Avoid reading large bodies\n if (json === undefined) return null\n if (!l.lexErrorData.matches(json)) return null\n return json.error\n}\n\nasync function peekJson(\n response: Response,\n maxSize = Infinity,\n): Promise<undefined | LexValue> {\n const type = extractType(response)\n if (type !== 'application/json') return undefined\n const length = extractLength(response)\n if (length != null && length > maxSize) return undefined\n\n try {\n return (await response.clone().json()) as Promise<LexValue>\n } catch {\n return undefined\n }\n}\n\nfunction extractLength({ headers }: Response) {\n return headers.get('Content-Length')\n ? Number(headers.get('Content-Length'))\n : undefined\n}\n\nfunction extractType({ headers }: Response) {\n return headers.get('Content-Type')?.split(';')[0]?.trim().toLowerCase()\n}\n\nexport function extractPdsUrl(didDoc?: LexMap): string | null {\n const pdsService = ifArray(didDoc?.service)?.find((service) =>\n ifString((service as any)?.id)?.endsWith('#atproto_pds'),\n )\n const pdsEndpoint = ifString((pdsService as any)?.serviceEndpoint)\n return pdsEndpoint && URL.canParse(pdsEndpoint) ? pdsEndpoint : null\n}\n\nconst ifString = <T>(v: T) =>\n (typeof v === 'string' ? v : undefined) as unknown extends T\n ? undefined | string\n : T extends string\n ? string\n : undefined\n\nconst ifArray = <T>(v: T) =>\n (Array.isArray(v) ? v : undefined) as unknown extends T\n ? undefined | unknown[]\n : T extends unknown[]\n ? Extract<T, unknown[]>\n : undefined\n"]}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAGA,oDAOC;AA4BD,sCAMC;AA3CD,oDAAwD;AAEjD,KAAK,UAAU,oBAAoB,CACxC,QAAkB;IAElB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA,CAAC,6BAA6B;IAC9E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACnC,IAAI,CAAC,+BAAkB,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAClD,OAAO,IAAI,CAAC,KAAK,CAAA;AACnB,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,QAAkB,EAClB,OAAO,GAAG,QAAQ;IAElB,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,IAAI,KAAK,kBAAkB;QAAE,OAAO,SAAS,CAAA;IACjD,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACtC,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,GAAG,OAAO;QAAE,OAAO,SAAS,CAAA;IAExD,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAsB,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EAAE,OAAO,EAAY;IAC1C,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACvC,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,OAAO,EAAY;IACxC,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;AACzE,CAAC;AAED,SAAgB,aAAa,CAAC,MAAe;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5D,QAAQ,CAAE,OAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CACzD,CAAA;IACD,MAAM,WAAW,GAAG,QAAQ,CAAE,UAAkB,EAAE,eAAe,CAAC,CAAA;IAClE,OAAO,WAAW,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA;AACtE,CAAC;AAED,MAAM,QAAQ,GAAG,CAAI,CAAI,EAAE,EAAE,CAC3B,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAIvB,CAAA;AAEjB,MAAM,OAAO,GAAG,CAAI,CAAI,EAAE,EAAE,CAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAIlB,CAAA","sourcesContent":["import { LexMap, LexValue } from '@atproto/lex-client'\nimport { lexErrorDataSchema } from '@atproto/lex-schema'\n\nexport async function extractXrpcErrorCode(\n response: Response,\n): Promise<string | null> {\n const json = await peekJson(response, 10 * 1024) // Avoid reading large bodies\n if (json === undefined) return null\n if (!lexErrorDataSchema.matches(json)) return null\n return json.error\n}\n\nasync function peekJson(\n response: Response,\n maxSize = Infinity,\n): Promise<undefined | LexValue> {\n const type = extractType(response)\n if (type !== 'application/json') return undefined\n const length = extractLength(response)\n if (length != null && length > maxSize) return undefined\n\n try {\n return (await response.clone().json()) as Promise<LexValue>\n } catch {\n return undefined\n }\n}\n\nfunction extractLength({ headers }: Response) {\n return headers.get('Content-Length')\n ? Number(headers.get('Content-Length'))\n : undefined\n}\n\nfunction extractType({ headers }: Response) {\n return headers.get('Content-Type')?.split(';')[0]?.trim().toLowerCase()\n}\n\nexport function extractPdsUrl(didDoc?: LexMap): string | null {\n const pdsService = ifArray(didDoc?.service)?.find((service) =>\n ifString((service as any)?.id)?.endsWith('#atproto_pds'),\n )\n const pdsEndpoint = ifString((pdsService as any)?.serviceEndpoint)\n return pdsEndpoint && URL.canParse(pdsEndpoint) ? pdsEndpoint : null\n}\n\nconst ifString = <T>(v: T) =>\n (typeof v === 'string' ? v : undefined) as unknown extends T\n ? undefined | string\n : T extends string\n ? string\n : undefined\n\nconst ifArray = <T>(v: T) =>\n (Array.isArray(v) ? v : undefined) as unknown extends T\n ? undefined | unknown[]\n : T extends unknown[]\n ? Extract<T, unknown[]>\n : undefined\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-password-session",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "license": "MIT",
5
5
  "description": "Password based client authentication for AT Lexicons",
6
6
  "keywords": [
@@ -36,13 +36,13 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "tslib": "^2.8.1",
39
- "@atproto/lex-client": "0.0.9",
40
- "@atproto/lex-schema": "0.0.9"
39
+ "@atproto/lex-client": "^0.0.11",
40
+ "@atproto/lex-schema": "^0.0.11"
41
41
  },
42
42
  "devDependencies": {
43
43
  "vitest": "^4.0.16",
44
- "@atproto/lex-builder": "0.0.11",
45
- "@atproto/lex-server": "0.0.5"
44
+ "@atproto/lex-builder": "^0.0.13",
45
+ "@atproto/lex-server": "^0.0.8"
46
46
  },
47
47
  "scripts": {
48
48
  "prebuild": "node ./scripts/lex-build.mjs",
package/src/error.ts CHANGED
@@ -1,14 +1,13 @@
1
- import { LexError, LexRpcResponseError } from '@atproto/lex-client'
2
- import { com } from './lexicons'
1
+ import { LexError, XrpcFailure } from '@atproto/lex-client'
3
2
 
4
3
  export class LexAuthFactorError extends LexError {
5
4
  name = 'LexAuthFactorError'
6
5
 
7
- constructor(
8
- readonly response: LexRpcResponseError<
9
- typeof com.atproto.server.createSession.main
10
- >,
11
- ) {
12
- super(response.error, response.message, { cause: response.reason })
6
+ constructor(readonly cause: XrpcFailure) {
7
+ super(cause.error, cause.message ?? 'Auth factor token required', { cause })
8
+ }
9
+
10
+ override toResponse(): Response {
11
+ return Response.json({ error: 'InternalServerError' }, { status: 500 })
13
12
  }
14
13
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-namespace */
2
2
 
3
3
  import { afterAll, assert, beforeAll, describe, expect, it, vi } from 'vitest'
4
- import { Client, LexRpcResponseError } from '@atproto/lex-client'
4
+ import { Client, XrpcAuthenticationError } from '@atproto/lex-client'
5
5
  import { l } from '@atproto/lex-schema'
6
6
  import { LexRouter, LexServerAuthError } from '@atproto/lex-server'
7
7
  import { Server, serve } from '@atproto/lex-server/nodejs'
@@ -171,7 +171,7 @@ describe(PasswordSession, () => {
171
171
  const onUpdated: PasswordSessionOptions['onUpdated'] = vi.fn()
172
172
 
173
173
  await expect(
174
- PasswordSession.create({
174
+ PasswordSession.login({
175
175
  ...defaultOptions,
176
176
  service: entrywayOrigin,
177
177
  identifier: 'alice',
@@ -181,7 +181,6 @@ describe(PasswordSession, () => {
181
181
  }),
182
182
  ).rejects.toMatchObject({
183
183
  success: false,
184
- status: 401,
185
184
  error: 'AuthenticationRequired',
186
185
  })
187
186
 
@@ -193,7 +192,7 @@ describe(PasswordSession, () => {
193
192
  const onDeleted: PasswordSessionOptions['onDeleted'] = vi.fn()
194
193
  const onUpdated: PasswordSessionOptions['onUpdated'] = vi.fn()
195
194
 
196
- const result = await PasswordSession.create({
195
+ const result = await PasswordSession.login({
197
196
  ...defaultOptions,
198
197
  service: entrywayOrigin,
199
198
  identifier: 'alice',
@@ -217,7 +216,7 @@ describe(PasswordSession, () => {
217
216
  const onDeleted: PasswordSessionOptions['onDeleted'] = vi.fn()
218
217
  const onUpdated: PasswordSessionOptions['onUpdated'] = vi.fn()
219
218
 
220
- const session = await PasswordSession.create({
219
+ const session = await PasswordSession.login({
221
220
  ...defaultOptions,
222
221
  service: entrywayOrigin,
223
222
  identifier: 'alice',
@@ -253,7 +252,12 @@ describe(PasswordSession, () => {
253
252
 
254
253
  await expect(
255
254
  client.call(app.example.customMethod, { message: 'hello' }),
256
- ).rejects.toThrow('Logged out')
255
+ ).rejects.toMatchObject({
256
+ message: 'Unable to fulfill XRPC request',
257
+ cause: expect.objectContaining({
258
+ message: 'Logged out',
259
+ }),
260
+ })
257
261
  })
258
262
 
259
263
  it('fails to perform unauthenticated call', async () => {
@@ -263,22 +267,21 @@ describe(PasswordSession, () => {
263
267
  })
264
268
 
265
269
  assert(result.success === false)
266
- assert(result instanceof LexRpcResponseError)
270
+ assert(result instanceof XrpcAuthenticationError)
267
271
  expect(result).toMatchObject({
268
272
  success: false,
269
- status: 401,
270
273
  error: 'AuthenticationRequired',
271
274
  })
272
- expect(result.headers.get('www-authenticate')).toBe(
273
- 'Bearer realm="access token"',
274
- )
275
+ expect(result.wwwAuthenticate).toEqual({
276
+ Bearer: { realm: 'access token' },
277
+ })
275
278
  })
276
279
 
277
280
  it('refreshes expired token', async () => {
278
281
  const onDeleted: PasswordSessionOptions['onDeleted'] = vi.fn()
279
282
  const onUpdated: PasswordSessionOptions['onUpdated'] = vi.fn()
280
283
 
281
- const session = await PasswordSession.create({
284
+ const session = await PasswordSession.login({
282
285
  ...defaultOptions,
283
286
  service: entrywayOrigin,
284
287
  identifier: 'bob',
@@ -332,7 +335,7 @@ describe(PasswordSession, () => {
332
335
  const onDeleted: PasswordSessionOptions['onDeleted'] = vi.fn()
333
336
  const onUpdated: PasswordSessionOptions['onUpdated'] = vi.fn()
334
337
 
335
- const initialAgent = await PasswordSession.create({
338
+ const initialAgent = await PasswordSession.login({
336
339
  ...defaultOptions,
337
340
  service: entrywayOrigin,
338
341
  identifier: 'carla',
@@ -368,7 +371,6 @@ describe(PasswordSession, () => {
368
371
  await expect(initialAgent.refresh()).rejects.toMatchObject({
369
372
  success: false,
370
373
  error: 'ExpiredToken',
371
- status: 401,
372
374
  })
373
375
 
374
376
  expect(onDeleted).toHaveBeenCalledTimes(1)
@@ -393,7 +395,7 @@ describe(PasswordSession, () => {
393
395
  it('silently ignores expected logout errors', async () => {
394
396
  let sessionData: SessionData | null = null
395
397
 
396
- const session = await PasswordSession.create({
398
+ const session = await PasswordSession.login({
397
399
  ...defaultOptions,
398
400
  service: entrywayOrigin,
399
401
  identifier: 'dave',
@@ -402,7 +404,6 @@ describe(PasswordSession, () => {
402
404
  onUpdated: (data) => {
403
405
  sessionData = structuredClone(data)
404
406
  },
405
- onDeleted: () => {},
406
407
  })
407
408
 
408
409
  assert(sessionData)
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  Agent,
3
- LexRpcError,
4
- LexRpcFailure,
3
+ XrpcFailure,
5
4
  buildAgent,
5
+ xrpc,
6
6
  xrpcSafe,
7
7
  } from '@atproto/lex-client'
8
8
  import { LexAuthFactorError } from './error.js'
9
9
  import { com } from './lexicons/index.js'
10
- import { extractLexRpcErrorCode, extractPdsUrl, noop } from './util.js'
10
+ import { extractPdsUrl, extractXrpcErrorCode } from './util.js'
11
11
 
12
- export type RefreshFailure = LexRpcFailure<
12
+ export type RefreshFailure = XrpcFailure<
13
13
  typeof com.atproto.server.refreshSession.main
14
14
  >
15
15
 
16
- export type DeleteFailure = LexRpcFailure<
16
+ export type DeleteFailure = XrpcFailure<
17
17
  typeof com.atproto.server.deleteSession.main
18
18
  >
19
19
 
@@ -37,7 +37,7 @@ export type PasswordSessionOptions = {
37
37
  *
38
38
  * @note this function **must** not throw
39
39
  */
40
- onUpdated: (this: PasswordSession, data: SessionData) => void | Promise<void>
40
+ onUpdated?: (this: PasswordSession, data: SessionData) => void | Promise<void>
41
41
 
42
42
  /**
43
43
  * Called whenever the session update fails due to an expected error, such as
@@ -61,7 +61,7 @@ export type PasswordSessionOptions = {
61
61
  *
62
62
  * @note this function **must** not throw
63
63
  */
64
- onDeleted: (this: PasswordSession, data: SessionData) => void | Promise<void>
64
+ onDeleted?: (this: PasswordSession, data: SessionData) => void | Promise<void>
65
65
 
66
66
  /**
67
67
  * Called whenever a session deletion fails due to an unexpected error, such
@@ -96,7 +96,7 @@ export class PasswordSession implements Agent {
96
96
 
97
97
  constructor(
98
98
  sessionData: SessionData,
99
- protected readonly options: PasswordSessionOptions,
99
+ protected readonly options: PasswordSessionOptions = {},
100
100
  ) {
101
101
  this.#serviceAgent = buildAgent({
102
102
  service: sessionData.service,
@@ -117,7 +117,7 @@ export class PasswordSession implements Agent {
117
117
 
118
118
  get session() {
119
119
  if (this.#sessionData) return this.#sessionData
120
- throw new LexRpcError('AuthenticationRequired', 'Logged out')
120
+ throw new Error('Logged out')
121
121
  }
122
122
 
123
123
  get destroyed(): boolean {
@@ -144,7 +144,7 @@ export class PasswordSession implements Agent {
144
144
  const refreshNeeded =
145
145
  initialRes.status === 401 ||
146
146
  (initialRes.status === 400 &&
147
- (await extractLexRpcErrorCode(initialRes)) === 'ExpiredToken')
147
+ (await extractXrpcErrorCode(initialRes)) === 'ExpiredToken')
148
148
 
149
149
  if (!refreshNeeded) {
150
150
  return initialRes
@@ -200,7 +200,7 @@ export class PasswordSession implements Agent {
200
200
 
201
201
  if (!response.success && response.matchesSchema()) {
202
202
  // Expected errors that indicate the session is no longer valid
203
- await this.options.onDeleted.call(this, sessionData)
203
+ await this.options.onDeleted?.call(this, sessionData)
204
204
 
205
205
  // Update the session promise to a rejected state
206
206
  this.#sessionData = null
@@ -208,6 +208,10 @@ export class PasswordSession implements Agent {
208
208
  }
209
209
 
210
210
  if (!response.success) {
211
+ response.error
212
+ if (response.matchesSchema()) {
213
+ response.error
214
+ }
211
215
  // We failed to refresh the token, assume the session might still be
212
216
  // valid by returning the existing session.
213
217
  await this.options.onUpdateFailure?.call(this, sessionData, response)
@@ -238,7 +242,7 @@ export class PasswordSession implements Agent {
238
242
  service: sessionData.service,
239
243
  }
240
244
 
241
- await this.options.onUpdated.call(this, newSession)
245
+ await this.options.onUpdated?.call(this, newSession)
242
246
 
243
247
  return (this.#sessionData = newSession)
244
248
  })
@@ -257,11 +261,11 @@ export class PasswordSession implements Agent {
257
261
  )
258
262
 
259
263
  if (result.success || result.matchesSchema()) {
260
- await this.options.onDeleted.call(this, sessionData)
264
+ await this.options.onDeleted?.call(this, sessionData)
261
265
 
262
266
  // Update the session promise to a rejected state
263
267
  this.#sessionData = null
264
- throw new LexRpcError('AuthenticationRequired', 'Logged out')
268
+ throw new Error('Logged out')
265
269
  } else {
266
270
  // Capture the reason for the failure to re-throw in the outer promise
267
271
  reason = result
@@ -286,6 +290,33 @@ export class PasswordSession implements Agent {
286
290
  )
287
291
  }
288
292
 
293
+ static async createAccount(
294
+ body: com.atproto.server.createAccount.InputBody,
295
+ {
296
+ service,
297
+ headers,
298
+ ...options
299
+ }: PasswordSessionOptions & {
300
+ headers?: HeadersInit
301
+ service: string | URL
302
+ },
303
+ ): Promise<PasswordSession> {
304
+ const response = await xrpc(
305
+ buildAgent({ service, headers, fetch: options.fetch }),
306
+ com.atproto.server.createAccount.main,
307
+ { body },
308
+ )
309
+
310
+ const data: SessionData = {
311
+ ...response.body,
312
+ service: String(service),
313
+ }
314
+
315
+ const agent = new PasswordSession(data, options)
316
+ await options.onUpdated?.call(agent, data)
317
+ return agent
318
+ }
319
+
289
320
  /**
290
321
  * @note It is **not** recommended to use {@link PasswordSession} with main
291
322
  * account credentials. Instead, it is strongly advised to use OAuth based
@@ -294,7 +325,7 @@ export class PasswordSession implements Agent {
294
325
  * use-cases.
295
326
  *
296
327
  * @throws If unable to create a session. In particular, if the server
297
- * requires a 2FA token, a {@link LexRpcResponseError} with the
328
+ * requires a 2FA token, a {@link XrpcResponseError} with the
298
329
  * `AuthFactorTokenRequired` error code will be thrown.
299
330
  *
300
331
  *
@@ -302,19 +333,19 @@ export class PasswordSession implements Agent {
302
333
  *
303
334
  * ```ts
304
335
  * try {
305
- * const session = await PasswordSession.create({
336
+ * const session = await PasswordSession.login({
306
337
  * service: 'https://example.com',
307
338
  * identifier: 'alice',
308
339
  * password: 'correct horse battery staple',
309
340
  * })
310
341
  * } catch (err) {
311
- * if (err instanceof LexRpcResponseError && err.error === 'AuthFactorTokenRequired') {
342
+ * if (err instanceof XrpcResponseError && err.error === 'AuthFactorTokenRequired') {
312
343
  * // Prompt user for 2FA token and re-attempt session creation
313
344
  * }
314
345
  * }
315
346
  * ```
316
347
  */
317
- static async create({
348
+ static async login({
318
349
  service,
319
350
  identifier,
320
351
  password,
@@ -352,7 +383,7 @@ export class PasswordSession implements Agent {
352
383
  }
353
384
 
354
385
  const agent = new PasswordSession(data, options)
355
- await options.onUpdated.call(agent, data)
386
+ await options.onUpdated?.call(agent, data)
356
387
  return agent
357
388
  }
358
389
 
@@ -387,13 +418,9 @@ export class PasswordSession implements Agent {
387
418
  */
388
419
  static async delete(
389
420
  data: SessionData,
390
- options?: Partial<PasswordSessionOptions>,
421
+ options?: PasswordSessionOptions,
391
422
  ): Promise<void> {
392
- const agent = new PasswordSession(data, {
393
- ...options,
394
- onUpdated: options?.onUpdated ?? noop,
395
- onDeleted: options?.onDeleted ?? noop,
396
- })
423
+ const agent = new PasswordSession(data, options)
397
424
  await agent.logout()
398
425
  }
399
426
  }
package/src/util.ts CHANGED
@@ -1,14 +1,12 @@
1
1
  import { LexMap, LexValue } from '@atproto/lex-client'
2
- import { l } from '@atproto/lex-schema'
2
+ import { lexErrorDataSchema } from '@atproto/lex-schema'
3
3
 
4
- export const noop = () => {}
5
-
6
- export async function extractLexRpcErrorCode(
4
+ export async function extractXrpcErrorCode(
7
5
  response: Response,
8
6
  ): Promise<string | null> {
9
7
  const json = await peekJson(response, 10 * 1024) // Avoid reading large bodies
10
8
  if (json === undefined) return null
11
- if (!l.lexErrorData.matches(json)) return null
9
+ if (!lexErrorDataSchema.matches(json)) return null
12
10
  return json.error
13
11
  }
14
12