@fedify/vocab-runtime 2.5.0-dev.2448 → 2.5.0-dev.2494
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/deno.json +5 -2
- package/dist/{docloader-BBnBpbsG.d.cts → docloader-DD6binpN.d.cts} +4 -8
- package/dist/{docloader-BBnBpbsG.d.ts → docloader-DD6binpN.d.ts} +4 -8
- package/dist/internal/jsonld-cache.cjs +1 -1
- package/dist/internal/jsonld-cache.d.cts +1 -1
- package/dist/internal/jsonld-cache.d.ts +1 -1
- package/dist/internal/jsonld-cache.js +1 -1
- package/dist/internal/portable-dereference.cjs +1 -1
- package/dist/internal/portable-dereference.d.cts +2 -2
- package/dist/internal/portable-dereference.d.ts +2 -2
- package/dist/internal/portable-dereference.js +1 -1
- package/dist/internal.cjs +6 -0
- package/dist/internal.d.cts +108 -0
- package/dist/internal.d.ts +108 -0
- package/dist/internal.js +3 -0
- package/dist/mod.cjs +104 -38
- package/dist/mod.d.cts +3 -336
- package/dist/mod.d.ts +4 -336
- package/dist/mod.js +104 -38
- package/dist/{portable-BDydjp5H.d.ts → portable-1VSnSJFj.d.ts} +1 -1
- package/dist/{portable-Dfvcg_Wy.d.cts → portable-Cr7NwZwb.d.cts} +1 -1
- package/dist/public-fetch-Dvfvawei.cjs +527 -0
- package/dist/public-fetch-ziZ39L5I.js +510 -0
- package/dist/tests/{body-DP2u2wUX.cjs → body-0Or8U7lA.cjs} +1 -1
- package/dist/tests/{body-oV99TVdA.mjs → body-DfH6VTh9.mjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +4 -4
- package/dist/tests/decimal.test.mjs +4 -4
- package/dist/tests/{docloader-D3mA7aPJ.mjs → docloader-BZvFOza_.mjs} +106 -40
- package/dist/tests/{docloader-CwxZguqN.cjs → docloader-W_RHEh5W.cjs} +111 -39
- package/dist/tests/docloader.test.cjs +531 -1293
- package/dist/tests/docloader.test.mjs +472 -1252
- package/dist/tests/esm-Ds-eHTfY.mjs +1229 -0
- package/dist/tests/esm-Q2tdh5f3.cjs +1215 -0
- package/dist/tests/internal/portable-dereference.test.cjs +3 -3
- package/dist/tests/internal/portable-dereference.test.mjs +3 -3
- package/dist/tests/{jsonld-cache-BEXiiM-V.cjs → jsonld-cache-DKIWKxEX.cjs} +1 -1
- package/dist/tests/{jsonld-cache-DFnWsLuZ.mjs → jsonld-cache-KH40z9_7.mjs} +1 -1
- package/dist/tests/jsonld-cache.test.cjs +2 -2
- package/dist/tests/jsonld-cache.test.mjs +2 -2
- package/dist/tests/node-CbBR-th-.mjs +84 -0
- package/dist/tests/node-Ch29PDXR.cjs +89 -0
- package/dist/tests/pin-grant-CPrpk1l4.cjs +70 -0
- package/dist/tests/pin-grant-H5XhM1Xs.mjs +53 -0
- package/dist/tests/pin-grant.test.cjs +87 -0
- package/dist/tests/pin-grant.test.d.cts +1 -0
- package/dist/tests/pin-grant.test.d.mts +1 -0
- package/dist/tests/pin-grant.test.mjs +88 -0
- package/dist/tests/pinners/bun.test.cjs +166 -0
- package/dist/tests/pinners/bun.test.d.cts +1 -0
- package/dist/tests/pinners/bun.test.d.mts +1 -0
- package/dist/tests/pinners/bun.test.mjs +167 -0
- package/dist/tests/pinners/node.test.cjs +269 -0
- package/dist/tests/pinners/node.test.d.cts +1 -0
- package/dist/tests/pinners/node.test.d.mts +1 -0
- package/dist/tests/pinners/node.test.mjs +268 -0
- package/dist/tests/pinners/pinners.test.cjs +196 -0
- package/dist/tests/pinners/pinners.test.d.cts +1 -0
- package/dist/tests/pinners/pinners.test.d.mts +1 -0
- package/dist/tests/pinners/pinners.test.mjs +197 -0
- package/dist/tests/pinners/testdata/node-child.cjs +135 -0
- package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
- package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
- package/dist/tests/pinners/testdata/node-child.mjs +134 -0
- package/dist/tests/pinners-DLH_npLf.cjs +220 -0
- package/dist/tests/pinners-tGcl49uF.mjs +203 -0
- package/dist/tests/{portable-dereference-DaTBBvCj.cjs → portable-dereference-BUoDzMxk.cjs} +2 -2
- package/dist/tests/{portable-dereference-sGRs_qdT.mjs → portable-dereference-Du2k3ngE.mjs} +2 -2
- package/dist/tests/{portable-media-B-oCeotd.mjs → portable-media-CqCq3wqj.mjs} +3 -3
- package/dist/tests/{portable-media-D7ClSjFO.cjs → portable-media-Ls_EOkMN.cjs} +3 -3
- package/dist/tests/portable-media.test.cjs +1 -1
- package/dist/tests/portable-media.test.mjs +1 -1
- package/dist/tests/portable-workers.test.cjs +2 -2
- package/dist/tests/portable-workers.test.mjs +2 -2
- package/dist/tests/public-fetch-BGj5pzUD.cjs +186 -0
- package/dist/tests/public-fetch-Cxo3Yzdf.mjs +170 -0
- package/dist/tests/public-fetch.test.cjs +1073 -0
- package/dist/tests/public-fetch.test.d.cts +1 -0
- package/dist/tests/public-fetch.test.d.mts +1 -0
- package/dist/tests/public-fetch.test.mjs +1074 -0
- package/dist/tests/{request-BlLYI4x3.mjs → request-Bo8cJJhV.mjs} +2 -2
- package/dist/tests/{request-B77_oZnk.cjs → request-Cb6h3X72.cjs} +2 -2
- package/dist/tests/request.test.cjs +2 -2
- package/dist/tests/request.test.mjs +2 -2
- package/dist/tests/support-Bj44xlQu.mjs +272 -0
- package/dist/tests/support-BrdlbLJk.cjs +363 -0
- package/dist/tests/{url-BNakuZ8k.cjs → url-Bdj-rTcC.cjs} +88 -12
- package/dist/tests/{url-DMxmp7ZG.mjs → url-COj5Q8mb.mjs} +65 -13
- package/dist/tests/url.test.cjs +4 -1
- package/dist/tests/url.test.mjs +4 -1
- package/dist/url-4EE56sNP.d.cts +352 -0
- package/dist/url-4EE56sNP.d.ts +352 -0
- package/dist/{url-DrGTR8yv.cjs → url-DIKZnaJv.cjs} +88 -12
- package/dist/{url-Dzyp-NsC.js → url-mWveR5GA.js} +65 -13
- package/package.json +14 -3
- package/src/docloader.test.ts +696 -48
- package/src/docloader.ts +186 -75
- package/src/internal.ts +13 -0
- package/src/pin-grant.test.ts +118 -0
- package/src/pin-grant.ts +99 -0
- package/src/pinners/bun.test.ts +278 -0
- package/src/pinners/bun.ts +223 -0
- package/src/pinners/mod.ts +59 -0
- package/src/pinners/node.test.ts +415 -0
- package/src/pinners/node.ts +144 -0
- package/src/pinners/pinners.test.ts +316 -0
- package/src/pinners/testdata/node-child.ts +147 -0
- package/src/pinners/testdata/proxy-child.ts +82 -0
- package/src/pinners/testdata/support.ts +343 -0
- package/src/pinners/testdata/tls-cert.pem +12 -0
- package/src/pinners/testdata/tls-child.ts +88 -0
- package/src/pinners/testdata/tls-key.pem +5 -0
- package/src/pinners/types.ts +48 -0
- package/src/public-fetch.test.ts +1286 -0
- package/src/public-fetch.ts +338 -0
- package/src/testdata/public-fetch-deno-worker.ts +74 -0
- package/src/url.test.ts +11 -0
- package/src/url.ts +113 -22
- package/tsdown.config.ts +7 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const require_url = require("../url-
|
|
2
|
-
const require_jsonld_cache = require("../jsonld-cache-
|
|
3
|
-
const require_portable_dereference = require("../portable-dereference-
|
|
1
|
+
const require_url = require("../url-Bdj-rTcC.cjs");
|
|
2
|
+
const require_jsonld_cache = require("../jsonld-cache-DKIWKxEX.cjs");
|
|
3
|
+
const require_portable_dereference = require("../portable-dereference-BUoDzMxk.cjs");
|
|
4
4
|
let node_assert_strict = require("node:assert/strict");
|
|
5
5
|
let node_test = require("node:test");
|
|
6
6
|
//#region src/internal/portable-dereference.test.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as createScopedContextLoader } from "../jsonld-cache-
|
|
3
|
-
import { a as recordPortableReferrer, i as isPortableIri, n as getPortableGatewayCandidates, r as getReferrerGateways, t as createSnapshotContextLoader } from "../portable-dereference-
|
|
1
|
+
import { E as withGatewayHints, b as parseIri } from "../url-COj5Q8mb.mjs";
|
|
2
|
+
import { n as createScopedContextLoader } from "../jsonld-cache-KH40z9_7.mjs";
|
|
3
|
+
import { a as recordPortableReferrer, i as isPortableIri, n as getPortableGatewayCandidates, r as getReferrerGateways, t as createSnapshotContextLoader } from "../portable-dereference-Du2k3ngE.mjs";
|
|
4
4
|
import { deepStrictEqual, ok, throws } from "node:assert/strict";
|
|
5
5
|
import { test } from "node:test";
|
|
6
6
|
//#region src/internal/portable-dereference.test.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as parseIri, c as fromCompatibleEf61Id, d as haveSameFe34Origin, f as haveSameIriOrigin, l as getFe34Origin, s as formatIri } from "./url-COj5Q8mb.mjs";
|
|
2
2
|
import { t as jsonld_default } from "./jsonld-DweIJ07b.mjs";
|
|
3
3
|
//#region src/internal/jsonld-cache.ts
|
|
4
4
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const require_url = require("./url-
|
|
1
|
+
const require_url = require("./url-Bdj-rTcC.cjs");
|
|
2
2
|
const require_jsonld = require("./jsonld-CmDhHNTx.cjs");
|
|
3
|
-
const require_jsonld_cache = require("./jsonld-cache-
|
|
3
|
+
const require_jsonld_cache = require("./jsonld-cache-DKIWKxEX.cjs");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
let node_assert = require("node:assert");
|
|
6
6
|
//#region src/jsonld-cache.test.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as parseIri } from "./url-COj5Q8mb.mjs";
|
|
2
2
|
import { t as jsonld_default } from "./jsonld-DweIJ07b.mjs";
|
|
3
|
-
import { a as isTrustedIriOrigin, i as getPortableActorGateways, o as normalizeJsonLdIris, r as getJsonLdContext, t as compactJsonLdCache } from "./jsonld-cache-
|
|
3
|
+
import { a as isTrustedIriOrigin, i as getPortableActorGateways, o as normalizeJsonLdIris, r as getJsonLdContext, t as compactJsonLdCache } from "./jsonld-cache-KH40z9_7.mjs";
|
|
4
4
|
import { test } from "node:test";
|
|
5
5
|
import { deepStrictEqual, ok, strictEqual } from "node:assert";
|
|
6
6
|
//#region src/jsonld-cache.test.ts
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { n as UrlError } from "./url-COj5Q8mb.mjs";
|
|
2
|
+
import { n as redeemPinGrant } from "./pin-grant-H5XhM1Xs.mjs";
|
|
3
|
+
import { isIP } from "node:net";
|
|
4
|
+
//#region src/pinners/node.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates a pinner for Node.js. Every request is sent through the global
|
|
7
|
+
* `fetch()` with an undici `Agent` as the `dispatcher`. The agent's
|
|
8
|
+
* `connect.lookup` answers only with the addresses of live grants for
|
|
9
|
+
* the hostname, checking each of them again right before connecting, and
|
|
10
|
+
* fails when there are none, so a connection can never be made to an address
|
|
11
|
+
* that was not validated.
|
|
12
|
+
*
|
|
13
|
+
* The agent is shared by the requests of all grants, so keep-alive
|
|
14
|
+
* connections are reused. A pooled connection was made to an address of
|
|
15
|
+
* a grant that was valid when it was connected, so reusing it from another
|
|
16
|
+
* session of the same origin still reaches a validated address. Response
|
|
17
|
+
* bodies have to be consumed or cancelled for their connections to return to
|
|
18
|
+
* the pool, which sessions do when they are closed.
|
|
19
|
+
*
|
|
20
|
+
* Since the dispatcher is passed explicitly, neither proxy environment
|
|
21
|
+
* variables nor the global dispatcher (e.g., `NODE_USE_ENV_PROXY=1`) apply.
|
|
22
|
+
* @param options The pinner options.
|
|
23
|
+
* @returns The pinner.
|
|
24
|
+
*/
|
|
25
|
+
function createNodePinner({ verifyAddress, undici }) {
|
|
26
|
+
const grants = /* @__PURE__ */ new Map();
|
|
27
|
+
function lookup(hostname, options, callback) {
|
|
28
|
+
let addresses;
|
|
29
|
+
try {
|
|
30
|
+
addresses = getGrantedAddresses(hostname, options.family);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
callback(error, []);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (options.all) callback(null, addresses);
|
|
36
|
+
else callback(null, addresses[0].address, addresses[0].family);
|
|
37
|
+
}
|
|
38
|
+
function getGrantedAddresses(hostname, family) {
|
|
39
|
+
const addresses = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const [grant, record] of grants.get(hostname) ?? []) {
|
|
41
|
+
try {
|
|
42
|
+
redeemPinGrant(grant, new URL(record.origin));
|
|
43
|
+
} catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
for (const address of record.addresses) addresses.set(address.address, address);
|
|
47
|
+
}
|
|
48
|
+
const familyNumber = family === "IPv4" ? 4 : family === "IPv6" ? 6 : typeof family === "number" ? family : 0;
|
|
49
|
+
const result = [...addresses.values()].filter((address) => familyNumber === 0 || address.family === familyNumber);
|
|
50
|
+
if (result.length < 1) throw new UrlError(`No validated address to connect to: ${hostname}`);
|
|
51
|
+
for (const address of result) verifyAddress(address);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
const agent = new undici.Agent({ connect: { lookup } });
|
|
55
|
+
return {
|
|
56
|
+
supports(url) {
|
|
57
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
58
|
+
},
|
|
59
|
+
async fetch(grant, request, signal) {
|
|
60
|
+
const record = redeemPinGrant(grant, new URL(request.url));
|
|
61
|
+
if (isIP(record.hostname) !== 0) for (const address of record.addresses) verifyAddress(address);
|
|
62
|
+
let registered = grants.get(record.hostname);
|
|
63
|
+
if (registered == null) {
|
|
64
|
+
registered = /* @__PURE__ */ new Map();
|
|
65
|
+
grants.set(record.hostname, registered);
|
|
66
|
+
}
|
|
67
|
+
registered.set(grant, record);
|
|
68
|
+
return await fetch(request, {
|
|
69
|
+
redirect: "manual",
|
|
70
|
+
signal: signal == null || signal === request.signal ? request.signal : AbortSignal.any([signal, request.signal]),
|
|
71
|
+
dispatcher: agent
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
release(grant) {
|
|
75
|
+
for (const [hostname, registered] of grants) {
|
|
76
|
+
if (!registered.delete(grant)) continue;
|
|
77
|
+
if (registered.size < 1) grants.delete(hostname);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { createNodePinner as t };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const require_url = require("./url-Bdj-rTcC.cjs");
|
|
2
|
+
const require_pin_grant = require("./pin-grant-CPrpk1l4.cjs");
|
|
3
|
+
let node_net = require("node:net");
|
|
4
|
+
//#region src/pinners/node.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates a pinner for Node.js. Every request is sent through the global
|
|
7
|
+
* `fetch()` with an undici `Agent` as the `dispatcher`. The agent's
|
|
8
|
+
* `connect.lookup` answers only with the addresses of live grants for
|
|
9
|
+
* the hostname, checking each of them again right before connecting, and
|
|
10
|
+
* fails when there are none, so a connection can never be made to an address
|
|
11
|
+
* that was not validated.
|
|
12
|
+
*
|
|
13
|
+
* The agent is shared by the requests of all grants, so keep-alive
|
|
14
|
+
* connections are reused. A pooled connection was made to an address of
|
|
15
|
+
* a grant that was valid when it was connected, so reusing it from another
|
|
16
|
+
* session of the same origin still reaches a validated address. Response
|
|
17
|
+
* bodies have to be consumed or cancelled for their connections to return to
|
|
18
|
+
* the pool, which sessions do when they are closed.
|
|
19
|
+
*
|
|
20
|
+
* Since the dispatcher is passed explicitly, neither proxy environment
|
|
21
|
+
* variables nor the global dispatcher (e.g., `NODE_USE_ENV_PROXY=1`) apply.
|
|
22
|
+
* @param options The pinner options.
|
|
23
|
+
* @returns The pinner.
|
|
24
|
+
*/
|
|
25
|
+
function createNodePinner({ verifyAddress, undici }) {
|
|
26
|
+
const grants = /* @__PURE__ */ new Map();
|
|
27
|
+
function lookup(hostname, options, callback) {
|
|
28
|
+
let addresses;
|
|
29
|
+
try {
|
|
30
|
+
addresses = getGrantedAddresses(hostname, options.family);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
callback(error, []);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (options.all) callback(null, addresses);
|
|
36
|
+
else callback(null, addresses[0].address, addresses[0].family);
|
|
37
|
+
}
|
|
38
|
+
function getGrantedAddresses(hostname, family) {
|
|
39
|
+
const addresses = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const [grant, record] of grants.get(hostname) ?? []) {
|
|
41
|
+
try {
|
|
42
|
+
require_pin_grant.redeemPinGrant(grant, new URL(record.origin));
|
|
43
|
+
} catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
for (const address of record.addresses) addresses.set(address.address, address);
|
|
47
|
+
}
|
|
48
|
+
const familyNumber = family === "IPv4" ? 4 : family === "IPv6" ? 6 : typeof family === "number" ? family : 0;
|
|
49
|
+
const result = [...addresses.values()].filter((address) => familyNumber === 0 || address.family === familyNumber);
|
|
50
|
+
if (result.length < 1) throw new require_url.UrlError(`No validated address to connect to: ${hostname}`);
|
|
51
|
+
for (const address of result) verifyAddress(address);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
const agent = new undici.Agent({ connect: { lookup } });
|
|
55
|
+
return {
|
|
56
|
+
supports(url) {
|
|
57
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
58
|
+
},
|
|
59
|
+
async fetch(grant, request, signal) {
|
|
60
|
+
const record = require_pin_grant.redeemPinGrant(grant, new URL(request.url));
|
|
61
|
+
if ((0, node_net.isIP)(record.hostname) !== 0) for (const address of record.addresses) verifyAddress(address);
|
|
62
|
+
let registered = grants.get(record.hostname);
|
|
63
|
+
if (registered == null) {
|
|
64
|
+
registered = /* @__PURE__ */ new Map();
|
|
65
|
+
grants.set(record.hostname, registered);
|
|
66
|
+
}
|
|
67
|
+
registered.set(grant, record);
|
|
68
|
+
return await fetch(request, {
|
|
69
|
+
redirect: "manual",
|
|
70
|
+
signal: signal == null || signal === request.signal ? request.signal : AbortSignal.any([signal, request.signal]),
|
|
71
|
+
dispatcher: agent
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
release(grant) {
|
|
75
|
+
for (const [hostname, registered] of grants) {
|
|
76
|
+
if (!registered.delete(grant)) continue;
|
|
77
|
+
if (registered.size < 1) grants.delete(hostname);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
Object.defineProperty(exports, "createNodePinner", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function() {
|
|
87
|
+
return createNodePinner;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//#region src/pin-grant.ts
|
|
2
|
+
const grants = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
/**
|
|
4
|
+
* Issues a grant for the given URL's origin and addresses. This does not
|
|
5
|
+
* validate the addresses; callers validate them before issuing, and pinners
|
|
6
|
+
* validate them again right before connecting.
|
|
7
|
+
* @param url The URL whose origin the grant is bound to.
|
|
8
|
+
* @param addresses The addresses to connect to.
|
|
9
|
+
* @returns A new grant.
|
|
10
|
+
*/
|
|
11
|
+
function issuePinGrant(url, addresses) {
|
|
12
|
+
const grant = Object.freeze(Object.create(null));
|
|
13
|
+
let hostname = url.hostname;
|
|
14
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
15
|
+
const record = Object.freeze({
|
|
16
|
+
origin: url.origin,
|
|
17
|
+
hostname,
|
|
18
|
+
addresses: Object.freeze(addresses.map(({ address, family }) => Object.freeze({
|
|
19
|
+
address,
|
|
20
|
+
family
|
|
21
|
+
})))
|
|
22
|
+
});
|
|
23
|
+
grants.set(grant, {
|
|
24
|
+
record,
|
|
25
|
+
revoked: false
|
|
26
|
+
});
|
|
27
|
+
return grant;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Checks that the given grant was issued by {@link issuePinGrant}, has not
|
|
31
|
+
* been revoked, and was issued for the origin of the given URL.
|
|
32
|
+
* @param grant The grant to redeem.
|
|
33
|
+
* @param url The URL to connect to.
|
|
34
|
+
* @returns What the grant stands for.
|
|
35
|
+
* @throws {TypeError} If the grant cannot be used for the URL.
|
|
36
|
+
*/
|
|
37
|
+
function redeemPinGrant(grant, url) {
|
|
38
|
+
const state = typeof grant === "object" && grant !== null ? grants.get(grant) : void 0;
|
|
39
|
+
if (state == null) throw new TypeError("Invalid pin grant.");
|
|
40
|
+
if (state.revoked) throw new TypeError("The pin grant has been revoked.");
|
|
41
|
+
if (url.origin !== state.record.origin) throw new TypeError(`The pin grant for ${state.record.origin} cannot be used for ${url.origin}.`);
|
|
42
|
+
return state.record;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Revokes the given grant so that it can no longer be redeemed.
|
|
46
|
+
* @param grant The grant to revoke.
|
|
47
|
+
*/
|
|
48
|
+
function revokePinGrant(grant) {
|
|
49
|
+
const state = grants.get(grant);
|
|
50
|
+
if (state != null) state.revoked = true;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
Object.defineProperty(exports, "issuePinGrant", {
|
|
54
|
+
enumerable: true,
|
|
55
|
+
get: function() {
|
|
56
|
+
return issuePinGrant;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
Object.defineProperty(exports, "redeemPinGrant", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
get: function() {
|
|
62
|
+
return redeemPinGrant;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
Object.defineProperty(exports, "revokePinGrant", {
|
|
66
|
+
enumerable: true,
|
|
67
|
+
get: function() {
|
|
68
|
+
return revokePinGrant;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/pin-grant.ts
|
|
2
|
+
const grants = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
/**
|
|
4
|
+
* Issues a grant for the given URL's origin and addresses. This does not
|
|
5
|
+
* validate the addresses; callers validate them before issuing, and pinners
|
|
6
|
+
* validate them again right before connecting.
|
|
7
|
+
* @param url The URL whose origin the grant is bound to.
|
|
8
|
+
* @param addresses The addresses to connect to.
|
|
9
|
+
* @returns A new grant.
|
|
10
|
+
*/
|
|
11
|
+
function issuePinGrant(url, addresses) {
|
|
12
|
+
const grant = Object.freeze(Object.create(null));
|
|
13
|
+
let hostname = url.hostname;
|
|
14
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
15
|
+
const record = Object.freeze({
|
|
16
|
+
origin: url.origin,
|
|
17
|
+
hostname,
|
|
18
|
+
addresses: Object.freeze(addresses.map(({ address, family }) => Object.freeze({
|
|
19
|
+
address,
|
|
20
|
+
family
|
|
21
|
+
})))
|
|
22
|
+
});
|
|
23
|
+
grants.set(grant, {
|
|
24
|
+
record,
|
|
25
|
+
revoked: false
|
|
26
|
+
});
|
|
27
|
+
return grant;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Checks that the given grant was issued by {@link issuePinGrant}, has not
|
|
31
|
+
* been revoked, and was issued for the origin of the given URL.
|
|
32
|
+
* @param grant The grant to redeem.
|
|
33
|
+
* @param url The URL to connect to.
|
|
34
|
+
* @returns What the grant stands for.
|
|
35
|
+
* @throws {TypeError} If the grant cannot be used for the URL.
|
|
36
|
+
*/
|
|
37
|
+
function redeemPinGrant(grant, url) {
|
|
38
|
+
const state = typeof grant === "object" && grant !== null ? grants.get(grant) : void 0;
|
|
39
|
+
if (state == null) throw new TypeError("Invalid pin grant.");
|
|
40
|
+
if (state.revoked) throw new TypeError("The pin grant has been revoked.");
|
|
41
|
+
if (url.origin !== state.record.origin) throw new TypeError(`The pin grant for ${state.record.origin} cannot be used for ${url.origin}.`);
|
|
42
|
+
return state.record;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Revokes the given grant so that it can no longer be redeemed.
|
|
46
|
+
* @param grant The grant to revoke.
|
|
47
|
+
*/
|
|
48
|
+
function revokePinGrant(grant) {
|
|
49
|
+
const state = grants.get(grant);
|
|
50
|
+
if (state != null) state.revoked = true;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { redeemPinGrant as n, revokePinGrant as r, issuePinGrant as t };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const require_pin_grant = require("./pin-grant-CPrpk1l4.cjs");
|
|
2
|
+
let node_assert = require("node:assert");
|
|
3
|
+
let _fedify_fixture = require("@fedify/fixture");
|
|
4
|
+
//#region src/pin-grant.test.ts
|
|
5
|
+
const addresses = [{
|
|
6
|
+
address: "8.8.8.8",
|
|
7
|
+
family: 4
|
|
8
|
+
}, {
|
|
9
|
+
address: "2001:4860:4860::8888",
|
|
10
|
+
family: 6
|
|
11
|
+
}];
|
|
12
|
+
(0, _fedify_fixture.test)("redeemPinGrant() returns what the grant was issued for", () => {
|
|
13
|
+
const url = new URL("https://pinned-host.invalid:8443/path?query");
|
|
14
|
+
const input = addresses.map((address) => ({ ...address }));
|
|
15
|
+
const grant = require_pin_grant.issuePinGrant(url, input);
|
|
16
|
+
const record = require_pin_grant.redeemPinGrant(grant, url);
|
|
17
|
+
(0, node_assert.deepStrictEqual)(record.origin, "https://pinned-host.invalid:8443");
|
|
18
|
+
(0, node_assert.deepStrictEqual)(record.hostname, "pinned-host.invalid");
|
|
19
|
+
(0, node_assert.deepStrictEqual)(record.addresses, addresses);
|
|
20
|
+
input[0].address = "127.0.0.1";
|
|
21
|
+
input.push({
|
|
22
|
+
address: "10.0.0.1",
|
|
23
|
+
family: 4
|
|
24
|
+
});
|
|
25
|
+
(0, node_assert.deepStrictEqual)(require_pin_grant.redeemPinGrant(grant, url).addresses, addresses);
|
|
26
|
+
(0, node_assert.ok)(Object.isFrozen(record));
|
|
27
|
+
(0, node_assert.ok)(Object.isFrozen(record.addresses));
|
|
28
|
+
(0, node_assert.ok)(Object.isFrozen(record.addresses[0]));
|
|
29
|
+
(0, node_assert.strictEqual)(require_pin_grant.redeemPinGrant(grant, new URL("https://pinned-host.invalid:8443/other")), record);
|
|
30
|
+
});
|
|
31
|
+
(0, _fedify_fixture.test)("redeemPinGrant() strips brackets from IPv6 hostnames", () => {
|
|
32
|
+
const url = new URL("http://[2001:4860:4860::8888]/");
|
|
33
|
+
const record = require_pin_grant.redeemPinGrant(require_pin_grant.issuePinGrant(url, [addresses[1]]), url);
|
|
34
|
+
(0, node_assert.deepStrictEqual)(record.hostname, "2001:4860:4860::8888");
|
|
35
|
+
(0, node_assert.deepStrictEqual)(record.origin, "http://[2001:4860:4860::8888]");
|
|
36
|
+
});
|
|
37
|
+
(0, _fedify_fixture.test)("redeemPinGrant() rejects grants not issued by issuePinGrant()", () => {
|
|
38
|
+
const url = new URL("https://pinned-host.invalid/");
|
|
39
|
+
const issued = require_pin_grant.issuePinGrant(url, addresses);
|
|
40
|
+
const forgeries = {
|
|
41
|
+
"object literal": {},
|
|
42
|
+
"null-prototype object": Object.create(null),
|
|
43
|
+
"frozen null-prototype object": Object.freeze(Object.create(null)),
|
|
44
|
+
"copy of a grant": { ...issued },
|
|
45
|
+
"structured clone of a grant": structuredClone(issued),
|
|
46
|
+
"object inheriting from a grant": Object.create(issued),
|
|
47
|
+
"null": null,
|
|
48
|
+
"undefined": void 0,
|
|
49
|
+
"string": "grant"
|
|
50
|
+
};
|
|
51
|
+
for (const [name, forgery] of Object.entries(forgeries)) (0, node_assert.throws)(() => require_pin_grant.redeemPinGrant(forgery, url), TypeError, name);
|
|
52
|
+
(0, node_assert.ok)(require_pin_grant.redeemPinGrant(issued, url));
|
|
53
|
+
});
|
|
54
|
+
(0, _fedify_fixture.test)("redeemPinGrant() rejects URLs of other origins", () => {
|
|
55
|
+
const grant = require_pin_grant.issuePinGrant(new URL("https://pinned-host.invalid/"), addresses);
|
|
56
|
+
for (const url of [
|
|
57
|
+
"http://pinned-host.invalid/",
|
|
58
|
+
"https://other-host.invalid/",
|
|
59
|
+
"https://sub.pinned-host.invalid/",
|
|
60
|
+
"https://pinned-host.invalid:8443/",
|
|
61
|
+
"https://8.8.8.8/"
|
|
62
|
+
]) (0, node_assert.throws)(() => require_pin_grant.redeemPinGrant(grant, new URL(url)), TypeError, url);
|
|
63
|
+
(0, node_assert.ok)(require_pin_grant.redeemPinGrant(grant, new URL("https://pinned-host.invalid:443/")));
|
|
64
|
+
});
|
|
65
|
+
(0, _fedify_fixture.test)("redeemPinGrant() rejects revoked grants", () => {
|
|
66
|
+
const url = new URL("https://pinned-host.invalid/");
|
|
67
|
+
const grant = require_pin_grant.issuePinGrant(url, addresses);
|
|
68
|
+
const other = require_pin_grant.issuePinGrant(url, addresses);
|
|
69
|
+
require_pin_grant.revokePinGrant(grant);
|
|
70
|
+
(0, node_assert.throws)(() => require_pin_grant.redeemPinGrant(grant, url), TypeError);
|
|
71
|
+
require_pin_grant.revokePinGrant(grant);
|
|
72
|
+
(0, node_assert.throws)(() => require_pin_grant.redeemPinGrant(grant, url), TypeError);
|
|
73
|
+
(0, node_assert.ok)(require_pin_grant.redeemPinGrant(other, url));
|
|
74
|
+
});
|
|
75
|
+
(0, _fedify_fixture.test)("revokePinGrant() ignores unknown objects", () => {
|
|
76
|
+
require_pin_grant.revokePinGrant({});
|
|
77
|
+
require_pin_grant.revokePinGrant(Object.create(null));
|
|
78
|
+
});
|
|
79
|
+
(0, _fedify_fixture.test)("issuePinGrant() returns a distinct frozen object every time", () => {
|
|
80
|
+
const url = new URL("https://pinned-host.invalid/");
|
|
81
|
+
const a = require_pin_grant.issuePinGrant(url, addresses);
|
|
82
|
+
(0, node_assert.ok)(a !== require_pin_grant.issuePinGrant(url, addresses));
|
|
83
|
+
(0, node_assert.ok)(Object.isFrozen(a));
|
|
84
|
+
(0, node_assert.strictEqual)(Object.getPrototypeOf(a), null);
|
|
85
|
+
(0, node_assert.deepStrictEqual)(Reflect.ownKeys(a), []);
|
|
86
|
+
});
|
|
87
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { n as redeemPinGrant, r as revokePinGrant, t as issuePinGrant } from "./pin-grant-H5XhM1Xs.mjs";
|
|
2
|
+
import { deepStrictEqual, ok, strictEqual, throws } from "node:assert";
|
|
3
|
+
import { test } from "@fedify/fixture";
|
|
4
|
+
//#region src/pin-grant.test.ts
|
|
5
|
+
const addresses = [{
|
|
6
|
+
address: "8.8.8.8",
|
|
7
|
+
family: 4
|
|
8
|
+
}, {
|
|
9
|
+
address: "2001:4860:4860::8888",
|
|
10
|
+
family: 6
|
|
11
|
+
}];
|
|
12
|
+
test("redeemPinGrant() returns what the grant was issued for", () => {
|
|
13
|
+
const url = new URL("https://pinned-host.invalid:8443/path?query");
|
|
14
|
+
const input = addresses.map((address) => ({ ...address }));
|
|
15
|
+
const grant = issuePinGrant(url, input);
|
|
16
|
+
const record = redeemPinGrant(grant, url);
|
|
17
|
+
deepStrictEqual(record.origin, "https://pinned-host.invalid:8443");
|
|
18
|
+
deepStrictEqual(record.hostname, "pinned-host.invalid");
|
|
19
|
+
deepStrictEqual(record.addresses, addresses);
|
|
20
|
+
input[0].address = "127.0.0.1";
|
|
21
|
+
input.push({
|
|
22
|
+
address: "10.0.0.1",
|
|
23
|
+
family: 4
|
|
24
|
+
});
|
|
25
|
+
deepStrictEqual(redeemPinGrant(grant, url).addresses, addresses);
|
|
26
|
+
ok(Object.isFrozen(record));
|
|
27
|
+
ok(Object.isFrozen(record.addresses));
|
|
28
|
+
ok(Object.isFrozen(record.addresses[0]));
|
|
29
|
+
strictEqual(redeemPinGrant(grant, new URL("https://pinned-host.invalid:8443/other")), record);
|
|
30
|
+
});
|
|
31
|
+
test("redeemPinGrant() strips brackets from IPv6 hostnames", () => {
|
|
32
|
+
const url = new URL("http://[2001:4860:4860::8888]/");
|
|
33
|
+
const record = redeemPinGrant(issuePinGrant(url, [addresses[1]]), url);
|
|
34
|
+
deepStrictEqual(record.hostname, "2001:4860:4860::8888");
|
|
35
|
+
deepStrictEqual(record.origin, "http://[2001:4860:4860::8888]");
|
|
36
|
+
});
|
|
37
|
+
test("redeemPinGrant() rejects grants not issued by issuePinGrant()", () => {
|
|
38
|
+
const url = new URL("https://pinned-host.invalid/");
|
|
39
|
+
const issued = issuePinGrant(url, addresses);
|
|
40
|
+
const forgeries = {
|
|
41
|
+
"object literal": {},
|
|
42
|
+
"null-prototype object": Object.create(null),
|
|
43
|
+
"frozen null-prototype object": Object.freeze(Object.create(null)),
|
|
44
|
+
"copy of a grant": { ...issued },
|
|
45
|
+
"structured clone of a grant": structuredClone(issued),
|
|
46
|
+
"object inheriting from a grant": Object.create(issued),
|
|
47
|
+
"null": null,
|
|
48
|
+
"undefined": void 0,
|
|
49
|
+
"string": "grant"
|
|
50
|
+
};
|
|
51
|
+
for (const [name, forgery] of Object.entries(forgeries)) throws(() => redeemPinGrant(forgery, url), TypeError, name);
|
|
52
|
+
ok(redeemPinGrant(issued, url));
|
|
53
|
+
});
|
|
54
|
+
test("redeemPinGrant() rejects URLs of other origins", () => {
|
|
55
|
+
const grant = issuePinGrant(new URL("https://pinned-host.invalid/"), addresses);
|
|
56
|
+
for (const url of [
|
|
57
|
+
"http://pinned-host.invalid/",
|
|
58
|
+
"https://other-host.invalid/",
|
|
59
|
+
"https://sub.pinned-host.invalid/",
|
|
60
|
+
"https://pinned-host.invalid:8443/",
|
|
61
|
+
"https://8.8.8.8/"
|
|
62
|
+
]) throws(() => redeemPinGrant(grant, new URL(url)), TypeError, url);
|
|
63
|
+
ok(redeemPinGrant(grant, new URL("https://pinned-host.invalid:443/")));
|
|
64
|
+
});
|
|
65
|
+
test("redeemPinGrant() rejects revoked grants", () => {
|
|
66
|
+
const url = new URL("https://pinned-host.invalid/");
|
|
67
|
+
const grant = issuePinGrant(url, addresses);
|
|
68
|
+
const other = issuePinGrant(url, addresses);
|
|
69
|
+
revokePinGrant(grant);
|
|
70
|
+
throws(() => redeemPinGrant(grant, url), TypeError);
|
|
71
|
+
revokePinGrant(grant);
|
|
72
|
+
throws(() => redeemPinGrant(grant, url), TypeError);
|
|
73
|
+
ok(redeemPinGrant(other, url));
|
|
74
|
+
});
|
|
75
|
+
test("revokePinGrant() ignores unknown objects", () => {
|
|
76
|
+
revokePinGrant({});
|
|
77
|
+
revokePinGrant(Object.create(null));
|
|
78
|
+
});
|
|
79
|
+
test("issuePinGrant() returns a distinct frozen object every time", () => {
|
|
80
|
+
const url = new URL("https://pinned-host.invalid/");
|
|
81
|
+
const a = issuePinGrant(url, addresses);
|
|
82
|
+
ok(a !== issuePinGrant(url, addresses));
|
|
83
|
+
ok(Object.isFrozen(a));
|
|
84
|
+
strictEqual(Object.getPrototypeOf(a), null);
|
|
85
|
+
deepStrictEqual(Reflect.ownKeys(a), []);
|
|
86
|
+
});
|
|
87
|
+
//#endregion
|
|
88
|
+
export {};
|