@fedify/cli 1.8.12 → 2.0.0-dev.1757
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 +72 -0
- package/dist/cache.js +17 -0
- package/dist/deno.js +72 -0
- package/dist/docloader.js +52 -0
- package/dist/globals.js +49 -0
- package/dist/imagerenderer.js +105 -0
- package/dist/inbox/rendercode.js +57 -0
- package/dist/inbox/view.js +508 -0
- package/dist/inbox.js +315 -0
- package/dist/init/action/configs.js +81 -0
- package/dist/init/action/deps.js +52 -0
- package/dist/init/action/dir.js +16 -0
- package/dist/init/action/env.js +13 -0
- package/dist/init/action/install.js +22 -0
- package/dist/init/action/mod.js +39 -0
- package/dist/init/action/notice.js +62 -0
- package/dist/init/action/patch.js +141 -0
- package/dist/init/action/precommand.js +23 -0
- package/dist/init/action/recommend.js +24 -0
- package/dist/init/action/set.js +31 -0
- package/dist/init/action/templates.js +57 -0
- package/dist/init/action/utils.js +50 -0
- package/dist/init/ask/dir.js +82 -0
- package/dist/init/ask/kv.js +33 -0
- package/dist/init/ask/mod.js +16 -0
- package/dist/init/ask/mq.js +33 -0
- package/dist/init/ask/pm.js +49 -0
- package/dist/init/ask/wf.js +29 -0
- package/dist/init/command.js +25 -0
- package/dist/init/const.js +31 -0
- package/dist/init/json/biome.js +24 -0
- package/dist/init/json/kv.js +53 -0
- package/dist/init/json/mq.js +72 -0
- package/dist/init/json/pm.js +44 -0
- package/dist/init/json/rt.js +39 -0
- package/dist/init/json/vscode-settings-for-deno.js +53 -0
- package/dist/init/json/vscode-settings.js +49 -0
- package/dist/init/lib.js +129 -0
- package/dist/init/mod.js +5 -0
- package/dist/init/webframeworks.js +133 -0
- package/dist/kv.bun.js +17 -0
- package/dist/kv.node.js +17 -0
- package/dist/log.js +52 -0
- package/dist/lookup.js +287 -0
- package/dist/mod.js +34 -0
- package/dist/nodeinfo.js +261 -0
- package/dist/table.js +24 -0
- package/dist/tempserver.js +71 -0
- package/dist/tunnel.js +21 -0
- package/dist/utils.js +67 -0
- package/dist/webfinger/action.js +44 -0
- package/dist/webfinger/command.js +20 -0
- package/dist/webfinger/error.js +47 -0
- package/dist/webfinger/lib.js +45 -0
- package/dist/webfinger/mod.js +5 -0
- package/package.json +64 -24
- package/scripts/pack.ts +64 -0
- package/src/cache.ts +17 -0
- package/src/docloader.ts +67 -0
- package/src/globals.ts +43 -0
- package/src/imagerenderer.ts +149 -0
- package/src/inbox/entry.ts +10 -0
- package/src/inbox/rendercode.ts +68 -0
- package/src/inbox/view.tsx +598 -0
- package/src/inbox.tsx +535 -0
- package/src/init/action/configs.ts +88 -0
- package/src/init/action/deps.ts +93 -0
- package/src/init/action/dir.ts +11 -0
- package/src/init/action/env.ts +14 -0
- package/src/init/action/install.ts +59 -0
- package/src/init/action/mod.ts +66 -0
- package/src/init/action/notice.ts +101 -0
- package/src/init/action/patch.ts +212 -0
- package/src/init/action/precommand.ts +22 -0
- package/src/init/action/recommend.ts +38 -0
- package/src/init/action/set.ts +78 -0
- package/src/init/action/templates.ts +95 -0
- package/src/init/action/utils.ts +64 -0
- package/src/init/ask/dir.ts +98 -0
- package/src/init/ask/kv.ts +39 -0
- package/src/init/ask/mod.ts +23 -0
- package/src/init/ask/mq.ts +37 -0
- package/src/init/ask/pm.ts +58 -0
- package/src/init/ask/wf.ts +27 -0
- package/src/init/command.ts +64 -0
- package/src/init/const.ts +4 -0
- package/src/init/json/biome.json +17 -0
- package/src/init/json/kv.json +39 -0
- package/src/init/json/mq.json +95 -0
- package/src/init/json/pm.json +47 -0
- package/src/init/json/rt.json +42 -0
- package/src/init/json/vscode-settings-for-deno.json +43 -0
- package/src/init/json/vscode-settings.json +41 -0
- package/src/init/lib.ts +220 -0
- package/src/init/mod.ts +2 -0
- package/src/init/templates/defaults/federation.ts.tpl +23 -0
- package/src/init/templates/defaults/logging.ts.tpl +23 -0
- package/src/init/templates/express/app.ts.tpl +16 -0
- package/src/init/templates/express/index.ts.tpl +6 -0
- package/src/init/templates/hono/app.tsx.tpl +14 -0
- package/src/init/templates/hono/index/bun.ts.tpl +10 -0
- package/src/init/templates/hono/index/deno.ts.tpl +13 -0
- package/src/init/templates/hono/index/node.ts.tpl +14 -0
- package/src/init/templates/next/middleware.ts.tpl +45 -0
- package/src/init/templates/nitro/nitro.config.ts.tpl +5 -0
- package/src/init/templates/nitro/server/error.ts.tpl +3 -0
- package/src/init/templates/nitro/server/middleware/federation.ts.tpl +8 -0
- package/src/init/types.ts +88 -0
- package/src/init/webframeworks.ts +151 -0
- package/src/kv.bun.ts +12 -0
- package/src/kv.node.ts +11 -0
- package/src/log.ts +64 -0
- package/src/lookup.test.ts +182 -0
- package/src/lookup.ts +558 -0
- package/src/mod.ts +45 -0
- package/src/nodeinfo.test.ts +229 -0
- package/src/nodeinfo.ts +447 -0
- package/src/table.ts +17 -0
- package/src/tempserver.ts +87 -0
- package/src/tunnel.ts +32 -0
- package/src/utils.ts +136 -0
- package/src/webfinger/action.ts +50 -0
- package/src/webfinger/command.ts +59 -0
- package/src/webfinger/error.ts +47 -0
- package/src/webfinger/lib.ts +37 -0
- package/src/webfinger/mod.test.ts +79 -0
- package/src/webfinger/mod.ts +2 -0
- package/tsdown.config.ts +24 -0
- package/src/install.mjs +0 -189
- package/src/run.mjs +0 -22
package/dist/inbox.js
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import deno_default from "./deno.js";
|
|
5
|
+
import { getDocumentLoader } from "./docloader.js";
|
|
6
|
+
import { recordingSink } from "./log.js";
|
|
7
|
+
import { configureLogging, debugOption } from "./globals.js";
|
|
8
|
+
import { ActivityEntryPage, ActivityListPage } from "./inbox/view.js";
|
|
9
|
+
import { tableStyle } from "./table.js";
|
|
10
|
+
import { spawnTemporaryServer } from "./tempserver.js";
|
|
11
|
+
import { colors } from "./utils.js";
|
|
12
|
+
import { command, constant, merge, message, multiple, object, option, optional, string, withDefault } from "@optique/core";
|
|
13
|
+
import { Accept, Activity, Application, Delete, Endpoints, Follow, Image, MemoryKvStore, PUBLIC_COLLECTION, createFederation, generateCryptoKeyPair, getActorHandle, isActor, lookupObject } from "@fedify/fedify";
|
|
14
|
+
import { getLogger } from "@logtape/logtape";
|
|
15
|
+
import Table from "cli-table3";
|
|
16
|
+
import { Hono } from "hono";
|
|
17
|
+
import process from "node:process";
|
|
18
|
+
import ora from "ora";
|
|
19
|
+
import { jsx } from "hono/jsx/jsx-runtime";
|
|
20
|
+
|
|
21
|
+
//#region src/inbox.tsx
|
|
22
|
+
const logger = getLogger([
|
|
23
|
+
"fedify",
|
|
24
|
+
"cli",
|
|
25
|
+
"inbox"
|
|
26
|
+
]);
|
|
27
|
+
const inboxCommand = command("inbox", merge(object("Inbox options", {
|
|
28
|
+
command: constant("inbox"),
|
|
29
|
+
follow: optional(multiple(option("-f", "--follow", string({ metavar: "URI" }), { description: message`Follow the given actor. The argument can be either an actor URI or a handle. Can be specified multiple times.` }))),
|
|
30
|
+
acceptFollow: optional(multiple(option("-a", "--accept-follow", string({ metavar: "URI" }), { description: 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.` }))),
|
|
31
|
+
noTunnel: option("-T", "--no-tunnel", { description: message`Do not tunnel the ephemeral ActivityPub server to the public Internet.` }),
|
|
32
|
+
actorName: withDefault(option("--actor-name", string({ metavar: "NAME" }), { description: message`Customize the actor display name.` }), "Fedify Ephemeral Inbox"),
|
|
33
|
+
actorSummary: withDefault(option("--actor-summary", string({ metavar: "SUMMARY" }), { description: message`Customize the actor description.` }), "An ephemeral ActivityPub inbox for testing purposes.")
|
|
34
|
+
}), debugOption), { description: 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.` });
|
|
35
|
+
async function runInbox(command$1) {
|
|
36
|
+
const fetch = createFetchHandler({
|
|
37
|
+
actorName: command$1.actorName,
|
|
38
|
+
actorSummary: command$1.actorSummary
|
|
39
|
+
});
|
|
40
|
+
const sendDeleteToPeers = createSendDeleteToPeers({
|
|
41
|
+
actorName: command$1.actorName,
|
|
42
|
+
actorSummary: command$1.actorSummary
|
|
43
|
+
});
|
|
44
|
+
if (command$1.debug) await configureLogging();
|
|
45
|
+
const spinner = ora({
|
|
46
|
+
text: "Spinning up an ephemeral ActivityPub server...",
|
|
47
|
+
discardStdin: false
|
|
48
|
+
}).start();
|
|
49
|
+
const server = await spawnTemporaryServer(fetch, { noTunnel: command$1.noTunnel });
|
|
50
|
+
spinner.succeed(`The ephemeral ActivityPub server is up and running: ${colors.green(server.url.href)}`);
|
|
51
|
+
process.on("SIGINT", () => {
|
|
52
|
+
spinner.stop();
|
|
53
|
+
const peersCnt = Object.keys(peers).length;
|
|
54
|
+
spinner.start(`Sending Delete(Application) activities to the ${peersCnt} ${peersCnt === 1 ? "peer" : "peers"}...`);
|
|
55
|
+
sendDeleteToPeers(server).then(() => {
|
|
56
|
+
spinner.text = "Stopping server...";
|
|
57
|
+
server.close().then(() => {
|
|
58
|
+
spinner.succeed("Server stopped.");
|
|
59
|
+
process.exit(0);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
spinner.start();
|
|
64
|
+
const fedCtx = federation.createContext(server.url, {
|
|
65
|
+
activityIndex: -1,
|
|
66
|
+
actorName: command$1.actorName,
|
|
67
|
+
actorSummary: command$1.actorSummary
|
|
68
|
+
});
|
|
69
|
+
if (command$1.acceptFollow != null && command$1.acceptFollow.length > 0) acceptFollows.push(...command$1.acceptFollow ?? []);
|
|
70
|
+
if (command$1.follow != null && command$1.follow.length > 0) {
|
|
71
|
+
spinner.text = "Following actors...";
|
|
72
|
+
const documentLoader = await fedCtx.getDocumentLoader({ identifier: "i" });
|
|
73
|
+
for (const uri of command$1.follow) {
|
|
74
|
+
spinner.text = `Following ${colors.green(uri)}...`;
|
|
75
|
+
const actor = await lookupObject(uri, { documentLoader });
|
|
76
|
+
if (!isActor(actor)) {
|
|
77
|
+
spinner.fail(`Not an actor: ${colors.red(uri)}`);
|
|
78
|
+
spinner.start();
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (actor.id != null) peers[actor.id?.href] = actor;
|
|
82
|
+
await fedCtx.sendActivity({ identifier: "i" }, actor, new Follow({
|
|
83
|
+
id: new URL(`#follows/${actor.id?.href}`, fedCtx.getActorUri("i")),
|
|
84
|
+
actor: fedCtx.getActorUri("i"),
|
|
85
|
+
object: actor.id
|
|
86
|
+
}));
|
|
87
|
+
spinner.succeed(`Sent follow request to ${colors.green(uri)}.`);
|
|
88
|
+
spinner.start();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
spinner.stop();
|
|
92
|
+
printServerInfo(fedCtx);
|
|
93
|
+
}
|
|
94
|
+
const federationDocumentLoader = await getDocumentLoader();
|
|
95
|
+
const federation = createFederation({
|
|
96
|
+
kv: new MemoryKvStore(),
|
|
97
|
+
documentLoaderFactory: () => {
|
|
98
|
+
return federationDocumentLoader;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const time = Temporal.Now.instant();
|
|
102
|
+
let actorKeyPairs = void 0;
|
|
103
|
+
federation.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
|
|
104
|
+
if (identifier !== "i") return null;
|
|
105
|
+
return new Application({
|
|
106
|
+
id: ctx.getActorUri(identifier),
|
|
107
|
+
preferredUsername: identifier,
|
|
108
|
+
name: ctx.data.actorName,
|
|
109
|
+
summary: ctx.data.actorSummary,
|
|
110
|
+
inbox: ctx.getInboxUri(identifier),
|
|
111
|
+
endpoints: new Endpoints({ sharedInbox: ctx.getInboxUri() }),
|
|
112
|
+
followers: ctx.getFollowersUri(identifier),
|
|
113
|
+
following: ctx.getFollowingUri(identifier),
|
|
114
|
+
outbox: ctx.getOutboxUri(identifier),
|
|
115
|
+
manuallyApprovesFollowers: true,
|
|
116
|
+
published: time,
|
|
117
|
+
icon: new Image({
|
|
118
|
+
url: new URL("https://fedify.dev/logo.png"),
|
|
119
|
+
mediaType: "image/png"
|
|
120
|
+
}),
|
|
121
|
+
publicKey: (await ctx.getActorKeyPairs(identifier))[0].cryptographicKey,
|
|
122
|
+
assertionMethods: (await ctx.getActorKeyPairs(identifier)).map((pair) => pair.multikey),
|
|
123
|
+
url: ctx.getActorUri(identifier)
|
|
124
|
+
});
|
|
125
|
+
}).setKeyPairsDispatcher(async (_ctxData, identifier) => {
|
|
126
|
+
if (identifier !== "i") return [];
|
|
127
|
+
if (actorKeyPairs == null) actorKeyPairs = [await generateCryptoKeyPair("RSASSA-PKCS1-v1_5"), await generateCryptoKeyPair("Ed25519")];
|
|
128
|
+
return actorKeyPairs;
|
|
129
|
+
});
|
|
130
|
+
const activities = [];
|
|
131
|
+
const acceptFollows = [];
|
|
132
|
+
async function acceptsFollowFrom(actor) {
|
|
133
|
+
const actorUri = actor.id;
|
|
134
|
+
let actorHandle = void 0;
|
|
135
|
+
if (actorUri == null) return false;
|
|
136
|
+
for (let uri of acceptFollows) {
|
|
137
|
+
if (uri === "*") return true;
|
|
138
|
+
if (uri.startsWith("http:") || uri.startsWith("https:")) {
|
|
139
|
+
uri = new URL(uri).href;
|
|
140
|
+
if (uri === actorUri.href) return true;
|
|
141
|
+
}
|
|
142
|
+
if (actorHandle == null) actorHandle = await getActorHandle(actor);
|
|
143
|
+
if (actorHandle === uri) return true;
|
|
144
|
+
}
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
const peers = {};
|
|
148
|
+
function createSendDeleteToPeers(actorOptions) {
|
|
149
|
+
return async function sendDeleteToPeers(server) {
|
|
150
|
+
const ctx = federation.createContext(new Request(server.url), {
|
|
151
|
+
activityIndex: -1,
|
|
152
|
+
actorName: actorOptions.actorName,
|
|
153
|
+
actorSummary: actorOptions.actorSummary
|
|
154
|
+
});
|
|
155
|
+
const actor = await ctx.getActor("i");
|
|
156
|
+
try {
|
|
157
|
+
await ctx.sendActivity({ identifier: "i" }, Object.values(peers), new Delete({
|
|
158
|
+
id: new URL(`#delete`, actor.id),
|
|
159
|
+
actor: actor.id,
|
|
160
|
+
to: PUBLIC_COLLECTION,
|
|
161
|
+
object: actor
|
|
162
|
+
}));
|
|
163
|
+
} catch (error) {
|
|
164
|
+
logger.error("Failed to send Delete(Application) activities to peers:\n{error}", { error });
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const followers = {};
|
|
169
|
+
federation.setInboxListeners("/{identifier}/inbox", "/inbox").setSharedKeyDispatcher((_) => ({ identifier: "i" })).on(Activity, async (ctx, activity) => {
|
|
170
|
+
activities[ctx.data.activityIndex].activity = activity;
|
|
171
|
+
for await (const actor of activity.getActors()) if (actor.id != null) peers[actor.id.href] = actor;
|
|
172
|
+
for await (const actor of activity.getAttributions()) if (actor.id != null) peers[actor.id.href] = actor;
|
|
173
|
+
if (activity instanceof Follow) {
|
|
174
|
+
if (acceptFollows.length < 1) return;
|
|
175
|
+
const objectId = activity.objectId;
|
|
176
|
+
if (objectId == null) return;
|
|
177
|
+
const parsed = ctx.parseUri(objectId);
|
|
178
|
+
if (parsed?.type !== "actor" || parsed.identifier !== "i") return;
|
|
179
|
+
const { identifier } = parsed;
|
|
180
|
+
const follower = await activity.getActor();
|
|
181
|
+
if (!isActor(follower)) return;
|
|
182
|
+
const accepts = await acceptsFollowFrom(follower);
|
|
183
|
+
if (!accepts || activity.id == null) {
|
|
184
|
+
logger.debug("Does not accept follow from {actor}.", { actor: follower.id?.href });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
logger.debug("Accepting follow from {actor}.", { actor: follower.id?.href });
|
|
188
|
+
followers[activity.id.href] = follower;
|
|
189
|
+
await ctx.sendActivity({ identifier }, follower, new Accept({
|
|
190
|
+
id: new URL(`#accepts/${follower.id?.href}`, ctx.getActorUri("i")),
|
|
191
|
+
actor: ctx.getActorUri(identifier),
|
|
192
|
+
object: activity.id
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
federation.setFollowersDispatcher("/{identifier}/followers", (_ctx, identifier) => {
|
|
197
|
+
if (identifier !== "i") return null;
|
|
198
|
+
const items = [];
|
|
199
|
+
for (const follower of Object.values(followers)) {
|
|
200
|
+
if (follower.id == null) continue;
|
|
201
|
+
items.push(follower);
|
|
202
|
+
}
|
|
203
|
+
return { items };
|
|
204
|
+
}).setCounter((_ctx, identifier) => {
|
|
205
|
+
if (identifier !== "i") return null;
|
|
206
|
+
return Object.keys(followers).length;
|
|
207
|
+
});
|
|
208
|
+
federation.setFollowingDispatcher("/{identifier}/following", (_ctx, _identifier) => null).setCounter((_ctx, _identifier) => 0);
|
|
209
|
+
federation.setOutboxDispatcher("/{identifier}/outbox", (_ctx, _identifier) => null).setCounter((_ctx, _identifier) => 0);
|
|
210
|
+
federation.setNodeInfoDispatcher("/nodeinfo/2.1", (_ctx) => {
|
|
211
|
+
return {
|
|
212
|
+
software: {
|
|
213
|
+
name: "fedify-cli",
|
|
214
|
+
version: deno_default.version,
|
|
215
|
+
repository: new URL("https://github.com/fedify-dev/fedify")
|
|
216
|
+
},
|
|
217
|
+
protocols: ["activitypub"],
|
|
218
|
+
usage: {
|
|
219
|
+
users: {
|
|
220
|
+
total: 1,
|
|
221
|
+
activeMonth: 1,
|
|
222
|
+
activeHalfyear: 1
|
|
223
|
+
},
|
|
224
|
+
localComments: 0,
|
|
225
|
+
localPosts: 0
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
function printServerInfo(fedCtx) {
|
|
230
|
+
const table = new Table({
|
|
231
|
+
chars: tableStyle,
|
|
232
|
+
style: {
|
|
233
|
+
head: [],
|
|
234
|
+
border: []
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
table.push({ "Actor handle:": colors.green(`i@${fedCtx.getActorUri("i").host}`) }, { "Actor URI:": colors.green(fedCtx.getActorUri("i").href) }, { "Actor inbox:": colors.green(fedCtx.getInboxUri("i").href) }, { "Shared inbox:": colors.green(fedCtx.getInboxUri().href) });
|
|
238
|
+
console.log(table.toString());
|
|
239
|
+
}
|
|
240
|
+
async function printActivityEntry(idx, entry) {
|
|
241
|
+
const request = entry.request.clone();
|
|
242
|
+
const response = entry.response?.clone();
|
|
243
|
+
const url = new URL(request.url);
|
|
244
|
+
const activity = entry.activity;
|
|
245
|
+
const object$1 = await activity?.getObject();
|
|
246
|
+
const table = new Table({
|
|
247
|
+
chars: tableStyle,
|
|
248
|
+
style: {
|
|
249
|
+
head: [],
|
|
250
|
+
border: []
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
table.push({ "Request #:": colors.bold(idx.toString()) }, { "Activity type:": activity == null ? colors.red("failed to parse") : colors.green(`${activity.constructor.name}(${object$1?.constructor.name})`) }, { "HTTP request:": `${request.method === "POST" ? colors.green("POST") : colors.red(request.method)} ${url.pathname + url.search}` }, ...response == null ? [] : [{ "HTTP response:": `${response.ok ? colors.green(response.status.toString()) : colors.red(response.status.toString())} ${response.statusText}` }], { "Details": new URL(`/r/${idx}`, url).href });
|
|
254
|
+
console.log(table.toString());
|
|
255
|
+
}
|
|
256
|
+
function getHandle(c) {
|
|
257
|
+
const url = new URL(c.req.url);
|
|
258
|
+
return `@i@${url.host}`;
|
|
259
|
+
}
|
|
260
|
+
const app = new Hono();
|
|
261
|
+
app.get("/", (c) => c.redirect("/r"));
|
|
262
|
+
app.get("/r", (c) => c.html(/* @__PURE__ */ jsx(ActivityListPage, {
|
|
263
|
+
handle: getHandle(c),
|
|
264
|
+
entries: activities
|
|
265
|
+
})));
|
|
266
|
+
app.get("/r/:idx{[0-9]+}", (c) => {
|
|
267
|
+
const idx = parseInt(c.req.param("idx"));
|
|
268
|
+
const tab = c.req.query("tab") ?? "request";
|
|
269
|
+
const activity = activities[idx];
|
|
270
|
+
if (activity == null) return c.notFound();
|
|
271
|
+
if (tab !== "request" && tab !== "response" && tab !== "raw-activity" && tab !== "compact-activity" && tab !== "expanded-activity" && tab !== "logs") return c.notFound();
|
|
272
|
+
return c.html(/* @__PURE__ */ jsx(ActivityEntryPage, {
|
|
273
|
+
handle: getHandle(c),
|
|
274
|
+
idx,
|
|
275
|
+
entry: activity,
|
|
276
|
+
tabPage: tab
|
|
277
|
+
}));
|
|
278
|
+
});
|
|
279
|
+
function createFetchHandler(actorOptions) {
|
|
280
|
+
return async function fetch(request) {
|
|
281
|
+
const timestamp = Temporal.Now.instant();
|
|
282
|
+
const idx = activities.length;
|
|
283
|
+
const pathname = new URL(request.url).pathname;
|
|
284
|
+
if (pathname === "/r" || pathname.startsWith("/r/")) return app.fetch(request);
|
|
285
|
+
const inboxRequest = pathname === "/inbox" || pathname.startsWith("/i/inbox");
|
|
286
|
+
if (inboxRequest) {
|
|
287
|
+
recordingSink.startRecording();
|
|
288
|
+
activities.push({
|
|
289
|
+
timestamp,
|
|
290
|
+
request: request.clone(),
|
|
291
|
+
logs: []
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
const response = await federation.fetch(request, {
|
|
295
|
+
contextData: {
|
|
296
|
+
activityIndex: inboxRequest ? idx : -1,
|
|
297
|
+
actorName: actorOptions.actorName,
|
|
298
|
+
actorSummary: actorOptions.actorSummary
|
|
299
|
+
},
|
|
300
|
+
onNotAcceptable: app.fetch.bind(app),
|
|
301
|
+
onNotFound: app.fetch.bind(app),
|
|
302
|
+
onUnauthorized: app.fetch.bind(app)
|
|
303
|
+
});
|
|
304
|
+
if (inboxRequest) {
|
|
305
|
+
recordingSink.stopRecording();
|
|
306
|
+
activities[idx].response = response.clone();
|
|
307
|
+
activities[idx].logs = recordingSink.getRecords();
|
|
308
|
+
await printActivityEntry(idx, activities[idx]);
|
|
309
|
+
}
|
|
310
|
+
return response;
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
//#endregion
|
|
315
|
+
export { inboxCommand, runInbox };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import biome_default from "../json/biome.js";
|
|
5
|
+
import vscode_settings_for_deno_default from "../json/vscode-settings-for-deno.js";
|
|
6
|
+
import vscode_settings_default from "../json/vscode-settings.js";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
//#region src/init/action/configs.ts
|
|
10
|
+
/**
|
|
11
|
+
* Loads Deno configuration object with compiler options, unstable features, and tasks.
|
|
12
|
+
* Combines unstable features required by KV store and message queue with framework-specific options.
|
|
13
|
+
*
|
|
14
|
+
* @param param0 - Destructured initialization data containing KV, MQ, initializer, and directory
|
|
15
|
+
* @returns Configuration object with path and Deno-specific settings
|
|
16
|
+
*/
|
|
17
|
+
const loadDenoConfig = ({ kv, mq, initializer, dir }) => ({
|
|
18
|
+
path: join(dir, "deno.json"),
|
|
19
|
+
data: { compilerOptions: initializer.compilerOptions },
|
|
20
|
+
unstable: [
|
|
21
|
+
"temporal",
|
|
22
|
+
...kv.denoUnstable ?? [],
|
|
23
|
+
...mq.denoUnstable ?? []
|
|
24
|
+
],
|
|
25
|
+
tasks: initializer.tasks
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Loads TypeScript configuration object for Node.js/Bun projects.
|
|
29
|
+
* Uses compiler options from the framework initializer.
|
|
30
|
+
*
|
|
31
|
+
* @param param0 - Destructured initialization data containing initializer and directory
|
|
32
|
+
* @returns Configuration object with path and TypeScript compiler options
|
|
33
|
+
*/
|
|
34
|
+
const loadTsConfig = ({ initializer, dir }) => ({
|
|
35
|
+
path: join(dir, "tsconfig.json"),
|
|
36
|
+
data: { compilerOptions: initializer.compilerOptions }
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Loads package.json configuration object for Node.js/Bun projects.
|
|
40
|
+
* Sets up ES modules and includes framework-specific npm scripts.
|
|
41
|
+
*
|
|
42
|
+
* @param param0 - Destructured initialization data containing initializer and directory
|
|
43
|
+
* @returns Configuration object with path and package.json settings
|
|
44
|
+
*/
|
|
45
|
+
const loadPackageJson = ({ initializer, dir }) => ({
|
|
46
|
+
path: join(dir, "package.json"),
|
|
47
|
+
data: {
|
|
48
|
+
type: "module",
|
|
49
|
+
scripts: initializer.tasks
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Configuration objects for various development tool setup files.
|
|
54
|
+
* Contains predefined configurations for code formatting, VS Code settings, and extensions
|
|
55
|
+
* based on the project type (Node.js/Bun or Deno).
|
|
56
|
+
*/
|
|
57
|
+
const devToolConfigs = {
|
|
58
|
+
biome: {
|
|
59
|
+
path: join("biome.json"),
|
|
60
|
+
data: biome_default
|
|
61
|
+
},
|
|
62
|
+
vscExt: {
|
|
63
|
+
path: join(".vscode", "extensions.json"),
|
|
64
|
+
data: { recommendations: ["biomejs.biome"] }
|
|
65
|
+
},
|
|
66
|
+
vscSet: {
|
|
67
|
+
path: join(".vscode", "settings.json"),
|
|
68
|
+
data: vscode_settings_default
|
|
69
|
+
},
|
|
70
|
+
vscSetDeno: {
|
|
71
|
+
path: join(".vscode", "settings.json"),
|
|
72
|
+
data: vscode_settings_for_deno_default
|
|
73
|
+
},
|
|
74
|
+
vscExtDeno: {
|
|
75
|
+
path: join(".vscode", "extensions.json"),
|
|
76
|
+
data: { recommendations: ["denoland.vscode-deno"] }
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { devToolConfigs, loadDenoConfig, loadPackageJson, loadTsConfig };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { merge } from "../../utils.js";
|
|
5
|
+
import { PACKAGE_VERSION } from "../lib.js";
|
|
6
|
+
import { entries, map, pipe, toArray } from "@fxts/core";
|
|
7
|
+
|
|
8
|
+
//#region src/init/action/deps.ts
|
|
9
|
+
/**
|
|
10
|
+
* Gathers all dependencies required for the project based on the initializer,
|
|
11
|
+
* key-value store, and message queue configurations.
|
|
12
|
+
*
|
|
13
|
+
* @param data - Web Framework initializer, key-value store and message queue descriptions
|
|
14
|
+
* @returns A record of dependencies with their versions
|
|
15
|
+
*/
|
|
16
|
+
const getDependencies = ({ initializer, kv, mq }) => pipe({
|
|
17
|
+
"@fedify/fedify": PACKAGE_VERSION,
|
|
18
|
+
"@logtape/logtape": "^1.1.0"
|
|
19
|
+
}, merge(initializer.dependencies), merge(kv.dependencies), merge(mq.dependencies));
|
|
20
|
+
/** Gathers all devDependencies required for the project based on the initializer,
|
|
21
|
+
* key-value store, and message queue configurations, including Biome for linting/formatting.
|
|
22
|
+
*
|
|
23
|
+
* @param data - Web Framework initializer, key-value store and message queue descriptions
|
|
24
|
+
* @returns A record of devDependencies with their versions
|
|
25
|
+
*/
|
|
26
|
+
const getDevDependencies = ({ initializer, kv, mq }) => pipe({ "@biomejs/biome": "^2.2.4" }, merge(initializer.devDependencies), merge(kv.devDependencies), merge(mq.devDependencies));
|
|
27
|
+
/**
|
|
28
|
+
* Generates the command-line arguments needed to add dependencies or devDependencies
|
|
29
|
+
* using the specified package manager.
|
|
30
|
+
* If it is devDependencies, the '-D' flag is included.
|
|
31
|
+
*
|
|
32
|
+
* @param param0 - Object containing the package manager and a boolean indicating if dev dependencies are to be added
|
|
33
|
+
* @yields The command-line arguments as strings
|
|
34
|
+
*/
|
|
35
|
+
function* getAddDepsArgs({ packageManager, dev = false }) {
|
|
36
|
+
yield packageManager;
|
|
37
|
+
yield "add";
|
|
38
|
+
if (dev) yield "-D";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Joins package names with their versions for installation commands.
|
|
42
|
+
* For Deno, it prefixes packages with 'jsr:' unless they already start with 'npm:'.
|
|
43
|
+
*
|
|
44
|
+
* @param data - Package manager and dependencies to be joined with versions
|
|
45
|
+
* @returns `${registry}:${package}@${version}`[] for deno or `${package}@${version}`[] for others
|
|
46
|
+
*/
|
|
47
|
+
const joinDepsVer = ({ packageManager: pm, dependencies }) => pipe(dependencies, entries, map(([name, version]) => `${getPackageName(pm, name)}@${getPackageVersion(pm, version)}`), toArray);
|
|
48
|
+
const getPackageName = (pm, name) => pm !== "deno" ? name : name.startsWith("npm:") ? name.substring(4) : !name.startsWith("npm:") ? `jsr:${name}` : name;
|
|
49
|
+
const getPackageVersion = (pm, version) => pm !== "deno" && version.includes("+") ? version.substring(0, version.indexOf("+")) : version;
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
export { getAddDepsArgs, getDependencies, getDevDependencies, joinDepsVer };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { mkdir } from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region src/init/action/dir.ts
|
|
7
|
+
/**
|
|
8
|
+
* Creates the target directory if it does not exist.
|
|
9
|
+
*
|
|
10
|
+
* @param data - The directory
|
|
11
|
+
* @returns A promise that resolves when the directory is created
|
|
12
|
+
*/
|
|
13
|
+
const makeDirIfHyd = ({ dir }) => mkdir(dir, { recursive: true });
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { makeDirIfHyd };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { notEmpty } from "../../utils.js";
|
|
5
|
+
import { noticeConfigEnv, noticeEnvKeyValue } from "./notice.js";
|
|
6
|
+
import { entries, forEach, pipeLazy, tap, toArray, when } from "@fxts/core";
|
|
7
|
+
|
|
8
|
+
//#region src/init/action/env.ts
|
|
9
|
+
const recommendConfigEnv = pipeLazy((data) => data.env, entries, toArray, when(notEmpty, tap(noticeConfigEnv)), forEach(noticeEnvKeyValue));
|
|
10
|
+
var env_default = recommendConfigEnv;
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { env_default as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { notEmpty, runSubCommand, set } from "../../utils.js";
|
|
5
|
+
import { noticeErrorWhileAddDeps } from "./notice.js";
|
|
6
|
+
import { getAddDepsArgs, getDependencies, getDevDependencies, joinDepsVer } from "./deps.js";
|
|
7
|
+
import { isDeno } from "./utils.js";
|
|
8
|
+
import { always, concat, pipe, tap, toArray, unless, when } from "@fxts/core";
|
|
9
|
+
|
|
10
|
+
//#region src/init/action/install.ts
|
|
11
|
+
const installDependencies = (data) => pipe(data, tap(installDeps), unless(isDeno, tap(installDevDeps)));
|
|
12
|
+
var install_default = installDependencies;
|
|
13
|
+
const installDeps = (data) => pipe(data, set("dependencies", getDependencies), getAddDepsCommand, when(notEmpty, runAddDeps(data)));
|
|
14
|
+
const installDevDeps = (data) => pipe(data, set("dependencies", getDevDependencies), set("dev", always(true)), getAddDepsCommand, when(notEmpty, runAddDeps(data)));
|
|
15
|
+
const getAddDepsCommand = (data) => pipe(data, joinDepsVer, when(notEmpty, concat(getAddDepsArgs(data))), toArray);
|
|
16
|
+
const runAddDeps = ({ dir }) => (command) => runSubCommand(command, {
|
|
17
|
+
cwd: dir,
|
|
18
|
+
stdio: "inherit"
|
|
19
|
+
}).catch(noticeErrorWhileAddDeps(command));
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { install_default as default };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import ask_default from "../ask/mod.js";
|
|
5
|
+
import { makeDirIfHyd } from "./dir.js";
|
|
6
|
+
import { drawDinosaur, noticeHowToRun, noticeOptions, noticePrecommand } from "./notice.js";
|
|
7
|
+
import env_default from "./env.js";
|
|
8
|
+
import { hasCommand, isDry } from "./utils.js";
|
|
9
|
+
import install_default from "./install.js";
|
|
10
|
+
import { patchFiles, recommendPatchFiles } from "./patch.js";
|
|
11
|
+
import precommand_default from "./precommand.js";
|
|
12
|
+
import recommend_default from "./recommend.js";
|
|
13
|
+
import set_default from "./set.js";
|
|
14
|
+
import { pipe, tap, unless, when } from "@fxts/core";
|
|
15
|
+
|
|
16
|
+
//#region src/init/action/mod.ts
|
|
17
|
+
/**
|
|
18
|
+
* options: InitCommand
|
|
19
|
+
* ├ drawDinosaur
|
|
20
|
+
* ┌─────┴──────┐
|
|
21
|
+
* │ askOptions │ InitCommand -> InitCommandOptions
|
|
22
|
+
* └─────┬──────┘
|
|
23
|
+
* ├ noticeOptions
|
|
24
|
+
* ┌────┴────┐
|
|
25
|
+
* │ setData │ InitCommandOptions -> InitCommandData
|
|
26
|
+
* └────┬────┘
|
|
27
|
+
* ┌─┴─┐ isDry
|
|
28
|
+
* handleDryRun ┤ ├ handleHydRun
|
|
29
|
+
* └─┬─┘
|
|
30
|
+
* ├ recommendConfigEnv
|
|
31
|
+
* ├ noticeHowToRun
|
|
32
|
+
*/
|
|
33
|
+
const runInit = (options) => pipe(options, tap(drawDinosaur), ask_default, tap(noticeOptions), set_default, when(isDry, handleDryRun), unless(isDry, handleHydRun), tap(env_default), tap(noticeHowToRun));
|
|
34
|
+
var action_default = runInit;
|
|
35
|
+
const handleDryRun = (data) => pipe(data, tap(when(hasCommand, noticePrecommand)), tap(recommendPatchFiles), tap(recommend_default));
|
|
36
|
+
const handleHydRun = (data) => pipe(data, tap(makeDirIfHyd), tap(when(hasCommand, precommand_default)), tap(patchFiles), tap(install_default));
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { action_default as default };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { colors } from "../../utils.js";
|
|
5
|
+
import { message } from "@optique/core";
|
|
6
|
+
import { print, printError } from "@optique/run";
|
|
7
|
+
import { flow } from "es-toolkit";
|
|
8
|
+
|
|
9
|
+
//#region src/init/action/notice.ts
|
|
10
|
+
const printMessage = flow(message, print);
|
|
11
|
+
const printErrorMessage = flow(message, printError);
|
|
12
|
+
function drawDinosaur() {
|
|
13
|
+
const d = flow(colors.bgBlue, colors.black);
|
|
14
|
+
const f = colors.blue;
|
|
15
|
+
console.error(`\
|
|
16
|
+
${d(" ___ ")} ${f(" _____ _ _ __")}
|
|
17
|
+
${d(" /'_') ")} ${f("| ___|__ __| (_)/ _|_ _")}
|
|
18
|
+
${d(" .-^^^-/ / ")} ${f("| |_ / _ \\/ _` | | |_| | | |")}
|
|
19
|
+
${d(" __/ / ")} ${f("| _| __/ (_| | | _| |_| |")}
|
|
20
|
+
${d(" <__.|_|-|_| ")} ${f("|_| \\___|\\__,_|_|_| \\__, |")}
|
|
21
|
+
${d(" ")} ${f(" |___/")}
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
const noticeOptions = ({ packageManager, webFramework, kvStore, messageQueue }) => printMessage`
|
|
25
|
+
Package manager: ${packageManager};
|
|
26
|
+
Web framework: ${webFramework};
|
|
27
|
+
Key–value store: ${kvStore};
|
|
28
|
+
Message queue: ${messageQueue};
|
|
29
|
+
`;
|
|
30
|
+
function noticePrecommand({ initializer: { command: command$1 }, dir }) {
|
|
31
|
+
printMessage`📦 Would run command:`;
|
|
32
|
+
printMessage` cd ${dir}`;
|
|
33
|
+
printMessage` ${command$1.join(" ")}\n`;
|
|
34
|
+
}
|
|
35
|
+
const noticeFilesToCreate = () => printMessage`📄 Would create files:\n`;
|
|
36
|
+
const noticeFilesToInsert = () => printMessage`Would create/update JSON files:\n`;
|
|
37
|
+
const noticeDepsIfExist = () => printMessage`📦 Would install dependencies:`;
|
|
38
|
+
const noticeDevDepsIfExist = () => printMessage`📦 Would install dev dependencies:`;
|
|
39
|
+
const noticeDeps = ([name, version]) => printMessage`${name}@${version}`;
|
|
40
|
+
function displayFile(path$1, content, emoji = "📄") {
|
|
41
|
+
printMessage`${emoji} ${path$1}`;
|
|
42
|
+
printMessage`${"─".repeat(60)}`;
|
|
43
|
+
printMessage`${content}`;
|
|
44
|
+
printMessage`${"─".repeat(60)}\n`;
|
|
45
|
+
}
|
|
46
|
+
const noticeConfigEnv = () => printMessage`Note that you probably want to edit the ${".env"} file.
|
|
47
|
+
It currently contains the following values:\n`;
|
|
48
|
+
const noticeEnvKeyValue = ([key, value]) => printMessage` ${key}=${value}`;
|
|
49
|
+
function noticeHowToRun({ initializer: { instruction, federationFile } }) {
|
|
50
|
+
printMessage`${instruction}`;
|
|
51
|
+
printMessage`Start by editing the ${federationFile} file to define your federation!
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
function noticeErrorWhileAddDeps(command$1) {
|
|
55
|
+
return (error) => {
|
|
56
|
+
printErrorMessage`The command ${command$1.join(" ")} failed with the error: ${String(error)}`;
|
|
57
|
+
throw new Error("Failed to add dependencies.");
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { displayFile, drawDinosaur, noticeConfigEnv, noticeDeps, noticeDepsIfExist, noticeDevDepsIfExist, noticeEnvKeyValue, noticeErrorWhileAddDeps, noticeFilesToCreate, noticeFilesToInsert, noticeHowToRun, noticeOptions, noticePrecommand };
|