@fedify/vocab-runtime 2.0.0-dev.100

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/src/mod.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * This package contains the runtime facilities for working with Activity
3
+ * Vocabulary objects, which are auto-generated from the IDL.
4
+ *
5
+ * @module
6
+ */
7
+ export { default as preloadedContexts } from "./contexts.ts";
8
+ export {
9
+ type AuthenticatedDocumentLoaderFactory,
10
+ type DocumentLoader,
11
+ type DocumentLoaderFactory,
12
+ type DocumentLoaderFactoryOptions,
13
+ type DocumentLoaderOptions,
14
+ getDocumentLoader,
15
+ type GetDocumentLoaderOptions,
16
+ getRemoteDocument,
17
+ type RemoteDocument,
18
+ } from "./docloader.ts";
19
+ export {
20
+ exportMultibaseKey,
21
+ exportSpki,
22
+ importMultibaseKey,
23
+ importPem,
24
+ importPkcs1,
25
+ importSpki,
26
+ } from "./key.ts";
27
+ export { LanguageString } from "./langstr.ts";
28
+ export {
29
+ decodeMultibase,
30
+ encodeMultibase,
31
+ encodingFromBaseData,
32
+ } from "./multibase/mod.ts";
33
+ export {
34
+ createActivityPubRequest,
35
+ type CreateRequestOptions,
36
+ FetchError,
37
+ getUserAgent,
38
+ type GetUserAgentOptions,
39
+ logRequest,
40
+ } from "./request.ts";
41
+ export {
42
+ expandIPv6Address,
43
+ isValidPublicIPv4Address,
44
+ isValidPublicIPv6Address,
45
+ UrlError,
46
+ validatePublicUrl,
47
+ } from "./url.ts";
@@ -0,0 +1,34 @@
1
+ import { encodeText } from "./util.ts";
2
+ import type { BaseCode, BaseName, Codec, CodecFactory } from "./types.d.ts";
3
+
4
+ /**
5
+ * Class to encode/decode in the supported Bases
6
+ */
7
+ export class Base {
8
+ public codeBuf: Uint8Array;
9
+ codec: Codec;
10
+
11
+ constructor(
12
+ public name: BaseName,
13
+ private code: BaseCode,
14
+ factory: CodecFactory,
15
+ private alphabet: string,
16
+ ) {
17
+ this.codeBuf = encodeText(this.code);
18
+ this.alphabet = alphabet;
19
+ this.codec = factory(alphabet);
20
+ }
21
+
22
+ encode(buf: Uint8Array): string {
23
+ return this.codec.encode(buf);
24
+ }
25
+
26
+ decode(string: string): Uint8Array {
27
+ for (const char of string) {
28
+ if (this.alphabet && this.alphabet.indexOf(char) < 0) {
29
+ throw new Error(`invalid character '${char}' in '${string}'`);
30
+ }
31
+ }
32
+ return this.codec.decode(string);
33
+ }
34
+ }
@@ -0,0 +1,89 @@
1
+ import baseX from "@multiformats/base-x";
2
+ import { Base } from "./base.ts";
3
+ import { rfc4648 } from "./rfc4648.ts";
4
+ import { decodeText, encodeText } from "./util.ts";
5
+ import type { BaseCode, BaseName, CodecFactory } from "./types.d.ts";
6
+
7
+ const identity: CodecFactory = () => {
8
+ return {
9
+ encode: decodeText,
10
+ decode: encodeText,
11
+ };
12
+ };
13
+
14
+ /**
15
+ * name, code, implementation, alphabet
16
+ *
17
+ * @type {Array<[BaseName, BaseCode, CodecFactory, string]>}
18
+ */
19
+ const constants: Array<[BaseName, BaseCode, CodecFactory, string]> = [
20
+ ["identity", "\x00", identity, ""],
21
+ ["base2", "0", rfc4648(1), "01"],
22
+ ["base8", "7", rfc4648(3), "01234567"],
23
+ ["base10", "9", baseX, "0123456789"],
24
+ ["base16", "f", rfc4648(4), "0123456789abcdef"],
25
+ ["base16upper", "F", rfc4648(4), "0123456789ABCDEF"],
26
+ ["base32hex", "v", rfc4648(5), "0123456789abcdefghijklmnopqrstuv"],
27
+ ["base32hexupper", "V", rfc4648(5), "0123456789ABCDEFGHIJKLMNOPQRSTUV"],
28
+ ["base32hexpad", "t", rfc4648(5), "0123456789abcdefghijklmnopqrstuv="],
29
+ ["base32hexpadupper", "T", rfc4648(5), "0123456789ABCDEFGHIJKLMNOPQRSTUV="],
30
+ ["base32", "b", rfc4648(5), "abcdefghijklmnopqrstuvwxyz234567"],
31
+ ["base32upper", "B", rfc4648(5), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"],
32
+ ["base32pad", "c", rfc4648(5), "abcdefghijklmnopqrstuvwxyz234567="],
33
+ ["base32padupper", "C", rfc4648(5), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="],
34
+ ["base32z", "h", rfc4648(5), "ybndrfg8ejkmcpqxot1uwisza345h769"],
35
+ ["base36", "k", baseX, "0123456789abcdefghijklmnopqrstuvwxyz"],
36
+ ["base36upper", "K", baseX, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"],
37
+ [
38
+ "base58btc",
39
+ "z",
40
+ baseX,
41
+ "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
42
+ ],
43
+ [
44
+ "base58flickr",
45
+ "Z",
46
+ baseX,
47
+ "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ",
48
+ ],
49
+ [
50
+ "base64",
51
+ "m",
52
+ rfc4648(6),
53
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
54
+ ],
55
+ [
56
+ "base64pad",
57
+ "M",
58
+ rfc4648(6),
59
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
60
+ ],
61
+ [
62
+ "base64url",
63
+ "u",
64
+ rfc4648(6),
65
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
66
+ ],
67
+ [
68
+ "base64urlpad",
69
+ "U",
70
+ rfc4648(6),
71
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",
72
+ ],
73
+ ];
74
+
75
+ export const names = constants.reduce<Record<BaseName, Base>>(
76
+ (prev, tupple) => {
77
+ prev[tupple[0]] = new Base(tupple[0], tupple[1], tupple[2], tupple[3]);
78
+ return prev;
79
+ },
80
+ {} as Record<BaseName, Base>,
81
+ );
82
+
83
+ export const codes = constants.reduce<Record<BaseCode, Base>>(
84
+ (prev, tupple) => {
85
+ prev[tupple[1]] = names[tupple[0]];
86
+ return prev;
87
+ },
88
+ {} as Record<BaseCode, Base>,
89
+ );
@@ -0,0 +1,82 @@
1
+ import type { Base } from "./base.ts";
2
+ import * as constants from "./constants.ts";
3
+ import type { BaseCode, BaseName, BaseNameOrCode } from "./types.d.ts";
4
+ import { concat, decodeText, encodeText } from "./util.ts";
5
+
6
+ /**
7
+ * Encode data with the specified base and add the multibase prefix.
8
+ *
9
+ * @throws {Error} Will throw if the encoding is not supported
10
+ */
11
+ export function encodeMultibase(
12
+ nameOrCode: BaseNameOrCode,
13
+ buf: Uint8Array,
14
+ ): Uint8Array {
15
+ const enc = encoding(nameOrCode);
16
+ const data = encodeText(enc.encode(buf));
17
+
18
+ return concat([enc.codeBuf, data], enc.codeBuf.length + data.length);
19
+ }
20
+
21
+ /**
22
+ * Takes a Uint8Array or string encoded with multibase header, decodes it and
23
+ * returns the decoded buffer
24
+ *
25
+ * @throws {Error} Will throw if the encoding is not supported
26
+ */
27
+ export function decodeMultibase(data: Uint8Array | string): Uint8Array {
28
+ if (data instanceof Uint8Array) {
29
+ data = decodeText(data);
30
+ }
31
+ const prefix = data[0];
32
+
33
+ // Make all encodings case-insensitive except the ones that include upper and lower chars in the alphabet
34
+ if (
35
+ ["f", "F", "v", "V", "t", "T", "b", "B", "c", "C", "h", "k", "K"].includes(
36
+ prefix,
37
+ )
38
+ ) {
39
+ data = data.toLowerCase();
40
+ }
41
+ const enc = encoding(data[0] as BaseCode);
42
+ return enc.decode(data.substring(1));
43
+ }
44
+
45
+ /**
46
+ * Get the encoding by name or code
47
+ * @throws {Error} Will throw if the encoding is not supported
48
+ */
49
+ function encoding(nameOrCode: BaseNameOrCode): Base {
50
+ if (
51
+ Object.prototype.hasOwnProperty.call(
52
+ constants.names,
53
+ nameOrCode as BaseName,
54
+ )
55
+ ) {
56
+ return constants.names[nameOrCode as BaseName];
57
+ } else if (
58
+ Object.prototype.hasOwnProperty.call(
59
+ constants.codes,
60
+ /** @type {BaseCode} */ (nameOrCode),
61
+ )
62
+ ) {
63
+ return constants.codes[nameOrCode as BaseCode];
64
+ } else {
65
+ throw new Error(`Unsupported encoding: ${nameOrCode}`);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Get encoding multibase from data
71
+ *
72
+ * @param {string|Uint8Array} data
73
+ * @returns {Base}
74
+ * @throws {Error} Will throw if the encoding is not supported
75
+ */
76
+ export function encodingFromBaseData(data: string | Uint8Array): Base {
77
+ if (data instanceof Uint8Array) {
78
+ data = decodeText(data);
79
+ }
80
+
81
+ return encoding(data[0] as BaseCode);
82
+ }
@@ -0,0 +1,117 @@
1
+ import { deepStrictEqual } from "node:assert";
2
+ import { test } from "node:test";
3
+ import * as constants from "./constants.ts";
4
+ import { decodeMultibase, encodeMultibase } from "./mod.ts";
5
+ import type { BaseName } from "./types.d.ts";
6
+ import { decodeText, encodeText } from "./util.ts";
7
+
8
+ test("multibase.encode and decode", async (t) => {
9
+ const testCases: Array<[BaseName, string, string]> = [
10
+ ["base16", decodeText(Uint8Array.from([0x01])), "f01"],
11
+ ["base16", decodeText(Uint8Array.from([15])), "f0f"],
12
+ ["base16", "f", "f66"],
13
+ ["base16", "fo", "f666f"],
14
+ ["base16", "foo", "f666f6f"],
15
+ ["base16", "foob", "f666f6f62"],
16
+ ["base16", "fooba", "f666f6f6261"],
17
+ ["base16", "foobar", "f666f6f626172"],
18
+ ["base32", "yes mani !", "bpfsxgidnmfxgsibb"],
19
+ ["base32", "f", "bmy"],
20
+ ["base32", "fo", "bmzxq"],
21
+ ["base32", "foo", "bmzxw6"],
22
+ ["base32", "foob", "bmzxw6yq"],
23
+ ["base32", "fooba", "bmzxw6ytb"],
24
+ ["base32", "foobar", "bmzxw6ytboi"],
25
+ ["base32pad", "yes mani !", "cpfsxgidnmfxgsibb"],
26
+ ["base32pad", "f", "cmy======"],
27
+ ["base32pad", "fo", "cmzxq===="],
28
+ ["base32pad", "foo", "cmzxw6==="],
29
+ ["base32pad", "foob", "cmzxw6yq="],
30
+ ["base32pad", "fooba", "cmzxw6ytb"],
31
+ ["base32pad", "foobar", "cmzxw6ytboi======"],
32
+ ["base32hex", "yes mani !", "vf5in683dc5n6i811"],
33
+ ["base32hex", "f", "vco"],
34
+ ["base32hex", "fo", "vcpng"],
35
+ ["base32hex", "foo", "vcpnmu"],
36
+ ["base32hex", "foob", "vcpnmuog"],
37
+ ["base32hex", "fooba", "vcpnmuoj1"],
38
+ ["base32hex", "foobar", "vcpnmuoj1e8"],
39
+ ["base32hexpad", "yes mani !", "tf5in683dc5n6i811"],
40
+ ["base32hexpad", "f", "tco======"],
41
+ ["base32hexpad", "fo", "tcpng===="],
42
+ ["base32hexpad", "foo", "tcpnmu==="],
43
+ ["base32hexpad", "foob", "tcpnmuog="],
44
+ ["base32hexpad", "fooba", "tcpnmuoj1"],
45
+ ["base32hexpad", "foobar", "tcpnmuoj1e8======"],
46
+ ["base32z", "yes mani !", "hxf1zgedpcfzg1ebb"],
47
+ ["base58flickr", "yes mani !", "Z7Pznk19XTTzBtx"],
48
+ ["base58btc", "yes mani !", "z7paNL19xttacUY"],
49
+ ["base64", "÷ïÿ", "mw7fDr8O/"],
50
+ ["base64", "f", "mZg"],
51
+ ["base64", "fo", "mZm8"],
52
+ ["base64", "foo", "mZm9v"],
53
+ ["base64", "foob", "mZm9vYg"],
54
+ ["base64", "fooba", "mZm9vYmE"],
55
+ ["base64", "foobar", "mZm9vYmFy"],
56
+ ["base64", "÷ïÿ🥰÷ïÿ😎🥶🤯", "mw7fDr8O/8J+lsMO3w6/Dv/CfmI7wn6W28J+krw"],
57
+ ["base64pad", "f", "MZg=="],
58
+ ["base64pad", "fo", "MZm8="],
59
+ ["base64pad", "foo", "MZm9v"],
60
+ ["base64pad", "foob", "MZm9vYg=="],
61
+ ["base64pad", "fooba", "MZm9vYmE="],
62
+ ["base64pad", "foobar", "MZm9vYmFy"],
63
+ ["base64url", "÷ïÿ", "uw7fDr8O_"],
64
+ ["base64url", "÷ïÿ🥰÷ïÿ😎🥶🤯", "uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw"],
65
+ ["base64urlpad", "f", "UZg=="],
66
+ ["base64urlpad", "fo", "UZm8="],
67
+ ["base64urlpad", "foo", "UZm9v"],
68
+ ["base64urlpad", "foob", "UZm9vYg=="],
69
+ ["base64urlpad", "fooba", "UZm9vYmE="],
70
+ ["base64urlpad", "foobar", "UZm9vYmFy"],
71
+ [
72
+ "base64urlpad",
73
+ "÷ïÿ🥰÷ïÿ😎🥶🤯",
74
+ "Uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw==",
75
+ ],
76
+ ];
77
+
78
+ for (const [name, input, expectedOutput] of testCases) {
79
+ await t.test(`Encoding/Decoding ${name} with ${input}`, () => {
80
+ const encoded = encodeMultibase(name, encodeText(input));
81
+ deepStrictEqual(
82
+ decodeText(encoded),
83
+ expectedOutput,
84
+ `Encoding ${name} failed`,
85
+ );
86
+
87
+ const decoded = decodeMultibase(expectedOutput);
88
+ deepStrictEqual(decoded, encodeText(input), `Decoding ${name} failed`);
89
+
90
+ const decodedFromBuffer = decodeMultibase(encodeText(expectedOutput));
91
+ deepStrictEqual(
92
+ decodedFromBuffer,
93
+ encodeText(input),
94
+ `Decoding buffer of ${name} failed`,
95
+ );
96
+ });
97
+ }
98
+
99
+ await t.test("should allow base32pad full alphabet", () => {
100
+ const encodedStr = "ctimaq4ygg2iegci7";
101
+ const decoded = decodeMultibase(encodedStr);
102
+ const encoded = encodeMultibase("c", decoded);
103
+ deepStrictEqual(decodeMultibase(encoded), decoded);
104
+ });
105
+ });
106
+
107
+ test("constants", async (t) => {
108
+ await t.test("constants indexed by name", () => {
109
+ const names = constants.names;
110
+ deepStrictEqual(Object.keys(names).length, 23);
111
+ });
112
+
113
+ await t.test("constants indexed by code", () => {
114
+ const codes = constants.codes;
115
+ deepStrictEqual(Object.keys(codes).length, 23);
116
+ });
117
+ });
@@ -0,0 +1,103 @@
1
+ import type { CodecFactory } from "./types.d.ts";
2
+
3
+ const decode = (
4
+ string: string,
5
+ alphabet: string,
6
+ bitsPerChar: number,
7
+ ): Uint8Array => {
8
+ // Build the character lookup table:
9
+ const codes: Record<string, number> = {};
10
+ for (let i = 0; i < alphabet.length; ++i) {
11
+ codes[alphabet[i]] = i;
12
+ }
13
+
14
+ // Count the padding bytes:
15
+ let end = string.length;
16
+ while (string[end - 1] === "=") {
17
+ --end;
18
+ }
19
+
20
+ // Allocate the output:
21
+ const out = new Uint8Array((end * bitsPerChar / 8) | 0);
22
+
23
+ // Parse the data:
24
+ let bits = 0; // Number of bits currently in the buffer
25
+ let buffer = 0; // Bits waiting to be written out, MSB first
26
+ let written = 0; // Next byte to write
27
+ for (let i = 0; i < end; ++i) {
28
+ // Read one character from the string:
29
+ const value = codes[string[i]];
30
+ if (value === undefined) {
31
+ throw new SyntaxError("Invalid character " + string[i]);
32
+ }
33
+
34
+ // Append the bits to the buffer:
35
+ buffer = (buffer << bitsPerChar) | value;
36
+ bits += bitsPerChar;
37
+
38
+ // Write out some bits if the buffer has a byte's worth:
39
+ if (bits >= 8) {
40
+ bits -= 8;
41
+ out[written++] = 0xff & (buffer >> bits);
42
+ }
43
+ }
44
+
45
+ // Verify that we have received just enough bits:
46
+ if (bits >= bitsPerChar || 0xff & (buffer << (8 - bits))) {
47
+ throw new SyntaxError("Unexpected end of data");
48
+ }
49
+
50
+ return out;
51
+ };
52
+
53
+ const encode = (
54
+ data: Uint8Array,
55
+ alphabet: string,
56
+ bitsPerChar: number,
57
+ ): string => {
58
+ const pad = alphabet[alphabet.length - 1] === "=";
59
+ const mask = (1 << bitsPerChar) - 1;
60
+ let out = "";
61
+
62
+ let bits = 0; // Number of bits currently in the buffer
63
+ let buffer = 0; // Bits waiting to be written out, MSB first
64
+ for (let i = 0; i < data.length; ++i) {
65
+ // Slurp data into the buffer:
66
+ buffer = (buffer << 8) | data[i];
67
+ bits += 8;
68
+
69
+ // Write out as much as we can:
70
+ while (bits > bitsPerChar) {
71
+ bits -= bitsPerChar;
72
+ out += alphabet[mask & (buffer >> bits)];
73
+ }
74
+ }
75
+
76
+ // Partial character:
77
+ if (bits) {
78
+ out += alphabet[mask & (buffer << (bitsPerChar - bits))];
79
+ }
80
+
81
+ // Add padding characters until we hit a byte boundary:
82
+ if (pad) {
83
+ while ((out.length * bitsPerChar) & 7) {
84
+ out += "=";
85
+ }
86
+ }
87
+
88
+ return out;
89
+ };
90
+
91
+ /**
92
+ * RFC4648 Factory
93
+ */
94
+ export const rfc4648 = (bitsPerChar: number): CodecFactory => (alphabet) => {
95
+ return {
96
+ encode(input: Uint8Array): string {
97
+ return encode(input, alphabet, bitsPerChar);
98
+ },
99
+ decode(input: string): Uint8Array {
100
+ return decode(input, alphabet, bitsPerChar);
101
+ },
102
+ };
103
+ };
@@ -0,0 +1,61 @@
1
+ export type BaseCode =
2
+ | "\x00"
3
+ | "0"
4
+ | "7"
5
+ | "9"
6
+ | "f"
7
+ | "F"
8
+ | "v"
9
+ | "V"
10
+ | "t"
11
+ | "T"
12
+ | "b"
13
+ | "B"
14
+ | "c"
15
+ | "C"
16
+ | "h"
17
+ | "k"
18
+ | "K"
19
+ | "z"
20
+ | "Z"
21
+ | "m"
22
+ | "M"
23
+ | "u"
24
+ | "U";
25
+
26
+ /**
27
+ * - Names of the supported encodings
28
+ */
29
+ export type BaseName =
30
+ | "identity"
31
+ | "base2"
32
+ | "base8"
33
+ | "base10"
34
+ | "base16"
35
+ | "base16upper"
36
+ | "base32hex"
37
+ | "base32hexupper"
38
+ | "base32hexpad"
39
+ | "base32hexpadupper"
40
+ | "base32"
41
+ | "base32upper"
42
+ | "base32pad"
43
+ | "base32padupper"
44
+ | "base32z"
45
+ | "base36"
46
+ | "base36upper"
47
+ | "base58btc"
48
+ | "base58flickr"
49
+ | "base64"
50
+ | "base64pad"
51
+ | "base64url"
52
+ | "base64urlpad";
53
+
54
+ export type BaseNameOrCode = BaseCode | BaseName;
55
+ export interface Codec {
56
+ encode: (buffer: Uint8Array) => string;
57
+ decode: (hash: string) => Uint8Array;
58
+ }
59
+ export interface CodecFactory {
60
+ (input: string): Codec;
61
+ }
@@ -0,0 +1,22 @@
1
+ const textDecoder = new TextDecoder();
2
+ export const decodeText = (bytes: DataView | Uint8Array): string =>
3
+ textDecoder.decode(bytes);
4
+
5
+ const textEncoder = new TextEncoder();
6
+ export const encodeText = (text: string): Uint8Array =>
7
+ textEncoder.encode(text);
8
+
9
+ export function concat(
10
+ arrs: Array<ArrayLike<number>>,
11
+ length: number,
12
+ ): Uint8Array {
13
+ const output = new Uint8Array(length);
14
+ let offset = 0;
15
+
16
+ for (const arr of arrs) {
17
+ output.set(arr, offset);
18
+ offset += arr.length;
19
+ }
20
+
21
+ return output;
22
+ }
@@ -0,0 +1,93 @@
1
+ import { deepStrictEqual } from "node:assert";
2
+ import process from "node:process";
3
+ import { test } from "node:test";
4
+ import metadata from "../deno.json" with { type: "json" };
5
+ import { getUserAgent } from "./request.ts";
6
+
7
+ test("getUserAgent()", () => {
8
+ if ("Deno" in globalThis) {
9
+ deepStrictEqual(
10
+ getUserAgent(),
11
+ `Fedify/${metadata.version} (Deno/${Deno.version.deno})`,
12
+ );
13
+ deepStrictEqual(
14
+ getUserAgent({ software: "MyApp/1.0.0" }),
15
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Deno/${Deno.version.deno})`,
16
+ );
17
+ deepStrictEqual(
18
+ getUserAgent({ url: "https://example.com/" }),
19
+ `Fedify/${metadata.version} (Deno/${Deno.version.deno}; +https://example.com/)`,
20
+ );
21
+ deepStrictEqual(
22
+ getUserAgent({
23
+ software: "MyApp/1.0.0",
24
+ url: new URL("https://example.com/"),
25
+ }),
26
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Deno/${Deno.version.deno}; +https://example.com/)`,
27
+ );
28
+ } else if ("Bun" in globalThis) {
29
+ deepStrictEqual(
30
+ getUserAgent(),
31
+ // @ts-ignore: `Bun` is a global variable in Bun
32
+ `Fedify/${metadata.version} (Bun/${Bun.version})`,
33
+ );
34
+ deepStrictEqual(
35
+ getUserAgent({ software: "MyApp/1.0.0" }),
36
+ // @ts-ignore: `Bun` is a global variable in Bun
37
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Bun/${Bun.version})`,
38
+ );
39
+ deepStrictEqual(
40
+ getUserAgent({ url: "https://example.com/" }),
41
+ // @ts-ignore: `Bun` is a global variable in Bun
42
+ `Fedify/${metadata.version} (Bun/${Bun.version}; +https://example.com/)`,
43
+ );
44
+ deepStrictEqual(
45
+ getUserAgent({
46
+ software: "MyApp/1.0.0",
47
+ url: new URL("https://example.com/"),
48
+ }),
49
+ // @ts-ignore: `Bun` is a global variable in Bun
50
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Bun/${Bun.version}; +https://example.com/)`,
51
+ );
52
+ } else if (navigator.userAgent === "Cloudflare-Workers") {
53
+ deepStrictEqual(
54
+ getUserAgent(),
55
+ `Fedify/${metadata.version} (Cloudflare-Workers)`,
56
+ );
57
+ deepStrictEqual(
58
+ getUserAgent({ software: "MyApp/1.0.0" }),
59
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Cloudflare-Workers)`,
60
+ );
61
+ deepStrictEqual(
62
+ getUserAgent({ url: "https://example.com/" }),
63
+ `Fedify/${metadata.version} (Cloudflare-Workers; +https://example.com/)`,
64
+ );
65
+ deepStrictEqual(
66
+ getUserAgent({
67
+ software: "MyApp/1.0.0",
68
+ url: new URL("https://example.com/"),
69
+ }),
70
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Cloudflare-Workers; +https://example.com/)`,
71
+ );
72
+ } else {
73
+ deepStrictEqual(
74
+ getUserAgent(),
75
+ `Fedify/${metadata.version} (Node.js/${process.versions.node})`,
76
+ );
77
+ deepStrictEqual(
78
+ getUserAgent({ software: "MyApp/1.0.0" }),
79
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Node.js/${process.versions.node})`,
80
+ );
81
+ deepStrictEqual(
82
+ getUserAgent({ url: "https://example.com/" }),
83
+ `Fedify/${metadata.version} (Node.js/${process.versions.node}; +https://example.com/)`,
84
+ );
85
+ deepStrictEqual(
86
+ getUserAgent({
87
+ software: "MyApp/1.0.0",
88
+ url: new URL("https://example.com/"),
89
+ }),
90
+ `MyApp/1.0.0 (Fedify/${metadata.version}; Node.js/${process.versions.node}; +https://example.com/)`,
91
+ );
92
+ }
93
+ });