From df2dfacca926786175ce81b27fb90dec4637b8e1 Mon Sep 17 00:00:00 2001
From: "moxey.eth" <jakemoxey@gmail.com>
Date: Mon, 12 Feb 2024 10:31:05 +1100
Subject: [PATCH] feat: attach

---
 .gitignore            |  3 ++-
 biome.json            |  3 +++
 example/src/index.tsx |  2 +-
 example/src/todos.tsx |  1 +
 src/farc.tsx          | 41 ++++++++++++++++++++++++++++++++++++-----
 5 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6f843372..d36353ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ coverage
 _lib
 node_modules
 tsconfig.*.tsbuildinfo
-tsconfig.tsbuildinfo
\ No newline at end of file
+tsconfig.tsbuildinfo
+.vercel
diff --git a/biome.json b/biome.json
index 69333ed7..35d18f75 100644
--- a/biome.json
+++ b/biome.json
@@ -31,6 +31,9 @@
       "suspicious": {
         "noExplicitAny": "off",
         "noRedeclare": "off"
+      },
+      "style": {
+        "noNonNullAssertion": "off"
       }
     }
   },
diff --git a/example/src/index.tsx b/example/src/index.tsx
index 8111bb35..9bd08875 100644
--- a/example/src/index.tsx
+++ b/example/src/index.tsx
@@ -141,7 +141,7 @@ app.frame('/falsy-intents', () => {
   }
 })
 
-app.route('/todos', todoApp)
+app.attach(todoApp)
 
 const server = Bun.serve(app)
 console.log(`𝑭𝒂𝒓𝒄 ▶︎ http://localhost:${server.port}/dev`)
diff --git a/example/src/todos.tsx b/example/src/todos.tsx
index 403e454b..a0a3aa5d 100644
--- a/example/src/todos.tsx
+++ b/example/src/todos.tsx
@@ -14,6 +14,7 @@ export const app = new Farc<State>({
     index: -1,
     todos: [],
   },
+  route: '/todos',
 })
 
 app.frame('/', ({ buttonValue, deriveState, inputText }) => {
diff --git a/src/farc.tsx b/src/farc.tsx
index 91552691..6cc4f694 100644
--- a/src/farc.tsx
+++ b/src/farc.tsx
@@ -11,7 +11,12 @@ import { ImageResponse } from 'hono-og'
 import { inspectRoutes } from 'hono/dev'
 import type { HonoOptions } from 'hono/hono-base'
 import { jsxRenderer } from 'hono/jsx-renderer'
-import { type Env, type Schema } from 'hono/types'
+import {
+  type Env,
+  type MergePath,
+  type MergeSchemaPath,
+  type Schema,
+} from 'hono/types'
 
 import { App, DevStyles, Preview } from './dev/components.js'
 import { getFrameRoutes, htmlToFrame, htmlToState } from './dev/utils.js'
@@ -32,7 +37,10 @@ import { toBaseUrl } from './utils/toBaseUrl.js'
 export type FarcConstructorParameters<
   state = undefined,
   env extends Env = Env,
-> = HonoOptions<env> & { initialState?: state | undefined }
+> = HonoOptions<env> & {
+  initialState?: state | undefined
+  route?: string | undefined
+}
 
 export type FrameHandlerReturnType = {
   action?: string | undefined
@@ -48,10 +56,31 @@ export class Farc<
   basePath extends string = '/',
 > extends Hono<env, schema, basePath> {
   #initialState: state = undefined as state
+  subPath: string | undefined
 
-  constructor({ initialState }: FarcConstructorParameters<state, env> = {}) {
-    super()
+  constructor({
+    initialState,
+    route,
+    ...options
+  }: FarcConstructorParameters<state, env> = {}) {
+    super(options)
     if (initialState) this.#initialState = initialState
+    if (route) this.subPath = route
+  }
+
+  attach<
+    subEnv extends Env,
+    subSchema extends Schema,
+    subBasePath extends string,
+  >(
+    app: Farc<any, subEnv, subSchema, subBasePath>,
+  ): Farc<
+    any,
+    env,
+    MergeSchemaPath<subSchema, MergePath<basePath, subBasePath>> & schema,
+    basePath
+  > {
+    return this.route(app.subPath!, app as any) as any
   }
 
   frame<path extends string>(
@@ -123,7 +152,9 @@ export class Farc<
               property="fc:frame:post_url"
               content={`${
                 action
-                  ? toBaseUrl(c.req.url) + parsePath(action || '')
+                  ? toBaseUrl(c.req.url) +
+                    parsePath(this.subPath || '') +
+                    parsePath(action || '')
                   : context.url
               }?${postSearch}`}
             />