@fedify/cli 2.0.0-pr.479.1922 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +3 -3
- package/dist/cache.js +17 -3
- package/dist/config.js +105 -0
- package/dist/deno.js +18 -8
- package/dist/generate-vocab/action.js +1 -1
- package/dist/imagerenderer.js +1 -1
- package/dist/inbox/rendercode.js +11 -21
- package/dist/inbox.js +162 -132
- package/dist/init/mod.js +3 -3
- package/dist/log.js +35 -1
- package/dist/lookup.js +55 -23
- package/dist/mod.js +95 -18
- package/dist/nodeinfo.js +39 -22
- package/dist/options.js +84 -0
- package/dist/relay.js +136 -0
- package/dist/tempserver.js +15 -8
- package/dist/tunnel.js +6 -10
- package/dist/utils.js +19 -108
- package/dist/webfinger/action.js +1 -1
- package/dist/webfinger/command.js +17 -9
- package/dist/webfinger/lib.js +3 -3
- package/package.json +50 -28
- package/deno.json +0 -71
- package/dist/globals.js +0 -49
- package/dist/init/action/configs.js +0 -91
- package/dist/init/action/const.js +0 -10
- package/dist/init/action/deps.js +0 -50
- package/dist/init/action/dir.js +0 -16
- package/dist/init/action/env.js +0 -13
- package/dist/init/action/install.js +0 -20
- package/dist/init/action/mod.js +0 -39
- package/dist/init/action/notice.js +0 -55
- package/dist/init/action/patch.js +0 -147
- package/dist/init/action/precommand.js +0 -28
- package/dist/init/action/recommend.js +0 -24
- package/dist/init/action/set.js +0 -31
- package/dist/init/action/templates.js +0 -58
- package/dist/init/action/utils.js +0 -50
- package/dist/init/ask/dir.js +0 -82
- package/dist/init/ask/kv.js +0 -44
- package/dist/init/ask/mod.js +0 -16
- package/dist/init/ask/mq.js +0 -46
- package/dist/init/ask/pm.js +0 -49
- package/dist/init/ask/wf.js +0 -29
- package/dist/init/command.js +0 -50
- package/dist/init/const.js +0 -31
- package/dist/init/json/biome.js +0 -24
- package/dist/init/json/kv.js +0 -53
- package/dist/init/json/mq.js +0 -72
- package/dist/init/json/pm.js +0 -44
- package/dist/init/json/rt.js +0 -39
- package/dist/init/json/vscode-settings-for-deno.js +0 -53
- package/dist/init/json/vscode-settings.js +0 -49
- package/dist/init/lib.js +0 -136
- package/dist/init/templates/defaults/federation.ts.tpl +0 -23
- package/dist/init/templates/defaults/logging.ts.tpl +0 -23
- package/dist/init/templates/express/app.ts.tpl +0 -16
- package/dist/init/templates/express/index.ts.tpl +0 -6
- package/dist/init/templates/hono/app.tsx.tpl +0 -14
- package/dist/init/templates/hono/index/bun.ts.tpl +0 -10
- package/dist/init/templates/hono/index/deno.ts.tpl +0 -13
- package/dist/init/templates/hono/index/node.ts.tpl +0 -14
- package/dist/init/templates/next/middleware.ts.tpl +0 -45
- package/dist/init/templates/nitro/.env.test.tpl +0 -1
- package/dist/init/templates/nitro/nitro.config.ts.tpl +0 -14
- package/dist/init/templates/nitro/server/error.ts.tpl +0 -3
- package/dist/init/templates/nitro/server/middleware/federation.ts.tpl +0 -8
- package/dist/init/test/action.js +0 -17
- package/dist/init/test/create.js +0 -100
- package/dist/init/test/fill.js +0 -32
- package/dist/init/test/lookup.js +0 -190
- package/dist/init/test/run.js +0 -25
- package/dist/init/test/utils.js +0 -17
- package/dist/init/webframeworks.js +0 -136
- package/scripts/pack.ts +0 -71
- package/src/cache.ts +0 -17
- package/src/docloader.ts +0 -67
- package/src/generate-vocab/action.ts +0 -17
- package/src/generate-vocab/command.ts +0 -44
- package/src/generate-vocab/mod.ts +0 -2
- package/src/globals.ts +0 -43
- package/src/imagerenderer.ts +0 -149
- package/src/inbox/entry.ts +0 -10
- package/src/inbox/rendercode.ts +0 -68
- package/src/inbox/view.tsx +0 -598
- package/src/inbox.tsx +0 -536
- package/src/init/action/configs.ts +0 -133
- package/src/init/action/const.ts +0 -9
- package/src/init/action/deps.ts +0 -161
- package/src/init/action/dir.ts +0 -11
- package/src/init/action/env.ts +0 -14
- package/src/init/action/install.ts +0 -24
- package/src/init/action/mod.ts +0 -66
- package/src/init/action/notice.ts +0 -103
- package/src/init/action/patch.ts +0 -233
- package/src/init/action/precommand.ts +0 -29
- package/src/init/action/recommend.ts +0 -38
- package/src/init/action/set.ts +0 -65
- package/src/init/action/templates.ts +0 -96
- package/src/init/action/utils.ts +0 -64
- package/src/init/ask/dir.ts +0 -98
- package/src/init/ask/kv.ts +0 -82
- package/src/init/ask/mod.ts +0 -23
- package/src/init/ask/mq.ts +0 -86
- package/src/init/ask/pm.ts +0 -58
- package/src/init/ask/wf.ts +0 -27
- package/src/init/command.ts +0 -135
- package/src/init/const.ts +0 -4
- package/src/init/json/biome.json +0 -17
- package/src/init/json/kv.json +0 -39
- package/src/init/json/mq.json +0 -95
- package/src/init/json/pm.json +0 -47
- package/src/init/json/rt.json +0 -42
- package/src/init/json/vscode-settings-for-deno.json +0 -43
- package/src/init/json/vscode-settings.json +0 -41
- package/src/init/lib.ts +0 -223
- package/src/init/mod.ts +0 -3
- package/src/init/templates/defaults/federation.ts.tpl +0 -23
- package/src/init/templates/defaults/logging.ts.tpl +0 -23
- package/src/init/templates/express/app.ts.tpl +0 -16
- package/src/init/templates/express/index.ts.tpl +0 -6
- package/src/init/templates/hono/app.tsx.tpl +0 -14
- package/src/init/templates/hono/index/bun.ts.tpl +0 -10
- package/src/init/templates/hono/index/deno.ts.tpl +0 -13
- package/src/init/templates/hono/index/node.ts.tpl +0 -14
- package/src/init/templates/next/middleware.ts.tpl +0 -45
- package/src/init/templates/nitro/.env.test.tpl +0 -1
- package/src/init/templates/nitro/nitro.config.ts.tpl +0 -14
- package/src/init/templates/nitro/server/error.ts.tpl +0 -3
- package/src/init/templates/nitro/server/middleware/federation.ts.tpl +0 -8
- package/src/init/test/action.ts +0 -28
- package/src/init/test/create.ts +0 -137
- package/src/init/test/fill.ts +0 -67
- package/src/init/test/lookup.ts +0 -254
- package/src/init/test/run.ts +0 -39
- package/src/init/test/types.ts +0 -27
- package/src/init/test/utils.ts +0 -21
- package/src/init/types.ts +0 -89
- package/src/init/webframeworks.ts +0 -168
- package/src/kv.bun.ts +0 -12
- package/src/kv.node.ts +0 -11
- package/src/log.ts +0 -64
- package/src/lookup.test.ts +0 -182
- package/src/lookup.ts +0 -563
- package/src/mod.ts +0 -62
- package/src/nodeinfo.test.ts +0 -229
- package/src/nodeinfo.ts +0 -454
- package/src/table.ts +0 -17
- package/src/tempserver.ts +0 -87
- package/src/tunnel.test.ts +0 -157
- package/src/tunnel.ts +0 -94
- package/src/utils.ts +0 -254
- package/src/webfinger/action.ts +0 -50
- package/src/webfinger/command.ts +0 -64
- package/src/webfinger/error.ts +0 -47
- package/src/webfinger/lib.ts +0 -37
- package/src/webfinger/mod.test.ts +0 -79
- package/src/webfinger/mod.ts +0 -2
- package/tsdown.config.ts +0 -35
package/src/inbox.tsx
DELETED
|
@@ -1,536 +0,0 @@
|
|
|
1
|
-
/** @jsx react-jsx */
|
|
2
|
-
/** @jsxImportSource hono/jsx */
|
|
3
|
-
import {
|
|
4
|
-
Accept,
|
|
5
|
-
Activity,
|
|
6
|
-
type Actor,
|
|
7
|
-
Application,
|
|
8
|
-
type Context,
|
|
9
|
-
createFederation,
|
|
10
|
-
Delete,
|
|
11
|
-
Endpoints,
|
|
12
|
-
Follow,
|
|
13
|
-
generateCryptoKeyPair,
|
|
14
|
-
getActorHandle,
|
|
15
|
-
Image,
|
|
16
|
-
isActor,
|
|
17
|
-
lookupObject,
|
|
18
|
-
MemoryKvStore,
|
|
19
|
-
PUBLIC_COLLECTION,
|
|
20
|
-
type Recipient,
|
|
21
|
-
} from "@fedify/fedify";
|
|
22
|
-
import { getLogger } from "@logtape/logtape";
|
|
23
|
-
import {
|
|
24
|
-
command,
|
|
25
|
-
constant,
|
|
26
|
-
type InferValue,
|
|
27
|
-
merge,
|
|
28
|
-
message,
|
|
29
|
-
multiple,
|
|
30
|
-
object,
|
|
31
|
-
option,
|
|
32
|
-
optional,
|
|
33
|
-
string,
|
|
34
|
-
withDefault,
|
|
35
|
-
} from "@optique/core";
|
|
36
|
-
import Table from "cli-table3";
|
|
37
|
-
import { type Context as HonoContext, Hono } from "hono";
|
|
38
|
-
import type { BlankEnv, BlankInput } from "hono/types";
|
|
39
|
-
import process from "node:process";
|
|
40
|
-
import ora from "ora";
|
|
41
|
-
import metadata from "../deno.json" with { type: "json" };
|
|
42
|
-
import { getDocumentLoader } from "./docloader.ts";
|
|
43
|
-
import { configureLogging, debugOption } from "./globals.ts";
|
|
44
|
-
import type { ActivityEntry } from "./inbox/entry.ts";
|
|
45
|
-
import { ActivityEntryPage, ActivityListPage } from "./inbox/view.tsx";
|
|
46
|
-
import { recordingSink } from "./log.ts";
|
|
47
|
-
import { tableStyle } from "./table.ts";
|
|
48
|
-
import { spawnTemporaryServer, type TemporaryServer } from "./tempserver.ts";
|
|
49
|
-
import { colors } from "./utils.ts";
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Context data for the ephemeral ActivityPub inbox server.
|
|
53
|
-
*
|
|
54
|
-
* This interface defines the shape of context data passed to federation
|
|
55
|
-
* handlers during inbox command execution.
|
|
56
|
-
*/
|
|
57
|
-
interface ContextData {
|
|
58
|
-
activityIndex: number;
|
|
59
|
-
actorName: string;
|
|
60
|
-
actorSummary: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const logger = getLogger(["fedify", "cli", "inbox"]);
|
|
64
|
-
|
|
65
|
-
export const inboxCommand = command(
|
|
66
|
-
"inbox",
|
|
67
|
-
merge(
|
|
68
|
-
object("Inbox options", {
|
|
69
|
-
command: constant("inbox"),
|
|
70
|
-
follow: optional(
|
|
71
|
-
multiple(
|
|
72
|
-
option("-f", "--follow", string({ metavar: "URI" }), {
|
|
73
|
-
description:
|
|
74
|
-
message`Follow the given actor. The argument can be either an actor URI or a handle. Can be specified multiple times.`,
|
|
75
|
-
}),
|
|
76
|
-
),
|
|
77
|
-
),
|
|
78
|
-
acceptFollow: optional(
|
|
79
|
-
multiple(
|
|
80
|
-
option("-a", "--accept-follow", string({ metavar: "URI" }), {
|
|
81
|
-
description:
|
|
82
|
-
message`Accept follow requests from the given actor. The argument can be either an actor URI or a handle, or a wildcard (${"*"}). Can be specified multiple times. If a wildcard is specified, all follow requests will be accepted.`,
|
|
83
|
-
}),
|
|
84
|
-
),
|
|
85
|
-
),
|
|
86
|
-
noTunnel: option(
|
|
87
|
-
"-T",
|
|
88
|
-
"--no-tunnel",
|
|
89
|
-
{
|
|
90
|
-
description:
|
|
91
|
-
message`Do not tunnel the ephemeral ActivityPub server to the public Internet.`,
|
|
92
|
-
},
|
|
93
|
-
),
|
|
94
|
-
actorName: withDefault(
|
|
95
|
-
option("--actor-name", string({ metavar: "NAME" }), {
|
|
96
|
-
description: message`Customize the actor display name.`,
|
|
97
|
-
}),
|
|
98
|
-
"Fedify Ephemeral Inbox",
|
|
99
|
-
),
|
|
100
|
-
actorSummary: withDefault(
|
|
101
|
-
option("--actor-summary", string({ metavar: "SUMMARY" }), {
|
|
102
|
-
description: message`Customize the actor description.`,
|
|
103
|
-
}),
|
|
104
|
-
"An ephemeral ActivityPub inbox for testing purposes.",
|
|
105
|
-
),
|
|
106
|
-
}),
|
|
107
|
-
debugOption,
|
|
108
|
-
),
|
|
109
|
-
{
|
|
110
|
-
brief: message`Run an ephemeral ActivityPub inbox server.`,
|
|
111
|
-
description:
|
|
112
|
-
message`Spins up an ephemeral server that serves the ActivityPub inbox with an one-time actor, through a short-lived public DNS with HTTPS. You can monitor the incoming activities in real-time.`,
|
|
113
|
-
},
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
export async function runInbox(
|
|
117
|
-
command: InferValue<typeof inboxCommand>,
|
|
118
|
-
) {
|
|
119
|
-
const fetch = createFetchHandler({
|
|
120
|
-
actorName: command.actorName,
|
|
121
|
-
actorSummary: command.actorSummary,
|
|
122
|
-
});
|
|
123
|
-
const sendDeleteToPeers = createSendDeleteToPeers({
|
|
124
|
-
actorName: command.actorName,
|
|
125
|
-
actorSummary: command.actorSummary,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Enable Debug mode if requested
|
|
129
|
-
if (command.debug) {
|
|
130
|
-
await configureLogging();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const spinner = ora({
|
|
134
|
-
text: "Spinning up an ephemeral ActivityPub server...",
|
|
135
|
-
discardStdin: false,
|
|
136
|
-
}).start();
|
|
137
|
-
const server = await spawnTemporaryServer(fetch, {
|
|
138
|
-
noTunnel: command.noTunnel,
|
|
139
|
-
});
|
|
140
|
-
spinner.succeed(
|
|
141
|
-
`The ephemeral ActivityPub server is up and running: ${
|
|
142
|
-
colors.green(
|
|
143
|
-
server.url.href,
|
|
144
|
-
)
|
|
145
|
-
}`,
|
|
146
|
-
);
|
|
147
|
-
process.on("SIGINT", () => {
|
|
148
|
-
spinner.stop();
|
|
149
|
-
const peersCnt = Object.keys(peers).length;
|
|
150
|
-
spinner.start(
|
|
151
|
-
`Sending Delete(Application) activities to the ${peersCnt} ${
|
|
152
|
-
peersCnt === 1 ? "peer" : "peers"
|
|
153
|
-
}...`,
|
|
154
|
-
);
|
|
155
|
-
sendDeleteToPeers(server).then(() => {
|
|
156
|
-
spinner.text = "Stopping server...";
|
|
157
|
-
server.close().then(() => {
|
|
158
|
-
spinner.succeed("Server stopped.");
|
|
159
|
-
process.exit(0);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
spinner.start();
|
|
164
|
-
|
|
165
|
-
const fedCtx = federation.createContext(server.url, {
|
|
166
|
-
activityIndex: -1,
|
|
167
|
-
actorName: command.actorName,
|
|
168
|
-
actorSummary: command.actorSummary,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
if (command.acceptFollow != null && command.acceptFollow.length > 0) {
|
|
172
|
-
acceptFollows.push(...(command.acceptFollow ?? []));
|
|
173
|
-
}
|
|
174
|
-
if (command.follow != null && command.follow.length > 0) {
|
|
175
|
-
spinner.text = "Following actors...";
|
|
176
|
-
const documentLoader = await fedCtx.getDocumentLoader({
|
|
177
|
-
identifier: "i",
|
|
178
|
-
});
|
|
179
|
-
for (const uri of command.follow) {
|
|
180
|
-
spinner.text = `Following ${colors.green(uri)}...`;
|
|
181
|
-
const actor = await lookupObject(uri, { documentLoader });
|
|
182
|
-
if (!isActor(actor)) {
|
|
183
|
-
spinner.fail(`Not an actor: ${colors.red(uri)}`);
|
|
184
|
-
spinner.start();
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (actor.id != null) peers[actor.id?.href] = actor;
|
|
188
|
-
await fedCtx.sendActivity(
|
|
189
|
-
{ identifier: "i" },
|
|
190
|
-
actor,
|
|
191
|
-
new Follow({
|
|
192
|
-
id: new URL(`#follows/${actor.id?.href}`, fedCtx.getActorUri("i")),
|
|
193
|
-
actor: fedCtx.getActorUri("i"),
|
|
194
|
-
object: actor.id,
|
|
195
|
-
}),
|
|
196
|
-
);
|
|
197
|
-
spinner.succeed(`Sent follow request to ${colors.green(uri)}.`);
|
|
198
|
-
spinner.start();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
spinner.stop();
|
|
202
|
-
printServerInfo(fedCtx);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const federationDocumentLoader = await getDocumentLoader();
|
|
206
|
-
|
|
207
|
-
const federation = createFederation<ContextData>({
|
|
208
|
-
kv: new MemoryKvStore(),
|
|
209
|
-
documentLoaderFactory: () => {
|
|
210
|
-
return federationDocumentLoader;
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const time = Temporal.Now.instant();
|
|
215
|
-
let actorKeyPairs: CryptoKeyPair[] | undefined = undefined;
|
|
216
|
-
|
|
217
|
-
federation
|
|
218
|
-
.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
|
|
219
|
-
if (identifier !== "i") return null;
|
|
220
|
-
return new Application({
|
|
221
|
-
id: ctx.getActorUri(identifier),
|
|
222
|
-
preferredUsername: identifier,
|
|
223
|
-
name: ctx.data.actorName,
|
|
224
|
-
summary: ctx.data.actorSummary,
|
|
225
|
-
inbox: ctx.getInboxUri(identifier),
|
|
226
|
-
endpoints: new Endpoints({
|
|
227
|
-
sharedInbox: ctx.getInboxUri(),
|
|
228
|
-
}),
|
|
229
|
-
followers: ctx.getFollowersUri(identifier),
|
|
230
|
-
following: ctx.getFollowingUri(identifier),
|
|
231
|
-
outbox: ctx.getOutboxUri(identifier),
|
|
232
|
-
manuallyApprovesFollowers: true,
|
|
233
|
-
published: time,
|
|
234
|
-
icon: new Image({
|
|
235
|
-
url: new URL("https://fedify.dev/logo.png"),
|
|
236
|
-
mediaType: "image/png",
|
|
237
|
-
}),
|
|
238
|
-
publicKey: (await ctx.getActorKeyPairs(identifier))[0].cryptographicKey,
|
|
239
|
-
assertionMethods: (await ctx.getActorKeyPairs(identifier))
|
|
240
|
-
.map((pair) => pair.multikey),
|
|
241
|
-
url: ctx.getActorUri(identifier),
|
|
242
|
-
});
|
|
243
|
-
})
|
|
244
|
-
.setKeyPairsDispatcher(async (_ctxData, identifier) => {
|
|
245
|
-
if (identifier !== "i") return [];
|
|
246
|
-
if (actorKeyPairs == null) {
|
|
247
|
-
actorKeyPairs = [
|
|
248
|
-
await generateCryptoKeyPair("RSASSA-PKCS1-v1_5"),
|
|
249
|
-
await generateCryptoKeyPair("Ed25519"),
|
|
250
|
-
];
|
|
251
|
-
}
|
|
252
|
-
return actorKeyPairs;
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
const activities: ActivityEntry[] = [];
|
|
256
|
-
|
|
257
|
-
const acceptFollows: string[] = [];
|
|
258
|
-
|
|
259
|
-
async function acceptsFollowFrom(actor: Actor): Promise<boolean> {
|
|
260
|
-
const actorUri = actor.id;
|
|
261
|
-
let actorHandle: string | undefined = undefined;
|
|
262
|
-
if (actorUri == null) return false;
|
|
263
|
-
for (let uri of acceptFollows) {
|
|
264
|
-
if (uri === "*") return true;
|
|
265
|
-
if (uri.startsWith("http:") || uri.startsWith("https:")) {
|
|
266
|
-
uri = new URL(uri).href; // normalize
|
|
267
|
-
if (uri === actorUri.href) return true;
|
|
268
|
-
}
|
|
269
|
-
if (actorHandle == null) actorHandle = await getActorHandle(actor);
|
|
270
|
-
if (actorHandle === uri) return true;
|
|
271
|
-
}
|
|
272
|
-
return false;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const peers: Record<string, Actor> = {};
|
|
276
|
-
|
|
277
|
-
function createSendDeleteToPeers(
|
|
278
|
-
actorOptions: { actorName: string; actorSummary: string },
|
|
279
|
-
): (server: TemporaryServer) => Promise<void> {
|
|
280
|
-
return async function sendDeleteToPeers(
|
|
281
|
-
server: TemporaryServer,
|
|
282
|
-
): Promise<void> {
|
|
283
|
-
const ctx = federation.createContext(new Request(server.url), {
|
|
284
|
-
activityIndex: -1,
|
|
285
|
-
actorName: actorOptions.actorName,
|
|
286
|
-
actorSummary: actorOptions.actorSummary,
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
const actor = (await ctx.getActor("i"))!;
|
|
290
|
-
try {
|
|
291
|
-
await ctx.sendActivity(
|
|
292
|
-
{ identifier: "i" },
|
|
293
|
-
Object.values(peers),
|
|
294
|
-
new Delete({
|
|
295
|
-
id: new URL(`#delete`, actor.id!),
|
|
296
|
-
actor: actor.id!,
|
|
297
|
-
to: PUBLIC_COLLECTION,
|
|
298
|
-
object: actor,
|
|
299
|
-
}),
|
|
300
|
-
);
|
|
301
|
-
} catch (error) {
|
|
302
|
-
logger.error(
|
|
303
|
-
"Failed to send Delete(Application) activities to peers:\n{error}",
|
|
304
|
-
{ error },
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const followers: Record<string, Actor> = {};
|
|
311
|
-
|
|
312
|
-
federation
|
|
313
|
-
.setInboxListeners("/{identifier}/inbox", "/inbox")
|
|
314
|
-
.setSharedKeyDispatcher((_) => ({ identifier: "i" }))
|
|
315
|
-
.on(Activity, async (ctx, activity) => {
|
|
316
|
-
activities[ctx.data.activityIndex].activity = activity;
|
|
317
|
-
for await (const actor of activity.getActors()) {
|
|
318
|
-
if (actor.id != null) peers[actor.id.href] = actor;
|
|
319
|
-
}
|
|
320
|
-
for await (const actor of activity.getAttributions()) {
|
|
321
|
-
if (actor.id != null) peers[actor.id.href] = actor;
|
|
322
|
-
}
|
|
323
|
-
if (activity instanceof Follow) {
|
|
324
|
-
if (acceptFollows.length < 1) return;
|
|
325
|
-
const objectId = activity.objectId;
|
|
326
|
-
if (objectId == null) return;
|
|
327
|
-
const parsed = ctx.parseUri(objectId);
|
|
328
|
-
if (parsed?.type !== "actor" || parsed.identifier !== "i") return;
|
|
329
|
-
const { identifier } = parsed;
|
|
330
|
-
const follower = await activity.getActor();
|
|
331
|
-
if (!isActor(follower)) return;
|
|
332
|
-
const accepts = await acceptsFollowFrom(follower);
|
|
333
|
-
if (!accepts || activity.id == null) {
|
|
334
|
-
logger.debug("Does not accept follow from {actor}.", {
|
|
335
|
-
actor: follower.id?.href,
|
|
336
|
-
});
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
logger.debug("Accepting follow from {actor}.", {
|
|
340
|
-
actor: follower.id?.href,
|
|
341
|
-
});
|
|
342
|
-
followers[activity.id.href] = follower;
|
|
343
|
-
await ctx.sendActivity(
|
|
344
|
-
{ identifier },
|
|
345
|
-
follower,
|
|
346
|
-
new Accept({
|
|
347
|
-
id: new URL(`#accepts/${follower.id?.href}`, ctx.getActorUri("i")),
|
|
348
|
-
actor: ctx.getActorUri(identifier),
|
|
349
|
-
object: activity.id,
|
|
350
|
-
}),
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
federation
|
|
356
|
-
.setFollowersDispatcher("/{identifier}/followers", (_ctx, identifier) => {
|
|
357
|
-
if (identifier !== "i") return null;
|
|
358
|
-
const items: Recipient[] = [];
|
|
359
|
-
for (const follower of Object.values(followers)) {
|
|
360
|
-
if (follower.id == null) continue;
|
|
361
|
-
items.push(follower);
|
|
362
|
-
}
|
|
363
|
-
return { items };
|
|
364
|
-
})
|
|
365
|
-
.setCounter((_ctx, identifier) => {
|
|
366
|
-
if (identifier !== "i") return null;
|
|
367
|
-
return Object.keys(followers).length;
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
federation
|
|
371
|
-
.setFollowingDispatcher(
|
|
372
|
-
"/{identifier}/following",
|
|
373
|
-
(_ctx, _identifier) => null,
|
|
374
|
-
)
|
|
375
|
-
.setCounter((_ctx, _identifier) => 0);
|
|
376
|
-
|
|
377
|
-
federation
|
|
378
|
-
.setOutboxDispatcher("/{identifier}/outbox", (_ctx, _identifier) => null)
|
|
379
|
-
.setCounter((_ctx, _identifier) => 0);
|
|
380
|
-
|
|
381
|
-
federation.setNodeInfoDispatcher("/nodeinfo/2.1", (_ctx) => {
|
|
382
|
-
return {
|
|
383
|
-
software: {
|
|
384
|
-
name: "fedify-cli",
|
|
385
|
-
version: metadata.version,
|
|
386
|
-
repository: new URL("https://github.com/fedify-dev/fedify"),
|
|
387
|
-
},
|
|
388
|
-
protocols: ["activitypub"],
|
|
389
|
-
usage: {
|
|
390
|
-
users: {
|
|
391
|
-
total: 1,
|
|
392
|
-
activeMonth: 1,
|
|
393
|
-
activeHalfyear: 1,
|
|
394
|
-
},
|
|
395
|
-
localComments: 0,
|
|
396
|
-
localPosts: 0,
|
|
397
|
-
},
|
|
398
|
-
};
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
function printServerInfo(fedCtx: Context<ContextData>): void {
|
|
402
|
-
const table = new Table({
|
|
403
|
-
chars: tableStyle,
|
|
404
|
-
style: { head: [], border: [] },
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
table.push(
|
|
408
|
-
{ "Actor handle:": colors.green(`i@${fedCtx.getActorUri("i").host}`) },
|
|
409
|
-
{ "Actor URI:": colors.green(fedCtx.getActorUri("i").href) },
|
|
410
|
-
{ "Actor inbox:": colors.green(fedCtx.getInboxUri("i").href) },
|
|
411
|
-
{ "Shared inbox:": colors.green(fedCtx.getInboxUri().href) },
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
console.log(table.toString());
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
async function printActivityEntry(
|
|
418
|
-
idx: number,
|
|
419
|
-
entry: ActivityEntry,
|
|
420
|
-
): Promise<void> {
|
|
421
|
-
const request = entry.request.clone();
|
|
422
|
-
const response = entry.response?.clone();
|
|
423
|
-
const url = new URL(request.url);
|
|
424
|
-
const activity = entry.activity;
|
|
425
|
-
const object = await activity?.getObject();
|
|
426
|
-
|
|
427
|
-
const table = new Table({
|
|
428
|
-
chars: tableStyle,
|
|
429
|
-
style: { head: [], border: [] },
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
table.push(
|
|
433
|
-
{ "Request #:": colors.bold(idx.toString()) },
|
|
434
|
-
{
|
|
435
|
-
"Activity type:": activity == null
|
|
436
|
-
? colors.red("failed to parse")
|
|
437
|
-
: colors.green(
|
|
438
|
-
`${activity.constructor.name}(${object?.constructor.name})`,
|
|
439
|
-
),
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
"HTTP request:": `${
|
|
443
|
-
request.method === "POST"
|
|
444
|
-
? colors.green("POST")
|
|
445
|
-
: colors.red(request.method)
|
|
446
|
-
} ${url.pathname + url.search}`,
|
|
447
|
-
},
|
|
448
|
-
...(response == null ? [] : [{
|
|
449
|
-
"HTTP response:": `${
|
|
450
|
-
response.ok
|
|
451
|
-
? colors.green(response.status.toString())
|
|
452
|
-
: colors.red(response.status.toString())
|
|
453
|
-
} ${response.statusText}`,
|
|
454
|
-
}]),
|
|
455
|
-
{ "Details": new URL(`/r/${idx}`, url).href },
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
console.log(table.toString());
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function getHandle<T extends string>(
|
|
462
|
-
c: HonoContext<BlankEnv, T, BlankInput>,
|
|
463
|
-
): string {
|
|
464
|
-
const url = new URL(c.req.url);
|
|
465
|
-
return `@i@${url.host}`;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const app = new Hono();
|
|
469
|
-
|
|
470
|
-
app.get("/", (c) => c.redirect("/r"));
|
|
471
|
-
|
|
472
|
-
app.get(
|
|
473
|
-
"/r",
|
|
474
|
-
(c) =>
|
|
475
|
-
c.html(
|
|
476
|
-
<ActivityListPage handle={getHandle(c)} entries={activities} />,
|
|
477
|
-
),
|
|
478
|
-
);
|
|
479
|
-
|
|
480
|
-
app.get("/r/:idx{[0-9]+}", (c) => {
|
|
481
|
-
const idx = parseInt(c.req.param("idx"));
|
|
482
|
-
const tab = c.req.query("tab") ?? "request";
|
|
483
|
-
const activity = activities[idx];
|
|
484
|
-
if (activity == null) return c.notFound();
|
|
485
|
-
if (
|
|
486
|
-
tab !== "request" && tab !== "response" && tab !== "raw-activity" &&
|
|
487
|
-
tab !== "compact-activity" && tab !== "expanded-activity" && tab !== "logs"
|
|
488
|
-
) {
|
|
489
|
-
return c.notFound();
|
|
490
|
-
}
|
|
491
|
-
return c.html(
|
|
492
|
-
<ActivityEntryPage
|
|
493
|
-
handle={getHandle(c)}
|
|
494
|
-
idx={idx}
|
|
495
|
-
entry={activity}
|
|
496
|
-
tabPage={tab}
|
|
497
|
-
/>,
|
|
498
|
-
);
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
function createFetchHandler(
|
|
502
|
-
actorOptions: { actorName: string; actorSummary: string },
|
|
503
|
-
): (request: Request) => Promise<Response> {
|
|
504
|
-
return async function fetch(request: Request): Promise<Response> {
|
|
505
|
-
const timestamp = Temporal.Now.instant();
|
|
506
|
-
const idx = activities.length;
|
|
507
|
-
const pathname = new URL(request.url).pathname;
|
|
508
|
-
if (pathname === "/r" || pathname.startsWith("/r/")) {
|
|
509
|
-
return app.fetch(request);
|
|
510
|
-
}
|
|
511
|
-
const inboxRequest = pathname === "/inbox" ||
|
|
512
|
-
pathname.startsWith("/i/inbox");
|
|
513
|
-
if (inboxRequest) {
|
|
514
|
-
recordingSink.startRecording();
|
|
515
|
-
// @ts-ignore: Work around `deno publish --dry-run` bug
|
|
516
|
-
activities.push({ timestamp, request: request.clone(), logs: [] });
|
|
517
|
-
}
|
|
518
|
-
const response = await federation.fetch(request, {
|
|
519
|
-
contextData: {
|
|
520
|
-
activityIndex: inboxRequest ? idx : -1,
|
|
521
|
-
actorName: actorOptions.actorName,
|
|
522
|
-
actorSummary: actorOptions.actorSummary,
|
|
523
|
-
},
|
|
524
|
-
onNotAcceptable: app.fetch.bind(app),
|
|
525
|
-
onNotFound: app.fetch.bind(app),
|
|
526
|
-
onUnauthorized: app.fetch.bind(app),
|
|
527
|
-
});
|
|
528
|
-
if (inboxRequest) {
|
|
529
|
-
recordingSink.stopRecording();
|
|
530
|
-
activities[idx].response = response.clone();
|
|
531
|
-
activities[idx].logs = recordingSink.getRecords();
|
|
532
|
-
await printActivityEntry(idx, activities[idx]);
|
|
533
|
-
}
|
|
534
|
-
return response;
|
|
535
|
-
};
|
|
536
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
concat,
|
|
3
|
-
filter,
|
|
4
|
-
keys,
|
|
5
|
-
map,
|
|
6
|
-
pick,
|
|
7
|
-
pipe,
|
|
8
|
-
toArray,
|
|
9
|
-
} from "@fxts/core/index.js";
|
|
10
|
-
import { uniq } from "es-toolkit";
|
|
11
|
-
import { realpathSync } from "node:fs";
|
|
12
|
-
import { join as joinPath, relative } from "node:path";
|
|
13
|
-
import { merge } from "../../utils.ts";
|
|
14
|
-
import biome from "../json/biome.json" with { type: "json" };
|
|
15
|
-
import vscodeSettingsForDeno from "../json/vscode-settings-for-deno.json" with {
|
|
16
|
-
type: "json",
|
|
17
|
-
};
|
|
18
|
-
import vscodeSettings from "../json/vscode-settings.json" with {
|
|
19
|
-
type: "json",
|
|
20
|
-
};
|
|
21
|
-
import type { InitCommandData } from "../types.ts";
|
|
22
|
-
import { PACKAGES_PATH } from "./const.ts";
|
|
23
|
-
import { getDependencies, getDevDependencies, joinDepsReg } from "./deps.ts";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Loads Deno configuration object with compiler options, unstable features, and tasks.
|
|
27
|
-
* Combines unstable features required by KV store and message queue with framework-specific options.
|
|
28
|
-
*
|
|
29
|
-
* @param param0 - Destructured initialization data containing KV, MQ, initializer, and directory
|
|
30
|
-
* @returns Configuration object with path and Deno-specific settings
|
|
31
|
-
*/
|
|
32
|
-
export const loadDenoConfig = (
|
|
33
|
-
data: InitCommandData,
|
|
34
|
-
) => ({
|
|
35
|
-
path: "deno.json",
|
|
36
|
-
data: {
|
|
37
|
-
...pick(["compilerOptions", "tasks"], data.initializer),
|
|
38
|
-
unstable: getUnstable(data),
|
|
39
|
-
nodeModulesDir: "auto",
|
|
40
|
-
imports: joinDepsReg("deno")(getDependencies(data)),
|
|
41
|
-
...(data.testMode ? { links: getLinks(data) } : {}),
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const getUnstable = <T extends Pick<InitCommandData, "kv" | "mq">>({
|
|
46
|
-
kv: { denoUnstable: kv = [] },
|
|
47
|
-
mq: { denoUnstable: mq = [] },
|
|
48
|
-
}: T) =>
|
|
49
|
-
pipe(
|
|
50
|
-
["temporal"],
|
|
51
|
-
concat(kv),
|
|
52
|
-
concat(mq),
|
|
53
|
-
toArray,
|
|
54
|
-
uniq,
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const getLinks = <
|
|
58
|
-
T extends Pick<InitCommandData, "kv" | "mq" | "initializer" | "dir">,
|
|
59
|
-
>({ kv, mq, initializer, dir }: T) =>
|
|
60
|
-
pipe(
|
|
61
|
-
{ "@fedify/fedify": "" },
|
|
62
|
-
merge(initializer.dependencies),
|
|
63
|
-
merge(kv.dependencies),
|
|
64
|
-
merge(mq.dependencies),
|
|
65
|
-
keys,
|
|
66
|
-
filter((dep) => dep.includes("@fedify/")),
|
|
67
|
-
map((dep) => dep.replace("@fedify/", "")),
|
|
68
|
-
map((dep) => joinPath(PACKAGES_PATH, dep)),
|
|
69
|
-
map((absolutePath) => realpathSync(absolutePath)),
|
|
70
|
-
map((realAbsolutePath) => relative(realpathSync(dir), realAbsolutePath)),
|
|
71
|
-
toArray,
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Loads TypeScript configuration object for Node.js/Bun projects.
|
|
76
|
-
* Uses compiler options from the framework initializer.
|
|
77
|
-
*
|
|
78
|
-
* @param param0 - Destructured initialization data containing initializer and directory
|
|
79
|
-
* @returns Configuration object with path and TypeScript compiler options
|
|
80
|
-
*/
|
|
81
|
-
export const loadTsConfig = ({ initializer, dir }: InitCommandData) => ({
|
|
82
|
-
path: joinPath(dir, "tsconfig.json"),
|
|
83
|
-
data: {
|
|
84
|
-
compilerOptions: initializer.compilerOptions,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Loads package.json configuration object for Node.js/Bun projects.
|
|
90
|
-
* Sets up ES modules and includes framework-specific npm scripts.
|
|
91
|
-
*
|
|
92
|
-
* @param param0 - Destructured initialization data containing initializer and directory
|
|
93
|
-
* @returns Configuration object with path and package.json settings
|
|
94
|
-
*/
|
|
95
|
-
export const loadPackageJson = (
|
|
96
|
-
data: InitCommandData,
|
|
97
|
-
) => ({
|
|
98
|
-
path: "package.json",
|
|
99
|
-
data: {
|
|
100
|
-
type: "module",
|
|
101
|
-
scripts: data.initializer.tasks,
|
|
102
|
-
dependencies: getDependencies(data),
|
|
103
|
-
devDependencies: getDevDependencies(data),
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Configuration objects for various development tool setup files.
|
|
109
|
-
* Contains predefined configurations for code formatting, VS Code settings, and extensions
|
|
110
|
-
* based on the project type (Node.js/Bun or Deno).
|
|
111
|
-
*/
|
|
112
|
-
export const devToolConfigs = {
|
|
113
|
-
biome: {
|
|
114
|
-
path: joinPath("biome.json"),
|
|
115
|
-
data: biome,
|
|
116
|
-
},
|
|
117
|
-
vscExt: {
|
|
118
|
-
path: joinPath(".vscode", "extensions.json"),
|
|
119
|
-
data: { recommendations: ["biomejs.biome"] },
|
|
120
|
-
},
|
|
121
|
-
vscSet: {
|
|
122
|
-
path: joinPath(".vscode", "settings.json"),
|
|
123
|
-
data: vscodeSettings,
|
|
124
|
-
},
|
|
125
|
-
vscSetDeno: {
|
|
126
|
-
path: joinPath(".vscode", "settings.json"),
|
|
127
|
-
data: vscodeSettingsForDeno,
|
|
128
|
-
},
|
|
129
|
-
vscExtDeno: {
|
|
130
|
-
path: joinPath(".vscode", "extensions.json"),
|
|
131
|
-
data: { recommendations: ["denoland.vscode-deno"] },
|
|
132
|
-
},
|
|
133
|
-
} as const;
|