@fedify/vocab-runtime 2.2.0 → 2.2.2
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 +4 -2
- package/dist/{chunk-CKQMccvm.cjs → chunk-M78iaK0I.cjs} +1 -0
- package/dist/jsonld.cjs +2 -1
- package/dist/jsonld.d.cts +1 -0
- package/dist/jsonld.d.ts +1 -0
- package/dist/jsonld.js +1 -0
- package/dist/mod.cjs +14 -4
- package/dist/mod.d.cts +1 -0
- package/dist/mod.d.ts +1 -0
- package/dist/mod.js +13 -3
- package/dist/temporal.cjs +60 -0
- package/dist/temporal.d.cts +54 -0
- package/dist/temporal.d.ts +54 -0
- package/dist/temporal.js +58 -0
- package/dist/tests/decimal.test.cjs +1 -1
- package/dist/tests/decimal.test.mjs +1 -1
- package/dist/tests/{docloader-Cm8MkfmU.cjs → docloader-CNFRvB3y.cjs} +2 -2
- package/dist/tests/{docloader-0xxbBwa9.mjs → docloader-bbaLrO6y.mjs} +2 -2
- package/dist/tests/docloader.test.cjs +3 -3
- package/dist/tests/docloader.test.mjs +3 -3
- package/dist/tests/{request-DLk6MHw1.cjs → request-BDej45SV.cjs} +1 -1
- package/dist/tests/{request-peBUF_HC.mjs → request-BrFKK0nk.mjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/temporal.test.cjs +134 -0
- package/dist/tests/temporal.test.d.cts +1 -0
- package/dist/tests/temporal.test.d.mts +1 -0
- package/dist/tests/temporal.test.mjs +134 -0
- package/dist/tests/{url-BQ_kgmCk.mjs → url-BzGwIxB4.mjs} +11 -2
- package/dist/tests/{url-pFuSds44.cjs → url-Cj9-Ycue.cjs} +11 -2
- package/dist/tests/url.test.cjs +4 -1
- package/dist/tests/url.test.mjs +4 -1
- package/package.json +13 -2
- package/src/temporal.test.ts +121 -0
- package/src/temporal.ts +74 -0
- package/src/url.test.ts +6 -0
- package/src/url.ts +17 -8
- package/tsdown.config.ts +4 -1
package/deno.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/vocab-runtime",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/mod.ts",
|
|
7
|
-
"./jsonld": "./src/jsonld.ts"
|
|
7
|
+
"./jsonld": "./src/jsonld.ts",
|
|
8
|
+
"./temporal": "./src/temporal.ts"
|
|
8
9
|
},
|
|
9
10
|
"description": "Runtime library for @fedify/vocab",
|
|
10
11
|
"author": {
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
"url": "https://hongminhee.org/"
|
|
14
15
|
},
|
|
15
16
|
"imports": {
|
|
17
|
+
"@js-temporal/polyfill": "npm:@js-temporal/polyfill@^0.5.1",
|
|
16
18
|
"@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1",
|
|
17
19
|
"asn1js": "npm:asn1js@^3.0.6",
|
|
18
20
|
"byte-encodings": "npm:byte-encodings@^1.0.11",
|
package/dist/jsonld.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
const require_chunk = require("./chunk-M78iaK0I.cjs");
|
|
2
3
|
let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
|
|
3
4
|
jsonld_dist_jsonld_esm_js = require_chunk.__toESM(jsonld_dist_jsonld_esm_js);
|
|
4
5
|
//#region src/jsonld.ts
|
package/dist/jsonld.d.cts
CHANGED
package/dist/jsonld.d.ts
CHANGED
package/dist/jsonld.js
CHANGED
package/dist/mod.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
|
|
1
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_chunk = require("./chunk-
|
|
3
|
+
const require_chunk = require("./chunk-M78iaK0I.cjs");
|
|
3
4
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
5
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
5
6
|
let node_process = require("node:process");
|
|
@@ -4383,7 +4384,7 @@ const preloadedContexts = {
|
|
|
4383
4384
|
//#endregion
|
|
4384
4385
|
//#region deno.json
|
|
4385
4386
|
var name = "@fedify/vocab-runtime";
|
|
4386
|
-
var version = "2.2.
|
|
4387
|
+
var version = "2.2.2";
|
|
4387
4388
|
//#endregion
|
|
4388
4389
|
//#region src/link.ts
|
|
4389
4390
|
const parametersNeedLowerCase = ["rel", "type"];
|
|
@@ -4650,8 +4651,13 @@ async function validatePublicUrl(url) {
|
|
|
4650
4651
|
const parsed = new URL(url);
|
|
4651
4652
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
4652
4653
|
let hostname = parsed.hostname;
|
|
4653
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.
|
|
4654
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
4654
4655
|
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
4656
|
+
const hostnameFamily = (0, node_net.isIP)(hostname);
|
|
4657
|
+
if (hostnameFamily !== 0) {
|
|
4658
|
+
validatePublicIpAddress(hostname, hostnameFamily);
|
|
4659
|
+
return;
|
|
4660
|
+
}
|
|
4655
4661
|
if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
|
|
4656
4662
|
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
4657
4663
|
}
|
|
@@ -4665,7 +4671,11 @@ async function validatePublicUrl(url) {
|
|
|
4665
4671
|
} catch {
|
|
4666
4672
|
addresses = [];
|
|
4667
4673
|
}
|
|
4668
|
-
for (const { address, family } of addresses)
|
|
4674
|
+
for (const { address, family } of addresses) validatePublicIpAddress(address, family);
|
|
4675
|
+
}
|
|
4676
|
+
function validatePublicIpAddress(address, family) {
|
|
4677
|
+
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
4678
|
+
throw new UrlError(`Invalid or private address: ${address}`);
|
|
4669
4679
|
}
|
|
4670
4680
|
function isValidPublicIPv4Address(address) {
|
|
4671
4681
|
const parts = address.split(".");
|
package/dist/mod.d.cts
CHANGED
package/dist/mod.d.ts
CHANGED
package/dist/mod.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import { getLogger } from "@logtape/logtape";
|
|
2
3
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
3
4
|
import process from "node:process";
|
|
@@ -4379,7 +4380,7 @@ const preloadedContexts = {
|
|
|
4379
4380
|
//#endregion
|
|
4380
4381
|
//#region deno.json
|
|
4381
4382
|
var name = "@fedify/vocab-runtime";
|
|
4382
|
-
var version = "2.2.
|
|
4383
|
+
var version = "2.2.2";
|
|
4383
4384
|
//#endregion
|
|
4384
4385
|
//#region src/link.ts
|
|
4385
4386
|
const parametersNeedLowerCase = ["rel", "type"];
|
|
@@ -4646,8 +4647,13 @@ async function validatePublicUrl(url) {
|
|
|
4646
4647
|
const parsed = new URL(url);
|
|
4647
4648
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
4648
4649
|
let hostname = parsed.hostname;
|
|
4649
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.
|
|
4650
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
4650
4651
|
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
4652
|
+
const hostnameFamily = isIP(hostname);
|
|
4653
|
+
if (hostnameFamily !== 0) {
|
|
4654
|
+
validatePublicIpAddress(hostname, hostnameFamily);
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4651
4657
|
if ("Deno" in globalThis && !isIP(hostname)) {
|
|
4652
4658
|
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
4653
4659
|
}
|
|
@@ -4661,7 +4667,11 @@ async function validatePublicUrl(url) {
|
|
|
4661
4667
|
} catch {
|
|
4662
4668
|
addresses = [];
|
|
4663
4669
|
}
|
|
4664
|
-
for (const { address, family } of addresses)
|
|
4670
|
+
for (const { address, family } of addresses) validatePublicIpAddress(address, family);
|
|
4671
|
+
}
|
|
4672
|
+
function validatePublicIpAddress(address, family) {
|
|
4673
|
+
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
4674
|
+
throw new UrlError(`Invalid or private address: ${address}`);
|
|
4665
4675
|
}
|
|
4666
4676
|
function isValidPublicIPv4Address(address) {
|
|
4667
4677
|
const parts = address.split(".");
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
//#region src/temporal.ts
|
|
4
|
+
/**
|
|
5
|
+
* Type guards for `Temporal` namespace objects.
|
|
6
|
+
*
|
|
7
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
8
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
9
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
10
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
11
|
+
* spec-conformant implementation regardless of which class produced the
|
|
12
|
+
* value.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
18
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
19
|
+
*
|
|
20
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
21
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
22
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
23
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
24
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
25
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
26
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
27
|
+
* timestamp.
|
|
28
|
+
*
|
|
29
|
+
* @param value The value to test.
|
|
30
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
31
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
32
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
33
|
+
*/
|
|
34
|
+
function isTemporalInstant(value) {
|
|
35
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Instant]" && "epochNanoseconds" in value && typeof value.epochNanoseconds === "bigint" && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
39
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
40
|
+
*
|
|
41
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
42
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
43
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
44
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
45
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
46
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
47
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
48
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
49
|
+
*
|
|
50
|
+
* @param value The value to test.
|
|
51
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
52
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
53
|
+
* and overrides `toString`; `false` otherwise.
|
|
54
|
+
*/
|
|
55
|
+
function isTemporalDuration(value) {
|
|
56
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Duration]" && "sign" in value && (value.sign === -1 || value.sign === 0 || value.sign === 1) && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
exports.isTemporalDuration = isTemporalDuration;
|
|
60
|
+
exports.isTemporalInstant = isTemporalInstant;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
//#region src/temporal.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Type guards for `Temporal` namespace objects.
|
|
5
|
+
*
|
|
6
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
7
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
8
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
9
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
10
|
+
* spec-conformant implementation regardless of which class produced the
|
|
11
|
+
* value.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
17
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
18
|
+
*
|
|
19
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
20
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
21
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
22
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
23
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
24
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
25
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
26
|
+
* timestamp.
|
|
27
|
+
*
|
|
28
|
+
* @param value The value to test.
|
|
29
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
30
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
31
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
32
|
+
*/
|
|
33
|
+
declare function isTemporalInstant(value: unknown): value is Temporal.Instant;
|
|
34
|
+
/**
|
|
35
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
36
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
37
|
+
*
|
|
38
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
39
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
40
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
41
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
42
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
43
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
44
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
45
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
46
|
+
*
|
|
47
|
+
* @param value The value to test.
|
|
48
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
49
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
50
|
+
* and overrides `toString`; `false` otherwise.
|
|
51
|
+
*/
|
|
52
|
+
declare function isTemporalDuration(value: unknown): value is Temporal.Duration;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { isTemporalDuration, isTemporalInstant };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
//#region src/temporal.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Type guards for `Temporal` namespace objects.
|
|
5
|
+
*
|
|
6
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
7
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
8
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
9
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
10
|
+
* spec-conformant implementation regardless of which class produced the
|
|
11
|
+
* value.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
17
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
18
|
+
*
|
|
19
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
20
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
21
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
22
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
23
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
24
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
25
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
26
|
+
* timestamp.
|
|
27
|
+
*
|
|
28
|
+
* @param value The value to test.
|
|
29
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
30
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
31
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
32
|
+
*/
|
|
33
|
+
declare function isTemporalInstant(value: unknown): value is Temporal.Instant;
|
|
34
|
+
/**
|
|
35
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
36
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
37
|
+
*
|
|
38
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
39
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
40
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
41
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
42
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
43
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
44
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
45
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
46
|
+
*
|
|
47
|
+
* @param value The value to test.
|
|
48
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
49
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
50
|
+
* and overrides `toString`; `false` otherwise.
|
|
51
|
+
*/
|
|
52
|
+
declare function isTemporalDuration(value: unknown): value is Temporal.Duration;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { isTemporalDuration, isTemporalInstant };
|
package/dist/temporal.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/temporal.ts
|
|
3
|
+
/**
|
|
4
|
+
* Type guards for `Temporal` namespace objects.
|
|
5
|
+
*
|
|
6
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
7
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
8
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
9
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
10
|
+
* spec-conformant implementation regardless of which class produced the
|
|
11
|
+
* value.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
17
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
18
|
+
*
|
|
19
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
20
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
21
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
22
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
23
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
24
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
25
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
26
|
+
* timestamp.
|
|
27
|
+
*
|
|
28
|
+
* @param value The value to test.
|
|
29
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
30
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
31
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
32
|
+
*/
|
|
33
|
+
function isTemporalInstant(value) {
|
|
34
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Instant]" && "epochNanoseconds" in value && typeof value.epochNanoseconds === "bigint" && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
38
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
39
|
+
*
|
|
40
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
41
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
42
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
43
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
44
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
45
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
46
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
47
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
48
|
+
*
|
|
49
|
+
* @param value The value to test.
|
|
50
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
51
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
52
|
+
* and overrides `toString`; `false` otherwise.
|
|
53
|
+
*/
|
|
54
|
+
function isTemporalDuration(value) {
|
|
55
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Duration]" && "sign" in value && (value.sign === -1 || value.sign === 0 || value.sign === 1) && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { isTemporalDuration, isTemporalInstant };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-BDej45SV.cjs");
|
|
3
3
|
const require_link = require("./link-FguCydMA.cjs");
|
|
4
|
-
const require_url = require("./url-
|
|
4
|
+
const require_url = require("./url-Cj9-Ycue.cjs");
|
|
5
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
6
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
7
7
|
//#region src/contexts/activitystreams.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BrFKK0nk.mjs";
|
|
2
2
|
import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
|
|
3
|
-
import { a as validatePublicUrl, t as UrlError } from "./url-
|
|
3
|
+
import { a as validatePublicUrl, t as UrlError } from "./url-BzGwIxB4.mjs";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
6
6
|
//#region src/contexts/activitystreams.json
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_docloader = require("./docloader-
|
|
3
|
-
const require_request = require("./request-
|
|
4
|
-
const require_url = require("./url-
|
|
2
|
+
const require_docloader = require("./docloader-CNFRvB3y.cjs");
|
|
3
|
+
const require_request = require("./request-BDej45SV.cjs");
|
|
4
|
+
const require_url = require("./url-Cj9-Ycue.cjs");
|
|
5
5
|
let node_assert = require("node:assert");
|
|
6
6
|
let node_test = require("node:test");
|
|
7
7
|
//#region ../../node_modules/.pnpm/glob-to-regexp@0.4.1/node_modules/glob-to-regexp/index.js
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as preloadedContexts, t as getDocumentLoader } from "./docloader-
|
|
2
|
-
import { t as FetchError } from "./request-
|
|
3
|
-
import { t as UrlError } from "./url-
|
|
1
|
+
import { n as preloadedContexts, t as getDocumentLoader } from "./docloader-bbaLrO6y.mjs";
|
|
2
|
+
import { t as FetchError } from "./request-BrFKK0nk.mjs";
|
|
3
|
+
import { t as UrlError } from "./url-BzGwIxB4.mjs";
|
|
4
4
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
5
5
|
import { test } from "node:test";
|
|
6
6
|
//#region \0rolldown/runtime.js
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-BDej45SV.cjs");
|
|
3
3
|
let node_assert = require("node:assert");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
let node_process = require("node:process");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as version, r as getUserAgent } from "./request-
|
|
1
|
+
import { o as version, r as getUserAgent } from "./request-BrFKK0nk.mjs";
|
|
2
2
|
import { deepStrictEqual } from "node:assert";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import process from "node:process";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
require("./chunk-Do9eywBl.cjs");
|
|
2
|
+
let node_assert = require("node:assert");
|
|
3
|
+
let node_test = require("node:test");
|
|
4
|
+
let _js_temporal_polyfill = require("@js-temporal/polyfill");
|
|
5
|
+
//#region src/temporal.ts
|
|
6
|
+
/**
|
|
7
|
+
* Type guards for `Temporal` namespace objects.
|
|
8
|
+
*
|
|
9
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
10
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
11
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
12
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
13
|
+
* spec-conformant implementation regardless of which class produced the
|
|
14
|
+
* value.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
20
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
21
|
+
*
|
|
22
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
23
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
24
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
25
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
26
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
27
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
28
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
29
|
+
* timestamp.
|
|
30
|
+
*
|
|
31
|
+
* @param value The value to test.
|
|
32
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
33
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
34
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
35
|
+
*/
|
|
36
|
+
function isTemporalInstant(value) {
|
|
37
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Instant]" && "epochNanoseconds" in value && typeof value.epochNanoseconds === "bigint" && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
41
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
42
|
+
*
|
|
43
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
44
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
45
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
46
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
47
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
48
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
49
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
50
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
51
|
+
*
|
|
52
|
+
* @param value The value to test.
|
|
53
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
54
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
55
|
+
* and overrides `toString`; `false` otherwise.
|
|
56
|
+
*/
|
|
57
|
+
function isTemporalDuration(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Duration]" && "sign" in value && (value.sign === -1 || value.sign === 0 || value.sign === 1) && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/temporal.test.ts
|
|
62
|
+
(0, node_test.test)("isTemporalInstant() accepts polyfill instances", () => {
|
|
63
|
+
(0, node_assert.strictEqual)(isTemporalInstant(_js_temporal_polyfill.Temporal.Instant.from("2026-05-14T00:00:00Z")), true);
|
|
64
|
+
});
|
|
65
|
+
(0, node_test.test)("isTemporalInstant() accepts spec-compliant non-polyfill objects", () => {
|
|
66
|
+
(0, node_assert.strictEqual)(isTemporalInstant(Object.create(null, {
|
|
67
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
68
|
+
epochNanoseconds: { value: 0n },
|
|
69
|
+
toString: { value: () => "1970-01-01T00:00:00Z" }
|
|
70
|
+
})), true);
|
|
71
|
+
});
|
|
72
|
+
(0, node_test.test)("isTemporalInstant() rejects unrelated values", () => {
|
|
73
|
+
(0, node_assert.strictEqual)(isTemporalInstant(null), false);
|
|
74
|
+
(0, node_assert.strictEqual)(isTemporalInstant(void 0), false);
|
|
75
|
+
(0, node_assert.strictEqual)(isTemporalInstant("2026-05-14T00:00:00Z"), false);
|
|
76
|
+
(0, node_assert.strictEqual)(isTemporalInstant(/* @__PURE__ */ new Date()), false);
|
|
77
|
+
(0, node_assert.strictEqual)(isTemporalInstant(_js_temporal_polyfill.Temporal.Duration.from({ seconds: 1 })), false);
|
|
78
|
+
});
|
|
79
|
+
(0, node_test.test)("isTemporalInstant() rejects bare objects tagged but missing shape", () => {
|
|
80
|
+
(0, node_assert.strictEqual)(isTemporalInstant(Object.create(null, { [Symbol.toStringTag]: { value: "Temporal.Instant" } })), false);
|
|
81
|
+
});
|
|
82
|
+
(0, node_test.test)("isTemporalInstant() rejects non-bigint epochNanoseconds", () => {
|
|
83
|
+
(0, node_assert.strictEqual)(isTemporalInstant(Object.create(null, {
|
|
84
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
85
|
+
epochNanoseconds: { value: 0 },
|
|
86
|
+
toString: { value: () => "1970-01-01T00:00:00Z" }
|
|
87
|
+
})), false);
|
|
88
|
+
});
|
|
89
|
+
(0, node_test.test)("isTemporalInstant() rejects default Object.prototype.toString", () => {
|
|
90
|
+
(0, node_assert.strictEqual)(isTemporalInstant({
|
|
91
|
+
[Symbol.toStringTag]: "Temporal.Instant",
|
|
92
|
+
epochNanoseconds: 0n
|
|
93
|
+
}), false);
|
|
94
|
+
});
|
|
95
|
+
(0, node_test.test)("isTemporalDuration() accepts polyfill instances", () => {
|
|
96
|
+
(0, node_assert.strictEqual)(isTemporalDuration(_js_temporal_polyfill.Temporal.Duration.from({ hours: 1 })), true);
|
|
97
|
+
});
|
|
98
|
+
(0, node_test.test)("isTemporalDuration() accepts spec-compliant non-polyfill objects", () => {
|
|
99
|
+
(0, node_assert.strictEqual)(isTemporalDuration(Object.create(null, {
|
|
100
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
101
|
+
sign: { value: 0 },
|
|
102
|
+
toString: { value: () => "PT0S" }
|
|
103
|
+
})), true);
|
|
104
|
+
});
|
|
105
|
+
(0, node_test.test)("isTemporalDuration() rejects unrelated values", () => {
|
|
106
|
+
(0, node_assert.strictEqual)(isTemporalDuration(null), false);
|
|
107
|
+
(0, node_assert.strictEqual)(isTemporalDuration(void 0), false);
|
|
108
|
+
(0, node_assert.strictEqual)(isTemporalDuration("PT1H"), false);
|
|
109
|
+
(0, node_assert.strictEqual)(isTemporalDuration(_js_temporal_polyfill.Temporal.Instant.from("2026-05-14T00:00:00Z")), false);
|
|
110
|
+
});
|
|
111
|
+
(0, node_test.test)("isTemporalDuration() rejects bare objects tagged but missing shape", () => {
|
|
112
|
+
(0, node_assert.strictEqual)(isTemporalDuration(Object.create(null, { [Symbol.toStringTag]: { value: "Temporal.Duration" } })), false);
|
|
113
|
+
});
|
|
114
|
+
(0, node_test.test)("isTemporalDuration() rejects non-number sign", () => {
|
|
115
|
+
(0, node_assert.strictEqual)(isTemporalDuration(Object.create(null, {
|
|
116
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
117
|
+
sign: { value: "0" },
|
|
118
|
+
toString: { value: () => "PT0S" }
|
|
119
|
+
})), false);
|
|
120
|
+
});
|
|
121
|
+
(0, node_test.test)("isTemporalDuration() rejects out-of-range sign values", () => {
|
|
122
|
+
(0, node_assert.strictEqual)(isTemporalDuration(Object.create(null, {
|
|
123
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
124
|
+
sign: { value: 42 },
|
|
125
|
+
toString: { value: () => "PT42S" }
|
|
126
|
+
})), false);
|
|
127
|
+
});
|
|
128
|
+
(0, node_test.test)("isTemporalDuration() rejects default Object.prototype.toString", () => {
|
|
129
|
+
(0, node_assert.strictEqual)(isTemporalDuration({
|
|
130
|
+
[Symbol.toStringTag]: "Temporal.Duration",
|
|
131
|
+
sign: 0
|
|
132
|
+
}), false);
|
|
133
|
+
});
|
|
134
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { strictEqual } from "node:assert";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
4
|
+
//#region src/temporal.ts
|
|
5
|
+
/**
|
|
6
|
+
* Type guards for `Temporal` namespace objects.
|
|
7
|
+
*
|
|
8
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
9
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
10
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
11
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
12
|
+
* spec-conformant implementation regardless of which class produced the
|
|
13
|
+
* value.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
19
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
20
|
+
*
|
|
21
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
22
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
23
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
24
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
25
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
26
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
27
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
28
|
+
* timestamp.
|
|
29
|
+
*
|
|
30
|
+
* @param value The value to test.
|
|
31
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
32
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
33
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
34
|
+
*/
|
|
35
|
+
function isTemporalInstant(value) {
|
|
36
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Instant]" && "epochNanoseconds" in value && typeof value.epochNanoseconds === "bigint" && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
40
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
41
|
+
*
|
|
42
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
43
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
44
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
45
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
46
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
47
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
48
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
49
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
50
|
+
*
|
|
51
|
+
* @param value The value to test.
|
|
52
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
53
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
54
|
+
* and overrides `toString`; `false` otherwise.
|
|
55
|
+
*/
|
|
56
|
+
function isTemporalDuration(value) {
|
|
57
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Temporal.Duration]" && "sign" in value && (value.sign === -1 || value.sign === 0 || value.sign === 1) && "toString" in value && typeof value.toString === "function" && value.toString !== Object.prototype.toString;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/temporal.test.ts
|
|
61
|
+
test("isTemporalInstant() accepts polyfill instances", () => {
|
|
62
|
+
strictEqual(isTemporalInstant(Temporal.Instant.from("2026-05-14T00:00:00Z")), true);
|
|
63
|
+
});
|
|
64
|
+
test("isTemporalInstant() accepts spec-compliant non-polyfill objects", () => {
|
|
65
|
+
strictEqual(isTemporalInstant(Object.create(null, {
|
|
66
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
67
|
+
epochNanoseconds: { value: 0n },
|
|
68
|
+
toString: { value: () => "1970-01-01T00:00:00Z" }
|
|
69
|
+
})), true);
|
|
70
|
+
});
|
|
71
|
+
test("isTemporalInstant() rejects unrelated values", () => {
|
|
72
|
+
strictEqual(isTemporalInstant(null), false);
|
|
73
|
+
strictEqual(isTemporalInstant(void 0), false);
|
|
74
|
+
strictEqual(isTemporalInstant("2026-05-14T00:00:00Z"), false);
|
|
75
|
+
strictEqual(isTemporalInstant(/* @__PURE__ */ new Date()), false);
|
|
76
|
+
strictEqual(isTemporalInstant(Temporal.Duration.from({ seconds: 1 })), false);
|
|
77
|
+
});
|
|
78
|
+
test("isTemporalInstant() rejects bare objects tagged but missing shape", () => {
|
|
79
|
+
strictEqual(isTemporalInstant(Object.create(null, { [Symbol.toStringTag]: { value: "Temporal.Instant" } })), false);
|
|
80
|
+
});
|
|
81
|
+
test("isTemporalInstant() rejects non-bigint epochNanoseconds", () => {
|
|
82
|
+
strictEqual(isTemporalInstant(Object.create(null, {
|
|
83
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
84
|
+
epochNanoseconds: { value: 0 },
|
|
85
|
+
toString: { value: () => "1970-01-01T00:00:00Z" }
|
|
86
|
+
})), false);
|
|
87
|
+
});
|
|
88
|
+
test("isTemporalInstant() rejects default Object.prototype.toString", () => {
|
|
89
|
+
strictEqual(isTemporalInstant({
|
|
90
|
+
[Symbol.toStringTag]: "Temporal.Instant",
|
|
91
|
+
epochNanoseconds: 0n
|
|
92
|
+
}), false);
|
|
93
|
+
});
|
|
94
|
+
test("isTemporalDuration() accepts polyfill instances", () => {
|
|
95
|
+
strictEqual(isTemporalDuration(Temporal.Duration.from({ hours: 1 })), true);
|
|
96
|
+
});
|
|
97
|
+
test("isTemporalDuration() accepts spec-compliant non-polyfill objects", () => {
|
|
98
|
+
strictEqual(isTemporalDuration(Object.create(null, {
|
|
99
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
100
|
+
sign: { value: 0 },
|
|
101
|
+
toString: { value: () => "PT0S" }
|
|
102
|
+
})), true);
|
|
103
|
+
});
|
|
104
|
+
test("isTemporalDuration() rejects unrelated values", () => {
|
|
105
|
+
strictEqual(isTemporalDuration(null), false);
|
|
106
|
+
strictEqual(isTemporalDuration(void 0), false);
|
|
107
|
+
strictEqual(isTemporalDuration("PT1H"), false);
|
|
108
|
+
strictEqual(isTemporalDuration(Temporal.Instant.from("2026-05-14T00:00:00Z")), false);
|
|
109
|
+
});
|
|
110
|
+
test("isTemporalDuration() rejects bare objects tagged but missing shape", () => {
|
|
111
|
+
strictEqual(isTemporalDuration(Object.create(null, { [Symbol.toStringTag]: { value: "Temporal.Duration" } })), false);
|
|
112
|
+
});
|
|
113
|
+
test("isTemporalDuration() rejects non-number sign", () => {
|
|
114
|
+
strictEqual(isTemporalDuration(Object.create(null, {
|
|
115
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
116
|
+
sign: { value: "0" },
|
|
117
|
+
toString: { value: () => "PT0S" }
|
|
118
|
+
})), false);
|
|
119
|
+
});
|
|
120
|
+
test("isTemporalDuration() rejects out-of-range sign values", () => {
|
|
121
|
+
strictEqual(isTemporalDuration(Object.create(null, {
|
|
122
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
123
|
+
sign: { value: 42 },
|
|
124
|
+
toString: { value: () => "PT42S" }
|
|
125
|
+
})), false);
|
|
126
|
+
});
|
|
127
|
+
test("isTemporalDuration() rejects default Object.prototype.toString", () => {
|
|
128
|
+
strictEqual(isTemporalDuration({
|
|
129
|
+
[Symbol.toStringTag]: "Temporal.Duration",
|
|
130
|
+
sign: 0
|
|
131
|
+
}), false);
|
|
132
|
+
});
|
|
133
|
+
//#endregion
|
|
134
|
+
export {};
|
|
@@ -14,8 +14,13 @@ async function validatePublicUrl(url) {
|
|
|
14
14
|
const parsed = new URL(url);
|
|
15
15
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
16
16
|
let hostname = parsed.hostname;
|
|
17
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.
|
|
17
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
18
18
|
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
19
|
+
const hostnameFamily = isIP(hostname);
|
|
20
|
+
if (hostnameFamily !== 0) {
|
|
21
|
+
validatePublicIpAddress(hostname, hostnameFamily);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
19
24
|
if ("Deno" in globalThis && !isIP(hostname)) {
|
|
20
25
|
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
21
26
|
}
|
|
@@ -29,7 +34,11 @@ async function validatePublicUrl(url) {
|
|
|
29
34
|
} catch {
|
|
30
35
|
addresses = [];
|
|
31
36
|
}
|
|
32
|
-
for (const { address, family } of addresses)
|
|
37
|
+
for (const { address, family } of addresses) validatePublicIpAddress(address, family);
|
|
38
|
+
}
|
|
39
|
+
function validatePublicIpAddress(address, family) {
|
|
40
|
+
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
41
|
+
throw new UrlError(`Invalid or private address: ${address}`);
|
|
33
42
|
}
|
|
34
43
|
function isValidPublicIPv4Address(address) {
|
|
35
44
|
const parts = address.split(".");
|
|
@@ -15,8 +15,13 @@ async function validatePublicUrl(url) {
|
|
|
15
15
|
const parsed = new URL(url);
|
|
16
16
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
17
17
|
let hostname = parsed.hostname;
|
|
18
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.
|
|
18
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
19
19
|
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
20
|
+
const hostnameFamily = (0, node_net.isIP)(hostname);
|
|
21
|
+
if (hostnameFamily !== 0) {
|
|
22
|
+
validatePublicIpAddress(hostname, hostnameFamily);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
20
25
|
if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
|
|
21
26
|
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
22
27
|
}
|
|
@@ -30,7 +35,11 @@ async function validatePublicUrl(url) {
|
|
|
30
35
|
} catch {
|
|
31
36
|
addresses = [];
|
|
32
37
|
}
|
|
33
|
-
for (const { address, family } of addresses)
|
|
38
|
+
for (const { address, family } of addresses) validatePublicIpAddress(address, family);
|
|
39
|
+
}
|
|
40
|
+
function validatePublicIpAddress(address, family) {
|
|
41
|
+
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
42
|
+
throw new UrlError(`Invalid or private address: ${address}`);
|
|
34
43
|
}
|
|
35
44
|
function isValidPublicIPv4Address(address) {
|
|
36
45
|
const parts = address.split(".");
|
package/dist/tests/url.test.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_url = require("./url-
|
|
2
|
+
const require_url = require("./url-Cj9-Ycue.cjs");
|
|
3
3
|
let node_assert = require("node:assert");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
//#region src/url.test.ts
|
|
@@ -9,6 +9,8 @@ let node_test = require("node:test");
|
|
|
9
9
|
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://localhost"), require_url.UrlError);
|
|
10
10
|
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://127.0.0.1"), require_url.UrlError);
|
|
11
11
|
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://[::1]"), require_url.UrlError);
|
|
12
|
+
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("http://[::ffff:7f00:1]/"), require_url.UrlError);
|
|
13
|
+
await require_url.validatePublicUrl("https://[2001:db8::1]");
|
|
12
14
|
});
|
|
13
15
|
(0, node_test.test)("isValidPublicIPv4Address()", () => {
|
|
14
16
|
(0, node_assert.ok)(require_url.isValidPublicIPv4Address("8.8.8.8"));
|
|
@@ -25,6 +27,7 @@ let node_test = require("node:test");
|
|
|
25
27
|
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("fe80::1"));
|
|
26
28
|
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("ff00::1"));
|
|
27
29
|
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("::"));
|
|
30
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("::ffff:7f00:1"));
|
|
28
31
|
});
|
|
29
32
|
(0, node_test.test)("expandIPv6Address()", () => {
|
|
30
33
|
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("::"), "0000:0000:0000:0000:0000:0000:0000:0000");
|
package/dist/tests/url.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as validatePublicUrl, i as isValidPublicIPv6Address, n as expandIPv6Address, r as isValidPublicIPv4Address, t as UrlError } from "./url-
|
|
1
|
+
import { a as validatePublicUrl, i as isValidPublicIPv6Address, n as expandIPv6Address, r as isValidPublicIPv4Address, t as UrlError } from "./url-BzGwIxB4.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
//#region src/url.test.ts
|
|
@@ -8,6 +8,8 @@ test("validatePublicUrl()", async () => {
|
|
|
8
8
|
await rejects(() => validatePublicUrl("https://localhost"), UrlError);
|
|
9
9
|
await rejects(() => validatePublicUrl("https://127.0.0.1"), UrlError);
|
|
10
10
|
await rejects(() => validatePublicUrl("https://[::1]"), UrlError);
|
|
11
|
+
await rejects(() => validatePublicUrl("http://[::ffff:7f00:1]/"), UrlError);
|
|
12
|
+
await validatePublicUrl("https://[2001:db8::1]");
|
|
11
13
|
});
|
|
12
14
|
test("isValidPublicIPv4Address()", () => {
|
|
13
15
|
ok(isValidPublicIPv4Address("8.8.8.8"));
|
|
@@ -24,6 +26,7 @@ test("isValidPublicIPv6Address()", () => {
|
|
|
24
26
|
ok(!isValidPublicIPv6Address("fe80::1"));
|
|
25
27
|
ok(!isValidPublicIPv6Address("ff00::1"));
|
|
26
28
|
ok(!isValidPublicIPv6Address("::"));
|
|
29
|
+
ok(!isValidPublicIPv6Address("::ffff:7f00:1"));
|
|
27
30
|
});
|
|
28
31
|
test("expandIPv6Address()", () => {
|
|
29
32
|
deepStrictEqual(expandIPv6Address("::"), "0000:0000:0000:0000:0000:0000:0000:0000");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/vocab-runtime",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"homepage": "https://fedify.dev/",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,6 +45,16 @@
|
|
|
45
45
|
"require": "./dist/jsonld.cjs",
|
|
46
46
|
"default": "./dist/jsonld.js"
|
|
47
47
|
},
|
|
48
|
+
"./temporal": {
|
|
49
|
+
"types": {
|
|
50
|
+
"import": "./dist/temporal.d.ts",
|
|
51
|
+
"require": "./dist/temporal.d.cts",
|
|
52
|
+
"default": "./dist/temporal.d.ts"
|
|
53
|
+
},
|
|
54
|
+
"import": "./dist/temporal.js",
|
|
55
|
+
"require": "./dist/temporal.cjs",
|
|
56
|
+
"default": "./dist/temporal.js"
|
|
57
|
+
},
|
|
48
58
|
"./package.json": "./package.json"
|
|
49
59
|
},
|
|
50
60
|
"keywords": [
|
|
@@ -62,9 +72,10 @@
|
|
|
62
72
|
"@types/node": "^24.2.1",
|
|
63
73
|
"fetch-mock": "^12.5.4",
|
|
64
74
|
"tsdown": "^0.21.6",
|
|
65
|
-
"typescript": "^
|
|
75
|
+
"typescript": "^6.0.0"
|
|
66
76
|
},
|
|
67
77
|
"dependencies": {
|
|
78
|
+
"@js-temporal/polyfill": "^0.5.1",
|
|
68
79
|
"@logtape/logtape": "^2.0.5",
|
|
69
80
|
"@multiformats/base-x": "^4.0.1",
|
|
70
81
|
"@opentelemetry/api": "^1.9.0",
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
2
|
+
import { strictEqual } from "node:assert";
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import { isTemporalDuration, isTemporalInstant } from "./temporal.ts";
|
|
5
|
+
|
|
6
|
+
test("isTemporalInstant() accepts polyfill instances", () => {
|
|
7
|
+
strictEqual(
|
|
8
|
+
isTemporalInstant(Temporal.Instant.from("2026-05-14T00:00:00Z")),
|
|
9
|
+
true,
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("isTemporalInstant() accepts spec-compliant non-polyfill objects", () => {
|
|
14
|
+
// Mimics the shape of a native `Temporal.Instant` from a host that does
|
|
15
|
+
// not share class identity with the bundled polyfill.
|
|
16
|
+
const nativeLike = Object.create(null, {
|
|
17
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
18
|
+
epochNanoseconds: { value: 0n },
|
|
19
|
+
toString: { value: () => "1970-01-01T00:00:00Z" },
|
|
20
|
+
});
|
|
21
|
+
strictEqual(isTemporalInstant(nativeLike), true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("isTemporalInstant() rejects unrelated values", () => {
|
|
25
|
+
strictEqual(isTemporalInstant(null), false);
|
|
26
|
+
strictEqual(isTemporalInstant(undefined), false);
|
|
27
|
+
strictEqual(isTemporalInstant("2026-05-14T00:00:00Z"), false);
|
|
28
|
+
strictEqual(isTemporalInstant(new Date()), false);
|
|
29
|
+
strictEqual(
|
|
30
|
+
isTemporalInstant(Temporal.Duration.from({ seconds: 1 })),
|
|
31
|
+
false,
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("isTemporalInstant() rejects bare objects tagged but missing shape", () => {
|
|
36
|
+
const decoy = Object.create(null, {
|
|
37
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
38
|
+
});
|
|
39
|
+
strictEqual(isTemporalInstant(decoy), false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("isTemporalInstant() rejects non-bigint epochNanoseconds", () => {
|
|
43
|
+
const decoy = Object.create(null, {
|
|
44
|
+
[Symbol.toStringTag]: { value: "Temporal.Instant" },
|
|
45
|
+
epochNanoseconds: { value: 0 },
|
|
46
|
+
toString: { value: () => "1970-01-01T00:00:00Z" },
|
|
47
|
+
});
|
|
48
|
+
strictEqual(isTemporalInstant(decoy), false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("isTemporalInstant() rejects default Object.prototype.toString", () => {
|
|
52
|
+
// A plain object inherits `toString` from `Object.prototype`, so calling
|
|
53
|
+
// it would produce `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
54
|
+
// timestamp. The guard must reject these to keep the serializer honest.
|
|
55
|
+
const decoy = {
|
|
56
|
+
[Symbol.toStringTag]: "Temporal.Instant",
|
|
57
|
+
epochNanoseconds: 0n,
|
|
58
|
+
};
|
|
59
|
+
strictEqual(isTemporalInstant(decoy), false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("isTemporalDuration() accepts polyfill instances", () => {
|
|
63
|
+
strictEqual(
|
|
64
|
+
isTemporalDuration(Temporal.Duration.from({ hours: 1 })),
|
|
65
|
+
true,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("isTemporalDuration() accepts spec-compliant non-polyfill objects", () => {
|
|
70
|
+
const nativeLike = Object.create(null, {
|
|
71
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
72
|
+
sign: { value: 0 },
|
|
73
|
+
toString: { value: () => "PT0S" },
|
|
74
|
+
});
|
|
75
|
+
strictEqual(isTemporalDuration(nativeLike), true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("isTemporalDuration() rejects unrelated values", () => {
|
|
79
|
+
strictEqual(isTemporalDuration(null), false);
|
|
80
|
+
strictEqual(isTemporalDuration(undefined), false);
|
|
81
|
+
strictEqual(isTemporalDuration("PT1H"), false);
|
|
82
|
+
strictEqual(
|
|
83
|
+
isTemporalDuration(Temporal.Instant.from("2026-05-14T00:00:00Z")),
|
|
84
|
+
false,
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("isTemporalDuration() rejects bare objects tagged but missing shape", () => {
|
|
89
|
+
const decoy = Object.create(null, {
|
|
90
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
91
|
+
});
|
|
92
|
+
strictEqual(isTemporalDuration(decoy), false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("isTemporalDuration() rejects non-number sign", () => {
|
|
96
|
+
const decoy = Object.create(null, {
|
|
97
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
98
|
+
sign: { value: "0" },
|
|
99
|
+
toString: { value: () => "PT0S" },
|
|
100
|
+
});
|
|
101
|
+
strictEqual(isTemporalDuration(decoy), false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("isTemporalDuration() rejects out-of-range sign values", () => {
|
|
105
|
+
// Real Temporal.Duration#sign is `-1 | 0 | 1` per spec, so anything else
|
|
106
|
+
// (here, 42) must be rejected even though it is a number.
|
|
107
|
+
const decoy = Object.create(null, {
|
|
108
|
+
[Symbol.toStringTag]: { value: "Temporal.Duration" },
|
|
109
|
+
sign: { value: 42 },
|
|
110
|
+
toString: { value: () => "PT42S" },
|
|
111
|
+
});
|
|
112
|
+
strictEqual(isTemporalDuration(decoy), false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("isTemporalDuration() rejects default Object.prototype.toString", () => {
|
|
116
|
+
const decoy = {
|
|
117
|
+
[Symbol.toStringTag]: "Temporal.Duration",
|
|
118
|
+
sign: 0,
|
|
119
|
+
};
|
|
120
|
+
strictEqual(isTemporalDuration(decoy), false);
|
|
121
|
+
});
|
package/src/temporal.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guards for `Temporal` namespace objects.
|
|
3
|
+
*
|
|
4
|
+
* Fedify accepts both runtime polyfills (e.g. `@js-temporal/polyfill`,
|
|
5
|
+
* `temporal-polyfill`) and the host's native `Temporal` implementation
|
|
6
|
+
* (Node.js 26+, Bun, Deno). The guards below rely on `Symbol.toStringTag`,
|
|
7
|
+
* which is mandated by the Temporal specification, so they accept any
|
|
8
|
+
* spec-conformant implementation regardless of which class produced the
|
|
9
|
+
* value.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Checks whether the given value is a `Temporal.Instant` object, regardless
|
|
16
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
17
|
+
*
|
|
18
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
19
|
+
* `epochNanoseconds` accessor exposes a `bigint`, and that `toString` is
|
|
20
|
+
* not the default inherited from `Object.prototype`. Together they reject
|
|
21
|
+
* bare objects whose tag was set to `"Temporal.Instant"` without exposing
|
|
22
|
+
* the rest of the shape; the `toString` check in particular prevents a
|
|
23
|
+
* spoof from reaching the JSON-LD serializer (which calls `toString()`)
|
|
24
|
+
* and emitting `"[object Temporal.Instant]"` instead of an RFC 3339
|
|
25
|
+
* timestamp.
|
|
26
|
+
*
|
|
27
|
+
* @param value The value to test.
|
|
28
|
+
* @returns `true` if the value reports `Temporal.Instant` via
|
|
29
|
+
* `Symbol.toStringTag`, exposes a `bigint`-valued
|
|
30
|
+
* `epochNanoseconds`, and overrides `toString`; `false` otherwise.
|
|
31
|
+
*/
|
|
32
|
+
export function isTemporalInstant(value: unknown): value is Temporal.Instant {
|
|
33
|
+
return (
|
|
34
|
+
typeof value === "object" &&
|
|
35
|
+
value !== null &&
|
|
36
|
+
Object.prototype.toString.call(value) === "[object Temporal.Instant]" &&
|
|
37
|
+
"epochNanoseconds" in value &&
|
|
38
|
+
typeof value.epochNanoseconds === "bigint" &&
|
|
39
|
+
"toString" in value &&
|
|
40
|
+
typeof value.toString === "function" &&
|
|
41
|
+
value.toString !== Object.prototype.toString
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Checks whether the given value is a `Temporal.Duration` object, regardless
|
|
47
|
+
* of whether it came from a polyfill or the host's native implementation.
|
|
48
|
+
*
|
|
49
|
+
* The guard verifies the spec-mandated `Symbol.toStringTag`, that the
|
|
50
|
+
* `sign` accessor returns one of the three spec-valid values (`-1`, `0`,
|
|
51
|
+
* or `1`), and that `toString` is not the default inherited from
|
|
52
|
+
* `Object.prototype`. Together they reject bare objects whose tag was set
|
|
53
|
+
* to `"Temporal.Duration"` without exposing the rest of the shape; the
|
|
54
|
+
* `toString` check in particular prevents a spoof from reaching the
|
|
55
|
+
* JSON-LD serializer (which calls `toString()`) and emitting
|
|
56
|
+
* `"[object Temporal.Duration]"` instead of an ISO 8601 duration.
|
|
57
|
+
*
|
|
58
|
+
* @param value The value to test.
|
|
59
|
+
* @returns `true` if the value reports `Temporal.Duration` via
|
|
60
|
+
* `Symbol.toStringTag`, exposes a `sign` of `-1`, `0`, or `1`,
|
|
61
|
+
* and overrides `toString`; `false` otherwise.
|
|
62
|
+
*/
|
|
63
|
+
export function isTemporalDuration(value: unknown): value is Temporal.Duration {
|
|
64
|
+
return (
|
|
65
|
+
typeof value === "object" &&
|
|
66
|
+
value !== null &&
|
|
67
|
+
Object.prototype.toString.call(value) === "[object Temporal.Duration]" &&
|
|
68
|
+
"sign" in value &&
|
|
69
|
+
(value.sign === -1 || value.sign === 0 || value.sign === 1) &&
|
|
70
|
+
"toString" in value &&
|
|
71
|
+
typeof value.toString === "function" &&
|
|
72
|
+
value.toString !== Object.prototype.toString
|
|
73
|
+
);
|
|
74
|
+
}
|
package/src/url.test.ts
CHANGED
|
@@ -19,6 +19,11 @@ test("validatePublicUrl()", async () => {
|
|
|
19
19
|
await rejects(() => validatePublicUrl("https://localhost"), UrlError);
|
|
20
20
|
await rejects(() => validatePublicUrl("https://127.0.0.1"), UrlError);
|
|
21
21
|
await rejects(() => validatePublicUrl("https://[::1]"), UrlError);
|
|
22
|
+
await rejects(
|
|
23
|
+
() => validatePublicUrl("http://[::ffff:7f00:1]/"),
|
|
24
|
+
UrlError,
|
|
25
|
+
);
|
|
26
|
+
await validatePublicUrl("https://[2001:db8::1]");
|
|
22
27
|
});
|
|
23
28
|
|
|
24
29
|
test("isValidPublicIPv4Address()", () => {
|
|
@@ -37,6 +42,7 @@ test("isValidPublicIPv6Address()", () => {
|
|
|
37
42
|
ok(!isValidPublicIPv6Address("fe80::1")); // link-local
|
|
38
43
|
ok(!isValidPublicIPv6Address("ff00::1")); // multicast
|
|
39
44
|
ok(!isValidPublicIPv6Address("::")); // unspecified
|
|
45
|
+
ok(!isValidPublicIPv6Address("::ffff:7f00:1")); // IPv4-mapped
|
|
40
46
|
});
|
|
41
47
|
|
|
42
48
|
test("expandIPv6Address()", () => {
|
package/src/url.ts
CHANGED
|
@@ -19,11 +19,16 @@ export async function validatePublicUrl(url: string): Promise<void> {
|
|
|
19
19
|
}
|
|
20
20
|
let hostname = parsed.hostname;
|
|
21
21
|
if (hostname.startsWith("[") && hostname.endsWith("]")) {
|
|
22
|
-
hostname = hostname.
|
|
22
|
+
hostname = hostname.slice(1, -1);
|
|
23
23
|
}
|
|
24
24
|
if (hostname === "localhost") {
|
|
25
25
|
throw new UrlError("Localhost is not allowed");
|
|
26
26
|
}
|
|
27
|
+
const hostnameFamily = isIP(hostname);
|
|
28
|
+
if (hostnameFamily !== 0) {
|
|
29
|
+
validatePublicIpAddress(hostname, hostnameFamily);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
27
32
|
if ("Deno" in globalThis && !isIP(hostname)) {
|
|
28
33
|
// If the `net` permission is not granted, we can't resolve the hostname.
|
|
29
34
|
// However, we can safely assume that it cannot gain access to private
|
|
@@ -50,14 +55,18 @@ export async function validatePublicUrl(url: string): Promise<void> {
|
|
|
50
55
|
addresses = [];
|
|
51
56
|
}
|
|
52
57
|
for (const { address, family } of addresses) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
validatePublicIpAddress(address, family);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function validatePublicIpAddress(address: string, family: number): void {
|
|
63
|
+
if (
|
|
64
|
+
family === 4 && isValidPublicIPv4Address(address) ||
|
|
65
|
+
family === 6 && isValidPublicIPv6Address(address)
|
|
66
|
+
) {
|
|
67
|
+
return;
|
|
60
68
|
}
|
|
69
|
+
throw new UrlError(`Invalid or private address: ${address}`);
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
export function isValidPublicIPv4Address(address: string): boolean {
|
package/tsdown.config.ts
CHANGED
|
@@ -4,11 +4,14 @@ import { defineConfig } from "tsdown";
|
|
|
4
4
|
|
|
5
5
|
export default [
|
|
6
6
|
defineConfig({
|
|
7
|
-
entry: ["src/mod.ts", "src/jsonld.ts"],
|
|
7
|
+
entry: ["src/mod.ts", "src/jsonld.ts", "src/temporal.ts"],
|
|
8
8
|
dts: { compilerOptions: { isolatedDeclarations: true, declaration: true } },
|
|
9
9
|
format: ["esm", "cjs"],
|
|
10
10
|
platform: "neutral",
|
|
11
11
|
external: [/^node:/],
|
|
12
|
+
banner: {
|
|
13
|
+
dts: `/// <reference lib="esnext.temporal" />`,
|
|
14
|
+
},
|
|
12
15
|
}),
|
|
13
16
|
defineConfig({
|
|
14
17
|
outDir: "dist/tests",
|