Skip to content

Commit

Permalink
feat: typed routes; search param refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Feb 9, 2024
1 parent bdd419d commit 6b950bd
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 34 deletions.
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"rules": {
"recommended": true,
"complexity": {
"noBannedTypes": "off",
"noUselessFragments": "off"
},
"suspicious": {
Expand Down
Binary file modified bun.lockb
Binary file not shown.
69 changes: 39 additions & 30 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import { type Context, Hono } from 'hono'
import { ImageResponse } from 'hono-og'
import { type JSXNode } from 'hono/jsx'
import { jsxRenderer } from 'hono/jsx-renderer'
import type { Env, Schema } from 'hono/types'
import { type HtmlEscapedString } from 'hono/utils/html'
import {
compressToEncodedURIComponent,
decompressFromEncodedURIComponent,
} from 'lz-string'

import { Preview, previewStyles } from './preview.js'
import {
Expand All @@ -33,22 +38,24 @@ type FrameHandlerReturnType = {
image: JSX.Element
intents?: Intents | undefined
}

export class Farc extends Hono {
frame(
path: string,
export class Farc<
E extends Env = Env,
S extends Schema = {},
BasePath extends string = '/',
> extends Hono<E, S, BasePath> {
frame<P extends string>(
path: P,
handler: (
context: FrameContext,
context: FrameContext<P>,
previousContext?: PreviousFrameContext | undefined,
) => FrameHandlerReturnType | Promise<FrameHandlerReturnType>,
) {
// Frame Route (implements GET & POST).
this.use(path, async (c) => {
const query = c.req.query()
const previousContext =
query.previousContext && query.previousContext !== 'undefined'
? deserializeJson<PreviousFrameContext>(query.previousContext)
: undefined
const previousContext = query.previousContext
? deserializeJson<PreviousFrameContext>(query.previousContext)
: undefined
const context = await getFrameContext(c, previousContext)

const { intents } = await handler(context, previousContext)
Expand All @@ -60,31 +67,30 @@ export class Farc extends Hono {
intents: parsedIntents,
})

const ogSearch = new URLSearchParams()
if (query.previousContext)
ogSearch.set('previousContext', query.previousContext)
if (serializedContext) ogSearch.set('context', serializedContext)

const postSearch = new URLSearchParams()
if (serializedPreviousContext)
postSearch.set('previousContext', serializedPreviousContext)

return c.render(
<html lang="en">
<head>
<meta property="fc:frame" content="vNext" />
<meta
property="fc:frame:image"
content={`${toBaseUrl(
context.url,
)}/image?context=${serializedContext}&previousContext=${
query.previousContext
}`}
content={`${toBaseUrl(context.url)}/image?${ogSearch.toString()}`}
/>
<meta
property="og:image"
content={`${toBaseUrl(
context.url,
)}/image?context=${serializedContext}&previousContext=${
query.previousContext
}`}
content={`${toBaseUrl(context.url)}/image?${ogSearch.toString()}`}
/>
<meta
property="fc:frame:post_url"
content={`${toBaseUrl(
context.url,
)}?previousContext=${serializedPreviousContext}`}
content={`${toBaseUrl(context.url)}?${postSearch}`}
/>
{parsedIntents}

Expand All @@ -104,11 +110,13 @@ export class Farc extends Hono {
this.get(`${toBaseUrl(path)}/image`, async (c) => {
const query = c.req.query()
const parsedContext = deserializeJson<FrameContext>(query.context)
const parsedPreviousContext =
query.previousContext && query.previousContext !== 'undefined'
? deserializeJson<PreviousFrameContext>(query.previousContext)
: undefined
const { image } = await handler(parsedContext, parsedPreviousContext)
const parsedPreviousContext = query.previousContext
? deserializeJson<PreviousFrameContext>(query.previousContext)
: undefined
const { image } = await handler(
{ ...parsedContext, request: c.req },
parsedPreviousContext,
)
return new ImageResponse(image)
})

Expand Down Expand Up @@ -401,6 +409,7 @@ async function getFrameContext(
buttonIndex,
buttonValue,
inputText,
request: req,
status,
trustedData,
untrustedData,
Expand Down Expand Up @@ -450,13 +459,13 @@ function toBaseUrl(path_: string) {
return path
}

function deserializeJson<returnType>(data = '{}'): returnType {
function deserializeJson<returnType>(data = ''): returnType {
if (data === 'undefined') return {} as returnType
return JSON.parse(decodeURIComponent(data))
return JSON.parse(decompressFromEncodedURIComponent(data))
}

function serializeJson(data: unknown = {}) {
return encodeURIComponent(JSON.stringify(data))
return compressToEncodedURIComponent(JSON.stringify(data))
}

export function htmlToMetaTags(html: string, selector: string) {
Expand Down
3 changes: 2 additions & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@farcaster/core": "^0.13.7",
"@noble/curves": "^1.3.0",
"happy-dom": "^13.3.8",
"hono-og": "~0.0.2"
"hono-og": "~0.0.2",
"lz-string": "^1.5.0"
}
}
2 changes: 1 addition & 1 deletion src/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function Frame(props: FrameProps) {
return (
<div style={{ maxWidth: '512px', width: '100%' }}>
<form
action="/preview"
action="preview"
method="post"
style={{ borderRadius: '0.5rem', position: 'relative', width: '100%' }}
>
Expand Down
5 changes: 3 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// TODO: TSDoc

import { type Context } from 'hono'
import { type Context, type Env } from 'hono'
import { type JSXNode } from 'hono/jsx'

export type FrameContext = {
export type FrameContext<P extends string = string> = {
buttonIndex?: number
buttonValue?: string
inputText?: string
request: Context<Env, P>['req']
/**
* Status of the frame in the frame lifecycle.
* - `initial` - The frame has not yet been interacted with.
Expand Down

0 comments on commit 6b950bd

Please sign in to comment.