@rsc-kit/mcp 0.27.0 → 0.28.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/dist/recipes.js +108 -11
- package/dist/recipes.js.map +1 -1
- package/guides/api-routes.md +4 -2
- package/guides/deployment.md +69 -0
- package/guides/go.md +3 -2
- package/guides/laravel.md +26 -13
- package/guides/sections.md +342 -40
- package/guides/your-own-backend.md +1 -1
- package/package.json +1 -1
package/guides/api-routes.md
CHANGED
|
@@ -215,8 +215,10 @@ export const GET = events(async function* ({ params, signal }) {
|
|
|
215
215
|
|
|
216
216
|
An ordinary `route.ts`, so everything above applies: it lives beside its
|
|
217
217
|
pages, runs the `middleware.ts` above it, takes the same params. `events()`
|
|
218
|
-
frames each yield as JSON, sends a keepalive
|
|
219
|
-
stream
|
|
218
|
+
frames each yield as JSON, sends a keepalive every eight seconds so nothing in
|
|
219
|
+
front drops an idle stream ([`RSC_STREAM_KEEPALIVE_MS`](/hosts/deployment#streams-and-idle-timeouts)
|
|
220
|
+
to change it), sets `text/event-stream` and `no-store, no-transform`, is never
|
|
221
|
+
compressed, and ends the generator when
|
|
220
222
|
the browser goes away — that is what `signal` is for. `named(event, data, {
|
|
221
223
|
id })` names a message or gives it an id, for a client that listens by name or
|
|
222
224
|
resumes after a reconnect.
|
package/guides/deployment.md
CHANGED
|
@@ -160,6 +160,75 @@ names the stylesheet and the entry. Nothing to configure, and nothing it
|
|
|
160
160
|
replaces: the stylesheet still blocks paint, and inlining critical CSS
|
|
161
161
|
stacks on top of it.
|
|
162
162
|
|
|
163
|
+
## Streams and idle timeouts
|
|
164
|
+
|
|
165
|
+
An `events()` route and the [`refreshOn`](/guides/sections#refreshing-on-a-change-from-outside)
|
|
166
|
+
stream hold a connection open and are quiet most of the time. Anything in
|
|
167
|
+
front that closes a quiet connection - the server's idle timeout, a proxy's
|
|
168
|
+
read timeout - would cut them, so each sends a keepalive line every **eight
|
|
169
|
+
seconds**: under Bun's default ten, and well under nginx's sixty and
|
|
170
|
+
Cloudflare's hundred.
|
|
171
|
+
|
|
172
|
+
If something in front closes idle connections sooner, set the keepalive
|
|
173
|
+
below it:
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
RSC_STREAM_KEEPALIVE_MS=4000 # under a 5-second idle timeout
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Too long a keepalive does not lose anything - the browser reconnects, and a
|
|
180
|
+
tab catches up on whatever moved - but every open tab is cut and reopens each
|
|
181
|
+
time the timeout passes. Both streams are sent with `Cache-Control: no-store,
|
|
182
|
+
no-transform` and are never compressed, so nothing in front buffers them.
|
|
183
|
+
|
|
184
|
+
## Secrets
|
|
185
|
+
|
|
186
|
+
rsc-kit reads up to three. Each is its own key for its own job - never reuse
|
|
187
|
+
one as another, so one leaking does not hand out the rest.
|
|
188
|
+
|
|
189
|
+
| Variable | What it protects | Who needs it | Read by |
|
|
190
|
+
| --- | --- | --- | --- |
|
|
191
|
+
| `RSC_HOST_CALL_SECRET` | calls between the renderer and a Go or Laravel backend | an app with a backend - required | the renderer **and** the backend |
|
|
192
|
+
| `RSC_SIGNING_SECRET` | the names a page hands a tab for [`refreshOn`](/guides/sections#refreshing-on-a-change-from-outside) | an app that uses `refreshOn` - required in production | the renderer |
|
|
193
|
+
| `RSC_ACTION_ENCRYPTION_KEY` | values a server action sends through the browser, [across a deploy](#server-actions-across-a-deploy) | optional | the renderer, from the build |
|
|
194
|
+
|
|
195
|
+
`bun create rsc-kit` and `rsc-kit init` generate the first two into `.env` for
|
|
196
|
+
an app with a backend, and `php artisan rsc:install` does the same for
|
|
197
|
+
Laravel. That is development. For production, generate each once and keep it
|
|
198
|
+
where you keep secrets - your platform's environment settings, not the repo:
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
openssl rand -base64 32
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
or, without openssl:
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
|
|
208
|
+
php -r "echo base64_encode(random_bytes(32)), PHP_EOL;"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Three rules apply to all of them:
|
|
212
|
+
|
|
213
|
+
- **The same value on every instance.** Each is checked by a process that may
|
|
214
|
+
not be the one that issued it: a tab served by one renderer, a call landing
|
|
215
|
+
on another backend replica. A value per instance fails for some requests and
|
|
216
|
+
not others.
|
|
217
|
+
- **At runtime, from the environment.** The built server reads them when it
|
|
218
|
+
starts, so one build deploys anywhere. `RSC_ACTION_ENCRYPTION_KEY` is the
|
|
219
|
+
exception: set it at build time too.
|
|
220
|
+
- **Never in git.** The scaffold's `.gitignore` already leaves out `.env`.
|
|
221
|
+
|
|
222
|
+
**Rotating one** is a deploy, and each says so differently:
|
|
223
|
+
|
|
224
|
+
- `RSC_HOST_CALL_SECRET` - the renderer and the backend must change together.
|
|
225
|
+
Between the two, every `rpc()` is refused, so deploy both at once.
|
|
226
|
+
- `RSC_SIGNING_SECRET` - pages already open hold names signed with the old
|
|
227
|
+
key; their watching is refused and they stop refreshing until reloaded.
|
|
228
|
+
Nothing else breaks.
|
|
229
|
+
- `RSC_ACTION_ENCRYPTION_KEY` - an action called from a page loaded before the
|
|
230
|
+
change fails, the same window as a deploy without the key pinned.
|
|
231
|
+
|
|
163
232
|
## Pages across a deploy
|
|
164
233
|
|
|
165
234
|
A page from the previous build asks for a payload from the new one, whose
|
package/guides/go.md
CHANGED
|
@@ -34,7 +34,7 @@ server in production:
|
|
|
34
34
|
|
|
35
35
|
```ini title=".env"
|
|
36
36
|
RSC_BACKEND=http://127.0.0.1:8080
|
|
37
|
-
RSC_HOST_CALL_SECRET
|
|
37
|
+
RSC_HOST_CALL_SECRET=… # openssl rand -base64 32 - see Secrets under Deployment
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
And the endpoint, on whatever mux you already have:
|
|
@@ -285,7 +285,8 @@ reg.Names(&rsckit.SQLTags{DB: db, Placeholder: rsckit.Dollar}) // Postgres; ni
|
|
|
285
285
|
```
|
|
286
286
|
|
|
287
287
|
Anything with a `Bump` and a `Versions` fits. Set `RSC_SIGNING_SECRET` for the renderer, the same on every instance:
|
|
288
|
-
it signs the names a page hands the tab, and is not the host-call secret.
|
|
288
|
+
it signs the names a page hands the tab, and is not the host-call secret.
|
|
289
|
+
Generating and rotating both: [Secrets](/hosts/deployment#secrets). [Sections](/guides/sections#refreshing-on-a-change-from-outside)
|
|
289
290
|
has the whole picture.
|
|
290
291
|
|
|
291
292
|
## Production
|
package/guides/laravel.md
CHANGED
|
@@ -356,29 +356,42 @@ server's. PHP answers the renderer's ask at once rather than holding it, so
|
|
|
356
356
|
a change reaches the tab on the renderer's next ask, a couple of seconds
|
|
357
357
|
later. Set `RSC_SIGNING_SECRET` for the renderer, the same on every instance:
|
|
358
358
|
it signs the names a page hands the tab, and is not the host-call secret.
|
|
359
|
+
Generating and rotating both: [Secrets](/hosts/deployment#secrets).
|
|
359
360
|
|
|
360
|
-
**
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
**What watching costs PHP.** The renderer asks Laravel which versions moved
|
|
362
|
+
once for every tab it serves, not once per tab: one request about every two
|
|
363
|
+
seconds per renderer process while any tab is watching, each a cache read.
|
|
364
|
+
Three renderers are one or two small requests a second for the whole fleet.
|
|
365
|
+
That is the default and the recommendation - Laravel stays the only thing
|
|
366
|
+
that talks to its database.
|
|
367
|
+
|
|
368
|
+
**Keeping versions in a table** (`RSC_VERSIONS=database`) is for when you
|
|
369
|
+
want them in the database rather than the cache: pruned on a schedule with
|
|
370
|
+
`rsc:prune-versions`, or written by something outside Laravel - a Go service,
|
|
371
|
+
a worker in another language - through the [shared table](/guides/sections#the-table).
|
|
372
|
+
Laravel still answers the renderer from it; nothing else changes.
|
|
363
373
|
|
|
364
374
|
```sh
|
|
365
375
|
php artisan vendor:publish --tag=rsc-migrations && php artisan migrate
|
|
366
376
|
RSC_VERSIONS=database
|
|
367
377
|
```
|
|
368
378
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
379
|
+
**Instant, with broadcasting.** If the app has Laravel's broadcasting set up
|
|
380
|
+
(`php artisan install:broadcasting`) with a server that speaks the Pusher
|
|
381
|
+
protocol - Reverb, Pusher, Soketi - each `Rsc::changed()` can announce itself,
|
|
382
|
+
and the renderer asks the moment it hears:
|
|
372
383
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
384
|
+
```sh
|
|
385
|
+
RSC_BROADCAST=true # Laravel: announce every change
|
|
386
|
+
RSC_BROADCAST_URL=wss://ws.example.com # the renderer: where to listen
|
|
387
|
+
RSC_BROADCAST_KEY=your-app-key # the renderer: the server's app key
|
|
376
388
|
```
|
|
377
389
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
the
|
|
381
|
-
|
|
390
|
+
A change then reaches the tabs at once, and between changes the renderer asks
|
|
391
|
+
only every thirty seconds, as a safety net. The announcement carries nothing -
|
|
392
|
+
the channel is public, and names may be one visitor's - so Laravel still
|
|
393
|
+
answers which names moved, and the renderer never touches the database. It is
|
|
394
|
+
sent after the transaction commits.
|
|
382
395
|
|
|
383
396
|
## Route middleware
|
|
384
397
|
|