@atproto-labs/handle-resolver-node 0.0.1 → 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/CHANGELOG.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # @atproto-labs/handle-resolver-node
2
2
 
3
- ## 0.0.1
3
+ ## 0.1.0
4
4
 
5
- ### Patch Changes
5
+ ### Minor Changes
6
+
7
+ - [#2482](https://github.com/bluesky-social/atproto/pull/2482) [`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add OAuth provider capability & support for DPoP signed tokens
6
8
 
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
9
+ ### Patch Changes
8
10
 
9
- - Updated dependencies [[`e134c79a0`](https://github.com/bluesky-social/atproto/commit/e134c79a0ffb000b2cb36437815673fa6bda664b)]:
10
- - @atproto-labs/handle-resolver@0.0.1
11
- - @atproto-labs/fetch-node@0.0.1
12
- - @atproto-labs/fetch@0.0.1
13
- - @atproto-labs/did@0.0.1
11
+ - Updated dependencies [[`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646)]:
12
+ - @atproto-labs/handle-resolver@0.1.0
13
+ - @atproto-labs/fetch-node@0.1.0
14
+ - @atproto/did@0.1.0
@@ -0,0 +1,22 @@
1
+ import { Fetch } from '@atproto-labs/fetch-node';
2
+ import { AtprotoHandleResolver, HandleResolver } from '@atproto-labs/handle-resolver';
3
+ export type AtprotoHandleResolverNodeOptions = {
4
+ /**
5
+ * List of backup nameservers to use in case the primary ones fail. Will
6
+ * default to no fallback nameservers.
7
+ */
8
+ fallbackNameservers?: string[];
9
+ /**
10
+ * Fetch function to use for HTTP requests. Allows customizing the request
11
+ * behavior, e.g. adding headers, setting a timeout, mocking, etc. The
12
+ * provided fetch function will be wrapped with a safeFetchWrap function that
13
+ * adds SSRF protection.
14
+ *
15
+ * @default `globalThis.fetch`
16
+ */
17
+ fetch?: Fetch;
18
+ };
19
+ export declare class AtprotoHandleResolverNode extends AtprotoHandleResolver implements HandleResolver {
20
+ constructor({ fetch, fallbackNameservers, }?: AtprotoHandleResolverNodeOptions);
21
+ }
22
+ //# sourceMappingURL=atproto-handle-resolver-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atproto-handle-resolver-node.d.ts","sourceRoot":"","sources":["../src/atproto-handle-resolver-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAiB,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EACL,qBAAqB,EACrB,cAAc,EACf,MAAM,+BAA+B,CAAA;AAOtC,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAA;IAE9B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,qBAAa,yBACX,SAAQ,qBACR,YAAW,cAAc;gBAEb,EACV,KAAwB,EACxB,mBAAmB,GACpB,GAAE,gCAAqC;CAczC"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AtprotoHandleResolverNode = void 0;
4
+ const fetch_node_1 = require("@atproto-labs/fetch-node");
5
+ const handle_resolver_1 = require("@atproto-labs/handle-resolver");
6
+ const node_resolve_txt_factory_js_1 = require("./node-resolve-txt-factory.js");
7
+ class AtprotoHandleResolverNode extends handle_resolver_1.AtprotoHandleResolver {
8
+ constructor({ fetch = globalThis.fetch, fallbackNameservers, } = {}) {
9
+ super({
10
+ fetch: (0, fetch_node_1.safeFetchWrap)({
11
+ fetch,
12
+ timeout: 3000, // 3 seconds
13
+ ssrfProtection: true,
14
+ responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
15
+ }),
16
+ resolveTxt: node_resolve_txt_factory_js_1.nodeResolveTxtDefault,
17
+ resolveTxtFallback: fallbackNameservers?.length
18
+ ? (0, node_resolve_txt_factory_js_1.nodeResolveTxtFactory)(fallbackNameservers)
19
+ : undefined,
20
+ });
21
+ }
22
+ }
23
+ exports.AtprotoHandleResolverNode = AtprotoHandleResolverNode;
24
+ //# sourceMappingURL=atproto-handle-resolver-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atproto-handle-resolver-node.js","sourceRoot":"","sources":["../src/atproto-handle-resolver-node.ts"],"names":[],"mappings":";;;AAAA,yDAA+D;AAC/D,mEAGsC;AAEtC,+EAGsC;AAoBtC,MAAa,yBACX,SAAQ,uCAAqB;IAG7B,YAAY,EACV,KAAK,GAAG,UAAU,CAAC,KAAK,EACxB,mBAAmB,MACiB,EAAE;QACtC,KAAK,CAAC;YACJ,KAAK,EAAE,IAAA,0BAAa,EAAC;gBACnB,KAAK;gBACL,OAAO,EAAE,IAAI,EAAE,YAAY;gBAC3B,cAAc,EAAE,IAAI;gBACpB,eAAe,EAAE,EAAE,GAAG,IAAI,EAAE,+CAA+C;aAC5E,CAAC;YACF,UAAU,EAAE,mDAAqB;YACjC,kBAAkB,EAAE,mBAAmB,EAAE,MAAM;gBAC7C,CAAC,CAAC,IAAA,mDAAqB,EAAC,mBAAmB,CAAC;gBAC5C,CAAC,CAAC,SAAS;SACd,CAAC,CAAA;IACJ,CAAC;CACF;AArBD,8DAqBC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './node-handle-resolver.js';
2
- export { NodeHandleResolver as default } from './node-handle-resolver.js';
3
- export * from './dns-handle-resolver.js';
1
+ export * from './atproto-handle-resolver-node.js';
2
+ export * from './node-resolve-txt-factory.js';
3
+ export { AtprotoHandleResolverNode as default } from './atproto-handle-resolver-node.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAA;AACzC,OAAO,EAAE,kBAAkB,IAAI,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGzE,cAAc,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,OAAO,EAAE,yBAAyB,IAAI,OAAO,EAAE,MAAM,mCAAmC,CAAA"}
package/dist/index.js CHANGED
@@ -16,9 +16,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.default = void 0;
18
18
  // Main export
19
- __exportStar(require("./node-handle-resolver.js"), exports);
20
- var node_handle_resolver_js_1 = require("./node-handle-resolver.js");
21
- Object.defineProperty(exports, "default", { enumerable: true, get: function () { return node_handle_resolver_js_1.NodeHandleResolver; } });
22
- // Utilities
23
- __exportStar(require("./dns-handle-resolver.js"), exports);
19
+ __exportStar(require("./atproto-handle-resolver-node.js"), exports);
20
+ __exportStar(require("./node-resolve-txt-factory.js"), exports);
21
+ var atproto_handle_resolver_node_js_1 = require("./atproto-handle-resolver-node.js");
22
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return atproto_handle_resolver_node_js_1.AtprotoHandleResolverNode; } });
24
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,4DAAyC;AACzC,qEAAyE;AAAhE,kHAAA,kBAAkB,OAAW;AAEtC,YAAY;AACZ,2DAAwC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,cAAc;AACd,oEAAiD;AACjD,gEAA6C;AAC7C,qFAAwF;AAA/E,0HAAA,yBAAyB,OAAW"}
@@ -0,0 +1,4 @@
1
+ import { ResolveTxt } from '@atproto-labs/handle-resolver';
2
+ export declare const nodeResolveTxtDefault: ResolveTxt;
3
+ export declare function nodeResolveTxtFactory(nameservers: string[]): ResolveTxt;
4
+ //# sourceMappingURL=node-resolve-txt-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-resolve-txt-factory.d.ts","sourceRoot":"","sources":["../src/node-resolve-txt-factory.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAE1D,eAAO,MAAM,qBAAqB,EAAE,UACiB,CAAA;AAErD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,UAAU,CA0CvE"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nodeResolveTxtFactory = exports.nodeResolveTxtDefault = void 0;
4
+ const promises_1 = require("node:dns/promises");
5
+ const node_net_1 = require("node:net");
6
+ const nodeResolveTxtDefault = (hostname) => (0, promises_1.resolveTxt)(hostname).then(groupChunks, handleError);
7
+ exports.nodeResolveTxtDefault = nodeResolveTxtDefault;
8
+ function nodeResolveTxtFactory(nameservers) {
9
+ // Optimization
10
+ if (!nameservers.length)
11
+ return async () => null;
12
+ // Build the resolver asynchronously (will be awaited on every use)
13
+ const resolverPromise = Promise.all(nameservers.map((nameserver) => {
14
+ const [domain, port = null] = nameserver.split(':', 2);
15
+ if (port !== null && !/^\d+$/.test(port)) {
16
+ throw new TypeError(`Invalid name server "${nameserver}"`);
17
+ }
18
+ return (0, node_net_1.isIP)(domain) === 4 || isBracedIPv6(domain)
19
+ ? [nameserver] // No need to lookup
20
+ : (0, promises_1.lookup)(domain, { all: true }).then((r) => r.map((a) => appendPort(a.address, port)),
21
+ // Let's just ignore failed nameservers resolution
22
+ (_err) => []);
23
+ })).then((results) => {
24
+ const backupIps = results.flat(1);
25
+ // No resolver if no valid IP
26
+ if (!backupIps.length)
27
+ return null;
28
+ const resolver = new promises_1.Resolver();
29
+ resolver.setServers(backupIps);
30
+ return resolver;
31
+ });
32
+ // Avoid uncaught promise rejection
33
+ void resolverPromise.catch(() => {
34
+ // Should never happen though...
35
+ });
36
+ return async (hostname) => {
37
+ const resolver = await resolverPromise;
38
+ return resolver
39
+ ? resolver.resolveTxt(hostname).then(groupChunks, handleError)
40
+ : null;
41
+ };
42
+ }
43
+ exports.nodeResolveTxtFactory = nodeResolveTxtFactory;
44
+ function isBracedIPv6(address) {
45
+ return (address.startsWith('[') &&
46
+ address.endsWith(']') &&
47
+ (0, node_net_1.isIP)(address.slice(1, -1)) === 6);
48
+ }
49
+ function groupChunks(results) {
50
+ return results.map((chunks) => chunks.join(''));
51
+ }
52
+ function handleError(err) {
53
+ // Invalid argument type (e.g. hostname is a number)
54
+ if (err instanceof TypeError)
55
+ throw err;
56
+ // If the hostname does not resolve, return null
57
+ if (err instanceof Error) {
58
+ if (err['code'] === 'ENOTFOUND')
59
+ return null;
60
+ // Hostname is not a valid domain name
61
+ if (err['code'] === 'EBADNAME')
62
+ throw err;
63
+ // DNS server unreachable
64
+ // if (err['code'] === 'ETIMEOUT') throw err
65
+ }
66
+ // Historically, errors were not thrown here. A "null" value indicates to the
67
+ // AtprotoHandleResolver that it should try the fallback resolver.
68
+ // @TODO We might want to re-visit this to only apply when an unexpected error
69
+ // occurs (by throwing here). For now, let's keep the same behavior as before.
70
+ // throw err
71
+ return null;
72
+ }
73
+ function appendPort(address, port) {
74
+ switch ((0, node_net_1.isIP)(address)) {
75
+ case 4:
76
+ return port ? `${address}:${port}` : address;
77
+ case 6:
78
+ return port ? `[${address}]:${port}` : `[${address}]`;
79
+ default:
80
+ throw new TypeError(`Invalid IP address "${address}"`);
81
+ }
82
+ }
83
+ //# sourceMappingURL=node-resolve-txt-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-resolve-txt-factory.js","sourceRoot":"","sources":["../src/node-resolve-txt-factory.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto-labs/handle-resolver-node",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "license": "MIT",
5
5
  "description": "Node specific ATProto handle to DID resolver",
6
6
  "keywords": [
@@ -14,7 +14,7 @@
14
14
  "repository": {
15
15
  "type": "git",
16
16
  "url": "https://github.com/bluesky-social/atproto",
17
- "directory": "packages/handle-resolver-node"
17
+ "directory": "packages/internal/handle-resolver-node"
18
18
  },
19
19
  "type": "commonjs",
20
20
  "main": "dist/index.js",
@@ -26,10 +26,9 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@atproto-labs/handle-resolver": "0.0.1",
30
- "@atproto-labs/fetch": "0.0.1",
31
- "@atproto-labs/fetch-node": "0.0.1",
32
- "@atproto-labs/did": "0.0.1"
29
+ "@atproto-labs/fetch-node": "0.1.0",
30
+ "@atproto-labs/handle-resolver": "0.1.0",
31
+ "@atproto/did": "0.1.0"
33
32
  },
34
33
  "devDependencies": {
35
34
  "typescript": "^5.3.3"
@@ -0,0 +1,51 @@
1
+ import { Fetch, safeFetchWrap } from '@atproto-labs/fetch-node'
2
+ import {
3
+ AtprotoHandleResolver,
4
+ HandleResolver,
5
+ } from '@atproto-labs/handle-resolver'
6
+
7
+ import {
8
+ nodeResolveTxtDefault,
9
+ nodeResolveTxtFactory,
10
+ } from './node-resolve-txt-factory.js'
11
+
12
+ export type AtprotoHandleResolverNodeOptions = {
13
+ /**
14
+ * List of backup nameservers to use in case the primary ones fail. Will
15
+ * default to no fallback nameservers.
16
+ */
17
+ fallbackNameservers?: string[]
18
+
19
+ /**
20
+ * Fetch function to use for HTTP requests. Allows customizing the request
21
+ * behavior, e.g. adding headers, setting a timeout, mocking, etc. The
22
+ * provided fetch function will be wrapped with a safeFetchWrap function that
23
+ * adds SSRF protection.
24
+ *
25
+ * @default `globalThis.fetch`
26
+ */
27
+ fetch?: Fetch
28
+ }
29
+
30
+ export class AtprotoHandleResolverNode
31
+ extends AtprotoHandleResolver
32
+ implements HandleResolver
33
+ {
34
+ constructor({
35
+ fetch = globalThis.fetch,
36
+ fallbackNameservers,
37
+ }: AtprotoHandleResolverNodeOptions = {}) {
38
+ super({
39
+ fetch: safeFetchWrap({
40
+ fetch,
41
+ timeout: 3000, // 3 seconds
42
+ ssrfProtection: true,
43
+ responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
44
+ }),
45
+ resolveTxt: nodeResolveTxtDefault,
46
+ resolveTxtFallback: fallbackNameservers?.length
47
+ ? nodeResolveTxtFactory(fallbackNameservers)
48
+ : undefined,
49
+ })
50
+ }
51
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  // Main export
2
- export * from './node-handle-resolver.js'
3
- export { NodeHandleResolver as default } from './node-handle-resolver.js'
4
-
5
- // Utilities
6
- export * from './dns-handle-resolver.js'
2
+ export * from './atproto-handle-resolver-node.js'
3
+ export * from './node-resolve-txt-factory.js'
4
+ export { AtprotoHandleResolverNode as default } from './atproto-handle-resolver-node.js'
@@ -0,0 +1,100 @@
1
+ import { Resolver, lookup, resolveTxt } from 'node:dns/promises'
2
+ import { isIP } from 'node:net'
3
+
4
+ import { ResolveTxt } from '@atproto-labs/handle-resolver'
5
+
6
+ export const nodeResolveTxtDefault: ResolveTxt = (hostname) =>
7
+ resolveTxt(hostname).then(groupChunks, handleError)
8
+
9
+ export function nodeResolveTxtFactory(nameservers: string[]): ResolveTxt {
10
+ // Optimization
11
+ if (!nameservers.length) return async () => null
12
+
13
+ // Build the resolver asynchronously (will be awaited on every use)
14
+ const resolverPromise: Promise<Resolver | null> = Promise.all<string[]>(
15
+ nameservers.map((nameserver) => {
16
+ const [domain, port = null] = nameserver.split(':', 2)
17
+
18
+ if (port !== null && !/^\d+$/.test(port)) {
19
+ throw new TypeError(`Invalid name server "${nameserver}"`)
20
+ }
21
+
22
+ return isIP(domain) === 4 || isBracedIPv6(domain)
23
+ ? [nameserver] // No need to lookup
24
+ : lookup(domain, { all: true }).then(
25
+ (r) => r.map((a) => appendPort(a.address, port)),
26
+ // Let's just ignore failed nameservers resolution
27
+ (_err) => [],
28
+ )
29
+ }),
30
+ ).then((results) => {
31
+ const backupIps = results.flat(1)
32
+ // No resolver if no valid IP
33
+ if (!backupIps.length) return null
34
+
35
+ const resolver = new Resolver()
36
+ resolver.setServers(backupIps)
37
+ return resolver
38
+ })
39
+
40
+ // Avoid uncaught promise rejection
41
+ void resolverPromise.catch(() => {
42
+ // Should never happen though...
43
+ })
44
+
45
+ return async (hostname) => {
46
+ const resolver = await resolverPromise
47
+ return resolver
48
+ ? resolver.resolveTxt(hostname).then(groupChunks, handleError)
49
+ : null
50
+ }
51
+ }
52
+
53
+ function isBracedIPv6(address: string): boolean {
54
+ return (
55
+ address.startsWith('[') &&
56
+ address.endsWith(']') &&
57
+ isIP(address.slice(1, -1)) === 6
58
+ )
59
+ }
60
+
61
+ function groupChunks(results: string[][]): string[] {
62
+ return results.map((chunks) => chunks.join(''))
63
+ }
64
+
65
+ function handleError(err: unknown) {
66
+ // Invalid argument type (e.g. hostname is a number)
67
+ if (err instanceof TypeError) throw err
68
+
69
+ // If the hostname does not resolve, return null
70
+ if (err instanceof Error) {
71
+ if (err['code'] === 'ENOTFOUND') return null
72
+
73
+ // Hostname is not a valid domain name
74
+ if (err['code'] === 'EBADNAME') throw err
75
+
76
+ // DNS server unreachable
77
+ // if (err['code'] === 'ETIMEOUT') throw err
78
+ }
79
+
80
+ // Historically, errors were not thrown here. A "null" value indicates to the
81
+ // AtprotoHandleResolver that it should try the fallback resolver.
82
+
83
+ // @TODO We might want to re-visit this to only apply when an unexpected error
84
+ // occurs (by throwing here). For now, let's keep the same behavior as before.
85
+
86
+ // throw err
87
+
88
+ return null
89
+ }
90
+
91
+ function appendPort(address: string, port: string | null): string {
92
+ switch (isIP(address)) {
93
+ case 4:
94
+ return port ? `${address}:${port}` : address
95
+ case 6:
96
+ return port ? `[${address}]:${port}` : `[${address}]`
97
+ default:
98
+ throw new TypeError(`Invalid IP address "${address}"`)
99
+ }
100
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../tsconfig/node.json",
2
+ "extends": ["../../../tsconfig/node.json"],
3
3
  "compilerOptions": {
4
4
  "rootDir": "./src",
5
5
  "outDir": "./dist"
@@ -1,15 +0,0 @@
1
- /// <reference types="node" />
2
- import { resolveTxt } from 'node:dns/promises';
3
- import { HandleResolveOptions, HandleResolver, ResolvedHandle } from '@atproto-labs/handle-resolver';
4
- export type DnsHandleResolverOptions = {
5
- /**
6
- * Nameservers to use in place of the system's default nameservers.
7
- */
8
- nameservers?: string[];
9
- };
10
- export declare class DnsHandleResolver implements HandleResolver {
11
- protected resolveTxt: typeof resolveTxt;
12
- constructor(options?: DnsHandleResolverOptions);
13
- resolve(handle: string, _options?: HandleResolveOptions): Promise<ResolvedHandle>;
14
- }
15
- //# sourceMappingURL=dns-handle-resolver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dns-handle-resolver.d.ts","sourceRoot":"","sources":["../src/dns-handle-resolver.ts"],"names":[],"mappings":";AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EAEf,MAAM,+BAA+B,CAAA;AAKtC,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAED,qBAAa,iBAAkB,YAAW,cAAc;IACtD,SAAS,CAAC,UAAU,EAAE,OAAO,UAAU,CAAA;gBAE3B,OAAO,CAAC,EAAE,wBAAwB;IAMjC,OAAO,CAClB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;CAO3B"}
@@ -1,58 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DnsHandleResolver = void 0;
4
- const promises_1 = require("node:dns/promises");
5
- const handle_resolver_1 = require("@atproto-labs/handle-resolver");
6
- const SUBDOMAIN = '_atproto';
7
- const PREFIX = 'did=';
8
- class DnsHandleResolver {
9
- constructor(options) {
10
- Object.defineProperty(this, "resolveTxt", {
11
- enumerable: true,
12
- configurable: true,
13
- writable: true,
14
- value: void 0
15
- });
16
- this.resolveTxt = options?.nameservers
17
- ? buildResolveTxt(options.nameservers)
18
- : promises_1.resolveTxt.bind(null);
19
- }
20
- async resolve(handle, _options) {
21
- try {
22
- return parseDnsResult(await this.resolveTxt(`${SUBDOMAIN}.${handle}`));
23
- }
24
- catch (err) {
25
- return null;
26
- }
27
- }
28
- }
29
- exports.DnsHandleResolver = DnsHandleResolver;
30
- function buildResolveTxt(nameservers) {
31
- // Build the resolver asynchronously
32
- const resolverPromise = Promise.allSettled(nameservers.map((h) => (0, promises_1.lookup)(h)))
33
- .then((responses) => responses.flatMap((r) => r.status === 'fulfilled' ? [r.value.address] : []))
34
- .then((backupIps) => {
35
- if (!backupIps.length)
36
- return null;
37
- const resolver = new promises_1.Resolver();
38
- resolver.setServers(backupIps);
39
- return resolver;
40
- });
41
- resolverPromise.catch(() => {
42
- // Should never happen
43
- });
44
- return async (hostname) => {
45
- const resolver = await resolverPromise;
46
- return resolver ? resolver.resolveTxt(hostname) : [];
47
- };
48
- }
49
- function parseDnsResult(chunkedResults) {
50
- const results = chunkedResults.map((chunks) => chunks.join(''));
51
- const found = results.filter((i) => i.startsWith(PREFIX));
52
- if (found.length !== 1) {
53
- return null;
54
- }
55
- const did = found[0].slice(PREFIX.length);
56
- return (0, handle_resolver_1.isResolvedHandle)(did) ? did : null;
57
- }
58
- //# sourceMappingURL=dns-handle-resolver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dns-handle-resolver.js","sourceRoot":"","sources":["../src/dns-handle-resolver.ts"],"names":[],"mappings":";;;AAAA,gDAAgE;AAEhE,mEAKsC;AAEtC,MAAM,SAAS,GAAG,UAAU,CAAA;AAC5B,MAAM,MAAM,GAAG,MAAM,CAAA;AASrB,MAAa,iBAAiB;IAG5B,YAAY,OAAkC;QAFpC;;;;;WAA6B;QAGrC,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,WAAW;YACpC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC;YACtC,CAAC,CAAC,qBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,MAAc,EACd,QAA+B;QAE/B,IAAI,CAAC;YACH,OAAO,cAAc,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,SAAS,IAAI,MAAM,EAAE,CAAC,CAAC,CAAA;QACxE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;CACF;AAnBD,8CAmBC;AAED,SAAS,eAAe,CAAC,WAAqB;IAC5C,oCAAoC;IACpC,MAAM,eAAe,GAA6B,OAAO,CAAC,UAAU,CAClE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,iBAAM,EAAC,CAAC,CAAC,CAAC,CAClC;SACE,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAClB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACtB,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAClD,CACF;SACA,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;QAClB,IAAI,CAAC,SAAS,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAClC,MAAM,QAAQ,GAAG,IAAI,mBAAQ,EAAE,CAAA;QAC/B,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;QAC9B,OAAO,QAAQ,CAAA;IACjB,CAAC,CAAC,CAAA;IAEJ,eAAe,CAAC,KAAK,CAAC,GAAG,EAAE;QACzB,sBAAsB;IACxB,CAAC,CAAC,CAAA;IAEF,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAA;QACtC,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACtD,CAAC,CAAA;AACH,CAAC;AAED,SAAS,cAAc,CAAC,cAA0B;IAChD,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,OAAO,IAAA,kCAAgB,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AAC3C,CAAC"}
@@ -1,29 +0,0 @@
1
- import { Fetch } from '@atproto-labs/fetch';
2
- import { SafeFetchWrapOptions } from '@atproto-labs/fetch-node';
3
- import { CachedHandleResolver, CachedHandleResolverOptions, HandleResolver } from '@atproto-labs/handle-resolver';
4
- import { DnsHandleResolverOptions } from './dns-handle-resolver.js';
5
- export type NodeHandleResolverOptions = {
6
- /**
7
- * List of domain names that are forbidden to be resolved using the
8
- * well-known/atproto-did method.
9
- */
10
- wellKnownExclude?: SafeFetchWrapOptions['forbiddenDomainNames'];
11
- /**
12
- * List of backup nameservers to use for DNS resolution.
13
- */
14
- fallbackNameservers?: DnsHandleResolverOptions['nameservers'];
15
- cache?: CachedHandleResolverOptions['cache'];
16
- /**
17
- * Fetch function to use for HTTP requests. Allows customizing the request
18
- * behavior, e.g. adding headers, setting a timeout, mocking, etc. The
19
- * provided fetch function will be wrapped with a safeFetchWrap function that
20
- * adds SSRF protection.
21
- *
22
- * @default `globalThis.fetch`
23
- */
24
- fetch?: Fetch;
25
- };
26
- export declare class NodeHandleResolver extends CachedHandleResolver implements HandleResolver {
27
- constructor({ cache, fetch, fallbackNameservers, wellKnownExclude, }?: NodeHandleResolverOptions);
28
- }
29
- //# sourceMappingURL=node-handle-resolver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"node-handle-resolver.d.ts","sourceRoot":"","sources":["../src/node-handle-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAiB,MAAM,0BAA0B,CAAA;AAC9E,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,cAAc,EAEf,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EAEL,wBAAwB,EACzB,MAAM,0BAA0B,CAAA;AAEjC,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,sBAAsB,CAAC,CAAA;IAE/D;;OAEG;IACH,mBAAmB,CAAC,EAAE,wBAAwB,CAAC,aAAa,CAAC,CAAA;IAE7D,KAAK,CAAC,EAAE,2BAA2B,CAAC,OAAO,CAAC,CAAA;IAE5C;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,qBAAa,kBACX,SAAQ,oBACR,YAAW,cAAc;gBAEb,EACV,KAAK,EACL,KAAwB,EACxB,mBAAmB,EACnB,gBAAgB,GACjB,GAAE,yBAA8B;CA+ClC"}
@@ -1,49 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NodeHandleResolver = void 0;
4
- const fetch_node_1 = require("@atproto-labs/fetch-node");
5
- const handle_resolver_1 = require("@atproto-labs/handle-resolver");
6
- const dns_handle_resolver_js_1 = require("./dns-handle-resolver.js");
7
- class NodeHandleResolver extends handle_resolver_1.CachedHandleResolver {
8
- constructor({ cache, fetch = globalThis.fetch, fallbackNameservers, wellKnownExclude, } = {}) {
9
- const safeFetch = (0, fetch_node_1.safeFetchWrap)({
10
- fetch,
11
- timeout: 3000, // 3 seconds
12
- forbiddenDomainNames: wellKnownExclude,
13
- ssrfProtection: true,
14
- responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
15
- });
16
- const httpResolver = new handle_resolver_1.WellKnownHandleResolver({
17
- fetch: safeFetch,
18
- });
19
- const dnsResolver = new dns_handle_resolver_js_1.DnsHandleResolver();
20
- const fallbackResolver = new dns_handle_resolver_js_1.DnsHandleResolver({
21
- nameservers: fallbackNameservers,
22
- });
23
- super({
24
- cache,
25
- resolver: {
26
- resolve: async (handle) => {
27
- const abortController = new AbortController();
28
- const dnsPromise = dnsResolver.resolve(handle);
29
- const httpPromise = httpResolver.resolve(handle, {
30
- signal: abortController.signal,
31
- });
32
- // Will be awaited later
33
- httpPromise.catch(() => { });
34
- const dnsRes = await dnsPromise;
35
- if (dnsRes) {
36
- abortController.abort();
37
- return dnsRes;
38
- }
39
- const res = await httpPromise;
40
- if (res)
41
- return res;
42
- return fallbackResolver.resolve(handle);
43
- },
44
- },
45
- });
46
- }
47
- }
48
- exports.NodeHandleResolver = NodeHandleResolver;
49
- //# sourceMappingURL=node-handle-resolver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"node-handle-resolver.js","sourceRoot":"","sources":["../src/node-handle-resolver.ts"],"names":[],"mappings":";;;AACA,yDAA8E;AAC9E,mEAKsC;AAEtC,qEAGiC;AA2BjC,MAAa,kBACX,SAAQ,sCAAoB;IAG5B,YAAY,EACV,KAAK,EACL,KAAK,GAAG,UAAU,CAAC,KAAK,EACxB,mBAAmB,EACnB,gBAAgB,MACa,EAAE;QAC/B,MAAM,SAAS,GAAG,IAAA,0BAAa,EAAC;YAC9B,KAAK;YACL,OAAO,EAAE,IAAI,EAAE,YAAY;YAC3B,oBAAoB,EAAE,gBAAgB;YACtC,cAAc,EAAE,IAAI;YACpB,eAAe,EAAE,EAAE,GAAG,IAAI,EAAE,+CAA+C;SAC5E,CAAC,CAAA;QAEF,MAAM,YAAY,GAAG,IAAI,yCAAuB,CAAC;YAC/C,KAAK,EAAE,SAAS;SACjB,CAAC,CAAA;QAEF,MAAM,WAAW,GAAG,IAAI,0CAAiB,EAAE,CAAA;QAE3C,MAAM,gBAAgB,GAAG,IAAI,0CAAiB,CAAC;YAC7C,WAAW,EAAE,mBAAmB;SACjC,CAAC,CAAA;QAEF,KAAK,CAAC;YACJ,KAAK;YACL,QAAQ,EAAE;gBACR,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;oBACxB,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAA;oBAE7C,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;oBAC9C,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE;wBAC/C,MAAM,EAAE,eAAe,CAAC,MAAM;qBAC/B,CAAC,CAAA;oBAEF,wBAAwB;oBACxB,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;oBAE3B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAA;oBAC/B,IAAI,MAAM,EAAE,CAAC;wBACX,eAAe,CAAC,KAAK,EAAE,CAAA;wBACvB,OAAO,MAAM,CAAA;oBACf,CAAC;oBAED,MAAM,GAAG,GAAG,MAAM,WAAW,CAAA;oBAC7B,IAAI,GAAG;wBAAE,OAAO,GAAG,CAAA;oBAEnB,OAAO,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;gBACzC,CAAC;aACF;SACF,CAAC,CAAA;IACJ,CAAC;CACF;AAxDD,gDAwDC"}
@@ -1,76 +0,0 @@
1
- import { Resolver, lookup, resolveTxt } from 'node:dns/promises'
2
-
3
- import {
4
- HandleResolveOptions,
5
- HandleResolver,
6
- ResolvedHandle,
7
- isResolvedHandle,
8
- } from '@atproto-labs/handle-resolver'
9
-
10
- const SUBDOMAIN = '_atproto'
11
- const PREFIX = 'did='
12
-
13
- export type DnsHandleResolverOptions = {
14
- /**
15
- * Nameservers to use in place of the system's default nameservers.
16
- */
17
- nameservers?: string[]
18
- }
19
-
20
- export class DnsHandleResolver implements HandleResolver {
21
- protected resolveTxt: typeof resolveTxt
22
-
23
- constructor(options?: DnsHandleResolverOptions) {
24
- this.resolveTxt = options?.nameservers
25
- ? buildResolveTxt(options.nameservers)
26
- : resolveTxt.bind(null)
27
- }
28
-
29
- public async resolve(
30
- handle: string,
31
- _options?: HandleResolveOptions,
32
- ): Promise<ResolvedHandle> {
33
- try {
34
- return parseDnsResult(await this.resolveTxt(`${SUBDOMAIN}.${handle}`))
35
- } catch (err) {
36
- return null
37
- }
38
- }
39
- }
40
-
41
- function buildResolveTxt(nameservers: string[]): typeof resolveTxt {
42
- // Build the resolver asynchronously
43
- const resolverPromise: Promise<Resolver | null> = Promise.allSettled(
44
- nameservers.map((h) => lookup(h)),
45
- )
46
- .then((responses) =>
47
- responses.flatMap((r) =>
48
- r.status === 'fulfilled' ? [r.value.address] : [],
49
- ),
50
- )
51
- .then((backupIps) => {
52
- if (!backupIps.length) return null
53
- const resolver = new Resolver()
54
- resolver.setServers(backupIps)
55
- return resolver
56
- })
57
-
58
- resolverPromise.catch(() => {
59
- // Should never happen
60
- })
61
-
62
- return async (hostname) => {
63
- const resolver = await resolverPromise
64
- return resolver ? resolver.resolveTxt(hostname) : []
65
- }
66
- }
67
-
68
- function parseDnsResult(chunkedResults: string[][]): ResolvedHandle {
69
- const results = chunkedResults.map((chunks) => chunks.join(''))
70
- const found = results.filter((i) => i.startsWith(PREFIX))
71
- if (found.length !== 1) {
72
- return null
73
- }
74
- const did = found[0].slice(PREFIX.length)
75
- return isResolvedHandle(did) ? did : null
76
- }
@@ -1,96 +0,0 @@
1
- import { Fetch } from '@atproto-labs/fetch'
2
- import { SafeFetchWrapOptions, safeFetchWrap } from '@atproto-labs/fetch-node'
3
- import {
4
- CachedHandleResolver,
5
- CachedHandleResolverOptions,
6
- HandleResolver,
7
- WellKnownHandleResolver,
8
- } from '@atproto-labs/handle-resolver'
9
-
10
- import {
11
- DnsHandleResolver,
12
- DnsHandleResolverOptions,
13
- } from './dns-handle-resolver.js'
14
-
15
- export type NodeHandleResolverOptions = {
16
- /**
17
- * List of domain names that are forbidden to be resolved using the
18
- * well-known/atproto-did method.
19
- */
20
- wellKnownExclude?: SafeFetchWrapOptions['forbiddenDomainNames']
21
-
22
- /**
23
- * List of backup nameservers to use for DNS resolution.
24
- */
25
- fallbackNameservers?: DnsHandleResolverOptions['nameservers']
26
-
27
- cache?: CachedHandleResolverOptions['cache']
28
-
29
- /**
30
- * Fetch function to use for HTTP requests. Allows customizing the request
31
- * behavior, e.g. adding headers, setting a timeout, mocking, etc. The
32
- * provided fetch function will be wrapped with a safeFetchWrap function that
33
- * adds SSRF protection.
34
- *
35
- * @default `globalThis.fetch`
36
- */
37
- fetch?: Fetch
38
- }
39
-
40
- export class NodeHandleResolver
41
- extends CachedHandleResolver
42
- implements HandleResolver
43
- {
44
- constructor({
45
- cache,
46
- fetch = globalThis.fetch,
47
- fallbackNameservers,
48
- wellKnownExclude,
49
- }: NodeHandleResolverOptions = {}) {
50
- const safeFetch = safeFetchWrap({
51
- fetch,
52
- timeout: 3000, // 3 seconds
53
- forbiddenDomainNames: wellKnownExclude,
54
- ssrfProtection: true,
55
- responseMaxSize: 10 * 1048, // DID are max 2048 characters, 10kb for safety
56
- })
57
-
58
- const httpResolver = new WellKnownHandleResolver({
59
- fetch: safeFetch,
60
- })
61
-
62
- const dnsResolver = new DnsHandleResolver()
63
-
64
- const fallbackResolver = new DnsHandleResolver({
65
- nameservers: fallbackNameservers,
66
- })
67
-
68
- super({
69
- cache,
70
- resolver: {
71
- resolve: async (handle) => {
72
- const abortController = new AbortController()
73
-
74
- const dnsPromise = dnsResolver.resolve(handle)
75
- const httpPromise = httpResolver.resolve(handle, {
76
- signal: abortController.signal,
77
- })
78
-
79
- // Will be awaited later
80
- httpPromise.catch(() => {})
81
-
82
- const dnsRes = await dnsPromise
83
- if (dnsRes) {
84
- abortController.abort()
85
- return dnsRes
86
- }
87
-
88
- const res = await httpPromise
89
- if (res) return res
90
-
91
- return fallbackResolver.resolve(handle)
92
- },
93
- },
94
- })
95
- }
96
- }