@atproto-labs/handle-resolver 0.0.1
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 +12 -0
- package/LICENSE.txt +7 -0
- package/dist/atproto-lexicon-handle-resolver.d.ts +36 -0
- package/dist/atproto-lexicon-handle-resolver.d.ts.map +1 -0
- package/dist/atproto-lexicon-handle-resolver.js +61 -0
- package/dist/atproto-lexicon-handle-resolver.js.map +1 -0
- package/dist/cached-handle-resolver.d.ts +17 -0
- package/dist/cached-handle-resolver.d.ts.map +1 -0
- package/dist/cached-handle-resolver.js +17 -0
- package/dist/cached-handle-resolver.js.map +1 -0
- package/dist/handle-resolver.d.ts +18 -0
- package/dist/handle-resolver.d.ts.map +1 -0
- package/dist/handle-resolver.js +9 -0
- package/dist/handle-resolver.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/serial-handle-resolver.d.ts +7 -0
- package/dist/serial-handle-resolver.d.ts.map +1 -0
- package/dist/serial-handle-resolver.js +29 -0
- package/dist/serial-handle-resolver.js.map +1 -0
- package/dist/universal-handle-resolver.d.ts +32 -0
- package/dist/universal-handle-resolver.d.ts.map +1 -0
- package/dist/universal-handle-resolver.js +25 -0
- package/dist/universal-handle-resolver.js.map +1 -0
- package/dist/well-known-handler-resolver.d.ts +11 -0
- package/dist/well-known-handler-resolver.d.ts.map +1 -0
- package/dist/well-known-handler-resolver.js +39 -0
- package/dist/well-known-handler-resolver.js.map +1 -0
- package/package.json +43 -0
- package/src/atproto-lexicon-handle-resolver.ts +88 -0
- package/src/cached-handle-resolver.ts +40 -0
- package/src/handle-resolver.ts +27 -0
- package/src/index.ts +11 -0
- package/src/serial-handle-resolver.ts +29 -0
- package/src/universal-handle-resolver.ts +58 -0
- package/src/well-known-handler-resolver.ts +51 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +4 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @atproto-labs/handle-resolver
|
|
2
|
+
|
|
3
|
+
## 0.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`e134c79a0`](https://github.com/bluesky-social/atproto/commit/e134c79a0ffb000b2cb36437815673fa6bda664b) Thanks [@devinivy](https://github.com/devinivy)! - Initial publish of experimental oauth packages to @atproto-labs
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`e134c79a0`](https://github.com/bluesky-social/atproto/commit/e134c79a0ffb000b2cb36437815673fa6bda664b)]:
|
|
10
|
+
- @atproto-labs/caching@0.0.1
|
|
11
|
+
- @atproto-labs/fetch@0.0.1
|
|
12
|
+
- @atproto-labs/did@0.0.1
|
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Dual MIT/Apache-2.0 License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2024 Bluesky PBC, and Contributors
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Fetch } from '@atproto-labs/fetch';
|
|
2
|
+
import z from 'zod';
|
|
3
|
+
import { HandleResolveOptions, HandleResolver, ResolvedHandle } from './handle-resolver.js';
|
|
4
|
+
export declare const xrpcErrorSchema: z.ZodObject<{
|
|
5
|
+
error: z.ZodString;
|
|
6
|
+
message: z.ZodOptional<z.ZodString>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
error: string;
|
|
9
|
+
message?: string | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
error: string;
|
|
12
|
+
message?: string | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
export type AtprotoLexiconHandleResolverOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
17
|
+
* behavior, e.g. adding headers, setting a timeout, mocking, etc.
|
|
18
|
+
*
|
|
19
|
+
* @default globalThis.fetch
|
|
20
|
+
*/
|
|
21
|
+
fetch?: Fetch;
|
|
22
|
+
/**
|
|
23
|
+
* URL of the atproto lexicon server. This is the base URL where the
|
|
24
|
+
* `com.atproto.identity.resolveHandle` XRPC method is located.
|
|
25
|
+
*
|
|
26
|
+
* @default 'https://bsky.social'
|
|
27
|
+
*/
|
|
28
|
+
url?: URL | string;
|
|
29
|
+
};
|
|
30
|
+
export declare class AtprotoLexiconHandleResolver implements HandleResolver {
|
|
31
|
+
protected readonly url: URL;
|
|
32
|
+
protected readonly fetch: Fetch;
|
|
33
|
+
constructor({ url, fetch, }?: AtprotoLexiconHandleResolverOptions);
|
|
34
|
+
resolve(handle: string, options?: HandleResolveOptions): Promise<ResolvedHandle>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=atproto-lexicon-handle-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atproto-lexicon-handle-resolver.d.ts","sourceRoot":"","sources":["../src/atproto-lexicon-handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,CAAC,MAAM,KAAK,CAAA;AAEnB,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EAEf,MAAM,sBAAsB,CAAA;AAE7B,eAAO,MAAM,eAAe;;;;;;;;;EAG1B,CAAA;AAEF,MAAM,MAAM,mCAAmC,GAAG;IAChD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,KAAK,CAAA;IAEb;;;;;OAKG;IACH,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,CAAA;CACnB,CAAA;AAED,qBAAa,4BAA6B,YAAW,cAAc;IACjE,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IAC3B,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;gBAEnB,EACV,GAA4B,EAC5B,KAAwB,GACzB,GAAE,mCAAwC;IAK9B,OAAO,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,cAAc,CAAC;CAuC3B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AtprotoLexiconHandleResolver = exports.xrpcErrorSchema = void 0;
|
|
7
|
+
const zod_1 = __importDefault(require("zod"));
|
|
8
|
+
const handle_resolver_js_1 = require("./handle-resolver.js");
|
|
9
|
+
exports.xrpcErrorSchema = zod_1.default.object({
|
|
10
|
+
error: zod_1.default.string(),
|
|
11
|
+
message: zod_1.default.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
class AtprotoLexiconHandleResolver {
|
|
14
|
+
constructor({ url = 'https://bsky.social/', fetch = globalThis.fetch, } = {}) {
|
|
15
|
+
Object.defineProperty(this, "url", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "fetch", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: void 0
|
|
26
|
+
});
|
|
27
|
+
this.url = new URL(url);
|
|
28
|
+
this.fetch = fetch;
|
|
29
|
+
}
|
|
30
|
+
async resolve(handle, options) {
|
|
31
|
+
const url = new URL('/xrpc/com.atproto.identity.resolveHandle', this.url);
|
|
32
|
+
url.searchParams.set('handle', handle);
|
|
33
|
+
const headers = new Headers();
|
|
34
|
+
if (options?.noCache)
|
|
35
|
+
headers.set('cache-control', 'no-cache');
|
|
36
|
+
const request = new Request(url, { headers, signal: options?.signal });
|
|
37
|
+
const response = await this.fetch.call(null, request);
|
|
38
|
+
const payload = await response.json();
|
|
39
|
+
// The response should either be
|
|
40
|
+
// - 400 Bad Request with { error: 'InvalidRequest', message: 'Unable to resolve handle' }
|
|
41
|
+
// - 200 OK with { did: NonNullable<ResolvedHandle> }
|
|
42
|
+
// Any other response is considered unexpected behavior an should throw an error.
|
|
43
|
+
if (response.status === 400) {
|
|
44
|
+
const data = exports.xrpcErrorSchema.parse(payload);
|
|
45
|
+
if (data.error === 'InvalidRequest' &&
|
|
46
|
+
data.message === 'Unable to resolve handle') {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error('Invalid response from resolveHandle method');
|
|
52
|
+
}
|
|
53
|
+
const value = payload?.did;
|
|
54
|
+
if (!value || !(0, handle_resolver_js_1.isResolvedHandle)(value)) {
|
|
55
|
+
throw new Error('Invalid DID returned from resolveHandle method');
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.AtprotoLexiconHandleResolver = AtprotoLexiconHandleResolver;
|
|
61
|
+
//# sourceMappingURL=atproto-lexicon-handle-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atproto-lexicon-handle-resolver.js","sourceRoot":"","sources":["../src/atproto-lexicon-handle-resolver.ts"],"names":[],"mappings":";;;;;;AACA,8CAAmB;AAEnB,6DAK6B;AAEhB,QAAA,eAAe,GAAG,aAAC,CAAC,MAAM,CAAC;IACtC,KAAK,EAAE,aAAC,CAAC,MAAM,EAAE;IACjB,OAAO,EAAE,aAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAA;AAoBF,MAAa,4BAA4B;IAIvC,YAAY,EACV,GAAG,GAAG,sBAAsB,EAC5B,KAAK,GAAG,UAAU,CAAC,KAAK,MACe,EAAE;QANxB;;;;;WAAQ;QACR;;;;;WAAY;QAM7B,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,MAAc,EACd,OAA8B;QAE9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,0CAA0C,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;QACzE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAEtC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;QAC7B,IAAI,OAAO,EAAE,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAA;QAE9D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAEtE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACrD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAErC,gCAAgC;QAChC,0FAA0F;QAC1F,qDAAqD;QACrD,iFAAiF;QAEjF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,uBAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAC3C,IACE,IAAI,CAAC,KAAK,KAAK,gBAAgB;gBAC/B,IAAI,CAAC,OAAO,KAAK,0BAA0B,EAC3C,CAAC;gBACD,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QAC/D,CAAC;QAED,MAAM,KAAK,GAAY,OAAO,EAAE,GAAG,CAAA;QAEnC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAA,qCAAgB,EAAC,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;QACnE,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;CACF;AAtDD,oEAsDC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CachedGetter, GenericStore } from '@atproto-labs/caching';
|
|
2
|
+
import { HandleResolveOptions, HandleResolver, ResolvedHandle } from './handle-resolver.js';
|
|
3
|
+
export type CachedHandleResolverOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* The resolver that will be used to resolve handles.
|
|
6
|
+
*/
|
|
7
|
+
resolver: HandleResolver;
|
|
8
|
+
/**
|
|
9
|
+
* A store that will be used to cache resolved values.
|
|
10
|
+
*/
|
|
11
|
+
cache?: GenericStore<string, ResolvedHandle>;
|
|
12
|
+
};
|
|
13
|
+
export declare class CachedHandleResolver extends CachedGetter<string, ResolvedHandle> implements HandleResolver {
|
|
14
|
+
constructor({ resolver, cache, }: CachedHandleResolverOptions);
|
|
15
|
+
resolve(handle: string, options?: HandleResolveOptions): Promise<ResolvedHandle>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=cached-handle-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-handle-resolver.d.ts","sourceRoot":"","sources":["../src/cached-handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAe,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EACf,MAAM,sBAAsB,CAAA;AAE7B,MAAM,MAAM,2BAA2B,GAAG;IACxC;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAA;IAExB;;OAEG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CAC7C,CAAA;AAED,qBAAa,oBACX,SAAQ,YAAY,CAAC,MAAM,EAAE,cAAc,CAC3C,YAAW,cAAc;gBAEb,EACV,QAAQ,EACR,KAGE,GACH,EAAE,2BAA2B;IAIxB,OAAO,CACX,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,cAAc,CAAC;CAG3B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CachedHandleResolver = void 0;
|
|
4
|
+
const caching_1 = require("@atproto-labs/caching");
|
|
5
|
+
class CachedHandleResolver extends caching_1.CachedGetter {
|
|
6
|
+
constructor({ resolver, cache = new caching_1.MemoryStore({
|
|
7
|
+
max: 1000,
|
|
8
|
+
ttl: 10 * 60e3,
|
|
9
|
+
}), }) {
|
|
10
|
+
super((handle, options) => resolver.resolve(handle, options), cache);
|
|
11
|
+
}
|
|
12
|
+
async resolve(handle, options) {
|
|
13
|
+
return this.get(handle, options);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.CachedHandleResolver = CachedHandleResolver;
|
|
17
|
+
//# sourceMappingURL=cached-handle-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-handle-resolver.js","sourceRoot":"","sources":["../src/cached-handle-resolver.ts"],"names":[],"mappings":";;;AAAA,mDAA+E;AAmB/E,MAAa,oBACX,SAAQ,sBAAoC;IAG5C,YAAY,EACV,QAAQ,EACR,KAAK,GAAG,IAAI,qBAAW,CAAyB;QAC9C,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,EAAE,GAAG,IAAI;KACf,CAAC,GAC0B;QAC5B,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;IACtE,CAAC;IAED,KAAK,CAAC,OAAO,CACX,MAAc,EACd,OAA8B;QAE9B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,CAAC;CACF;AApBD,oDAoBC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Did } from '@atproto-labs/did';
|
|
2
|
+
export type HandleResolveOptions = {
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
noCache?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type ResolvedHandle = null | Did;
|
|
7
|
+
export declare function isResolvedHandle<T = unknown>(value: T): value is T & ResolvedHandle;
|
|
8
|
+
export interface HandleResolver {
|
|
9
|
+
/**
|
|
10
|
+
* @returns the DID that corresponds to the given handle, or `null` if no DID
|
|
11
|
+
* is found. `null` should only be returned if no unexpected behavior occurred
|
|
12
|
+
* during the resolution process.
|
|
13
|
+
* @throws Error if the resolution method fails due to an unexpected error, or
|
|
14
|
+
* if the resolution is aborted ({@link HandleResolveOptions.signal}).
|
|
15
|
+
*/
|
|
16
|
+
resolve(handle: string, options?: HandleResolveOptions): Promise<ResolvedHandle>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=handle-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-resolver.d.ts","sourceRoot":"","sources":["../src/handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAS,MAAM,mBAAmB,CAAA;AAE9C,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AACD,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,GAAG,CAAA;AAEvC,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAC1C,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,CAAC,GAAG,cAAc,CAE7B;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,OAAO,CACL,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,cAAc,CAAC,CAAA;CAC3B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isResolvedHandle = void 0;
|
|
4
|
+
const did_1 = require("@atproto-labs/did");
|
|
5
|
+
function isResolvedHandle(value) {
|
|
6
|
+
return value === null || (typeof value === 'string' && (0, did_1.isDid)(value));
|
|
7
|
+
}
|
|
8
|
+
exports.isResolvedHandle = isResolvedHandle;
|
|
9
|
+
//# sourceMappingURL=handle-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-resolver.js","sourceRoot":"","sources":["../src/handle-resolver.ts"],"names":[],"mappings":";;;AAAA,2CAA8C;AAQ9C,SAAgB,gBAAgB,CAC9B,KAAQ;IAER,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,IAAA,WAAK,EAAC,KAAK,CAAC,CAAC,CAAA;AACtE,CAAC;AAJD,4CAIC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './handle-resolver.js';
|
|
2
|
+
export * from './universal-handle-resolver.js';
|
|
3
|
+
export { UniversalHandleResolver as default } from './universal-handle-resolver.js';
|
|
4
|
+
export * from './cached-handle-resolver.js';
|
|
5
|
+
export * from './atproto-lexicon-handle-resolver.js';
|
|
6
|
+
export * from './serial-handle-resolver.js';
|
|
7
|
+
export * from './well-known-handler-resolver.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AAGpC,cAAc,gCAAgC,CAAA;AAC9C,OAAO,EAAE,uBAAuB,IAAI,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAGnF,cAAc,6BAA6B,CAAA;AAC3C,cAAc,sCAAsC,CAAA;AACpD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,kCAAkC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.default = void 0;
|
|
18
|
+
__exportStar(require("./handle-resolver.js"), exports);
|
|
19
|
+
// Main export
|
|
20
|
+
__exportStar(require("./universal-handle-resolver.js"), exports);
|
|
21
|
+
var universal_handle_resolver_js_1 = require("./universal-handle-resolver.js");
|
|
22
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return universal_handle_resolver_js_1.UniversalHandleResolver; } });
|
|
23
|
+
// Utilities
|
|
24
|
+
__exportStar(require("./cached-handle-resolver.js"), exports);
|
|
25
|
+
__exportStar(require("./atproto-lexicon-handle-resolver.js"), exports);
|
|
26
|
+
__exportStar(require("./serial-handle-resolver.js"), exports);
|
|
27
|
+
__exportStar(require("./well-known-handler-resolver.js"), exports);
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,uDAAoC;AAEpC,cAAc;AACd,iEAA8C;AAC9C,+EAAmF;AAA1E,uHAAA,uBAAuB,OAAW;AAE3C,YAAY;AACZ,8DAA2C;AAC3C,uEAAoD;AACpD,8DAA2C;AAC3C,mEAAgD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HandleResolveOptions, HandleResolver, ResolvedHandle } from './handle-resolver.js';
|
|
2
|
+
export declare class SerialHandleResolver implements HandleResolver {
|
|
3
|
+
protected readonly resolvers: readonly HandleResolver[];
|
|
4
|
+
constructor(resolvers: readonly HandleResolver[]);
|
|
5
|
+
resolve(handle: string, options?: HandleResolveOptions): Promise<ResolvedHandle>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=serial-handle-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serial-handle-resolver.d.ts","sourceRoot":"","sources":["../src/serial-handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EACf,MAAM,sBAAsB,CAAA;AAE7B,qBAAa,oBAAqB,YAAW,cAAc;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE;gBAApC,SAAS,EAAE,SAAS,cAAc,EAAE;IAMtD,OAAO,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,cAAc,CAAC;CAY3B"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SerialHandleResolver = void 0;
|
|
4
|
+
class SerialHandleResolver {
|
|
5
|
+
constructor(resolvers) {
|
|
6
|
+
Object.defineProperty(this, "resolvers", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
writable: true,
|
|
10
|
+
value: resolvers
|
|
11
|
+
});
|
|
12
|
+
if (!resolvers.length) {
|
|
13
|
+
throw new TypeError('At least one resolver is required');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async resolve(handle, options) {
|
|
17
|
+
for (const resolver of this.resolvers) {
|
|
18
|
+
options?.signal?.throwIfAborted();
|
|
19
|
+
const value = await resolver.resolve(handle, options);
|
|
20
|
+
if (value)
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
// If no resolver was able to resolve the handle, assume there is no DID
|
|
24
|
+
// corresponding to the handle.
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.SerialHandleResolver = SerialHandleResolver;
|
|
29
|
+
//# sourceMappingURL=serial-handle-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serial-handle-resolver.js","sourceRoot":"","sources":["../src/serial-handle-resolver.ts"],"names":[],"mappings":";;;AAMA,MAAa,oBAAoB;IAC/B,YAA+B,SAAoC;QAAvD;;;;mBAAmB,SAAS;WAA2B;QACjE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,MAAc,EACd,OAA8B;QAE9B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;YAEjC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YACrD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAA;QACzB,CAAC;QAED,wEAAwE;QACxE,+BAA+B;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC;CACF;AAtBD,oDAsBC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GenericStore } from '@atproto-labs/caching';
|
|
2
|
+
import { Fetch } from '@atproto-labs/fetch';
|
|
3
|
+
import { CachedHandleResolver } from './cached-handle-resolver.js';
|
|
4
|
+
import { HandleResolver, ResolvedHandle } from './handle-resolver.js';
|
|
5
|
+
import { AtprotoLexiconHandleResolverOptions } from './atproto-lexicon-handle-resolver.js';
|
|
6
|
+
export type HandleResolverCache = GenericStore<string, ResolvedHandle>;
|
|
7
|
+
export type UniversalHandleResolverOptions = {
|
|
8
|
+
cache?: HandleResolverCache;
|
|
9
|
+
/**
|
|
10
|
+
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
11
|
+
* behavior, e.g. adding headers, setting a timeout, mocking, etc.
|
|
12
|
+
*
|
|
13
|
+
* When using this library from a Node.js environment, you may want to use
|
|
14
|
+
* `safeFetchWrap()` from `@atproto-labs/fetch-node` to add SSRF protection.
|
|
15
|
+
*
|
|
16
|
+
* @default `globalThis.fetch`
|
|
17
|
+
*/
|
|
18
|
+
fetch?: Fetch;
|
|
19
|
+
/**
|
|
20
|
+
* @see {@link AtprotoLexiconHandleResolverOptions.url}
|
|
21
|
+
*/
|
|
22
|
+
atprotoLexiconUrl?: AtprotoLexiconHandleResolverOptions['url'];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* A handle resolver that works in any environment that supports `fetch()`. This
|
|
26
|
+
* relies on the a public XRPC implementing "com.atproto.identity.resolveHandle"
|
|
27
|
+
* to resolve handles.
|
|
28
|
+
*/
|
|
29
|
+
export declare class UniversalHandleResolver extends CachedHandleResolver implements HandleResolver {
|
|
30
|
+
constructor({ fetch, cache, atprotoLexiconUrl, }?: UniversalHandleResolverOptions);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=universal-handle-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"universal-handle-resolver.d.ts","sourceRoot":"","sources":["../src/universal-handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAE3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrE,OAAO,EAEL,mCAAmC,EACpC,MAAM,sCAAsC,CAAA;AAI7C,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;AAEtE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,KAAK,CAAC,EAAE,mBAAmB,CAAA;IAE3B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,KAAK,CAAA;IAEb;;OAEG;IACH,iBAAiB,CAAC,EAAE,mCAAmC,CAAC,KAAK,CAAC,CAAA;CAC/D,CAAA;AAED;;;;GAIG;AACH,qBAAa,uBACX,SAAQ,oBACR,YAAW,cAAc;gBAEb,EACV,KAAwB,EACxB,KAAK,EACL,iBAAiB,GAClB,GAAE,8BAAmC;CAUvC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UniversalHandleResolver = void 0;
|
|
4
|
+
const cached_handle_resolver_js_1 = require("./cached-handle-resolver.js");
|
|
5
|
+
const atproto_lexicon_handle_resolver_js_1 = require("./atproto-lexicon-handle-resolver.js");
|
|
6
|
+
const serial_handle_resolver_js_1 = require("./serial-handle-resolver.js");
|
|
7
|
+
const well_known_handler_resolver_js_1 = require("./well-known-handler-resolver.js");
|
|
8
|
+
/**
|
|
9
|
+
* A handle resolver that works in any environment that supports `fetch()`. This
|
|
10
|
+
* relies on the a public XRPC implementing "com.atproto.identity.resolveHandle"
|
|
11
|
+
* to resolve handles.
|
|
12
|
+
*/
|
|
13
|
+
class UniversalHandleResolver extends cached_handle_resolver_js_1.CachedHandleResolver {
|
|
14
|
+
constructor({ fetch = globalThis.fetch, cache, atprotoLexiconUrl, } = {}) {
|
|
15
|
+
const resolver = new serial_handle_resolver_js_1.SerialHandleResolver([
|
|
16
|
+
// Try the well-known method first, allowing to reduce the load on the
|
|
17
|
+
// XRPC.
|
|
18
|
+
new well_known_handler_resolver_js_1.WellKnownHandleResolver({ fetch }),
|
|
19
|
+
new atproto_lexicon_handle_resolver_js_1.AtprotoLexiconHandleResolver({ fetch, url: atprotoLexiconUrl }),
|
|
20
|
+
]);
|
|
21
|
+
super({ resolver, cache });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.UniversalHandleResolver = UniversalHandleResolver;
|
|
25
|
+
//# sourceMappingURL=universal-handle-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"universal-handle-resolver.js","sourceRoot":"","sources":["../src/universal-handle-resolver.ts"],"names":[],"mappings":";;;AAGA,2EAAkE;AAElE,6FAG6C;AAC7C,2EAAkE;AAClE,qFAA0E;AAwB1E;;;;GAIG;AACH,MAAa,uBACX,SAAQ,gDAAoB;IAG5B,YAAY,EACV,KAAK,GAAG,UAAU,CAAC,KAAK,EACxB,KAAK,EACL,iBAAiB,MACiB,EAAE;QACpC,MAAM,QAAQ,GAAG,IAAI,gDAAoB,CAAC;YACxC,sEAAsE;YACtE,QAAQ;YACR,IAAI,wDAAuB,CAAC,EAAE,KAAK,EAAE,CAAC;YACtC,IAAI,iEAA4B,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC;SACpE,CAAC,CAAA;QAEF,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;IAC5B,CAAC;CACF;AAlBD,0DAkBC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Fetch } from '@atproto-labs/fetch';
|
|
2
|
+
import { HandleResolveOptions, HandleResolver, ResolvedHandle } from './handle-resolver.js';
|
|
3
|
+
export type WellKnownHandleResolverOptions = {
|
|
4
|
+
fetch?: Fetch;
|
|
5
|
+
};
|
|
6
|
+
export declare class WellKnownHandleResolver implements HandleResolver {
|
|
7
|
+
protected readonly fetch: Fetch;
|
|
8
|
+
constructor({ fetch, }?: WellKnownHandleResolverOptions);
|
|
9
|
+
resolve(handle: string, options?: HandleResolveOptions): Promise<ResolvedHandle>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=well-known-handler-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"well-known-handler-resolver.d.ts","sourceRoot":"","sources":["../src/well-known-handler-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAE3C,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EAEf,MAAM,sBAAsB,CAAA;AAE7B,MAAM,MAAM,8BAA8B,GAAG;IAC3C,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,qBAAa,uBAAwB,YAAW,cAAc;IAC5D,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;gBAEnB,EACV,KAAwB,GACzB,GAAE,8BAAmC;IAIzB,OAAO,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,cAAc,CAAC;CAyB3B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WellKnownHandleResolver = void 0;
|
|
4
|
+
const handle_resolver_js_1 = require("./handle-resolver.js");
|
|
5
|
+
class WellKnownHandleResolver {
|
|
6
|
+
constructor({ fetch = globalThis.fetch, } = {}) {
|
|
7
|
+
Object.defineProperty(this, "fetch", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true,
|
|
11
|
+
value: void 0
|
|
12
|
+
});
|
|
13
|
+
this.fetch = fetch;
|
|
14
|
+
}
|
|
15
|
+
async resolve(handle, options) {
|
|
16
|
+
const url = new URL('/.well-known/atproto-did', `https://${handle}`);
|
|
17
|
+
const headers = new Headers();
|
|
18
|
+
if (options?.noCache)
|
|
19
|
+
headers.set('cache-control', 'no-cache');
|
|
20
|
+
const request = new Request(url, { headers, signal: options?.signal });
|
|
21
|
+
try {
|
|
22
|
+
const response = await (0, this.fetch)(request);
|
|
23
|
+
const text = await response.text();
|
|
24
|
+
const firstLine = text.split('\n')[0].trim();
|
|
25
|
+
if ((0, handle_resolver_js_1.isResolvedHandle)(firstLine))
|
|
26
|
+
return firstLine;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
// The the request failed, assume the handle does not resolve to a DID,
|
|
31
|
+
// unless the failure was due to the signal being aborted.
|
|
32
|
+
options?.signal?.throwIfAborted();
|
|
33
|
+
// TODO: propagate some errors as-is (?)
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.WellKnownHandleResolver = WellKnownHandleResolver;
|
|
39
|
+
//# sourceMappingURL=well-known-handler-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"well-known-handler-resolver.js","sourceRoot":"","sources":["../src/well-known-handler-resolver.ts"],"names":[],"mappings":";;;AAEA,6DAK6B;AAM7B,MAAa,uBAAuB;IAGlC,YAAY,EACV,KAAK,GAAG,UAAU,CAAC,KAAK,MACU,EAAE;QAJnB;;;;;WAAY;QAK7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,MAAc,EACd,OAA8B;QAE9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,0BAA0B,EAAE,WAAW,MAAM,EAAE,CAAC,CAAA;QAEpE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;QAC7B,IAAI,OAAO,EAAE,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAA;QAC9D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAEtE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAA;YAC/C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAClC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;YAE7C,IAAI,IAAA,qCAAgB,EAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAA;YAEjD,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,uEAAuE;YACvE,0DAA0D;YAC1D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;YAEjC,wCAAwC;YAExC,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;CACF;AArCD,0DAqCC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atproto-labs/handle-resolver",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Isomorphic ATProto handle to DID resolver",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"atproto",
|
|
8
|
+
"oauth",
|
|
9
|
+
"handle",
|
|
10
|
+
"identity",
|
|
11
|
+
"browser",
|
|
12
|
+
"node",
|
|
13
|
+
"isomorphic"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://atproto.com",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/bluesky-social/atproto",
|
|
19
|
+
"directory": "packages/handle-resolver"
|
|
20
|
+
},
|
|
21
|
+
"type": "commonjs",
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"lru-cache": "^10.2.0",
|
|
32
|
+
"zod": "^3.22.4",
|
|
33
|
+
"@atproto-labs/caching": "0.0.1",
|
|
34
|
+
"@atproto-labs/did": "0.0.1",
|
|
35
|
+
"@atproto-labs/fetch": "0.0.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"typescript": "^5.3.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc --build tsconfig.build.json"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Fetch } from '@atproto-labs/fetch'
|
|
2
|
+
import z from 'zod'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
HandleResolveOptions,
|
|
6
|
+
HandleResolver,
|
|
7
|
+
ResolvedHandle,
|
|
8
|
+
isResolvedHandle,
|
|
9
|
+
} from './handle-resolver.js'
|
|
10
|
+
|
|
11
|
+
export const xrpcErrorSchema = z.object({
|
|
12
|
+
error: z.string(),
|
|
13
|
+
message: z.string().optional(),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export type AtprotoLexiconHandleResolverOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
19
|
+
* behavior, e.g. adding headers, setting a timeout, mocking, etc.
|
|
20
|
+
*
|
|
21
|
+
* @default globalThis.fetch
|
|
22
|
+
*/
|
|
23
|
+
fetch?: Fetch
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* URL of the atproto lexicon server. This is the base URL where the
|
|
27
|
+
* `com.atproto.identity.resolveHandle` XRPC method is located.
|
|
28
|
+
*
|
|
29
|
+
* @default 'https://bsky.social'
|
|
30
|
+
*/
|
|
31
|
+
url?: URL | string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class AtprotoLexiconHandleResolver implements HandleResolver {
|
|
35
|
+
protected readonly url: URL
|
|
36
|
+
protected readonly fetch: Fetch
|
|
37
|
+
|
|
38
|
+
constructor({
|
|
39
|
+
url = 'https://bsky.social/',
|
|
40
|
+
fetch = globalThis.fetch,
|
|
41
|
+
}: AtprotoLexiconHandleResolverOptions = {}) {
|
|
42
|
+
this.url = new URL(url)
|
|
43
|
+
this.fetch = fetch
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public async resolve(
|
|
47
|
+
handle: string,
|
|
48
|
+
options?: HandleResolveOptions,
|
|
49
|
+
): Promise<ResolvedHandle> {
|
|
50
|
+
const url = new URL('/xrpc/com.atproto.identity.resolveHandle', this.url)
|
|
51
|
+
url.searchParams.set('handle', handle)
|
|
52
|
+
|
|
53
|
+
const headers = new Headers()
|
|
54
|
+
if (options?.noCache) headers.set('cache-control', 'no-cache')
|
|
55
|
+
|
|
56
|
+
const request = new Request(url, { headers, signal: options?.signal })
|
|
57
|
+
|
|
58
|
+
const response = await this.fetch.call(null, request)
|
|
59
|
+
const payload = await response.json()
|
|
60
|
+
|
|
61
|
+
// The response should either be
|
|
62
|
+
// - 400 Bad Request with { error: 'InvalidRequest', message: 'Unable to resolve handle' }
|
|
63
|
+
// - 200 OK with { did: NonNullable<ResolvedHandle> }
|
|
64
|
+
// Any other response is considered unexpected behavior an should throw an error.
|
|
65
|
+
|
|
66
|
+
if (response.status === 400) {
|
|
67
|
+
const data = xrpcErrorSchema.parse(payload)
|
|
68
|
+
if (
|
|
69
|
+
data.error === 'InvalidRequest' &&
|
|
70
|
+
data.message === 'Unable to resolve handle'
|
|
71
|
+
) {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
throw new Error('Invalid response from resolveHandle method')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const value: unknown = payload?.did
|
|
81
|
+
|
|
82
|
+
if (!value || !isResolvedHandle(value)) {
|
|
83
|
+
throw new Error('Invalid DID returned from resolveHandle method')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return value
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CachedGetter, GenericStore, MemoryStore } from '@atproto-labs/caching'
|
|
2
|
+
import {
|
|
3
|
+
HandleResolveOptions,
|
|
4
|
+
HandleResolver,
|
|
5
|
+
ResolvedHandle,
|
|
6
|
+
} from './handle-resolver.js'
|
|
7
|
+
|
|
8
|
+
export type CachedHandleResolverOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* The resolver that will be used to resolve handles.
|
|
11
|
+
*/
|
|
12
|
+
resolver: HandleResolver
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A store that will be used to cache resolved values.
|
|
16
|
+
*/
|
|
17
|
+
cache?: GenericStore<string, ResolvedHandle>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class CachedHandleResolver
|
|
21
|
+
extends CachedGetter<string, ResolvedHandle>
|
|
22
|
+
implements HandleResolver
|
|
23
|
+
{
|
|
24
|
+
constructor({
|
|
25
|
+
resolver,
|
|
26
|
+
cache = new MemoryStore<string, ResolvedHandle>({
|
|
27
|
+
max: 1000,
|
|
28
|
+
ttl: 10 * 60e3,
|
|
29
|
+
}),
|
|
30
|
+
}: CachedHandleResolverOptions) {
|
|
31
|
+
super((handle, options) => resolver.resolve(handle, options), cache)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async resolve(
|
|
35
|
+
handle: string,
|
|
36
|
+
options?: HandleResolveOptions,
|
|
37
|
+
): Promise<ResolvedHandle> {
|
|
38
|
+
return this.get(handle, options)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Did, isDid } from '@atproto-labs/did'
|
|
2
|
+
|
|
3
|
+
export type HandleResolveOptions = {
|
|
4
|
+
signal?: AbortSignal
|
|
5
|
+
noCache?: boolean
|
|
6
|
+
}
|
|
7
|
+
export type ResolvedHandle = null | Did
|
|
8
|
+
|
|
9
|
+
export function isResolvedHandle<T = unknown>(
|
|
10
|
+
value: T,
|
|
11
|
+
): value is T & ResolvedHandle {
|
|
12
|
+
return value === null || (typeof value === 'string' && isDid(value))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface HandleResolver {
|
|
16
|
+
/**
|
|
17
|
+
* @returns the DID that corresponds to the given handle, or `null` if no DID
|
|
18
|
+
* is found. `null` should only be returned if no unexpected behavior occurred
|
|
19
|
+
* during the resolution process.
|
|
20
|
+
* @throws Error if the resolution method fails due to an unexpected error, or
|
|
21
|
+
* if the resolution is aborted ({@link HandleResolveOptions.signal}).
|
|
22
|
+
*/
|
|
23
|
+
resolve(
|
|
24
|
+
handle: string,
|
|
25
|
+
options?: HandleResolveOptions,
|
|
26
|
+
): Promise<ResolvedHandle>
|
|
27
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './handle-resolver.js'
|
|
2
|
+
|
|
3
|
+
// Main export
|
|
4
|
+
export * from './universal-handle-resolver.js'
|
|
5
|
+
export { UniversalHandleResolver as default } from './universal-handle-resolver.js'
|
|
6
|
+
|
|
7
|
+
// Utilities
|
|
8
|
+
export * from './cached-handle-resolver.js'
|
|
9
|
+
export * from './atproto-lexicon-handle-resolver.js'
|
|
10
|
+
export * from './serial-handle-resolver.js'
|
|
11
|
+
export * from './well-known-handler-resolver.js'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HandleResolveOptions,
|
|
3
|
+
HandleResolver,
|
|
4
|
+
ResolvedHandle,
|
|
5
|
+
} from './handle-resolver.js'
|
|
6
|
+
|
|
7
|
+
export class SerialHandleResolver implements HandleResolver {
|
|
8
|
+
constructor(protected readonly resolvers: readonly HandleResolver[]) {
|
|
9
|
+
if (!resolvers.length) {
|
|
10
|
+
throw new TypeError('At least one resolver is required')
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async resolve(
|
|
15
|
+
handle: string,
|
|
16
|
+
options?: HandleResolveOptions,
|
|
17
|
+
): Promise<ResolvedHandle> {
|
|
18
|
+
for (const resolver of this.resolvers) {
|
|
19
|
+
options?.signal?.throwIfAborted()
|
|
20
|
+
|
|
21
|
+
const value = await resolver.resolve(handle, options)
|
|
22
|
+
if (value) return value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// If no resolver was able to resolve the handle, assume there is no DID
|
|
26
|
+
// corresponding to the handle.
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { GenericStore } from '@atproto-labs/caching'
|
|
2
|
+
import { Fetch } from '@atproto-labs/fetch'
|
|
3
|
+
|
|
4
|
+
import { CachedHandleResolver } from './cached-handle-resolver.js'
|
|
5
|
+
import { HandleResolver, ResolvedHandle } from './handle-resolver.js'
|
|
6
|
+
import {
|
|
7
|
+
AtprotoLexiconHandleResolver,
|
|
8
|
+
AtprotoLexiconHandleResolverOptions,
|
|
9
|
+
} from './atproto-lexicon-handle-resolver.js'
|
|
10
|
+
import { SerialHandleResolver } from './serial-handle-resolver.js'
|
|
11
|
+
import { WellKnownHandleResolver } from './well-known-handler-resolver.js'
|
|
12
|
+
|
|
13
|
+
export type HandleResolverCache = GenericStore<string, ResolvedHandle>
|
|
14
|
+
|
|
15
|
+
export type UniversalHandleResolverOptions = {
|
|
16
|
+
cache?: HandleResolverCache
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fetch function to use for HTTP requests. Allows customizing the request
|
|
20
|
+
* behavior, e.g. adding headers, setting a timeout, mocking, etc.
|
|
21
|
+
*
|
|
22
|
+
* When using this library from a Node.js environment, you may want to use
|
|
23
|
+
* `safeFetchWrap()` from `@atproto-labs/fetch-node` to add SSRF protection.
|
|
24
|
+
*
|
|
25
|
+
* @default `globalThis.fetch`
|
|
26
|
+
*/
|
|
27
|
+
fetch?: Fetch
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @see {@link AtprotoLexiconHandleResolverOptions.url}
|
|
31
|
+
*/
|
|
32
|
+
atprotoLexiconUrl?: AtprotoLexiconHandleResolverOptions['url']
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A handle resolver that works in any environment that supports `fetch()`. This
|
|
37
|
+
* relies on the a public XRPC implementing "com.atproto.identity.resolveHandle"
|
|
38
|
+
* to resolve handles.
|
|
39
|
+
*/
|
|
40
|
+
export class UniversalHandleResolver
|
|
41
|
+
extends CachedHandleResolver
|
|
42
|
+
implements HandleResolver
|
|
43
|
+
{
|
|
44
|
+
constructor({
|
|
45
|
+
fetch = globalThis.fetch,
|
|
46
|
+
cache,
|
|
47
|
+
atprotoLexiconUrl,
|
|
48
|
+
}: UniversalHandleResolverOptions = {}) {
|
|
49
|
+
const resolver = new SerialHandleResolver([
|
|
50
|
+
// Try the well-known method first, allowing to reduce the load on the
|
|
51
|
+
// XRPC.
|
|
52
|
+
new WellKnownHandleResolver({ fetch }),
|
|
53
|
+
new AtprotoLexiconHandleResolver({ fetch, url: atprotoLexiconUrl }),
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
super({ resolver, cache })
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Fetch } from '@atproto-labs/fetch'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
HandleResolveOptions,
|
|
5
|
+
HandleResolver,
|
|
6
|
+
ResolvedHandle,
|
|
7
|
+
isResolvedHandle,
|
|
8
|
+
} from './handle-resolver.js'
|
|
9
|
+
|
|
10
|
+
export type WellKnownHandleResolverOptions = {
|
|
11
|
+
fetch?: Fetch
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class WellKnownHandleResolver implements HandleResolver {
|
|
15
|
+
protected readonly fetch: Fetch
|
|
16
|
+
|
|
17
|
+
constructor({
|
|
18
|
+
fetch = globalThis.fetch,
|
|
19
|
+
}: WellKnownHandleResolverOptions = {}) {
|
|
20
|
+
this.fetch = fetch
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async resolve(
|
|
24
|
+
handle: string,
|
|
25
|
+
options?: HandleResolveOptions,
|
|
26
|
+
): Promise<ResolvedHandle> {
|
|
27
|
+
const url = new URL('/.well-known/atproto-did', `https://${handle}`)
|
|
28
|
+
|
|
29
|
+
const headers = new Headers()
|
|
30
|
+
if (options?.noCache) headers.set('cache-control', 'no-cache')
|
|
31
|
+
const request = new Request(url, { headers, signal: options?.signal })
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const response = await (0, this.fetch)(request)
|
|
35
|
+
const text = await response.text()
|
|
36
|
+
const firstLine = text.split('\n')[0]!.trim()
|
|
37
|
+
|
|
38
|
+
if (isResolvedHandle(firstLine)) return firstLine
|
|
39
|
+
|
|
40
|
+
return null
|
|
41
|
+
} catch (err) {
|
|
42
|
+
// The the request failed, assume the handle does not resolve to a DID,
|
|
43
|
+
// unless the failure was due to the signal being aborted.
|
|
44
|
+
options?.signal?.throwIfAborted()
|
|
45
|
+
|
|
46
|
+
// TODO: propagate some errors as-is (?)
|
|
47
|
+
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/tsconfig.json
ADDED