@rsc-kit/core 0.1.0
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/README.md +95 -0
- package/SECURITY.md +60 -0
- package/dist/action.d.ts +90 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +137 -0
- package/dist/action.js.map +1 -0
- package/dist/build-rsc-vite.d.ts +39 -0
- package/dist/build-rsc-vite.d.ts.map +1 -0
- package/dist/build-rsc-vite.js +172 -0
- package/dist/build-rsc-vite.js.map +1 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +108 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/cli.js.map +1 -0
- package/dist/compile.d.ts +55 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +141 -0
- package/dist/compile.js.map +1 -0
- package/dist/devServer.d.ts +18 -0
- package/dist/devServer.d.ts.map +1 -0
- package/dist/devServer.js +70 -0
- package/dist/devServer.js.map +1 -0
- package/dist/devUrls.d.ts +11 -0
- package/dist/devUrls.d.ts.map +1 -0
- package/dist/devUrls.js +68 -0
- package/dist/devUrls.js.map +1 -0
- package/dist/embed.d.ts +21 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +119 -0
- package/dist/embed.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +38 -0
- package/dist/errors.js.map +1 -0
- package/dist/export.d.ts +41 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +100 -0
- package/dist/export.js.map +1 -0
- package/dist/files.d.ts +39 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +146 -0
- package/dist/files.js.map +1 -0
- package/dist/headers.d.ts +81 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +96 -0
- package/dist/headers.js.map +1 -0
- package/dist/host.d.ts +86 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +590 -0
- package/dist/host.js.map +1 -0
- package/dist/js/ActivityRouter.d.ts +47 -0
- package/dist/js/ActivityRouter.d.ts.map +1 -0
- package/dist/js/ActivityRouter.js +87 -0
- package/dist/js/ActivityRouter.js.map +1 -0
- package/dist/js/DocumentTitle.d.ts +22 -0
- package/dist/js/DocumentTitle.d.ts.map +1 -0
- package/dist/js/DocumentTitle.js +28 -0
- package/dist/js/DocumentTitle.js.map +1 -0
- package/dist/js/Form.d.ts +54 -0
- package/dist/js/Form.d.ts.map +1 -0
- package/dist/js/Form.js +225 -0
- package/dist/js/Form.js.map +1 -0
- package/dist/js/Link.d.ts +29 -0
- package/dist/js/Link.d.ts.map +1 -0
- package/dist/js/Link.js +112 -0
- package/dist/js/Link.js.map +1 -0
- package/dist/js/PathnameProvider.d.ts +8 -0
- package/dist/js/PathnameProvider.d.ts.map +1 -0
- package/dist/js/PathnameProvider.js +24 -0
- package/dist/js/PathnameProvider.js.map +1 -0
- package/dist/js/RedirectBoundary.d.ts +18 -0
- package/dist/js/RedirectBoundary.d.ts.map +1 -0
- package/dist/js/RedirectBoundary.js +34 -0
- package/dist/js/RedirectBoundary.js.map +1 -0
- package/dist/js/SegmentBoundary.d.ts +8 -0
- package/dist/js/SegmentBoundary.d.ts.map +1 -0
- package/dist/js/SegmentBoundary.js +38 -0
- package/dist/js/SegmentBoundary.js.map +1 -0
- package/dist/js/SlotBoundary.d.ts +15 -0
- package/dist/js/SlotBoundary.d.ts.map +1 -0
- package/dist/js/SlotBoundary.js +17 -0
- package/dist/js/SlotBoundary.js.map +1 -0
- package/dist/js/createViteRscApp.d.ts +8 -0
- package/dist/js/createViteRscApp.d.ts.map +1 -0
- package/dist/js/createViteRscApp.js +185 -0
- package/dist/js/createViteRscApp.js.map +1 -0
- package/dist/js/errors.d.ts +51 -0
- package/dist/js/errors.d.ts.map +1 -0
- package/dist/js/errors.js +93 -0
- package/dist/js/errors.js.map +1 -0
- package/dist/js/navigate.d.ts +118 -0
- package/dist/js/navigate.d.ts.map +1 -0
- package/dist/js/navigate.js +780 -0
- package/dist/js/navigate.js.map +1 -0
- package/dist/js/onlineStore.d.ts +23 -0
- package/dist/js/onlineStore.d.ts.map +1 -0
- package/dist/js/onlineStore.js +42 -0
- package/dist/js/onlineStore.js.map +1 -0
- package/dist/js/pagePayload.d.ts +15 -0
- package/dist/js/pagePayload.d.ts.map +1 -0
- package/dist/js/pagePayload.js +39 -0
- package/dist/js/pagePayload.js.map +1 -0
- package/dist/js/router.d.ts +29 -0
- package/dist/js/router.d.ts.map +1 -0
- package/dist/js/router.js +40 -0
- package/dist/js/router.js.map +1 -0
- package/dist/js/section.d.ts +16 -0
- package/dist/js/section.d.ts.map +1 -0
- package/dist/js/section.js +55 -0
- package/dist/js/section.js.map +1 -0
- package/dist/js/segmentStore.d.ts +69 -0
- package/dist/js/segmentStore.d.ts.map +1 -0
- package/dist/js/segmentStore.js +127 -0
- package/dist/js/segmentStore.js.map +1 -0
- package/dist/js/slotStore.d.ts +35 -0
- package/dist/js/slotStore.d.ts.map +1 -0
- package/dist/js/slotStore.js +46 -0
- package/dist/js/slotStore.js.map +1 -0
- package/dist/js/standardSchema.d.ts +47 -0
- package/dist/js/standardSchema.d.ts.map +1 -0
- package/dist/js/standardSchema.js +41 -0
- package/dist/js/standardSchema.js.map +1 -0
- package/dist/js/useForm.d.ts +43 -0
- package/dist/js/useForm.d.ts.map +1 -0
- package/dist/js/useForm.js +164 -0
- package/dist/js/useForm.js.map +1 -0
- package/dist/js/useLinkStatus.d.ts +2 -0
- package/dist/js/useLinkStatus.d.ts.map +1 -0
- package/dist/js/useLinkStatus.js +3 -0
- package/dist/js/useLinkStatus.js.map +1 -0
- package/dist/js/useOnline.d.ts +8 -0
- package/dist/js/useOnline.d.ts.map +1 -0
- package/dist/js/useOnline.js +20 -0
- package/dist/js/useOnline.js.map +1 -0
- package/dist/js/usePathname.d.ts +9 -0
- package/dist/js/usePathname.d.ts.map +1 -0
- package/dist/js/usePathname.js +32 -0
- package/dist/js/usePathname.js.map +1 -0
- package/dist/js/useSearchParams.d.ts +2 -0
- package/dist/js/useSearchParams.d.ts.map +1 -0
- package/dist/js/useSearchParams.js +60 -0
- package/dist/js/useSearchParams.js.map +1 -0
- package/dist/manifest.d.ts +66 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +13 -0
- package/dist/manifest.js.map +1 -0
- package/dist/prerender.d.ts +138 -0
- package/dist/prerender.d.ts.map +1 -0
- package/dist/prerender.js +387 -0
- package/dist/prerender.js.map +1 -0
- package/dist/redirect.d.ts +34 -0
- package/dist/redirect.d.ts.map +1 -0
- package/dist/redirect.js +108 -0
- package/dist/redirect.js.map +1 -0
- package/dist/redirectDigest.d.ts +20 -0
- package/dist/redirectDigest.d.ts.map +1 -0
- package/dist/redirectDigest.js +66 -0
- package/dist/redirectDigest.js.map +1 -0
- package/dist/request.d.ts +93 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +308 -0
- package/dist/request.js.map +1 -0
- package/dist/revalidate.d.ts +43 -0
- package/dist/revalidate.d.ts.map +1 -0
- package/dist/revalidate.js +99 -0
- package/dist/revalidate.js.map +1 -0
- package/dist/routes.d.ts +37 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +28 -0
- package/dist/routes.js.map +1 -0
- package/dist/routing.d.ts +52 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +146 -0
- package/dist/routing.js.map +1 -0
- package/dist/runtime.d.ts +36 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +86 -0
- package/dist/runtime.js.map +1 -0
- package/dist/safeUrl.d.ts +10 -0
- package/dist/safeUrl.d.ts.map +1 -0
- package/dist/safeUrl.js +40 -0
- package/dist/safeUrl.js.map +1 -0
- package/dist/streaming.d.ts +62 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +108 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/vite.d.ts +109 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +1909 -0
- package/dist/vite.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +863 -0
- package/dist/worker.js.map +1 -0
- package/package.json +223 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @rsc-kit/core
|
|
2
|
+
|
|
3
|
+
The engine: a Vite plugin that discovers the route tree, and a host adapter that serves it.
|
|
4
|
+
|
|
5
|
+
This is the library. Most people want [`rsc-kit`](https://www.npmjs.com/package/rsc-kit) (the command line) or `bun create rsc-kit` (a new app).
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun create rsc-kit my-app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Why not just use Next?
|
|
12
|
+
|
|
13
|
+
That is the right first question, and the honest answer is that the routing
|
|
14
|
+
conventions here *are* Next's — `app/`, `page`/`layout`/`loading`, `@slot`,
|
|
15
|
+
`(.)` interception, `"use client"`, server actions, `generateMetadata`.
|
|
16
|
+
Inventing different names for the same ideas would cost every reader a
|
|
17
|
+
translation table and buy nothing.
|
|
18
|
+
|
|
19
|
+
The difference is what sits underneath.
|
|
20
|
+
|
|
21
|
+
**It is a Vite plugin, not a bundler.** You keep your `vite.config.ts`, your
|
|
22
|
+
plugins, your ecosystem. Adding RSC to an existing app is one entry in the
|
|
23
|
+
plugins array — not a migration.
|
|
24
|
+
|
|
25
|
+
**It runs on your server.** Hono, Elysia, `Bun.serve`, `node:http`,
|
|
26
|
+
Cloudflare Workers. The adapter is a `Request` in and a `Response` out, so
|
|
27
|
+
binding it is one line and anything it does not claim falls through to your
|
|
28
|
+
own routes.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
app.all('*', async (c) => (await rsc(c.req.raw)) ?? c.notFound())
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**A route can ship no JavaScript at all.** `clientJs = false` renders to HTML
|
|
35
|
+
and stops — no bootstrap, no React in the browser, no router. That is about
|
|
36
|
+
70 kB gzipped back on a page with nothing to hydrate, of which react-dom alone
|
|
37
|
+
is ~54 kB. The build refuses the combination rather than shipping a button that
|
|
38
|
+
does nothing.
|
|
39
|
+
|
|
40
|
+
**And the framework itself is small, because most of what ships is React.**
|
|
41
|
+
Measured on the example app: 74.7 kB gzipped of JavaScript, of which this
|
|
42
|
+
framework's own runtime is 17 kB — 7.3%. React, react-dom, the Flight client
|
|
43
|
+
and the scheduler are 90%. A frozen page is served in 27 µs and the host
|
|
44
|
+
adapter costs about 9 µs per request.
|
|
45
|
+
|
|
46
|
+
**It compiles to a single binary.** `bun build --compile` with the assets and
|
|
47
|
+
frozen pages inside it.
|
|
48
|
+
|
|
49
|
+
**And it is not only JavaScript.** The same engine drives a Laravel host over a
|
|
50
|
+
socket, which is where this started.
|
|
51
|
+
|
|
52
|
+
## What you get
|
|
53
|
+
|
|
54
|
+
- File-based routing with layouts, loading boundaries, parallel routes and
|
|
55
|
+
route interception
|
|
56
|
+
- Streaming SSR with Suspense, and partial prerendering — the static shell is
|
|
57
|
+
stored, the part that needs the request is rendered per visit
|
|
58
|
+
- Server actions, with an optional builder that gives you validation
|
|
59
|
+
(any Standard Schema library — Zod, Valibot, ArkType) and middleware
|
|
60
|
+
- Typed routes: every build writes the routes it found, so a link to a page
|
|
61
|
+
that does not exist stops compiling
|
|
62
|
+
- `middleware.ts` that runs before every render below it, on every path
|
|
63
|
+
- Request and response access — `headers()`, `cookies()`, `responseHeaders()`
|
|
64
|
+
|
|
65
|
+
## Status
|
|
66
|
+
|
|
67
|
+
**0.x. Early, and honest about it.** The API will change before 1.0, and
|
|
68
|
+
`@vitejs/plugin-rsc` — which this builds on — is itself pre-1.0.
|
|
69
|
+
|
|
70
|
+
It is tested (500+ tests across the engine, the client router and the build),
|
|
71
|
+
it has had an adversarial security review, and it runs a real documentation
|
|
72
|
+
site in production. It has not been used by anyone but its author. Issues and
|
|
73
|
+
questions are welcome; promises are not being made yet.
|
|
74
|
+
|
|
75
|
+
See [SECURITY.md](./SECURITY.md) for the threat model and how to report
|
|
76
|
+
something.
|
|
77
|
+
|
|
78
|
+
## Getting started
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
bun create rsc-kit my-app # a new app
|
|
82
|
+
cd my-app && bun run dev
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Adding it to a project you already have:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
bunx create-rsc-kit --init
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Docs: https://rsc-kit.dev
|
|
92
|
+
|
|
93
|
+
## Licence
|
|
94
|
+
|
|
95
|
+
MIT
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
**Please do not open a public issue.**
|
|
6
|
+
|
|
7
|
+
Use GitHub's private vulnerability reporting: the **Security** tab on this
|
|
8
|
+
repository → **Report a vulnerability**. It is private to the maintainers and
|
|
9
|
+
gives us somewhere to discuss a fix before it is public.
|
|
10
|
+
|
|
11
|
+
> Enable it once, per repository: Settings → Code security → Private
|
|
12
|
+
> vulnerability reporting. Until it is on, the Security tab shows no report
|
|
13
|
+
> button and a reporter's only option is a public issue.
|
|
14
|
+
|
|
15
|
+
What helps, roughly in order:
|
|
16
|
+
|
|
17
|
+
- The request that demonstrates it — a `curl` line is ideal, since most of the surface here is HTTP headers.
|
|
18
|
+
- What you expected the server to refuse, and what it did instead.
|
|
19
|
+
- Which host you ran (`createRscHandler` on Bun/Node/Workers, or the Laravel adapter), and the version.
|
|
20
|
+
|
|
21
|
+
You will get an acknowledgement within a few days. If a report is valid we will
|
|
22
|
+
agree a disclosure date with you and credit you in the advisory unless you would
|
|
23
|
+
rather we did not.
|
|
24
|
+
|
|
25
|
+
## Supported versions
|
|
26
|
+
|
|
27
|
+
Pre-1.0. Only the latest published version of `@rsc-kit/core` receives fixes;
|
|
28
|
+
there are no backports yet.
|
|
29
|
+
|
|
30
|
+
## What is in scope
|
|
31
|
+
|
|
32
|
+
Anything that lets a request do something the application did not intend:
|
|
33
|
+
|
|
34
|
+
- Reaching content or an action past a check the app wrote.
|
|
35
|
+
- Making the server run code the app did not ask it to run.
|
|
36
|
+
- Reading files outside the directories a host handed to `assets` or `prerendered`.
|
|
37
|
+
- Making one user's response reachable by another — through a cache, a shared
|
|
38
|
+
scope, or state that outlives a request.
|
|
39
|
+
- Crashing or hanging a server from an unauthenticated request.
|
|
40
|
+
|
|
41
|
+
The protocol headers (`X-RSC-*`) are the largest surface and the most
|
|
42
|
+
interesting place to look. They are all attacker-controlled and none of them is
|
|
43
|
+
verifiable — see **Part 3b: the trust boundary** in
|
|
44
|
+
[`PROTOCOL.md`](./PROTOCOL.md) for the rule they are supposed to obey, and
|
|
45
|
+
`packages/core/tests/js/protocolAbuse.test.ts` for the cases already covered.
|
|
46
|
+
|
|
47
|
+
## What is not
|
|
48
|
+
|
|
49
|
+
- An application's own bugs — an app that puts a secret in a client component ships it to the browser, and that is the app's decision, not this package's.
|
|
50
|
+
- Server actions being callable without a session. That is what a server action *is*: a public endpoint whose id is not a secret. The check belongs inside the action, and [the docs say so](https://rsc-kit.dev/guides/authorization).
|
|
51
|
+
- Anything requiring the attacker to already control the server, the build, or a dependency.
|
|
52
|
+
- Denial of service by volume.
|
|
53
|
+
|
|
54
|
+
## Known, documented, unfixed
|
|
55
|
+
|
|
56
|
+
Kept here so a reporter does not spend time on something we already know:
|
|
57
|
+
|
|
58
|
+
- **A check written in a layout is skippable.** A navigation says which layouts it holds and the server trusts it. Checks belong in `guard.ts`, which is never skipped; a layout is chrome and may be skipped by design. The docs say so, but nothing stops an app writing one in the wrong place.
|
|
59
|
+
- **No `Cache-Control` on rendered responses.** `Vary: X-RSC` is set, so a shared cache will not serve a payload as a document, but the absence of an explicit directive leaves heuristic caching to the intermediary. Not audited.
|
|
60
|
+
- **`X-RSC-Redirect` is followed without an origin check.** An app that redirects to a value it took from user input has an open redirect; the client does not second-guess the destination.
|
package/dist/action.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type StandardSchemaV1 } from './js/standardSchema.js';
|
|
2
|
+
/** What an action answers with. Exactly one of the three is set. */
|
|
3
|
+
export interface ActionResult<Data> {
|
|
4
|
+
/** What the handler returned. */
|
|
5
|
+
data?: Data;
|
|
6
|
+
/** Field name to messages, in the shape a form already renders. */
|
|
7
|
+
validationErrors?: Record<string, string[]>;
|
|
8
|
+
/** Something else went wrong, reduced to a message the browser may see. */
|
|
9
|
+
serverError?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A middleware that neither continued nor refused.
|
|
13
|
+
*
|
|
14
|
+
* Never reported through `onError`: that reduces an error to a message for the
|
|
15
|
+
* browser, and this one is for whoever wrote the middleware. A check that
|
|
16
|
+
* forgot to call `next()` would otherwise look exactly like a check that
|
|
17
|
+
* passed.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ActionMisuse extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
/** Refuse from inside a handler, naming the fields. */
|
|
23
|
+
export declare class ActionValidationError extends Error {
|
|
24
|
+
readonly errors: Record<string, string[]>;
|
|
25
|
+
constructor(errors: Record<string, string[]>);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Fail with field errors the form can show.
|
|
29
|
+
*
|
|
30
|
+
* For what a schema cannot know — a name already taken, a balance too low.
|
|
31
|
+
* Throws, so the handler stops where it is; the action turns it into a
|
|
32
|
+
* returned result on the way out.
|
|
33
|
+
*/
|
|
34
|
+
export declare function fieldErrors(errors: Record<string, string[] | string>): never;
|
|
35
|
+
/**
|
|
36
|
+
* What `next()` hands back, carrying what the step added.
|
|
37
|
+
*
|
|
38
|
+
* The context a middleware contributes cannot be inferred from the arguments
|
|
39
|
+
* it passes to `next` — TypeScript infers from a function's return, not from a
|
|
40
|
+
* call inside it. So `next` returns this, the middleware returns it, and the
|
|
41
|
+
* addition is read off the middleware's own return type.
|
|
42
|
+
*/
|
|
43
|
+
export interface MiddlewareResult<Extra> {
|
|
44
|
+
readonly ctx: Extra;
|
|
45
|
+
readonly value: unknown;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A step that runs before the handler.
|
|
49
|
+
*
|
|
50
|
+
* It calls `next` to continue, optionally adding to the context, and what it
|
|
51
|
+
* adds shows up in the handler's types. Returning without calling `next` — or
|
|
52
|
+
* throwing — stops the action, which is how a check refuses.
|
|
53
|
+
*/
|
|
54
|
+
export type ActionMiddleware<Ctx, Extra extends Record<string, unknown>> = (args: {
|
|
55
|
+
ctx: Ctx;
|
|
56
|
+
next: <E extends Record<string, unknown> = Record<string, never>>(opts?: {
|
|
57
|
+
ctx?: E;
|
|
58
|
+
}) => Promise<MiddlewareResult<E>>;
|
|
59
|
+
}) => Promise<MiddlewareResult<Extra>>;
|
|
60
|
+
type Output<S> = S extends StandardSchemaV1<unknown, infer O> ? O : never;
|
|
61
|
+
export interface ActionBuilder<Ctx extends Record<string, unknown>, Input> {
|
|
62
|
+
/** Add a step, and whatever context it contributes. */
|
|
63
|
+
use<Extra extends Record<string, unknown> = Record<string, never>>(middleware: (args: {
|
|
64
|
+
ctx: Ctx;
|
|
65
|
+
next: <E extends Record<string, unknown> = Record<string, never>>(opts?: {
|
|
66
|
+
ctx?: E;
|
|
67
|
+
}) => Promise<MiddlewareResult<E>>;
|
|
68
|
+
}) => Promise<MiddlewareResult<Extra>>): ActionBuilder<Ctx & Extra, Input>;
|
|
69
|
+
/** Parse and check what the caller sent. The handler's `input` follows. */
|
|
70
|
+
input<S extends StandardSchemaV1>(schema: S): ActionBuilder<Ctx, Output<S>>;
|
|
71
|
+
/** The body. */
|
|
72
|
+
handler<Data>(fn: (args: {
|
|
73
|
+
input: Input;
|
|
74
|
+
ctx: Ctx;
|
|
75
|
+
}) => Promise<Data> | Data): (input?: unknown) => Promise<ActionResult<Data>>;
|
|
76
|
+
}
|
|
77
|
+
export interface ActionClientOptions {
|
|
78
|
+
/**
|
|
79
|
+
* What the browser is told when something unexpected throws.
|
|
80
|
+
*
|
|
81
|
+
* Everything reaching here is a bug or an outage, and its message may say
|
|
82
|
+
* more than a stranger should see — a query, a path, a host. Returning a
|
|
83
|
+
* fixed string is the safe default; return the message only for errors you
|
|
84
|
+
* raised deliberately.
|
|
85
|
+
*/
|
|
86
|
+
onError?: (error: unknown) => string;
|
|
87
|
+
}
|
|
88
|
+
export declare function createActionClient(options?: ActionClientOptions): ActionBuilder<Record<never, never>, undefined>;
|
|
89
|
+
export {};
|
|
90
|
+
//# sourceMappingURL=action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"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"}
|
package/dist/action.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Server actions with a schema, middleware, and types that follow from both.
|
|
2
|
+
//
|
|
3
|
+
// export const action = createActionClient({ onError: report })
|
|
4
|
+
//
|
|
5
|
+
// export const createPost = action
|
|
6
|
+
// .use(async ({ next }) => next({ ctx: { user: await currentUser() } }))
|
|
7
|
+
// .input(z.object({ title: z.string().min(3) }))
|
|
8
|
+
// .handler(async ({ input, ctx }) => savePost(ctx.user.id, input.title))
|
|
9
|
+
//
|
|
10
|
+
// `input` is typed from the schema and `ctx` from every middleware that ran, so
|
|
11
|
+
// the handler is checked against both without either being written down twice.
|
|
12
|
+
//
|
|
13
|
+
// The built action RETURNS its failures rather than throwing them, and that is
|
|
14
|
+
// not a style choice. React serialises a rejected server action opaquely —
|
|
15
|
+
// production strips the message and leaves a digest — so a thrown validation
|
|
16
|
+
// error reaches the browser as "an error occurred" and the fields it named are
|
|
17
|
+
// gone. A returned object crosses the boundary intact.
|
|
18
|
+
//
|
|
19
|
+
// Distinct from `middleware.ts` in a route directory, which decides whether a
|
|
20
|
+
// page may render. This wraps one action. They are different questions: an
|
|
21
|
+
// action is reachable without any page, which is why it defends itself.
|
|
22
|
+
import { validateWith } from './js/standardSchema.js';
|
|
23
|
+
/**
|
|
24
|
+
* A middleware that neither continued nor refused.
|
|
25
|
+
*
|
|
26
|
+
* Never reported through `onError`: that reduces an error to a message for the
|
|
27
|
+
* browser, and this one is for whoever wrote the middleware. A check that
|
|
28
|
+
* forgot to call `next()` would otherwise look exactly like a check that
|
|
29
|
+
* passed.
|
|
30
|
+
*/
|
|
31
|
+
export class ActionMisuse extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'ActionMisuse';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Refuse from inside a handler, naming the fields. */
|
|
38
|
+
export class ActionValidationError extends Error {
|
|
39
|
+
errors;
|
|
40
|
+
constructor(errors) {
|
|
41
|
+
super('Validation failed');
|
|
42
|
+
this.name = 'ActionValidationError';
|
|
43
|
+
this.errors = errors;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fail with field errors the form can show.
|
|
48
|
+
*
|
|
49
|
+
* For what a schema cannot know — a name already taken, a balance too low.
|
|
50
|
+
* Throws, so the handler stops where it is; the action turns it into a
|
|
51
|
+
* returned result on the way out.
|
|
52
|
+
*/
|
|
53
|
+
export function fieldErrors(errors) {
|
|
54
|
+
const normalised = {};
|
|
55
|
+
for (const [field, message] of Object.entries(errors)) {
|
|
56
|
+
normalised[field] = Array.isArray(message) ? message : [message];
|
|
57
|
+
}
|
|
58
|
+
throw new ActionValidationError(normalised);
|
|
59
|
+
}
|
|
60
|
+
const GENERIC = 'Something went wrong.';
|
|
61
|
+
/** FormData in, a plain object out — what a schema expects to be handed. */
|
|
62
|
+
function fromFormData(body) {
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const key of new Set(body.keys())) {
|
|
65
|
+
const values = body.getAll(key);
|
|
66
|
+
// One value stays a value. Several stay several — a multi-select that
|
|
67
|
+
// collapsed to its last entry would be a silent data loss.
|
|
68
|
+
out[key] = values.length > 1 ? values : values[0];
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
export function createActionClient(options = {}) {
|
|
73
|
+
const report = options.onError ?? (() => GENERIC);
|
|
74
|
+
function build(middlewares, schema) {
|
|
75
|
+
return {
|
|
76
|
+
use(middleware) {
|
|
77
|
+
return build([...middlewares, middleware], schema);
|
|
78
|
+
},
|
|
79
|
+
input(next) {
|
|
80
|
+
return build(middlewares, next);
|
|
81
|
+
},
|
|
82
|
+
handler(fn) {
|
|
83
|
+
return async (raw) => {
|
|
84
|
+
try {
|
|
85
|
+
const value = raw instanceof FormData ? fromFormData(raw) : raw;
|
|
86
|
+
if (schema) {
|
|
87
|
+
const invalid = await validateWith(schema, value);
|
|
88
|
+
if (invalid)
|
|
89
|
+
return { validationErrors: invalid };
|
|
90
|
+
}
|
|
91
|
+
const parsed = schema
|
|
92
|
+
? (await schema['~standard'].validate(value)).value
|
|
93
|
+
: value;
|
|
94
|
+
// Composed inside-out so the first `use` is the outermost — it
|
|
95
|
+
// sees the others run, which is what makes timing and cleanup
|
|
96
|
+
// possible rather than only checks.
|
|
97
|
+
let ctx = {};
|
|
98
|
+
let index = 0;
|
|
99
|
+
const run = async () => {
|
|
100
|
+
const middleware = middlewares[index++];
|
|
101
|
+
if (!middleware)
|
|
102
|
+
return await fn({ input: parsed, ctx });
|
|
103
|
+
let continued = false;
|
|
104
|
+
const result = await middleware({
|
|
105
|
+
ctx,
|
|
106
|
+
next: (async (opts) => {
|
|
107
|
+
continued = true;
|
|
108
|
+
ctx = { ...ctx, ...(opts?.ctx ?? {}) };
|
|
109
|
+
return { ctx, value: await run() };
|
|
110
|
+
}),
|
|
111
|
+
});
|
|
112
|
+
// Silence is not refusal. A middleware that neither called
|
|
113
|
+
// next() nor threw has done nothing, and guessing which it meant
|
|
114
|
+
// turns a forgotten `return` into a check that quietly passes.
|
|
115
|
+
if (!continued) {
|
|
116
|
+
throw new ActionMisuse('A middleware returned without calling next(). Call it to continue, or throw to refuse.');
|
|
117
|
+
}
|
|
118
|
+
return result?.value;
|
|
119
|
+
};
|
|
120
|
+
return { data: (await run()) };
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
// Past onError deliberately — see ActionMisuse.
|
|
124
|
+
if (error instanceof ActionMisuse)
|
|
125
|
+
throw error;
|
|
126
|
+
if (error instanceof ActionValidationError) {
|
|
127
|
+
return { validationErrors: error.errors };
|
|
128
|
+
}
|
|
129
|
+
return { serverError: report(error) };
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return build([], null);
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,oEAAoE;AACpE,EAAE;AACF,uCAAuC;AACvC,+EAA+E;AAC/E,uDAAuD;AACvD,+EAA+E;AAC/E,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,uDAAuD;AACvD,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,wEAAwE;AAExE,OAAO,EAAE,YAAY,EAAyB,MAAM,wBAAwB,CAAA;AAY5E;;;;;;;GAOG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;IAC5B,CAAC;CACF;AAED,uDAAuD;AACvD,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9B,MAAM,CAA0B;IAEhD,YAAY,MAAgC;QAC1C,KAAK,CAAC,mBAAmB,CAAC,CAAA;QAC1B,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAAyC;IACnE,MAAM,UAAU,GAA6B,EAAE,CAAA;IAE/C,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACtD,UAAU,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IAClE,CAAC;IAED,MAAM,IAAI,qBAAqB,CAAC,UAAU,CAAC,CAAA;AAC7C,CAAC;AA6DD,MAAM,OAAO,GAAG,uBAAuB,CAAA;AAEvC,4EAA4E;AAC5E,SAAS,YAAY,CAAC,IAAc;IAClC,MAAM,GAAG,GAA4B,EAAE,CAAA;IAEvC,KAAK,MAAM,GAAG,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAE/B,sEAAsE;QACtE,2DAA2D;QAC3D,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IACnD,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAAO,GAAwB,EAAE;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAA;IAEjD,SAAS,KAAK,CACZ,WAA6C,EAC7C,MAA+B;QAE/B,OAAO;YACL,GAAG,CAAC,UAAU;gBACZ,OAAO,KAAK,CAAC,CAAC,GAAG,WAAW,EAAE,UAAmB,CAAC,EAAE,MAAM,CAAU,CAAA;YACtE,CAAC;YACD,KAAK,CAAC,IAAI;gBACR,OAAO,KAAK,CAAC,WAAW,EAAE,IAAI,CAAU,CAAA;YAC1C,CAAC;YACD,OAAO,CAAC,EAAE;gBACR,OAAO,KAAK,EAAE,GAAa,EAAE,EAAE;oBAC7B,IAAI,CAAC;wBACH,MAAM,KAAK,GAAG,GAAG,YAAY,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;wBAE/D,IAAI,MAAM,EAAE,CAAC;4BACX,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;4BAEjD,IAAI,OAAO;gCAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAA;wBACnD,CAAC;wBAED,MAAM,MAAM,GAAG,MAAM;4BACnB,CAAC,CAAE,CAAC,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,KAAe;4BAC9D,CAAC,CAAE,KAAe,CAAA;wBAEpB,+DAA+D;wBAC/D,8DAA8D;wBAC9D,oCAAoC;wBACpC,IAAI,GAAG,GAAG,EAAS,CAAA;wBACnB,IAAI,KAAK,GAAG,CAAC,CAAA;wBAEb,MAAM,GAAG,GAAG,KAAK,IAAsB,EAAE;4BACvC,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,CAAA;4BAEvC,IAAI,CAAC,UAAU;gCAAE,OAAO,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;4BAExD,IAAI,SAAS,GAAG,KAAK,CAAA;4BAErB,MAAM,MAAM,GAAG,MAAO,UAAwE,CAAC;gCAC7F,GAAG;gCACH,IAAI,EAAE,CAAC,KAAK,EAAE,IAAwC,EAAE,EAAE;oCACxD,SAAS,GAAG,IAAI,CAAA;oCAChB,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,EAAS,CAAA;oCAE7C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,EAAE,CAAA;gCACpC,CAAC,CAAU;6BACZ,CAAC,CAAA;4BAEF,2DAA2D;4BAC3D,iEAAiE;4BACjE,+DAA+D;4BAC/D,IAAI,CAAC,SAAS,EAAE,CAAC;gCACf,MAAM,IAAI,YAAY,CACpB,wFAAwF,CACzF,CAAA;4BACH,CAAC;4BAED,OAAQ,MAA8B,EAAE,KAAK,CAAA;wBAC/C,CAAC,CAAA;wBAED,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,CAAmC,EAAE,CAAA;oBAClE,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,gDAAgD;wBAChD,IAAI,KAAK,YAAY,YAAY;4BAAE,MAAM,KAAK,CAAA;wBAE9C,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;4BAC3C,OAAO,EAAE,gBAAgB,EAAE,KAAK,CAAC,MAAM,EAAE,CAAA;wBAC3C,CAAC;wBAED,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;oBACvC,CAAC;gBACH,CAAC,CAAA;YACH,CAAC;SACF,CAAA;IACH,CAAC;IAED,OAAO,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;AACxB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config names, most specific first.
|
|
3
|
+
*
|
|
4
|
+
* A project's Vite config belongs at its root, so the ordinary case is the
|
|
5
|
+
* root vite.config.* with rscRoutes() in its plugins. The vite.rsc.config.*
|
|
6
|
+
* names exist for apps whose root config already drives a separate asset
|
|
7
|
+
* pipeline — a Laravel app running laravel-vite-plugin, say, whose input,
|
|
8
|
+
* outDir and manifest settings would otherwise apply to the RSC build too.
|
|
9
|
+
*/
|
|
10
|
+
export declare const USER_CONFIG_NAMES: readonly ['vite.rsc.config.ts', 'vite.rsc.config.mts', 'vite.rsc.config.js', 'vite.rsc.config.mjs', 'vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs'];
|
|
11
|
+
/**
|
|
12
|
+
* Find the Vite config for the RSC build.
|
|
13
|
+
*
|
|
14
|
+
* Nothing is generated: the build runs the project's own config, so what it
|
|
15
|
+
* does is visible in the repo and anything the app wants in it — Tailwind, the
|
|
16
|
+
* React Compiler, extra aliases — is declared there like any Vite project.
|
|
17
|
+
*/
|
|
18
|
+
export declare function findUserViteConfig(root: string): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Packages the generated entries import directly, which the project must have.
|
|
21
|
+
*
|
|
22
|
+
* Without this the build fails deep inside the bundler — a missing
|
|
23
|
+
* @vitejs/plugin-rsc surfaces as `Rolldown failed to resolve import
|
|
24
|
+
* "@vitejs/plugin-rsc/rsc"` from a generated file the user never wrote.
|
|
25
|
+
*/
|
|
26
|
+
export declare const REQUIRED_PEERS: readonly ['@vitejs/plugin-rsc', 'vite', 'react', 'react-dom'];
|
|
27
|
+
/**
|
|
28
|
+
* Whether a package is installed in the project's node_modules, or a parent's.
|
|
29
|
+
*
|
|
30
|
+
* A plain directory walk rather than require.resolve: Bun resolves packages
|
|
31
|
+
* from its global cache when a project has no node_modules at all, so the
|
|
32
|
+
* resolver reports success for something the build will not find.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isInstalled(root: string, name: string): boolean;
|
|
35
|
+
/** Required packages the project does not have, in declaration order. */
|
|
36
|
+
export declare function missingPeers(root: string): string[];
|
|
37
|
+
/** Shown when a project has no Vite config at all. */
|
|
38
|
+
export declare function missingConfigMessage(root: string): string;
|
|
39
|
+
//# sourceMappingURL=build-rsc-vite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-rsc-vite.d.ts","sourceRoot":"","sources":["../src/build-rsc-vite.ts"],"names":[],"mappings":"AAqBA;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,YAC5B,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,CACT,CAAA;AAEV;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe9D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,YAAI,oBAAoB,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAU,CAAA;AAE3F;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAU/D;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnD;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAczD"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// CLI behind `php artisan rsc:build`.
|
|
2
|
+
//
|
|
3
|
+
// The build itself lives in the Vite plugin (resources/vite.ts). This only
|
|
4
|
+
// decides which config Vite runs: the app's own vite.rsc.config if it has one,
|
|
5
|
+
// otherwise a minimal generated config that just uses the plugin. Either way
|
|
6
|
+
// Vite runs that config directly — nothing is merged on top of it.
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { dirname, join, resolve } from 'node:path';
|
|
10
|
+
import { isBun } from './runtime.js';
|
|
11
|
+
const projectRoot = resolve(process.env.RSC_PROJECT_ROOT || process.cwd());
|
|
12
|
+
// Mirrors the plugin's default so the completion message names the
|
|
13
|
+
// directory the build actually wrote to.
|
|
14
|
+
const outDir = resolve(process.env.RSC_OUT_DIR || join(projectRoot, '.rsc'));
|
|
15
|
+
function log(...args) {
|
|
16
|
+
console.error('[laravel-rsc]', ...args);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Config names, most specific first.
|
|
20
|
+
*
|
|
21
|
+
* A project's Vite config belongs at its root, so the ordinary case is the
|
|
22
|
+
* root vite.config.* with rscRoutes() in its plugins. The vite.rsc.config.*
|
|
23
|
+
* names exist for apps whose root config already drives a separate asset
|
|
24
|
+
* pipeline — a Laravel app running laravel-vite-plugin, say, whose input,
|
|
25
|
+
* outDir and manifest settings would otherwise apply to the RSC build too.
|
|
26
|
+
*/
|
|
27
|
+
export const USER_CONFIG_NAMES = [
|
|
28
|
+
'vite.rsc.config.ts',
|
|
29
|
+
'vite.rsc.config.mts',
|
|
30
|
+
'vite.rsc.config.js',
|
|
31
|
+
'vite.rsc.config.mjs',
|
|
32
|
+
'vite.config.ts',
|
|
33
|
+
'vite.config.mts',
|
|
34
|
+
'vite.config.js',
|
|
35
|
+
'vite.config.mjs',
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* Find the Vite config for the RSC build.
|
|
39
|
+
*
|
|
40
|
+
* Nothing is generated: the build runs the project's own config, so what it
|
|
41
|
+
* does is visible in the repo and anything the app wants in it — Tailwind, the
|
|
42
|
+
* React Compiler, extra aliases — is declared there like any Vite project.
|
|
43
|
+
*/
|
|
44
|
+
export function findUserViteConfig(root) {
|
|
45
|
+
const explicit = process.env.RSC_VITE_CONFIG;
|
|
46
|
+
if (explicit) {
|
|
47
|
+
const path = resolve(explicit);
|
|
48
|
+
return existsSync(path) ? path : null;
|
|
49
|
+
}
|
|
50
|
+
for (const name of USER_CONFIG_NAMES) {
|
|
51
|
+
const path = join(root, name);
|
|
52
|
+
if (existsSync(path))
|
|
53
|
+
return path;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Packages the generated entries import directly, which the project must have.
|
|
59
|
+
*
|
|
60
|
+
* Without this the build fails deep inside the bundler — a missing
|
|
61
|
+
* @vitejs/plugin-rsc surfaces as `Rolldown failed to resolve import
|
|
62
|
+
* "@vitejs/plugin-rsc/rsc"` from a generated file the user never wrote.
|
|
63
|
+
*/
|
|
64
|
+
export const REQUIRED_PEERS = ['@vitejs/plugin-rsc', 'vite', 'react', 'react-dom'];
|
|
65
|
+
/**
|
|
66
|
+
* Whether a package is installed in the project's node_modules, or a parent's.
|
|
67
|
+
*
|
|
68
|
+
* A plain directory walk rather than require.resolve: Bun resolves packages
|
|
69
|
+
* from its global cache when a project has no node_modules at all, so the
|
|
70
|
+
* resolver reports success for something the build will not find.
|
|
71
|
+
*/
|
|
72
|
+
export function isInstalled(root, name) {
|
|
73
|
+
let dir = resolve(root);
|
|
74
|
+
while (true) {
|
|
75
|
+
if (existsSync(join(dir, 'node_modules', name, 'package.json')))
|
|
76
|
+
return true;
|
|
77
|
+
const parent = dirname(dir);
|
|
78
|
+
if (parent === dir)
|
|
79
|
+
return false;
|
|
80
|
+
dir = parent;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Required packages the project does not have, in declaration order. */
|
|
84
|
+
export function missingPeers(root) {
|
|
85
|
+
return REQUIRED_PEERS.filter((name) => !isInstalled(root, name));
|
|
86
|
+
}
|
|
87
|
+
/** Shown when a project has no Vite config at all. */
|
|
88
|
+
export function missingConfigMessage(root) {
|
|
89
|
+
return [
|
|
90
|
+
`No Vite config found in ${root}.`,
|
|
91
|
+
'',
|
|
92
|
+
'Create vite.config.ts:',
|
|
93
|
+
'',
|
|
94
|
+
" import { rscRoutes } from 'laravel-rsc/vite'",
|
|
95
|
+
" import { defineConfig } from 'vite'",
|
|
96
|
+
'',
|
|
97
|
+
' export default defineConfig({ plugins: [rscRoutes()] })',
|
|
98
|
+
'',
|
|
99
|
+
'If your root config already drives another asset pipeline (laravel-vite-plugin),',
|
|
100
|
+
'put the RSC build in vite.rsc.config.ts instead so the two do not share settings.',
|
|
101
|
+
].join('\n');
|
|
102
|
+
}
|
|
103
|
+
/** This file's directory — import.meta.dir is Bun-only. */
|
|
104
|
+
function packageDir() {
|
|
105
|
+
return resolve(new URL('.', import.meta.url).pathname);
|
|
106
|
+
}
|
|
107
|
+
function main() {
|
|
108
|
+
const missing = missingPeers(projectRoot);
|
|
109
|
+
if (missing.length > 0) {
|
|
110
|
+
log(`Missing required package(s): ${missing.join(', ')}`);
|
|
111
|
+
log(`Install them in ${projectRoot}:`);
|
|
112
|
+
log('');
|
|
113
|
+
log(` npm install ${missing.join(' ')}`);
|
|
114
|
+
log('');
|
|
115
|
+
log('rscRoutes() composes @vitejs/plugin-rsc itself — it only has to be installed,');
|
|
116
|
+
log('not added to your Vite config.');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
const configPath = findUserViteConfig(projectRoot);
|
|
120
|
+
if (!configPath) {
|
|
121
|
+
log(missingConfigMessage(projectRoot));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
log(`Using Vite config: ${configPath}`);
|
|
125
|
+
const watch = process.env.RSC_WATCH === '1';
|
|
126
|
+
// A host driving the build from outside prerenders on its own terms, and has
|
|
127
|
+
// to: its pages call back into it, and that callback does not exist inside a
|
|
128
|
+
// `vite build`. Prerendering here would render every such page into an error
|
|
129
|
+
// and fail a build that is perfectly fine. `RSC_PRERENDER=1` opts back in for
|
|
130
|
+
// a host whose pages need nothing from it.
|
|
131
|
+
process.env.RSC_PRERENDER ??= '0';
|
|
132
|
+
// A development build keeps React's development bundle, so a failure reads
|
|
133
|
+
// as "Maximum update depth exceeded" rather than "Minified React error #185"
|
|
134
|
+
// and a link to look it up. Implied by watching, and available on its own for
|
|
135
|
+
// reproducing something a production build only reports by number.
|
|
136
|
+
const dev = watch || process.env.RSC_DEV === '1';
|
|
137
|
+
const viteArgs = ['build', '--config', configPath];
|
|
138
|
+
if (watch)
|
|
139
|
+
viteArgs.push('--watch');
|
|
140
|
+
// Vite's build command is production mode unless told otherwise, whatever
|
|
141
|
+
// NODE_ENV says — without this the dev build still resolves React's
|
|
142
|
+
// production entry.
|
|
143
|
+
if (dev)
|
|
144
|
+
viteArgs.push('--mode', 'development');
|
|
145
|
+
// Under Bun, `bun x --bun vite` keeps Vite itself on the Bun runtime. Under
|
|
146
|
+
// Node there is no such wrapper, so invoke the locally installed binary.
|
|
147
|
+
const [command, args] = isBun
|
|
148
|
+
? [process.execPath, ['x', '--bun', 'vite', ...viteArgs]]
|
|
149
|
+
: [join(projectRoot, 'node_modules/.bin/vite'), viteArgs];
|
|
150
|
+
log(`Running vite build${watch ? ' --watch' : ''}${dev ? ' (development)' : ''}...`);
|
|
151
|
+
const proc = spawnSync(command, args, {
|
|
152
|
+
cwd: projectRoot,
|
|
153
|
+
env: {
|
|
154
|
+
...process.env,
|
|
155
|
+
NODE_ENV: dev ? 'development' : 'production',
|
|
156
|
+
// Vite stages the config through node_modules/.vite-temp, so the plugin
|
|
157
|
+
// cannot locate this package from its own import.meta. Pass it through.
|
|
158
|
+
RSC_PACKAGE_DIR: packageDir(),
|
|
159
|
+
},
|
|
160
|
+
stdio: 'inherit',
|
|
161
|
+
});
|
|
162
|
+
if (proc.status !== 0) {
|
|
163
|
+
log('vite build failed');
|
|
164
|
+
process.exit(proc.status ?? 1);
|
|
165
|
+
}
|
|
166
|
+
log(`Build complete → ${join(outDir, 'dist')}`);
|
|
167
|
+
}
|
|
168
|
+
// Run only when invoked directly, not when imported for its helpers.
|
|
169
|
+
if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
|
|
170
|
+
main();
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=build-rsc-vite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-rsc-vite.js","sourceRoot":"","sources":["../src/build-rsc-vite.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoise a function for the length of one request.
|
|
3
|
+
*
|
|
4
|
+
* The *promise* is cached, not the value, so callers that arrive while the
|
|
5
|
+
* first is still in flight wait on it rather than starting a second. A
|
|
6
|
+
* rejection is cached too: within one request the answer to a question does
|
|
7
|
+
* not change because you asked again.
|
|
8
|
+
*
|
|
9
|
+
* Outside a request this calls straight through. Nothing is stored, nothing
|
|
10
|
+
* leaks between requests, and shared code does not have to know which side of
|
|
11
|
+
* the boundary it is on — the same reason revalidate() is a no-op outside an
|
|
12
|
+
* action.
|
|
13
|
+
*/
|
|
14
|
+
export declare function cache<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
|
|
15
|
+
/** Whether anything is listening, for code that wants to know. */
|
|
16
|
+
export declare function isCaching(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Run one request with a memo table of its own.
|
|
19
|
+
*
|
|
20
|
+
* Opened by whoever owns the request. Nesting is harmless and does not create a
|
|
21
|
+
* second table — an inner call reuses the one already open, so a host that
|
|
22
|
+
* wraps and an engine that also wraps do not end up with two.
|
|
23
|
+
*/
|
|
24
|
+
export declare function withCache<T>(run: () => Promise<T>): Promise<T>;
|
|
25
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AA2FA;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,CAuBtF;AAED,kEAAkE;AAClE,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAcpE"}
|