@frames.js/debugger
Version:
Debug your Frames.js applications
139 lines (119 loc) • 3.55 kB
text/typescript
"use server";
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { sortedSearchParamsString } from "../lib/utils";
import { type FrameActionHubContext } from "frames.js";
export type MockHubActionContext = FrameActionHubContext & {
enabled: boolean;
};
const MOCK_FILE_NAME = "framesjsmocks.json";
type MockFileData = {
enabled: boolean;
requests: { [pathname: string]: { ok: boolean } };
};
function resolveMocksFilePath() {
return path.join(os.tmpdir(), MOCK_FILE_NAME);
}
/**
* Checks if a mock response is available for the given request and hub path.
*/
export async function loadMockResponseForDebugHubRequest(
req: Request,
hubPath: string[]
): Promise<Response | undefined> {
// Only available in local development
const file = resolveMocksFilePath();
const fileExists = await fs
.access(file, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
if (!fileExists) {
return;
}
const json = await fs.readFile(file, "utf-8");
let mocks: MockFileData;
try {
mocks = JSON.parse(json);
} catch (err) {
console.error(err);
return;
}
if (!mocks.enabled) {
return;
}
const searchParams = new URL(req.url).searchParams;
const pathAndQuery = `/${hubPath.join("/")}?${sortedSearchParamsString(searchParams)}`;
const mockResult = mocks.requests[pathAndQuery];
if (mockResult?.ok != null) {
console.log(
`info: Mock hub: Found mock for ${pathAndQuery}, returning ${mockResult.ok ? "200" : "404"}`
);
return new Response(JSON.stringify(mockResult), {
headers: {
"content-type": "application/json",
},
status: mockResult.ok ? 200 : 404,
});
}
}
export async function persistMockResponsesForDebugHubRequests(req: Request) {
const { mockData, untrustedData } = (await req.clone().json()) as {
mockData?: MockHubActionContext;
untrustedData?: { fid: string; castId?: { fid: string; hash: string } };
};
if (!mockData || !untrustedData?.castId) {
return;
}
const { fid: requesterFid, castId } = untrustedData;
const requesterFollowsCaster = `/v1/linkById?${sortedSearchParamsString(
new URLSearchParams({
fid: requesterFid,
target_fid: castId.fid,
link_type: "follow",
})
)}`;
const casterFollowsRequester = `/v1/linkById?${sortedSearchParamsString(
new URLSearchParams({
fid: castId.fid,
target_fid: requesterFid,
link_type: "follow",
})
)}`;
const likedCast = `/v1/reactionById?${sortedSearchParamsString(
new URLSearchParams({
fid: requesterFid,
reaction_type: "1",
target_fid: castId.fid,
target_hash: castId.hash,
})
)}`;
const recastedCast = `/v1/reactionById?${sortedSearchParamsString(
new URLSearchParams({
fid: requesterFid,
reaction_type: "2",
target_fid: castId.fid,
target_hash: castId.hash,
})
)}`;
// Write to file
const file = resolveMocksFilePath();
const json: MockFileData = {
enabled: mockData.enabled,
requests: {
[requesterFollowsCaster]: {
ok: mockData.enabled && mockData.requesterFollowsCaster,
},
[casterFollowsRequester]: {
ok: mockData.enabled && mockData.casterFollowsRequester,
},
[likedCast]: {
ok: mockData.enabled && mockData.likedCast,
},
[recastedCast]: {
ok: mockData.enabled && mockData.recastedCast,
},
},
};
await fs.writeFile(file, JSON.stringify(json, null, 2));
}