@poolzin/pool-bot 2026.2.23 → 2026.2.25
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/CHANGELOG.md +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
type SpawnTarget = {
|
|
5
|
+
command: string;
|
|
6
|
+
argv: string[];
|
|
7
|
+
windowsHide?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function isFilePath(value: string): boolean {
|
|
11
|
+
try {
|
|
12
|
+
const stat = fs.statSync(value);
|
|
13
|
+
return stat.isFile();
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveWindowsExecutablePath(execPath: string, env: NodeJS.ProcessEnv): string {
|
|
20
|
+
if (execPath.includes("/") || execPath.includes("\\") || path.isAbsolute(execPath)) {
|
|
21
|
+
return execPath;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const pathValue = env.PATH ?? env.Path ?? process.env.PATH ?? process.env.Path ?? "";
|
|
25
|
+
const pathEntries = pathValue
|
|
26
|
+
.split(";")
|
|
27
|
+
.map((entry) => entry.trim())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
|
|
30
|
+
const hasExtension = path.extname(execPath).length > 0;
|
|
31
|
+
const pathExtRaw =
|
|
32
|
+
env.PATHEXT ??
|
|
33
|
+
env.Pathext ??
|
|
34
|
+
process.env.PATHEXT ??
|
|
35
|
+
process.env.Pathext ??
|
|
36
|
+
".EXE;.CMD;.BAT;.COM";
|
|
37
|
+
const pathExt = hasExtension
|
|
38
|
+
? [""]
|
|
39
|
+
: pathExtRaw
|
|
40
|
+
.split(";")
|
|
41
|
+
.map((ext) => ext.trim())
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.map((ext) => (ext.startsWith(".") ? ext : `.${ext}`));
|
|
44
|
+
|
|
45
|
+
for (const dir of pathEntries) {
|
|
46
|
+
for (const ext of pathExt) {
|
|
47
|
+
for (const candidateExt of [ext, ext.toLowerCase(), ext.toUpperCase()]) {
|
|
48
|
+
const candidate = path.join(dir, `${execPath}${candidateExt}`);
|
|
49
|
+
if (isFilePath(candidate)) {
|
|
50
|
+
return candidate;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return execPath;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveBinEntry(binField: string | Record<string, string> | undefined): string | null {
|
|
60
|
+
if (typeof binField === "string") {
|
|
61
|
+
const trimmed = binField.trim();
|
|
62
|
+
return trimmed || null;
|
|
63
|
+
}
|
|
64
|
+
if (!binField || typeof binField !== "object") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const preferred = binField.lobster;
|
|
69
|
+
if (typeof preferred === "string" && preferred.trim()) {
|
|
70
|
+
return preferred.trim();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const value of Object.values(binField)) {
|
|
74
|
+
if (typeof value === "string" && value.trim()) {
|
|
75
|
+
return value.trim();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveLobsterScriptFromPackageJson(wrapperPath: string): string | null {
|
|
82
|
+
const wrapperDir = path.dirname(wrapperPath);
|
|
83
|
+
const packageDirs = [
|
|
84
|
+
// Local install: <repo>/node_modules/.bin/lobster.cmd -> ../lobster
|
|
85
|
+
path.resolve(wrapperDir, "..", "lobster"),
|
|
86
|
+
// Global npm install: <npm-prefix>/lobster.cmd -> ./node_modules/lobster
|
|
87
|
+
path.resolve(wrapperDir, "node_modules", "lobster"),
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
for (const packageDir of packageDirs) {
|
|
91
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
92
|
+
if (!isFilePath(packageJsonPath)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as {
|
|
98
|
+
bin?: string | Record<string, string>;
|
|
99
|
+
};
|
|
100
|
+
const scriptRel = resolveBinEntry(packageJson.bin);
|
|
101
|
+
if (!scriptRel) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const scriptPath = path.resolve(packageDir, scriptRel);
|
|
105
|
+
if (isFilePath(scriptPath)) {
|
|
106
|
+
return scriptPath;
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
// Ignore malformed package metadata; caller will throw a guided error.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resolveLobsterScriptFromCmdShim(wrapperPath: string): string | null {
|
|
117
|
+
if (!isFilePath(wrapperPath)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const content = fs.readFileSync(wrapperPath, "utf8");
|
|
123
|
+
const candidates: string[] = [];
|
|
124
|
+
const extractRelativeFromToken = (token: string): string | null => {
|
|
125
|
+
const match = token.match(/%~?dp0%\s*[\\/]*(.*)$/i);
|
|
126
|
+
if (!match) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const relative = match[1];
|
|
130
|
+
if (!relative) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return relative;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const matches = content.matchAll(/"([^"\r\n]*)"/g);
|
|
137
|
+
for (const match of matches) {
|
|
138
|
+
const token = match[1] ?? "";
|
|
139
|
+
const relative = extractRelativeFromToken(token);
|
|
140
|
+
if (!relative) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const normalizedRelative = relative
|
|
145
|
+
.trim()
|
|
146
|
+
.replace(/[\\/]+/g, path.sep)
|
|
147
|
+
.replace(/^[\\/]+/, "");
|
|
148
|
+
const candidate = path.resolve(path.dirname(wrapperPath), normalizedRelative);
|
|
149
|
+
if (isFilePath(candidate)) {
|
|
150
|
+
candidates.push(candidate);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const nonNode = candidates.find((candidate) => {
|
|
155
|
+
const base = path.basename(candidate).toLowerCase();
|
|
156
|
+
return base !== "node.exe" && base !== "node";
|
|
157
|
+
});
|
|
158
|
+
if (nonNode) {
|
|
159
|
+
return nonNode;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// Ignore unreadable shims; caller will throw a guided error.
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function resolveWindowsLobsterSpawn(
|
|
169
|
+
execPath: string,
|
|
170
|
+
argv: string[],
|
|
171
|
+
env: NodeJS.ProcessEnv,
|
|
172
|
+
): SpawnTarget {
|
|
173
|
+
const resolvedExecPath = resolveWindowsExecutablePath(execPath, env);
|
|
174
|
+
const ext = path.extname(resolvedExecPath).toLowerCase();
|
|
175
|
+
if (ext !== ".cmd" && ext !== ".bat") {
|
|
176
|
+
return { command: resolvedExecPath, argv };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const scriptPath =
|
|
180
|
+
resolveLobsterScriptFromCmdShim(resolvedExecPath) ??
|
|
181
|
+
resolveLobsterScriptFromPackageJson(resolvedExecPath);
|
|
182
|
+
if (!scriptPath) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`${path.basename(resolvedExecPath)} wrapper resolved, but no Node entrypoint could be resolved without shell execution. Ensure Lobster is installed and runnable on PATH (prefer lobster.exe).`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const entryExt = path.extname(scriptPath).toLowerCase();
|
|
189
|
+
if (entryExt === ".exe") {
|
|
190
|
+
return { command: scriptPath, argv, windowsHide: true };
|
|
191
|
+
}
|
|
192
|
+
return { command: process.execPath, argv: [scriptPath, ...argv], windowsHide: true };
|
|
193
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { PoolBotConfig } from "poolbot/plugin-sdk";
|
|
2
|
+
import { expect, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
export function createMattermostTestConfig(): PoolBotConfig {
|
|
5
|
+
return {
|
|
6
|
+
channels: {
|
|
7
|
+
mattermost: {
|
|
8
|
+
enabled: true,
|
|
9
|
+
botToken: "test-token",
|
|
10
|
+
baseUrl: "https://chat.example.com",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createMattermostReactionFetchMock(params: {
|
|
17
|
+
postId: string;
|
|
18
|
+
emojiName: string;
|
|
19
|
+
mode: "add" | "remove" | "both";
|
|
20
|
+
userId?: string;
|
|
21
|
+
status?: number;
|
|
22
|
+
body?: unknown;
|
|
23
|
+
}) {
|
|
24
|
+
const userId = params.userId ?? "BOT123";
|
|
25
|
+
const mode = params.mode;
|
|
26
|
+
const allowAdd = mode === "add" || mode === "both";
|
|
27
|
+
const allowRemove = mode === "remove" || mode === "both";
|
|
28
|
+
const addStatus = params.status ?? 201;
|
|
29
|
+
const removeStatus = params.status ?? 204;
|
|
30
|
+
const removePath = `/api/v4/users/${userId}/posts/${params.postId}/reactions/${encodeURIComponent(params.emojiName)}`;
|
|
31
|
+
|
|
32
|
+
return vi.fn(async (url: any, init?: any) => {
|
|
33
|
+
if (String(url).endsWith("/api/v4/users/me")) {
|
|
34
|
+
return new Response(JSON.stringify({ id: userId }), {
|
|
35
|
+
status: 200,
|
|
36
|
+
headers: { "content-type": "application/json" },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (allowAdd && String(url).endsWith("/api/v4/reactions")) {
|
|
41
|
+
expect(init?.method).toBe("POST");
|
|
42
|
+
expect(JSON.parse(init?.body)).toEqual({
|
|
43
|
+
user_id: userId,
|
|
44
|
+
post_id: params.postId,
|
|
45
|
+
emoji_name: params.emojiName,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const responseBody = params.body === undefined ? { ok: true } : params.body;
|
|
49
|
+
return new Response(
|
|
50
|
+
responseBody === null ? null : JSON.stringify(responseBody),
|
|
51
|
+
responseBody === null
|
|
52
|
+
? { status: addStatus, headers: { "content-type": "text/plain" } }
|
|
53
|
+
: { status: addStatus, headers: { "content-type": "application/json" } },
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (allowRemove && String(url).endsWith(removePath)) {
|
|
58
|
+
expect(init?.method).toBe("DELETE");
|
|
59
|
+
const responseBody = params.body === undefined ? null : params.body;
|
|
60
|
+
return new Response(
|
|
61
|
+
responseBody === null ? null : JSON.stringify(responseBody),
|
|
62
|
+
responseBody === null
|
|
63
|
+
? { status: removeStatus, headers: { "content-type": "text/plain" } }
|
|
64
|
+
: { status: removeStatus, headers: { "content-type": "application/json" } },
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw new Error(`unexpected url: ${url}`);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function withMockedGlobalFetch<T>(
|
|
73
|
+
fetchImpl: typeof fetch,
|
|
74
|
+
run: () => Promise<T>,
|
|
75
|
+
): Promise<T> {
|
|
76
|
+
const prevFetch = globalThis.fetch;
|
|
77
|
+
(globalThis as any).fetch = fetchImpl;
|
|
78
|
+
try {
|
|
79
|
+
return await run();
|
|
80
|
+
} finally {
|
|
81
|
+
(globalThis as any).fetch = prevFetch;
|
|
82
|
+
}
|
|
83
|
+
}
|