@beignet/web 0.0.3 → 0.0.4

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 CHANGED
@@ -1,5 +1,92 @@
1
1
  # @beignet/web
2
2
 
3
+ ## 0.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 8bcb31f: Mark package READMEs with Beignet's experimental alpha status and 0.0.x stability expectations.
8
+ - d137044: Declare `@beignet/core` as a peer dependency with a lockstep version range in
9
+ every integration and provider package instead of a regular `"*"` dependency.
10
+ Installs now always resolve a single shared copy of core, so `instanceof`
11
+ checks such as `isContractError` and upload error identity keep working, and
12
+ mixed Beignet versions fail loudly at install time instead of at runtime.
13
+
14
+ If your package manager does not install peer dependencies automatically, add
15
+ `@beignet/core` to your app alongside these packages. `@beignet/nuqs` now also
16
+ declares `@beignet/react-query` as a peer dependency, and
17
+ `@beignet/provider-storage-s3` now expects you to install
18
+ `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner` yourself, matching
19
+ how other providers treat their SDKs.
20
+
21
+ - ac78cdf: Make `createAuthHooks` the single route-scoped auth hook API. The factory is
22
+ now curried — `createAuthHooks<AppContext>()({ resolve })` — so added context
23
+ fields are inferred from `resolve` instead of passed as a type argument, and a
24
+ new optional `headers` schema validates the raw lowercase request header
25
+ record before `resolve` runs, giving `resolve` typed credential headers
26
+ without contract casts. On `required()` hooks a header schema failure returns
27
+ a framework-owned 401; `optional()` hooks skip auth resolution; `public()`
28
+ hooks never parse headers. `defineRouteHook` (and the `RouteHookBuilder`
29
+ types) are removed — write non-auth route hooks as plain `RouteHook` object
30
+ literals.
31
+ - 1a79090: Emit Node-compatible ESM: all relative imports in published packages now carry explicit .js extensions, fixing ERR_MODULE_NOT_FOUND when running the CLI or importing package dist files under plain Node.
32
+ - 82c48dc: Add canonical test port and context fixtures, and update generated tests to use them.
33
+ - 303ba07: Define the public HTTP adapter contract and expose the Web Fetch adapter implementation.
34
+ - 493d23b: Make the framework own context assembly with a server context blueprint and first-class service contexts.
35
+
36
+ - `createServer(...)` (and the Next/Web adapters) replace the `createContext` option with `context`. Gate-less contexts keep the plain request-factory shorthand; contexts that declare a `gate` must use the blueprint form `{ gate: (ports) => ports.gate, request, service }`. Returning `gate` from a context factory or hook addition is now a compile error.
37
+ - The gate port gains `gate.attach(ctx)`: it attaches a live, non-enumerable `ctx.gate` getter that re-binds against the receiving object on every access, so identity changes (including auth-hook elevation) can never authorize against a stale context, and spread copies drop the gate loudly instead of silently keeping stale identity. `bind(...)` remains the low-level primitive.
38
+ - Servers expose `createRequestContext(req)` and `createServiceContext(input?)`; the optional `context.service` factory powers schedule, outbox, command, and background contexts. Provider `setup`, `start`, and `stop` receive a late-bound `createServiceContext` so infra providers no longer hand-build background contexts.
39
+ - `createTestContextFactory(...)` now attaches the gate after all `extra` and override fields merge, fixing a stale-identity bug in tests.
40
+ - CLI templates, generators, and doctor diagnostics emit and check the new context blueprint and service-context wrappers.
41
+
42
+ - 89390fe: Decouple devtools from app code with server-owned instrumentation.
43
+
44
+ - `@beignet/core/tracing` is a new dependency-free subpath with the W3C trace
45
+ primitives (`TraceContext`, `createTraceContext`, `createChildTraceContext`,
46
+ `parseTraceparent`, `createTraceparent`, `createTraceId`, `createSpanId`).
47
+ App context types now use `Partial<TraceContext>` instead of importing
48
+ `DevtoolsTraceContext` from `@beignet/devtools`.
49
+ - `createServer(...)` owns request instrumentation through a new
50
+ `instrumentation` option (`ServerInstrumentationOptions | false`). The server
51
+ resolves request IDs and trace context before user hooks and context
52
+ creation, passes them to context factories as `requestId` and `trace`,
53
+ writes `x-request-id`/`traceparent` response headers by default, and records
54
+ request/error events into the instrumentation port resolved from final ports
55
+ (`ports.instrumentation`, then `ports.devtools`). The ambient request
56
+ context (`enterActiveRequestContext` and friends) moved into
57
+ `@beignet/core/server`.
58
+ - `createUseCase(...)` instruments runs by default, resolving the
59
+ instrumentation port from `ctx.ports` per run; opt out with
60
+ `instrumentation: false`. App `onRun` observers run in addition.
61
+ - `createInstrumentedAuditLog({ audit, instrumentation })` in
62
+ `@beignet/core/ports` replaces `createDevtoolsAuditLog`.
63
+ - `createServer(...)` gains `validateResponses` (default `true`) to skip
64
+ route-owned response validation, mirroring the client option.
65
+ - Removed from `@beignet/devtools`: `createDevtoolsHooks`,
66
+ `createDevtoolsUseCaseObserver`, `createDevtoolsAuditLog`, and the
67
+ trace/request-context modules. Apps now only need `createDevtoolsProvider()`
68
+ plus the devtools route; deleting both leaves the rest of the app compiling
69
+ and running unchanged.
70
+ - `@beignet/next` cron and upload helpers resolve their instrumentation sink
71
+ with `resolveProviderInstrumentationPort(ctx.ports)` instead of hardcoding
72
+ `ports.devtools`.
73
+ - `beignet create` templates generate the decoupled shape.
74
+
75
+ - e9c3209: Add reusable actor, tenant, audit assertion, and default-header request helpers for production app tests.
76
+ - 493d23b: Add typed provider-contributed ports. `InferProviderPorts` (replacing
77
+ `ProvidedPortsOfList`) merges the ports a provider list contributes so apps can
78
+ type `ctx.ports` as `AppPorts & InferProviderPorts<typeof providers>` without
79
+ casts. The curried `definePorts<AppPorts>()({ bound, deferred })` form replaces
80
+ throwing stub boilerplate: deferred keys boot as descriptive placeholders and
81
+ `createServer(...)` fails startup with the unbound key list (configurable via
82
+ `onUnboundPorts: "error" | "warn" | "ignore"`). App-local providers can declare
83
+ required ports, app context, and service-context input through the curried
84
+ `createProvider<Requires, Context, ServiceInput>()` form, which types
85
+ `setup({ ports, createServiceContext })` and lifecycle contexts end to end.
86
+ CLI templates and `beignet make` generate and maintain the new forms.
87
+ - 192c6ad: Routes can bind a contract directly to a use case with `{ contract, useCase }` — status is inferred from a sole 2xx response, input defaults to merged path/query/body parts, and validated inputs are not re-parsed when the use case reuses the contract schema by reference. Full `handle` routes remain the escape hatch for headers, streaming, and multi-status responses.
88
+ - 255527d: `createTestApp` defaults `onUnboundPorts` to "ignore" and surfaces unhandled error messages in test responses.
89
+
3
90
  ## 0.0.3
4
91
 
5
92
  ### Patch Changes
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @beignet/web
2
2
 
3
+ > [!CAUTION]
4
+ > Beignet is experimental alpha software. The `0.0.x` package line is for early
5
+ > evaluation, and APIs may change between releases while the framework settles.
6
+
3
7
  Web Fetch adapter for Beignet's framework runtime.
4
8
 
5
9
  Use this package when your runtime accepts a standard `Request` and returns a
@@ -9,7 +13,7 @@ or tests that should avoid a framework-specific adapter.
9
13
  ## Installation
10
14
 
11
15
  ```bash
12
- npm install @beignet/web
16
+ npm install @beignet/web @beignet/core
13
17
  ```
14
18
 
15
19
  ## Quick start
@@ -21,8 +25,8 @@ import { routes } from "./server/routes";
21
25
  export const server = await createFetchServer({
22
26
  ports,
23
27
  routes,
24
- createContext: ({ ports, req }) => ({
25
- requestId: req.headers.get("x-request-id") ?? crypto.randomUUID(),
28
+ context: ({ ports, requestId }) => ({
29
+ requestId,
26
30
  ports,
27
31
  }),
28
32
  mapUnhandledError: () => ({
@@ -56,6 +60,7 @@ import {
56
60
  createFetchHandler,
57
61
  toRequestLike,
58
62
  toWebResponse,
63
+ webFetchAdapter,
59
64
  } from "@beignet/web";
60
65
  ```
61
66
 
@@ -65,28 +70,53 @@ import {
65
70
  framework-neutral request shape.
66
71
  - `toWebResponse(response)` converts a Beignet response to a standard
67
72
  `Response`.
73
+ - `webFetchAdapter` is the concrete
74
+ `HttpAdapter<Request, Response>` implementation for Web Fetch runtimes.
68
75
 
69
76
  Native `Response` instances returned by handlers are passed through unchanged.
70
77
  Plain Beignet responses are serialized as JSON unless the body is `undefined`
71
78
  or `null`.
72
79
 
80
+ ## Adapter contract
81
+
82
+ `@beignet/core/server` owns framework behavior: route matching, hooks, request
83
+ validation, response validation, error mapping, response ownership, and provider
84
+ lifecycle. `@beignet/web` owns only the Web Fetch edge conversion.
85
+
86
+ The exported `webFetchAdapter` implements the formal core adapter contract:
87
+
88
+ ```typescript
89
+ import type { HttpAdapter } from "@beignet/core/server";
90
+
91
+ export const adapter: HttpAdapter<Request, Response> = webFetchAdapter;
92
+ ```
93
+
94
+ Use the same shape for a future runtime adapter with non-Fetch native request
95
+ or response types.
96
+
73
97
  ## Testing
74
98
 
75
99
  Use `@beignet/web/testing` to exercise routes through the same Web Fetch
76
- adapter without opening a network port.
100
+ adapter without opening a network port. Use `@beignet/core/testing` beside it
101
+ when the route needs an app-style context and test ports.
77
102
 
78
103
  ```typescript
104
+ import { createTestContextFactory, createTestPorts } from "@beignet/core/testing";
79
105
  import { createTestApp } from "@beignet/web/testing";
80
106
  import { getTodo } from "./features/todos/contracts";
81
107
  import { routes } from "./server/routes";
82
108
 
109
+ const fixture = createTestPorts<AppContext["ports"]>({
110
+ base: appPorts,
111
+ overrides: { todos: createInMemoryTodoRepository() },
112
+ });
113
+ const createContext = createTestContextFactory<AppContext, AppContext["ports"]>({
114
+ ports: fixture.ports,
115
+ });
83
116
  const app = await createTestApp({
84
- ports,
117
+ ports: fixture.ports,
85
118
  routes,
86
- createContext: ({ ports }) => ({
87
- requestId: "test-request",
88
- ports,
89
- }),
119
+ context: () => createContext(),
90
120
  });
91
121
 
92
122
  const todo = await app.request(getTodo, {
@@ -97,3 +127,43 @@ const todo = await app.request(getTodo, {
97
127
  `app.request(contract, args)` uses the same typed call arguments as
98
128
  `@beignet/core/client`, while `app.safeRequest(contract, args)` returns a typed
99
129
  success/error result instead of throwing.
130
+
131
+ `createTestApp(...)` applies two test-friendly defaults, and an explicit
132
+ option always wins:
133
+
134
+ - `onUnboundPorts` defaults to `"ignore"`, so apps with deferred provider
135
+ ports boot without installing every provider. Reading an unbound port still
136
+ throws on use. Production servers created with `createFetchServer(...)` keep
137
+ the strict `"error"` default.
138
+ - `mapUnhandledError` defaults to a mapper that returns
139
+ `{ status: 500, body: { code: "INTERNAL_SERVER_ERROR", message: err.message } }`,
140
+ so failing tests show the real error message instead of a generic response.
141
+
142
+ Apps that declare their context blueprint with `defineServerContext(...)` in
143
+ `server/context.ts` can pass the same value to both the runtime server and
144
+ `createTestApp(...)`:
145
+
146
+ ```typescript
147
+ import { appContext } from "../server/context";
148
+
149
+ const app = await createTestApp({ ports, routes, context: appContext });
150
+ ```
151
+
152
+ Use `createTestRequester(...)` when a test suite repeats the same auth, tenant,
153
+ or correlation headers:
154
+
155
+ ```typescript
156
+ import { createTestApp, createTestRequester } from "@beignet/web/testing";
157
+
158
+ const app = await createTestApp({ ports, routes, context: createContext });
159
+ const authedRequest = createTestRequester(app, {
160
+ headers: {
161
+ "x-user-id": "user_1",
162
+ "x-tenant-id": "tenant_1",
163
+ },
164
+ });
165
+
166
+ const todo = await authedRequest.request(getTodo, {
167
+ path: { id: "todo_1" },
168
+ });
169
+ ```
package/dist/index.d.ts CHANGED
@@ -1,20 +1,20 @@
1
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";
2
+ import type { InferProviderPorts, ServiceProvider } from "@beignet/core/providers";
3
+ import type { ContractLike, CreateServerOptions, Handler, HttpAdapter, HttpRequestLike, HttpResponse, ResolveContract, RouteDef, ServerInstance } from "@beignet/core/server";
4
4
  import type { StandardSchemaV1 } from "@standard-schema/spec";
5
5
  /**
6
6
  * Server types re-exported for Web Fetch apps.
7
7
  */
8
- export type { CreateServerOptions, HttpRequestLike, HttpResponse, RouteDef, RouteGroup, RouteHook, ServerInstance, } from "@beignet/core/server";
8
+ export type { AnyUseCaseLike, CreateServerOptions, HandlerRouteDef, HttpAdapter, HttpAdapterApiHandler, HttpAdapterHandler, HttpRequestLike, HttpResponse, RouteDef, RouteGroup, RouteHook, ServerInstance, UseCaseRouteDef, } from "@beignet/core/server";
9
9
  /**
10
10
  * Server helpers re-exported for Web Fetch apps.
11
11
  */
12
- export { contractsFromRoutes, createServer, defineRoute, defineRouteGroup, defineRouteHook, defineRoutes, } from "@beignet/core/server";
13
- type AnyProvider = ServiceProvider<unknown, StandardSchemaV1<any, any>, AnyPorts>;
12
+ export { contractsFromRoutes, createServer, defaultBinderInput, defineRoute, defineRouteGroup, defineRoutes, } from "@beignet/core/server";
13
+ type AnyProvider = ServiceProvider<unknown, StandardSchemaV1<any, any>, AnyPorts, any, any>;
14
14
  /**
15
15
  * Beignet server adapted to the Web Fetch API.
16
16
  */
17
- export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts> {
17
+ export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts, ServiceInput = void> {
18
18
  /**
19
19
  * Catch-all Web Fetch handler.
20
20
  */
@@ -29,6 +29,15 @@ export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts> {
29
29
  route: <CLike extends ContractLike>(contractLike: CLike) => {
30
30
  handle: (fn: Handler<Ctx, ResolveContract<CLike>>) => (req: Request) => Promise<Response>;
31
31
  };
32
+ /**
33
+ * Build a fully assembled request context from a framework-neutral request.
34
+ */
35
+ createRequestContext: ServerInstance<Ctx, Ports, ServiceInput>["createRequestContext"];
36
+ /**
37
+ * Build a fully assembled service context for schedules, outbox drains,
38
+ * commands, and background work.
39
+ */
40
+ createServiceContext: ServerInstance<Ctx, Ports, ServiceInput>["createServiceContext"];
32
41
  /**
33
42
  * Registered contract inputs.
34
43
  */
@@ -50,6 +59,11 @@ export declare function toRequestLike(req: Request): HttpRequestLike;
50
59
  * Convert a Beignet response into a native `Response`.
51
60
  */
52
61
  export declare function toWebResponse(resLike: HttpResponse): Response;
62
+ /**
63
+ * Concrete adapter implementation for Web-standard `Request` and `Response`
64
+ * runtimes.
65
+ */
66
+ export declare const webFetchAdapter: HttpAdapter<Request, Response>;
53
67
  /**
54
68
  * Create a Web Fetch handler from a Beignet server or API handler.
55
69
  */
@@ -62,5 +76,5 @@ export declare function createFetchHandler(serverOrHandler: Pick<ServerInstance<
62
76
  * `@beignet/next`, which builds on the same Web adapter and adds Next-specific
63
77
  * helpers.
64
78
  */
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>>>;
79
+ export declare function createFetchServer<Ctx, Ports extends AnyPorts = AnyPorts, ServiceInput = void, Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[], Providers extends readonly AnyProvider[] = readonly AnyProvider[]>(options: CreateServerOptions<Ctx, Ports, ServiceInput, Routes, Providers>): Promise<FetchServer<Ctx, Ports & InferProviderPorts<Providers>, ServiceInput>>;
66
80
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
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,kBAAkB,EAClB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,OAAO,EACP,WAAW,EACX,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,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAE9B;;GAEG;AACH,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,YAAY,GACb,MAAM,sBAAsB,CAAC;AAE9B,KAAK,WAAW,GAAG,eAAe,CAChC,OAAO,EAEP,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,EAC1B,QAAQ,EAER,GAAG,EAEH,GAAG,CACJ,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW,CAC1B,GAAG,EACH,KAAK,SAAS,QAAQ,GAAG,QAAQ,EACjC,YAAY,GAAG,IAAI;IAEnB;;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,oBAAoB,EAAE,cAAc,CAClC,GAAG,EACH,KAAK,EACL,YAAY,CACb,CAAC,sBAAsB,CAAC,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,EAAE,cAAc,CAClC,GAAG,EACH,KAAK,EACL,YAAY,CACb,CAAC,sBAAsB,CAAC,CAAC;IAC1B;;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;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,OAAO,EAAE,QAAQ,CAQ1D,CAAC;AAEF;;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,YAAY,GAAG,IAAI,EACnB,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,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,GACxE,OAAO,CACR,WAAW,CAAC,GAAG,EAAE,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC,CACtE,CAgBA"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createServer } from "@beignet/core/server";
2
2
  /**
3
3
  * Server helpers re-exported for Web Fetch apps.
4
4
  */
5
- export { contractsFromRoutes, createServer, defineRoute, defineRouteGroup, defineRouteHook, defineRoutes, } from "@beignet/core/server";
5
+ export { contractsFromRoutes, createServer, defaultBinderInput, defineRoute, defineRouteGroup, defineRoutes, } from "@beignet/core/server";
6
6
  /**
7
7
  * Convert a native `Request` into Beignet's framework-neutral request shape.
8
8
  */
@@ -35,6 +35,16 @@ export function toWebResponse(resLike) {
35
35
  // biome-ignore lint/suspicious/noExplicitAny: Body can be any JSON-serializable value.
36
36
  return Response.json(body, { status: resLike.status, headers });
37
37
  }
38
+ /**
39
+ * Concrete adapter implementation for Web-standard `Request` and `Response`
40
+ * runtimes.
41
+ */
42
+ export const webFetchAdapter = {
43
+ name: "web-fetch",
44
+ toRequestLike,
45
+ toNativeResponse: toWebResponse,
46
+ createHandler: (handler) => async (req) => webFetchAdapter.toNativeResponse(await handler(webFetchAdapter.toRequestLike(req))),
47
+ };
38
48
  /**
39
49
  * Create a Web Fetch handler from a Beignet server or API handler.
40
50
  */
@@ -42,7 +52,7 @@ export function createFetchHandler(serverOrHandler) {
42
52
  const handler = typeof serverOrHandler === "function"
43
53
  ? serverOrHandler
44
54
  : serverOrHandler.api;
45
- return async (req) => toWebResponse(await handler(toRequestLike(req)));
55
+ return webFetchAdapter.createHandler(handler);
46
56
  }
47
57
  /**
48
58
  * Create a Beignet server adapted to the Web Fetch API.
@@ -61,6 +71,8 @@ export async function createFetchServer(options) {
61
71
  route: (contract) => ({
62
72
  handle: (fn) => createFetchHandler(runtime.route(contract).handle(fn)),
63
73
  }),
74
+ createRequestContext: runtime.createRequestContext,
75
+ createServiceContext: runtime.createServiceContext,
64
76
  contracts: runtime.contracts,
65
77
  stop: () => runtime.stop(),
66
78
  ports: runtime.ports,
package/dist/index.js.map CHANGED
@@ -1 +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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/testing.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { type CallArgs, type ClientConfig, type EndpointResult, type InferEndpointContractError, type InferSuccessResponse } from "@beignet/core/client";
2
2
  import type { AnyPorts } from "@beignet/core/ports";
3
- import type { ProvidedPortsOfList, ServiceProvider } from "@beignet/core/providers";
3
+ import type { InferProviderPorts, ServiceProvider } from "@beignet/core/providers";
4
4
  import type { ContractLike, CreateServerOptions, ResolveContract, RouteDef } from "@beignet/core/server";
5
5
  import type { StandardSchemaV1 } from "@standard-schema/spec";
6
- import { type FetchServer } from "./index";
7
- type AnyProvider = ServiceProvider<unknown, StandardSchemaV1<any, any>, AnyPorts>;
6
+ import { type FetchServer } from "./index.js";
7
+ type AnyProvider = ServiceProvider<unknown, StandardSchemaV1<any, any>, AnyPorts, any, any>;
8
8
  /**
9
9
  * Client options accepted by `createTestApp(...)`.
10
10
  */
@@ -19,7 +19,7 @@ export type TestAppClientOptions<TProvidedHeaders extends string = never> = Omit
19
19
  /**
20
20
  * Options for creating a Web Fetch test app.
21
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> & {
22
+ export type CreateTestAppOptions<Ctx, Ports extends AnyPorts, Routes extends readonly RouteDef<Ctx>[], Providers extends readonly AnyProvider[], TProvidedHeaders extends string = never, ServiceInput = void> = CreateServerOptions<Ctx, Ports, ServiceInput, Routes, Providers> & {
23
23
  /**
24
24
  * Typed client behavior for `request(...)` and `safeRequest(...)`.
25
25
  */
@@ -59,6 +59,29 @@ export type TestApp<Ctx, Ports extends AnyPorts, TProvidedHeaders extends string
59
59
  */
60
60
  stop: () => Promise<void>;
61
61
  };
62
+ /**
63
+ * Default call arguments merged into every typed test request.
64
+ */
65
+ export type TestRequestDefaults<TDefaultHeaders extends string = string> = {
66
+ /**
67
+ * Headers to apply to each request unless a call overrides the same key.
68
+ */
69
+ headers?: Record<TDefaultHeaders, string>;
70
+ };
71
+ /**
72
+ * Request helpers with default call arguments already applied.
73
+ */
74
+ export type TestRequester<TProvidedHeaders extends string = never> = {
75
+ /**
76
+ * Call a contract through the test app using default request values.
77
+ */
78
+ request: <CLike extends ContractLike>(contract: CLike, ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>) => Promise<InferSuccessResponse<ResolveContract<CLike>>>;
79
+ /**
80
+ * Call a contract through the test app using default request values and
81
+ * return a typed success/error result instead of throwing.
82
+ */
83
+ safeRequest: <CLike extends ContractLike>(contract: CLike, ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>) => Promise<EndpointResult<ResolveContract<CLike>, InferEndpointContractError<ResolveContract<CLike>>>>;
84
+ };
62
85
  /**
63
86
  * Create a `fetch` implementation that dispatches requests to a Beignet Web
64
87
  * server without opening a network port.
@@ -67,6 +90,17 @@ export type TestApp<Ctx, Ports extends AnyPorts, TProvidedHeaders extends string
67
90
  * @returns Fetch-compatible function backed by `server.fetch`.
68
91
  */
69
92
  export declare function createTestFetch<Ctx, Ports extends AnyPorts>(server: Pick<FetchServer<Ctx, Ports>, "fetch">): typeof fetch;
93
+ /**
94
+ * Create typed request helpers that merge default headers into each call.
95
+ *
96
+ * Use this for route tests that repeatedly need the same auth, tenant, locale,
97
+ * or correlation headers while still letting individual calls override them.
98
+ *
99
+ * @param app - Test app returned by `createTestApp(...)`.
100
+ * @param defaults - Default call values to merge into every request.
101
+ * @returns Typed `request(...)` and `safeRequest(...)` helpers.
102
+ */
103
+ export declare function createTestRequester<Ctx, Ports extends AnyPorts, const TProvidedHeaders extends string = never, const TDefaultHeaders extends string = never>(app: Pick<TestApp<Ctx, Ports, TProvidedHeaders>, "request" | "safeRequest">, defaults: TestRequestDefaults<TDefaultHeaders>): TestRequester<TProvidedHeaders | TDefaultHeaders>;
70
104
  /**
71
105
  * Create a Beignet route/runtime test harness.
72
106
  *
@@ -74,9 +108,18 @@ export declare function createTestFetch<Ctx, Ports extends AnyPorts>(server: Pic
74
108
  * request parsing, route hooks, response validation, and error ownership as a
75
109
  * Web Fetch runtime without requiring Next.js or a network listener.
76
110
  *
111
+ * Test-friendly defaults differ from production servers in two ways, and an
112
+ * explicit option always wins:
113
+ *
114
+ * - `onUnboundPorts` defaults to `"ignore"` so apps with deferred provider
115
+ * ports boot without installing every provider. Reading an unbound port
116
+ * still throws on use.
117
+ * - `mapUnhandledError` defaults to a mapper that surfaces `err.message` in
118
+ * the 500 response body so failing tests show the real error.
119
+ *
77
120
  * @param options - Server options plus optional typed-client settings.
78
121
  * @returns Test app with typed `request(...)` helpers and a bound `fetch`.
79
122
  */
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>>;
123
+ export declare function createTestApp<Ctx, Ports extends AnyPorts = AnyPorts, ServiceInput = void, 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, ServiceInput>): Promise<TestApp<Ctx, Ports & InferProviderPorts<Providers>, TProvidedHeaders>>;
81
124
  export {};
82
125
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createClient, } from "@beignet/core/client";
2
- import { createFetchServer } from "./index";
2
+ import { createFetchServer } from "./index.js";
3
3
  /**
4
4
  * Create a `fetch` implementation that dispatches requests to a Beignet Web
5
5
  * server without opening a network port.
@@ -13,6 +13,37 @@ export function createTestFetch(server) {
13
13
  return server.fetch(req);
14
14
  });
15
15
  }
16
+ /**
17
+ * Create typed request helpers that merge default headers into each call.
18
+ *
19
+ * Use this for route tests that repeatedly need the same auth, tenant, locale,
20
+ * or correlation headers while still letting individual calls override them.
21
+ *
22
+ * @param app - Test app returned by `createTestApp(...)`.
23
+ * @param defaults - Default call values to merge into every request.
24
+ * @returns Typed `request(...)` and `safeRequest(...)` helpers.
25
+ */
26
+ export function createTestRequester(app, defaults) {
27
+ return {
28
+ request: (contract, ...args) => {
29
+ const mergedArgs = mergeTestRequestArgs(args, defaults);
30
+ return app.request(contract, ...mergedArgs);
31
+ },
32
+ safeRequest: (contract, ...args) => {
33
+ const mergedArgs = mergeTestRequestArgs(args, defaults);
34
+ return app.safeRequest(contract, ...mergedArgs);
35
+ },
36
+ };
37
+ }
38
+ function defaultTestUnhandledErrorMapper({ err, }) {
39
+ return {
40
+ status: 500,
41
+ body: {
42
+ code: "INTERNAL_SERVER_ERROR",
43
+ message: err instanceof Error ? err.message : "Unknown error",
44
+ },
45
+ };
46
+ }
16
47
  /**
17
48
  * Create a Beignet route/runtime test harness.
18
49
  *
@@ -20,12 +51,25 @@ export function createTestFetch(server) {
20
51
  * request parsing, route hooks, response validation, and error ownership as a
21
52
  * Web Fetch runtime without requiring Next.js or a network listener.
22
53
  *
54
+ * Test-friendly defaults differ from production servers in two ways, and an
55
+ * explicit option always wins:
56
+ *
57
+ * - `onUnboundPorts` defaults to `"ignore"` so apps with deferred provider
58
+ * ports boot without installing every provider. Reading an unbound port
59
+ * still throws on use.
60
+ * - `mapUnhandledError` defaults to a mapper that surfaces `err.message` in
61
+ * the 500 response body so failing tests show the real error.
62
+ *
23
63
  * @param options - Server options plus optional typed-client settings.
24
64
  * @returns Test app with typed `request(...)` helpers and a bound `fetch`.
25
65
  */
26
66
  export async function createTestApp(options) {
27
67
  const { client: clientOptions, ...serverOptions } = options;
28
- const server = await createFetchServer(serverOptions);
68
+ const server = await createFetchServer({
69
+ ...serverOptions,
70
+ onUnboundPorts: serverOptions.onUnboundPorts ?? "ignore",
71
+ mapUnhandledError: serverOptions.mapUnhandledError ?? defaultTestUnhandledErrorMapper,
72
+ });
29
73
  const fetch = createTestFetch(server);
30
74
  const client = createClient({
31
75
  ...clientOptions,
@@ -42,4 +86,20 @@ export async function createTestApp(options) {
42
86
  stop: () => server.stop(),
43
87
  };
44
88
  }
89
+ function mergeTestRequestArgs(args, defaults) {
90
+ const [arg] = args;
91
+ const defaultHeaders = defaults.headers ?? {};
92
+ if (!arg) {
93
+ return [{ headers: defaultHeaders }];
94
+ }
95
+ return [
96
+ {
97
+ ...arg,
98
+ headers: {
99
+ ...defaultHeaders,
100
+ ...arg.headers,
101
+ },
102
+ },
103
+ ];
104
+ }
45
105
  //# sourceMappingURL=testing.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beignet/web",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "description": "Web Fetch adapter for Beignet",
6
6
  "main": "./dist/index.js",
@@ -55,8 +55,10 @@
55
55
  "engines": {
56
56
  "node": ">=18.0.0"
57
57
  },
58
+ "peerDependencies": {
59
+ "@beignet/core": ">=0.0.3 <1.0.0"
60
+ },
58
61
  "dependencies": {
59
- "@beignet/core": "*",
60
62
  "@standard-schema/spec": "^1.0.0"
61
63
  },
62
64
  "devDependencies": {
package/src/index.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import type { AnyPorts } from "@beignet/core/ports";
2
2
  import type {
3
- ProvidedPortsOfList,
3
+ InferProviderPorts,
4
4
  ServiceProvider,
5
5
  } from "@beignet/core/providers";
6
6
  import type {
7
7
  ContractLike,
8
8
  CreateServerOptions,
9
9
  Handler,
10
+ HttpAdapter,
10
11
  HttpRequestLike,
11
12
  HttpResponse,
12
13
  ResolveContract,
@@ -20,13 +21,19 @@ import type { StandardSchemaV1 } from "@standard-schema/spec";
20
21
  * Server types re-exported for Web Fetch apps.
21
22
  */
22
23
  export type {
24
+ AnyUseCaseLike,
23
25
  CreateServerOptions,
26
+ HandlerRouteDef,
27
+ HttpAdapter,
28
+ HttpAdapterApiHandler,
29
+ HttpAdapterHandler,
24
30
  HttpRequestLike,
25
31
  HttpResponse,
26
32
  RouteDef,
27
33
  RouteGroup,
28
34
  RouteHook,
29
35
  ServerInstance,
36
+ UseCaseRouteDef,
30
37
  } from "@beignet/core/server";
31
38
 
32
39
  /**
@@ -35,9 +42,9 @@ export type {
35
42
  export {
36
43
  contractsFromRoutes,
37
44
  createServer,
45
+ defaultBinderInput,
38
46
  defineRoute,
39
47
  defineRouteGroup,
40
- defineRouteHook,
41
48
  defineRoutes,
42
49
  } from "@beignet/core/server";
43
50
 
@@ -45,13 +52,21 @@ type AnyProvider = ServiceProvider<
45
52
  unknown,
46
53
  // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
47
54
  StandardSchemaV1<any, any>,
48
- AnyPorts
55
+ AnyPorts,
56
+ // biome-ignore lint/suspicious/noExplicitAny: provider context types are erased at this level
57
+ any,
58
+ // biome-ignore lint/suspicious/noExplicitAny: provider service-input types are erased at this level
59
+ any
49
60
  >;
50
61
 
51
62
  /**
52
63
  * Beignet server adapted to the Web Fetch API.
53
64
  */
54
- export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts> {
65
+ export interface FetchServer<
66
+ Ctx,
67
+ Ports extends AnyPorts = AnyPorts,
68
+ ServiceInput = void,
69
+ > {
55
70
  /**
56
71
  * Catch-all Web Fetch handler.
57
72
  */
@@ -70,6 +85,23 @@ export interface FetchServer<Ctx, Ports extends AnyPorts = AnyPorts> {
70
85
  fn: Handler<Ctx, ResolveContract<CLike>>,
71
86
  ) => (req: Request) => Promise<Response>;
72
87
  };
88
+ /**
89
+ * Build a fully assembled request context from a framework-neutral request.
90
+ */
91
+ createRequestContext: ServerInstance<
92
+ Ctx,
93
+ Ports,
94
+ ServiceInput
95
+ >["createRequestContext"];
96
+ /**
97
+ * Build a fully assembled service context for schedules, outbox drains,
98
+ * commands, and background work.
99
+ */
100
+ createServiceContext: ServerInstance<
101
+ Ctx,
102
+ Ports,
103
+ ServiceInput
104
+ >["createServiceContext"];
73
105
  /**
74
106
  * Registered contract inputs.
75
107
  */
@@ -121,6 +153,20 @@ export function toWebResponse(resLike: HttpResponse): Response {
121
153
  return Response.json(body as any, { status: resLike.status, headers });
122
154
  }
123
155
 
156
+ /**
157
+ * Concrete adapter implementation for Web-standard `Request` and `Response`
158
+ * runtimes.
159
+ */
160
+ export const webFetchAdapter: HttpAdapter<Request, Response> = {
161
+ name: "web-fetch",
162
+ toRequestLike,
163
+ toNativeResponse: toWebResponse,
164
+ createHandler: (handler) => async (req) =>
165
+ webFetchAdapter.toNativeResponse(
166
+ await handler(webFetchAdapter.toRequestLike(req)),
167
+ ),
168
+ };
169
+
124
170
  /**
125
171
  * Create a Web Fetch handler from a Beignet server or API handler.
126
172
  */
@@ -134,7 +180,7 @@ export function createFetchHandler(
134
180
  ? serverOrHandler
135
181
  : serverOrHandler.api;
136
182
 
137
- return async (req) => toWebResponse(await handler(toRequestLike(req)));
183
+ return webFetchAdapter.createHandler(handler);
138
184
  }
139
185
 
140
186
  /**
@@ -148,11 +194,14 @@ export function createFetchHandler(
148
194
  export async function createFetchServer<
149
195
  Ctx,
150
196
  Ports extends AnyPorts = AnyPorts,
197
+ ServiceInput = void,
151
198
  Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[],
152
199
  Providers extends readonly AnyProvider[] = readonly AnyProvider[],
153
200
  >(
154
- options: CreateServerOptions<Ctx, Ports, Routes, Providers>,
155
- ): Promise<FetchServer<Ctx, Ports & ProvidedPortsOfList<Providers>>> {
201
+ options: CreateServerOptions<Ctx, Ports, ServiceInput, Routes, Providers>,
202
+ ): Promise<
203
+ FetchServer<Ctx, Ports & InferProviderPorts<Providers>, ServiceInput>
204
+ > {
156
205
  const runtime = await createServer(options);
157
206
  const fetch = createFetchHandler(runtime);
158
207
 
@@ -162,6 +211,8 @@ export async function createFetchServer<
162
211
  route: (contract) => ({
163
212
  handle: (fn) => createFetchHandler(runtime.route(contract).handle(fn)),
164
213
  }),
214
+ createRequestContext: runtime.createRequestContext,
215
+ createServiceContext: runtime.createServiceContext,
165
216
  contracts: runtime.contracts,
166
217
  stop: () => runtime.stop(),
167
218
  ports: runtime.ports,
package/src/testing.ts CHANGED
@@ -8,23 +8,28 @@ import {
8
8
  } from "@beignet/core/client";
9
9
  import type { AnyPorts } from "@beignet/core/ports";
10
10
  import type {
11
- ProvidedPortsOfList,
11
+ InferProviderPorts,
12
12
  ServiceProvider,
13
13
  } from "@beignet/core/providers";
14
14
  import type {
15
15
  ContractLike,
16
16
  CreateServerOptions,
17
+ HttpResponse,
17
18
  ResolveContract,
18
19
  RouteDef,
19
20
  } from "@beignet/core/server";
20
21
  import type { StandardSchemaV1 } from "@standard-schema/spec";
21
- import { createFetchServer, type FetchServer } from "./index";
22
+ import { createFetchServer, type FetchServer } from "./index.js";
22
23
 
23
24
  type AnyProvider = ServiceProvider<
24
25
  unknown,
25
26
  // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level.
26
27
  StandardSchemaV1<any, any>,
27
- AnyPorts
28
+ AnyPorts,
29
+ // biome-ignore lint/suspicious/noExplicitAny: provider context types are erased at this level.
30
+ any,
31
+ // biome-ignore lint/suspicious/noExplicitAny: provider service-input types are erased at this level.
32
+ any
28
33
  >;
29
34
 
30
35
  /**
@@ -49,7 +54,8 @@ export type CreateTestAppOptions<
49
54
  Routes extends readonly RouteDef<Ctx>[],
50
55
  Providers extends readonly AnyProvider[],
51
56
  TProvidedHeaders extends string = never,
52
- > = CreateServerOptions<Ctx, Ports, Routes, Providers> & {
57
+ ServiceInput = void,
58
+ > = CreateServerOptions<Ctx, Ports, ServiceInput, Routes, Providers> & {
53
59
  /**
54
60
  * Typed client behavior for `request(...)` and `safeRequest(...)`.
55
61
  */
@@ -106,6 +112,42 @@ export type TestApp<
106
112
  stop: () => Promise<void>;
107
113
  };
108
114
 
115
+ /**
116
+ * Default call arguments merged into every typed test request.
117
+ */
118
+ export type TestRequestDefaults<TDefaultHeaders extends string = string> = {
119
+ /**
120
+ * Headers to apply to each request unless a call overrides the same key.
121
+ */
122
+ headers?: Record<TDefaultHeaders, string>;
123
+ };
124
+
125
+ /**
126
+ * Request helpers with default call arguments already applied.
127
+ */
128
+ export type TestRequester<TProvidedHeaders extends string = never> = {
129
+ /**
130
+ * Call a contract through the test app using default request values.
131
+ */
132
+ request: <CLike extends ContractLike>(
133
+ contract: CLike,
134
+ ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>
135
+ ) => Promise<InferSuccessResponse<ResolveContract<CLike>>>;
136
+ /**
137
+ * Call a contract through the test app using default request values and
138
+ * return a typed success/error result instead of throwing.
139
+ */
140
+ safeRequest: <CLike extends ContractLike>(
141
+ contract: CLike,
142
+ ...args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>
143
+ ) => Promise<
144
+ EndpointResult<
145
+ ResolveContract<CLike>,
146
+ InferEndpointContractError<ResolveContract<CLike>>
147
+ >
148
+ >;
149
+ };
150
+
109
151
  /**
110
152
  * Create a `fetch` implementation that dispatches requests to a Beignet Web
111
153
  * server without opening a network port.
@@ -123,6 +165,59 @@ export function createTestFetch<Ctx, Ports extends AnyPorts>(
123
165
  }) as typeof fetch;
124
166
  }
125
167
 
168
+ /**
169
+ * Create typed request helpers that merge default headers into each call.
170
+ *
171
+ * Use this for route tests that repeatedly need the same auth, tenant, locale,
172
+ * or correlation headers while still letting individual calls override them.
173
+ *
174
+ * @param app - Test app returned by `createTestApp(...)`.
175
+ * @param defaults - Default call values to merge into every request.
176
+ * @returns Typed `request(...)` and `safeRequest(...)` helpers.
177
+ */
178
+ export function createTestRequester<
179
+ Ctx,
180
+ Ports extends AnyPorts,
181
+ const TProvidedHeaders extends string = never,
182
+ const TDefaultHeaders extends string = never,
183
+ >(
184
+ app: Pick<TestApp<Ctx, Ports, TProvidedHeaders>, "request" | "safeRequest">,
185
+ defaults: TestRequestDefaults<TDefaultHeaders>,
186
+ ): TestRequester<TProvidedHeaders | TDefaultHeaders> {
187
+ return {
188
+ request: (contract, ...args) => {
189
+ const mergedArgs = mergeTestRequestArgs(args, defaults) as CallArgs<
190
+ ResolveContract<typeof contract>,
191
+ TProvidedHeaders
192
+ >;
193
+
194
+ return app.request(contract, ...mergedArgs);
195
+ },
196
+ safeRequest: (contract, ...args) => {
197
+ const mergedArgs = mergeTestRequestArgs(args, defaults) as CallArgs<
198
+ ResolveContract<typeof contract>,
199
+ TProvidedHeaders
200
+ >;
201
+
202
+ return app.safeRequest(contract, ...mergedArgs);
203
+ },
204
+ };
205
+ }
206
+
207
+ function defaultTestUnhandledErrorMapper({
208
+ err,
209
+ }: {
210
+ err: unknown;
211
+ }): HttpResponse {
212
+ return {
213
+ status: 500,
214
+ body: {
215
+ code: "INTERNAL_SERVER_ERROR",
216
+ message: err instanceof Error ? err.message : "Unknown error",
217
+ },
218
+ };
219
+ }
220
+
126
221
  /**
127
222
  * Create a Beignet route/runtime test harness.
128
223
  *
@@ -130,12 +225,22 @@ export function createTestFetch<Ctx, Ports extends AnyPorts>(
130
225
  * request parsing, route hooks, response validation, and error ownership as a
131
226
  * Web Fetch runtime without requiring Next.js or a network listener.
132
227
  *
228
+ * Test-friendly defaults differ from production servers in two ways, and an
229
+ * explicit option always wins:
230
+ *
231
+ * - `onUnboundPorts` defaults to `"ignore"` so apps with deferred provider
232
+ * ports boot without installing every provider. Reading an unbound port
233
+ * still throws on use.
234
+ * - `mapUnhandledError` defaults to a mapper that surfaces `err.message` in
235
+ * the 500 response body so failing tests show the real error.
236
+ *
133
237
  * @param options - Server options plus optional typed-client settings.
134
238
  * @returns Test app with typed `request(...)` helpers and a bound `fetch`.
135
239
  */
136
240
  export async function createTestApp<
137
241
  Ctx,
138
242
  Ports extends AnyPorts = AnyPorts,
243
+ ServiceInput = void,
139
244
  Routes extends readonly RouteDef<Ctx>[] = readonly RouteDef<Ctx>[],
140
245
  Providers extends readonly AnyProvider[] = readonly AnyProvider[],
141
246
  const TProvidedHeaders extends string = never,
@@ -145,13 +250,19 @@ export async function createTestApp<
145
250
  Ports,
146
251
  Routes,
147
252
  Providers,
148
- TProvidedHeaders
253
+ TProvidedHeaders,
254
+ ServiceInput
149
255
  >,
150
256
  ): Promise<
151
- TestApp<Ctx, Ports & ProvidedPortsOfList<Providers>, TProvidedHeaders>
257
+ TestApp<Ctx, Ports & InferProviderPorts<Providers>, TProvidedHeaders>
152
258
  > {
153
259
  const { client: clientOptions, ...serverOptions } = options;
154
- const server = await createFetchServer(serverOptions);
260
+ const server = await createFetchServer({
261
+ ...serverOptions,
262
+ onUnboundPorts: serverOptions.onUnboundPorts ?? "ignore",
263
+ mapUnhandledError:
264
+ serverOptions.mapUnhandledError ?? defaultTestUnhandledErrorMapper,
265
+ });
155
266
  const fetch = createTestFetch(server);
156
267
  const client = createClient<TProvidedHeaders>({
157
268
  ...clientOptions,
@@ -170,3 +281,31 @@ export async function createTestApp<
170
281
  stop: () => server.stop(),
171
282
  };
172
283
  }
284
+
285
+ function mergeTestRequestArgs<
286
+ CLike extends ContractLike,
287
+ TProvidedHeaders extends string,
288
+ >(
289
+ args: CallArgs<ResolveContract<CLike>, TProvidedHeaders>,
290
+ defaults: TestRequestDefaults<string>,
291
+ ): CallArgs<ResolveContract<CLike>, TProvidedHeaders> {
292
+ const [arg] = args;
293
+ const defaultHeaders = defaults.headers ?? {};
294
+
295
+ if (!arg) {
296
+ return [{ headers: defaultHeaders }] as CallArgs<
297
+ ResolveContract<CLike>,
298
+ TProvidedHeaders
299
+ >;
300
+ }
301
+
302
+ return [
303
+ {
304
+ ...arg,
305
+ headers: {
306
+ ...defaultHeaders,
307
+ ...arg.headers,
308
+ },
309
+ },
310
+ ] as CallArgs<ResolveContract<CLike>, TProvidedHeaders>;
311
+ }