<html lang="en">
<head></head>
<body>

<form id="mainForm" method="post" action="https://stackblitz.com/run" target="_self">
<input type="hidden" name="project[files][.gitignore]" value="# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.output
stats.html
stats-*.json
.wxt
web-ext.config.ts

# Editor directories and files
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
">
<input type="hidden" name="project[files][README.md]" value="# ORPC Playground

This is a playground for [oRPC](https://orpc.unnoq.com) and [WXT](https://wxt.dev/)

## Getting Started

First, run the development server:

```bash
npm run dev
```

## Sponsors

&lt;p align=&quot;center&quot;&gt;
  &lt;a href=&quot;https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg&quot;&gt;
    &lt;img src=&#39;https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg&#39;/&gt;
  &lt;/a&gt;
&lt;/p&gt;
">
<input type="hidden" name="project[files][package.json]" value="{&quot;name&quot;:&quot;@orpc/browser-extension-playground&quot;,&quot;type&quot;:&quot;module&quot;,&quot;version&quot;:&quot;0.0.0&quot;,&quot;private&quot;:true,&quot;description&quot;:&quot;manifest.json description&quot;,&quot;scripts&quot;:{&quot;dev&quot;:&quot;wxt&quot;,&quot;dev:firefox&quot;:&quot;wxt -b firefox&quot;,&quot;build&quot;:&quot;wxt build&quot;,&quot;build:firefox&quot;:&quot;wxt build -b firefox&quot;,&quot;zip&quot;:&quot;wxt zip&quot;,&quot;zip:firefox&quot;:&quot;wxt zip -b firefox&quot;,&quot;typecheck&quot;:&quot;tsc --noEmit&quot;,&quot;postinstall&quot;:&quot;wxt prepare&quot;},&quot;devDependencies&quot;:{&quot;@orpc/client&quot;:&quot;https://pkg.pr.new/unnoq/orpc/@orpc/client@af63fe1&quot;,&quot;@orpc/openapi&quot;:&quot;https://pkg.pr.new/unnoq/orpc/@orpc/openapi@af63fe1&quot;,&quot;@orpc/react&quot;:&quot;https://pkg.pr.new/unnoq/orpc/@orpc/react@af63fe1&quot;,&quot;@orpc/react-query&quot;:&quot;https://pkg.pr.new/unnoq/orpc/@orpc/react-query@af63fe1&quot;,&quot;@orpc/server&quot;:&quot;https://pkg.pr.new/unnoq/orpc/@orpc/server@af63fe1&quot;,&quot;@orpc/zod&quot;:&quot;https://pkg.pr.new/unnoq/orpc/@orpc/zod@af63fe1&quot;,&quot;@tanstack/react-query&quot;:&quot;^5.75.7&quot;,&quot;@types/react&quot;:&quot;^19.1.2&quot;,&quot;@types/react-dom&quot;:&quot;^19.1.3&quot;,&quot;@wxt-dev/module-react&quot;:&quot;^1.1.3&quot;,&quot;react&quot;:&quot;^19.1.0&quot;,&quot;react-dom&quot;:&quot;^19.1.0&quot;,&quot;typescript&quot;:&quot;^5.8.3&quot;,&quot;wxt&quot;:&quot;^0.20.6&quot;,&quot;zod&quot;:&quot;^3.25.11&quot;}}">
<input type="hidden" name="project[files][tsconfig.json]" value="{
  &quot;extends&quot;: &quot;./.wxt/tsconfig.json&quot;,
  &quot;compilerOptions&quot;: {
    &quot;jsx&quot;: &quot;react-jsx&quot;,
    &quot;allowImportingTsExtensions&quot;: true
  }
}
">
<input type="hidden" name="project[files][wxt.config.ts]" value="import { defineConfig } from &#39;wxt&#39;

// See https://wxt.dev/api/config.html
export default defineConfig({
  modules: [&#39;@wxt-dev/module-react&#39;],
})
">
<input type="hidden" name="project[files][.vscode/settings.json]" value="{
  &quot;editor.formatOnSave&quot;: true
}
">
<input type="hidden" name="project[files][entrypoints/content.ts]" value="export default defineContentScript({
  matches: [&#39;*://*.google.com/*&#39;],
  main() {
    console.log(&#39;Hello content.&#39;)
  },
})
">
<input type="hidden" name="project[files][public/wxt.svg]" value="&lt;svg width=&quot;72&quot; height=&quot;72&quot; viewBox=&quot;0 0 72 72&quot; fill=&quot;none&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
&lt;g clip-path=&quot;url(#clip0_305_516)&quot;&gt;
&lt;g clip-path=&quot;url(#clip1_305_516)&quot;&gt;
&lt;path d=&quot;M49.0229 69.1875C54.1272 69.1875 58.265 65.0497 58.265 59.9454V50.7033H59.9454C65.0497 50.7033 69.1875 46.5655 69.1875 41.4612C69.1875 36.357 65.0497 32.2191 59.9454 32.2191H58.265V22.9771C58.265 17.8728 54.1272 13.735 49.0229 13.735H39.7809V12.0546C39.7809 6.95032 35.643 2.8125 30.5388 2.8125C25.4345 2.8125 21.2967 6.95032 21.2967 12.0546V13.735H12.0546C6.95032 13.735 2.8125 17.8728 2.8125 22.9771V32.2191H4.49288C9.59714 32.2191 13.735 36.357 13.735 41.4612C13.735 46.5655 9.59714 50.7033 4.49288 50.7033H2.8125V69.1875H21.2967V67.5071C21.2967 62.4029 25.4345 58.265 30.5388 58.265C35.643 58.265 39.7809 62.4029 39.7809 67.5071V69.1875H49.0229Z&quot; stroke=&quot;#67D55E&quot; stroke-width=&quot;5.625&quot;/&gt;
&lt;/g&gt;
&lt;/g&gt;
&lt;defs&gt;
&lt;clipPath id=&quot;clip0_305_516&quot;&gt;
&lt;rect width=&quot;72&quot; height=&quot;72&quot; fill=&quot;white&quot;/&gt;
&lt;/clipPath&gt;
&lt;clipPath id=&quot;clip1_305_516&quot;&gt;
&lt;rect width=&quot;72&quot; height=&quot;72&quot; fill=&quot;white&quot;/&gt;
&lt;/clipPath&gt;
&lt;/defs&gt;
&lt;/svg&gt;
">
<input type="hidden" name="project[files][entrypoints/background/index.ts]" value="import { experimental_RPCHandler as RPCHandler } from &#39;@orpc/server/message-port&#39;
import { router } from &#39;./router&#39;

const handler = new RPCHandler(router)

export default defineBackground(() =&gt; {
  browser.runtime.onConnect.addListener((port) =&gt; {
    handler.upgrade(port)
  })
})
">
<input type="hidden" name="project[files][entrypoints/background/orpc.ts]" value="import { os } from &#39;@orpc/server&#39;
import { dbProviderMiddleware } from &#39;./middlewares/db&#39;
import { requiredAuthMiddleware } from &#39;./middlewares/auth&#39;

export const pub = os
  .use(dbProviderMiddleware)

export const authed = pub
  .use(requiredAuthMiddleware)
">
<input type="hidden" name="project[files][entrypoints/popup/App.tsx]" value="import { CreatePlanetMutationForm } from &#39;./components/orpc-mutation&#39;
import { ListPlanetsQuery } from &#39;./components/orpc-query&#39;

export default function App() {
  return (
    &lt;div&gt;
      &lt;h1&gt;ORPC Playground&lt;/h1&gt;
      &lt;hr /&gt;
      &lt;CreatePlanetMutationForm /&gt;
      &lt;hr /&gt;
      &lt;ListPlanetsQuery /&gt;
    &lt;/div&gt;
  )
}
">
<input type="hidden" name="project[files][entrypoints/popup/index.html]" value="&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Default Popup Title&lt;/title&gt;
    &lt;meta name=&quot;manifest.type&quot; content=&quot;browser_action&quot; /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
    &lt;script type=&quot;module&quot; src=&quot;./main.tsx&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
">
<input type="hidden" name="project[files][entrypoints/popup/main.tsx]" value="import React from &#39;react&#39;
import ReactDOM from &#39;react-dom/client&#39;
import App from &#39;./App.tsx&#39;
import { QueryClient, QueryClientProvider } from &#39;@tanstack/react-query&#39;

const queryClient = new QueryClient()

ReactDOM.createRoot(document.getElementById(&#39;root&#39;)!).render(
  &lt;React.StrictMode&gt;
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;App /&gt;
    &lt;/QueryClientProvider&gt;
  &lt;/React.StrictMode&gt;,
)
">
<input type="hidden" name="project[files][entrypoints/popup/playground-client.ts]" value="import { client as orpc } from &#39;./lib/orpc&#39;
import { safe } from &#39;@orpc/client&#39;

const token = await orpc.auth.signin({
  email: &#39;john@doe.com&#39;,
  password: &#39;123456&#39;,
})

const [error, planet, isDefined] = await safe(orpc.planet.update({ id: 1, name: &#39;Earth&#39;, description: &#39;The planet Earth&#39; }))

if (error) {
  if (isDefined) {
    const id = error.data.id
    //    ^    type-safe
  }

  console.log(&#39;ERROR&#39;, error)
}
else {
  console.log(&#39;PLANET&#39;, planet)
}
">
<input type="hidden" name="project[files][entrypoints/popup/playground-query.ts]" value="import { orpc } from &#39;./lib/orpc&#39;
import { isDefinedError } from &#39;@orpc/client&#39;
import { useInfiniteQuery, useMutation, useQueryClient } from &#39;@tanstack/react-query&#39;

const query = useInfiniteQuery(
  orpc.planet.list.infiniteOptions({
    input: cursor =&gt; ({ cursor }),
    getNextPageParam: lastPage =&gt; (lastPage.at(-1)?.id ?? -1) + 1,
    initialPageParam: 0,
  }),
)

const queryClient = useQueryClient()

const mutation = useMutation(
  orpc.planet.update.mutationOptions({
    onError(error) {
      if (isDefinedError(error)) {
        const id = error.data.id
        //    ^    type-safe
      }
    },
    onSuccess() {
      queryClient.invalidateQueries({
        queryKey: orpc.planet.key(),
      })
    },
  }),
)
">
<input type="hidden" name="project[files][public/icon/128.png]" value="https://pkg.pr.new/template/8f8d45a3-a1ba-49f5-ab42-445a64fa1280">
<input type="hidden" name="project[files][public/icon/16.png]" value="https://pkg.pr.new/template/22dac44e-8d05-4687-8317-1359321f0085">
<input type="hidden" name="project[files][public/icon/32.png]" value="https://pkg.pr.new/template/04f18bb9-d40b-4bc3-96f1-6572b75a2653">
<input type="hidden" name="project[files][public/icon/48.png]" value="https://pkg.pr.new/template/1a902a75-3fd1-45e7-9802-4cb9606a07cc">
<input type="hidden" name="project[files][public/icon/96.png]" value="https://pkg.pr.new/template/d3911abd-37c4-4f52-b67b-c703ba9afb34">
<input type="hidden" name="project[files][entrypoints/background/middlewares/auth.ts]" value="import type { User } from &#39;../schemas/user&#39;
import { os } from &#39;@orpc/server&#39;

export const requiredAuthMiddleware = os
  .$context&lt;{ session?: { user?: User } }&gt;()
  .middleware(async ({ context, next }) =&gt; {
    /**
     * Why we should ?? here?
     * Because it can avoid `getSession` being called when unnecessary.
     * {@link https://orpc.unnoq.com/docs/best-practices/dedupe-middleware}
     */
    const session = context.session ?? await getSession()

    if (!session.user) {
      throw new Error(&#39;UNAUTHORIZED&#39;)
    }

    return next({
      context: { user: session.user },
    })
  })

async function getSession() {
  return { user: { id: &#39;unique&#39;, name: &#39;unnoq&#39;, email: &#39;contact@unnoq.com&#39; } }
}
">
<input type="hidden" name="project[files][entrypoints/background/middlewares/db.ts]" value="import type { NewPlanet, Planet, UpdatePlanet } from &#39;../schemas/planet&#39;
import type { User } from &#39;../schemas/user&#39;
import { os } from &#39;@orpc/server&#39;

export interface DB {
  planets: {
    find: (id: number) =&gt; Promise&lt;Planet | undefined&gt;
    list: (limit: number, cursor: number) =&gt; Promise&lt;Planet[]&gt;
    create: (newPlanet: NewPlanet, creator: User) =&gt; Promise&lt;Planet&gt;
    update: (updatePlanet: UpdatePlanet) =&gt; Promise&lt;Planet&gt;
  }
}

export const dbProviderMiddleware = os
  .$context&lt;{ db?: DB }&gt;()
  .middleware(async ({ context, next }) =&gt; {
    /**
     * Why we should ?? here?
     * Because it can avoid `createFakeDB` being called when unnecessary.
     * {@link https://orpc.unnoq.com/docs/best-practices/dedupe-middleware}
     */
    const db: DB = context.db ?? createFakeDB()

    return next({
      context: {
        db,
      },
    })
  })

const planets: Planet[] = [
  {
    id: 1,
    name: &#39;Earth&#39;,
    description: &#39;The planet Earth&#39;,
    imageUrl: &#39;https://picsum.photos/200/300&#39;,
    creator: {
      id: &#39;1&#39;,
      name: &#39;John Doe&#39;,
      email: &#39;john@doe.com&#39;,
    },
  },
  {
    id: 2,
    name: &#39;Mars&#39;,
    description: &#39;The planet Mars&#39;,
    imageUrl: &#39;https://picsum.photos/200/300&#39;,
    creator: {
      id: &#39;1&#39;,
      name: &#39;John Doe&#39;,
      email: &#39;john@doe.com&#39;,
    },
  },
  {
    id: 3,
    name: &#39;Jupiter&#39;,
    description: &#39;The planet Jupiter&#39;,
    imageUrl: &#39;https://picsum.photos/200/300&#39;,
    creator: {
      id: &#39;1&#39;,
      name: &#39;John Doe&#39;,
      email: &#39;john@doe.com&#39;,
    },
  },
]

export function createFakeDB(): DB {
  return {
    planets: {
      find: async (id) =&gt; {
        return planets.find(planet =&gt; planet.id === id)
      },
      list: async (limit: number, cursor: number) =&gt; {
        return planets.slice(cursor, cursor + limit)
      },
      create: async (newPlanet, creator) =&gt; {
        const id = planets.length + 1
        const imageUrl = newPlanet.image ? `https://example.com/cdn/${newPlanet.image.name}` : undefined

        const planet: Planet = {
          creator,
          id,
          name: newPlanet.name,
          description: newPlanet.description,
          imageUrl,
        }

        planets.push(planet)

        return planet
      },
      update: async (planet) =&gt; {
        const index = planets.findIndex(p =&gt; p.id === planet.id)

        if (index === -1) {
          throw new Error(&#39;Planet not found&#39;)
        }

        planets[index] = {
          ...planets[index],
          ...planet,
          imageUrl: planet.image ? `https://example.com/cdn/${planet.image.name}` : planets[index].imageUrl,
        }

        return planets[index]
      },
    },
  }
}
">
<input type="hidden" name="project[files][entrypoints/background/middlewares/retry.ts]" value="import { os } from &#39;@orpc/server&#39;

export function retry(options: { times: number }) {
  /**
   * Best practices for dedupe-middlewares
   * {@link https://orpc.unnoq.com/docs/best-practices/dedupe-middleware}
   */
  return os
    .$context&lt;{ canRetry?: boolean }&gt;()
    .middleware(({ context, next }) =&gt; {
      const canRetry = context.canRetry ?? true

      if (!canRetry) {
        return next()
      }

      let times = 0
      while (true) {
        try {
          return next({
            context: {
              canRetry: false,
            },
          })
        }
        catch (e) {
          if (times &gt;= options.times) {
            throw e
          }

          times++
        }
      }
    })
}
">
<input type="hidden" name="project[files][entrypoints/background/router/auth.ts]" value="import { authed, pub } from &#39;../orpc&#39;
import { CredentialSchema, TokenSchema } from &#39;../schemas/auth&#39;
import { NewUserSchema, UserSchema } from &#39;../schemas/user&#39;

export const signup = pub
  .route({
    method: &#39;POST&#39;,
    path: &#39;/auth/signup&#39;,
    summary: &#39;Sign up a new user&#39;,
    tags: [&#39;Authentication&#39;],
  })
  .input(NewUserSchema)
  .output(UserSchema)
  .handler(async ({ input, context }) =&gt; {
    return {
      id: &#39;28aa6286-48e9-4f23-adea-3486c86acd55&#39;,
      email: input.email,
      name: input.name,
    }
  })

export const signin = pub
  .route({
    method: &#39;POST&#39;,
    path: &#39;/auth/signin&#39;,
    summary: &#39;Sign in a user&#39;,
    tags: [&#39;Authentication&#39;],
  })
  .input(CredentialSchema)
  .output(TokenSchema)
  .handler(async ({ input, context }) =&gt; {
    return { token: &#39;token&#39; }
  })

export const me = authed
  .route({
    method: &#39;GET&#39;,
    path: &#39;/auth/me&#39;,
    summary: &#39;Get the current user&#39;,
    tags: [&#39;Authentication&#39;],
  })
  .output(UserSchema)
  .handler(async ({ input, context }) =&gt; {
    return context.user
  })
">
<input type="hidden" name="project[files][entrypoints/background/router/index.ts]" value="import { me, signin, signup } from &#39;./auth&#39;
import { createPlanet, findPlanet, listPlanets, updatePlanet } from &#39;./planet&#39;
import { sse } from &#39;./sse&#39;

export const router = {
  auth: {
    signup,
    signin,
    me,
  },

  planet: {
    list: listPlanets,
    create: createPlanet,
    find: findPlanet,
    update: updatePlanet,
  },

  sse,
}
">
<input type="hidden" name="project[files][entrypoints/background/router/planet.ts]" value="import { ORPCError } from &#39;@orpc/server&#39;
import { z } from &#39;zod&#39;
import { authed, pub } from &#39;../orpc&#39;
import { NewPlanetSchema, PlanetSchema, UpdatePlanetSchema } from &#39;../schemas/planet&#39;
import { retry } from &#39;../middlewares/retry&#39;

export const listPlanets = pub
  .use(retry({ times: 3 }))
  .route({
    method: &#39;GET&#39;,
    path: &#39;/planets&#39;,
    summary: &#39;List all planets&#39;,
    tags: [&#39;Planets&#39;],
  })
  .input(
    z.object({
      limit: z.number().int().min(1).max(100).default(10),
      cursor: z.number().int().min(0).default(0),
    }),
  )
  .output(z.array(PlanetSchema))
  .handler(async ({ input, context }) =&gt; {
    return context.db.planets.list(input.limit, input.cursor)
  })

export const createPlanet = authed
  .route({
    method: &#39;POST&#39;,
    path: &#39;/planets&#39;,
    summary: &#39;Create a planet&#39;,
    tags: [&#39;Planets&#39;],
  })
  .input(NewPlanetSchema)
  .output(PlanetSchema)
  .handler(async ({ input, context }) =&gt; {
    return context.db.planets.create(input, context.user)
  })

export const findPlanet = pub
  .use(retry({ times: 3 }))
  .route({
    method: &#39;GET&#39;,
    path: &#39;/planets/{id}&#39;,
    summary: &#39;Find a planet&#39;,
    tags: [&#39;Planets&#39;],
  })
  .input(
    z.object({
      id: z.number().int().min(1),
    }),
  )
  .output(PlanetSchema)
  .handler(async ({ input, context }) =&gt; {
    const planet = await context.db.planets.find(input.id)

    if (!planet) {
      throw new ORPCError(&#39;NOT_FOUND&#39;, { message: &#39;Planet not found&#39; })
    }

    return planet
  })

export const updatePlanet = authed
  .route({
    method: &#39;PUT&#39;,
    path: &#39;/planets/{id}&#39;,
    summary: &#39;Update a planet&#39;,
    tags: [&#39;Planets&#39;],
  })
  .errors({
    NOT_FOUND: {
      message: &#39;Planet not found&#39;,
      data: z.object({ id: UpdatePlanetSchema.shape.id }),
    },
  })
  .input(UpdatePlanetSchema)
  .output(PlanetSchema)
  .handler(async ({ input, context, errors }) =&gt; {
    const planet = await context.db.planets.find(input.id)

    if (!planet) {
      /**
       *  1. Type-Safe Error Handling
       *
       * {@link https://orpc.unnoq.com/docs/error-handling#type%E2%80%90safe-error-handling}
       */
      throw errors.NOT_FOUND({ data: { id: input.id } })

      /**
       * 2. Normal Approach
       *
       * {@link https://orpc.unnoq.com/docs/error-handling#normal-approach}
       */
      // throw new ORPCError(&#39;NOT_FOUND&#39;, { message: &#39;Planet not found&#39; })
    }

    return context.db.planets.update(input)
  })
">
<input type="hidden" name="project[files][entrypoints/background/router/sse.ts]" value="import { eventIterator, os } from &#39;@orpc/server&#39;
import { z } from &#39;zod&#39;

const MAX_EVENTS = 5

export const sse = os
  .route({
    method: &#39;GET&#39;,
    path: &#39;/sse&#39;,
    tags: [&#39;SSE&#39;],
    summary: &#39;Server-Sent Events&#39;,
  })
  .output(eventIterator(z.object({ time: z.date() })))
  .handler(async function* () {
    let count = 0

    while (count &lt; MAX_EVENTS) {
      count++
      yield { time: new Date() }
      await new Promise(resolve =&gt; setTimeout(resolve, 1000))
    }
  })
">
<input type="hidden" name="project[files][entrypoints/background/schemas/auth.ts]" value="import { z } from &#39;zod&#39;

export const CredentialSchema = z.object({
  email: z.string().email(),
  password: z.string(),
})

export const TokenSchema = z.object({
  token: z.string(),
})
">
<input type="hidden" name="project[files][entrypoints/background/schemas/planet.ts]" value="import { oz } from &#39;@orpc/zod&#39;
import { z } from &#39;zod&#39;
import { UserSchema } from &#39;./user&#39;

export type NewPlanet = z.infer&lt;typeof NewPlanetSchema&gt;
export type UpdatePlanet = z.infer&lt;typeof UpdatePlanetSchema&gt;
export type Planet = z.infer&lt;typeof PlanetSchema&gt;

export const NewPlanetSchema = z.object({
  name: z.string(),
  description: z.string().optional(),
  image: oz.file().type(&#39;image/*&#39;).optional(),
})

export const UpdatePlanetSchema = z.object({
  id: z.number().int().min(1),
  name: z.string(),
  description: z.string().optional(),
  image: oz.file().type(&#39;image/*&#39;).optional(),
})

export const PlanetSchema = z.object({
  id: z.number().int().min(1),
  name: z.string(),
  description: z.string().optional(),
  imageUrl: z.string().url().optional(),
  creator: UserSchema,
})
">
<input type="hidden" name="project[files][entrypoints/background/schemas/user.ts]" value="import { oz } from &#39;@orpc/zod&#39;
import { z } from &#39;zod&#39;

export type NewUser = z.infer&lt;typeof NewUserSchema&gt;
export type User = z.infer&lt;typeof UserSchema&gt;

export const NewUserSchema = oz.openapi(
  z.object({
    name: z.string(),
    email: z.string().email(),
    password: z.string(),
  }),
  {
    examples: [
      {
        name: &#39;John Doe&#39;,
        email: &#39;john@doe.com&#39;,
        password: &#39;123456&#39;,
      },
    ],
  },
)

export const UserSchema = oz.openapi(
  z.object({
    id: z.string(),
    name: z.string(),
    email: z.string().email(),
  }),
  {
    examples: [
      {
        id: &#39;1&#39;,
        name: &#39;John Doe&#39;,
        email: &#39;john@doe.com&#39;,
      },
    ],
  },
)
">
<input type="hidden" name="project[files][entrypoints/popup/components/orpc-mutation.tsx]" value="import { orpc } from &#39;../lib/orpc&#39;
import { useMutation, useQueryClient } from &#39;@tanstack/react-query&#39;

export function CreatePlanetMutationForm() {
  const queryClient = useQueryClient()

  const { mutate } = useMutation(
    orpc.planet.create.mutationOptions({
      onSuccess() {
        queryClient.invalidateQueries({
          queryKey: orpc.planet.key(),
        })
      },
      onError(error) {
        console.error(error)
        alert(error.message)
      },
    }),
  )

  return (
    &lt;div&gt;
      &lt;h2&gt;oRPC and Tanstack Query | Create Planet example&lt;/h2&gt;

      &lt;form
        onSubmit={(e) =&gt; {
          e.preventDefault()
          const form = new FormData(e.target as HTMLFormElement)

          const name = form.get(&#39;name&#39;) as string
          const description
          = (form.get(&#39;description&#39;) as string | null) ?? undefined
          const image = form.get(&#39;image&#39;) as File | undefined

          mutate({
            name,
            description,
            image: image &amp;&amp; image.size &gt; 0 ? image : undefined,
          })
        }}
      &gt;
        &lt;label&gt;
          Name
          &lt;input type=&quot;text&quot; name=&quot;name&quot; required /&gt;
        &lt;/label&gt;
        &lt;label&gt;
          Description
          &lt;textarea name=&quot;description&quot; /&gt;
        &lt;/label&gt;
        {/* &lt;label&gt;
          Image
          &lt;input type=&quot;file&quot; name=&quot;image&quot; accept=&quot;image/*&quot; /&gt;
        &lt;/label&gt; */}
        &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  )
}
">
<input type="hidden" name="project[files][entrypoints/popup/components/orpc-query.tsx]" value="import { useInfiniteQuery } from &#39;@tanstack/react-query&#39;
import { orpc } from &#39;../lib/orpc&#39;

export function ListPlanetsQuery() {
  const { data, refetch, fetchNextPage, hasNextPage, status } = useInfiniteQuery(
    orpc.planet.list.infiniteOptions({
      input: cursor =&gt; ({ cursor, limit: 10 }),
      getNextPageParam: lastPage =&gt; lastPage.length === 10 ? lastPage.at(-1)?.id : null,
      initialPageParam: 0,
    }),
  )

  if (status === &#39;pending&#39;) {
    return (
      &lt;p&gt;
        Loading...
      &lt;/p&gt;
    )
  }

  if (status === &#39;error&#39;) {
    return (
      &lt;p&gt;
        Something went wrong.
      &lt;/p&gt;
    )
  }

  return (
    &lt;div&gt;
      &lt;h2&gt;oRPC and Tanstack Query | List Planets example&lt;/h2&gt;

      &lt;table&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;ID&lt;/th&gt;
            &lt;th&gt;Name&lt;/th&gt;
            &lt;th&gt;Description&lt;/th&gt;
            &lt;th&gt;Image&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
          {data.pages.flatMap((page, i) =&gt;
            page.map(planet =&gt; (
              &lt;tr key={`${planet.id}-${i}`}&gt;
                &lt;td&gt;{planet.id}&lt;/td&gt;
                &lt;td&gt;{planet.name}&lt;/td&gt;
                &lt;td&gt;{planet.description}&lt;/td&gt;
                &lt;td&gt;{planet.imageUrl}&lt;/td&gt;
              &lt;/tr&gt;
            )),
          )}
        &lt;/tbody&gt;

        &lt;tfoot&gt;
          &lt;tr&gt;
            &lt;td colSpan={4}&gt;
              &lt;button
                type=&quot;button&quot;
                onClick={() =&gt; fetchNextPage()}
                disabled={!hasNextPage}
              &gt;
                Load more
              &lt;/button&gt;

              &lt;button type=&quot;button&quot; onClick={() =&gt; refetch()}&gt;
                Refresh
              &lt;/button&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/tfoot&gt;
      &lt;/table&gt;
    &lt;/div&gt;

  )
}
">
<input type="hidden" name="project[files][entrypoints/popup/lib/orpc.ts]" value="import type { router } from &#39;../.../../../background/router&#39;
import type { RouterClient } from &#39;@orpc/server&#39;
import { createORPCClient } from &#39;@orpc/client&#39;
import { experimental_RPCLink as RPCLink } from &#39;@orpc/client/message-port&#39;
import { createORPCReactQueryUtils } from &#39;@orpc/react-query&#39;

const port = browser.runtime.connect()

const link = new RPCLink({
  port,
})

export const client: RouterClient&lt;typeof router&gt; = createORPCClient(link)

export const orpc = createORPCReactQueryUtils(client)
">
<input type="hidden" name="project[description]" value="generated by https://pkg.pr.new">
<input type="hidden" name="project[template]" value="node">
<input type="hidden" name="project[title]" value="@orpc/browser-extension-playground">
</form>
<script>document.getElementById("mainForm").submit();</script>

</body></html>