@fedify/cli 2.2.0-dev.945 → 2.2.0-dev.949
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/dist/deno.js +1 -1
- package/dist/lookup.js +76 -49
- package/package.json +9 -9
package/dist/deno.js
CHANGED
package/dist/lookup.js
CHANGED
|
@@ -6,16 +6,18 @@ import { colorEnabled, colors, formatObject } from "./utils.js";
|
|
|
6
6
|
import { configContext } from "./config.js";
|
|
7
7
|
import { createTunnelServiceOption, userAgentOption } from "./options.js";
|
|
8
8
|
import { renderImages } from "./imagerenderer.js";
|
|
9
|
+
import { url } from "@optique/core/message";
|
|
9
10
|
import { path, printError } from "@optique/run";
|
|
10
11
|
import process from "node:process";
|
|
11
|
-
import { argument, choice, command, constant, flag, float, integer, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
|
|
12
|
+
import { argument, choice, command, constant, flag, float, integer, map, merge, message as message$1, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
|
|
12
13
|
import { generateCryptoKeyPair, getAuthenticatedDocumentLoader, respondWithObject } from "@fedify/fedify";
|
|
13
14
|
import { Application, Collection, CryptographicKey, Object as Object$1, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
14
15
|
import { getLogger } from "@logtape/logtape";
|
|
15
16
|
import ora from "ora";
|
|
16
|
-
import { UrlError } from "@fedify/vocab-runtime";
|
|
17
|
+
import { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address } from "@fedify/vocab-runtime";
|
|
17
18
|
import { bindConfig } from "@optique/config";
|
|
18
19
|
import { createWriteStream } from "node:fs";
|
|
20
|
+
import { isIP } from "node:net";
|
|
19
21
|
//#region src/lookup.ts
|
|
20
22
|
const logger = getLogger([
|
|
21
23
|
"fedify",
|
|
@@ -37,27 +39,25 @@ const recurseProperties = [
|
|
|
37
39
|
MISSKEY_QUOTE_IRI,
|
|
38
40
|
FEDIBIRD_QUOTE_IRI
|
|
39
41
|
];
|
|
40
|
-
const suppressErrorsOption = bindConfig(flag("-S", "--suppress-errors", { description: message`Suppress partial errors during traversal or recursion.` }), {
|
|
42
|
+
const suppressErrorsOption = bindConfig(flag("-S", "--suppress-errors", { description: message$1`Suppress partial errors during traversal or recursion.` }), {
|
|
41
43
|
context: configContext,
|
|
42
44
|
key: (config) => config.lookup?.suppressErrors ?? false,
|
|
43
45
|
default: false
|
|
44
46
|
});
|
|
45
|
-
const allowPrivateAddressOption = bindConfig(flag("-p", "--allow-private-address", { description: message`Allow private IP addresses for URLs discovered \
|
|
46
|
-
during traversal
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
recursive fetches via ${optionNames(["--recurse"])} always disallow \
|
|
50
|
-
them.` }), {
|
|
47
|
+
const allowPrivateAddressOption = bindConfig(flag("-p", "--allow-private-address", { description: message$1`Allow private IP addresses for URLs discovered \
|
|
48
|
+
during traversal or recursive object fetches. Recursive JSON-LD \
|
|
49
|
+
context URLs always remain blocked. URLs explicitly provided on the \
|
|
50
|
+
command line always allow private addresses.` }), {
|
|
51
51
|
context: configContext,
|
|
52
52
|
key: (config) => config.lookup?.allowPrivateAddress ?? false,
|
|
53
53
|
default: false
|
|
54
54
|
});
|
|
55
55
|
const authorizedFetchOption = withDefault(object("Authorized fetch options", {
|
|
56
|
-
authorizedFetch: bindConfig(map(flag("-a", "--authorized-fetch", { description: message`Sign the request with an one-time key.` }), () => true), {
|
|
56
|
+
authorizedFetch: bindConfig(map(flag("-a", "--authorized-fetch", { description: message$1`Sign the request with an one-time key.` }), () => true), {
|
|
57
57
|
context: configContext,
|
|
58
58
|
key: (config) => config.lookup?.authorizedFetch ? true : void 0
|
|
59
59
|
}),
|
|
60
|
-
firstKnock: bindConfig(option("--first-knock", choice(["draft-cavage-http-signatures-12", "rfc9421"]), { description: message`The first-knock spec for ${optionNames(["-a", "--authorized-fetch"])}. It is used for the double-knocking technique.` }), {
|
|
60
|
+
firstKnock: bindConfig(option("--first-knock", choice(["draft-cavage-http-signatures-12", "rfc9421"]), { description: message$1`The first-knock spec for ${optionNames(["-a", "--authorized-fetch"])}. It is used for the double-knocking technique.` }), {
|
|
61
61
|
context: configContext,
|
|
62
62
|
key: (config) => config.lookup?.firstKnock ?? "draft-cavage-http-signatures-12",
|
|
63
63
|
default: "draft-cavage-http-signatures-12"
|
|
@@ -70,20 +70,20 @@ const authorizedFetchOption = withDefault(object("Authorized fetch options", {
|
|
|
70
70
|
});
|
|
71
71
|
const lookupModeOption = withDefault(or(object("Recurse options", {
|
|
72
72
|
traverse: constant(false),
|
|
73
|
-
recurse: bindConfig(option("--recurse", choice(recurseProperties, { metavar: "PROPERTY" }), { description: message`Recursively follow a relationship property.` }), {
|
|
73
|
+
recurse: bindConfig(option("--recurse", choice(recurseProperties, { metavar: "PROPERTY" }), { description: message$1`Recursively follow a relationship property.` }), {
|
|
74
74
|
context: configContext,
|
|
75
75
|
key: (config) => config.lookup?.recurse
|
|
76
76
|
}),
|
|
77
77
|
recurseDepth: withDefault(bindConfig(option("--recurse-depth", integer({
|
|
78
78
|
min: 1,
|
|
79
79
|
metavar: "DEPTH"
|
|
80
|
-
}), { description: message`Maximum recursion depth for ${optionNames(["--recurse"])}.` }), {
|
|
80
|
+
}), { description: message$1`Maximum recursion depth for ${optionNames(["--recurse"])}.` }), {
|
|
81
81
|
context: configContext,
|
|
82
82
|
key: (config) => config.lookup?.recurseDepth
|
|
83
83
|
}), 20),
|
|
84
84
|
suppressErrors: suppressErrorsOption
|
|
85
85
|
}), object("Traverse options", {
|
|
86
|
-
traverse: bindConfig(flag("-t", "--traverse", { description: message`Traverse the given collection(s) to fetch all items.` }), {
|
|
86
|
+
traverse: bindConfig(flag("-t", "--traverse", { description: message$1`Traverse the given collection(s) to fetch all items.` }), {
|
|
87
87
|
context: configContext,
|
|
88
88
|
key: (config) => config.lookup?.traverse ?? false,
|
|
89
89
|
default: false
|
|
@@ -102,22 +102,22 @@ const lookupCommand = command("lookup", merge(object({ command: constant("lookup
|
|
|
102
102
|
timeout: optional(bindConfig(option("-T", "--timeout", float({
|
|
103
103
|
min: 0,
|
|
104
104
|
metavar: "SECONDS"
|
|
105
|
-
}), { description: message`Set timeout for network requests in seconds.` }), {
|
|
105
|
+
}), { description: message$1`Set timeout for network requests in seconds.` }), {
|
|
106
106
|
context: configContext,
|
|
107
107
|
key: (config) => config.lookup?.timeout
|
|
108
108
|
}))
|
|
109
|
-
})), object("Arguments", { urls: multiple(argument(string({ metavar: "URL_OR_HANDLE" }), { description: message`One or more URLs or handles to look up.` }), { min: 1 }) }), object("Output options", {
|
|
110
|
-
reverse: bindConfig(flag("--reverse", { description: message`Reverse the output order of fetched objects or items.` }), {
|
|
109
|
+
})), object("Arguments", { urls: multiple(argument(string({ metavar: "URL_OR_HANDLE" }), { description: message$1`One or more URLs or handles to look up.` }), { min: 1 }) }), object("Output options", {
|
|
110
|
+
reverse: bindConfig(flag("--reverse", { description: message$1`Reverse the output order of fetched objects or items.` }), {
|
|
111
111
|
context: configContext,
|
|
112
112
|
key: (config) => config.lookup?.reverse ?? false,
|
|
113
113
|
default: false
|
|
114
114
|
}),
|
|
115
|
-
format: bindConfig(optional(or(map(flag("-r", "--raw", { description: message`Print the fetched JSON-LD document as is.` }), () => "raw"), map(flag("-C", "--compact", { description: message`Compact the fetched JSON-LD document.` }), () => "compact"), map(flag("-e", "--expand", { description: message`Expand the fetched JSON-LD document.` }), () => "expand"))), {
|
|
115
|
+
format: bindConfig(optional(or(map(flag("-r", "--raw", { description: message$1`Print the fetched JSON-LD document as is.` }), () => "raw"), map(flag("-C", "--compact", { description: message$1`Compact the fetched JSON-LD document.` }), () => "compact"), map(flag("-e", "--expand", { description: message$1`Expand the fetched JSON-LD document.` }), () => "expand"))), {
|
|
116
116
|
context: configContext,
|
|
117
117
|
key: (config) => config.lookup?.defaultFormat ?? "default",
|
|
118
118
|
default: "default"
|
|
119
119
|
}),
|
|
120
|
-
separator: bindConfig(option("-s", "--separator", string({ metavar: "SEPARATOR" }), { description: message`Specify the separator between adjacent output objects or collection items.` }), {
|
|
120
|
+
separator: bindConfig(option("-s", "--separator", string({ metavar: "SEPARATOR" }), { description: message$1`Specify the separator between adjacent output objects or collection items.` }), {
|
|
121
121
|
context: configContext,
|
|
122
122
|
key: (config) => config.lookup?.separator ?? "----",
|
|
123
123
|
default: "----"
|
|
@@ -126,10 +126,10 @@ const lookupCommand = command("lookup", merge(object({ command: constant("lookup
|
|
|
126
126
|
metavar: "OUTPUT_PATH",
|
|
127
127
|
type: "file",
|
|
128
128
|
allowCreate: true
|
|
129
|
-
}), { description: message`Specify the output file path.` }))
|
|
129
|
+
}), { description: message$1`Specify the output file path.` }))
|
|
130
130
|
})), {
|
|
131
|
-
brief: message`Look up Activity Streams objects.`,
|
|
132
|
-
description: message`Look up Activity Streams objects by URL or actor handle.
|
|
131
|
+
brief: message$1`Look up Activity Streams objects.`,
|
|
132
|
+
description: message$1`Look up Activity Streams objects by URL or actor handle.
|
|
133
133
|
|
|
134
134
|
The arguments can be either URLs or actor handles (e.g., ${"@username@domain"}), and they can be multiple.`
|
|
135
135
|
});
|
|
@@ -281,13 +281,47 @@ function wrapDocumentLoaderWithTimeout(loader, timeoutSeconds) {
|
|
|
281
281
|
function handleTimeoutError(spinner, timeoutSeconds, url) {
|
|
282
282
|
const urlText = url ? ` for: ${colors.red(url)}` : "";
|
|
283
283
|
spinner.fail(`Request timed out after ${timeoutSeconds} seconds${urlText}.`);
|
|
284
|
-
printError(message`Try increasing the timeout with -T
|
|
284
|
+
printError(message$1`Try increasing the timeout with ${optionNames(["-T", "--timeout"])} option or check network connectivity.`);
|
|
285
285
|
}
|
|
286
286
|
function isPrivateAddressError(error) {
|
|
287
287
|
const lowerMessage = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
288
288
|
if (error instanceof UrlError) return lowerMessage.includes("invalid or private address") || lowerMessage.includes("localhost is not allowed");
|
|
289
289
|
return lowerMessage.includes("private address") || lowerMessage.includes("private ip") || lowerMessage.includes("localhost") || lowerMessage.includes("loopback");
|
|
290
290
|
}
|
|
291
|
+
function getPrivateUrlCandidate(candidate) {
|
|
292
|
+
if (typeof candidate !== "string" && !(candidate instanceof URL)) return null;
|
|
293
|
+
try {
|
|
294
|
+
const url = new URL(candidate);
|
|
295
|
+
const hostname = url.hostname;
|
|
296
|
+
if (hostname === "localhost") return url;
|
|
297
|
+
const normalized = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
298
|
+
const ipVersion = isIP(normalized);
|
|
299
|
+
if (ipVersion === 4) return isValidPublicIPv4Address(normalized) ? null : url;
|
|
300
|
+
if (ipVersion === 6) return isValidPublicIPv6Address(expandIPv6Address(normalized)) ? null : url;
|
|
301
|
+
return null;
|
|
302
|
+
} catch {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function isPrivateAddressTarget(target) {
|
|
307
|
+
return getPrivateUrlCandidate(target) != null;
|
|
308
|
+
}
|
|
309
|
+
function getPrivateContextUrl(error) {
|
|
310
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
311
|
+
if (!(error instanceof Error) || error.name !== "jsonld.InvalidUrl" || !errorMessage.includes("valid JSON-LD object")) return null;
|
|
312
|
+
const structuredError = error;
|
|
313
|
+
const structuredUrl = getPrivateUrlCandidate(structuredError.details?.url) ?? getPrivateUrlCandidate(structuredError.url);
|
|
314
|
+
if (structuredUrl != null) return structuredUrl;
|
|
315
|
+
const match = errorMessage.match(/URL:\s*"([^"]+)"/);
|
|
316
|
+
if (match == null) return null;
|
|
317
|
+
return getPrivateUrlCandidate(match[1]);
|
|
318
|
+
}
|
|
319
|
+
function printRecursivePrivateAddressHint() {
|
|
320
|
+
printError(message$1`The recursive target appears to be private or localhost. Try with ${optionNames(["-p", "--allow-private-address"])}, or use ${optionNames(["-S", "--suppress-errors"])} to skip blocked steps.`);
|
|
321
|
+
}
|
|
322
|
+
function printRecursivePrivateContextHint(privateContextUrl) {
|
|
323
|
+
printError(message$1`Recursive JSON-LD context URL ${url(privateContextUrl)} is always blocked, even with ${optionNames(["-p", "--allow-private-address"])}. Use ${optionNames(["-S", "--suppress-errors"])} to skip blocked steps.`);
|
|
324
|
+
}
|
|
291
325
|
function getLookupFailureHint(error, options = {}) {
|
|
292
326
|
if (isPrivateAddressError(error)) return options.recursive ? "recursive-private-address" : "private-address";
|
|
293
327
|
return "authorized-fetch";
|
|
@@ -303,13 +337,13 @@ function printLookupFailureHint(authLoader, error, options = {}) {
|
|
|
303
337
|
if (!shouldPrintLookupFailureHint(authLoader, hint)) return;
|
|
304
338
|
switch (hint) {
|
|
305
339
|
case "private-address":
|
|
306
|
-
printError(message`The URL appears to be private or localhost. Try with -p
|
|
340
|
+
printError(message$1`The URL appears to be private or localhost. Try with ${optionNames(["-p", "--allow-private-address"])}.`);
|
|
307
341
|
return;
|
|
308
342
|
case "recursive-private-address":
|
|
309
|
-
|
|
343
|
+
printRecursivePrivateAddressHint();
|
|
310
344
|
return;
|
|
311
345
|
case "authorized-fetch":
|
|
312
|
-
printError(message`It may be a private object. Try with -a
|
|
346
|
+
printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
|
|
313
347
|
return;
|
|
314
348
|
}
|
|
315
349
|
}
|
|
@@ -383,7 +417,7 @@ async function runLookup(command, deps = {}) {
|
|
|
383
417
|
...deps
|
|
384
418
|
};
|
|
385
419
|
if (command.urls.length < 1) {
|
|
386
|
-
printError(message`At least one URL or actor handle must be provided.`);
|
|
420
|
+
printError(message$1`At least one URL or actor handle must be provided.`);
|
|
387
421
|
effectiveDeps.exit(1);
|
|
388
422
|
}
|
|
389
423
|
if (command.debug) await configureLogging();
|
|
@@ -494,26 +528,12 @@ async function runLookup(command, deps = {}) {
|
|
|
494
528
|
}
|
|
495
529
|
spinner.text = `Looking up the ${command.recurse != null ? "object chain" : command.traverse ? "collection" : command.urls.length > 1 ? "objects" : "object"}...`;
|
|
496
530
|
if (command.recurse != null) {
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
allowPrivateAddress: false
|
|
500
|
-
}), command.timeout);
|
|
531
|
+
const initialLookupDocumentLoader = initialAuthLoader ?? initialDocumentLoader;
|
|
532
|
+
const recursiveLookupDocumentLoader = authLoader ?? documentLoader;
|
|
501
533
|
const recursiveContextLoader = wrapDocumentLoaderWithTimeout(await getContextLoader({
|
|
502
534
|
userAgent: command.userAgent,
|
|
503
535
|
allowPrivateAddress: false
|
|
504
536
|
}), command.timeout);
|
|
505
|
-
const recursiveAuthLoader = command.authorizedFetch && authIdentity != null ? wrapDocumentLoaderWithTimeout(getAuthenticatedDocumentLoader(authIdentity, {
|
|
506
|
-
allowPrivateAddress: false,
|
|
507
|
-
userAgent: command.userAgent,
|
|
508
|
-
specDeterminer: {
|
|
509
|
-
determineSpec() {
|
|
510
|
-
return command.firstKnock;
|
|
511
|
-
},
|
|
512
|
-
rememberSpec() {}
|
|
513
|
-
}
|
|
514
|
-
}), command.timeout) : void 0;
|
|
515
|
-
const initialLookupDocumentLoader = initialAuthLoader ?? initialDocumentLoader;
|
|
516
|
-
const recursiveLookupDocumentLoader = recursiveAuthLoader ?? recursiveDocumentLoader;
|
|
517
537
|
let totalObjects = 0;
|
|
518
538
|
const recurseDepth = command.recurseDepth;
|
|
519
539
|
for (let urlIndex = 0; urlIndex < command.urls.length; urlIndex++) {
|
|
@@ -538,7 +558,7 @@ async function runLookup(command, deps = {}) {
|
|
|
538
558
|
}
|
|
539
559
|
if (current == null) {
|
|
540
560
|
spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
|
|
541
|
-
if (authLoader == null) printError(message`It may be a private object. Try with -a
|
|
561
|
+
if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
|
|
542
562
|
await finalizeAndExit(1);
|
|
543
563
|
return;
|
|
544
564
|
}
|
|
@@ -579,11 +599,18 @@ async function runLookup(command, deps = {}) {
|
|
|
579
599
|
if (error instanceof TimeoutError) handleTimeoutError(spinner, command.timeout);
|
|
580
600
|
else if (error instanceof RecursiveLookupError) {
|
|
581
601
|
spinner.fail(`Failed to recursively fetch object: ${colors.red(error.target)}.`);
|
|
582
|
-
if (
|
|
602
|
+
if (!command.allowPrivateAddress && isPrivateAddressTarget(error.target)) printRecursivePrivateAddressHint();
|
|
603
|
+
else if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
|
|
583
604
|
} else {
|
|
584
605
|
spinner.fail("Failed to recursively fetch object.");
|
|
606
|
+
const privateContextUrl = getPrivateContextUrl(error);
|
|
607
|
+
if (privateContextUrl != null) {
|
|
608
|
+
printRecursivePrivateContextHint(privateContextUrl);
|
|
609
|
+
await finalizeAndExit(1);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
585
612
|
const hint = getLookupFailureHint(error, { recursive: true });
|
|
586
|
-
if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message`Use the -S
|
|
613
|
+
if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message$1`Use the ${optionNames(["-S", "--suppress-errors"])} option to suppress partial errors.`);
|
|
587
614
|
else printLookupFailureHint(authLoader, error, { recursive: true });
|
|
588
615
|
}
|
|
589
616
|
await finalizeAndExit(1);
|
|
@@ -657,7 +684,7 @@ async function runLookup(command, deps = {}) {
|
|
|
657
684
|
}
|
|
658
685
|
if (collection == null) {
|
|
659
686
|
spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
|
|
660
|
-
if (authLoader == null) printError(message`It may be a private object. Try with -a
|
|
687
|
+
if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
|
|
661
688
|
await finalizeAndExit(1);
|
|
662
689
|
return;
|
|
663
690
|
}
|
|
@@ -719,7 +746,7 @@ async function runLookup(command, deps = {}) {
|
|
|
719
746
|
else {
|
|
720
747
|
spinner.fail(`Failed to complete the traversal for: ${colors.red(url)}.`);
|
|
721
748
|
const hint = getLookupFailureHint(error);
|
|
722
|
-
if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message`Use the -S
|
|
749
|
+
if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message$1`Use the ${optionNames(["-S", "--suppress-errors"])} option to suppress partial errors.`);
|
|
723
750
|
else printLookupFailureHint(authLoader, error);
|
|
724
751
|
}
|
|
725
752
|
await finalizeAndExit(1);
|
|
@@ -754,7 +781,7 @@ async function runLookup(command, deps = {}) {
|
|
|
754
781
|
const url = command.urls[i];
|
|
755
782
|
if (obj == null) {
|
|
756
783
|
spinner.fail(`Failed to fetch ${colors.red(url)}`);
|
|
757
|
-
if (authLoader == null) printError(message`It may be a private object. Try with -a
|
|
784
|
+
if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
|
|
758
785
|
success = false;
|
|
759
786
|
} else {
|
|
760
787
|
spinner.succeed(`Fetched object: ${colors.green(url)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/cli",
|
|
3
|
-
"version": "2.2.0-dev.
|
|
3
|
+
"version": "2.2.0-dev.949+bd260a9f",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"README.md",
|
|
@@ -86,14 +86,14 @@
|
|
|
86
86
|
"smol-toml": "^1.6.0",
|
|
87
87
|
"srvx": "^0.8.7",
|
|
88
88
|
"valibot": "^1.2.0",
|
|
89
|
-
"@fedify/fedify": "2.2.0-dev.
|
|
90
|
-
"@fedify/
|
|
91
|
-
"@fedify/
|
|
92
|
-
"@fedify/vocab": "2.2.0-dev.
|
|
93
|
-
"@fedify/vocab-
|
|
94
|
-
"@fedify/
|
|
95
|
-
"@fedify/
|
|
96
|
-
"@fedify/
|
|
89
|
+
"@fedify/fedify": "2.2.0-dev.949+bd260a9f",
|
|
90
|
+
"@fedify/init": "2.2.0-dev.949+bd260a9f",
|
|
91
|
+
"@fedify/relay": "2.2.0-dev.949+bd260a9f",
|
|
92
|
+
"@fedify/vocab": "2.2.0-dev.949+bd260a9f",
|
|
93
|
+
"@fedify/vocab-runtime": "2.2.0-dev.949+bd260a9f",
|
|
94
|
+
"@fedify/sqlite": "2.2.0-dev.949+bd260a9f",
|
|
95
|
+
"@fedify/vocab-tools": "2.2.0-dev.949+bd260a9f",
|
|
96
|
+
"@fedify/webfinger": "2.2.0-dev.949+bd260a9f"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@types/bun": "^1.2.23",
|