@atproto/lex-resolver 0.0.3 → 0.0.5
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 +65 -0
- package/dist/lex-resolver-error.d.ts +1 -0
- package/dist/lex-resolver-error.d.ts.map +1 -1
- package/dist/lex-resolver-error.js +4 -0
- package/dist/lex-resolver-error.js.map +1 -1
- package/dist/lex-resolver.d.ts +42 -7
- package/dist/lex-resolver.d.ts.map +1 -1
- package/dist/lex-resolver.js +112 -44
- package/dist/lex-resolver.js.map +1 -1
- package/dist/lexicons/com/atproto/sync/getRecord.d.ts +3 -0
- package/dist/lexicons/com/atproto/sync/getRecord.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync/getRecord.defs.d.ts +32 -0
- package/dist/lexicons/com/atproto/sync/getRecord.defs.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync/getRecord.defs.js +30 -0
- package/dist/lexicons/com/atproto/sync/getRecord.defs.js.map +1 -0
- package/dist/lexicons/com/atproto/sync/getRecord.js +10 -0
- package/dist/lexicons/com/atproto/sync/getRecord.js.map +1 -0
- package/dist/lexicons/com/atproto/sync.d.ts +2 -0
- package/dist/lexicons/com/atproto/sync.d.ts.map +1 -0
- package/dist/lexicons/com/atproto/sync.js +9 -0
- package/dist/lexicons/com/atproto/sync.js.map +1 -0
- package/dist/lexicons/com/atproto.d.ts +2 -0
- package/dist/lexicons/com/atproto.d.ts.map +1 -0
- package/dist/lexicons/com/atproto.js +9 -0
- package/dist/lexicons/com/atproto.js.map +1 -0
- package/dist/lexicons/com.d.ts +2 -0
- package/dist/lexicons/com.d.ts.map +1 -0
- package/dist/lexicons/com.js +9 -0
- package/dist/lexicons/com.js.map +1 -0
- package/package.json +18 -7
- package/src/lex-resolver-error.ts +7 -0
- package/src/lex-resolver.ts +197 -75
- package/src/lexicons/com/atproto/sync/getRecord.defs.ts +38 -0
- package/src/lexicons/com/atproto/sync/getRecord.ts +6 -0
- package/src/lexicons/com/atproto/sync.ts +5 -0
- package/src/lexicons/com/atproto.ts +5 -0
- package/src/lexicons/com.ts +5 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +7 -0
- package/tsconfig.tests.json +9 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @atproto/lex-resolver
|
|
2
|
+
|
|
3
|
+
## 0.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`693784c`](https://github.com/bluesky-social/atproto/commit/693784c3a0dee4b6a29aa1e018fce682dcae148f), [`d551b0e`](https://github.com/bluesky-social/atproto/commit/d551b0e3527714c111c3ec6e4c90ad7f46369fab)]:
|
|
8
|
+
- @atproto/lex-data@0.0.3
|
|
9
|
+
- @atproto/lex-document@0.0.5
|
|
10
|
+
- @atproto/lex-client@0.0.4
|
|
11
|
+
- @atproto/lex-schema@0.0.4
|
|
12
|
+
- @atproto/repo@0.8.12
|
|
13
|
+
|
|
14
|
+
## 0.0.4
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [#4384](https://github.com/bluesky-social/atproto/pull/4384) [`d396de0`](https://github.com/bluesky-social/atproto/commit/d396de016d1d55d08cfad1dabd3ffd9eaeea76ea) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Verify the signature when fetching lexicon document from a repo
|
|
19
|
+
|
|
20
|
+
- [#4390](https://github.com/bluesky-social/atproto/pull/4390) [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Replace use of `CID` with `Cid`
|
|
21
|
+
|
|
22
|
+
- [#4383](https://github.com/bluesky-social/atproto/pull/4383) [`8012627`](https://github.com/bluesky-social/atproto/commit/8012627a1226cb2f1c753385ad2497b6b43ffd2e) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add hooks
|
|
23
|
+
|
|
24
|
+
- [#4383](https://github.com/bluesky-social/atproto/pull/4383) [`8012627`](https://github.com/bluesky-social/atproto/commit/8012627a1226cb2f1c753385ad2497b6b43ffd2e) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `LexResolverError.from(string)` utility
|
|
25
|
+
|
|
26
|
+
- [#4383](https://github.com/bluesky-social/atproto/pull/4383) [`8012627`](https://github.com/bluesky-social/atproto/commit/8012627a1226cb2f1c753385ad2497b6b43ffd2e) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove `didAuthority` option (Replaced by `hooks.onResolveAuthority`)
|
|
27
|
+
|
|
28
|
+
- [#4390](https://github.com/bluesky-social/atproto/pull/4390) [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Rename schema methods `validate`, `check` and `maybe` to `safeParse`, `matches` and `ifMatches` respectively.
|
|
29
|
+
|
|
30
|
+
- [#4397](https://github.com/bluesky-social/atproto/pull/4397) [`688f9d6`](https://github.com/bluesky-social/atproto/commit/688f9d67597ba96d6e9c4a4aec4d394d42f4cbf4) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `CHANGELOG.md` to npm package
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [[`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`2d13d05`](https://github.com/bluesky-social/atproto/commit/2d13d05ab06576703742b1b638d2f243b6b2915f), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`a487ab8`](https://github.com/bluesky-social/atproto/commit/a487ab8afe8f18d00662e666049be8d28de2b57e), [`a487ab8`](https://github.com/bluesky-social/atproto/commit/a487ab8afe8f18d00662e666049be8d28de2b57e), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`bcae2b7`](https://github.com/bluesky-social/atproto/commit/bcae2b77b68da6dc2ec202651c8bf41fd5769f69), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`a487ab8`](https://github.com/bluesky-social/atproto/commit/a487ab8afe8f18d00662e666049be8d28de2b57e), [`a487ab8`](https://github.com/bluesky-social/atproto/commit/a487ab8afe8f18d00662e666049be8d28de2b57e), [`bcae2b7`](https://github.com/bluesky-social/atproto/commit/bcae2b77b68da6dc2ec202651c8bf41fd5769f69), [`03a2a4b`](https://github.com/bluesky-social/atproto/commit/03a2a4bb3814ced7ad1d4fe6c94b5348a3bbc097), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`bcae2b7`](https://github.com/bluesky-social/atproto/commit/bcae2b77b68da6dc2ec202651c8bf41fd5769f69), [`bcae2b7`](https://github.com/bluesky-social/atproto/commit/bcae2b77b68da6dc2ec202651c8bf41fd5769f69), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`d396de0`](https://github.com/bluesky-social/atproto/commit/d396de016d1d55d08cfad1dabd3ffd9eaeea76ea), [`a487ab8`](https://github.com/bluesky-social/atproto/commit/a487ab8afe8f18d00662e666049be8d28de2b57e), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`9f87ff3`](https://github.com/bluesky-social/atproto/commit/9f87ff3aa60090c8c38b6ce400cba6ceff5cd2e9), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba), [`688f9d6`](https://github.com/bluesky-social/atproto/commit/688f9d67597ba96d6e9c4a4aec4d394d42f4cbf4), [`1d445af`](https://github.com/bluesky-social/atproto/commit/1d445af2a7fc27eca5a45869b29266e6a2a7f3ba)]:
|
|
33
|
+
- @atproto/lex-schema@0.0.3
|
|
34
|
+
- @atproto/lex-document@0.0.4
|
|
35
|
+
- @atproto/lex-client@0.0.3
|
|
36
|
+
- @atproto/lex-data@0.0.2
|
|
37
|
+
- @atproto/syntax@0.4.2
|
|
38
|
+
- @atproto/crypto@0.4.5
|
|
39
|
+
- @atproto-labs/did-resolver@0.2.4
|
|
40
|
+
|
|
41
|
+
## 0.0.3
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- Updated dependencies []:
|
|
46
|
+
- @atproto/lex-client@0.0.2
|
|
47
|
+
- @atproto/lex-document@0.0.3
|
|
48
|
+
|
|
49
|
+
## 0.0.2
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- Updated dependencies [[`5ffd612`](https://github.com/bluesky-social/atproto/commit/5ffd6129909071e979c30f31266119865ab582b6)]:
|
|
54
|
+
- @atproto/lex-document@0.0.2
|
|
55
|
+
- @atproto/lex-client@0.0.1
|
|
56
|
+
|
|
57
|
+
## 0.0.1
|
|
58
|
+
|
|
59
|
+
### Patch Changes
|
|
60
|
+
|
|
61
|
+
- [#4371](https://github.com/bluesky-social/atproto/pull/4371) [`46550d6`](https://github.com/bluesky-social/atproto/commit/46550d6c1ffb298f57d54eb1904067b2df5a40af) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Release
|
|
62
|
+
|
|
63
|
+
- Updated dependencies [[`46550d6`](https://github.com/bluesky-social/atproto/commit/46550d6c1ffb298f57d54eb1904067b2df5a40af)]:
|
|
64
|
+
- @atproto/lex-client@0.0.1
|
|
65
|
+
- @atproto/lex-document@0.0.1
|
|
@@ -4,5 +4,6 @@ export declare class LexResolverError extends Error {
|
|
|
4
4
|
readonly description: string;
|
|
5
5
|
name: string;
|
|
6
6
|
constructor(nsid: NSID, description?: string, options?: ErrorOptions);
|
|
7
|
+
static from(nsid: NSID | string, description?: string): LexResolverError;
|
|
7
8
|
}
|
|
8
9
|
//# sourceMappingURL=lex-resolver-error.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-resolver-error.d.ts","sourceRoot":"","sources":["../src/lex-resolver-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC,qBAAa,gBAAiB,SAAQ,KAAK;aAIvB,IAAI,EAAE,IAAI;aACV,WAAW;IAJ7B,IAAI,SAAqB;gBAGP,IAAI,EAAE,IAAI,EACV,WAAW,SAAuC,EAClE,OAAO,CAAC,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"lex-resolver-error.d.ts","sourceRoot":"","sources":["../src/lex-resolver-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC,qBAAa,gBAAiB,SAAQ,KAAK;aAIvB,IAAI,EAAE,IAAI;aACV,WAAW;IAJ7B,IAAI,SAAqB;gBAGP,IAAI,EAAE,IAAI,EACV,WAAW,SAAuC,EAClE,OAAO,CAAC,EAAE,YAAY;IAKxB,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;CAMtD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LexResolverError = void 0;
|
|
4
|
+
const syntax_1 = require("@atproto/syntax");
|
|
4
5
|
class LexResolverError extends Error {
|
|
5
6
|
nsid;
|
|
6
7
|
description;
|
|
@@ -10,6 +11,9 @@ class LexResolverError extends Error {
|
|
|
10
11
|
this.nsid = nsid;
|
|
11
12
|
this.description = description;
|
|
12
13
|
}
|
|
14
|
+
static from(nsid, description) {
|
|
15
|
+
return new LexResolverError(typeof nsid === 'string' ? syntax_1.NSID.from(nsid) : nsid, description);
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
exports.LexResolverError = LexResolverError;
|
|
15
19
|
//# sourceMappingURL=lex-resolver-error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-resolver-error.js","sourceRoot":"","sources":["../src/lex-resolver-error.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"lex-resolver-error.js","sourceRoot":"","sources":["../src/lex-resolver-error.ts"],"names":[],"mappings":";;;AAAA,4CAAsC;AAEtC,MAAa,gBAAiB,SAAQ,KAAK;IAIvB;IACA;IAJlB,IAAI,GAAG,kBAAkB,CAAA;IAEzB,YACkB,IAAU,EACV,cAAc,oCAAoC,EAClE,OAAsB;QAEtB,KAAK,CAAC,GAAG,WAAW,KAAK,IAAI,GAAG,EAAE,OAAO,CAAC,CAAA;QAJ1B,SAAI,GAAJ,IAAI,CAAM;QACV,gBAAW,GAAX,WAAW,CAAuC;IAIpE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,IAAmB,EAAE,WAAoB;QACnD,OAAO,IAAI,gBAAgB,CACzB,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EACjD,WAAW,CACZ,CAAA;IACH,CAAC;CACF;AAjBD,4CAiBC","sourcesContent":["import { NSID } from '@atproto/syntax'\n\nexport class LexResolverError extends Error {\n name = 'LexResolverError'\n\n constructor(\n public readonly nsid: NSID,\n public readonly description = `Could not resolve Lexicon for NSID`,\n options?: ErrorOptions,\n ) {\n super(`${description} (${nsid})`, options)\n }\n\n static from(nsid: NSID | string, description?: string) {\n return new LexResolverError(\n typeof nsid === 'string' ? NSID.from(nsid) : nsid,\n description,\n )\n }\n}\n"]}
|
package/dist/lex-resolver.d.ts
CHANGED
|
@@ -1,20 +1,55 @@
|
|
|
1
|
+
import { Cid } from '@atproto/lex-data';
|
|
1
2
|
import { LexiconDocument } from '@atproto/lex-document';
|
|
2
3
|
import { AtUri, NSID } from '@atproto/syntax';
|
|
3
4
|
import { CreateDidResolverOptions, Did, DidResolver, ResolveDidOptions } from '@atproto-labs/did-resolver';
|
|
5
|
+
export type LexResolverResult = {
|
|
6
|
+
uri: AtUri;
|
|
7
|
+
cid: Cid;
|
|
8
|
+
lexicon: LexiconDocument;
|
|
9
|
+
};
|
|
10
|
+
export type LexResolverFetchResult = {
|
|
11
|
+
cid: Cid;
|
|
12
|
+
lexicon: LexiconDocument;
|
|
13
|
+
};
|
|
14
|
+
type Awaitable<T> = T | Promise<T>;
|
|
15
|
+
export type LexResolverHooks = {
|
|
16
|
+
onResolveAuthority?(data: {
|
|
17
|
+
nsid: NSID;
|
|
18
|
+
}): Awaitable<void | Did>;
|
|
19
|
+
onResolveAuthorityResult?(data: {
|
|
20
|
+
nsid: NSID;
|
|
21
|
+
did: Did;
|
|
22
|
+
}): Awaitable<void>;
|
|
23
|
+
onResolveAuthorityError?(data: {
|
|
24
|
+
nsid: NSID;
|
|
25
|
+
err: unknown;
|
|
26
|
+
}): Awaitable<void>;
|
|
27
|
+
onFetch?(data: {
|
|
28
|
+
uri: AtUri;
|
|
29
|
+
}): Awaitable<void | LexResolverFetchResult>;
|
|
30
|
+
onFetchResult?(data: {
|
|
31
|
+
uri: AtUri;
|
|
32
|
+
cid: Cid;
|
|
33
|
+
lexicon: LexiconDocument;
|
|
34
|
+
}): Awaitable<void>;
|
|
35
|
+
onFetchError?(data: {
|
|
36
|
+
uri: AtUri;
|
|
37
|
+
err: unknown;
|
|
38
|
+
}): Awaitable<void>;
|
|
39
|
+
};
|
|
4
40
|
export type LexResolverOptions = CreateDidResolverOptions & {
|
|
5
|
-
|
|
41
|
+
hooks?: LexResolverHooks;
|
|
6
42
|
};
|
|
7
|
-
export { AtUri, NSID };
|
|
43
|
+
export { AtUri, type Cid, NSID };
|
|
8
44
|
export type { LexiconDocument, ResolveDidOptions };
|
|
9
45
|
export declare class LexResolver {
|
|
10
46
|
protected readonly options: LexResolverOptions;
|
|
11
47
|
protected readonly didResolver: DidResolver<'plc' | 'web'>;
|
|
12
48
|
constructor(options: LexResolverOptions);
|
|
13
|
-
get(nsidStr: NSID | string, options?: ResolveDidOptions): Promise<
|
|
14
|
-
uri: AtUri;
|
|
15
|
-
document: LexiconDocument;
|
|
16
|
-
}>;
|
|
49
|
+
get(nsidStr: NSID | string, options?: ResolveDidOptions): Promise<LexResolverResult>;
|
|
17
50
|
resolve(nsidStr: NSID | string): Promise<AtUri>;
|
|
18
|
-
|
|
51
|
+
protected resolveLexiconAuthority(nsid: NSID): Promise<Did>;
|
|
52
|
+
fetch(uriStr: AtUri | string, options?: ResolveDidOptions): Promise<LexResolverResult>;
|
|
53
|
+
protected fetchLexiconUri(uri: AtUri, options?: ResolveDidOptions): Promise<LexResolverFetchResult>;
|
|
19
54
|
}
|
|
20
55
|
//# sourceMappingURL=lex-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-resolver.d.ts","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lex-resolver.d.ts","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AACvC,OAAO,EAAE,eAAe,EAAyB,MAAM,uBAAuB,CAAA;AAQ9E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAc,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAEL,wBAAwB,EACxB,GAAG,EACH,WAAW,EACX,iBAAiB,EAIlB,MAAM,4BAA4B,CAAA;AAInC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,KAAK,CAAA;IACV,GAAG,EAAE,GAAG,CAAA;IACR,OAAO,EAAE,eAAe,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,EAAE,GAAG,CAAA;IACR,OAAO,EAAE,eAAe,CAAA;CACzB,CAAA;AAED,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kBAAkB,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,SAAS,CAAC,IAAI,GAAG,GAAG,CAAC,CAAA;IAChE,wBAAwB,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,GAAG,CAAA;KAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC1E,uBAAuB,CAAC,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAE7E,OAAO,CAAC,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,KAAK,CAAA;KAAE,GAAG,SAAS,CAAC,IAAI,GAAG,sBAAsB,CAAC,CAAA;IACxE,aAAa,CAAC,CAAC,IAAI,EAAE;QACnB,GAAG,EAAE,KAAK,CAAA;QACV,GAAG,EAAE,GAAG,CAAA;QACR,OAAO,EAAE,eAAe,CAAA;KACzB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IACnB,YAAY,CAAC,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;CACnE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,wBAAwB,GAAG;IAC1D,KAAK,CAAC,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,EAAE,IAAI,EAAE,CAAA;AAChC,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAA;AAElD,qBAAa,WAAW;IAGV,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB;IAF1D,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,CAAA;gBAE3B,OAAO,EAAE,kBAAkB;IAIpD,GAAG,CACP,OAAO,EAAE,IAAI,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,iBAAiB,CAAC;IAKvB,OAAO,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;cAuBrC,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC;IAY3D,KAAK,CACT,MAAM,EAAE,KAAK,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,iBAAiB,CAAC;cAmBb,eAAe,CAC7B,GAAG,EAAE,KAAK,EACV,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,sBAAsB,CAAC;CAqEnC"}
|
package/dist/lex-resolver.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LexResolver = exports.NSID = exports.AtUri = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
4
5
|
const promises_1 = require("node:dns/promises");
|
|
6
|
+
const crypto = tslib_1.__importStar(require("@atproto/crypto"));
|
|
5
7
|
const lex_client_1 = require("@atproto/lex-client");
|
|
6
8
|
const lex_document_1 = require("@atproto/lex-document");
|
|
9
|
+
const repo_1 = require("@atproto/repo");
|
|
7
10
|
const syntax_1 = require("@atproto/syntax");
|
|
8
11
|
Object.defineProperty(exports, "AtUri", { enumerable: true, get: function () { return syntax_1.AtUri; } });
|
|
9
12
|
Object.defineProperty(exports, "NSID", { enumerable: true, get: function () { return syntax_1.NSID; } });
|
|
10
13
|
const did_resolver_1 = require("@atproto-labs/did-resolver");
|
|
11
14
|
const lex_resolver_error_js_1 = require("./lex-resolver-error.js");
|
|
15
|
+
const com = tslib_1.__importStar(require("./lexicons/com.js"));
|
|
12
16
|
class LexResolver {
|
|
13
17
|
options;
|
|
14
18
|
didResolver;
|
|
@@ -18,89 +22,153 @@ class LexResolver {
|
|
|
18
22
|
}
|
|
19
23
|
async get(nsidStr, options) {
|
|
20
24
|
const uri = await this.resolve(nsidStr);
|
|
21
|
-
|
|
22
|
-
return { uri, document };
|
|
25
|
+
return this.fetch(uri, options);
|
|
23
26
|
}
|
|
24
27
|
async resolve(nsidStr) {
|
|
25
28
|
const nsid = syntax_1.NSID.from(nsidStr);
|
|
26
|
-
const did = this.options.
|
|
27
|
-
(await
|
|
28
|
-
|
|
29
|
+
const did = (await this.options.hooks?.onResolveAuthority?.({ nsid })) ??
|
|
30
|
+
(await this.resolveLexiconAuthority(nsid).then(async (did) => {
|
|
31
|
+
await this.options.hooks?.onResolveAuthorityResult?.({ nsid, did });
|
|
32
|
+
return did;
|
|
33
|
+
}, async (err) => {
|
|
34
|
+
await this.options.hooks?.onResolveAuthorityError?.({ nsid, err });
|
|
35
|
+
throw err;
|
|
29
36
|
}));
|
|
30
37
|
return syntax_1.AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString());
|
|
31
38
|
}
|
|
39
|
+
// @TODO This class could be made compatible with browsers by making the
|
|
40
|
+
// following method abstract and/or by allowing the caller to inject a DNS
|
|
41
|
+
// resolver implementation (based on DNS-over-HTTPS or similar), instead of
|
|
42
|
+
// using the Node.js built-in resolver.
|
|
43
|
+
async resolveLexiconAuthority(nsid) {
|
|
44
|
+
try {
|
|
45
|
+
return await getDomainTxtDid(`_lexicon.${nsid.authority}`);
|
|
46
|
+
}
|
|
47
|
+
catch (cause) {
|
|
48
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Failed to resolve lexicon DID authority for ${nsid}`, { cause });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
32
51
|
async fetch(uriStr, options) {
|
|
33
52
|
const uri = typeof uriStr === 'string' ? new syntax_1.AtUri(uriStr) : uriStr;
|
|
53
|
+
const { lexicon, cid } = (await this.options.hooks?.onFetch?.({ uri })) ??
|
|
54
|
+
(await this.fetchLexiconUri(uri, options).then(async (res) => {
|
|
55
|
+
await this.options.hooks?.onFetchResult?.({ uri, ...res });
|
|
56
|
+
return res;
|
|
57
|
+
}, async (err) => {
|
|
58
|
+
await this.options.hooks?.onFetchError?.({ uri, err });
|
|
59
|
+
throw err;
|
|
60
|
+
}));
|
|
61
|
+
return { uri, cid, lexicon };
|
|
62
|
+
}
|
|
63
|
+
async fetchLexiconUri(uri, options) {
|
|
34
64
|
const { did, nsid } = parseLexiconUri(uri);
|
|
35
|
-
|
|
36
|
-
throw new lex_resolver_error_js_1.LexResolverError(nsid, `DID authority mismatch: expected ${this.options.didAuthority}, got ${did}`);
|
|
37
|
-
}
|
|
38
|
-
const didDocument = await this.didResolver
|
|
65
|
+
const { pds, key } = await this.didResolver
|
|
39
66
|
.resolve(did, options)
|
|
67
|
+
.then(did_resolver_1.extractAtprotoData)
|
|
40
68
|
.catch((cause) => {
|
|
41
|
-
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Failed to resolve DID document for ${
|
|
69
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Failed to resolve DID document for ${did}`, { cause });
|
|
42
70
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
service = (0, did_resolver_1.extractPdsUrl)(didDocument);
|
|
46
|
-
}
|
|
47
|
-
catch (cause) {
|
|
48
|
-
throw new lex_resolver_error_js_1.LexResolverError(nsid, `No PDS service endpoint found in DID document for ${uri}`, { cause });
|
|
71
|
+
if (!key || !pds || !URL.canParse(pds.serviceEndpoint)) {
|
|
72
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `No atproto PDS service endpoint or signing key found in ${did} DID document`);
|
|
49
73
|
}
|
|
50
74
|
const agent = (0, lex_client_1.buildAgent)({
|
|
51
|
-
service,
|
|
75
|
+
service: pds.serviceEndpoint,
|
|
52
76
|
fetch: this.options.fetch,
|
|
53
77
|
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
const collection = 'com.atproto.lexicon.schema';
|
|
79
|
+
const rkey = nsid.toString();
|
|
80
|
+
const { cid, record } = await (0, lex_client_1.xrpc)(agent, com.atproto.sync.getRecord, {
|
|
81
|
+
signal: options?.signal,
|
|
82
|
+
headers: options?.noCache ? { 'Cache-Control': 'no-cache' } : undefined,
|
|
83
|
+
params: { did, collection, rkey },
|
|
84
|
+
}).then(({ body: carBytes }) => {
|
|
85
|
+
return verifyRecordProof(carBytes, did, key, collection, rkey).catch((cause) => {
|
|
86
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Failed to verify Lexicon record proof at ${uri}`, { cause });
|
|
87
|
+
});
|
|
88
|
+
}, (cause) => {
|
|
89
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Failed to fetch Record ${uri}`, {
|
|
90
|
+
cause,
|
|
91
|
+
});
|
|
60
92
|
});
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
93
|
+
const validationResult = lex_document_1.lexiconDocumentSchema.safeParse(record);
|
|
94
|
+
if (!validationResult.success) {
|
|
63
95
|
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {
|
|
64
|
-
cause:
|
|
96
|
+
cause: validationResult.error,
|
|
65
97
|
});
|
|
66
98
|
}
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Invalid document id "${
|
|
99
|
+
const lexicon = validationResult.value;
|
|
100
|
+
if (lexicon.id !== uri.rkey) {
|
|
101
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `Invalid document id "${lexicon.id}" at ${uri}`);
|
|
70
102
|
}
|
|
71
|
-
return
|
|
103
|
+
return { lexicon, cid };
|
|
72
104
|
}
|
|
73
105
|
}
|
|
74
106
|
exports.LexResolver = LexResolver;
|
|
75
107
|
function parseLexiconUri(uri) {
|
|
76
108
|
// Validate input URI
|
|
77
109
|
const nsid = syntax_1.NSID.from(uri.rkey);
|
|
78
|
-
const did = uri.host;
|
|
79
|
-
(0, did_resolver_1.assertDid)(did);
|
|
80
|
-
return { did, nsid };
|
|
81
|
-
}
|
|
82
|
-
async function resolveLexiconDidAuthority(nsid) {
|
|
83
110
|
try {
|
|
84
|
-
|
|
111
|
+
const did = uri.host;
|
|
112
|
+
(0, did_resolver_1.assertDid)(did);
|
|
113
|
+
return { did, nsid };
|
|
85
114
|
}
|
|
86
115
|
catch (cause) {
|
|
87
|
-
throw new lex_resolver_error_js_1.LexResolverError(nsid, `
|
|
116
|
+
throw new lex_resolver_error_js_1.LexResolverError(nsid, `URI host is not a DID ${uri}`, { cause });
|
|
88
117
|
}
|
|
89
118
|
}
|
|
90
119
|
async function getDomainTxtDid(domain) {
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
function parseDnsResult(chunkedResults) {
|
|
94
|
-
const didDefs = chunkedResults
|
|
120
|
+
const didLines = (await (0, promises_1.resolveTxt)(domain))
|
|
95
121
|
.map((chunks) => chunks.join(''))
|
|
96
122
|
.filter((i) => i.startsWith('did='));
|
|
97
|
-
if (
|
|
98
|
-
const did =
|
|
123
|
+
if (didLines.length === 1) {
|
|
124
|
+
const did = didLines[0].slice(4);
|
|
99
125
|
(0, did_resolver_1.assertDid)(did);
|
|
100
126
|
return did;
|
|
101
127
|
}
|
|
102
|
-
throw
|
|
128
|
+
throw didLines.length > 1
|
|
103
129
|
? new Error('Multiple DIDs found in DNS TXT records')
|
|
104
130
|
: new Error('No DID found in DNS TXT records');
|
|
105
131
|
}
|
|
132
|
+
async function verifyRecordProof(car, did, key, collection, rkey) {
|
|
133
|
+
const { root, blocks } = await (0, repo_1.readCarWithRoot)(car);
|
|
134
|
+
const blockstore = new repo_1.MemoryBlockstore(blocks);
|
|
135
|
+
const commit = await blockstore.readObj(root, repo_1.def.commit);
|
|
136
|
+
if (commit.did !== did) {
|
|
137
|
+
throw new Error(`Invalid repo did: ${commit.did}`);
|
|
138
|
+
}
|
|
139
|
+
const signingKey = getDidKeyFromMultibase(key);
|
|
140
|
+
const validSig = await (0, repo_1.verifyCommitSig)(commit, signingKey);
|
|
141
|
+
if (!validSig) {
|
|
142
|
+
throw new Error(`Invalid signature on commit: ${root.toString()}`);
|
|
143
|
+
}
|
|
144
|
+
const mst = repo_1.MST.load(blockstore, commit.data);
|
|
145
|
+
const cid = await mst.get(`${collection}/${rkey}`);
|
|
146
|
+
if (!cid)
|
|
147
|
+
throw new Error('Record not found in proof');
|
|
148
|
+
const record = await blockstore.readRecord(cid);
|
|
149
|
+
if (record?.$type !== collection) {
|
|
150
|
+
throw new Error(`Invalid record type: expected ${collection}, got ${record?.$type}`);
|
|
151
|
+
}
|
|
152
|
+
return { cid, record };
|
|
153
|
+
}
|
|
154
|
+
function getDidKeyFromMultibase(key) {
|
|
155
|
+
switch (key.type) {
|
|
156
|
+
case 'EcdsaSecp256r1VerificationKey2019': {
|
|
157
|
+
const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase);
|
|
158
|
+
return crypto.formatDidKey(crypto.P256_JWT_ALG, keyBytes);
|
|
159
|
+
}
|
|
160
|
+
case 'EcdsaSecp256k1VerificationKey2019': {
|
|
161
|
+
const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase);
|
|
162
|
+
return crypto.formatDidKey(crypto.SECP256K1_JWT_ALG, keyBytes);
|
|
163
|
+
}
|
|
164
|
+
case 'Multikey': {
|
|
165
|
+
const { jwtAlg, keyBytes } = crypto.parseMultikey(key.publicKeyMultibase);
|
|
166
|
+
return crypto.formatDidKey(jwtAlg, keyBytes);
|
|
167
|
+
}
|
|
168
|
+
default: {
|
|
169
|
+
// Should never happen
|
|
170
|
+
throw new Error(`Unsupported verification method type: ${key.type}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
106
174
|
//# sourceMappingURL=lex-resolver.js.map
|
package/dist/lex-resolver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lex-resolver.js","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveTxt } from 'node:dns/promises'\nimport { Client, buildAgent } from '@atproto/lex-client'\nimport { LexiconDocument, lexiconDocumentSchema } from '@atproto/lex-document'\nimport { AtUri, NSID } from '@atproto/syntax'\nimport {\n CreateDidResolverOptions,\n Did,\n DidResolver,\n ResolveDidOptions,\n assertDid,\n createDidResolver,\n extractPdsUrl,\n} from '@atproto-labs/did-resolver'\nimport { LexResolverError } from './lex-resolver-error.js'\n\nexport type LexResolverOptions = CreateDidResolverOptions & {\n didAuthority?: Did\n}\n\nexport { AtUri, NSID }\nexport type { LexiconDocument, ResolveDidOptions }\n\nexport class LexResolver {\n protected readonly didResolver: DidResolver<'plc' | 'web'>\n\n constructor(protected readonly options: LexResolverOptions) {\n this.didResolver = createDidResolver(options)\n }\n\n async get(\n nsidStr: NSID | string,\n options?: ResolveDidOptions,\n ): Promise<{\n uri: AtUri\n document: LexiconDocument\n }> {\n const uri = await this.resolve(nsidStr)\n const document = await this.fetch(uri, options)\n return { uri, document }\n }\n\n async resolve(nsidStr: NSID | string): Promise<AtUri> {\n const nsid = NSID.from(nsidStr)\n const did =\n this.options.didAuthority ??\n (await resolveLexiconDidAuthority(nsid).catch((cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to resolve DID authority for Lexicon`,\n { cause },\n )\n }))\n\n return AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString())\n }\n\n async fetch(\n uriStr: AtUri | string,\n options?: ResolveDidOptions,\n ): Promise<LexiconDocument> {\n const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr\n const { did, nsid } = parseLexiconUri(uri)\n\n if (this.options.didAuthority && this.options.didAuthority !== did) {\n throw new LexResolverError(\n nsid,\n `DID authority mismatch: expected ${this.options.didAuthority}, got ${did}`,\n )\n }\n\n const didDocument = await this.didResolver\n .resolve(did, options)\n .catch((cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to resolve DID document for ${uri}`,\n { cause },\n )\n })\n\n let service: URL\n try {\n service = extractPdsUrl(didDocument)\n } catch (cause) {\n throw new LexResolverError(\n nsid,\n `No PDS service endpoint found in DID document for ${uri}`,\n { cause },\n )\n }\n\n const agent = buildAgent({\n service,\n fetch: this.options.fetch,\n })\n\n // TODO: use com.atproto.sync.getRecord and check signature using\n // DID document key\n const response = await new Client(agent)\n .getRecord('com.atproto.lexicon.schema', nsid.toString(), { repo: did })\n .catch((cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to fetch Lexicon document at ${uri}`,\n { cause },\n )\n })\n\n const result = lexiconDocumentSchema.validate(response.body.value)\n if (!result.success) {\n throw new LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {\n cause: result.error,\n })\n }\n\n const document = result.value\n\n if (document.id !== nsid.toString()) {\n throw new LexResolverError(\n nsid,\n `Invalid document id \"${document.id}\" for ${uri}`,\n )\n }\n\n return document\n }\n}\n\nfunction parseLexiconUri(uri: AtUri): {\n did: Did\n nsid: NSID\n} {\n // Validate input URI\n const nsid = NSID.from(uri.rkey)\n const did = uri.host\n assertDid(did)\n\n return { did, nsid }\n}\n\nasync function resolveLexiconDidAuthority(nsid: NSID): Promise<Did> {\n try {\n return await getDomainTxtDid(`_lexicon.${nsid.authority}`)\n } catch (cause) {\n throw new LexResolverError(\n nsid,\n `Failed to resolve lexicon DID authority`,\n { cause },\n )\n }\n}\n\nasync function getDomainTxtDid(domain: string): Promise<Did> {\n return parseDnsResult(await resolveTxt(domain))\n}\n\nfunction parseDnsResult(chunkedResults: string[][]): Did {\n const didDefs = chunkedResults\n .map((chunks) => chunks.join(''))\n .filter((i) => i.startsWith('did='))\n\n if (didDefs.length === 1) {\n const did = didDefs[0].slice(4)\n assertDid(did)\n return did\n }\n\n throw didDefs.length > 1\n ? new Error('Multiple DIDs found in DNS TXT records')\n : new Error('No DID found in DNS TXT records')\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lex-resolver.js","sourceRoot":"","sources":["../src/lex-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveTxt } from 'node:dns/promises'\nimport * as crypto from '@atproto/crypto'\nimport { buildAgent, xrpc } from '@atproto/lex-client'\nimport { Cid } from '@atproto/lex-data'\nimport { LexiconDocument, lexiconDocumentSchema } from '@atproto/lex-document'\nimport {\n MST,\n MemoryBlockstore,\n def as repoDef,\n readCarWithRoot,\n verifyCommitSig,\n} from '@atproto/repo'\nimport { AtUri, NSID, NsidString } from '@atproto/syntax'\nimport {\n AtprotoVerificationMethod,\n CreateDidResolverOptions,\n Did,\n DidResolver,\n ResolveDidOptions,\n assertDid,\n createDidResolver,\n extractAtprotoData,\n} from '@atproto-labs/did-resolver'\nimport { LexResolverError } from './lex-resolver-error.js'\nimport * as com from './lexicons/com.js'\n\nexport type LexResolverResult = {\n uri: AtUri\n cid: Cid\n lexicon: LexiconDocument\n}\n\nexport type LexResolverFetchResult = {\n cid: Cid\n lexicon: LexiconDocument\n}\n\ntype Awaitable<T> = T | Promise<T>\n\nexport type LexResolverHooks = {\n onResolveAuthority?(data: { nsid: NSID }): Awaitable<void | Did>\n onResolveAuthorityResult?(data: { nsid: NSID; did: Did }): Awaitable<void>\n onResolveAuthorityError?(data: { nsid: NSID; err: unknown }): Awaitable<void>\n\n onFetch?(data: { uri: AtUri }): Awaitable<void | LexResolverFetchResult>\n onFetchResult?(data: {\n uri: AtUri\n cid: Cid\n lexicon: LexiconDocument\n }): Awaitable<void>\n onFetchError?(data: { uri: AtUri; err: unknown }): Awaitable<void>\n}\n\nexport type LexResolverOptions = CreateDidResolverOptions & {\n hooks?: LexResolverHooks\n}\n\nexport { AtUri, type Cid, NSID }\nexport type { LexiconDocument, ResolveDidOptions }\n\nexport class LexResolver {\n protected readonly didResolver: DidResolver<'plc' | 'web'>\n\n constructor(protected readonly options: LexResolverOptions) {\n this.didResolver = createDidResolver(options)\n }\n\n async get(\n nsidStr: NSID | string,\n options?: ResolveDidOptions,\n ): Promise<LexResolverResult> {\n const uri = await this.resolve(nsidStr)\n return this.fetch(uri, options)\n }\n\n async resolve(nsidStr: NSID | string): Promise<AtUri> {\n const nsid = NSID.from(nsidStr)\n\n const did =\n (await this.options.hooks?.onResolveAuthority?.({ nsid })) ??\n (await this.resolveLexiconAuthority(nsid).then(\n async (did) => {\n await this.options.hooks?.onResolveAuthorityResult?.({ nsid, did })\n return did\n },\n async (err) => {\n await this.options.hooks?.onResolveAuthorityError?.({ nsid, err })\n throw err\n },\n ))\n\n return AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString())\n }\n\n // @TODO This class could be made compatible with browsers by making the\n // following method abstract and/or by allowing the caller to inject a DNS\n // resolver implementation (based on DNS-over-HTTPS or similar), instead of\n // using the Node.js built-in resolver.\n protected async resolveLexiconAuthority(nsid: NSID): Promise<Did> {\n try {\n return await getDomainTxtDid(`_lexicon.${nsid.authority}`)\n } catch (cause) {\n throw new LexResolverError(\n nsid,\n `Failed to resolve lexicon DID authority for ${nsid}`,\n { cause },\n )\n }\n }\n\n async fetch(\n uriStr: AtUri | string,\n options?: ResolveDidOptions,\n ): Promise<LexResolverResult> {\n const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr\n\n const { lexicon, cid } =\n (await this.options.hooks?.onFetch?.({ uri })) ??\n (await this.fetchLexiconUri(uri, options).then(\n async (res) => {\n await this.options.hooks?.onFetchResult?.({ uri, ...res })\n return res\n },\n async (err) => {\n await this.options.hooks?.onFetchError?.({ uri, err })\n throw err\n },\n ))\n\n return { uri, cid, lexicon }\n }\n\n protected async fetchLexiconUri(\n uri: AtUri,\n options?: ResolveDidOptions,\n ): Promise<LexResolverFetchResult> {\n const { did, nsid } = parseLexiconUri(uri)\n\n const { pds, key } = await this.didResolver\n .resolve(did, options)\n .then(extractAtprotoData)\n .catch((cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to resolve DID document for ${did}`,\n { cause },\n )\n })\n\n if (!key || !pds || !URL.canParse(pds.serviceEndpoint)) {\n throw new LexResolverError(\n nsid,\n `No atproto PDS service endpoint or signing key found in ${did} DID document`,\n )\n }\n\n const agent = buildAgent({\n service: pds.serviceEndpoint,\n fetch: this.options.fetch,\n })\n\n const collection = 'com.atproto.lexicon.schema'\n const rkey = nsid.toString()\n\n const { cid, record } = await xrpc(agent, com.atproto.sync.getRecord, {\n signal: options?.signal,\n headers: options?.noCache ? { 'Cache-Control': 'no-cache' } : undefined,\n params: { did, collection, rkey },\n }).then(\n ({ body: carBytes }) => {\n return verifyRecordProof(carBytes, did, key, collection, rkey).catch(\n (cause) => {\n throw new LexResolverError(\n nsid,\n `Failed to verify Lexicon record proof at ${uri}`,\n { cause },\n )\n },\n )\n },\n (cause) => {\n throw new LexResolverError(nsid, `Failed to fetch Record ${uri}`, {\n cause,\n })\n },\n )\n\n const validationResult = lexiconDocumentSchema.safeParse(record)\n if (!validationResult.success) {\n throw new LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {\n cause: validationResult.error,\n })\n }\n\n const lexicon = validationResult.value\n if (lexicon.id !== uri.rkey) {\n throw new LexResolverError(\n nsid,\n `Invalid document id \"${lexicon.id}\" at ${uri}`,\n )\n }\n\n return { lexicon, cid }\n }\n}\n\nfunction parseLexiconUri(uri: AtUri): {\n did: Did\n nsid: NSID\n} {\n // Validate input URI\n const nsid = NSID.from(uri.rkey)\n try {\n const did = uri.host\n assertDid(did)\n return { did, nsid }\n } catch (cause) {\n throw new LexResolverError(nsid, `URI host is not a DID ${uri}`, { cause })\n }\n}\n\nasync function getDomainTxtDid(domain: string): Promise<Did> {\n const didLines = (await resolveTxt(domain))\n .map((chunks) => chunks.join(''))\n .filter((i) => i.startsWith('did='))\n\n if (didLines.length === 1) {\n const did = didLines[0].slice(4)\n assertDid(did)\n return did\n }\n\n throw didLines.length > 1\n ? new Error('Multiple DIDs found in DNS TXT records')\n : new Error('No DID found in DNS TXT records')\n}\n\nasync function verifyRecordProof(\n car: Uint8Array,\n did: Did,\n key: AtprotoVerificationMethod,\n collection: NsidString,\n rkey: string,\n) {\n const { root, blocks } = await readCarWithRoot(car)\n const blockstore = new MemoryBlockstore(blocks)\n\n const commit = await blockstore.readObj(root, repoDef.commit)\n if (commit.did !== did) {\n throw new Error(`Invalid repo did: ${commit.did}`)\n }\n\n const signingKey = getDidKeyFromMultibase(key)\n const validSig = await verifyCommitSig(commit, signingKey)\n if (!validSig) {\n throw new Error(`Invalid signature on commit: ${root.toString()}`)\n }\n\n const mst = MST.load(blockstore, commit.data)\n\n const cid = await mst.get(`${collection}/${rkey}`)\n if (!cid) throw new Error('Record not found in proof')\n\n const record = await blockstore.readRecord(cid)\n if (record?.$type !== collection) {\n throw new Error(\n `Invalid record type: expected ${collection}, got ${record?.$type}`,\n )\n }\n\n return { cid, record }\n}\n\nfunction getDidKeyFromMultibase(key: AtprotoVerificationMethod) {\n switch (key.type) {\n case 'EcdsaSecp256r1VerificationKey2019': {\n const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)\n return crypto.formatDidKey(crypto.P256_JWT_ALG, keyBytes)\n }\n case 'EcdsaSecp256k1VerificationKey2019': {\n const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)\n return crypto.formatDidKey(crypto.SECP256K1_JWT_ALG, keyBytes)\n }\n case 'Multikey': {\n const { jwtAlg, keyBytes } = crypto.parseMultikey(key.publicKeyMultibase)\n return crypto.formatDidKey(jwtAlg, keyBytes)\n }\n default: {\n // Should never happen\n throw new Error(`Unsupported verification method type: ${key.type}`)\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRecord.d.ts","sourceRoot":"","sources":["../../../../../src/lexicons/com/atproto/sync/getRecord.ts"],"names":[],"mappings":"AAIA,cAAc,qBAAqB,CAAA;AACnC,OAAO,KAAK,KAAK,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { l } from '@atproto/lex-schema';
|
|
2
|
+
declare const $nsid = "com.atproto.sync.getRecord";
|
|
3
|
+
export { $nsid };
|
|
4
|
+
/**
|
|
5
|
+
* Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.
|
|
6
|
+
*/
|
|
7
|
+
declare const main: l.Query<"com.atproto.sync.getRecord", l.ParamsSchema<{
|
|
8
|
+
readonly did: l.StringSchema<{
|
|
9
|
+
readonly format: "did";
|
|
10
|
+
}>;
|
|
11
|
+
readonly collection: l.StringSchema<{
|
|
12
|
+
readonly format: "nsid";
|
|
13
|
+
}>;
|
|
14
|
+
readonly rkey: l.StringSchema<{
|
|
15
|
+
readonly format: "record-key";
|
|
16
|
+
}>;
|
|
17
|
+
}>, l.Payload<"application/vnd.ipld.car", undefined>, readonly ["RecordNotFound", "RepoNotFound", "RepoTakendown", "RepoSuspended", "RepoDeactivated"]>;
|
|
18
|
+
export { main };
|
|
19
|
+
export declare const $params: l.ParamsSchema<{
|
|
20
|
+
readonly did: l.StringSchema<{
|
|
21
|
+
readonly format: "did";
|
|
22
|
+
}>;
|
|
23
|
+
readonly collection: l.StringSchema<{
|
|
24
|
+
readonly format: "nsid";
|
|
25
|
+
}>;
|
|
26
|
+
readonly rkey: l.StringSchema<{
|
|
27
|
+
readonly format: "record-key";
|
|
28
|
+
}>;
|
|
29
|
+
}>, $output: l.Payload<"application/vnd.ipld.car", undefined>;
|
|
30
|
+
export type Params = l.InferQueryParameters<typeof main>;
|
|
31
|
+
export type Output = l.InferQueryOutputBody<typeof main>;
|
|
32
|
+
//# sourceMappingURL=getRecord.defs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRecord.defs.d.ts","sourceRoot":"","sources":["../../../../../src/lexicons/com/atproto/sync/getRecord.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAA;AAEvC,QAAA,MAAM,KAAK,+BAA+B,CAAA;AAE1C,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB;;GAEG;AACH,QAAA,MAAM,IAAI;;;;;;;;;;uJAiBP,CAAA;AACH,OAAO,EAAE,IAAI,EAAE,CAAA;AAEf,eAAO,MAAM,OAAO;;;;;;;;;;EAAkB,EACpC,OAAO,kDAAc,CAAA;AAEvB,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,oBAAoB,CAAC,OAAO,IAAI,CAAC,CAAA;AACxD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,oBAAoB,CAAC,OAAO,IAAI,CAAC,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.$output = exports.$params = exports.main = exports.$nsid = void 0;
|
|
7
|
+
const lex_schema_1 = require("@atproto/lex-schema");
|
|
8
|
+
const $nsid = 'com.atproto.sync.getRecord';
|
|
9
|
+
exports.$nsid = $nsid;
|
|
10
|
+
/**
|
|
11
|
+
* Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.
|
|
12
|
+
*/
|
|
13
|
+
const main =
|
|
14
|
+
/*#__PURE__*/
|
|
15
|
+
lex_schema_1.l.query($nsid,
|
|
16
|
+
/*#__PURE__*/ lex_schema_1.l.params({
|
|
17
|
+
did: /*#__PURE__*/ lex_schema_1.l.string({ format: 'did' }),
|
|
18
|
+
collection: /*#__PURE__*/ lex_schema_1.l.string({ format: 'nsid' }),
|
|
19
|
+
rkey: /*#__PURE__*/ lex_schema_1.l.string({ format: 'record-key' }),
|
|
20
|
+
}),
|
|
21
|
+
/*#__PURE__*/ lex_schema_1.l.payload('application/vnd.ipld.car'), [
|
|
22
|
+
'RecordNotFound',
|
|
23
|
+
'RepoNotFound',
|
|
24
|
+
'RepoTakendown',
|
|
25
|
+
'RepoSuspended',
|
|
26
|
+
'RepoDeactivated',
|
|
27
|
+
]);
|
|
28
|
+
exports.main = main;
|
|
29
|
+
exports.$params = main.parameters, exports.$output = main.output;
|
|
30
|
+
//# sourceMappingURL=getRecord.defs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRecord.defs.js","sourceRoot":"","sources":["../../../../../src/lexicons/com/atproto/sync/getRecord.defs.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,oDAAuC;AAEvC,MAAM,KAAK,GAAG,4BAA4B,CAAA;AAEjC,sBAAK;AAEd;;GAEG;AACH,MAAM,IAAI;AACR,aAAa;AACb,cAAC,CAAC,KAAK,CACL,KAAK;AACL,aAAa,CAAC,cAAC,CAAC,MAAM,CAAC;IACrB,GAAG,EAAE,aAAa,CAAC,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9C,UAAU,EAAE,aAAa,CAAC,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACtD,IAAI,EAAE,aAAa,CAAC,cAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;CACvD,CAAC;AACF,aAAa,CAAC,cAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC,EACnD;IACE,gBAAgB;IAChB,cAAc;IACd,eAAe;IACf,eAAe;IACf,iBAAiB;CAClB,CACF,CAAA;AACM,oBAAI;AAEA,QAAA,OAAO,GAAG,IAAI,CAAC,UAAU,EACpC,QAAA,OAAO,GAAG,IAAI,CAAC,MAAM,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex-schema'\n\nconst $nsid = 'com.atproto.sync.getRecord'\n\nexport { $nsid }\n\n/**\n * Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.\n */\nconst main =\n /*#__PURE__*/\n l.query(\n $nsid,\n /*#__PURE__*/ l.params({\n did: /*#__PURE__*/ l.string({ format: 'did' }),\n collection: /*#__PURE__*/ l.string({ format: 'nsid' }),\n rkey: /*#__PURE__*/ l.string({ format: 'record-key' }),\n }),\n /*#__PURE__*/ l.payload('application/vnd.ipld.car'),\n [\n 'RecordNotFound',\n 'RepoNotFound',\n 'RepoTakendown',\n 'RepoSuspended',\n 'RepoDeactivated',\n ],\n )\nexport { main }\n\nexport const $params = main.parameters,\n $output = main.output\n\nexport type Params = l.InferQueryParameters<typeof main>\nexport type Output = l.InferQueryOutputBody<typeof main>\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.$defs = void 0;
|
|
7
|
+
const tslib_1 = require("tslib");
|
|
8
|
+
tslib_1.__exportStar(require("./getRecord.defs.js"), exports);
|
|
9
|
+
exports.$defs = tslib_1.__importStar(require("./getRecord.defs.js"));
|
|
10
|
+
//# sourceMappingURL=getRecord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRecord.js","sourceRoot":"","sources":["../../../../../src/lexicons/com/atproto/sync/getRecord.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;AAEH,8DAAmC;AACnC,qEAA4C","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * from './getRecord.defs.js'\nexport * as $defs from './getRecord.defs.js'\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/com/atproto/sync.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,SAAS,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getRecord = void 0;
|
|
7
|
+
const tslib_1 = require("tslib");
|
|
8
|
+
exports.getRecord = tslib_1.__importStar(require("./sync/getRecord.js"));
|
|
9
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../../src/lexicons/com/atproto/sync.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;AAEH,yEAAgD","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as getRecord from './sync/getRecord.js'\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atproto.d.ts","sourceRoot":"","sources":["../../../src/lexicons/com/atproto.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sync = void 0;
|
|
7
|
+
const tslib_1 = require("tslib");
|
|
8
|
+
exports.sync = tslib_1.__importStar(require("./atproto/sync.js"));
|
|
9
|
+
//# sourceMappingURL=atproto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atproto.js","sourceRoot":"","sources":["../../../src/lexicons/com/atproto.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;AAEH,kEAAyC","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as sync from './atproto/sync.js'\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"com.d.ts","sourceRoot":"","sources":["../../src/lexicons/com.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.atproto = void 0;
|
|
7
|
+
const tslib_1 = require("tslib");
|
|
8
|
+
exports.atproto = tslib_1.__importStar(require("./com/atproto.js"));
|
|
9
|
+
//# sourceMappingURL=com.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"com.js","sourceRoot":"","sources":["../../src/lexicons/com.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;AAEH,oEAA2C","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as atproto from './com/atproto.js'\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lex-resolver",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Lexicon document resolver utility for AT Lexicons",
|
|
6
6
|
"keywords": [
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"./src",
|
|
20
|
-
"./
|
|
20
|
+
"./tsconfig.build.json",
|
|
21
|
+
"./tsconfig.tests.json",
|
|
22
|
+
"./tsconfig.json",
|
|
23
|
+
"./dist",
|
|
24
|
+
"./CHANGELOG.md"
|
|
21
25
|
],
|
|
22
26
|
"sideEffects": false,
|
|
23
27
|
"type": "commonjs",
|
|
@@ -32,13 +36,20 @@
|
|
|
32
36
|
},
|
|
33
37
|
"dependencies": {
|
|
34
38
|
"tslib": "^2.8.1",
|
|
35
|
-
"@atproto-labs/did-resolver": "0.2.
|
|
36
|
-
"@atproto/
|
|
37
|
-
"@atproto/lex-
|
|
38
|
-
"@atproto/
|
|
39
|
+
"@atproto-labs/did-resolver": "0.2.4",
|
|
40
|
+
"@atproto/crypto": "0.4.5",
|
|
41
|
+
"@atproto/lex-client": "0.0.4",
|
|
42
|
+
"@atproto/lex-data": "0.0.3",
|
|
43
|
+
"@atproto/lex-document": "0.0.5",
|
|
44
|
+
"@atproto/lex-schema": "0.0.4",
|
|
45
|
+
"@atproto/repo": "0.8.12",
|
|
46
|
+
"@atproto/syntax": "0.4.2"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@atproto/lex-builder": "0.0.6"
|
|
39
50
|
},
|
|
40
|
-
"devDependencies": {},
|
|
41
51
|
"scripts": {
|
|
52
|
+
"prebuild": "node ./scripts/lex-build.mjs",
|
|
42
53
|
"build": "tsc --build tsconfig.build.json"
|
|
43
54
|
}
|
|
44
55
|
}
|
|
@@ -10,4 +10,11 @@ export class LexResolverError extends Error {
|
|
|
10
10
|
) {
|
|
11
11
|
super(`${description} (${nsid})`, options)
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
static from(nsid: NSID | string, description?: string) {
|
|
15
|
+
return new LexResolverError(
|
|
16
|
+
typeof nsid === 'string' ? NSID.from(nsid) : nsid,
|
|
17
|
+
description,
|
|
18
|
+
)
|
|
19
|
+
}
|
|
13
20
|
}
|
package/src/lex-resolver.ts
CHANGED
|
@@ -1,23 +1,61 @@
|
|
|
1
1
|
import { resolveTxt } from 'node:dns/promises'
|
|
2
|
-
import
|
|
2
|
+
import * as crypto from '@atproto/crypto'
|
|
3
|
+
import { buildAgent, xrpc } from '@atproto/lex-client'
|
|
4
|
+
import { Cid } from '@atproto/lex-data'
|
|
3
5
|
import { LexiconDocument, lexiconDocumentSchema } from '@atproto/lex-document'
|
|
4
|
-
import { AtUri, NSID } from '@atproto/syntax'
|
|
5
6
|
import {
|
|
7
|
+
MST,
|
|
8
|
+
MemoryBlockstore,
|
|
9
|
+
def as repoDef,
|
|
10
|
+
readCarWithRoot,
|
|
11
|
+
verifyCommitSig,
|
|
12
|
+
} from '@atproto/repo'
|
|
13
|
+
import { AtUri, NSID, NsidString } from '@atproto/syntax'
|
|
14
|
+
import {
|
|
15
|
+
AtprotoVerificationMethod,
|
|
6
16
|
CreateDidResolverOptions,
|
|
7
17
|
Did,
|
|
8
18
|
DidResolver,
|
|
9
19
|
ResolveDidOptions,
|
|
10
20
|
assertDid,
|
|
11
21
|
createDidResolver,
|
|
12
|
-
|
|
22
|
+
extractAtprotoData,
|
|
13
23
|
} from '@atproto-labs/did-resolver'
|
|
14
24
|
import { LexResolverError } from './lex-resolver-error.js'
|
|
25
|
+
import * as com from './lexicons/com.js'
|
|
26
|
+
|
|
27
|
+
export type LexResolverResult = {
|
|
28
|
+
uri: AtUri
|
|
29
|
+
cid: Cid
|
|
30
|
+
lexicon: LexiconDocument
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type LexResolverFetchResult = {
|
|
34
|
+
cid: Cid
|
|
35
|
+
lexicon: LexiconDocument
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type Awaitable<T> = T | Promise<T>
|
|
39
|
+
|
|
40
|
+
export type LexResolverHooks = {
|
|
41
|
+
onResolveAuthority?(data: { nsid: NSID }): Awaitable<void | Did>
|
|
42
|
+
onResolveAuthorityResult?(data: { nsid: NSID; did: Did }): Awaitable<void>
|
|
43
|
+
onResolveAuthorityError?(data: { nsid: NSID; err: unknown }): Awaitable<void>
|
|
44
|
+
|
|
45
|
+
onFetch?(data: { uri: AtUri }): Awaitable<void | LexResolverFetchResult>
|
|
46
|
+
onFetchResult?(data: {
|
|
47
|
+
uri: AtUri
|
|
48
|
+
cid: Cid
|
|
49
|
+
lexicon: LexiconDocument
|
|
50
|
+
}): Awaitable<void>
|
|
51
|
+
onFetchError?(data: { uri: AtUri; err: unknown }): Awaitable<void>
|
|
52
|
+
}
|
|
15
53
|
|
|
16
54
|
export type LexResolverOptions = CreateDidResolverOptions & {
|
|
17
|
-
|
|
55
|
+
hooks?: LexResolverHooks
|
|
18
56
|
}
|
|
19
57
|
|
|
20
|
-
export { AtUri, NSID }
|
|
58
|
+
export { AtUri, type Cid, NSID }
|
|
21
59
|
export type { LexiconDocument, ResolveDidOptions }
|
|
22
60
|
|
|
23
61
|
export class LexResolver {
|
|
@@ -30,99 +68,139 @@ export class LexResolver {
|
|
|
30
68
|
async get(
|
|
31
69
|
nsidStr: NSID | string,
|
|
32
70
|
options?: ResolveDidOptions,
|
|
33
|
-
): Promise<{
|
|
34
|
-
uri: AtUri
|
|
35
|
-
document: LexiconDocument
|
|
36
|
-
}> {
|
|
71
|
+
): Promise<LexResolverResult> {
|
|
37
72
|
const uri = await this.resolve(nsidStr)
|
|
38
|
-
|
|
39
|
-
return { uri, document }
|
|
73
|
+
return this.fetch(uri, options)
|
|
40
74
|
}
|
|
41
75
|
|
|
42
76
|
async resolve(nsidStr: NSID | string): Promise<AtUri> {
|
|
43
77
|
const nsid = NSID.from(nsidStr)
|
|
78
|
+
|
|
44
79
|
const did =
|
|
45
|
-
this.options.
|
|
46
|
-
(await
|
|
47
|
-
|
|
48
|
-
nsid,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
52
|
-
|
|
80
|
+
(await this.options.hooks?.onResolveAuthority?.({ nsid })) ??
|
|
81
|
+
(await this.resolveLexiconAuthority(nsid).then(
|
|
82
|
+
async (did) => {
|
|
83
|
+
await this.options.hooks?.onResolveAuthorityResult?.({ nsid, did })
|
|
84
|
+
return did
|
|
85
|
+
},
|
|
86
|
+
async (err) => {
|
|
87
|
+
await this.options.hooks?.onResolveAuthorityError?.({ nsid, err })
|
|
88
|
+
throw err
|
|
89
|
+
},
|
|
90
|
+
))
|
|
53
91
|
|
|
54
92
|
return AtUri.make(did, 'com.atproto.lexicon.schema', nsid.toString())
|
|
55
93
|
}
|
|
56
94
|
|
|
95
|
+
// @TODO This class could be made compatible with browsers by making the
|
|
96
|
+
// following method abstract and/or by allowing the caller to inject a DNS
|
|
97
|
+
// resolver implementation (based on DNS-over-HTTPS or similar), instead of
|
|
98
|
+
// using the Node.js built-in resolver.
|
|
99
|
+
protected async resolveLexiconAuthority(nsid: NSID): Promise<Did> {
|
|
100
|
+
try {
|
|
101
|
+
return await getDomainTxtDid(`_lexicon.${nsid.authority}`)
|
|
102
|
+
} catch (cause) {
|
|
103
|
+
throw new LexResolverError(
|
|
104
|
+
nsid,
|
|
105
|
+
`Failed to resolve lexicon DID authority for ${nsid}`,
|
|
106
|
+
{ cause },
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
57
111
|
async fetch(
|
|
58
112
|
uriStr: AtUri | string,
|
|
59
113
|
options?: ResolveDidOptions,
|
|
60
|
-
): Promise<
|
|
114
|
+
): Promise<LexResolverResult> {
|
|
61
115
|
const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr
|
|
62
|
-
const { did, nsid } = parseLexiconUri(uri)
|
|
63
116
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
117
|
+
const { lexicon, cid } =
|
|
118
|
+
(await this.options.hooks?.onFetch?.({ uri })) ??
|
|
119
|
+
(await this.fetchLexiconUri(uri, options).then(
|
|
120
|
+
async (res) => {
|
|
121
|
+
await this.options.hooks?.onFetchResult?.({ uri, ...res })
|
|
122
|
+
return res
|
|
123
|
+
},
|
|
124
|
+
async (err) => {
|
|
125
|
+
await this.options.hooks?.onFetchError?.({ uri, err })
|
|
126
|
+
throw err
|
|
127
|
+
},
|
|
128
|
+
))
|
|
129
|
+
|
|
130
|
+
return { uri, cid, lexicon }
|
|
131
|
+
}
|
|
70
132
|
|
|
71
|
-
|
|
133
|
+
protected async fetchLexiconUri(
|
|
134
|
+
uri: AtUri,
|
|
135
|
+
options?: ResolveDidOptions,
|
|
136
|
+
): Promise<LexResolverFetchResult> {
|
|
137
|
+
const { did, nsid } = parseLexiconUri(uri)
|
|
138
|
+
|
|
139
|
+
const { pds, key } = await this.didResolver
|
|
72
140
|
.resolve(did, options)
|
|
141
|
+
.then(extractAtprotoData)
|
|
73
142
|
.catch((cause) => {
|
|
74
143
|
throw new LexResolverError(
|
|
75
144
|
nsid,
|
|
76
|
-
`Failed to resolve DID document for ${
|
|
145
|
+
`Failed to resolve DID document for ${did}`,
|
|
77
146
|
{ cause },
|
|
78
147
|
)
|
|
79
148
|
})
|
|
80
149
|
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
service = extractPdsUrl(didDocument)
|
|
84
|
-
} catch (cause) {
|
|
150
|
+
if (!key || !pds || !URL.canParse(pds.serviceEndpoint)) {
|
|
85
151
|
throw new LexResolverError(
|
|
86
152
|
nsid,
|
|
87
|
-
`No PDS service endpoint
|
|
88
|
-
{ cause },
|
|
153
|
+
`No atproto PDS service endpoint or signing key found in ${did} DID document`,
|
|
89
154
|
)
|
|
90
155
|
}
|
|
91
156
|
|
|
92
157
|
const agent = buildAgent({
|
|
93
|
-
service,
|
|
158
|
+
service: pds.serviceEndpoint,
|
|
94
159
|
fetch: this.options.fetch,
|
|
95
160
|
})
|
|
96
161
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
162
|
+
const collection = 'com.atproto.lexicon.schema'
|
|
163
|
+
const rkey = nsid.toString()
|
|
164
|
+
|
|
165
|
+
const { cid, record } = await xrpc(agent, com.atproto.sync.getRecord, {
|
|
166
|
+
signal: options?.signal,
|
|
167
|
+
headers: options?.noCache ? { 'Cache-Control': 'no-cache' } : undefined,
|
|
168
|
+
params: { did, collection, rkey },
|
|
169
|
+
}).then(
|
|
170
|
+
({ body: carBytes }) => {
|
|
171
|
+
return verifyRecordProof(carBytes, did, key, collection, rkey).catch(
|
|
172
|
+
(cause) => {
|
|
173
|
+
throw new LexResolverError(
|
|
174
|
+
nsid,
|
|
175
|
+
`Failed to verify Lexicon record proof at ${uri}`,
|
|
176
|
+
{ cause },
|
|
177
|
+
)
|
|
178
|
+
},
|
|
106
179
|
)
|
|
107
|
-
}
|
|
180
|
+
},
|
|
181
|
+
(cause) => {
|
|
182
|
+
throw new LexResolverError(nsid, `Failed to fetch Record ${uri}`, {
|
|
183
|
+
cause,
|
|
184
|
+
})
|
|
185
|
+
},
|
|
186
|
+
)
|
|
108
187
|
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
188
|
+
const validationResult = lexiconDocumentSchema.safeParse(record)
|
|
189
|
+
if (!validationResult.success) {
|
|
111
190
|
throw new LexResolverError(nsid, `Invalid Lexicon document at ${uri}`, {
|
|
112
|
-
cause:
|
|
191
|
+
cause: validationResult.error,
|
|
113
192
|
})
|
|
114
193
|
}
|
|
115
194
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
if (document.id !== nsid.toString()) {
|
|
195
|
+
const lexicon = validationResult.value
|
|
196
|
+
if (lexicon.id !== uri.rkey) {
|
|
119
197
|
throw new LexResolverError(
|
|
120
198
|
nsid,
|
|
121
|
-
`Invalid document id "${
|
|
199
|
+
`Invalid document id "${lexicon.id}" at ${uri}`,
|
|
122
200
|
)
|
|
123
201
|
}
|
|
124
202
|
|
|
125
|
-
return
|
|
203
|
+
return { lexicon, cid }
|
|
126
204
|
}
|
|
127
205
|
}
|
|
128
206
|
|
|
@@ -132,40 +210,84 @@ function parseLexiconUri(uri: AtUri): {
|
|
|
132
210
|
} {
|
|
133
211
|
// Validate input URI
|
|
134
212
|
const nsid = NSID.from(uri.rkey)
|
|
135
|
-
const did = uri.host
|
|
136
|
-
assertDid(did)
|
|
137
|
-
|
|
138
|
-
return { did, nsid }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async function resolveLexiconDidAuthority(nsid: NSID): Promise<Did> {
|
|
142
213
|
try {
|
|
143
|
-
|
|
214
|
+
const did = uri.host
|
|
215
|
+
assertDid(did)
|
|
216
|
+
return { did, nsid }
|
|
144
217
|
} catch (cause) {
|
|
145
|
-
throw new LexResolverError(
|
|
146
|
-
nsid,
|
|
147
|
-
`Failed to resolve lexicon DID authority`,
|
|
148
|
-
{ cause },
|
|
149
|
-
)
|
|
218
|
+
throw new LexResolverError(nsid, `URI host is not a DID ${uri}`, { cause })
|
|
150
219
|
}
|
|
151
220
|
}
|
|
152
221
|
|
|
153
222
|
async function getDomainTxtDid(domain: string): Promise<Did> {
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function parseDnsResult(chunkedResults: string[][]): Did {
|
|
158
|
-
const didDefs = chunkedResults
|
|
223
|
+
const didLines = (await resolveTxt(domain))
|
|
159
224
|
.map((chunks) => chunks.join(''))
|
|
160
225
|
.filter((i) => i.startsWith('did='))
|
|
161
226
|
|
|
162
|
-
if (
|
|
163
|
-
const did =
|
|
227
|
+
if (didLines.length === 1) {
|
|
228
|
+
const did = didLines[0].slice(4)
|
|
164
229
|
assertDid(did)
|
|
165
230
|
return did
|
|
166
231
|
}
|
|
167
232
|
|
|
168
|
-
throw
|
|
233
|
+
throw didLines.length > 1
|
|
169
234
|
? new Error('Multiple DIDs found in DNS TXT records')
|
|
170
235
|
: new Error('No DID found in DNS TXT records')
|
|
171
236
|
}
|
|
237
|
+
|
|
238
|
+
async function verifyRecordProof(
|
|
239
|
+
car: Uint8Array,
|
|
240
|
+
did: Did,
|
|
241
|
+
key: AtprotoVerificationMethod,
|
|
242
|
+
collection: NsidString,
|
|
243
|
+
rkey: string,
|
|
244
|
+
) {
|
|
245
|
+
const { root, blocks } = await readCarWithRoot(car)
|
|
246
|
+
const blockstore = new MemoryBlockstore(blocks)
|
|
247
|
+
|
|
248
|
+
const commit = await blockstore.readObj(root, repoDef.commit)
|
|
249
|
+
if (commit.did !== did) {
|
|
250
|
+
throw new Error(`Invalid repo did: ${commit.did}`)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const signingKey = getDidKeyFromMultibase(key)
|
|
254
|
+
const validSig = await verifyCommitSig(commit, signingKey)
|
|
255
|
+
if (!validSig) {
|
|
256
|
+
throw new Error(`Invalid signature on commit: ${root.toString()}`)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const mst = MST.load(blockstore, commit.data)
|
|
260
|
+
|
|
261
|
+
const cid = await mst.get(`${collection}/${rkey}`)
|
|
262
|
+
if (!cid) throw new Error('Record not found in proof')
|
|
263
|
+
|
|
264
|
+
const record = await blockstore.readRecord(cid)
|
|
265
|
+
if (record?.$type !== collection) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Invalid record type: expected ${collection}, got ${record?.$type}`,
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return { cid, record }
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function getDidKeyFromMultibase(key: AtprotoVerificationMethod) {
|
|
275
|
+
switch (key.type) {
|
|
276
|
+
case 'EcdsaSecp256r1VerificationKey2019': {
|
|
277
|
+
const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)
|
|
278
|
+
return crypto.formatDidKey(crypto.P256_JWT_ALG, keyBytes)
|
|
279
|
+
}
|
|
280
|
+
case 'EcdsaSecp256k1VerificationKey2019': {
|
|
281
|
+
const keyBytes = crypto.multibaseToBytes(key.publicKeyMultibase)
|
|
282
|
+
return crypto.formatDidKey(crypto.SECP256K1_JWT_ALG, keyBytes)
|
|
283
|
+
}
|
|
284
|
+
case 'Multikey': {
|
|
285
|
+
const { jwtAlg, keyBytes } = crypto.parseMultikey(key.publicKeyMultibase)
|
|
286
|
+
return crypto.formatDidKey(jwtAlg, keyBytes)
|
|
287
|
+
}
|
|
288
|
+
default: {
|
|
289
|
+
// Should never happen
|
|
290
|
+
throw new Error(`Unsupported verification method type: ${key.type}`)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { l } from '@atproto/lex-schema'
|
|
6
|
+
|
|
7
|
+
const $nsid = 'com.atproto.sync.getRecord'
|
|
8
|
+
|
|
9
|
+
export { $nsid }
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.
|
|
13
|
+
*/
|
|
14
|
+
const main =
|
|
15
|
+
/*#__PURE__*/
|
|
16
|
+
l.query(
|
|
17
|
+
$nsid,
|
|
18
|
+
/*#__PURE__*/ l.params({
|
|
19
|
+
did: /*#__PURE__*/ l.string({ format: 'did' }),
|
|
20
|
+
collection: /*#__PURE__*/ l.string({ format: 'nsid' }),
|
|
21
|
+
rkey: /*#__PURE__*/ l.string({ format: 'record-key' }),
|
|
22
|
+
}),
|
|
23
|
+
/*#__PURE__*/ l.payload('application/vnd.ipld.car'),
|
|
24
|
+
[
|
|
25
|
+
'RecordNotFound',
|
|
26
|
+
'RepoNotFound',
|
|
27
|
+
'RepoTakendown',
|
|
28
|
+
'RepoSuspended',
|
|
29
|
+
'RepoDeactivated',
|
|
30
|
+
],
|
|
31
|
+
)
|
|
32
|
+
export { main }
|
|
33
|
+
|
|
34
|
+
export const $params = main.parameters,
|
|
35
|
+
$output = main.output
|
|
36
|
+
|
|
37
|
+
export type Params = l.InferQueryParameters<typeof main>
|
|
38
|
+
export type Output = l.InferQueryOutputBody<typeof main>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../../tsconfig/node.json"],
|
|
3
|
+
"include": ["./src"],
|
|
4
|
+
"exclude": ["**/*.test.ts"],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"noImplicitAny": true,
|
|
7
|
+
"importHelpers": true,
|
|
8
|
+
"target": "ES2023",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"types": ["node"]
|
|
12
|
+
}
|
|
13
|
+
}
|
package/tsconfig.json
ADDED