@tanstack-router-testing/react-start-testing 0.0.1

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/dist/index.mjs ADDED
@@ -0,0 +1,337 @@
1
+ import { t as __setStartOptionsForTesting } from "./shim-DR16QaLL.mjs";
2
+ import { callMiddleware, clearAllMiddlewareMocks, clearAllServerFnMocks, runInEnv, setMiddlewareMock, setServerFnMock } from "@tanstack-router-testing/router-testing-core";
3
+ import { createElement } from "react";
4
+ import { renderToReadableStream, renderToString } from "react-dom/server";
5
+ import { runWithStartContext } from "@tanstack/start-storage-context";
6
+ //#region src/clearStartMocks.ts
7
+ /**
8
+ * Clear every server-function and middleware mock installed by
9
+ * {@link mockServerFn} or {@link mockMiddleware}.
10
+ *
11
+ * @returns `void`.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { afterEach } from 'vitest';
16
+ * import { clearStartMocks } from '@tanstack-router-testing/react-start-testing';
17
+ *
18
+ * afterEach(() => {
19
+ * clearStartMocks();
20
+ * });
21
+ * ```
22
+ *
23
+ * @remarks
24
+ * This is the same function wired to {@link StartTestRuntime.cleanup}, so you
25
+ * only need to call it explicitly when installing mocks outside of a runtime
26
+ * (i.e. via {@link mockServerFn} / {@link mockMiddleware} directly).
27
+ */
28
+ const clearStartMocks = () => {
29
+ clearAllServerFnMocks();
30
+ clearAllMiddlewareMocks();
31
+ };
32
+ //#endregion
33
+ //#region src/mockServerFn.ts
34
+ const hasKey = (value, key) => typeof value === "object" && value !== null && key in value;
35
+ const toCallableOptions = (ctx) => {
36
+ if (!hasKey(ctx, "data")) return ctx;
37
+ const source = ctx;
38
+ return {
39
+ data: source.data,
40
+ ...source.headers !== void 0 ? { headers: source.headers } : {},
41
+ ...source.signal !== void 0 ? { signal: source.signal } : {},
42
+ ...source.fetch !== void 0 ? { fetch: source.fetch } : {}
43
+ };
44
+ };
45
+ /**
46
+ * Install a mock implementation for a TanStack Start server function.
47
+ *
48
+ * @param fn - The server function created by `createServerFn().handler(...)`.
49
+ * @param impl - A {@link ServerFnMock} that replaces the real handler for the
50
+ * duration of the test. It receives the same callable-shape arguments (e.g.
51
+ * `{ data }`) as the original function.
52
+ * @returns A disposer function that restores the original implementation when
53
+ * called. Alternatively, call {@link clearStartMocks} to remove all mocks at
54
+ * once.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { createServerFn } from '@tanstack/react-start';
59
+ * import { mockServerFn } from '@tanstack-router-testing/react-start-testing';
60
+ *
61
+ * const listOrders = createServerFn()
62
+ * .validator((input: { userId: string }) => input)
63
+ * .handler(async ({ data }) => db.orders.findMany(data.userId));
64
+ *
65
+ * const dispose = mockServerFn(listOrders, async ({ data }) => [
66
+ * { id: '1', userId: data.userId, total: 42 },
67
+ * ]);
68
+ *
69
+ * // ... run your test ...
70
+ * dispose();
71
+ * ```
72
+ *
73
+ * @remarks
74
+ * The mock is authored against the public callable shape, so a server function
75
+ * normally called as `listOrders({ data })` is mocked with that same signature.
76
+ * Internal context fields are normalised automatically before reaching the mock.
77
+ */
78
+ const mockServerFn = (fn, impl) => {
79
+ const handler = (ctx) => impl(toCallableOptions(ctx));
80
+ return setServerFnMock(fn, handler);
81
+ };
82
+ //#endregion
83
+ //#region src/mockMiddleware.ts
84
+ /**
85
+ * Override one or both phases of a registered middleware.
86
+ *
87
+ * @param mw - The middleware object returned by
88
+ * `createMiddleware().server(...).client(...)`.
89
+ * @param options - A {@link MockMiddlewareOptions} object specifying which
90
+ * phases to replace.
91
+ * @returns A disposer function that restores the original middleware handlers.
92
+ * Alternatively, call {@link clearStartMocks} to remove all mocks at once.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * import { createMiddleware } from '@tanstack/react-start';
97
+ * import { mockMiddleware } from '@tanstack-router-testing/react-start-testing';
98
+ *
99
+ * const authMiddleware = createMiddleware()
100
+ * .server(async ({ next }) => next({ context: { user: await getUser() } }))
101
+ * .client(async ({ next }) => next());
102
+ *
103
+ * const dispose = mockMiddleware(authMiddleware, {
104
+ * server: async ({ next }) => next({ context: { user: { id: 'test-user' } } }),
105
+ * });
106
+ *
107
+ * // ... run your test ...
108
+ * dispose();
109
+ * ```
110
+ *
111
+ * @throws If `mw` is not registered. Registration is the shim's
112
+ * responsibility (see ADR 0001).
113
+ *
114
+ * @remarks
115
+ * Server functions are invoked directly and mocked middleware participates in
116
+ * that normal call path. Only the phases you specify in `options` are replaced;
117
+ * the rest continue to run their real implementations.
118
+ */
119
+ const mockMiddleware = (mw, options) => setMiddlewareMock(mw, options);
120
+ //#endregion
121
+ //#region src/isomorphic.ts
122
+ /**
123
+ * Execute `fn` with the simulated TanStack Start environment forced to `env`,
124
+ * then restore the prior value.
125
+ *
126
+ * @typeParam T - The return type of `fn`.
127
+ * @param env - The environment to simulate: `'server'` or `'client'`.
128
+ * @param fn - A synchronous or asynchronous function to run inside the
129
+ * simulated environment.
130
+ * @returns A `Promise` resolving to the return value of `fn`.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * import { runInStartEnv } from '@tanstack-router-testing/react-start-testing';
135
+ *
136
+ * const result = await runInStartEnv('server', () => {
137
+ * // Code here sees `import.meta.env.SSR === true`
138
+ * return fetchDataOnServer();
139
+ * });
140
+ * ```
141
+ *
142
+ * @remarks
143
+ * The environment is scoped to the execution of `fn` and is restored
144
+ * automatically even if `fn` throws. Used internally by
145
+ * {@link createStartTestRuntime} to set the environment for each
146
+ * {@link StartTestRuntime.run | run} invocation.
147
+ */
148
+ const runInStartEnv = async (env, fn) => runInEnv(env, fn);
149
+ //#endregion
150
+ //#region src/runtime.ts
151
+ /**
152
+ * Create a {@link StartTestRuntime} for executing server functions in a
153
+ * simulated TanStack Start environment.
154
+ *
155
+ * @param options - Runtime configuration. See {@link StartTestRuntimeOptions}.
156
+ * @returns A `Promise` resolving to a {@link StartTestRuntime} instance.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * import { createServerFn } from '@tanstack/react-start';
161
+ * import {
162
+ * createStartTestRuntime,
163
+ * mockServerFn,
164
+ * } from '@tanstack-router-testing/react-start-testing';
165
+ *
166
+ * const runtime = await createStartTestRuntime({
167
+ * request: 'http://localhost:3000/api/orders',
168
+ * env: 'server',
169
+ * });
170
+ *
171
+ * const listOrders = createServerFn()
172
+ * .validator((input: { userId: string }) => input)
173
+ * .handler(async ({ data }) => [{ id: '1', userId: data.userId }]);
174
+ *
175
+ * mockServerFn(listOrders, async ({ data }) => [
176
+ * { id: 'mock-1', userId: data.userId },
177
+ * ]);
178
+ *
179
+ * const orders = await runtime.call(listOrders, [{ data: { userId: 'u1' } }]);
180
+ * // orders === [{ id: 'mock-1', userId: 'u1' }]
181
+ *
182
+ * runtime.cleanup();
183
+ * ```
184
+ *
185
+ * @remarks
186
+ * The runtime resolves Start options from either `startOptions` or
187
+ * `startInstance.getOptions()`, sets up the Start storage context, and
188
+ * executes global request middlewares before each invocation.
189
+ */
190
+ const createStartTestRuntime = async (options = {}) => {
191
+ const startOptions = options.startOptions ?? await options.startInstance?.getOptions() ?? {};
192
+ const request = toRequest(options.request);
193
+ __setStartOptionsForTesting(startOptions);
194
+ const createContext = (runOptions = {}) => ({
195
+ getRouter: () => {
196
+ if (!options.router) throw new Error("[tanstack-router-testing] createStartTestRuntime: no router was provided for this test runtime.");
197
+ return options.router;
198
+ },
199
+ request: toRequest(runOptions.request ?? request),
200
+ startOptions,
201
+ contextAfterGlobalMiddlewares: runOptions.context ?? options.context ?? {},
202
+ executedRequestMiddlewares: /* @__PURE__ */ new Set(),
203
+ handlerType: runOptions.handlerType ?? options.handlerType ?? "serverFn"
204
+ });
205
+ const runtime = {
206
+ request,
207
+ startOptions,
208
+ run: async (fn, runOptions) => {
209
+ const env = runOptions?.env ?? options.env ?? "server";
210
+ const storage = createContext(runOptions);
211
+ return runWithStartContext(storage, async () => {
212
+ storage.contextAfterGlobalMiddlewares = await executeGlobalRequestMiddlewares(storage, storage.contextAfterGlobalMiddlewares);
213
+ return runInStartEnv(env, fn);
214
+ });
215
+ },
216
+ call: (fn, args, runOptions) => runtime.run(() => fn(...args), runOptions),
217
+ cleanup: clearStartMocks
218
+ };
219
+ return runtime;
220
+ };
221
+ const toRequest = (request) => {
222
+ if (request instanceof Request) return request;
223
+ const url = request?.toString() ?? "http://tanstack-router-testing.test/";
224
+ return new Request(url);
225
+ };
226
+ const executeGlobalRequestMiddlewares = async (storage, initialContext) => {
227
+ const middlewares = [...storage.startOptions?.requestMiddleware ?? []];
228
+ const { pathname } = new URL(storage.request.url);
229
+ const next = async (parentContext, ctx = {}) => {
230
+ const middleware = middlewares.shift();
231
+ const context = mergeContext(parentContext, ctx.context);
232
+ if (!middleware) return {
233
+ context,
234
+ response: new Response(null)
235
+ };
236
+ storage.executedRequestMiddlewares.add(middleware);
237
+ const server = middleware.options?.server;
238
+ if (typeof server !== "function") return next(context);
239
+ const result = await server({
240
+ request: storage.request,
241
+ pathname,
242
+ context,
243
+ next: (nextCtx) => next(context, nextCtx)
244
+ });
245
+ if (result instanceof Response) return {
246
+ context,
247
+ response: result
248
+ };
249
+ return {
250
+ context: mergeContext(context, result?.context),
251
+ response: result?.response ?? new Response(null)
252
+ };
253
+ };
254
+ return (await next(initialContext)).context;
255
+ };
256
+ const mergeContext = (left, right) => {
257
+ if (isRecord(left) && isRecord(right)) return {
258
+ ...left,
259
+ ...right
260
+ };
261
+ return right ?? left ?? {};
262
+ };
263
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
264
+ //#endregion
265
+ //#region src/rsc.tsx
266
+ /**
267
+ * Create an {@link RscTestRuntime} for rendering React Server Components in a
268
+ * simulated TanStack Start environment.
269
+ *
270
+ * @param options - Runtime configuration. See {@link RscTestRuntimeOptions}.
271
+ * @returns A `Promise` resolving to an {@link RscTestRuntime} instance.
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * import { createRscTestRuntime } from '@tanstack-router-testing/react-start-testing';
276
+ *
277
+ * const runtime = await createRscTestRuntime({ streaming: true });
278
+ *
279
+ * function Greeting({ name }: { name: string }) {
280
+ * return <h1>Hello, {name}!</h1>;
281
+ * }
282
+ *
283
+ * const { html, chunks } = await runtime.renderServerComponent(Greeting, {
284
+ * name: 'TanStack',
285
+ * });
286
+ * // html === '<h1>Hello, TanStack!</h1>'
287
+ *
288
+ * runtime.cleanup();
289
+ * ```
290
+ *
291
+ * @remarks
292
+ * Delegates to {@link createStartTestRuntime} for the base runtime, then layers
293
+ * on `renderServerComponent` which uses `react-dom/server` under the hood. Set
294
+ * `streaming: true` in options to render via `renderToReadableStream` instead of
295
+ * the default `renderToString`.
296
+ */
297
+ const createRscTestRuntime = async (options = {}) => {
298
+ const base = await createStartTestRuntime(options);
299
+ const streaming = options.streaming ?? false;
300
+ return {
301
+ ...base,
302
+ renderServerComponent: async (component, props) => {
303
+ const element = createElement(component, props);
304
+ if (!streaming) return {
305
+ html: renderToString(element),
306
+ stream: null,
307
+ chunks: []
308
+ };
309
+ const stream = await renderToReadableStream(element);
310
+ const chunks = [];
311
+ const decoder = new TextDecoder();
312
+ const [collectStream, returnStream] = stream.tee();
313
+ const reader = collectStream.getReader();
314
+ const collectChunks = async () => {
315
+ const result = await reader.read();
316
+ if (result.value !== void 0) chunks.push(decoder.decode(result.value, { stream: !result.done }));
317
+ if (!result.done) await collectChunks();
318
+ };
319
+ await collectChunks();
320
+ return {
321
+ html: chunks.join(""),
322
+ stream: returnStream,
323
+ chunks
324
+ };
325
+ }
326
+ };
327
+ };
328
+ //#endregion
329
+ //#region src/index.ts
330
+ /**
331
+ * Current package version. Bumped at release time.
332
+ */
333
+ const VERSION = "0.0.0";
334
+ //#endregion
335
+ export { VERSION, callMiddleware, clearStartMocks, createRscTestRuntime, createStartTestRuntime, mockMiddleware, mockServerFn, runInStartEnv };
336
+
337
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/clearStartMocks.ts","../src/mockServerFn.ts","../src/mockMiddleware.ts","../src/isomorphic.ts","../src/runtime.ts","../src/rsc.tsx","../src/index.ts"],"sourcesContent":["import { clearAllMiddlewareMocks, clearAllServerFnMocks } from '@tanstack-router-testing/router-testing-core';\n\n/**\n * Clear every server-function and middleware mock installed by\n * {@link mockServerFn} or {@link mockMiddleware}.\n *\n * @returns `void`.\n *\n * @example\n * ```ts\n * import { afterEach } from 'vitest';\n * import { clearStartMocks } from '@tanstack-router-testing/react-start-testing';\n *\n * afterEach(() => {\n * clearStartMocks();\n * });\n * ```\n *\n * @remarks\n * This is the same function wired to {@link StartTestRuntime.cleanup}, so you\n * only need to call it explicitly when installing mocks outside of a runtime\n * (i.e. via {@link mockServerFn} / {@link mockMiddleware} directly).\n */\nexport const clearStartMocks = (): void => {\n clearAllServerFnMocks();\n clearAllMiddlewareMocks();\n};\n","import type { AnyFn } from '@tanstack-router-testing/router-testing-core';\n\nimport { setServerFnMock } from '@tanstack-router-testing/router-testing-core';\n\n/**\n * Catch-all type representing any TanStack Start server function.\n *\n * @remarks\n * Used as a generic constraint in {@link mockServerFn} and {@link ServerFnMock}\n * to accept any function created by `createServerFn().handler(...)`.\n */\nexport type AnyServerFn = (...args: never[]) => unknown;\n\n/**\n * The mock implementation signature for a server function of type `TFn`.\n *\n * @typeParam TFn - The server function type being mocked.\n *\n * @remarks\n * The mock receives the same callable-shape arguments as the real server\n * function (e.g. `{ data }`) and must return a compatible result. Full generic\n * inference is preserved so autocomplete works identically to the real call.\n *\n * @see {@link mockServerFn}\n */\nexport type ServerFnMock<TFn extends AnyServerFn> = (...args: Parameters<TFn>) => ReturnType<TFn>;\n\nconst hasKey = (value: unknown, key: string): boolean => typeof value === 'object' && value !== null && key in value;\n\nconst toCallableOptions = (ctx: unknown): unknown => {\n if (!hasKey(ctx, 'data')) return ctx;\n\n const source = ctx as {\n readonly data?: unknown;\n readonly headers?: HeadersInit;\n readonly signal?: AbortSignal;\n readonly fetch?: typeof globalThis.fetch;\n };\n\n return {\n data: source.data,\n ...(source.headers !== undefined ? { headers: source.headers } : {}),\n ...(source.signal !== undefined ? { signal: source.signal } : {}),\n ...(source.fetch !== undefined ? { fetch: source.fetch } : {}),\n };\n};\n\n/**\n * Install a mock implementation for a TanStack Start server function.\n *\n * @param fn - The server function created by `createServerFn().handler(...)`.\n * @param impl - A {@link ServerFnMock} that replaces the real handler for the\n * duration of the test. It receives the same callable-shape arguments (e.g.\n * `{ data }`) as the original function.\n * @returns A disposer function that restores the original implementation when\n * called. Alternatively, call {@link clearStartMocks} to remove all mocks at\n * once.\n *\n * @example\n * ```ts\n * import { createServerFn } from '@tanstack/react-start';\n * import { mockServerFn } from '@tanstack-router-testing/react-start-testing';\n *\n * const listOrders = createServerFn()\n * .validator((input: { userId: string }) => input)\n * .handler(async ({ data }) => db.orders.findMany(data.userId));\n *\n * const dispose = mockServerFn(listOrders, async ({ data }) => [\n * { id: '1', userId: data.userId, total: 42 },\n * ]);\n *\n * // ... run your test ...\n * dispose();\n * ```\n *\n * @remarks\n * The mock is authored against the public callable shape, so a server function\n * normally called as `listOrders({ data })` is mocked with that same signature.\n * Internal context fields are normalised automatically before reaching the mock.\n */\nexport const mockServerFn = <TFn extends AnyServerFn>(fn: TFn, impl: ServerFnMock<TFn>): (() => void) => {\n const handler: AnyFn = (ctx?: unknown) => (impl as AnyFn)(toCallableOptions(ctx));\n return setServerFnMock(fn as unknown as AnyFn, handler);\n};\n","import type { AnyFn } from '@tanstack-router-testing/router-testing-core';\n\nimport { setMiddlewareMock } from '@tanstack-router-testing/router-testing-core';\n\n/**\n * Phase override options for {@link mockMiddleware}.\n *\n * @remarks\n * Provide `client`, `server`, or both. Omitted keys leave the corresponding\n * phase untouched, so you can mock only the server side without affecting the\n * client phase and vice-versa.\n */\nexport interface MockMiddlewareOptions {\n /** Override for the `.client()` phase. When omitted the real client handler runs. */\n readonly client?: AnyFn;\n /** Override for the `.server()` phase. When omitted the real server handler runs. */\n readonly server?: AnyFn;\n}\n\n/**\n * Override one or both phases of a registered middleware.\n *\n * @param mw - The middleware object returned by\n * `createMiddleware().server(...).client(...)`.\n * @param options - A {@link MockMiddlewareOptions} object specifying which\n * phases to replace.\n * @returns A disposer function that restores the original middleware handlers.\n * Alternatively, call {@link clearStartMocks} to remove all mocks at once.\n *\n * @example\n * ```ts\n * import { createMiddleware } from '@tanstack/react-start';\n * import { mockMiddleware } from '@tanstack-router-testing/react-start-testing';\n *\n * const authMiddleware = createMiddleware()\n * .server(async ({ next }) => next({ context: { user: await getUser() } }))\n * .client(async ({ next }) => next());\n *\n * const dispose = mockMiddleware(authMiddleware, {\n * server: async ({ next }) => next({ context: { user: { id: 'test-user' } } }),\n * });\n *\n * // ... run your test ...\n * dispose();\n * ```\n *\n * @throws If `mw` is not registered. Registration is the shim's\n * responsibility (see ADR 0001).\n *\n * @remarks\n * Server functions are invoked directly and mocked middleware participates in\n * that normal call path. Only the phases you specify in `options` are replaced;\n * the rest continue to run their real implementations.\n */\nexport const mockMiddleware = (mw: object, options: MockMiddlewareOptions): (() => void) => setMiddlewareMock(mw, options);\n","import type { TestEnv } from '@tanstack-router-testing/router-testing-core';\n\nimport { runInEnv } from '@tanstack-router-testing/router-testing-core';\n\n/**\n * Execute `fn` with the simulated TanStack Start environment forced to `env`,\n * then restore the prior value.\n *\n * @typeParam T - The return type of `fn`.\n * @param env - The environment to simulate: `'server'` or `'client'`.\n * @param fn - A synchronous or asynchronous function to run inside the\n * simulated environment.\n * @returns A `Promise` resolving to the return value of `fn`.\n *\n * @example\n * ```ts\n * import { runInStartEnv } from '@tanstack-router-testing/react-start-testing';\n *\n * const result = await runInStartEnv('server', () => {\n * // Code here sees `import.meta.env.SSR === true`\n * return fetchDataOnServer();\n * });\n * ```\n *\n * @remarks\n * The environment is scoped to the execution of `fn` and is restored\n * automatically even if `fn` throws. Used internally by\n * {@link createStartTestRuntime} to set the environment for each\n * {@link StartTestRuntime.run | run} invocation.\n */\nexport const runInStartEnv = async <T>(env: TestEnv, fn: () => T | Promise<T>): Promise<T> => runInEnv(env, fn);\n","import type { TestEnv } from '@tanstack-router-testing/router-testing-core';\nimport type { AnyRouter } from '@tanstack/react-router';\nimport type { AnyStartInstanceOptions } from '@tanstack/start-client-core';\nimport type { StartHandlerType, StartStorageContext } from '@tanstack/start-storage-context';\n\nimport { runWithStartContext } from '@tanstack/start-storage-context';\n\nimport { clearStartMocks } from './clearStartMocks.ts';\nimport { runInStartEnv } from './isomorphic.ts';\nimport { __setStartOptionsForTesting } from './shim.ts';\n\n/**\n * Configuration for {@link createStartTestRuntime}.\n *\n * @remarks\n * At most one of `startInstance` and `startOptions` should be provided. If both\n * are given, `startOptions` takes precedence. When neither is supplied the\n * runtime creates an empty options object.\n */\nexport interface StartTestRuntimeOptions {\n /**\n * A Start instance whose `getOptions()` method is called once during\n * runtime creation. Ignored when {@link startOptions} is also provided.\n */\n readonly startInstance?: {\n readonly getOptions: () => AnyStartInstanceOptions | Promise<AnyStartInstanceOptions>;\n };\n /** Explicit Start options to use instead of resolving from a `startInstance`. */\n readonly startOptions?: AnyStartInstanceOptions;\n /**\n * The incoming request available to server functions via `getRequest()`.\n * Accepts a full `Request`, a URL string, or a `URL` object.\n * Defaults to `http://tanstack-router-testing.test/`.\n */\n readonly request?: Request | string | URL;\n /** The TanStack Router instance made available via `getRouter()` inside server functions. */\n readonly router?: AnyRouter;\n /** Initial middleware context. Merged into each call's context before global request middlewares run. */\n readonly context?: unknown;\n /** Default simulated environment. Defaults to `'server'`. */\n readonly env?: TestEnv;\n /** Default handler type passed to the storage context. Defaults to `'serverFn'`. */\n readonly handlerType?: StartHandlerType;\n}\n\n/**\n * Per-invocation overrides passed to {@link StartTestRuntime.run} and\n * {@link StartTestRuntime.call}.\n *\n * @remarks\n * Every property mirrors a field from {@link StartTestRuntimeOptions}. When\n * provided here it takes precedence over the runtime-level default for that\n * single invocation only.\n */\nexport interface StartTestRunOptions {\n /** Override the request for this invocation. */\n readonly request?: Request | string | URL;\n /** Override the middleware context for this invocation. */\n readonly context?: unknown;\n /** Override the simulated environment (`'server'` or `'client'`) for this invocation. */\n readonly env?: TestEnv;\n /** Override the handler type for this invocation. */\n readonly handlerType?: StartHandlerType;\n}\n\n/**\n * A test runtime that simulates the TanStack Start server environment.\n *\n * @remarks\n * Created by {@link createStartTestRuntime}. Provides the storage context,\n * request, and environment that server functions expect at runtime. Call\n * {@link cleanup} (or {@link clearStartMocks}) in `afterEach` to restore all\n * mocks.\n */\nexport interface StartTestRuntime {\n /** The `Request` object available to server functions via `getRequest()`. */\n readonly request: Request;\n /** The resolved Start options used by the runtime. */\n readonly startOptions: AnyStartInstanceOptions;\n /**\n * Run an arbitrary function inside the Start storage context.\n *\n * @typeParam T - The return type of `fn`.\n * @param fn - The function to execute.\n * @param options - Optional per-invocation overrides. See {@link StartTestRunOptions}.\n * @returns A `Promise` resolving to the return value of `fn`.\n */\n readonly run: <T>(fn: () => T | Promise<T>, options?: StartTestRunOptions) => Promise<T>;\n /**\n * Invoke a server function (or any callable) with explicit arguments inside\n * the Start storage context.\n *\n * @typeParam TArgs - Tuple of argument types.\n * @typeParam TReturn - The return type of `fn`.\n * @param fn - The function to call.\n * @param args - Arguments forwarded to `fn`.\n * @param options - Optional per-invocation overrides. See {@link StartTestRunOptions}.\n * @returns A `Promise` resolving to the awaited return value of `fn`.\n */\n readonly call: <TArgs extends readonly unknown[], TReturn>(\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n args: TArgs,\n options?: StartTestRunOptions,\n ) => Promise<Awaited<TReturn>>;\n /**\n * Remove all server-function and middleware mocks. Equivalent to calling\n * {@link clearStartMocks}.\n */\n readonly cleanup: () => void;\n}\n\n/**\n * Create a {@link StartTestRuntime} for executing server functions in a\n * simulated TanStack Start environment.\n *\n * @param options - Runtime configuration. See {@link StartTestRuntimeOptions}.\n * @returns A `Promise` resolving to a {@link StartTestRuntime} instance.\n *\n * @example\n * ```ts\n * import { createServerFn } from '@tanstack/react-start';\n * import {\n * createStartTestRuntime,\n * mockServerFn,\n * } from '@tanstack-router-testing/react-start-testing';\n *\n * const runtime = await createStartTestRuntime({\n * request: 'http://localhost:3000/api/orders',\n * env: 'server',\n * });\n *\n * const listOrders = createServerFn()\n * .validator((input: { userId: string }) => input)\n * .handler(async ({ data }) => [{ id: '1', userId: data.userId }]);\n *\n * mockServerFn(listOrders, async ({ data }) => [\n * { id: 'mock-1', userId: data.userId },\n * ]);\n *\n * const orders = await runtime.call(listOrders, [{ data: { userId: 'u1' } }]);\n * // orders === [{ id: 'mock-1', userId: 'u1' }]\n *\n * runtime.cleanup();\n * ```\n *\n * @remarks\n * The runtime resolves Start options from either `startOptions` or\n * `startInstance.getOptions()`, sets up the Start storage context, and\n * executes global request middlewares before each invocation.\n */\nexport const createStartTestRuntime = async (options: StartTestRuntimeOptions = {}): Promise<StartTestRuntime> => {\n const startOptions = options.startOptions ?? (await options.startInstance?.getOptions()) ?? ({} as AnyStartInstanceOptions);\n const request = toRequest(options.request);\n\n __setStartOptionsForTesting(startOptions);\n\n const createContext = (runOptions: StartTestRunOptions = {}): StartStorageContext => ({\n getRouter: () => {\n if (!options.router) {\n throw new Error('[tanstack-router-testing] createStartTestRuntime: no router was provided for this test runtime.');\n }\n return options.router as never;\n },\n request: toRequest(runOptions.request ?? request),\n startOptions,\n contextAfterGlobalMiddlewares: runOptions.context ?? options.context ?? {},\n executedRequestMiddlewares: new Set(),\n handlerType: runOptions.handlerType ?? options.handlerType ?? 'serverFn',\n });\n\n const runtime: StartTestRuntime = {\n request,\n startOptions,\n run: async (fn, runOptions) => {\n const env = runOptions?.env ?? options.env ?? 'server';\n const storage = createContext(runOptions);\n return runWithStartContext(storage, async () => {\n storage.contextAfterGlobalMiddlewares = await executeGlobalRequestMiddlewares(storage, storage.contextAfterGlobalMiddlewares);\n return runInStartEnv(env, fn);\n });\n },\n call: <TArgs extends readonly unknown[], TReturn>(fn: (...args: TArgs) => TReturn | Promise<TReturn>, args: TArgs, runOptions?: StartTestRunOptions) =>\n runtime.run(() => fn(...args), runOptions) as Promise<Awaited<TReturn>>,\n cleanup: clearStartMocks,\n };\n\n return runtime;\n};\n\nconst toRequest = (request: Request | string | URL | undefined): Request => {\n if (request instanceof Request) return request;\n const url = request?.toString() ?? 'http://tanstack-router-testing.test/';\n return new Request(url);\n};\n\nconst executeGlobalRequestMiddlewares = async (storage: StartStorageContext, initialContext: unknown): Promise<unknown> => {\n const middlewares = [...((storage.startOptions?.requestMiddleware ?? []) as { readonly options?: { readonly server?: unknown } }[])];\n const { pathname } = new URL(storage.request.url);\n\n const next = async (\n parentContext: unknown,\n ctx: { readonly context?: unknown } | undefined = {},\n ): Promise<{ readonly context: unknown; readonly response: Response }> => {\n const middleware = middlewares.shift();\n const context = mergeContext(parentContext, ctx.context);\n if (!middleware) {\n return {\n context,\n response: new Response(null),\n };\n }\n\n storage.executedRequestMiddlewares.add(middleware);\n\n const server = middleware.options?.server;\n if (typeof server !== 'function') return next(context);\n\n const result = await server({\n request: storage.request,\n pathname,\n context,\n next: (nextCtx?: { readonly context?: unknown }) => next(context, nextCtx),\n });\n\n if (result instanceof Response) {\n return { context, response: result };\n }\n\n return {\n context: mergeContext(context, (result as { readonly context?: unknown } | undefined)?.context),\n response: (result as { readonly response?: Response } | undefined)?.response ?? new Response(null),\n };\n };\n\n const result = await next(initialContext);\n return result.context;\n};\n\nconst mergeContext = (left: unknown, right: unknown): unknown => {\n if (isRecord(left) && isRecord(right)) {\n return { ...left, ...right };\n }\n return right ?? left ?? {};\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n","import type { StartTestRuntime, StartTestRuntimeOptions } from './runtime.ts';\nimport type { ComponentType } from 'react';\n\nimport { createElement } from 'react';\nimport { renderToReadableStream, renderToString } from 'react-dom/server';\n\nimport { createStartTestRuntime } from './runtime.ts';\n\n/**\n * Configuration for {@link createRscTestRuntime}.\n *\n * @remarks\n * Extends {@link StartTestRuntimeOptions} with an additional `streaming` flag\n * that controls the server rendering mode.\n */\nexport interface RscTestRuntimeOptions extends StartTestRuntimeOptions {\n /**\n * When `true`, renders via `renderToReadableStream` and collects individual\n * chunks. When `false` (default), uses `renderToString` for a single\n * synchronous HTML output.\n */\n readonly streaming?: boolean;\n}\n\n/**\n * The result of rendering a React Server Component via\n * {@link RscTestRuntime.renderServerComponent}.\n */\nexport interface RscRenderResult {\n /** The complete HTML string produced by the render. */\n readonly html: string;\n /**\n * A cloned `ReadableStream` of the raw bytes. Only present when the runtime\n * was created with `streaming: true`; otherwise `null`.\n */\n readonly stream: ReadableStream<Uint8Array> | null;\n /**\n * Decoded text chunks collected from the stream. Empty when `streaming` is\n * `false`.\n */\n readonly chunks: readonly string[];\n}\n\n/**\n * Extended test runtime that adds React Server Component rendering on top of\n * the base {@link StartTestRuntime}.\n *\n * @remarks\n * Created by {@link createRscTestRuntime}. Inherits all members from\n * {@link StartTestRuntime} and adds {@link renderServerComponent}.\n */\nexport interface RscTestRuntime extends StartTestRuntime {\n /**\n * Render a React Server Component to HTML.\n *\n * @typeParam TProps - The component's props type.\n * @param component - The React component to render.\n * @param props - Props forwarded to `createElement(component, props)`.\n * @returns A `Promise` resolving to an {@link RscRenderResult} containing the\n * rendered HTML and, when streaming is enabled, the raw stream and decoded\n * chunks.\n */\n readonly renderServerComponent: <TProps extends Record<string, unknown>>(component: ComponentType<TProps>, props: TProps) => Promise<RscRenderResult>;\n}\n\n/**\n * Create an {@link RscTestRuntime} for rendering React Server Components in a\n * simulated TanStack Start environment.\n *\n * @param options - Runtime configuration. See {@link RscTestRuntimeOptions}.\n * @returns A `Promise` resolving to an {@link RscTestRuntime} instance.\n *\n * @example\n * ```ts\n * import { createRscTestRuntime } from '@tanstack-router-testing/react-start-testing';\n *\n * const runtime = await createRscTestRuntime({ streaming: true });\n *\n * function Greeting({ name }: { name: string }) {\n * return <h1>Hello, {name}!</h1>;\n * }\n *\n * const { html, chunks } = await runtime.renderServerComponent(Greeting, {\n * name: 'TanStack',\n * });\n * // html === '<h1>Hello, TanStack!</h1>'\n *\n * runtime.cleanup();\n * ```\n *\n * @remarks\n * Delegates to {@link createStartTestRuntime} for the base runtime, then layers\n * on `renderServerComponent` which uses `react-dom/server` under the hood. Set\n * `streaming: true` in options to render via `renderToReadableStream` instead of\n * the default `renderToString`.\n */\nexport const createRscTestRuntime = async (options: RscTestRuntimeOptions = {}): Promise<RscTestRuntime> => {\n const base = await createStartTestRuntime(options);\n const streaming = options.streaming ?? false;\n\n return {\n ...base,\n renderServerComponent: async <TProps extends Record<string, unknown>>(component: ComponentType<TProps>, props: TProps): Promise<RscRenderResult> => {\n const element = createElement(component, props);\n\n if (!streaming) {\n const html = renderToString(element);\n return { html, stream: null, chunks: [] };\n }\n\n const stream = await renderToReadableStream(element);\n const chunks: string[] = [];\n const decoder = new TextDecoder();\n\n const [collectStream, returnStream] = stream.tee();\n\n const reader = collectStream.getReader();\n const collectChunks = async (): Promise<void> => {\n const result = await reader.read();\n if (result.value !== undefined) {\n chunks.push(decoder.decode(result.value, { stream: !result.done }));\n }\n if (!result.done) await collectChunks();\n };\n await collectChunks();\n\n const html = chunks.join('');\n return { html, stream: returnStream, chunks };\n },\n };\n};\n","/**\n * Upstream-shaped test helpers for `@tanstack/react-start`.\n *\n * Server functions are called directly in tests, exactly as production code\n * calls them. This package only exposes mock and environment controls.\n */\n\nexport { type CallMiddlewareOptions, type CallMiddlewareResult, callMiddleware } from '@tanstack-router-testing/router-testing-core';\nexport { clearStartMocks } from './clearStartMocks.ts';\nexport { type AnyServerFn, mockServerFn, type ServerFnMock } from './mockServerFn.ts';\nexport { type MockMiddlewareOptions, mockMiddleware } from './mockMiddleware.ts';\nexport { runInStartEnv } from './isomorphic.ts';\nexport { createRscTestRuntime, type RscRenderResult, type RscTestRuntime, type RscTestRuntimeOptions } from './rsc.tsx';\nexport { createStartTestRuntime, type StartTestRuntime, type StartTestRuntimeOptions, type StartTestRunOptions } from './runtime.ts';\n\n/**\n * Current package version. Bumped at release time.\n */\nexport const VERSION = '0.0.0';\n"],"mappings":"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"}