@beignet/web 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # @beignet/web
2
+
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 8bd9085: Add `@beignet/web` as a framework-neutral Web Fetch adapter, include a `@beignet/web/testing` route test harness, and have `@beignet/next` reuse the Web adapter for standard Request/Response handling.
8
+ - Updated dependencies [3160184]
9
+ - Updated dependencies [254ef6d]
10
+ - Updated dependencies [4cb1784]
11
+ - Updated dependencies [8bd9085]
12
+ - @beignet/core@0.0.3
13
+
14
+ ## 0.0.2
15
+
16
+ ### Patch Changes
17
+
18
+ - Add the Web Fetch adapter package.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # @beignet/web
2
+
3
+ Web Fetch adapter for Beignet's framework runtime.
4
+
5
+ Use this package when your runtime accepts a standard `Request` and returns a
6
+ standard `Response`, such as Cloudflare Workers, Bun, Deno, Node fetch servers,
7
+ or tests that should avoid a framework-specific adapter.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @beignet/web
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```typescript
18
+ import { createFetchServer } from "@beignet/web";
19
+ import { routes } from "./server/routes";
20
+
21
+ export const server = await createFetchServer({
22
+ ports,
23
+ routes,
24
+ createContext: ({ ports, req }) => ({
25
+ requestId: req.headers.get("x-request-id") ?? crypto.randomUUID(),
26
+ ports,
27
+ }),
28
+ mapUnhandledError: () => ({
29
+ status: 500,
30
+ body: {
31
+ code: "INTERNAL_SERVER_ERROR",
32
+ message: "Internal server error",
33
+ },
34
+ }),
35
+ });
36
+
37
+ export default {
38
+ fetch: server.fetch,
39
+ };
40
+ ```
41
+
42
+ ## Bun
43
+
44
+ ```typescript
45
+ import { server } from "./server";
46
+
47
+ Bun.serve({
48
+ fetch: server.fetch,
49
+ });
50
+ ```
51
+
52
+ ## Lower-level helpers
53
+
54
+ ```typescript
55
+ import {
56
+ createFetchHandler,
57
+ toRequestLike,
58
+ toWebResponse,
59
+ } from "@beignet/web";
60
+ ```
61
+
62
+ - `createFetchHandler(server)` adapts a Beignet server instance or API handler
63
+ to `(req: Request) => Promise<Response>`.
64
+ - `toRequestLike(req)` converts a standard `Request` to Beignet's
65
+ framework-neutral request shape.
66
+ - `toWebResponse(response)` converts a Beignet response to a standard
67
+ `Response`.
68
+
69
+ Native `Response` instances returned by handlers are passed through unchanged.
70
+ Plain Beignet responses are serialized as JSON unless the body is `undefined`
71
+ or `null`.
72
+
73
+ ## Testing
74
+
75
+ Use `@beignet/web/testing` to exercise routes through the same Web Fetch
76
+ adapter without opening a network port.
77
+
78
+ ```typescript
79
+ import { createTestApp } from "@beignet/web/testing";
80
+ import { getTodo } from "./features/todos/contracts";
81
+ import { routes } from "./server/routes";
82
+
83
+ const app = await createTestApp({
84
+ ports,
85
+ routes,
86
+ createContext: ({ ports }) => ({
87
+ requestId: "test-request",
88
+ ports,
89
+ }),
90
+ });
91
+
92
+ const todo = await app.request(getTodo, {
93
+ path: { id: "todo_1" },
94
+ });
95
+ ```
96
+
97
+ `app.request(contract, args)` uses the same typed call arguments as
98
+ `@beignet/core/client`, while `app.safeRequest(contract, args)` returns a typed
99
+ success/error result instead of throwing.
@@ -0,0 +1,66 @@
1
+ import type { AnyPorts } from "@beignet/core/ports";
2
+ import type { ProvidedPortsOfList, ServiceProvider } from "@beignet/core/providers";
3
+ import type { ContractLike, CreateServerOptions, Handler, HttpRequestLike, HttpResponse, ResolveContract, RouteDef, ServerInstance } from "@beignet/core/server";
4
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
5
+ /**
6
+ * Server types re-exported for Web Fetch apps.
7
+ */
8
+ export type { CreateServerOptions, HttpRequestLike, HttpResponse, RouteDef, RouteGroup, RouteHook, ServerInstance, } from "@beignet/core/server";
9
+ /**
10
+ * Server helpers re-exported for Web Fetch apps.
11
+ */
12
+ export { contractsFromRoutes, createServer, defineRoute, defineRouteGroup, defineRouteHook, defineRoutes, } from "@beignet/core/server";
13
+ type AnyProvider = ServiceProvider<unknown, StandardSchemaV1<any, any>, AnyPorts>;
14
+ /**
15
+ * Beignet server adapted to the Web Fetch API.
16
+ */
17
+ export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts> {
18
+ /**
19
+ * Catch-all Web Fetch handler.
20
+ */
21
+ fetch: (req: Request) => Promise<Response>;
22
+ /**
23
+ * Alias for `fetch` for API route adapters that expect an `api` property.
24
+ */
25
+ api: (req: Request) => Promise<Response>;
26
+ /**
27
+ * Register and build a single Web Fetch route handler imperatively.
28
+ */
29
+ route: <CLike extends ContractLike>(contractLike: CLike) => {
30
+ handle: (fn: Handler<Ctx, ResolveContract<CLike>>) => (req: Request) => Promise<Response>;
31
+ };
32
+ /**
33
+ * Registered contract inputs.
34
+ */
35
+ contracts: readonly ContractLike[];
36
+ /**
37
+ * Stop installed providers.
38
+ */
39
+ stop: () => Promise<void>;
40
+ /**
41
+ * Final app ports after provider setup.
42
+ */
43
+ ports: Ports;
44
+ }
45
+ /**
46
+ * Convert a native `Request` into Beignet's framework-neutral request shape.
47
+ */
48
+ export declare function toRequestLike(req: Request): HttpRequestLike;
49
+ /**
50
+ * Convert a Beignet response into a native `Response`.
51
+ */
52
+ export declare function toWebResponse(resLike: HttpResponse): Response;
53
+ /**
54
+ * Create a Web Fetch handler from a Beignet server or API handler.
55
+ */
56
+ export declare function createFetchHandler(serverOrHandler: Pick<ServerInstance<unknown>, "api"> | ((req: HttpRequestLike) => Promise<HttpResponse>)): (req: Request) => Promise<Response>;
57
+ /**
58
+ * Create a Beignet server adapted to the Web Fetch API.
59
+ *
60
+ * Use this for runtimes that accept standard `Request` objects and return
61
+ * standard `Response` objects. Next.js apps should usually use
62
+ * `@beignet/next`, which builds on the same Web adapter and adds Next-specific
63
+ * helpers.
64
+ */
65
+ export declare function createFetchServer<Ctx, Ports extends AnyPorts = AnyPorts, Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[], Providers extends readonly AnyProvider[] = readonly AnyProvider[]>(options: CreateServerOptions<Ctx, Ports, Routes, Providers>): Promise<FetchServer<Ctx, Ports & ProvidedPortsOfList<Providers>>>;
66
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,OAAO,EACP,eAAe,EACf,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,cAAc,GACf,MAAM,sBAAsB,CAAC;AAE9B;;GAEG;AACH,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,YAAY,GACb,MAAM,sBAAsB,CAAC;AAE9B,KAAK,WAAW,GAAG,eAAe,CAChC,OAAO,EAEP,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,EAC1B,QAAQ,CACT,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,GAAG,EAAE,KAAK,SAAS,QAAQ,GAAG,QAAQ;IACjE;;OAEG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3C;;OAEG;IACH,GAAG,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzC;;OAEG;IACH,KAAK,EAAE,CAAC,KAAK,SAAS,YAAY,EAChC,YAAY,EAAE,KAAK,KAChB;QACH,MAAM,EAAE,CACN,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,KACrC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;KAC1C,CAAC;IACF;;OAEG;IACH,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC;;OAEG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,CAa3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,CAc7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,eAAe,EACX,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,GACpC,CAAC,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC,GACpD,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAOrC;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EACH,KAAK,SAAS,QAAQ,GAAG,QAAQ,EACjC,MAAM,SAAS,SAAS,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,QAAQ,CAAC,GAAG,CAAC,EAAE,EAClE,SAAS,SAAS,SAAS,WAAW,EAAE,GAAG,SAAS,WAAW,EAAE,EAEjE,OAAO,EAAE,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,GAC1D,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAcnE"}
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { createServer } from "@beignet/core/server";
2
+ /**
3
+ * Server helpers re-exported for Web Fetch apps.
4
+ */
5
+ export { contractsFromRoutes, createServer, defineRoute, defineRouteGroup, defineRouteHook, defineRoutes, } from "@beignet/core/server";
6
+ /**
7
+ * Convert a native `Request` into Beignet's framework-neutral request shape.
8
+ */
9
+ export function toRequestLike(req) {
10
+ return {
11
+ method: req.method,
12
+ url: req.url,
13
+ headers: new Headers(req.headers),
14
+ raw: req,
15
+ json: () => req.json(),
16
+ text: () => req.text(),
17
+ arrayBuffer: () => req.arrayBuffer(),
18
+ blob: () => req.blob(),
19
+ formData: () => req.formData(),
20
+ clone: () => toRequestLike(req.clone()),
21
+ };
22
+ }
23
+ /**
24
+ * Convert a Beignet response into a native `Response`.
25
+ */
26
+ export function toWebResponse(resLike) {
27
+ if (resLike instanceof Response) {
28
+ return resLike;
29
+ }
30
+ const headers = new Headers(resLike.headers);
31
+ const body = resLike.body;
32
+ if (body === undefined || body === null) {
33
+ return new Response(null, { status: resLike.status, headers });
34
+ }
35
+ // biome-ignore lint/suspicious/noExplicitAny: Body can be any JSON-serializable value.
36
+ return Response.json(body, { status: resLike.status, headers });
37
+ }
38
+ /**
39
+ * Create a Web Fetch handler from a Beignet server or API handler.
40
+ */
41
+ export function createFetchHandler(serverOrHandler) {
42
+ const handler = typeof serverOrHandler === "function"
43
+ ? serverOrHandler
44
+ : serverOrHandler.api;
45
+ return async (req) => toWebResponse(await handler(toRequestLike(req)));
46
+ }
47
+ /**
48
+ * Create a Beignet server adapted to the Web Fetch API.
49
+ *
50
+ * Use this for runtimes that accept standard `Request` objects and return
51
+ * standard `Response` objects. Next.js apps should usually use
52
+ * `@beignet/next`, which builds on the same Web adapter and adds Next-specific
53
+ * helpers.
54
+ */
55
+ export async function createFetchServer(options) {
56
+ const runtime = await createServer(options);
57
+ const fetch = createFetchHandler(runtime);
58
+ return {
59
+ fetch,
60
+ api: fetch,
61
+ route: (contract) => ({
62
+ handle: (fn) => createFetchHandler(runtime.route(contract).handle(fn)),
63
+ }),
64
+ contracts: runtime.contracts,
65
+ stop: () => runtime.stop(),
66
+ ports: runtime.ports,
67
+ };
68
+ }
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAgBpD;;GAEG;AACH,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,YAAY,GACb,MAAM,sBAAsB,CAAC;AA6C9B;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,OAAO,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QACjC,GAAG,EAAE,GAAG;QACR,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE;QACtB,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE;QACtB,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE;QACpC,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE;QACtB,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE;QAC9B,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;KACxC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAqB;IACjD,IAAI,OAAO,YAAY,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACxC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,uFAAuF;IACvF,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAW,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AACzE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,eAEqD;IAErD,MAAM,OAAO,GACX,OAAO,eAAe,KAAK,UAAU;QACnC,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC;IAE1B,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAMrC,OAA2D;IAE3D,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE1C,OAAO;QACL,KAAK;QACL,GAAG,EAAE,KAAK;QACV,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACpB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SACvE,CAAC;QACF,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { type CallArgs, type ClientConfig, type EndpointResult, type InferEndpointContractError, type InferSuccessResponse } from "@beignet/core/client";
2
+ import type { AnyPorts } from "@beignet/core/ports";
3
+ import type { ProvidedPortsOfList, ServiceProvider } from "@beignet/core/providers";
4
+ import type { ContractLike, CreateServerOptions, ResolveContract, RouteDef } from "@beignet/core/server";
5
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
6
+ import { type FetchServer } from "./index";
7
+ type AnyProvider = ServiceProvider<unknown, StandardSchemaV1<any, any>, AnyPorts>;
8
+ /**
9
+ * Client options accepted by `createTestApp(...)`.
10
+ */
11
+ export type TestAppClientOptions<TProvidedHeaders extends string = never> = Omit<ClientConfig<TProvidedHeaders>, "baseUrl" | "fetch"> & {
12
+ /**
13
+ * Base URL used when the typed client builds requests.
14
+ *
15
+ * @default "http://beignet.test"
16
+ */
17
+ baseUrl?: string;
18
+ };
19
+ /**
20
+ * Options for creating a Web Fetch test app.
21
+ */
22
+ export type CreateTestAppOptions<Ctx, Ports extends AnyPorts, Routes extends readonly RouteDef<Ctx>[], Providers extends readonly AnyProvider[], TProvidedHeaders extends string = never> = CreateServerOptions<Ctx, Ports, Routes, Providers> & {
23
+ /**
24
+ * Typed client behavior for `request(...)` and `safeRequest(...)`.
25
+ */
26
+ client?: TestAppClientOptions<TProvidedHeaders>;
27
+ };
28
+ /**
29
+ * Beignet app harness for route/runtime tests.
30
+ */
31
+ export type TestApp<Ctx, Ports extends AnyPorts, TProvidedHeaders extends string = never> = {
32
+ /**
33
+ * Underlying Web Fetch server.
34
+ */
35
+ server: FetchServer<Ctx, Ports>;
36
+ /**
37
+ * Fetch implementation bound to the in-memory Beignet server.
38
+ */
39
+ fetch: typeof fetch;
40
+ /**
41
+ * Final app ports after provider setup.
42
+ */
43
+ ports: Ports;
44
+ /**
45
+ * Contracts registered through the server route list.
46
+ */
47
+ contracts: FetchServer<Ctx, Ports>["contracts"];
48
+ /**
49
+ * Call a contract through the test app and return the parsed success body.
50
+ */
51
+ request: <CLike extends ContractLike>(contract: CLike, ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>) => Promise<InferSuccessResponse<ResolveContract<CLike>>>;
52
+ /**
53
+ * Call a contract through the test app and return a typed success/error
54
+ * result instead of throwing.
55
+ */
56
+ safeRequest: <CLike extends ContractLike>(contract: CLike, ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>) => Promise<EndpointResult<ResolveContract<CLike>, InferEndpointContractError<ResolveContract<CLike>>>>;
57
+ /**
58
+ * Stop installed providers.
59
+ */
60
+ stop: () => Promise<void>;
61
+ };
62
+ /**
63
+ * Create a `fetch` implementation that dispatches requests to a Beignet Web
64
+ * server without opening a network port.
65
+ *
66
+ * @param server - Web Fetch server under test.
67
+ * @returns Fetch-compatible function backed by `server.fetch`.
68
+ */
69
+ export declare function createTestFetch<Ctx, Ports extends AnyPorts>(server: Pick<FetchServer<Ctx, Ports>, "fetch">): typeof fetch;
70
+ /**
71
+ * Create a Beignet route/runtime test harness.
72
+ *
73
+ * The harness uses `@beignet/web` under the hood, so tests exercise the same
74
+ * request parsing, route hooks, response validation, and error ownership as a
75
+ * Web Fetch runtime without requiring Next.js or a network listener.
76
+ *
77
+ * @param options - Server options plus optional typed-client settings.
78
+ * @returns Test app with typed `request(...)` helpers and a bound `fetch`.
79
+ */
80
+ export declare function createTestApp<Ctx, Ports extends AnyPorts = AnyPorts, Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[], Providers extends readonly AnyProvider[] = readonly AnyProvider[], const TProvidedHeaders extends string = never>(options: CreateTestAppOptions<Ctx, Ports, Routes, Providers, TProvidedHeaders>): Promise<TestApp<Ctx, Ports & ProvidedPortsOfList<Providers>, TProvidedHeaders>>;
81
+ export {};
82
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { createClient, } from "@beignet/core/client";
2
+ import { createFetchServer } from "./index";
3
+ /**
4
+ * Create a `fetch` implementation that dispatches requests to a Beignet Web
5
+ * server without opening a network port.
6
+ *
7
+ * @param server - Web Fetch server under test.
8
+ * @returns Fetch-compatible function backed by `server.fetch`.
9
+ */
10
+ export function createTestFetch(server) {
11
+ return ((input, init) => {
12
+ const req = new Request(input, init);
13
+ return server.fetch(req);
14
+ });
15
+ }
16
+ /**
17
+ * Create a Beignet route/runtime test harness.
18
+ *
19
+ * The harness uses `@beignet/web` under the hood, so tests exercise the same
20
+ * request parsing, route hooks, response validation, and error ownership as a
21
+ * Web Fetch runtime without requiring Next.js or a network listener.
22
+ *
23
+ * @param options - Server options plus optional typed-client settings.
24
+ * @returns Test app with typed `request(...)` helpers and a bound `fetch`.
25
+ */
26
+ export async function createTestApp(options) {
27
+ const { client: clientOptions, ...serverOptions } = options;
28
+ const server = await createFetchServer(serverOptions);
29
+ const fetch = createTestFetch(server);
30
+ const client = createClient({
31
+ ...clientOptions,
32
+ baseUrl: clientOptions?.baseUrl ?? "http://beignet.test",
33
+ fetch,
34
+ });
35
+ return {
36
+ server,
37
+ fetch,
38
+ ports: server.ports,
39
+ contracts: server.contracts,
40
+ request: (contract, ...args) => client.endpoint(contract).call(...args),
41
+ safeRequest: (contract, ...args) => client.endpoint(contract).safeCall(...args),
42
+ stop: () => server.stop(),
43
+ };
44
+ }
45
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,YAAY,GAIb,MAAM,sBAAsB,CAAC;AAa9B,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAC;AAwF9D;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA8C;IAE9C,OAAO,CAAC,CAAC,KAAwB,EAAE,IAAkB,EAAE,EAAE;QACvD,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAErC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC,CAAiB,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAOjC,OAMC;IAID,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,YAAY,CAAmB;QAC5C,GAAG,aAAa;QAChB,OAAO,EAAE,aAAa,EAAE,OAAO,IAAI,qBAAqB;QACxD,KAAK;KACN,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,KAAK;QACL,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACvE,WAAW,EAAE,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,CACjC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;QAC7C,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE;KAC1B,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@beignet/web",
3
+ "version": "0.0.3",
4
+ "type": "module",
5
+ "description": "Web Fetch adapter for Beignet",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./testing": {
14
+ "types": "./dist/testing.d.ts",
15
+ "default": "./dist/testing.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src",
21
+ "!src/**/*.test.ts",
22
+ "!src/**/*.test.tsx",
23
+ "!src/**/*.test-d.ts",
24
+ "README.md",
25
+ "CHANGELOG.md"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "dev": "tsc --watch",
30
+ "clean": "rm -rf dist coverage .turbo",
31
+ "test": "bun test",
32
+ "test:coverage": "bun test --coverage",
33
+ "lint": "biome check ."
34
+ },
35
+ "keywords": [
36
+ "contract",
37
+ "api",
38
+ "typescript",
39
+ "fetch",
40
+ "web-standard"
41
+ ],
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/taylorbryant/beignet.git",
46
+ "directory": "packages/web"
47
+ },
48
+ "author": "Taylor Bryant",
49
+ "homepage": "https://github.com/taylorbryant/beignet#readme",
50
+ "bugs": "https://github.com/taylorbryant/beignet/issues",
51
+ "sideEffects": false,
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ },
58
+ "dependencies": {
59
+ "@beignet/core": "*",
60
+ "@standard-schema/spec": "^1.0.0"
61
+ },
62
+ "devDependencies": {
63
+ "@types/bun": "^1.3.13",
64
+ "@types/node": "^20.10.0",
65
+ "typescript": "^5.3.0",
66
+ "zod": "^4.0.0"
67
+ }
68
+ }
package/src/index.ts ADDED
@@ -0,0 +1,169 @@
1
+ import type { AnyPorts } from "@beignet/core/ports";
2
+ import type {
3
+ ProvidedPortsOfList,
4
+ ServiceProvider,
5
+ } from "@beignet/core/providers";
6
+ import type {
7
+ ContractLike,
8
+ CreateServerOptions,
9
+ Handler,
10
+ HttpRequestLike,
11
+ HttpResponse,
12
+ ResolveContract,
13
+ RouteDef,
14
+ ServerInstance,
15
+ } from "@beignet/core/server";
16
+ import { createServer } from "@beignet/core/server";
17
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
18
+
19
+ /**
20
+ * Server types re-exported for Web Fetch apps.
21
+ */
22
+ export type {
23
+ CreateServerOptions,
24
+ HttpRequestLike,
25
+ HttpResponse,
26
+ RouteDef,
27
+ RouteGroup,
28
+ RouteHook,
29
+ ServerInstance,
30
+ } from "@beignet/core/server";
31
+
32
+ /**
33
+ * Server helpers re-exported for Web Fetch apps.
34
+ */
35
+ export {
36
+ contractsFromRoutes,
37
+ createServer,
38
+ defineRoute,
39
+ defineRouteGroup,
40
+ defineRouteHook,
41
+ defineRoutes,
42
+ } from "@beignet/core/server";
43
+
44
+ type AnyProvider = ServiceProvider<
45
+ unknown,
46
+ // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
47
+ StandardSchemaV1<any, any>,
48
+ AnyPorts
49
+ >;
50
+
51
+ /**
52
+ * Beignet server adapted to the Web Fetch API.
53
+ */
54
+ export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts> {
55
+ /**
56
+ * Catch-all Web Fetch handler.
57
+ */
58
+ fetch: (req: Request) => Promise<Response>;
59
+ /**
60
+ * Alias for `fetch` for API route adapters that expect an `api` property.
61
+ */
62
+ api: (req: Request) => Promise<Response>;
63
+ /**
64
+ * Register and build a single Web Fetch route handler imperatively.
65
+ */
66
+ route: <CLike extends ContractLike>(
67
+ contractLike: CLike,
68
+ ) => {
69
+ handle: (
70
+ fn: Handler<Ctx, ResolveContract<CLike>>,
71
+ ) => (req: Request) => Promise<Response>;
72
+ };
73
+ /**
74
+ * Registered contract inputs.
75
+ */
76
+ contracts: readonly ContractLike[];
77
+ /**
78
+ * Stop installed providers.
79
+ */
80
+ stop: () => Promise<void>;
81
+ /**
82
+ * Final app ports after provider setup.
83
+ */
84
+ ports: Ports;
85
+ }
86
+
87
+ /**
88
+ * Convert a native `Request` into Beignet's framework-neutral request shape.
89
+ */
90
+ export function toRequestLike(req: Request): HttpRequestLike {
91
+ return {
92
+ method: req.method,
93
+ url: req.url,
94
+ headers: new Headers(req.headers),
95
+ raw: req,
96
+ json: () => req.json(),
97
+ text: () => req.text(),
98
+ arrayBuffer: () => req.arrayBuffer(),
99
+ blob: () => req.blob(),
100
+ formData: () => req.formData(),
101
+ clone: () => toRequestLike(req.clone()),
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Convert a Beignet response into a native `Response`.
107
+ */
108
+ export function toWebResponse(resLike: HttpResponse): Response {
109
+ if (resLike instanceof Response) {
110
+ return resLike;
111
+ }
112
+
113
+ const headers = new Headers(resLike.headers);
114
+ const body = resLike.body;
115
+
116
+ if (body === undefined || body === null) {
117
+ return new Response(null, { status: resLike.status, headers });
118
+ }
119
+
120
+ // biome-ignore lint/suspicious/noExplicitAny: Body can be any JSON-serializable value.
121
+ return Response.json(body as any, { status: resLike.status, headers });
122
+ }
123
+
124
+ /**
125
+ * Create a Web Fetch handler from a Beignet server or API handler.
126
+ */
127
+ export function createFetchHandler(
128
+ serverOrHandler:
129
+ | Pick<ServerInstance<unknown>, "api">
130
+ | ((req: HttpRequestLike) => Promise<HttpResponse>),
131
+ ): (req: Request) => Promise<Response> {
132
+ const handler =
133
+ typeof serverOrHandler === "function"
134
+ ? serverOrHandler
135
+ : serverOrHandler.api;
136
+
137
+ return async (req) => toWebResponse(await handler(toRequestLike(req)));
138
+ }
139
+
140
+ /**
141
+ * Create a Beignet server adapted to the Web Fetch API.
142
+ *
143
+ * Use this for runtimes that accept standard `Request` objects and return
144
+ * standard `Response` objects. Next.js apps should usually use
145
+ * `@beignet/next`, which builds on the same Web adapter and adds Next-specific
146
+ * helpers.
147
+ */
148
+ export async function createFetchServer<
149
+ Ctx,
150
+ Ports extends AnyPorts = AnyPorts,
151
+ Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[],
152
+ Providers extends readonly AnyProvider[] = readonly AnyProvider[],
153
+ >(
154
+ options: CreateServerOptions<Ctx, Ports, Routes, Providers>,
155
+ ): Promise<FetchServer<Ctx, Ports & ProvidedPortsOfList<Providers>>> {
156
+ const runtime = await createServer(options);
157
+ const fetch = createFetchHandler(runtime);
158
+
159
+ return {
160
+ fetch,
161
+ api: fetch,
162
+ route: (contract) => ({
163
+ handle: (fn) => createFetchHandler(runtime.route(contract).handle(fn)),
164
+ }),
165
+ contracts: runtime.contracts,
166
+ stop: () => runtime.stop(),
167
+ ports: runtime.ports,
168
+ };
169
+ }
package/src/testing.ts ADDED
@@ -0,0 +1,172 @@
1
+ import {
2
+ type CallArgs,
3
+ type ClientConfig,
4
+ createClient,
5
+ type EndpointResult,
6
+ type InferEndpointContractError,
7
+ type InferSuccessResponse,
8
+ } from "@beignet/core/client";
9
+ import type { AnyPorts } from "@beignet/core/ports";
10
+ import type {
11
+ ProvidedPortsOfList,
12
+ ServiceProvider,
13
+ } from "@beignet/core/providers";
14
+ import type {
15
+ ContractLike,
16
+ CreateServerOptions,
17
+ ResolveContract,
18
+ RouteDef,
19
+ } from "@beignet/core/server";
20
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
21
+ import { createFetchServer, type FetchServer } from "./index";
22
+
23
+ type AnyProvider = ServiceProvider<
24
+ unknown,
25
+ // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level.
26
+ StandardSchemaV1<any, any>,
27
+ AnyPorts
28
+ >;
29
+
30
+ /**
31
+ * Client options accepted by `createTestApp(...)`.
32
+ */
33
+ export type TestAppClientOptions<TProvidedHeaders extends string = never> =
34
+ Omit<ClientConfig<TProvidedHeaders>, "baseUrl" | "fetch"> & {
35
+ /**
36
+ * Base URL used when the typed client builds requests.
37
+ *
38
+ * @default "http://beignet.test"
39
+ */
40
+ baseUrl?: string;
41
+ };
42
+
43
+ /**
44
+ * Options for creating a Web Fetch test app.
45
+ */
46
+ export type CreateTestAppOptions<
47
+ Ctx,
48
+ Ports extends AnyPorts,
49
+ Routes extends readonly RouteDef<Ctx>[],
50
+ Providers extends readonly AnyProvider[],
51
+ TProvidedHeaders extends string = never,
52
+ > = CreateServerOptions<Ctx, Ports, Routes, Providers> & {
53
+ /**
54
+ * Typed client behavior for `request(...)` and `safeRequest(...)`.
55
+ */
56
+ client?: TestAppClientOptions<TProvidedHeaders>;
57
+ };
58
+
59
+ /**
60
+ * Beignet app harness for route/runtime tests.
61
+ */
62
+ export type TestApp<
63
+ Ctx,
64
+ Ports extends AnyPorts,
65
+ TProvidedHeaders extends string = never,
66
+ > = {
67
+ /**
68
+ * Underlying Web Fetch server.
69
+ */
70
+ server: FetchServer<Ctx, Ports>;
71
+ /**
72
+ * Fetch implementation bound to the in-memory Beignet server.
73
+ */
74
+ fetch: typeof fetch;
75
+ /**
76
+ * Final app ports after provider setup.
77
+ */
78
+ ports: Ports;
79
+ /**
80
+ * Contracts registered through the server route list.
81
+ */
82
+ contracts: FetchServer<Ctx, Ports>["contracts"];
83
+ /**
84
+ * Call a contract through the test app and return the parsed success body.
85
+ */
86
+ request: <CLike extends ContractLike>(
87
+ contract: CLike,
88
+ ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>
89
+ ) => Promise<InferSuccessResponse<ResolveContract<CLike>>>;
90
+ /**
91
+ * Call a contract through the test app and return a typed success/error
92
+ * result instead of throwing.
93
+ */
94
+ safeRequest: <CLike extends ContractLike>(
95
+ contract: CLike,
96
+ ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>
97
+ ) => Promise<
98
+ EndpointResult<
99
+ ResolveContract<CLike>,
100
+ InferEndpointContractError<ResolveContract<CLike>>
101
+ >
102
+ >;
103
+ /**
104
+ * Stop installed providers.
105
+ */
106
+ stop: () => Promise<void>;
107
+ };
108
+
109
+ /**
110
+ * Create a `fetch` implementation that dispatches requests to a Beignet Web
111
+ * server without opening a network port.
112
+ *
113
+ * @param server - Web Fetch server under test.
114
+ * @returns Fetch-compatible function backed by `server.fetch`.
115
+ */
116
+ export function createTestFetch<Ctx, Ports extends AnyPorts>(
117
+ server: Pick<FetchServer<Ctx, Ports>, "fetch">,
118
+ ): typeof fetch {
119
+ return ((input: RequestInfo | URL, init?: RequestInit) => {
120
+ const req = new Request(input, init);
121
+
122
+ return server.fetch(req);
123
+ }) as typeof fetch;
124
+ }
125
+
126
+ /**
127
+ * Create a Beignet route/runtime test harness.
128
+ *
129
+ * The harness uses `@beignet/web` under the hood, so tests exercise the same
130
+ * request parsing, route hooks, response validation, and error ownership as a
131
+ * Web Fetch runtime without requiring Next.js or a network listener.
132
+ *
133
+ * @param options - Server options plus optional typed-client settings.
134
+ * @returns Test app with typed `request(...)` helpers and a bound `fetch`.
135
+ */
136
+ export async function createTestApp<
137
+ Ctx,
138
+ Ports extends AnyPorts = AnyPorts,
139
+ Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[],
140
+ Providers extends readonly AnyProvider[] = readonly AnyProvider[],
141
+ const TProvidedHeaders extends string = never,
142
+ >(
143
+ options: CreateTestAppOptions<
144
+ Ctx,
145
+ Ports,
146
+ Routes,
147
+ Providers,
148
+ TProvidedHeaders
149
+ >,
150
+ ): Promise<
151
+ TestApp<Ctx, Ports & ProvidedPortsOfList<Providers>, TProvidedHeaders>
152
+ > {
153
+ const { client: clientOptions, ...serverOptions } = options;
154
+ const server = await createFetchServer(serverOptions);
155
+ const fetch = createTestFetch(server);
156
+ const client = createClient<TProvidedHeaders>({
157
+ ...clientOptions,
158
+ baseUrl: clientOptions?.baseUrl ?? "http://beignet.test",
159
+ fetch,
160
+ });
161
+
162
+ return {
163
+ server,
164
+ fetch,
165
+ ports: server.ports,
166
+ contracts: server.contracts,
167
+ request: (contract, ...args) => client.endpoint(contract).call(...args),
168
+ safeRequest: (contract, ...args) =>
169
+ client.endpoint(contract).safeCall(...args),
170
+ stop: () => server.stop(),
171
+ };
172
+ }