35 lines
1.6 KiB
TypeScript
35 lines
1.6 KiB
TypeScript
"use client"
|
|
|
|
// How this module's interactive components reach the API: through a transport the app provides.
|
|
//
|
|
// A module may not import the app (the layer rules), and a server component cannot hand a function
|
|
// to a client one, so the seam is a context. `apps/web/src/ui/transport.tsx` is a client component
|
|
// that provides the app's own fetch-based `call` (CSRF token, problem+json errors) and wraps the
|
|
// module's pages; the components below ask for it. This is the "data facade" of spec §6.3 in its
|
|
// smallest form, and it is what `useRead`/`useWrite` from `@conusai-kit/ui/data` replace.
|
|
|
|
import { createContext, type ReactElement, type ReactNode, useContext } from "react"
|
|
|
|
/** Calls a procedure by name with an input, and answers with its output or throws its refusal. */
|
|
export type Transport = (procedure: string, input: unknown) => Promise<unknown>
|
|
|
|
/** A refusal's detail for the user, extracted by the app's transport; anything else is "unknown". */
|
|
export type DescribeError = (error: unknown) => string
|
|
|
|
interface Provided {
|
|
readonly call: Transport
|
|
readonly describe: DescribeError
|
|
}
|
|
|
|
const TransportContext = createContext<Provided | undefined>(undefined)
|
|
|
|
export function TransportProvider({ call, describe, children }: Provided & { children: ReactNode }): ReactElement {
|
|
return <TransportContext.Provider value={{ call, describe }}>{children}</TransportContext.Provider>
|
|
}
|
|
|
|
export function useTransport(): Provided {
|
|
const provided = useContext(TransportContext)
|
|
if (!provided) throw new Error("documents: wrap the page in a TransportProvider (apps/web/src/ui/transport.tsx)")
|
|
return provided
|
|
}
|