@atproto/lexicon-resolver 0.2.5 → 0.2.6
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 +6 -0
- package/LICENSE.txt +1 -1
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +11 -5
- package/dist/record.js.map +1 -1
- package/package.json +4 -4
- package/src/record.ts +16 -7
- package/tests/record.test.ts +92 -2
- package/tsconfig.tests.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @atproto/lexicon-resolver
|
|
2
2
|
|
|
3
|
+
## 0.2.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4577](https://github.com/bluesky-social/atproto/pull/4577) [`78e8ec2`](https://github.com/bluesky-social/atproto/commit/78e8ec25df860f6d383f3a0e38a2c3a5670bfe01) Thanks [@devinivy](https://github.com/devinivy)! - Do not require at:// handle for lexicon resolution
|
|
8
|
+
|
|
3
9
|
## 0.2.5
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Dual MIT/Apache-2.0 License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2022-
|
|
3
|
+
Copyright (c) 2022-2026 Bluesky Social PBC, and Contributors
|
|
4
4
|
|
|
5
5
|
Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
|
|
6
6
|
|
package/dist/record.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,EAAE,UAAU,EAA0B,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EACL,MAAM,EAMP,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,KAAK,EAAkB,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAKtE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAE/E;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,GAAG,EAAE,KAAK,GAAG,MAAM,EACnB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAE9B,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,YAAY,CAAA;CACvD,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,KAAK,CAAA;IACV,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,MAAM,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,CAAA;AAE/D;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,qBAAqB,CAoDvB;AAED,eAAO,MAAM,aAAa,uBAAwB,CAAA;AAElD,eAAO,MAAM,SAAS,sFAIpB,CAAA;AAEF,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIrD"}
|
package/dist/record.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable import/no-deprecated */
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.RecordResolutionError = exports.safeFetch = exports.resolveRecord = exports.IdResolver = exports.CID = exports.AtUri = void 0;
|
|
5
4
|
exports.buildRecordResolver = buildRecordResolver;
|
|
@@ -21,18 +20,25 @@ function buildRecordResolver(options = {}) {
|
|
|
21
20
|
return async function resolveRecord(uriStr, opts = {}) {
|
|
22
21
|
const uri = typeof uriStr === 'string' ? new syntax_1.AtUri(uriStr) : uriStr;
|
|
23
22
|
const did = await getDidFromUri(uri, { idResolver });
|
|
24
|
-
const
|
|
25
|
-
.
|
|
23
|
+
const identityDoc = await idResolver.did
|
|
24
|
+
.ensureResolve(did, opts.forceRefresh)
|
|
26
25
|
.catch((err) => {
|
|
27
26
|
throw new RecordResolutionError('Could not resolve DID identity data', {
|
|
28
27
|
cause: err,
|
|
29
28
|
});
|
|
30
29
|
});
|
|
30
|
+
const { pds, signingKey } = (0, identity_1.parseToAtprotoDocument)(identityDoc);
|
|
31
|
+
if (!pds) {
|
|
32
|
+
throw new RecordResolutionError('Incomplete DID identity data: missing pds');
|
|
33
|
+
}
|
|
34
|
+
if (!signingKey) {
|
|
35
|
+
throw new RecordResolutionError('Incomplete DID identity data: missing signing key');
|
|
36
|
+
}
|
|
31
37
|
const client = new index_js_1.AtpBaseClient(typeof rpc === 'function'
|
|
32
38
|
? rpc
|
|
33
39
|
: {
|
|
34
40
|
...rpc,
|
|
35
|
-
service: rpc?.service ??
|
|
41
|
+
service: rpc?.service ?? pds,
|
|
36
42
|
fetch: rpc?.fetch ?? exports.safeFetch,
|
|
37
43
|
});
|
|
38
44
|
const { data: proofBytes } = await client.com.atproto.sync
|
|
@@ -48,7 +54,7 @@ function buildRecordResolver(options = {}) {
|
|
|
48
54
|
});
|
|
49
55
|
const verified = await verifyRecordProof(proofBytes, {
|
|
50
56
|
uri: syntax_1.AtUri.make(did, uri.collection, uri.rkey),
|
|
51
|
-
signingKey
|
|
57
|
+
signingKey,
|
|
52
58
|
});
|
|
53
59
|
return verified;
|
|
54
60
|
};
|
package/dist/record.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../src/record.ts"],"names":[],"mappings":"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","sourcesContent":["import { CID } from 'multiformats/cid'\nimport { IdResolver, parseToAtprotoDocument } from '@atproto/identity'\nimport { RepoRecord } from '@atproto/lexicon'\nimport {\n Commit,\n MST,\n MemoryBlockstore,\n def as repoDef,\n readCarWithRoot,\n verifyCommitSig,\n} from '@atproto/repo'\nimport { AtUri, ensureValidDid } from '@atproto/syntax'\nimport { BuildFetchHandlerOptions, FetchHandler } from '@atproto/xrpc'\nimport { safeFetchWrap } from '@atproto-labs/fetch-node'\nimport { AtpBaseClient as Client } from './client/index.js'\nimport { isValidDid } from './util.js'\n\n/**\n * Resolve a record from the network.\n */\nexport type RecordResolver = (uri: AtUri | string) => Promise<RecordResolution>\n\n/**\n * Resolve a record from the network, verifying its authenticity.\n */\nexport type AtprotoRecordResolver = (\n uri: AtUri | string,\n options?: ResolveRecordOptions,\n) => Promise<RecordResolution>\n\nexport type BuildRecordResolverOptions = {\n idResolver?: IdResolver\n rpc?: Partial<BuildFetchHandlerOptions> | FetchHandler\n}\n\nexport type ResolveRecordOptions = {\n forceRefresh?: boolean\n}\n\nexport type RecordResolution = {\n commit: Commit\n uri: AtUri\n cid: CID\n record: RepoRecord\n}\n\nexport { AtUri, CID, type Commit, IdResolver, type RepoRecord }\n\n/**\n * Build a record resolver function.\n */\nexport function buildRecordResolver(\n options: BuildRecordResolverOptions = {},\n): AtprotoRecordResolver {\n const { idResolver = new IdResolver(), rpc } = options\n return async function resolveRecord(\n uriStr: AtUri | string,\n opts: ResolveRecordOptions = {},\n ): Promise<RecordResolution> {\n const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr\n const did = await getDidFromUri(uri, { idResolver })\n const identityDoc = await idResolver.did\n .ensureResolve(did, opts.forceRefresh)\n .catch((err) => {\n throw new RecordResolutionError('Could not resolve DID identity data', {\n cause: err,\n })\n })\n const { pds, signingKey } = parseToAtprotoDocument(identityDoc)\n if (!pds) {\n throw new RecordResolutionError(\n 'Incomplete DID identity data: missing pds',\n )\n }\n if (!signingKey) {\n throw new RecordResolutionError(\n 'Incomplete DID identity data: missing signing key',\n )\n }\n const client = new Client(\n typeof rpc === 'function'\n ? rpc\n : {\n ...rpc,\n service: rpc?.service ?? pds,\n fetch: rpc?.fetch ?? safeFetch,\n },\n )\n const { data: proofBytes } = await client.com.atproto.sync\n .getRecord({\n did,\n collection: uri.collection,\n rkey: uri.rkey,\n })\n .catch((err) => {\n throw new RecordResolutionError('Could not fetch record proof', {\n cause: err,\n })\n })\n const verified = await verifyRecordProof(proofBytes, {\n uri: AtUri.make(did, uri.collection, uri.rkey),\n signingKey,\n })\n return verified\n }\n}\n\nexport const resolveRecord = buildRecordResolver()\n\nexport const safeFetch = safeFetchWrap({\n allowIpHost: false,\n allowImplicitRedirect: true,\n responseMaxSize: (1024 + 10) * 1024, // 1MB + 10kB, just a bit larger than max record size\n})\n\nexport class RecordResolutionError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options)\n this.name = 'RecordResolutionError'\n }\n}\n\nasync function getDidFromUri(\n uri: AtUri,\n { idResolver }: { idResolver: IdResolver },\n) {\n if (uri.host.startsWith('did:')) {\n ensureValidDid(uri.host)\n return uri.host\n }\n const resolved = await idResolver.handle.resolve(uri.host)\n if (!resolved || !isValidDid(resolved)) {\n throw new RecordResolutionError('Could not resolve handle found in AT-URI')\n }\n return resolved\n}\n\nasync function verifyRecordProof(\n proofBytes: Uint8Array,\n { uri, signingKey }: { uri: AtUri; signingKey: string },\n) {\n const { root, blocks } = await readCarWithRoot(proofBytes).catch((err) => {\n throw new RecordResolutionError('Malformed record proof', { cause: err })\n })\n const blockstore = new MemoryBlockstore(blocks)\n const commit = await blockstore.readObj(root, repoDef.commit).catch((err) => {\n throw new RecordResolutionError('Invalid commit in record proof', {\n cause: err,\n })\n })\n if (commit.did !== uri.host) {\n throw new RecordResolutionError(`Invalid repo did: ${commit.did}`)\n }\n const validSig = await verifyCommitSig(commit, signingKey)\n if (!validSig) {\n throw new RecordResolutionError(\n `Invalid signature on commit: ${root.toString()}`,\n )\n }\n const mst = MST.load(blockstore, commit.data)\n const cid = await mst.get(`${uri.collection}/${uri.rkey}`)\n if (!cid) {\n throw new RecordResolutionError('Record not found in proof')\n }\n const record = await blockstore.readRecord(cid)\n return { commit, uri, cid, record }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/lexicon-resolver",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "ATProto Lexicon resolution",
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"multiformats": "^9.9.0",
|
|
27
27
|
"@atproto-labs/fetch-node": "^0.2.0",
|
|
28
28
|
"@atproto/identity": "^0.4.10",
|
|
29
|
-
"@atproto/lexicon": "^0.6.
|
|
29
|
+
"@atproto/lexicon": "^0.6.1",
|
|
30
30
|
"@atproto/repo": "^0.8.12",
|
|
31
|
-
"@atproto/syntax": "^0.4.
|
|
31
|
+
"@atproto/syntax": "^0.4.3",
|
|
32
32
|
"@atproto/xrpc": "^0.7.7"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"jest": "^28.1.2",
|
|
36
36
|
"typescript": "^5.6.3",
|
|
37
|
-
"@atproto/common": "^0.5.
|
|
37
|
+
"@atproto/common": "^0.5.9",
|
|
38
38
|
"@atproto/lex-cli": "^0.9.8"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
package/src/record.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
/* eslint-disable import/no-deprecated */
|
|
2
|
-
|
|
3
1
|
import { CID } from 'multiformats/cid'
|
|
4
|
-
import { IdResolver } from '@atproto/identity'
|
|
2
|
+
import { IdResolver, parseToAtprotoDocument } from '@atproto/identity'
|
|
5
3
|
import { RepoRecord } from '@atproto/lexicon'
|
|
6
4
|
import {
|
|
7
5
|
Commit,
|
|
@@ -61,19 +59,30 @@ export function buildRecordResolver(
|
|
|
61
59
|
): Promise<RecordResolution> {
|
|
62
60
|
const uri = typeof uriStr === 'string' ? new AtUri(uriStr) : uriStr
|
|
63
61
|
const did = await getDidFromUri(uri, { idResolver })
|
|
64
|
-
const
|
|
65
|
-
.
|
|
62
|
+
const identityDoc = await idResolver.did
|
|
63
|
+
.ensureResolve(did, opts.forceRefresh)
|
|
66
64
|
.catch((err) => {
|
|
67
65
|
throw new RecordResolutionError('Could not resolve DID identity data', {
|
|
68
66
|
cause: err,
|
|
69
67
|
})
|
|
70
68
|
})
|
|
69
|
+
const { pds, signingKey } = parseToAtprotoDocument(identityDoc)
|
|
70
|
+
if (!pds) {
|
|
71
|
+
throw new RecordResolutionError(
|
|
72
|
+
'Incomplete DID identity data: missing pds',
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
if (!signingKey) {
|
|
76
|
+
throw new RecordResolutionError(
|
|
77
|
+
'Incomplete DID identity data: missing signing key',
|
|
78
|
+
)
|
|
79
|
+
}
|
|
71
80
|
const client = new Client(
|
|
72
81
|
typeof rpc === 'function'
|
|
73
82
|
? rpc
|
|
74
83
|
: {
|
|
75
84
|
...rpc,
|
|
76
|
-
service: rpc?.service ??
|
|
85
|
+
service: rpc?.service ?? pds,
|
|
77
86
|
fetch: rpc?.fetch ?? safeFetch,
|
|
78
87
|
},
|
|
79
88
|
)
|
|
@@ -90,7 +99,7 @@ export function buildRecordResolver(
|
|
|
90
99
|
})
|
|
91
100
|
const verified = await verifyRecordProof(proofBytes, {
|
|
92
101
|
uri: AtUri.make(did, uri.collection, uri.rkey),
|
|
93
|
-
signingKey
|
|
102
|
+
signingKey,
|
|
94
103
|
})
|
|
95
104
|
return verified
|
|
96
105
|
}
|
package/tests/record.test.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable import/no-deprecated */
|
|
2
|
-
|
|
3
1
|
import { dataToCborBlock } from '@atproto/common'
|
|
4
2
|
import { SeedClient, TestNetworkNoAppView, usersSeed } from '@atproto/dev-env'
|
|
5
3
|
import { AtprotoRecordResolver, buildRecordResolver } from '../src/index.js'
|
|
@@ -97,4 +95,96 @@ describe('Record resolution', () => {
|
|
|
97
95
|
}),
|
|
98
96
|
).rejects.toThrow('Malformed record proof')
|
|
99
97
|
})
|
|
98
|
+
|
|
99
|
+
it('does not resolve record with missing signing key.', async () => {
|
|
100
|
+
const post = await sc.post(sc.dids.alice, 'post5')
|
|
101
|
+
await network.pds.ctx.plcClient.updateData(
|
|
102
|
+
sc.dids.alice,
|
|
103
|
+
network.pds.ctx.plcRotationKey,
|
|
104
|
+
(doc) => {
|
|
105
|
+
doc.verificationMethods = {
|
|
106
|
+
not_atproto: doc.verificationMethods.atproto,
|
|
107
|
+
}
|
|
108
|
+
return doc
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
await expect(
|
|
112
|
+
resolveRecord(post.ref.uri, {
|
|
113
|
+
forceRefresh: true,
|
|
114
|
+
}),
|
|
115
|
+
).rejects.toThrow('Incomplete DID identity data: missing signing key')
|
|
116
|
+
// reset alice's key
|
|
117
|
+
await network.pds.ctx.plcClient.updateData(
|
|
118
|
+
sc.dids.alice,
|
|
119
|
+
network.pds.ctx.plcRotationKey,
|
|
120
|
+
(doc) => {
|
|
121
|
+
doc.verificationMethods = {
|
|
122
|
+
atproto: doc.verificationMethods.not_atproto,
|
|
123
|
+
}
|
|
124
|
+
return doc
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('does not resolve record with missing pds.', async () => {
|
|
130
|
+
const post = await sc.post(sc.dids.alice, 'post6')
|
|
131
|
+
await network.pds.ctx.plcClient.updateData(
|
|
132
|
+
sc.dids.alice,
|
|
133
|
+
network.pds.ctx.plcRotationKey,
|
|
134
|
+
(doc) => {
|
|
135
|
+
doc.services = {
|
|
136
|
+
not_atproto_pds: doc.services.atproto_pds,
|
|
137
|
+
}
|
|
138
|
+
return doc
|
|
139
|
+
},
|
|
140
|
+
)
|
|
141
|
+
await expect(
|
|
142
|
+
resolveRecord(post.ref.uri, {
|
|
143
|
+
forceRefresh: true,
|
|
144
|
+
}),
|
|
145
|
+
).rejects.toThrow('Incomplete DID identity data: missing pds')
|
|
146
|
+
// reset alice's pds
|
|
147
|
+
await network.pds.ctx.plcClient.updateData(
|
|
148
|
+
sc.dids.alice,
|
|
149
|
+
network.pds.ctx.plcRotationKey,
|
|
150
|
+
(doc) => {
|
|
151
|
+
doc.services = {
|
|
152
|
+
atproto_pds: doc.services.not_atproto_pds,
|
|
153
|
+
}
|
|
154
|
+
return doc
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('resolves record despite missing at:// handle.', async () => {
|
|
160
|
+
const post = await sc.post(sc.dids.alice, 'post7')
|
|
161
|
+
await network.pds.ctx.plcClient.updateData(
|
|
162
|
+
sc.dids.alice,
|
|
163
|
+
network.pds.ctx.plcRotationKey,
|
|
164
|
+
(doc) => {
|
|
165
|
+
doc.alsoKnownAs = doc.alsoKnownAs.map((aka) =>
|
|
166
|
+
aka.replace('at://', 'notat://'),
|
|
167
|
+
)
|
|
168
|
+
return doc
|
|
169
|
+
},
|
|
170
|
+
)
|
|
171
|
+
const result = await resolveRecord(post.ref.uriStr, {
|
|
172
|
+
forceRefresh: true,
|
|
173
|
+
})
|
|
174
|
+
expect(result.commit.did).toEqual(sc.dids.alice)
|
|
175
|
+
expect(result.cid.toString()).toEqual(post.ref.cidStr)
|
|
176
|
+
expect(result.uri.toString()).toEqual(post.ref.uriStr)
|
|
177
|
+
expect(result.record.text).toEqual('post7')
|
|
178
|
+
// reset alice's handle
|
|
179
|
+
await network.pds.ctx.plcClient.updateData(
|
|
180
|
+
sc.dids.alice,
|
|
181
|
+
network.pds.ctx.plcRotationKey,
|
|
182
|
+
(doc) => {
|
|
183
|
+
doc.alsoKnownAs = doc.alsoKnownAs.map((aka) =>
|
|
184
|
+
aka.replace('notat://', 'at://'),
|
|
185
|
+
)
|
|
186
|
+
return doc
|
|
187
|
+
},
|
|
188
|
+
)
|
|
189
|
+
})
|
|
100
190
|
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"root":["./tests/lexicon.test.ts","./tests/record.test.ts"],"version":"5.8.3"}
|