@atcute/lexicon-resolver 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +14 -0
- package/README.md +64 -0
- package/dist/authority/doh-json.d.ts +12 -0
- package/dist/authority/doh-json.js +107 -0
- package/dist/authority/doh-json.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/verify.d.ts +18 -0
- package/dist/schemas/verify.js +169 -0
- package/dist/schemas/verify.js.map +1 -0
- package/dist/schemas/xrpc.d.ts +13 -0
- package/dist/schemas/xrpc.js +82 -0
- package/dist/schemas/xrpc.js.map +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -0
- package/lib/authority/doh-json.ts +142 -0
- package/lib/constants.ts +1 -0
- package/lib/errors.ts +85 -0
- package/lib/index.ts +5 -0
- package/lib/schemas/verify.ts +245 -0
- package/lib/schemas/xrpc.ts +107 -0
- package/lib/types.ts +25 -0
- package/lib/utils.ts +7 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
BSD Zero Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mary
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
10
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
12
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
13
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
14
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @atcute/lexicon-resolver
|
|
2
|
+
|
|
3
|
+
atproto lexicon authority resolution and schema retrieval
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// authority resolution
|
|
7
|
+
const authorityResolver = new DohJsonLexiconAuthorityResolver({
|
|
8
|
+
dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const authority = await authorityResolver.resolve('app.bsky.feed.post');
|
|
13
|
+
// ^? 'did:plc:4v4y5r3lwsbtmsxhile2ljac'
|
|
14
|
+
} catch (err) {
|
|
15
|
+
if (err instanceof AuthorityNotFoundError) {
|
|
16
|
+
// nsid returned no did
|
|
17
|
+
}
|
|
18
|
+
if (err instanceof InvalidResolvedAuthorityError) {
|
|
19
|
+
// nsid returned a did, but isn't a valid atproto did
|
|
20
|
+
}
|
|
21
|
+
if (err instanceof AmbiguousAuthorityError) {
|
|
22
|
+
// nsid returned multiple did values
|
|
23
|
+
}
|
|
24
|
+
if (err instanceof FailedAuthorityResolutionError) {
|
|
25
|
+
// nsid resolution had thrown something unexpected (fetch error)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (err instanceof LexiconAuthorityResolutionError) {
|
|
29
|
+
// the errors above extend this class, so you can do a catch-all.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// schema resolution
|
|
34
|
+
const schemaResolver = new LexiconSchemaResolver({
|
|
35
|
+
didDocumentResolver: new CompositeDidDocumentResolver({
|
|
36
|
+
methods: {
|
|
37
|
+
plc: new PlcDidDocumentResolver(),
|
|
38
|
+
web: new WebDidDocumentResolver(),
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const resolved = await schemaResolver.resolve(authority, 'app.bsky.feed.post');
|
|
45
|
+
// ^? { uri: string, cid: string, schema: LexiconDoc }
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err instanceof LexiconNotFoundError) {
|
|
48
|
+
// lexicon record not found
|
|
49
|
+
}
|
|
50
|
+
if (err instanceof InvalidLexiconSchemaError) {
|
|
51
|
+
// lexicon schema is malformed
|
|
52
|
+
}
|
|
53
|
+
if (err instanceof InvalidLexiconProofError) {
|
|
54
|
+
// lexicon record proof verification failed
|
|
55
|
+
}
|
|
56
|
+
if (err instanceof FailedLexiconResolutionError) {
|
|
57
|
+
// lexicon resolution had thrown something unexpected (fetch error)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (err instanceof LexiconResolutionError) {
|
|
61
|
+
// the errors above extend this class, so you can do a catch-all.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AtprotoDid, Nsid } from '@atcute/lexicons/syntax';
|
|
2
|
+
import type { LexiconAuthorityResolver, ResolveLexiconAuthorityOptions } from '../types.js';
|
|
3
|
+
export interface DohJsonLexiconAuthorityResolverOptions {
|
|
4
|
+
dohUrl: string;
|
|
5
|
+
fetch?: typeof fetch;
|
|
6
|
+
}
|
|
7
|
+
export declare class DohJsonLexiconAuthorityResolver implements LexiconAuthorityResolver {
|
|
8
|
+
#private;
|
|
9
|
+
readonly dohUrl: string;
|
|
10
|
+
constructor({ dohUrl, fetch: fetchThis }: DohJsonLexiconAuthorityResolverOptions);
|
|
11
|
+
resolve(nsid: Nsid, options?: ResolveLexiconAuthorityOptions): Promise<AtprotoDid>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as v from '@badrap/valita';
|
|
2
|
+
import { isAtprotoDid } from '@atcute/identity';
|
|
3
|
+
import { isResponseOk, parseResponseAsJson, pipe, validateJsonWith } from '@atcute/util-fetch';
|
|
4
|
+
import * as err from '../errors.js';
|
|
5
|
+
import { nsidToLookupDomain } from '../utils.js';
|
|
6
|
+
const uint32 = v.number().assert((input) => Number.isInteger(input) && input >= 0 && input <= 2 ** 32 - 1);
|
|
7
|
+
const question = v.object({
|
|
8
|
+
name: v.string(),
|
|
9
|
+
type: v.literal(16), // TXT
|
|
10
|
+
});
|
|
11
|
+
const answer = v.object({
|
|
12
|
+
name: v.string(),
|
|
13
|
+
type: v.literal(16), // TXT
|
|
14
|
+
TTL: uint32,
|
|
15
|
+
data: v.string().chain((input) => {
|
|
16
|
+
return v.ok(input.replace(/^"|"$/g, '').replace(/\\"/g, '"'));
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
const authority = v.object({
|
|
20
|
+
name: v.string(),
|
|
21
|
+
type: uint32,
|
|
22
|
+
TTL: uint32,
|
|
23
|
+
data: v.string(),
|
|
24
|
+
});
|
|
25
|
+
const result = v.object({
|
|
26
|
+
/** DNS response code */
|
|
27
|
+
Status: uint32,
|
|
28
|
+
/** Whether response is truncated */
|
|
29
|
+
TC: v.boolean(),
|
|
30
|
+
/** Whether recursive desired bit is set, always true for Google and Cloudflare DoH */
|
|
31
|
+
RD: v.boolean(),
|
|
32
|
+
/** Whether recursive available bit is set, always true for Google and Cloudflare DoH */
|
|
33
|
+
RA: v.boolean(),
|
|
34
|
+
/** Whether response data was validated with DNSSEC */
|
|
35
|
+
AD: v.boolean(),
|
|
36
|
+
/** Whether client asked to disable DNSSEC validation */
|
|
37
|
+
CD: v.boolean(),
|
|
38
|
+
/** Requested records */
|
|
39
|
+
Question: v.tuple([question]),
|
|
40
|
+
/** Answers */
|
|
41
|
+
Answer: v.array(answer).optional(() => []),
|
|
42
|
+
/** Authority */
|
|
43
|
+
Authority: v.array(authority).optional(),
|
|
44
|
+
/** Comment from the DNS server */
|
|
45
|
+
Comment: v.string().optional(),
|
|
46
|
+
});
|
|
47
|
+
const SUBDOMAIN = '_lexicon';
|
|
48
|
+
const PREFIX = 'did=';
|
|
49
|
+
const fetchDohJsonHandler = pipe(isResponseOk, parseResponseAsJson(/^application\/(dns-)?json$/, 16 * 1024), validateJsonWith(result, { mode: 'passthrough' }));
|
|
50
|
+
export class DohJsonLexiconAuthorityResolver {
|
|
51
|
+
dohUrl;
|
|
52
|
+
#fetch;
|
|
53
|
+
constructor({ dohUrl, fetch: fetchThis = fetch }) {
|
|
54
|
+
this.dohUrl = dohUrl;
|
|
55
|
+
this.#fetch = fetchThis;
|
|
56
|
+
}
|
|
57
|
+
async resolve(nsid, options) {
|
|
58
|
+
const lookupDomain = nsidToLookupDomain(nsid);
|
|
59
|
+
let json;
|
|
60
|
+
try {
|
|
61
|
+
const url = new URL(this.dohUrl);
|
|
62
|
+
url.searchParams.set('name', `${SUBDOMAIN}.${lookupDomain}`);
|
|
63
|
+
url.searchParams.set('type', 'TXT');
|
|
64
|
+
const response = await (0, this.#fetch)(url, {
|
|
65
|
+
signal: options?.signal,
|
|
66
|
+
cache: options?.noCache ? 'no-cache' : undefined,
|
|
67
|
+
headers: { accept: 'application/dns-json' },
|
|
68
|
+
});
|
|
69
|
+
const handled = await fetchDohJsonHandler(response);
|
|
70
|
+
json = handled.json;
|
|
71
|
+
}
|
|
72
|
+
catch (cause) {
|
|
73
|
+
throw new err.FailedAuthorityResolutionError(nsid, { cause });
|
|
74
|
+
}
|
|
75
|
+
const status = json.Status;
|
|
76
|
+
const answers = json.Answer;
|
|
77
|
+
if (status !== 0 /* NOERROR */) {
|
|
78
|
+
if (status === 3 /* NXDOMAIN */) {
|
|
79
|
+
throw new err.AuthorityNotFoundError(nsid);
|
|
80
|
+
}
|
|
81
|
+
throw new err.FailedAuthorityResolutionError(nsid, {
|
|
82
|
+
cause: new TypeError(`dns returned ${status}`),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
for (let i = 0, il = answers.length; i < il; i++) {
|
|
86
|
+
const answer = answers[i];
|
|
87
|
+
const data = answer.data;
|
|
88
|
+
if (!data.startsWith(PREFIX)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
for (let j = i + 1; j < il; j++) {
|
|
92
|
+
const data = answers[j].data;
|
|
93
|
+
if (data.startsWith(PREFIX)) {
|
|
94
|
+
throw new err.AmbiguousAuthorityError(nsid);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const did = data.slice(PREFIX.length);
|
|
98
|
+
if (!isAtprotoDid(did)) {
|
|
99
|
+
throw new err.InvalidResolvedAuthorityError(nsid, did);
|
|
100
|
+
}
|
|
101
|
+
return did;
|
|
102
|
+
}
|
|
103
|
+
// theoretically this shouldn't happen, it should've returned NXDOMAIN
|
|
104
|
+
throw new err.AuthorityNotFoundError(nsid);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=doh-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doh-json.js","sourceRoot":"","sources":["../../lib/authority/doh-json.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LEXICON_SCHEMA_COLLECTION = "com.atproto.lexicon.schema";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../lib/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG,4BAA4B,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Nsid } from '@atcute/lexicons/syntax';
|
|
2
|
+
export declare class LexiconAuthorityResolutionError extends Error {
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class AuthorityNotFoundError extends LexiconAuthorityResolutionError {
|
|
6
|
+
nsid: Nsid;
|
|
7
|
+
name: string;
|
|
8
|
+
constructor(nsid: Nsid);
|
|
9
|
+
}
|
|
10
|
+
export declare class FailedAuthorityResolutionError extends LexiconAuthorityResolutionError {
|
|
11
|
+
nsid: Nsid;
|
|
12
|
+
name: string;
|
|
13
|
+
constructor(nsid: Nsid, options?: ErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
export declare class InvalidResolvedAuthorityError extends LexiconAuthorityResolutionError {
|
|
16
|
+
nsid: Nsid;
|
|
17
|
+
did: string;
|
|
18
|
+
name: string;
|
|
19
|
+
constructor(nsid: Nsid, did: string);
|
|
20
|
+
}
|
|
21
|
+
export declare class AmbiguousAuthorityError extends LexiconAuthorityResolutionError {
|
|
22
|
+
nsid: Nsid;
|
|
23
|
+
name: string;
|
|
24
|
+
constructor(nsid: Nsid);
|
|
25
|
+
}
|
|
26
|
+
export declare class LexiconResolutionError extends Error {
|
|
27
|
+
name: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class FailedLexiconResolutionError extends LexiconResolutionError {
|
|
30
|
+
nsid: Nsid;
|
|
31
|
+
name: string;
|
|
32
|
+
constructor(nsid: Nsid, options?: ErrorOptions);
|
|
33
|
+
}
|
|
34
|
+
export declare class InvalidLexiconSchemaError extends LexiconResolutionError {
|
|
35
|
+
nsid: Nsid;
|
|
36
|
+
name: string;
|
|
37
|
+
constructor(nsid: Nsid, options?: ErrorOptions);
|
|
38
|
+
}
|
|
39
|
+
export declare class InvalidLexiconProofError extends LexiconResolutionError {
|
|
40
|
+
nsid: Nsid;
|
|
41
|
+
name: string;
|
|
42
|
+
constructor(nsid: Nsid, options?: ErrorOptions);
|
|
43
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// #region Lexicon authority resolution errors
|
|
2
|
+
export class LexiconAuthorityResolutionError extends Error {
|
|
3
|
+
name = 'LexiconAuthorityResolutionError';
|
|
4
|
+
}
|
|
5
|
+
export class AuthorityNotFoundError extends LexiconAuthorityResolutionError {
|
|
6
|
+
nsid;
|
|
7
|
+
name = 'AuthorityNotFoundError';
|
|
8
|
+
constructor(nsid) {
|
|
9
|
+
super(`lexicon authority not found; nsid=${nsid}`);
|
|
10
|
+
this.nsid = nsid;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class FailedAuthorityResolutionError extends LexiconAuthorityResolutionError {
|
|
14
|
+
nsid;
|
|
15
|
+
name = 'FailedAuthorityResolutionError';
|
|
16
|
+
constructor(nsid, options) {
|
|
17
|
+
super(`failed to resolve lexicon authority; nsid=${nsid}`, options);
|
|
18
|
+
this.nsid = nsid;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class InvalidResolvedAuthorityError extends LexiconAuthorityResolutionError {
|
|
22
|
+
nsid;
|
|
23
|
+
did;
|
|
24
|
+
name = 'InvalidResolvedAuthorityError';
|
|
25
|
+
constructor(nsid, did) {
|
|
26
|
+
super(`lexicon authority returned invalid did; nsid=${nsid}; did=${did}`);
|
|
27
|
+
this.nsid = nsid;
|
|
28
|
+
this.did = did;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class AmbiguousAuthorityError extends LexiconAuthorityResolutionError {
|
|
32
|
+
nsid;
|
|
33
|
+
name = 'AmbiguousAuthorityError';
|
|
34
|
+
constructor(nsid) {
|
|
35
|
+
super(`lexicon authority returned multiple did values; nsid=${nsid}`);
|
|
36
|
+
this.nsid = nsid;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// #endregion
|
|
40
|
+
// #region Lexicon resolution errors
|
|
41
|
+
export class LexiconResolutionError extends Error {
|
|
42
|
+
name = 'LexiconResolutionError';
|
|
43
|
+
}
|
|
44
|
+
export class FailedLexiconResolutionError extends LexiconResolutionError {
|
|
45
|
+
nsid;
|
|
46
|
+
name = 'FailedLexiconResolutionError';
|
|
47
|
+
constructor(nsid, options) {
|
|
48
|
+
super(`failed to resolve lexicon; nsid=${nsid}`, options);
|
|
49
|
+
this.nsid = nsid;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class InvalidLexiconSchemaError extends LexiconResolutionError {
|
|
53
|
+
nsid;
|
|
54
|
+
name = 'InvalidLexiconSchemaError';
|
|
55
|
+
constructor(nsid, options) {
|
|
56
|
+
super(`invalid lexicon schema; nsid=${nsid}`, options);
|
|
57
|
+
this.nsid = nsid;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export class InvalidLexiconProofError extends LexiconResolutionError {
|
|
61
|
+
nsid;
|
|
62
|
+
name = 'InvalidLexiconProofError';
|
|
63
|
+
constructor(nsid, options) {
|
|
64
|
+
super(`invalid lexicon record proof; nsid=${nsid}`, options);
|
|
65
|
+
this.nsid = nsid;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// #endregion
|
|
69
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../lib/errors.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IAChD,IAAI,GAAG,iCAAiC,CAAC;CAClD;AAED,MAAM,OAAO,sBAAuB,SAAQ,+BAA+B;IAGvD;IAFV,IAAI,GAAG,wBAAwB,CAAC;IAEzC,YAAmB,IAAU;QAC5B,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;QADjC,SAAI,GAAJ,IAAI,CAAM;IAE7B,CAAC;CACD;AAED,MAAM,OAAO,8BAA+B,SAAQ,+BAA+B;IAI1E;IAHC,IAAI,GAAG,gCAAgC,CAAC;IAEjD,YACQ,IAAU,EACjB,OAAsB;QAEtB,KAAK,CAAC,6CAA6C,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAH7D,SAAI,GAAJ,IAAI,CAAM;IAIlB,CAAC;CACD;AAED,MAAM,OAAO,6BAA8B,SAAQ,+BAA+B;IAIzE;IACA;IAJC,IAAI,GAAG,+BAA+B,CAAC;IAEhD,YACQ,IAAU,EACV,GAAW;QAElB,KAAK,CAAC,gDAAgD,IAAI,SAAS,GAAG,EAAE,CAAC,CAAC;QAHnE,SAAI,GAAJ,IAAI,CAAM;QACV,QAAG,GAAH,GAAG,CAAQ;IAGnB,CAAC;CACD;AAED,MAAM,OAAO,uBAAwB,SAAQ,+BAA+B;IAGxD;IAFV,IAAI,GAAG,yBAAyB,CAAC;IAE1C,YAAmB,IAAU;QAC5B,KAAK,CAAC,wDAAwD,IAAI,EAAE,CAAC,CAAC;QADpD,SAAI,GAAJ,IAAI,CAAM;IAE7B,CAAC;CACD;AACD,aAAa;AAEb,oCAAoC;AACpC,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACvC,IAAI,GAAG,wBAAwB,CAAC;CACzC;AAGD,MAAM,OAAO,4BAA6B,SAAQ,sBAAsB;IAI/D;IAHC,IAAI,GAAG,8BAA8B,CAAC;IAE/C,YACQ,IAAU,EACjB,OAAsB;QAEtB,KAAK,CAAC,mCAAmC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAHnD,SAAI,GAAJ,IAAI,CAAM;IAIlB,CAAC;CACD;AAED,MAAM,OAAO,yBAA0B,SAAQ,sBAAsB;IAI5D;IAHC,IAAI,GAAG,2BAA2B,CAAC;IAE5C,YACQ,IAAU,EACjB,OAAsB;QAEtB,KAAK,CAAC,gCAAgC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAHhD,SAAI,GAAJ,IAAI,CAAM;IAIlB,CAAC;CACD;AAED,MAAM,OAAO,wBAAyB,SAAQ,sBAAsB;IAI3D;IAHC,IAAI,GAAG,0BAA0B,CAAC;IAE3C,YACQ,IAAU,EACjB,OAAsB;QAEtB,KAAK,CAAC,sCAAsC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAHtD,SAAI,GAAJ,IAAI,CAAM;IAIlB,CAAC;CACD;AACD,aAAa"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type DidDocument } from '@atcute/identity';
|
|
2
|
+
import { type AtprotoDid } from '@atcute/lexicons/syntax';
|
|
3
|
+
export interface VerifiedRecord {
|
|
4
|
+
/** AT-URI of the record */
|
|
5
|
+
uri: string;
|
|
6
|
+
/** CID of the record */
|
|
7
|
+
cid: string;
|
|
8
|
+
/** Record data */
|
|
9
|
+
record: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface VerifyRecordOptions {
|
|
12
|
+
did: AtprotoDid;
|
|
13
|
+
collection: string;
|
|
14
|
+
rkey: string;
|
|
15
|
+
didDocument: DidDocument;
|
|
16
|
+
carBytes: Uint8Array;
|
|
17
|
+
}
|
|
18
|
+
export declare const verifyRecord: ({ did, collection, rkey, didDocument, carBytes, }: VerifyRecordOptions) => Promise<VerifiedRecord>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as CAR from '@atcute/car';
|
|
2
|
+
import { CarReader } from '@atcute/car/v4';
|
|
3
|
+
import * as CBOR from '@atcute/cbor';
|
|
4
|
+
import * as CID from '@atcute/cid';
|
|
5
|
+
import { getPublicKeyFromDidController, verifySig } from '@atcute/crypto';
|
|
6
|
+
import { getAtprotoVerificationMaterial } from '@atcute/identity';
|
|
7
|
+
import {} from '@atcute/lexicons/syntax';
|
|
8
|
+
import { toSha256 } from '@atcute/uint8array';
|
|
9
|
+
export const verifyRecord = async ({ did, collection, rkey, didDocument, carBytes, }) => {
|
|
10
|
+
// grab public key from did document
|
|
11
|
+
let publicKey;
|
|
12
|
+
{
|
|
13
|
+
const controller = getAtprotoVerificationMaterial(didDocument);
|
|
14
|
+
if (!controller) {
|
|
15
|
+
throw new Error(`did document does not contain verification material`);
|
|
16
|
+
}
|
|
17
|
+
publicKey = getPublicKeyFromDidController(controller);
|
|
18
|
+
}
|
|
19
|
+
// read the car
|
|
20
|
+
let blockmap;
|
|
21
|
+
let commit;
|
|
22
|
+
{
|
|
23
|
+
const reader = CarReader.fromUint8Array(carBytes);
|
|
24
|
+
if (reader.header.data.roots.length !== 1) {
|
|
25
|
+
throw new Error(`car must have exactly one root`);
|
|
26
|
+
}
|
|
27
|
+
blockmap = new Map();
|
|
28
|
+
for (const entry of reader) {
|
|
29
|
+
const cidString = CID.toString(entry.cid);
|
|
30
|
+
// Verify that `bytes` matches its associated CID
|
|
31
|
+
const expectedCid = CID.toString(await CID.create(entry.cid.codec, entry.bytes));
|
|
32
|
+
if (cidString !== expectedCid) {
|
|
33
|
+
throw new Error(`cid does not match bytes`);
|
|
34
|
+
}
|
|
35
|
+
blockmap.set(cidString, entry);
|
|
36
|
+
}
|
|
37
|
+
if (blockmap.size === 0) {
|
|
38
|
+
throw new Error(`car must have at least one block`);
|
|
39
|
+
}
|
|
40
|
+
commit = CAR.readBlock(blockmap, reader.header.data.roots[0], CAR.isCommit);
|
|
41
|
+
}
|
|
42
|
+
// verify did in commit matches the did
|
|
43
|
+
if (commit.did !== did) {
|
|
44
|
+
throw new Error(`did in commit does not match expected did`);
|
|
45
|
+
}
|
|
46
|
+
// verify signature contained in commit is valid
|
|
47
|
+
{
|
|
48
|
+
const { sig, ...unsigned } = commit;
|
|
49
|
+
const data = CBOR.encode(unsigned);
|
|
50
|
+
const valid = await verifySig(publicKey, CBOR.fromBytes(sig), data);
|
|
51
|
+
if (!valid) {
|
|
52
|
+
throw new Error(`signature verification failed`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// find and verify the record in the commit
|
|
56
|
+
const targetKey = `${collection}/${rkey}`;
|
|
57
|
+
const { found } = await dfs(blockmap, commit.data.$link, targetKey);
|
|
58
|
+
if (!found) {
|
|
59
|
+
throw new Error(`could not find record in car`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
uri: `at://${did}/${collection}/${rkey}`,
|
|
63
|
+
cid: found.cid,
|
|
64
|
+
record: found.record,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const encoder = new TextEncoder();
|
|
68
|
+
const decoder = new TextDecoder();
|
|
69
|
+
const dfs = async (blockmap, from, targetKey, visited = new Set()) => {
|
|
70
|
+
// If there's no starting point, return empty state
|
|
71
|
+
if (from == null) {
|
|
72
|
+
return { found: false };
|
|
73
|
+
}
|
|
74
|
+
// Check for cycles
|
|
75
|
+
{
|
|
76
|
+
if (visited.has(from)) {
|
|
77
|
+
throw new Error(`cycle detected; cid=${from}`);
|
|
78
|
+
}
|
|
79
|
+
visited.add(from);
|
|
80
|
+
}
|
|
81
|
+
// Get the block data
|
|
82
|
+
let node;
|
|
83
|
+
{
|
|
84
|
+
const entry = blockmap.get(from);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
return { found: false };
|
|
87
|
+
}
|
|
88
|
+
const decoded = CBOR.decode(entry.bytes);
|
|
89
|
+
if (!CAR.isMstNode(decoded)) {
|
|
90
|
+
throw new Error(`invalid mst node; cid=${from}`);
|
|
91
|
+
}
|
|
92
|
+
node = decoded;
|
|
93
|
+
}
|
|
94
|
+
// Recursively process the left child
|
|
95
|
+
const left = await dfs(blockmap, node.l?.$link, targetKey, visited);
|
|
96
|
+
let key = '';
|
|
97
|
+
let found = left.found;
|
|
98
|
+
let depth;
|
|
99
|
+
let firstKey;
|
|
100
|
+
let lastKey;
|
|
101
|
+
// Process all entries in this node
|
|
102
|
+
for (const entry of node.e) {
|
|
103
|
+
// Construct the key by truncating and appending
|
|
104
|
+
key = key.substring(0, entry.p) + decoder.decode(CBOR.fromBytes(entry.k));
|
|
105
|
+
// Check if this is our target key
|
|
106
|
+
if (key === targetKey) {
|
|
107
|
+
const recordBlock = blockmap.get(entry.v.$link);
|
|
108
|
+
if (recordBlock) {
|
|
109
|
+
const record = CBOR.decode(recordBlock.bytes);
|
|
110
|
+
found = { cid: entry.v.$link, record };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Calculate depth based on leading zeros in the hash
|
|
114
|
+
const keyDigest = await toSha256(encoder.encode(key));
|
|
115
|
+
let zeroCount = 0;
|
|
116
|
+
outerLoop: for (const byte of keyDigest) {
|
|
117
|
+
for (let bit = 7; bit >= 0; bit--) {
|
|
118
|
+
if (((byte >> bit) & 1) !== 0) {
|
|
119
|
+
break outerLoop;
|
|
120
|
+
}
|
|
121
|
+
zeroCount++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const thisDepth = Math.floor(zeroCount / 2);
|
|
125
|
+
// Ensure consistent depth
|
|
126
|
+
if (depth === undefined) {
|
|
127
|
+
depth = thisDepth;
|
|
128
|
+
}
|
|
129
|
+
else if (depth !== thisDepth) {
|
|
130
|
+
throw new Error(`node has entries with different depths; cid=${from}`);
|
|
131
|
+
}
|
|
132
|
+
// Track first and last keys
|
|
133
|
+
if (lastKey === undefined) {
|
|
134
|
+
firstKey = key;
|
|
135
|
+
lastKey = key;
|
|
136
|
+
}
|
|
137
|
+
// Check key ordering
|
|
138
|
+
if (lastKey > key) {
|
|
139
|
+
throw new Error(`entries are out of order; cid=${from}`);
|
|
140
|
+
}
|
|
141
|
+
// Process right child
|
|
142
|
+
const right = await dfs(blockmap, entry.t?.$link, targetKey, visited);
|
|
143
|
+
// Check ordering with right subtree
|
|
144
|
+
if (right.min && right.min < lastKey) {
|
|
145
|
+
throw new Error(`entries are out of order; cid=${from}`);
|
|
146
|
+
}
|
|
147
|
+
found = found || right.found;
|
|
148
|
+
// Check depth ordering
|
|
149
|
+
if (left.depth !== undefined && left.depth >= thisDepth) {
|
|
150
|
+
throw new Error(`depths are out of order; cid=${from}`);
|
|
151
|
+
}
|
|
152
|
+
if (right.depth !== undefined && right.depth >= thisDepth) {
|
|
153
|
+
throw new Error(`depths are out of order; cid=${from}`);
|
|
154
|
+
}
|
|
155
|
+
// Update last key based on right subtree
|
|
156
|
+
lastKey = right.max ?? key;
|
|
157
|
+
}
|
|
158
|
+
// Check ordering with left subtree
|
|
159
|
+
if (left.max && firstKey && left.max > firstKey) {
|
|
160
|
+
throw new Error(`entries are out of order; cid=${from}`);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
found,
|
|
164
|
+
min: firstKey,
|
|
165
|
+
max: lastKey,
|
|
166
|
+
depth,
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../lib/schemas/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DidDocumentResolver } from '@atcute/identity-resolver';
|
|
2
|
+
import type { AtprotoDid, Nsid } from '@atcute/lexicons/syntax';
|
|
3
|
+
import type { ResolvedSchema, ResolveLexiconRecordOptions } from '../types.js';
|
|
4
|
+
export interface LexiconSchemaResolverOptions {
|
|
5
|
+
didDocumentResolver: DidDocumentResolver;
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
export declare class LexiconSchemaResolver {
|
|
9
|
+
#private;
|
|
10
|
+
readonly didDocumentResolver: DidDocumentResolver;
|
|
11
|
+
constructor({ didDocumentResolver, fetch: fetchThis }: LexiconSchemaResolverOptions);
|
|
12
|
+
resolve(authority: AtprotoDid, nsid: Nsid, options?: ResolveLexiconRecordOptions): Promise<ResolvedSchema>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getPdsEndpoint } from '@atcute/identity';
|
|
2
|
+
import { lexiconDoc } from '@atcute/lexicon-doc';
|
|
3
|
+
import { FailedResponseError } from '@atcute/util-fetch';
|
|
4
|
+
import { LEXICON_SCHEMA_COLLECTION } from '../constants.js';
|
|
5
|
+
import * as err from '../errors.js';
|
|
6
|
+
import { verifyRecord } from './verify.js';
|
|
7
|
+
export class LexiconSchemaResolver {
|
|
8
|
+
didDocumentResolver;
|
|
9
|
+
#fetch;
|
|
10
|
+
constructor({ didDocumentResolver, fetch: fetchThis = fetch }) {
|
|
11
|
+
this.didDocumentResolver = didDocumentResolver;
|
|
12
|
+
this.#fetch = fetchThis;
|
|
13
|
+
}
|
|
14
|
+
async resolve(authority, nsid, options) {
|
|
15
|
+
// Step 1: Resolve DID to get PDS service endpoint
|
|
16
|
+
const didDocument = await this.didDocumentResolver.resolve(authority, {
|
|
17
|
+
signal: options?.signal,
|
|
18
|
+
noCache: options?.noCache,
|
|
19
|
+
});
|
|
20
|
+
const pdsEndpoint = getPdsEndpoint(didDocument);
|
|
21
|
+
if (!pdsEndpoint) {
|
|
22
|
+
throw new err.FailedLexiconResolutionError(nsid, {
|
|
23
|
+
cause: new TypeError(`no pds service in did document; did=${authority}`),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
// Step 2: Fetch the record
|
|
27
|
+
let carBytes;
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL('/xrpc/com.atproto.sync.getRecord', pdsEndpoint);
|
|
30
|
+
url.searchParams.set('did', authority);
|
|
31
|
+
url.searchParams.set('collection', LEXICON_SCHEMA_COLLECTION);
|
|
32
|
+
url.searchParams.set('rkey', nsid);
|
|
33
|
+
const response = await (0, this.#fetch)(url, {
|
|
34
|
+
signal: options?.signal,
|
|
35
|
+
cache: options?.noCache ? 'no-cache' : undefined,
|
|
36
|
+
headers: { accept: 'application/vnd.ipld.car' },
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new FailedResponseError(response.status, `got http ${response.status}`);
|
|
40
|
+
}
|
|
41
|
+
carBytes = await response.bytes();
|
|
42
|
+
}
|
|
43
|
+
catch (cause) {
|
|
44
|
+
throw new err.FailedLexiconResolutionError(nsid, { cause });
|
|
45
|
+
}
|
|
46
|
+
// Step 3: Verify record and extract data
|
|
47
|
+
let verifiedRecord;
|
|
48
|
+
try {
|
|
49
|
+
verifiedRecord = await verifyRecord({
|
|
50
|
+
did: authority,
|
|
51
|
+
collection: LEXICON_SCHEMA_COLLECTION,
|
|
52
|
+
rkey: nsid,
|
|
53
|
+
didDocument,
|
|
54
|
+
carBytes,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (cause) {
|
|
58
|
+
throw new err.InvalidLexiconProofError(nsid, { cause });
|
|
59
|
+
}
|
|
60
|
+
// Step 4: Parse into lexicon schema
|
|
61
|
+
const rawSchema = verifiedRecord.record;
|
|
62
|
+
if (typeof rawSchema !== 'object' ||
|
|
63
|
+
rawSchema === null ||
|
|
64
|
+
rawSchema.$type !== LEXICON_SCHEMA_COLLECTION ||
|
|
65
|
+
rawSchema.id !== nsid) {
|
|
66
|
+
throw new err.InvalidLexiconSchemaError(nsid);
|
|
67
|
+
}
|
|
68
|
+
let schema;
|
|
69
|
+
try {
|
|
70
|
+
schema = lexiconDoc.parse(rawSchema, { mode: 'passthrough' });
|
|
71
|
+
}
|
|
72
|
+
catch (cause) {
|
|
73
|
+
throw new err.InvalidLexiconSchemaError(nsid, { cause });
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
uri: verifiedRecord.uri,
|
|
77
|
+
cid: verifiedRecord.cid,
|
|
78
|
+
schema,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=xrpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xrpc.js","sourceRoot":"","sources":["../../lib/schemas/xrpc.ts"],"names":[],"mappings":"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"}
|