waku 1.0.0-beta.5 → 1.0.0-beta.7

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.
Files changed (87) hide show
  1. package/README.md +18 -16
  2. package/dist/client.d.ts +1 -1
  3. package/dist/client.js +1 -1
  4. package/dist/client.js.map +1 -1
  5. package/dist/lib/global-types.d.ts +0 -1
  6. package/dist/lib/types.d.ts +3 -0
  7. package/dist/lib/types.js.map +1 -1
  8. package/dist/lib/utils/etags.d.ts +8 -0
  9. package/dist/lib/utils/etags.js +30 -0
  10. package/dist/lib/utils/etags.js.map +1 -0
  11. package/dist/lib/utils/path.d.ts +0 -41
  12. package/dist/lib/utils/path.js +0 -178
  13. package/dist/lib/utils/path.js.map +1 -1
  14. package/dist/lib/utils/render.js +6 -0
  15. package/dist/lib/utils/render.js.map +1 -1
  16. package/dist/lib/utils/request.d.ts +2 -1
  17. package/dist/lib/utils/request.js +18 -5
  18. package/dist/lib/utils/request.js.map +1 -1
  19. package/dist/lib/vite-plugins/fs-router-typegen.js +19 -3
  20. package/dist/lib/vite-plugins/fs-router-typegen.js.map +1 -1
  21. package/dist/lib/vite-rsc/handler.js +2 -2
  22. package/dist/lib/vite-rsc/handler.js.map +1 -1
  23. package/dist/minimal/client-utils/fetch-store.d.ts +3 -0
  24. package/dist/minimal/client-utils/fetch-store.js +1 -0
  25. package/dist/minimal/client-utils/fetch-store.js.map +1 -1
  26. package/dist/minimal/client.d.ts +37 -4
  27. package/dist/minimal/client.js +173 -49
  28. package/dist/minimal/client.js.map +1 -1
  29. package/dist/minimal/server.d.ts +16 -0
  30. package/dist/minimal/server.js +30 -0
  31. package/dist/minimal/server.js.map +1 -1
  32. package/dist/router/base-types.d.ts +13 -2
  33. package/dist/router/base-types.js +6 -1
  34. package/dist/router/base-types.js.map +1 -1
  35. package/dist/router/client-utils/navigate.d.ts +39 -0
  36. package/dist/router/client-utils/navigate.js +95 -0
  37. package/dist/router/client-utils/navigate.js.map +1 -0
  38. package/dist/router/client.d.ts +37 -19
  39. package/dist/router/client.js +329 -281
  40. package/dist/router/client.js.map +1 -1
  41. package/dist/router/create-pages-utils/inferred-path-types.d.ts +7 -1
  42. package/dist/router/create-pages-utils/inferred-path-types.js.map +1 -1
  43. package/dist/router/create-pages-utils/path-spec.d.ts +6 -0
  44. package/dist/router/create-pages-utils/path-spec.js +22 -0
  45. package/dist/router/create-pages-utils/path-spec.js.map +1 -0
  46. package/dist/router/create-pages.d.ts +8 -8
  47. package/dist/router/create-pages.js +6 -6
  48. package/dist/router/create-pages.js.map +1 -1
  49. package/dist/router/define-router-utils/path-spec.d.ts +5 -0
  50. package/dist/router/define-router-utils/path-spec.js +19 -0
  51. package/dist/router/define-router-utils/path-spec.js.map +1 -0
  52. package/dist/router/define-router-utils/task-runner.js.map +1 -0
  53. package/dist/router/define-router.d.ts +9 -5
  54. package/dist/router/define-router.js +147 -130
  55. package/dist/router/define-router.js.map +1 -1
  56. package/dist/router/fs-router.d.ts +6 -5
  57. package/dist/router/fs-router.js +1 -1
  58. package/dist/router/fs-router.js.map +1 -1
  59. package/dist/router/{client-utils → isomorphic-utils}/build-route-href.d.ts +6 -1
  60. package/dist/router/{client-utils → isomorphic-utils}/build-route-href.js +2 -2
  61. package/dist/router/isomorphic-utils/build-route-href.js.map +1 -0
  62. package/dist/router/{client-utils → isomorphic-utils}/match-route-params.js +2 -2
  63. package/dist/router/isomorphic-utils/match-route-params.js.map +1 -0
  64. package/dist/router/isomorphic-utils/path-spec.d.ts +32 -0
  65. package/dist/router/isomorphic-utils/path-spec.js +143 -0
  66. package/dist/router/isomorphic-utils/path-spec.js.map +1 -0
  67. package/dist/router/{common-utils → isomorphic-utils}/route-path.d.ts +0 -2
  68. package/dist/router/{common-utils → isomorphic-utils}/route-path.js +0 -3
  69. package/dist/router/isomorphic-utils/route-path.js.map +1 -0
  70. package/dist/router/isomorphic-utils/search-codec-registry.js.map +1 -0
  71. package/dist/router/prefetch-cache.d.ts +22 -0
  72. package/dist/router/prefetch-cache.js +79 -0
  73. package/dist/router/prefetch-cache.js.map +1 -0
  74. package/package.json +1 -1
  75. package/dist/lib/utils/prefetch-cache.d.ts +0 -15
  76. package/dist/lib/utils/prefetch-cache.js +0 -37
  77. package/dist/lib/utils/prefetch-cache.js.map +0 -1
  78. package/dist/lib/utils/task-runner.js.map +0 -1
  79. package/dist/router/client-utils/build-route-href.js.map +0 -1
  80. package/dist/router/client-utils/match-route-params.js.map +0 -1
  81. package/dist/router/client-utils/search-codec-registry.js.map +0 -1
  82. package/dist/router/common-utils/route-path.js.map +0 -1
  83. /package/dist/{lib/utils → router/define-router-utils}/task-runner.d.ts +0 -0
  84. /package/dist/{lib/utils → router/define-router-utils}/task-runner.js +0 -0
  85. /package/dist/router/{client-utils → isomorphic-utils}/match-route-params.d.ts +0 -0
  86. /package/dist/router/{client-utils → isomorphic-utils}/search-codec-registry.d.ts +0 -0
  87. /package/dist/router/{client-utils → isomorphic-utils}/search-codec-registry.js +0 -0
package/README.md CHANGED
@@ -13,7 +13,7 @@ visit [waku.gg](https://waku.gg) or `npm create waku@latest`
13
13
 
14
14
  ## Introduction
15
15
 
16
- **Waku** _(wah-ku)_ or **わく** is the minimal React framework. It’s lightweight and designed for a fun developer experience, yet supports all the latest React 19 features like server components and actions. Built for marketing sites, headless commerce, and web apps. For large enterprise applications, you may prefer a heavier framework.
16
+ **Waku** _(wah-ku)_ or **わく** is the minimal React framework. It's lightweight and designed for a fun developer experience, yet supports all the latest React 19 features like server components and actions. Built for marketing sites, headless commerce, and full-stack web apps, small or large. Whether Waku fits is about the architecture you want, not the size of your project: Waku keeps its framework surface minimal and composes with ecosystem libraries, while heavier frameworks own more of those concerns for you.
17
17
 
18
18
  ## Getting started
19
19
 
@@ -31,19 +31,21 @@ npm create waku@latest
31
31
 
32
32
  **Node.js version requirement:** `^26.0.0` or `^24.0.0` or `^22.15.0`
33
33
 
34
+ For a guided path, start with the [Quick Start](https://waku.gg/guides/quick-start) guide and continue with the Learn series on [waku.gg/guides](https://waku.gg/guides), which builds a small app step by step.
35
+
34
36
  ## Rendering
35
37
 
36
- While there’s a bit of a learning curve to modern React rendering, it introduces powerful new patterns of full-stack composability that are only possible with the advent of [server components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md).
38
+ While there's a bit of a learning curve to modern React rendering, it introduces powerful new patterns of full-stack composability that are only possible with the advent of [server components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md).
37
39
 
38
- So please don’t be intimidated by the `'use client'` directive! Once you get the hang of it, you’ll appreciate how awesome it is to flexibly move server-client boundaries with a single line of code as your full-stack React codebase evolves over time. It’s way simpler than maintaining separate codebases for your backend and frontend.
40
+ So please don't be intimidated by the `'use client'` directive! Once you get the hang of it, you'll appreciate how awesome it is to flexibly move server-client boundaries with a single line of code as your full-stack React codebase evolves over time. It's way simpler than maintaining separate codebases for your backend and frontend.
39
41
 
40
- And please don’t fret about client components! Even if you only lightly optimize towards server components, your client bundle size will be smaller than traditional React frameworks, which are always 100% client components.
42
+ And please don't fret about client components! Even if you only lightly optimize towards server components, your client bundle size will be smaller than traditional React frameworks, which are always 100% client components.
41
43
 
42
44
  > Future versions of Waku may provide additional opt-in APIs to abstract some of the complexity away for an improved developer experience.
43
45
 
44
46
  #### Server components
45
47
 
46
- Server components can be made async and can securely perform server-side logic and data fetching. Feel free to access the local file-system and import heavy dependencies since they aren’t included in the client bundle. They have no state, interactivity, or access to browser APIs since they run _exclusively_ on the server.
48
+ Server components can be made async and can securely perform server-side logic and data fetching. Feel free to access the local file-system and import heavy dependencies since they aren't included in the client bundle. They have no state, interactivity, or access to browser APIs since they run _exclusively_ on the server.
47
49
 
48
50
  ```tsx
49
51
  // server component
@@ -133,9 +135,9 @@ Waku provides static prerendering (SSG) and server-side rendering (SSR) options
133
135
 
134
136
  Each layout and page in Waku is composed of a React component hierarchy.
135
137
 
136
- It begins with a server component at the top of the tree. Then at points down the hierarchy, you’ll eventually import a component that needs client component APIs. Mark this file with a `'use client'` directive at the top. When imported into a server component, it will create a server-client boundary. Below this point, all imported components are hydrated and will run in the browser as well.
138
+ It begins with a server component at the top of the tree. Then at points down the hierarchy, you'll eventually import a component that needs client component APIs. Mark this file with a `'use client'` directive at the top. When imported into a server component, it will create a server-client boundary. Below this point, all imported components are hydrated and will run in the browser as well.
137
139
 
138
- Server components can be rendered below this boundary, but only via composition (e.g., `children` props). Together they form [a new “React server” layer](https://github.com/reactwg/server-components/discussions/4) that runs _before_ the traditional “React client” layer with which you’re already familiar.
140
+ Server components can be rendered below this boundary, but only via composition (e.g., `children` props). Together they form [a new "React server" layer](https://github.com/reactwg/server-components/discussions/4) that runs _before_ the traditional "React client" layer with which you're already familiar.
139
141
 
140
142
  Client components are still server-side rendered as SSR is separate from RSC. Please see the [linked diagrams](https://github.com/reactwg/server-components/discussions/4) for a helpful visual.
141
143
 
@@ -145,7 +147,7 @@ To learn more about the modern React architecture, we recommend [Making Sense of
145
147
 
146
148
  ## Routing
147
149
 
148
- Waku provides a minimal file-based “pages router” experience built for the server components era.
150
+ Waku provides a minimal file-based "pages router" experience built for the server components era.
149
151
 
150
152
  Its underlying [low-level API](https://github.com/wakujs/waku/blob/main/docs/create-pages.mdx) is also available for those that prefer programmatic routing. This documentation covers file-based routing since many React developers prefer it, but please feel free to try both and see which you like more!
151
153
 
@@ -395,7 +397,7 @@ export const getConfig = async () => {
395
397
 
396
398
  #### Catch-all routes
397
399
 
398
- Catch-all or “wildcard” segment routes (e.g., `/app/[...catchAll]`) are marked with an ellipsis before the name and have indefinite segments.
400
+ Catch-all or "wildcard" segment routes (e.g., `/app/[...catchAll]`) are marked with an ellipsis before the name and have indefinite segments.
399
401
 
400
402
  Wildcard routes receive a prop with segment values as an ordered array. For example, the `/app/profile/settings` route would receive a `catchAll` prop with the value `['profile', 'settings']`. These values can then be used to determine what to render in the component.
401
403
 
@@ -1250,7 +1252,7 @@ export const ContactForm = () => {
1250
1252
 
1251
1253
  #### Configuring API routes
1252
1254
 
1253
- API routes are dynamic by default, but if you’re using them to create a static resource such as an XML document, you can export a `getConfig` function that returns a config object with the render property set to `'static'`.
1255
+ API routes are dynamic by default, but if you're using them to create a static resource such as an XML document, you can export a `getConfig` function that returns a config object with the render property set to `'static'`.
1254
1256
 
1255
1257
  ```ts
1256
1258
  // ./src/pages/_api/blog/rss.xml.ts
@@ -1413,17 +1415,17 @@ export const ContactForm = ({ author = 'guest' }) => {
1413
1415
 
1414
1416
  Server actions integrate with many other React APIs such as the [`useTransition`](https://react.dev/reference/react/useTransition) hook for handling pending states, the [`useActionState`](https://react.dev/reference/react/useActionState) hook for accessing returned values, and the [`useOptimistic`](https://react.dev/reference/react/useOptimistic) hook for performing optimistic UI updates.
1415
1417
 
1416
- See the talk [What’s new in React 19?](https://www.youtube.com/watch?v=AJOGzVygGcY) to learn more.
1418
+ See the talk [What's new in React 19?](https://www.youtube.com/watch?v=AJOGzVygGcY) to learn more.
1417
1419
 
1418
1420
  ## State management
1419
1421
 
1420
- We recommend [Jotai](https://jotai.org) for global React state management based on the atomic model’s performance and scalability, but Waku is compatible with all React state management libraries such as Zustand and Valtio.
1422
+ We recommend [Jotai](https://jotai.org) for global React state management based on the atomic model's performance and scalability, but Waku is compatible with all React state management libraries such as Zustand and Valtio.
1421
1423
 
1422
- > We’re exploring a deeper integration of atomic state management into Waku to achieve the performance and developer experience of signals while preserving React’s declarative programming model.
1424
+ > We're exploring a deeper integration of atomic state management into Waku to achieve the performance and developer experience of signals while preserving React's declarative programming model.
1423
1425
 
1424
1426
  ## Environment variables
1425
1427
 
1426
- It’s important to distinguish environment variables that must be kept secret from those that can be made public.
1428
+ It's important to distinguish environment variables that must be kept secret from those that can be made public.
1427
1429
 
1428
1430
  #### Private
1429
1431
 
@@ -1431,7 +1433,7 @@ By default all environment variables are considered private and are accessible o
1431
1433
 
1432
1434
  #### Public
1433
1435
 
1434
- A special `WAKU_PUBLIC_` prefix is required to make an environment variable public and accessible in client components. They will be present as cleartext in the production JavaScript bundle sent to users’ browsers.
1436
+ A special `WAKU_PUBLIC_` prefix is required to make an environment variable public and accessible in client components. They will be present as cleartext in the production JavaScript bundle sent to users' browsers.
1435
1437
 
1436
1438
  ### Runtime agnostic (recommended)
1437
1439
 
@@ -1616,7 +1618,7 @@ Please join our friendly [GitHub discussions](https://github.com/wakujs/waku/dis
1616
1618
 
1617
1619
  ## Roadmap
1618
1620
 
1619
- Waku is in active development and we’re seeking additional contributors. Check out our [roadmap](https://github.com/wakujs/waku/issues/24) for more information.
1621
+ Waku is in active development and we're seeking additional contributors. Check out our [roadmap](https://github.com/wakujs/waku/issues/24) for more information.
1620
1622
 
1621
1623
  ## Contributing
1622
1624
 
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Highly experimental, the name might change.
2
+ * Experimental, the name and the behavior might change.
3
3
  */
4
4
  export declare const unstable_allowServer: <T>(x: T) => T;
5
5
  /**
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getErrorInfo } from './lib/utils/custom-errors.js';
2
2
  /**
3
- * Highly experimental, the name might change.
3
+ * Experimental, the name and the behavior might change.
4
4
  */ export const unstable_allowServer = (x)=>x;
5
5
  /**
6
6
  * Unsure if this is going to be a public API in the future.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { getErrorInfo } from './lib/utils/custom-errors.js';\n\n/**\n * Highly experimental, the name might change.\n */\nexport const unstable_allowServer = <T>(x: T) => x;\n\n/**\n * Unsure if this is going to be a public API in the future.\n */\nexport const unstable_defaultRootOptions = {\n onCaughtError(error: unknown) {\n if (getErrorInfo(error)) {\n return;\n }\n console.error(error);\n },\n};\n"],"names":["getErrorInfo","unstable_allowServer","x","unstable_defaultRootOptions","onCaughtError","error","console"],"mappings":"AAAA,SAASA,YAAY,QAAQ,+BAA+B;AAE5D;;CAEC,GACD,OAAO,MAAMC,uBAAuB,CAAIC,IAASA,EAAE;AAEnD;;CAEC,GACD,OAAO,MAAMC,8BAA8B;IACzCC,eAAcC,KAAc;QAC1B,IAAIL,aAAaK,QAAQ;YACvB;QACF;QACAC,QAAQD,KAAK,CAACA;IAChB;AACF,EAAE"}
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { getErrorInfo } from './lib/utils/custom-errors.js';\n\n/**\n * Experimental, the name and the behavior might change.\n */\nexport const unstable_allowServer = <T>(x: T) => x;\n\n/**\n * Unsure if this is going to be a public API in the future.\n */\nexport const unstable_defaultRootOptions = {\n onCaughtError(error: unknown) {\n if (getErrorInfo(error)) {\n return;\n }\n console.error(error);\n },\n};\n"],"names":["getErrorInfo","unstable_allowServer","x","unstable_defaultRootOptions","onCaughtError","error","console"],"mappings":"AAAA,SAASA,YAAY,QAAQ,+BAA+B;AAE5D;;CAEC,GACD,OAAO,MAAMC,uBAAuB,CAAIC,IAASA,EAAE;AAEnD;;CAEC,GACD,OAAO,MAAMC,8BAA8B;IACzCC,eAAcC,KAAc;QAC1B,IAAIL,aAAaK,QAAQ;YACvB;QACF;QACAC,QAAQD,KAAK,CAACA;IAChB;AACF,EAAE"}
@@ -4,7 +4,6 @@ declare global {
4
4
  var __WAKU_REFETCH_ROUTE__: (() => void) | undefined;
5
5
  var __WAKU_START_PREVIEW_SERVER__: (() => Promise<import('./vite-rsc/preview.js').PreviewServer>) | undefined;
6
6
  var __WAKU_SERVER_ENV__: Readonly<Record<string, string>> | undefined;
7
- var __WAKU_PREFETCHED__: import('./utils/prefetch-cache.js').PrefetchEntry[] | undefined;
8
7
  var __WAKU_INITIAL_RSC__: import('./utils/initial-rsc.js').InitialRscEntry | undefined;
9
8
  var __WAKU_DEBUG_CHANNELS__: Map<string, {
10
9
  readable: ReadableStream;
@@ -1,8 +1,10 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { Config } from '../config.js';
3
+ import type { Etags } from './utils/etags.js';
3
4
  type Elements = Record<string, unknown>;
4
5
  export type Unstable_RenderRsc = (elements: Elements, options?: {
5
6
  value?: unknown;
7
+ etags?: Etags;
6
8
  unstable_clientModuleCallback?: (ids: string[]) => void;
7
9
  }) => Promise<ReadableStream>;
8
10
  export type Unstable_ParseRsc = (rscStream: ReadableStream) => Promise<Elements>;
@@ -30,6 +32,7 @@ export type Unstable_HandleRequest = (input: ({
30
32
  }) & {
31
33
  pathname: string;
32
34
  req: Request;
35
+ etags?: Etags;
33
36
  }, utils: {
34
37
  renderRsc: Unstable_RenderRsc;
35
38
  parseRsc: Unstable_ParseRsc;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/types.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { Config } from '../config.js';\n\ntype Elements = Record<string, unknown>;\n\nexport type Unstable_RenderRsc = (\n elements: Elements,\n options?: {\n value?: unknown;\n unstable_clientModuleCallback?: (ids: string[]) => void;\n },\n) => Promise<ReadableStream>;\n\nexport type Unstable_ParseRsc = (\n rscStream: ReadableStream,\n) => Promise<Elements>;\n\nexport type Unstable_RenderHtml = (\n elementsStream: ReadableStream,\n html: ReactNode,\n options: {\n rscPath: string;\n formState?: unknown;\n status?: number;\n nonce?: string;\n unstable_extraScriptContent?: string;\n },\n) => Promise<Response>;\n\nexport type Unstable_EmitFile = (\n filePath: string,\n body: ReadableStream,\n) => Promise<void>;\n\nexport type Unstable_HandleRequest = (\n input: (\n | { type: 'component'; rscPath: string; rscParams: unknown }\n | {\n type: 'function';\n fn: (...args: unknown[]) => Promise<unknown>;\n args: unknown[];\n }\n | {\n type: 'action';\n fn: () => Promise<unknown>;\n }\n | { type: 'custom' }\n ) & {\n pathname: string;\n req: Request;\n },\n utils: {\n renderRsc: Unstable_RenderRsc;\n parseRsc: Unstable_ParseRsc;\n renderHtml: Unstable_RenderHtml;\n loadBuildMetadata: (key: string) => Promise<string | undefined>;\n },\n) => Promise<ReadableStream | Response | 'fallback' | null | undefined>;\n\nexport type Unstable_HandleBuild = (utils: {\n renderRsc: Unstable_RenderRsc;\n parseRsc: Unstable_ParseRsc;\n renderHtml: Unstable_RenderHtml;\n rscPath2pathname: (rscPath: string) => string;\n saveBuildMetadata: (key: string, value: string) => Promise<void>;\n generateFile: (\n fileName: string,\n body: ReadableStream | string,\n ) => Promise<void>;\n generateDefaultHtml: (fileName: string) => Promise<void>;\n unstable_registerPrunableFile: (srcPath: string) => void;\n}) => Promise<void>;\n\nexport type Unstable_Handlers = {\n handleRequest: Unstable_HandleRequest;\n handleBuild: Unstable_HandleBuild;\n [someOtherProperty: string]: unknown;\n};\n\nexport type Unstable_ServerEntry = {\n fetch: (req: Request, ...args: any[]) => Response | Promise<Response>;\n build: (\n utils: {\n emitFile: Unstable_EmitFile;\n unstable_registerPrunableFile: (srcPath: string) => void;\n },\n ...args: any[]\n ) => Promise<void>;\n buildOptions?: Record<string, unknown>;\n buildEnhancers?: string[]; // enhancer module ids\n defaultExport?: unknown;\n [someOtherProperty: string]: unknown;\n};\n\nexport type Unstable_ProcessRequest = (\n arg: Parameters<Unstable_ServerEntry['fetch']>[0],\n) => Promise<Response | null>;\n\nexport type Unstable_ProcessBuild = (\n arg: Parameters<Unstable_ServerEntry['build']>[0],\n) => Promise<void>;\n\nexport type Unstable_CreateServerEntryAdapter = <Options>(\n fn: (\n args: {\n handlers: Unstable_Handlers;\n processRequest: Unstable_ProcessRequest;\n processBuild: Unstable_ProcessBuild;\n setAllEnv: (newEnv: Readonly<Record<string, unknown>>) => void;\n config: Omit<Required<Config>, 'vite'>;\n isBuild: boolean;\n notFoundHtml: string;\n },\n options?: Options,\n ) => Unstable_ServerEntry,\n) => (handlers: Unstable_Handlers, options?: Options) => Unstable_ServerEntry;\n"],"names":[],"mappings":"AAsGA,WAa8E"}
1
+ {"version":3,"sources":["../../src/lib/types.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { Config } from '../config.js';\nimport type { Etags } from './utils/etags.js';\n\ntype Elements = Record<string, unknown>;\n\nexport type Unstable_RenderRsc = (\n elements: Elements,\n options?: {\n value?: unknown;\n etags?: Etags;\n unstable_clientModuleCallback?: (ids: string[]) => void;\n },\n) => Promise<ReadableStream>;\n\nexport type Unstable_ParseRsc = (\n rscStream: ReadableStream,\n) => Promise<Elements>;\n\nexport type Unstable_RenderHtml = (\n elementsStream: ReadableStream,\n html: ReactNode,\n options: {\n rscPath: string;\n formState?: unknown;\n status?: number;\n nonce?: string;\n unstable_extraScriptContent?: string;\n },\n) => Promise<Response>;\n\nexport type Unstable_EmitFile = (\n filePath: string,\n body: ReadableStream,\n) => Promise<void>;\n\nexport type Unstable_HandleRequest = (\n input: (\n | { type: 'component'; rscPath: string; rscParams: unknown }\n | {\n type: 'function';\n fn: (...args: unknown[]) => Promise<unknown>;\n args: unknown[];\n }\n | {\n type: 'action';\n fn: () => Promise<unknown>;\n }\n | { type: 'custom' }\n ) & {\n pathname: string;\n req: Request;\n etags?: Etags;\n },\n utils: {\n renderRsc: Unstable_RenderRsc;\n parseRsc: Unstable_ParseRsc;\n renderHtml: Unstable_RenderHtml;\n loadBuildMetadata: (key: string) => Promise<string | undefined>;\n },\n) => Promise<ReadableStream | Response | 'fallback' | null | undefined>;\n\nexport type Unstable_HandleBuild = (utils: {\n renderRsc: Unstable_RenderRsc;\n parseRsc: Unstable_ParseRsc;\n renderHtml: Unstable_RenderHtml;\n rscPath2pathname: (rscPath: string) => string;\n saveBuildMetadata: (key: string, value: string) => Promise<void>;\n generateFile: (\n fileName: string,\n body: ReadableStream | string,\n ) => Promise<void>;\n generateDefaultHtml: (fileName: string) => Promise<void>;\n unstable_registerPrunableFile: (srcPath: string) => void;\n}) => Promise<void>;\n\nexport type Unstable_Handlers = {\n handleRequest: Unstable_HandleRequest;\n handleBuild: Unstable_HandleBuild;\n [someOtherProperty: string]: unknown;\n};\n\nexport type Unstable_ServerEntry = {\n fetch: (req: Request, ...args: any[]) => Response | Promise<Response>;\n build: (\n utils: {\n emitFile: Unstable_EmitFile;\n unstable_registerPrunableFile: (srcPath: string) => void;\n },\n ...args: any[]\n ) => Promise<void>;\n buildOptions?: Record<string, unknown>;\n buildEnhancers?: string[]; // enhancer module ids\n defaultExport?: unknown;\n [someOtherProperty: string]: unknown;\n};\n\nexport type Unstable_ProcessRequest = (\n arg: Parameters<Unstable_ServerEntry['fetch']>[0],\n) => Promise<Response | null>;\n\nexport type Unstable_ProcessBuild = (\n arg: Parameters<Unstable_ServerEntry['build']>[0],\n) => Promise<void>;\n\nexport type Unstable_CreateServerEntryAdapter = <Options>(\n fn: (\n args: {\n handlers: Unstable_Handlers;\n processRequest: Unstable_ProcessRequest;\n processBuild: Unstable_ProcessBuild;\n setAllEnv: (newEnv: Readonly<Record<string, unknown>>) => void;\n config: Omit<Required<Config>, 'vite'>;\n isBuild: boolean;\n notFoundHtml: string;\n },\n options?: Options,\n ) => Unstable_ServerEntry,\n) => (handlers: Unstable_Handlers, options?: Options) => Unstable_ServerEntry;\n"],"names":[],"mappings":"AAyGA,WAa8E"}
@@ -0,0 +1,8 @@
1
+ export declare const ETAG_ID_PREFIX = "_etag:";
2
+ export declare const ETAGS_HEADER = "X-Waku-Etags";
3
+ export declare const IMMUTABLE_ETAG = 1;
4
+ export type Etag = string | typeof IMMUTABLE_ETAG;
5
+ export type Etags = Record<string, Etag>;
6
+ export declare const isValidEtag: (value: unknown) => value is Etag;
7
+ export declare const parseClientEtags: (serialized: string | null) => Etags;
8
+ export declare const serializeClientEtags: (etags: Etags) => string;
@@ -0,0 +1,30 @@
1
+ export const ETAG_ID_PREFIX = '_etag:';
2
+ export const ETAGS_HEADER = 'X-Waku-Etags';
3
+ // Non-string sentinel, so it cannot collide with a (string) content validator.
4
+ export const IMMUTABLE_ETAG = 1;
5
+ // '' clears a tag; only printable Latin-1 (no control chars) is header-safe
6
+ export const isValidEtag = (value)=>value === IMMUTABLE_ETAG || typeof value === 'string' && value !== '' && /^[\x20-\x7e\xa0-\xff]+$/.test(value);
7
+ export const parseClientEtags = (serialized)=>{
8
+ if (!serialized) {
9
+ return {};
10
+ }
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse(serialized);
14
+ } catch {
15
+ return {};
16
+ }
17
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
18
+ return {};
19
+ }
20
+ const etags = {};
21
+ for (const [key, value] of Object.entries(parsed)){
22
+ if (isValidEtag(value)) {
23
+ etags[key] = value;
24
+ }
25
+ }
26
+ return etags;
27
+ };
28
+ export const serializeClientEtags = (etags)=>JSON.stringify(etags);
29
+
30
+ //# sourceMappingURL=etags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/utils/etags.ts"],"sourcesContent":["export const ETAG_ID_PREFIX = '_etag:';\nexport const ETAGS_HEADER = 'X-Waku-Etags';\n\n// Non-string sentinel, so it cannot collide with a (string) content validator.\nexport const IMMUTABLE_ETAG = 1;\n\nexport type Etag = string | typeof IMMUTABLE_ETAG;\nexport type Etags = Record<string, Etag>;\n\n// '' clears a tag; only printable Latin-1 (no control chars) is header-safe\nexport const isValidEtag = (value: unknown): value is Etag =>\n value === IMMUTABLE_ETAG ||\n (typeof value === 'string' &&\n value !== '' &&\n /^[\\x20-\\x7e\\xa0-\\xff]+$/.test(value));\n\nexport const parseClientEtags = (serialized: string | null): Etags => {\n if (!serialized) {\n return {};\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized);\n } catch {\n return {};\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return {};\n }\n const etags: Etags = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (isValidEtag(value)) {\n etags[key] = value;\n }\n }\n return etags;\n};\n\nexport const serializeClientEtags = (etags: Etags): string =>\n JSON.stringify(etags);\n"],"names":["ETAG_ID_PREFIX","ETAGS_HEADER","IMMUTABLE_ETAG","isValidEtag","value","test","parseClientEtags","serialized","parsed","JSON","parse","Array","isArray","etags","key","Object","entries","serializeClientEtags","stringify"],"mappings":"AAAA,OAAO,MAAMA,iBAAiB,SAAS;AACvC,OAAO,MAAMC,eAAe,eAAe;AAE3C,+EAA+E;AAC/E,OAAO,MAAMC,iBAAiB,EAAE;AAKhC,4EAA4E;AAC5E,OAAO,MAAMC,cAAc,CAACC,QAC1BA,UAAUF,kBACT,OAAOE,UAAU,YAChBA,UAAU,MACV,0BAA0BC,IAAI,CAACD,OAAQ;AAE3C,OAAO,MAAME,mBAAmB,CAACC;IAC/B,IAAI,CAACA,YAAY;QACf,OAAO,CAAC;IACV;IACA,IAAIC;IACJ,IAAI;QACFA,SAASC,KAAKC,KAAK,CAACH;IACtB,EAAE,OAAM;QACN,OAAO,CAAC;IACV;IACA,IAAI,OAAOC,WAAW,YAAYA,WAAW,QAAQG,MAAMC,OAAO,CAACJ,SAAS;QAC1E,OAAO,CAAC;IACV;IACA,MAAMK,QAAe,CAAC;IACtB,KAAK,MAAM,CAACC,KAAKV,MAAM,IAAIW,OAAOC,OAAO,CAACR,QAAS;QACjD,IAAIL,YAAYC,QAAQ;YACtBS,KAAK,CAACC,IAAI,GAAGV;QACf;IACF;IACA,OAAOS;AACT,EAAE;AAEF,OAAO,MAAMI,uBAAuB,CAACJ,QACnCJ,KAAKS,SAAS,CAACL,OAAO"}
@@ -4,46 +4,5 @@ export declare const filePathToFileURL: (filePath: string) => string;
4
4
  export declare const fileURLToFilePath: (fileURL: string) => string;
5
5
  export declare const joinPath: (...paths: string[]) => string;
6
6
  export declare const extname: (filePath: string) => string;
7
- export type PathSpecItem = {
8
- type: 'literal';
9
- name: string;
10
- } | {
11
- type: 'group';
12
- name?: string;
13
- prefix?: string;
14
- suffix?: string;
15
- } | {
16
- type: 'wildcard';
17
- name?: string;
18
- };
19
- export type PathSpec = readonly PathSpecItem[];
20
- export declare const parsePathWithSlug: (path: string) => PathSpec;
21
- export declare const parseExactPath: (path: string) => PathSpec;
22
- /**
23
- * Transform a path spec to a regular expression.
24
- */
25
- export declare const path2regexp: (path: PathSpec) => string;
26
- /** Convert a path spec to a string for the path */
27
- export declare const pathSpecAsString: (path: PathSpec) => string;
28
- /**
29
- * Helper function to get the path mapping from the path spec and the pathname.
30
- *
31
- * @param pathSpec
32
- * @param pathname - route as a string
33
- * @example
34
- * getPathMapping(
35
- * [
36
- * { type: 'literal', name: 'foo' },
37
- * { type: 'group', name: 'a' },
38
- * ],
39
- * '/foo/bar',
40
- * );
41
- * // => { a: 'bar' }
42
- */
43
- export declare const getPathMapping: (pathSpec: PathSpec, pathname: string) => Record<string, string | string[]> | null;
44
7
  export declare function removeBase(url: string, base: string): string;
45
8
  export declare function addBase(url: string, base: string): string;
46
- export declare function countSlugsAndWildcards(pathSpec: PathSpec): {
47
- numSlugs: number;
48
- numWildcards: number;
49
- };
@@ -59,168 +59,6 @@ export const extname = (filePath)=>{
59
59
  }
60
60
  return filePath.slice(index);
61
61
  };
62
- const SLUG_PATTERN = /^(.*?)\[([^\]]+)\](.*)$/;
63
- export const parsePathWithSlug = (path)=>path.split('/').filter(Boolean).map((name)=>{
64
- const match = SLUG_PATTERN.exec(name);
65
- if (!match) {
66
- return {
67
- type: 'literal',
68
- name
69
- };
70
- }
71
- const [, prefix, inner, suffix] = match;
72
- if (inner.startsWith('...')) {
73
- return {
74
- type: 'wildcard',
75
- name: inner.slice(3)
76
- };
77
- }
78
- return {
79
- type: 'group',
80
- name: inner,
81
- ...prefix ? {
82
- prefix
83
- } : {},
84
- ...suffix ? {
85
- suffix
86
- } : {}
87
- };
88
- });
89
- export const parseExactPath = (path)=>path.split('/').filter(Boolean).map((name)=>({
90
- type: 'literal',
91
- name
92
- }));
93
- const escapeRegExp = (s)=>s.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
94
- /**
95
- * Transform a path spec to a regular expression.
96
- */ export const path2regexp = (path)=>{
97
- const parts = path.map((item)=>{
98
- if (item.type === 'literal') {
99
- return escapeRegExp(item.name);
100
- } else if (item.type === 'group') {
101
- const prefix = escapeRegExp(item.prefix ?? '');
102
- const suffix = escapeRegExp(item.suffix ?? '');
103
- return `${prefix}([^/]+)${suffix}`;
104
- } else {
105
- return `(.*)`;
106
- }
107
- });
108
- return `^/${parts.join('/')}$`;
109
- };
110
- /** Convert a path spec to a string for the path */ export const pathSpecAsString = (path)=>{
111
- return '/' + path.map((item)=>{
112
- if (item.type === 'literal') {
113
- return item.name;
114
- } else if (item.type === 'group') {
115
- const prefix = item.prefix ?? '';
116
- const suffix = item.suffix ?? '';
117
- return `${prefix}[${item.name}]${suffix}`;
118
- } else {
119
- return `[...${item.name}]`;
120
- }
121
- }).join('/');
122
- };
123
- const matchSpecSegment = (spec, segment, mapping)=>{
124
- if (spec.type === 'literal') {
125
- return spec.name === segment;
126
- }
127
- if (segment === undefined) {
128
- return false;
129
- }
130
- const prefix = spec.prefix ?? '';
131
- const suffix = spec.suffix ?? '';
132
- if (prefix || suffix) {
133
- if (!segment.startsWith(prefix) || !segment.endsWith(suffix)) {
134
- return false;
135
- }
136
- const value = segment.slice(prefix.length, suffix ? -suffix.length : undefined);
137
- if (!value) {
138
- return false;
139
- }
140
- if (spec.name) {
141
- mapping[spec.name] = value;
142
- }
143
- } else if (spec.name) {
144
- mapping[spec.name] = segment;
145
- }
146
- return true;
147
- };
148
- /**
149
- * Helper function to get the path mapping from the path spec and the pathname.
150
- *
151
- * @param pathSpec
152
- * @param pathname - route as a string
153
- * @example
154
- * getPathMapping(
155
- * [
156
- * { type: 'literal', name: 'foo' },
157
- * { type: 'group', name: 'a' },
158
- * ],
159
- * '/foo/bar',
160
- * );
161
- * // => { a: 'bar' }
162
- */ export const getPathMapping = (pathSpec, pathname)=>{
163
- const actual = pathname.split('/').filter(Boolean);
164
- if (pathSpec.length > actual.length) {
165
- const wildcardIndex = pathSpec.findIndex((spec)=>spec.type === 'wildcard');
166
- if (wildcardIndex === -1) {
167
- return null;
168
- }
169
- const isTerminalWildcard = wildcardIndex === pathSpec.length - 1;
170
- if (isTerminalWildcard) {
171
- // Terminal wildcards only pass through with zero actual segments
172
- // (handled by the root-wildcard special case below)
173
- if (actual.length > 0) {
174
- return null;
175
- }
176
- } else if (actual.length < pathSpec.length - 1) {
177
- // Non-terminal wildcards can match zero segments; just need enough
178
- // actual segments for every non-wildcard spec in the pathSpec
179
- return null;
180
- }
181
- }
182
- const mapping = {};
183
- let wildcardStartIndex = -1;
184
- for(let i = 0; i < pathSpec.length; i++){
185
- const spec = pathSpec[i];
186
- if (spec.type === 'wildcard') {
187
- wildcardStartIndex = i;
188
- break;
189
- }
190
- if (!matchSpecSegment(spec, actual[i], mapping)) {
191
- return null;
192
- }
193
- }
194
- if (wildcardStartIndex === -1) {
195
- if (pathSpec.length !== actual.length) {
196
- return null;
197
- }
198
- return mapping;
199
- }
200
- if (wildcardStartIndex === 0 && actual.length === 0) {
201
- const wildcardName = pathSpec[wildcardStartIndex].name;
202
- if (wildcardName) {
203
- mapping[wildcardName] = [];
204
- }
205
- return mapping;
206
- }
207
- let wildcardEndIndex = -1;
208
- for(let i = 0; i < pathSpec.length; i++){
209
- const spec = pathSpec[pathSpec.length - i - 1];
210
- if (spec.type === 'wildcard') {
211
- wildcardEndIndex = actual.length - i - 1;
212
- break;
213
- }
214
- if (!matchSpecSegment(spec, actual[actual.length - i - 1], mapping)) {
215
- return null;
216
- }
217
- }
218
- const wildcardName = pathSpec[wildcardStartIndex].name;
219
- if (wildcardName) {
220
- mapping[wildcardName] = actual.slice(wildcardStartIndex, wildcardEndIndex + 1);
221
- }
222
- return mapping;
223
- };
224
62
  // basePath config is ensured to have trailing slash (see plugin)
225
63
  export function removeBase(url, base) {
226
64
  if (base !== '/') {
@@ -237,21 +75,5 @@ export function addBase(url, base) {
237
75
  }
238
76
  return url;
239
77
  }
240
- export function countSlugsAndWildcards(pathSpec) {
241
- let numSlugs = 0;
242
- let numWildcards = 0;
243
- for (const slug of pathSpec){
244
- if (slug.type !== 'literal') {
245
- numSlugs++;
246
- }
247
- if (slug.type === 'wildcard') {
248
- numWildcards++;
249
- }
250
- }
251
- return {
252
- numSlugs,
253
- numWildcards
254
- };
255
- }
256
78
 
257
79
  //# sourceMappingURL=path.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/path.ts"],"sourcesContent":["// Terminology:\n// - filePath: posix-like file path, e.g. `/foo/bar.js` or `c:/foo/bar.js`\n// This is used by Vite.\n// - fileURL: file URL, e.g. `file:///foo/bar.js` or `file:///c:/foo/bar.js`\n// This is used by import().\n// - osPath: os dependent path, e.g. `/foo/bar.js` or `c:\\foo\\bar.js`\n// This is used by node:fs.\n\nconst ABSOLUTE_WIN32_PATH_REGEXP = /^\\/[a-zA-Z]:\\//;\n\nexport const encodeFilePathToAbsolute = (filePath: string) => {\n if (ABSOLUTE_WIN32_PATH_REGEXP.test(filePath)) {\n throw new Error('Unsupported absolute file path: ' + filePath);\n }\n if (filePath.startsWith('/')) {\n return filePath;\n }\n return '/' + filePath;\n};\n\nexport const decodeFilePathFromAbsolute = (filePath: string) => {\n if (ABSOLUTE_WIN32_PATH_REGEXP.test(filePath)) {\n return filePath.slice(1);\n }\n return filePath;\n};\n\nexport const filePathToFileURL = (filePath: string) =>\n 'file://' + encodeURI(filePath);\n\nexport const fileURLToFilePath = (fileURL: string) => {\n if (!fileURL.startsWith('file://')) {\n throw new Error('Not a file URL');\n }\n return decodeURI(fileURL.slice('file://'.length));\n};\n\n// for filePath\nexport const joinPath = (...paths: string[]) => {\n const isAbsolute = paths[0]?.startsWith('/');\n const items = ([] as string[]).concat(\n ...paths.map((path) => path.split('/')),\n );\n const stack: string[] = [];\n for (const item of items) {\n if (item === '..') {\n if (stack.length && stack[stack.length - 1] !== '..') {\n stack.pop();\n } else if (!isAbsolute) {\n stack.push('..');\n }\n } else if (item && item !== '.') {\n stack.push(item);\n }\n }\n return (isAbsolute ? '/' : '') + stack.join('/') || '.';\n};\n\nexport const extname = (filePath: string) => {\n const index = filePath.lastIndexOf('.');\n if (index <= 0) {\n return '';\n }\n if (['/', '.'].includes(filePath[index - 1]!)) {\n return '';\n }\n return filePath.slice(index);\n};\n\nexport type PathSpecItem =\n | { type: 'literal'; name: string }\n | { type: 'group'; name?: string; prefix?: string; suffix?: string }\n | { type: 'wildcard'; name?: string };\nexport type PathSpec = readonly PathSpecItem[];\n\nconst SLUG_PATTERN = /^(.*?)\\[([^\\]]+)\\](.*)$/;\n\nexport const parsePathWithSlug = (path: string): PathSpec =>\n path\n .split('/')\n .filter(Boolean)\n .map((name) => {\n const match = SLUG_PATTERN.exec(name);\n if (!match) {\n return { type: 'literal' as const, name };\n }\n const [, prefix, inner, suffix] = match;\n if (inner!.startsWith('...')) {\n return {\n type: 'wildcard' as const,\n name: inner!.slice(3),\n };\n }\n return {\n type: 'group' as const,\n name: inner!,\n ...(prefix ? { prefix } : {}),\n ...(suffix ? { suffix } : {}),\n };\n });\n\nexport const parseExactPath = (path: string): PathSpec =>\n path\n .split('/')\n .filter(Boolean)\n .map((name) => ({ type: 'literal', name }));\n\nconst escapeRegExp = (s: string) => s.replace(/[\\\\^$.*+?()[\\]{}|]/g, '\\\\$&');\n\n/**\n * Transform a path spec to a regular expression.\n */\nexport const path2regexp = (path: PathSpec) => {\n const parts = path.map((item) => {\n if (item.type === 'literal') {\n return escapeRegExp(item.name);\n } else if (item.type === 'group') {\n const prefix = escapeRegExp(item.prefix ?? '');\n const suffix = escapeRegExp(item.suffix ?? '');\n return `${prefix}([^/]+)${suffix}`;\n } else {\n return `(.*)`;\n }\n });\n return `^/${parts.join('/')}$`;\n};\n\n/** Convert a path spec to a string for the path */\nexport const pathSpecAsString = (path: PathSpec) => {\n return (\n '/' +\n path\n .map((item) => {\n if (item.type === 'literal') {\n return item.name;\n } else if (item.type === 'group') {\n const prefix = item.prefix ?? '';\n const suffix = item.suffix ?? '';\n return `${prefix}[${item.name}]${suffix}`;\n } else {\n return `[...${item.name}]`;\n }\n })\n .join('/')\n );\n};\n\nconst matchSpecSegment = (\n spec: Exclude<PathSpecItem, { type: 'wildcard' }>,\n segment: string | undefined,\n mapping: Record<string, string | string[]>,\n): boolean => {\n if (spec.type === 'literal') {\n return spec.name === segment;\n }\n if (segment === undefined) {\n return false;\n }\n const prefix = spec.prefix ?? '';\n const suffix = spec.suffix ?? '';\n if (prefix || suffix) {\n if (!segment.startsWith(prefix) || !segment.endsWith(suffix)) {\n return false;\n }\n const value = segment.slice(\n prefix.length,\n suffix ? -suffix.length : undefined,\n );\n if (!value) {\n return false;\n }\n if (spec.name) {\n mapping[spec.name] = value;\n }\n } else if (spec.name) {\n mapping[spec.name] = segment;\n }\n return true;\n};\n\n/**\n * Helper function to get the path mapping from the path spec and the pathname.\n *\n * @param pathSpec\n * @param pathname - route as a string\n * @example\n * getPathMapping(\n * [\n * { type: 'literal', name: 'foo' },\n * { type: 'group', name: 'a' },\n * ],\n * '/foo/bar',\n * );\n * // => { a: 'bar' }\n */\nexport const getPathMapping = (\n pathSpec: PathSpec,\n pathname: string,\n): Record<string, string | string[]> | null => {\n const actual = pathname.split('/').filter(Boolean);\n if (pathSpec.length > actual.length) {\n const wildcardIndex = pathSpec.findIndex(\n (spec) => spec.type === 'wildcard',\n );\n if (wildcardIndex === -1) {\n return null;\n }\n const isTerminalWildcard = wildcardIndex === pathSpec.length - 1;\n if (isTerminalWildcard) {\n // Terminal wildcards only pass through with zero actual segments\n // (handled by the root-wildcard special case below)\n if (actual.length > 0) {\n return null;\n }\n } else if (actual.length < pathSpec.length - 1) {\n // Non-terminal wildcards can match zero segments; just need enough\n // actual segments for every non-wildcard spec in the pathSpec\n return null;\n }\n }\n const mapping: Record<string, string | string[]> = {};\n let wildcardStartIndex = -1;\n for (let i = 0; i < pathSpec.length; i++) {\n const spec = pathSpec[i]!;\n if (spec.type === 'wildcard') {\n wildcardStartIndex = i;\n break;\n }\n if (!matchSpecSegment(spec, actual[i], mapping)) {\n return null;\n }\n }\n if (wildcardStartIndex === -1) {\n if (pathSpec.length !== actual.length) {\n return null;\n }\n return mapping;\n }\n\n if (wildcardStartIndex === 0 && actual.length === 0) {\n const wildcardName = pathSpec[wildcardStartIndex]!.name;\n if (wildcardName) {\n mapping[wildcardName] = [];\n }\n return mapping;\n }\n\n let wildcardEndIndex = -1;\n for (let i = 0; i < pathSpec.length; i++) {\n const spec = pathSpec[pathSpec.length - i - 1]!;\n if (spec.type === 'wildcard') {\n wildcardEndIndex = actual.length - i - 1;\n break;\n }\n if (!matchSpecSegment(spec, actual[actual.length - i - 1], mapping)) {\n return null;\n }\n }\n const wildcardName = pathSpec[wildcardStartIndex]!.name;\n if (wildcardName) {\n mapping[wildcardName] = actual.slice(\n wildcardStartIndex,\n wildcardEndIndex + 1,\n );\n }\n return mapping;\n};\n\n// basePath config is ensured to have trailing slash (see plugin)\nexport function removeBase(url: string, base: string) {\n if (base !== '/') {\n if (!url.startsWith(base)) {\n throw new Error('pathname must start with basePath: ' + url);\n }\n return url.slice(base.length - 1);\n }\n return url;\n}\n\nexport function addBase(url: string, base: string) {\n if (base !== '/' && url.startsWith('/')) {\n return base.slice(0, -1) + url;\n }\n return url;\n}\n\nexport function countSlugsAndWildcards(pathSpec: PathSpec) {\n let numSlugs = 0;\n let numWildcards = 0;\n for (const slug of pathSpec) {\n if (slug.type !== 'literal') {\n numSlugs++;\n }\n if (slug.type === 'wildcard') {\n numWildcards++;\n }\n }\n return { numSlugs, numWildcards };\n}\n"],"names":["ABSOLUTE_WIN32_PATH_REGEXP","encodeFilePathToAbsolute","filePath","test","Error","startsWith","decodeFilePathFromAbsolute","slice","filePathToFileURL","encodeURI","fileURLToFilePath","fileURL","decodeURI","length","joinPath","paths","isAbsolute","items","concat","map","path","split","stack","item","pop","push","join","extname","index","lastIndexOf","includes","SLUG_PATTERN","parsePathWithSlug","filter","Boolean","name","match","exec","type","prefix","inner","suffix","parseExactPath","escapeRegExp","s","replace","path2regexp","parts","pathSpecAsString","matchSpecSegment","spec","segment","mapping","undefined","endsWith","value","getPathMapping","pathSpec","pathname","actual","wildcardIndex","findIndex","isTerminalWildcard","wildcardStartIndex","i","wildcardName","wildcardEndIndex","removeBase","url","base","addBase","countSlugsAndWildcards","numSlugs","numWildcards","slug"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/lib/utils/path.ts"],"sourcesContent":["// Terminology:\n// - filePath: posix-like file path, e.g. `/foo/bar.js` or `c:/foo/bar.js`\n// This is used by Vite.\n// - fileURL: file URL, e.g. `file:///foo/bar.js` or `file:///c:/foo/bar.js`\n// This is used by import().\n// - osPath: os dependent path, e.g. `/foo/bar.js` or `c:\\foo\\bar.js`\n// This is used by node:fs.\n\nconst ABSOLUTE_WIN32_PATH_REGEXP = /^\\/[a-zA-Z]:\\//;\n\nexport const encodeFilePathToAbsolute = (filePath: string) => {\n if (ABSOLUTE_WIN32_PATH_REGEXP.test(filePath)) {\n throw new Error('Unsupported absolute file path: ' + filePath);\n }\n if (filePath.startsWith('/')) {\n return filePath;\n }\n return '/' + filePath;\n};\n\nexport const decodeFilePathFromAbsolute = (filePath: string) => {\n if (ABSOLUTE_WIN32_PATH_REGEXP.test(filePath)) {\n return filePath.slice(1);\n }\n return filePath;\n};\n\nexport const filePathToFileURL = (filePath: string) =>\n 'file://' + encodeURI(filePath);\n\nexport const fileURLToFilePath = (fileURL: string) => {\n if (!fileURL.startsWith('file://')) {\n throw new Error('Not a file URL');\n }\n return decodeURI(fileURL.slice('file://'.length));\n};\n\n// for filePath\nexport const joinPath = (...paths: string[]) => {\n const isAbsolute = paths[0]?.startsWith('/');\n const items = ([] as string[]).concat(\n ...paths.map((path) => path.split('/')),\n );\n const stack: string[] = [];\n for (const item of items) {\n if (item === '..') {\n if (stack.length && stack[stack.length - 1] !== '..') {\n stack.pop();\n } else if (!isAbsolute) {\n stack.push('..');\n }\n } else if (item && item !== '.') {\n stack.push(item);\n }\n }\n return (isAbsolute ? '/' : '') + stack.join('/') || '.';\n};\n\nexport const extname = (filePath: string) => {\n const index = filePath.lastIndexOf('.');\n if (index <= 0) {\n return '';\n }\n if (['/', '.'].includes(filePath[index - 1]!)) {\n return '';\n }\n return filePath.slice(index);\n};\n\n// basePath config is ensured to have trailing slash (see plugin)\nexport function removeBase(url: string, base: string) {\n if (base !== '/') {\n if (!url.startsWith(base)) {\n throw new Error('pathname must start with basePath: ' + url);\n }\n return url.slice(base.length - 1);\n }\n return url;\n}\n\nexport function addBase(url: string, base: string) {\n if (base !== '/' && url.startsWith('/')) {\n return base.slice(0, -1) + url;\n }\n return url;\n}\n"],"names":["ABSOLUTE_WIN32_PATH_REGEXP","encodeFilePathToAbsolute","filePath","test","Error","startsWith","decodeFilePathFromAbsolute","slice","filePathToFileURL","encodeURI","fileURLToFilePath","fileURL","decodeURI","length","joinPath","paths","isAbsolute","items","concat","map","path","split","stack","item","pop","push","join","extname","index","lastIndexOf","includes","removeBase","url","base","addBase"],"mappings":"AAAA,eAAe;AACf,0EAA0E;AAC1E,0BAA0B;AAC1B,4EAA4E;AAC5E,8BAA8B;AAC9B,qEAAqE;AACrE,6BAA6B;AAE7B,MAAMA,6BAA6B;AAEnC,OAAO,MAAMC,2BAA2B,CAACC;IACvC,IAAIF,2BAA2BG,IAAI,CAACD,WAAW;QAC7C,MAAM,IAAIE,MAAM,qCAAqCF;IACvD;IACA,IAAIA,SAASG,UAAU,CAAC,MAAM;QAC5B,OAAOH;IACT;IACA,OAAO,MAAMA;AACf,EAAE;AAEF,OAAO,MAAMI,6BAA6B,CAACJ;IACzC,IAAIF,2BAA2BG,IAAI,CAACD,WAAW;QAC7C,OAAOA,SAASK,KAAK,CAAC;IACxB;IACA,OAAOL;AACT,EAAE;AAEF,OAAO,MAAMM,oBAAoB,CAACN,WAChC,YAAYO,UAAUP,UAAU;AAElC,OAAO,MAAMQ,oBAAoB,CAACC;IAChC,IAAI,CAACA,QAAQN,UAAU,CAAC,YAAY;QAClC,MAAM,IAAID,MAAM;IAClB;IACA,OAAOQ,UAAUD,QAAQJ,KAAK,CAAC,UAAUM,MAAM;AACjD,EAAE;AAEF,eAAe;AACf,OAAO,MAAMC,WAAW,CAAC,GAAGC;IAC1B,MAAMC,aAAaD,KAAK,CAAC,EAAE,EAAEV,WAAW;IACxC,MAAMY,QAAQ,AAAC,EAAE,CAAcC,MAAM,IAChCH,MAAMI,GAAG,CAAC,CAACC,OAASA,KAAKC,KAAK,CAAC;IAEpC,MAAMC,QAAkB,EAAE;IAC1B,KAAK,MAAMC,QAAQN,MAAO;QACxB,IAAIM,SAAS,MAAM;YACjB,IAAID,MAAMT,MAAM,IAAIS,KAAK,CAACA,MAAMT,MAAM,GAAG,EAAE,KAAK,MAAM;gBACpDS,MAAME,GAAG;YACX,OAAO,IAAI,CAACR,YAAY;gBACtBM,MAAMG,IAAI,CAAC;YACb;QACF,OAAO,IAAIF,QAAQA,SAAS,KAAK;YAC/BD,MAAMG,IAAI,CAACF;QACb;IACF;IACA,OAAO,AAACP,CAAAA,aAAa,MAAM,EAAC,IAAKM,MAAMI,IAAI,CAAC,QAAQ;AACtD,EAAE;AAEF,OAAO,MAAMC,UAAU,CAACzB;IACtB,MAAM0B,QAAQ1B,SAAS2B,WAAW,CAAC;IACnC,IAAID,SAAS,GAAG;QACd,OAAO;IACT;IACA,IAAI;QAAC;QAAK;KAAI,CAACE,QAAQ,CAAC5B,QAAQ,CAAC0B,QAAQ,EAAE,GAAI;QAC7C,OAAO;IACT;IACA,OAAO1B,SAASK,KAAK,CAACqB;AACxB,EAAE;AAEF,iEAAiE;AACjE,OAAO,SAASG,WAAWC,GAAW,EAAEC,IAAY;IAClD,IAAIA,SAAS,KAAK;QAChB,IAAI,CAACD,IAAI3B,UAAU,CAAC4B,OAAO;YACzB,MAAM,IAAI7B,MAAM,wCAAwC4B;QAC1D;QACA,OAAOA,IAAIzB,KAAK,CAAC0B,KAAKpB,MAAM,GAAG;IACjC;IACA,OAAOmB;AACT;AAEA,OAAO,SAASE,QAAQF,GAAW,EAAEC,IAAY;IAC/C,IAAIA,SAAS,OAAOD,IAAI3B,UAAU,CAAC,MAAM;QACvC,OAAO4B,KAAK1B,KAAK,CAAC,GAAG,CAAC,KAAKyB;IAC7B;IACA,OAAOA;AACT"}
@@ -1,3 +1,4 @@
1
+ import { ETAG_ID_PREFIX } from './etags.js';
1
2
  import { sanitizeLog } from './log.js';
2
3
  const validateRscElementIds = (elements)=>{
3
4
  for (const id of Object.keys(elements)){
@@ -25,6 +26,11 @@ export function createRenderUtils(temporaryReferences, renderToReadableStream, c
25
26
  if (options && 'value' in options) {
26
27
  data._value = options.value;
27
28
  }
29
+ if (options?.etags) {
30
+ for (const [slotId, etag] of Object.entries(options.etags)){
31
+ data[ETAG_ID_PREFIX + slotId] = etag;
32
+ }
33
+ }
28
34
  return renderToReadableStream(data, {
29
35
  temporaryReferences,
30
36
  onError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/utils/render.ts"],"sourcesContent":["import type {\n Unstable_ParseRsc,\n Unstable_RenderHtml,\n Unstable_RenderRsc,\n} from '../types.js';\nimport { sanitizeLog } from './log.js';\n\nconst validateRscElementIds = (elements: Record<string, unknown>) => {\n for (const id of Object.keys(elements)) {\n if (id.startsWith('_')) {\n throw new Error(\n `RSC element IDs starting with \"_\" are reserved for Waku internals: ${id}`,\n );\n }\n }\n};\n\nexport function createRenderUtils(\n temporaryReferences: unknown,\n renderToReadableStream: (\n data: unknown,\n options?: object,\n extraOptions?: object,\n ) => ReadableStream,\n createFromReadableStream: (\n stream: ReadableStream,\n options?: object,\n ) => Promise<unknown>,\n loadSsrEntryModule: () => Promise<\n typeof import('../vite-entries/entry.ssr.js')\n >,\n buildId: string,\n debugChannel?: { readable?: ReadableStream; writable?: WritableStream },\n debugId?: string,\n): {\n renderRsc: Unstable_RenderRsc;\n parseRsc: Unstable_ParseRsc;\n renderHtml: Unstable_RenderHtml;\n} {\n const onError = (e: unknown) => {\n if (\n e &&\n typeof e === 'object' &&\n 'digest' in e &&\n typeof e.digest === 'string'\n ) {\n return e.digest;\n }\n console.error('Error during rendering:', sanitizeLog(e));\n };\n\n return {\n async renderRsc(elements, options) {\n validateRscElementIds(elements);\n const data: Record<string, unknown> = buildId\n ? { ...elements, _buildId: buildId }\n : { ...elements };\n if (options && 'value' in options) {\n data._value = options.value;\n }\n return renderToReadableStream(\n data,\n {\n temporaryReferences,\n onError,\n debugChannel,\n },\n {\n onClientReference(metadata: {\n id: string;\n name: string;\n deps: { js: string[]; css: string[] };\n }) {\n options?.unstable_clientModuleCallback?.(metadata.deps.js);\n },\n },\n );\n },\n async parseRsc(stream) {\n return createFromReadableStream(stream, {}) as Promise<\n Record<string, unknown>\n >;\n },\n async renderHtml(elementsStream, html, options) {\n const { INTERNAL_renderHtmlStream: renderHtmlStream } =\n await loadSsrEntryModule();\n\n const rscHtmlStream = renderToReadableStream(html, {\n onError,\n });\n const htmlResult = await renderHtmlStream(elementsStream, rscHtmlStream, {\n rscPath: options.rscPath,\n formState: options.formState as never,\n nonce: options.nonce,\n extraScriptContent: options.unstable_extraScriptContent,\n debugId,\n });\n return new Response(htmlResult.stream, {\n status: htmlResult.status || options.status || 200,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n });\n },\n };\n}\n"],"names":["sanitizeLog","validateRscElementIds","elements","id","Object","keys","startsWith","Error","createRenderUtils","temporaryReferences","renderToReadableStream","createFromReadableStream","loadSsrEntryModule","buildId","debugChannel","debugId","onError","e","digest","console","error","renderRsc","options","data","_buildId","_value","value","onClientReference","metadata","unstable_clientModuleCallback","deps","js","parseRsc","stream","renderHtml","elementsStream","html","INTERNAL_renderHtmlStream","renderHtmlStream","rscHtmlStream","htmlResult","rscPath","formState","nonce","extraScriptContent","unstable_extraScriptContent","Response","status","headers"],"mappings":"AAKA,SAASA,WAAW,QAAQ,WAAW;AAEvC,MAAMC,wBAAwB,CAACC;IAC7B,KAAK,MAAMC,MAAMC,OAAOC,IAAI,CAACH,UAAW;QACtC,IAAIC,GAAGG,UAAU,CAAC,MAAM;YACtB,MAAM,IAAIC,MACR,CAAC,mEAAmE,EAAEJ,IAAI;QAE9E;IACF;AACF;AAEA,OAAO,SAASK,kBACdC,mBAA4B,EAC5BC,sBAImB,EACnBC,wBAGqB,EACrBC,kBAEC,EACDC,OAAe,EACfC,YAAuE,EACvEC,OAAgB;IAMhB,MAAMC,UAAU,CAACC;QACf,IACEA,KACA,OAAOA,MAAM,YACb,YAAYA,KACZ,OAAOA,EAAEC,MAAM,KAAK,UACpB;YACA,OAAOD,EAAEC,MAAM;QACjB;QACAC,QAAQC,KAAK,CAAC,2BAA2BpB,YAAYiB;IACvD;IAEA,OAAO;QACL,MAAMI,WAAUnB,QAAQ,EAAEoB,OAAO;YAC/BrB,sBAAsBC;YACtB,MAAMqB,OAAgCV,UAClC;gBAAE,GAAGX,QAAQ;gBAAEsB,UAAUX;YAAQ,IACjC;gBAAE,GAAGX,QAAQ;YAAC;YAClB,IAAIoB,WAAW,WAAWA,SAAS;gBACjCC,KAAKE,MAAM,GAAGH,QAAQI,KAAK;YAC7B;YACA,OAAOhB,uBACLa,MACA;gBACEd;gBACAO;gBACAF;YACF,GACA;gBACEa,mBAAkBC,QAIjB;oBACCN,SAASO,gCAAgCD,SAASE,IAAI,CAACC,EAAE;gBAC3D;YACF;QAEJ;QACA,MAAMC,UAASC,MAAM;YACnB,OAAOtB,yBAAyBsB,QAAQ,CAAC;QAG3C;QACA,MAAMC,YAAWC,cAAc,EAAEC,IAAI,EAAEd,OAAO;YAC5C,MAAM,EAAEe,2BAA2BC,gBAAgB,EAAE,GACnD,MAAM1B;YAER,MAAM2B,gBAAgB7B,uBAAuB0B,MAAM;gBACjDpB;YACF;YACA,MAAMwB,aAAa,MAAMF,iBAAiBH,gBAAgBI,eAAe;gBACvEE,SAASnB,QAAQmB,OAAO;gBACxBC,WAAWpB,QAAQoB,SAAS;gBAC5BC,OAAOrB,QAAQqB,KAAK;gBACpBC,oBAAoBtB,QAAQuB,2BAA2B;gBACvD9B;YACF;YACA,OAAO,IAAI+B,SAASN,WAAWP,MAAM,EAAE;gBACrCc,QAAQP,WAAWO,MAAM,IAAIzB,QAAQyB,MAAM,IAAI;gBAC/CC,SAAS;oBAAE,gBAAgB;gBAA2B;YACxD;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/lib/utils/render.ts"],"sourcesContent":["import type {\n Unstable_ParseRsc,\n Unstable_RenderHtml,\n Unstable_RenderRsc,\n} from '../types.js';\nimport { ETAG_ID_PREFIX } from './etags.js';\nimport { sanitizeLog } from './log.js';\n\nconst validateRscElementIds = (elements: Record<string, unknown>) => {\n for (const id of Object.keys(elements)) {\n if (id.startsWith('_')) {\n throw new Error(\n `RSC element IDs starting with \"_\" are reserved for Waku internals: ${id}`,\n );\n }\n }\n};\n\nexport function createRenderUtils(\n temporaryReferences: unknown,\n renderToReadableStream: (\n data: unknown,\n options?: object,\n extraOptions?: object,\n ) => ReadableStream,\n createFromReadableStream: (\n stream: ReadableStream,\n options?: object,\n ) => Promise<unknown>,\n loadSsrEntryModule: () => Promise<\n typeof import('../vite-entries/entry.ssr.js')\n >,\n buildId: string,\n debugChannel?: { readable?: ReadableStream; writable?: WritableStream },\n debugId?: string,\n): {\n renderRsc: Unstable_RenderRsc;\n parseRsc: Unstable_ParseRsc;\n renderHtml: Unstable_RenderHtml;\n} {\n const onError = (e: unknown) => {\n if (\n e &&\n typeof e === 'object' &&\n 'digest' in e &&\n typeof e.digest === 'string'\n ) {\n return e.digest;\n }\n console.error('Error during rendering:', sanitizeLog(e));\n };\n\n return {\n async renderRsc(elements, options) {\n validateRscElementIds(elements);\n const data: Record<string, unknown> = buildId\n ? { ...elements, _buildId: buildId }\n : { ...elements };\n if (options && 'value' in options) {\n data._value = options.value;\n }\n if (options?.etags) {\n for (const [slotId, etag] of Object.entries(options.etags)) {\n data[ETAG_ID_PREFIX + slotId] = etag;\n }\n }\n return renderToReadableStream(\n data,\n {\n temporaryReferences,\n onError,\n debugChannel,\n },\n {\n onClientReference(metadata: {\n id: string;\n name: string;\n deps: { js: string[]; css: string[] };\n }) {\n options?.unstable_clientModuleCallback?.(metadata.deps.js);\n },\n },\n );\n },\n async parseRsc(stream) {\n return createFromReadableStream(stream, {}) as Promise<\n Record<string, unknown>\n >;\n },\n async renderHtml(elementsStream, html, options) {\n const { INTERNAL_renderHtmlStream: renderHtmlStream } =\n await loadSsrEntryModule();\n\n const rscHtmlStream = renderToReadableStream(html, {\n onError,\n });\n const htmlResult = await renderHtmlStream(elementsStream, rscHtmlStream, {\n rscPath: options.rscPath,\n formState: options.formState as never,\n nonce: options.nonce,\n extraScriptContent: options.unstable_extraScriptContent,\n debugId,\n });\n return new Response(htmlResult.stream, {\n status: htmlResult.status || options.status || 200,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n });\n },\n };\n}\n"],"names":["ETAG_ID_PREFIX","sanitizeLog","validateRscElementIds","elements","id","Object","keys","startsWith","Error","createRenderUtils","temporaryReferences","renderToReadableStream","createFromReadableStream","loadSsrEntryModule","buildId","debugChannel","debugId","onError","e","digest","console","error","renderRsc","options","data","_buildId","_value","value","etags","slotId","etag","entries","onClientReference","metadata","unstable_clientModuleCallback","deps","js","parseRsc","stream","renderHtml","elementsStream","html","INTERNAL_renderHtmlStream","renderHtmlStream","rscHtmlStream","htmlResult","rscPath","formState","nonce","extraScriptContent","unstable_extraScriptContent","Response","status","headers"],"mappings":"AAKA,SAASA,cAAc,QAAQ,aAAa;AAC5C,SAASC,WAAW,QAAQ,WAAW;AAEvC,MAAMC,wBAAwB,CAACC;IAC7B,KAAK,MAAMC,MAAMC,OAAOC,IAAI,CAACH,UAAW;QACtC,IAAIC,GAAGG,UAAU,CAAC,MAAM;YACtB,MAAM,IAAIC,MACR,CAAC,mEAAmE,EAAEJ,IAAI;QAE9E;IACF;AACF;AAEA,OAAO,SAASK,kBACdC,mBAA4B,EAC5BC,sBAImB,EACnBC,wBAGqB,EACrBC,kBAEC,EACDC,OAAe,EACfC,YAAuE,EACvEC,OAAgB;IAMhB,MAAMC,UAAU,CAACC;QACf,IACEA,KACA,OAAOA,MAAM,YACb,YAAYA,KACZ,OAAOA,EAAEC,MAAM,KAAK,UACpB;YACA,OAAOD,EAAEC,MAAM;QACjB;QACAC,QAAQC,KAAK,CAAC,2BAA2BpB,YAAYiB;IACvD;IAEA,OAAO;QACL,MAAMI,WAAUnB,QAAQ,EAAEoB,OAAO;YAC/BrB,sBAAsBC;YACtB,MAAMqB,OAAgCV,UAClC;gBAAE,GAAGX,QAAQ;gBAAEsB,UAAUX;YAAQ,IACjC;gBAAE,GAAGX,QAAQ;YAAC;YAClB,IAAIoB,WAAW,WAAWA,SAAS;gBACjCC,KAAKE,MAAM,GAAGH,QAAQI,KAAK;YAC7B;YACA,IAAIJ,SAASK,OAAO;gBAClB,KAAK,MAAM,CAACC,QAAQC,KAAK,IAAIzB,OAAO0B,OAAO,CAACR,QAAQK,KAAK,EAAG;oBAC1DJ,IAAI,CAACxB,iBAAiB6B,OAAO,GAAGC;gBAClC;YACF;YACA,OAAOnB,uBACLa,MACA;gBACEd;gBACAO;gBACAF;YACF,GACA;gBACEiB,mBAAkBC,QAIjB;oBACCV,SAASW,gCAAgCD,SAASE,IAAI,CAACC,EAAE;gBAC3D;YACF;QAEJ;QACA,MAAMC,UAASC,MAAM;YACnB,OAAO1B,yBAAyB0B,QAAQ,CAAC;QAG3C;QACA,MAAMC,YAAWC,cAAc,EAAEC,IAAI,EAAElB,OAAO;YAC5C,MAAM,EAAEmB,2BAA2BC,gBAAgB,EAAE,GACnD,MAAM9B;YAER,MAAM+B,gBAAgBjC,uBAAuB8B,MAAM;gBACjDxB;YACF;YACA,MAAM4B,aAAa,MAAMF,iBAAiBH,gBAAgBI,eAAe;gBACvEE,SAASvB,QAAQuB,OAAO;gBACxBC,WAAWxB,QAAQwB,SAAS;gBAC5BC,OAAOzB,QAAQyB,KAAK;gBACpBC,oBAAoB1B,QAAQ2B,2BAA2B;gBACvDlC;YACF;YACA,OAAO,IAAImC,SAASN,WAAWP,MAAM,EAAE;gBACrCc,QAAQP,WAAWO,MAAM,IAAI7B,QAAQ6B,MAAM,IAAI;gBAC/CC,SAAS;oBAAE,gBAAgB;gBAA2B;YACxD;QACF;IACF;AACF"}
@@ -1,6 +1,6 @@
1
1
  import type { ReactFormState } from 'react-dom/client';
2
2
  import type { Config } from '../../config.js';
3
- export declare function getInput(req: Request, config: Omit<Required<Config>, 'vite'>, temporaryReferences: unknown, decodeReply: (body: string | FormData, options?: object) => Promise<unknown[]>, decodeAction: (body: FormData) => Promise<() => Promise<void>>, decodeFormState: (actionResult: unknown, body: FormData) => Promise<ReactFormState | undefined>, loadServerAction: (id: string) => Promise<unknown>): Promise<({
3
+ export declare function getInput(req: Request, config: Omit<Required<Config>, 'vite'>, temporaryReferences: unknown, decodeReply: (body: string | FormData, options?: object) => Promise<unknown[]>, decodeAction: (body: FormData) => Promise<() => Promise<void>> | null, decodeFormState: (actionResult: unknown, body: FormData) => Promise<ReactFormState | undefined>, loadServerAction: (id: string) => Promise<unknown>): Promise<({
4
4
  type: "component";
5
5
  rscPath: string;
6
6
  rscParams: unknown;
@@ -16,4 +16,5 @@ export declare function getInput(req: Request, config: Omit<Required<Config>, 'v
16
16
  }) & {
17
17
  pathname: string;
18
18
  req: Request;
19
+ etags?: import("./etags.js").Etags;
19
20
  }>;