Skip to content

Commit

Permalink
fix: even more issues
Browse files Browse the repository at this point in the history
  • Loading branch information
michalkvasnicak committed Apr 3, 2024
1 parent 1dd14b9 commit 5dc523a
Show file tree
Hide file tree
Showing 13 changed files with 94 additions and 56 deletions.
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ module.exports = {
parserOptions: {
project: true,
},
ignorePatterns: ["**/farcaster/generated/*.ts"],
};
2 changes: 2 additions & 0 deletions packages/eslint-config/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ module.exports = {
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unnecessary-condition": "warn",
"@typescript-eslint/consistent-type-imports": "error",
"jest/expect-expect": "warn",
"react/jsx-sort-props": "off",
"unicorn/filename-case": "off",
eqeqeq: "off",
"no-await-in-loop": "off",
},
};
6 changes: 4 additions & 2 deletions packages/frames.js/src/core/createFrames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ export function createFrames<
* This function takes handler function that does the logic with the help of context and returns one of possible results
*/
return function createFramesRequestHandler(handler, options = {}) {
const perRouteMiddleware: FramesMiddleware<any, FramesContext<TState>>[] =
Array.isArray(options.middleware) ? options.middleware : [];
const perRouteMiddleware: FramesMiddleware<
JsonValue | undefined,
FramesContext<TState>
>[] = Array.isArray(options.middleware) ? options.middleware : [];

const composedMiddleware = composeMiddleware<
FramesContext<TState>,
Expand Down
2 changes: 1 addition & 1 deletion packages/frames.js/src/getTokenFromUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function getTokenFromUrl(url: string): ParsedToken {
}

return {
namespace: namespace,
namespace,
chainId: parseInt(chainId),
address,
tokenId: tokenId || undefined,
Expand Down
6 changes: 3 additions & 3 deletions packages/frames.js/src/hono/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ describe("hono adapter", () => {

it("correctly integrates with Hono", async () => {
const frames = lib.createFrames();
const handler = frames(async (ctx) => {
const handler = frames((ctx) => {
expect(ctx.request.url).toBe("http://localhost:3000/");

return {
image: <span>Test</span>,
buttons: [<lib.Button action="post">Click me</lib.Button>],
buttons: [<lib.Button action="post" key="1">Click me</lib.Button>],
};
});

Expand All @@ -39,7 +39,7 @@ describe("hono adapter", () => {
},
});

const handler = frames(async (ctx) => {
const handler = frames((ctx) => {
expect(ctx.state).toEqual({ test: false });

return {
Expand Down
24 changes: 17 additions & 7 deletions packages/frames.js/src/hono/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export { Button, type types } from "../core";
import { createFrames as coreCreateFrames, types } from "../core";
import type { Handler } from "hono";
import { CoreMiddleware } from "../middleware";
import type { types } from "../core";
import { createFrames as coreCreateFrames } from "../core";
import type { CoreMiddleware } from "../middleware";

export { Button, type types } from "../core";

type CreateFramesForHono = types.CreateFramesFunctionDefinition<
CoreMiddleware,
Expand All @@ -12,6 +14,7 @@ type CreateFramesForHono = types.CreateFramesFunctionDefinition<
* Creates Frames instance to use with you Hono server
*
* @example
* ```tsx
* import { createFrames, Button } from 'frames.js/hono';
* import { Hono } from 'hono';
*
Expand All @@ -30,17 +33,24 @@ type CreateFramesForHono = types.CreateFramesFunctionDefinition<
* const app = new Hono();
*
* app.on(['GET', 'POST'], '/', honoHandler);
* ```
*/
// @ts-expect-error
// @ts-expect-error -- this code is correct just function doesn't satisfy the type
export const createFrames: CreateFramesForHono = function createFramesForHono(
options?: types.FramesOptions<any, any>
options?: types.FramesOptions<types.JsonValue | undefined, undefined>
) {
const frames = coreCreateFrames(options);

return function honoFramesHandler<
TPerRouteMiddleware extends types.FramesMiddleware<any, any>[],
TPerRouteMiddleware extends types.FramesMiddleware<
types.JsonValue | undefined,
Record<string, unknown>
>[],
>(
handler: types.FrameHandlerFunction<any, any>,
handler: types.FrameHandlerFunction<
types.JsonValue | undefined,
Record<string, unknown>
>,
handlerOptions?: types.FramesRequestHandlerFunctionOptions<TPerRouteMiddleware>
) {
const framesHandler = frames(handler, handlerOptions);
Expand Down
31 changes: 27 additions & 4 deletions packages/frames.js/src/hono/test.types.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Handler } from 'hono';
import { createFrames, types } from '.';
/* eslint-disable @typescript-eslint/require-await -- we are testing for promise compatibility */
import type { Handler } from 'hono';
import type { types } from '.';
import { createFrames } from '.';

const framesWithoutState = createFrames();
framesWithoutState(async ctx => {
Expand Down Expand Up @@ -38,8 +40,29 @@ framesWithExplicitState(async ctx => {
test: boolean;
};
ctx satisfies {
message?: any;
pressedButton?: any;
message?: unknown;
pressedButton?: unknown;
request: Request;
}

return {
image: 'http://test.png'
};
}) satisfies Handler;

const framesWithExplicitStateNoPromise = createFrames<{
test: boolean;
}>({});
framesWithExplicitStateNoPromise(ctx => {
ctx.state satisfies {
test: boolean;
};
ctx.initialState satisfies {
test: boolean;
};
ctx satisfies {
message?: unknown;
pressedButton?: unknown;
request: Request;
}

Expand Down
59 changes: 29 additions & 30 deletions packages/frames.js/src/lib/stream-pump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,25 @@ export class StreamPump {
new Stream.Readable().readableHighWaterMark;
this.accumalatedSize = 0;
this.stream = stream;
this.enqueue = this.enqueue.bind(this);
this.error = this.error.bind(this);
this.close = this.close.bind(this);
}

size(chunk: Uint8Array) {
return chunk?.byteLength || 0;
size(chunk: Uint8Array): number {
return chunk.byteLength || 0;
}

start(controller: ReadableStreamController<Uint8Array>) {
start(controller: ReadableStreamController<Uint8Array>): void {
this.controller = controller;
this.stream.on("data", this.enqueue);
this.stream.once("error", this.error);
this.stream.once("end", this.close);
this.stream.once("close", this.close);
}

pull() {
pull(): void {
this.resume();
}

cancel(reason?: Error) {
cancel(reason?: Error): void {
if (this.stream.destroy) {
this.stream.destroy(reason);
}
Expand All @@ -59,17 +56,17 @@ export class StreamPump {
this.stream.off("close", this.close);
}

enqueue(chunk: Uint8Array | string) {
enqueue = (chunk: Uint8Array | string): void => {
if (this.controller) {
try {
let bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
const bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);

let available = (this.controller.desiredSize || 0) - bytes.byteLength;
const available = (this.controller.desiredSize || 0) - bytes.byteLength;
this.controller.enqueue(bytes);
if (available <= 0) {
this.pause();
}
} catch (error: any) {
} catch (error) {
this.controller.error(
new Error(
"Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object"
Expand All @@ -78,60 +75,62 @@ export class StreamPump {
this.cancel();
}
}
}
};

pause() {
pause(): void {
if (this.stream.pause) {
this.stream.pause();
}
}

resume() {
resume(): void {
if (this.stream.readable && this.stream.resume) {
this.stream.resume();
}
}

close() {
close = (): void => {
if (this.controller) {
this.controller.close();
delete this.controller;
}
}
};

error(error: Error) {
error = (error: Error): void => {
if (this.controller) {
this.controller.error(error);
delete this.controller;
}
}
};
}

export const createReadableStreamFromReadable = (
export function createReadableStreamFromReadable(
source: Readable & { readableHighWaterMark?: number }
) => {
let pump = new StreamPump(source);
let stream = new ReadableStream(pump, pump);
): ReadableStream<Uint8Array> {
const pump = new StreamPump(source);
const stream = new ReadableStream(pump, pump);
return stream;
};
}

export async function writeReadableStreamToWritable(
stream: ReadableStream,
export async function writeReadableStreamToWritable<R = any>(
stream: ReadableStream<R>,
writable: Writable
) {
let reader = stream.getReader();
let flushable = writable as { flush?: Function };
): Promise<void> {
const reader = stream.getReader();
const flushable = writable as { flush?: () => void };

try {
// eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition -- this is expected to be exhaustive
while (true) {
let { done, value } = await reader.read();
const { done, value } = await reader.read();

if (done) {
writable.end();
break;
}

writable.write(value);

if (typeof flushable.flush === "function") {
flushable.flush();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Message } from "../farcaster";
import { redirect } from "../core/redirect";
import type { FramesContext } from "../core/types";
import { createFrames } from "../core";
import type { UserDataReturnType } from "..";
import { farcasterHubContext } from "./farcasterHubContext";

describe("farcasterHubContext middleware", () => {
Expand Down Expand Up @@ -113,7 +114,7 @@ describe("farcasterHubContext middleware", () => {
inputText: "hello",
requesterFid: 123,
state: JSON.stringify({ test: true }),
requestedUserData: expect.anything(),
requestedUserData: expect.anything() as UserDataReturnType,
},
})
);
Expand Down
4 changes: 2 additions & 2 deletions packages/frames.js/src/middleware/farcasterHubContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ async function decodeFrameActionPayloadFromRequest(
): Promise<FrameActionPayload | undefined> {
try {
// use clone just in case someone wants to read body somewhere along the way
const body = await request
const body = (await request
.clone()
.json()
.catch(() => {
throw new RequestBodyNotJSONError();
});
})) as JSON;

if (!isValidFrameActionPayload(body)) {
throw new InvalidFrameActionPayloadError();
Expand Down
10 changes: 5 additions & 5 deletions packages/frames.js/src/middleware/openframes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,17 @@ describe("openframes middleware", () => {
clientProtocol: "foo@vNext",
handler: {
isValidPayload: () => false,
getFrameMessage: async () => {
return { test1: true };
getFrameMessage: () => {
return Promise.resolve({ test1: true });
},
},
});
const mw2 = openframes({
clientProtocol: "bar@vNext",
handler: {
isValidPayload: () => true,
getFrameMessage: async () => {
return { test2: true };
getFrameMessage: () => {
return Promise.resolve({ test2: true });
},
},
});
Expand All @@ -227,7 +227,7 @@ describe("openframes middleware", () => {
middleware: [mw1, mw2],
});

const routeHandler = handler(async (ctx) => {
const routeHandler = handler((ctx) => {
return {
image: `/?test1=${ctx.message?.test1}&test2=${ctx.message?.test2}`,
};
Expand Down
1 change: 1 addition & 0 deletions packages/frames.js/src/validateFrameMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export async function validateFrameMessage(
isValid: boolean;
message: FrameActionMessage | undefined;
}> {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- just in case
if (!body) {
throw new Error(
"Tried to call validateFrameMessage with no frame action payload. You may be calling it incorrectly on the homeframe"
Expand Down

0 comments on commit 5dc523a

Please sign in to comment.