@rsc-kit/core 0.29.1 → 0.29.2
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/js/section.d.ts +14 -4
- package/dist/js/section.js.map +1 -1
- package/dist/vite.js +13 -4
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/dist/js/section.d.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
2
|
import type { RefreshOnList } from "./refreshOn";
|
|
3
|
-
import type { PageProps } from "../routeSchema";
|
|
3
|
+
import type { ApiParams, PageProps } from "../routeSchema";
|
|
4
|
+
import type { RoutePattern } from "../routes";
|
|
4
5
|
export type { RefreshOnInput, RefreshOnList } from "./refreshOn";
|
|
5
6
|
/**
|
|
6
|
-
* A page's `export const refreshOn`, typed from its own url
|
|
7
|
+
* A page's `export const refreshOn`, typed from its own url schema or from
|
|
8
|
+
* its route's pattern:
|
|
7
9
|
*
|
|
8
10
|
* export const params = z.object({ team: z.string() })
|
|
9
11
|
* export const refreshOn: PageRefreshOn<typeof params> = ({ params }) => [`team:${params.team}:repos`]
|
|
10
12
|
*
|
|
11
|
-
*
|
|
13
|
+
* // no schema: the pattern names the params, each a string
|
|
14
|
+
* export const refreshOn: PageRefreshOn<'/[team]/[app]/[env]'> = ({ params }) => [`app:${params.app}`]
|
|
15
|
+
*
|
|
16
|
+
* The pattern is checked against the routes the build found, so one that is
|
|
17
|
+
* not a page - or a param it does not have, `params.ap` - fails the
|
|
18
|
+
* typecheck. `params` arrives awaited; with neither, a record of strings.
|
|
12
19
|
*/
|
|
13
|
-
export type PageRefreshOn<P = never, S = never> = RefreshOnList<PageProps<
|
|
20
|
+
export type PageRefreshOn<P extends RoutePattern | object = never, S = never> = [P] extends [never] ? RefreshOnList<PageProps<never, S>> : [P] extends [string] ? RefreshOnList<{
|
|
21
|
+
params: Promise<ApiParams<P>>;
|
|
22
|
+
searchParams: Promise<URLSearchParams>;
|
|
23
|
+
}> : RefreshOnList<PageProps<P, S>>;
|
|
14
24
|
export interface SectionOptions<P> {
|
|
15
25
|
/**
|
|
16
26
|
* What this section refreshes on: names for the data it shows, or a
|
package/dist/js/section.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../src/js/section.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../src/js/section.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA2BxC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,6EAA6E;AAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AAE5D,yDAAyD;AACzD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;AAoB1D,MAAM,UAAU,OAAO,CACrB,IAAY,EACZ,SAA2B,EAC3B,OAAO,GAAsB,EAAE;IAE/B,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAE9B,2EAA2E;IAC3E,gEAAgE;IAChE,MAAM,KAAK,GAAqB,SAAS;QACvC,CAAC,CAAC,SAAS,gBAAgB,CAAC,KAAQ;YAChC,OAAO,aAAa,CAClB,QAAQ,EACR,IAAI,EACJ,aAAa,CACX,SAAiC,EACjC,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAW,CAC9D,EACD,aAAa,CACX,SAAiC,EACjC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAI,KAAgB,EAAW,CAClD,CACF,CAAC;QACJ,CAAC;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,2EAA2E;IAC3E,qDAAqD;IACrD,SAAS,OAAO,CAAC,KAAQ;QACvB,sEAAsE;QACtE,oDAAoD;QACpD,OAAO,aAAa,CAClB,YAA+C,EAC/C,EAAE,IAAI,EAAE,EACR,aAAa,CAAC,KAA6B,EAAE,KAAc,CAAC,CAC7D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,WAAW,GAAG,WAAW,IAAI,GAAG,CAAC;IACzC,sEAAsE;IACrE,OAA8C,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IAC/D,IAAI,OAAO,CAAC,MAAM;QACf,OAA8C,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAEjE,OAAO,OAA2B,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAiB;IAEjB,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAErD,OAAQ,QAA+C,CAAC,KAAK,CACT,CAAC;AACvD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,eAAe,CAAC,QAAiB;IAC/C,OAAO,CACL,OAAO,QAAQ,KAAK,UAAU;QAC7B,QAA+C,CAAC,MAAM,CAAC,KAAK,IAAI,CAClE,CAAC;AACJ,CAAC","sourcesContent":["import { Fragment, createElement } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { SlotBoundary } from \"./SlotBoundary\";\nimport { RefreshOn } from \"./refreshOn\";\nimport type { RefreshOnList } from \"./refreshOn\";\nimport type { ApiParams, PageProps } from \"../routeSchema\";\nimport type { RoutePattern } from \"../routes\";\n\nexport type { RefreshOnInput, RefreshOnList } from \"./refreshOn\";\n\n/**\n * A page's `export const refreshOn`, typed from its own url schema or from\n * its route's pattern:\n *\n * export const params = z.object({ team: z.string() })\n * export const refreshOn: PageRefreshOn<typeof params> = ({ params }) => [`team:${params.team}:repos`]\n *\n * // no schema: the pattern names the params, each a string\n * export const refreshOn: PageRefreshOn<'/[team]/[app]/[env]'> = ({ params }) => [`app:${params.app}`]\n *\n * The pattern is checked against the routes the build found, so one that is\n * not a page - or a param it does not have, `params.ap` - fails the\n * typecheck. `params` arrives awaited; with neither, a record of strings.\n */\nexport type PageRefreshOn<P extends RoutePattern | object = never, S = never> = [P] extends [never]\n ? RefreshOnList<PageProps<never, S>>\n : [P] extends [string]\n ? RefreshOnList<{ params: Promise<ApiParams<P>>; searchParams: Promise<URLSearchParams> }>\n : RefreshOnList<PageProps<P, S>>;\n\n/**\n * Mark a region of a page as separately refreshable.\n *\n * Deliberately not a client module. The boundary it wraps things in is one,\n * but the things it wraps are server components — an async component that\n * fetches its own data. Marking this file \"use client\" would turn every one of\n * them into a client reference, which async components cannot be.\n *\n * export default section('orders', async function Orders() { ... })\n *\n * The page renders it like any component. What it buys is a name the server\n * can address: `Rsc::revalidate('orders')` re-renders this and nothing else,\n * and the answer replaces it in place.\n *\n * With `refreshOn`, it also says what it depends on, and refreshes itself when\n * that changes - from a webhook, a job, another visitor - with nothing polling:\n *\n * export default section('repos', Repos, {\n * refreshOn: ({ params }) => [`team:${params.team}:repos`],\n * })\n *\n * The name has to be unique within the page. It does not have to be unique in\n * the app: the component is carried on the wrapper it returns, so the server\n * resolves a section through the module the route declares rather than by\n * looking the name up in a registry every section in the app also writes to.\n * Two pages may both call theirs 'stats'.\n */\n\n/** Where the unwrapped component hangs off the wrapper section() returns. */\nconst INNER = Symbol.for(\"@rsc-kit/core.section-component\");\n\n/** Set on the wrapper of a section declared `shared`. */\nconst SHARED = Symbol.for(\"@rsc-kit/core.section-shared\");\n\nexport interface SectionOptions<P> {\n /**\n * What this section refreshes on: names for the data it shows, or a\n * function of the section's props - the page's params and searchParams -\n * that makes them. When one is said to have changed, the section refreshes.\n */\n refreshOn?: RefreshOnList<P>;\n /**\n * The section renders the same for everyone allowed to see the page, so\n * when a change makes many tabs ask for it, one render answers them all.\n * Each tab's guards still run on its own request; only the render is\n * shared. Never set it on a section that shows anything per visitor - their\n * name, their data, what they may do: the first tab's render is what every\n * other tab is shown.\n */\n shared?: boolean;\n}\n\nexport function section<P extends Record<string, unknown>>(\n name: string,\n Component: ComponentType<P>,\n options: SectionOptions<P> = {},\n): ComponentType<P> {\n const { refreshOn } = options;\n\n // The names render inside the boundary, with the component, so a refresh -\n // which renders the inside alone - carries the versions it saw.\n const Inner: ComponentType<P> = refreshOn\n ? function RefreshOnSection(props: P): ReactNode {\n return createElement(\n Fragment,\n null,\n createElement(\n RefreshOn as ComponentType<never>,\n { key: \"refreshOn\", target: name, refreshOn, props } as never,\n ),\n createElement(\n Component as ComponentType<never>,\n { key: \"section\", ...(props as object) } as never,\n ),\n );\n }\n : Component;\n\n // The boundary wraps the component here rather than at the call site, so a\n // page renders a section exactly like anything else.\n function Section(props: P): ReactNode {\n // createElement cannot line its overloads up with a component generic\n // over its own props. The call is the ordinary one.\n return createElement(\n SlotBoundary as ComponentType<{ name: string }>,\n { name },\n createElement(Inner as ComponentType<never>, props as never),\n );\n }\n\n Section.displayName = `Section(${name})`;\n // Reached through the route's own module, never through a shared map.\n (Section as unknown as Record<symbol, unknown>)[INNER] = Inner;\n if (options.shared)\n (Section as unknown as Record<symbol, unknown>)[SHARED] = true;\n\n return Section as ComponentType<P>;\n}\n\n/**\n * The component inside a section module, without its boundary.\n *\n * Revalidation renders this rather than the wrapper: the client swaps what is\n * inside the boundary, so returning the wrapper would nest a new boundary\n * inside the old one on every refresh.\n *\n * Takes the module's own export rather than a name. A name-keyed registry is\n * written to by every section in the app at bundle load — the generated entry\n * imports them all eagerly — so a lookup by name reached any page's region from\n * any url, bounded only by whatever guard happened to sit on the url asked for.\n */\nexport function sectionComponent(\n exported: unknown,\n): ComponentType<Record<string, unknown>> | undefined {\n if (typeof exported !== \"function\") return undefined;\n\n return (exported as unknown as Record<symbol, unknown>)[INNER] as\n ComponentType<Record<string, unknown>> | undefined;\n}\n\n/** Whether a section module's export was declared `shared`. */\nexport function isSharedSection(exported: unknown): boolean {\n return (\n typeof exported === \"function\" &&\n (exported as unknown as Record<symbol, unknown>)[SHARED] === true\n );\n}\n"]}
|
package/dist/vite.js
CHANGED
|
@@ -4796,8 +4796,14 @@ export async function handleAction(
|
|
|
4796
4796
|
// to ask is the whole point — the answer carries what went stale with it.
|
|
4797
4797
|
const targets = takeRevalidated?.() ?? []
|
|
4798
4798
|
|
|
4799
|
+
// flightOnError on both, as on every other render: it is what turns a
|
|
4800
|
+
// redirect() or notFound() into the digest the client's boundaries read.
|
|
4801
|
+
// Without it a re-render that reached a redirect inside a Suspense
|
|
4802
|
+
// boundary - the old address of a page the action just renamed - sent an
|
|
4803
|
+
// anonymous error, and the boundary showed the error page instead of
|
|
4804
|
+
// following it.
|
|
4799
4805
|
if (targets.length === 0 || !page) {
|
|
4800
|
-
return { stream: renderToReadableStream(result) }
|
|
4806
|
+
return { stream: renderToReadableStream(result, { onError: flightOnError }) }
|
|
4801
4807
|
}
|
|
4802
4808
|
|
|
4803
4809
|
// The render is of the world after the write. Everything cache() answered
|
|
@@ -4822,7 +4828,7 @@ export async function handleAction(
|
|
|
4822
4828
|
|
|
4823
4829
|
// Marked, so an action whose own result happens to be an object with a
|
|
4824
4830
|
// 'result' key is not mistaken for this envelope.
|
|
4825
|
-
return { stream: renderToReadableStream({ __rscRevalidated: revalidated, result }) }
|
|
4831
|
+
return { stream: renderToReadableStream({ __rscRevalidated: revalidated, result }, { onError: flightOnError }) }
|
|
4826
4832
|
}
|
|
4827
4833
|
|
|
4828
4834
|
return page.href ? await withUrl(page.href, render) : await render()
|
|
@@ -5076,13 +5082,16 @@ export async function handleRscRevalidate(
|
|
|
5076
5082
|
await runMiddleware(page.component, page.props)
|
|
5077
5083
|
|
|
5078
5084
|
const rscPayload = await shareRender(page.component + ' ' + target + ' ' + share, async () =>
|
|
5079
|
-
new Response(renderToReadableStream(await renderRevalidated(target, page, false, true))).text(),
|
|
5085
|
+
new Response(renderToReadableStream(await renderRevalidated(target, page, false, true), { onError: flightOnError })).text(),
|
|
5080
5086
|
)
|
|
5081
5087
|
|
|
5082
5088
|
return { rscPayload }
|
|
5083
5089
|
}
|
|
5084
5090
|
|
|
5085
|
-
|
|
5091
|
+
// A redirect the refreshed region reaches has to arrive as one - a region
|
|
5092
|
+
// refreshed because refreshOn heard its data change, on a page that now
|
|
5093
|
+
// redirects, showed the error page without the digest.
|
|
5094
|
+
const flight = renderToReadableStream(await renderRevalidated(target, page), { onError: flightOnError })
|
|
5086
5095
|
|
|
5087
5096
|
return { rscPayload: await new Response(flight).text() }
|
|
5088
5097
|
}
|