redweb 0.16.1 → 0.16.3
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/CHANGELOG.md +35 -22
- package/README.md +293 -289
- package/contract.d.ts +11 -11
- package/docs/API_EXAMPLES_VERIFICATION.md +22 -22
- package/docs/APPLICATION.md +96 -94
- package/docs/CLI.md +122 -116
- package/docs/CLIENT_DEVELOPMENT.md +9 -9
- package/docs/CONNECTED_CLIENTS_VERIFICATION.md +65 -65
- package/docs/DEVELOPMENT.md +81 -81
- package/docs/GETTING_STARTED.md +78 -58
- package/docs/LIVE_HTML.md +555 -478
- package/docs/MIGRATION.md +28 -28
- package/docs/RELEASE_TRUST.md +88 -88
- package/docs/RUNTIME_DIAGNOSTICS.md +78 -78
- package/docs/SOCKET_CONTRACTS.md +42 -42
- package/docs/SOCKET_PAGES.md +172 -172
- package/docs/SOCKET_PAGE_RELEASE_PREPARATION.md +120 -120
- package/docs/SOCKET_PAGE_VERIFICATION.md +85 -85
- package/docs/generated.json +2286 -2286
- package/docs/guides/chatroom.md +1 -1
- package/docs/guides/jsx-without-react.md +14 -14
- package/docs/reference.json +1329 -1329
- package/docs/releases/0.15.0.json +2217 -2217
- package/docs/releases/0.16.0.json +2217 -2217
- package/docs/releases/0.16.1.json +2286 -2286
- package/docs/releases/0.16.2.json +2286 -0
- package/docs/releases/0.16.3.json +2286 -0
- package/docs/snippets/components.tsx +24 -24
- package/docs/snippets/counter.tsx +16 -16
- package/docs/snippets/room-access.tsx +11 -11
- package/docs/snippets/site.css +2 -2
- package/docs/snippets/site.tsx +22 -22
- package/docs/topics.json +3 -3
- package/index.d.ts +92 -57
- package/index.js +13 -8
- package/package.json +8 -8
- package/recipes/foundation/README.md +7 -0
- package/recipes/foundation/app.test.cjs +15 -0
- package/recipes/foundation/app.tsx +12 -0
- package/recipes/shared/README.md +7 -7
- package/src/Application.js +4 -4
- package/src/access/failure-codes.json +4 -0
- package/src/cli/ProjectInitializer.js +1 -1
- package/src/cli/arguments.js +15 -3
- package/src/cli/run.js +10 -1
- package/src/cli/templates.js +34 -21
- package/src/docs/Documentation.js +25 -15
- package/src/htmx/Jsx.js +2 -2
- package/src/htmx/LiveHtmlServer.js +5 -1
- package/src/htmx/LivePage.js +5 -1
- package/src/htmx/LiveResource.js +96 -0
- package/src/htmx/PageManager.js +126 -13
- package/src/htmx/PageSocketRoute.js +132 -132
- package/src/htmx/PageTaskLane.js +39 -0
- package/src/htmx/ReactiveRenderer.js +8 -8
- package/src/htmx/SocketAction.js +19 -19
- package/src/htmx/TemplateRenderer.js +1 -1
- package/src/htmx/index.js +3 -2
- package/src/htmx/metadata.js +117 -6
- package/src/ws/BaseHandler.js +6 -6
- package/src/ws/ConnectedClients.js +207 -207
- package/src/ws/HandlerGuard.js +4 -4
- package/src/ws/RoomRegistry.js +4 -4
- package/src/ws/RouteRuntime.js +11 -11
- package/src/ws/SocketAction.js +16 -16
- package/src/ws/SocketContract.js +3 -3
- package/src/ws/SocketRoute.js +7 -7
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
# Development refresh and inspection
|
|
2
|
-
|
|
3
|
-
This API is available in `redweb@0.16.
|
|
4
|
-
|
|
5
|
-
## Browser refresh
|
|
6
|
-
|
|
7
|
-
The generated `npm run dev` command enables browser refresh while rebuilding and restarting your application. No extra application code is needed. For an existing Live HTML application, enable it explicitly:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
const app = defineApp({ pages: [CounterPage], development: { refresh: true } });
|
|
11
|
-
app.run();
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Alternatively, set `REDWEB_DEV_REFRESH=1` only for your development process. Explicit `development: { refresh: false }` overrides that environment flag. Setting `NODE_ENV=development` alone enables neither refresh nor inspection. Both features are refused at construction under `NODE_ENV=production`; changing environment variables after construction is not a mode switch. `npm start` does not set the refresh flag. Keep it out of production environments.
|
|
15
|
-
|
|
16
|
-
On direct loopback access, the initial HTML embeds the serving process's revision. The browser polls the same listener sequentially, with a two-second request deadline and one second between completed attempts. Only a valid, different revision triggers refresh. Failed builds, unavailable listeners, malformed responses and redirects do not cause reload loops. A restart before the external script finishes loading is still detected against the revision embedded in the original document.
|
|
17
|
-
|
|
18
|
-
Clean pages reload automatically after a new revision appears. The edit guard conservatively keeps the current document if it observes input/change events, sees differing form defaults when it starts, encounters an editable element with focus, or finds contenteditable content. A native, keyboard-operable notice offers **Reload and discard drafts**. Ordinary untouched selects are compared against the browser's actual reset defaults, not simply their `selected` attributes. Once confirmation is required, resetting or submitting a form does not silently permit automatic reload.
|
|
19
|
-
|
|
20
|
-
This is not autosave or a precise unsaved-change detector. False positives are intentional; custom editors or programmatic changes without input/change events may not be detected. The helper does not persist or transmit form contents, write browser storage, replay actions, or restore files/passwords after reload. It keeps the current DOM while waiting for your decision. Manual navigation, browser termination, and confirmed reload can discard drafts.
|
|
21
|
-
|
|
22
|
-
The notice uses a shadow root so ordinary reactive root updates preserve it and the application controls' existing focus/draft behavior. Its script, stylesheet and revision fetch are same-origin external resources; your CSP must allow those resources. Navigation away stops polling. History restoration resumes it, including back-forward-cache restoration where the browser supports and chooses it.
|
|
23
|
-
|
|
24
|
-
### Connections and server state
|
|
25
|
-
|
|
26
|
-
Keeping the old document does **not** make its old page token/session valid on a replacement process. Existing reconnect rules still apply to a temporary connection outage on the same server. A process restart resets in-memory page/chat/counter state; persistent application data remains the application's responsibility. Actions in flight may have uncertain outcomes. Refresh does not retry them, guarantee completion or migrate state. Reload creates a new page session.
|
|
27
|
-
|
|
28
|
-
### Access and resource boundaries
|
|
29
|
-
|
|
30
|
-
Refresh is for direct `localhost`, literal `127.x.x.x`, or `[::1]` URLs at the listener's actual port. It verifies the actual loopback peer, Host, any supplied Origin, and Fetch Metadata. It does not trust forwarding headers or support custom hostnames, tunnels or reverse-proxy origins. Rejected requests receive no refresh bootstrap; this restriction does not make the rest of your application private. An application can still bind publicly unless you separately set `bind`.
|
|
31
|
-
|
|
32
|
-
Enabled refresh reserves `/__redweb/development`, `/__redweb/development.js` and `/__redweb/development.css`. They reveal only a boot revision, fixed client code and styling—not inspection snapshots, application state or compiler output. Responses and decorated HTML are no-store. There is no extra listener or server timer. Served `live: false` pages support refresh without a live socket client; `exportStatic()` output remains script-free and never enables it from the environment. Raw `SocketServer`/`SecureSocketServer` do not accept the HTML `refresh` option.
|
|
33
|
-
|
|
34
|
-
The injected `rw-dev-refresh` element and `__redweb_dev` ID belong to this helper; do not reuse them in application markup. The repository's `npm run verify:development:browser` gate runs actual generated watchers and Chromium against real HTTP/WebSocket listeners, including edited-document confirmation and failed-build recovery. CI runs it separately from the production browser regression gate.
|
|
35
|
-
|
|
36
|
-
## Inspection
|
|
37
|
-
|
|
38
|
-
Enable inspection explicitly when starting a development application:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
const app = defineApp({
|
|
42
|
-
pages: [CounterPage],
|
|
43
|
-
development: { inspect: true },
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Await startup before reading live metadata (ESM entry point).
|
|
47
|
-
await app.run();
|
|
48
|
-
console.dir(app.inspect(), { depth: null });
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
`SocketServer` and `SecureSocketServer` accept the same option and expose the same `inspect()` method. Without the option, `inspect()` returns `null`. Merely setting `NODE_ENV=development` does not enable inspection. Explicitly enabling it while `NODE_ENV=production` throws before routes or listeners are attached. The environment check occurs at construction; changing environment variables afterward is not a runtime mode switch.
|
|
52
|
-
|
|
53
|
-
Inspection itself is an in-process, read-only API. It does not create an HTTP/debugging route, listener, browser script, background timer, or automatic logger; browser refresh is a separate option and never exposes inspection data. Do not expose inspection results through an application endpoint in production. Your existing `redweb doctor --json` command remains the source/configuration checker; it does not inspect a running process.
|
|
54
|
-
|
|
55
|
-
## What the snapshot means
|
|
56
|
-
|
|
57
|
-
`inspect()` returns immutable, versioned JSON-compatible data:
|
|
58
|
-
|
|
59
|
-
- `pages.registrations`: configured route paths, live/shared flags, class names, decorated action/state names, and descriptions of currently available owned components. It never constructs a page to discover its members. Standard decorator metadata and component fields may not exist before first construction, so `instanceMetadata: "unobserved"` means an incomplete inventory, not “this page has no actions.” Static or expired pages can have no current instance even when class metadata was observed previously.
|
|
60
|
-
- `pages.connections`: separate counts for pending HTTP-created sessions, connected sockets, closing transports or disconnect hooks in progress (`detaching`), and disconnected sessions retained for reconnect. An attached socket that is no longer open is not yet a retained/reconnectable session. Shared pages appear once in each registration's instance list even when several visitors use them.
|
|
61
|
-
- `pages.sessions`: bounded per-render descriptions using inspector-local numeric IDs. These are not page tokens, credentials, principal IDs or socket IDs. A reconnect to the same retained page session keeps its renderer ID.
|
|
62
|
-
- `sockets.routes`: registered socket paths and handler names, registered connection counts, draining status, and room/session counts. Runtime-added routes and handlers appear on the next read. `pendingUpgrades` counts currently tracked handshakes. Room names, session identifiers and stored data are omitted. A registered raw connection is not a promise that every transport is currently open; lifecycle cleanup may be in progress.
|
|
63
|
-
- `history`: the latest reactive state invalidations and flush attempts. A state invalidation lists the member/component name and affected render-owner IDs. An empty affected-owner list means no current reactive owner read that member. Several invalidations can be batched into one flush.
|
|
64
|
-
|
|
65
|
-
Page-session and underlying socket counts describe overlapping resources—do not add them together as independent visitors. Counts remain available independently of truncated detail lists. A description failure yields `available: false` for its section without reflecting exception text.
|
|
66
|
-
|
|
67
|
-
## Render history is not delivery tracing
|
|
68
|
-
|
|
69
|
-
`flush-started` reports whether the attempt is a reconnect/attach snapshot and which owners were dirty. `flush-completed`, `flush-superseded`, and `flush-failed` describe that attempt, with elapsed milliseconds. Completion can mean unchanged HTML, no transport write, or a transport write that the peer did not receive. It is **not** a delivery acknowledgement. Supersession means a disconnect, disposal or generation change made the attempt obsolete.
|
|
70
|
-
|
|
71
|
-
History does not attribute an invalidation to a particular action: timers, services and application code can also assign state. It does not retain action arguments, state values, HTML, request headers, cookies, query parameters, identities, socket contexts, or exception messages. It does not serialize getters or call action/render/lifecycle callbacks to produce a snapshot. Application accessors replacing declaration fields are skipped; standard action metadata excludes replaced accessor methods. This is not a security sandbox against hostile JavaScript Proxies or global monkey-patching.
|
|
72
|
-
|
|
73
|
-
The history observes the reactive TSX renderer's connected update path, starting with socket attachment. Initial HTTP rendering, static output, changes with no connected reactive renderer, and nonreactive template `redweb:state` transport messages are not traced. `pages.sessions[].reactive` distinguishes those sessions. Existing runtime failure diagnostics and application tests remain necessary.
|
|
74
|
-
|
|
75
|
-
## Bounds and overhead
|
|
76
|
-
|
|
77
|
-
History retains at most 256 entries per inspected server and only primitive metadata; each event's owner list retains at most 100 names. Current page/socket description lists retain at most 100 items each and share a separate 1,000-item budget per snapshot. Lists include `total` and `truncated`. Labels are limited to 128 UTF-16 code units. Names and route paths are application-defined declarations: do not put secrets in them. The local history's `total` is its sequence counter, including any contained recording failure; retained entries may therefore have gaps.
|
|
78
|
-
|
|
79
|
-
Inspection selects a specialized renderer once for the enabled server. Ordinary reactive invalidation, flush, and socket-message paths have no inspector callbacks or added inspection branches. Disabled servers retain the original renderer class. The only ordinary rendering seam is renderer-class selection when a page session is constructed. Enabled inspection deliberately does extra metadata work and allocations; its timings are diagnostic observations, not production benchmarks.
|
|
80
|
-
|
|
81
|
-
Shutdown removes page sessions and connections through the existing lifecycle. The bounded primitive history remains readable while you retain the server object; it does not keep disposed page instances alive. The inspector's local ID table uses weak keys.
|
|
1
|
+
# Development refresh and inspection
|
|
2
|
+
|
|
3
|
+
This API is available in `redweb@0.16.3`. Use documentation matching the installed package before enabling it.
|
|
4
|
+
|
|
5
|
+
## Browser refresh
|
|
6
|
+
|
|
7
|
+
The generated `npm run dev` command enables browser refresh while rebuilding and restarting your application. No extra application code is needed. For an existing Live HTML application, enable it explicitly:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const app = defineApp({ pages: [CounterPage], development: { refresh: true } });
|
|
11
|
+
app.run();
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Alternatively, set `REDWEB_DEV_REFRESH=1` only for your development process. Explicit `development: { refresh: false }` overrides that environment flag. Setting `NODE_ENV=development` alone enables neither refresh nor inspection. Both features are refused at construction under `NODE_ENV=production`; changing environment variables after construction is not a mode switch. `npm start` does not set the refresh flag. Keep it out of production environments.
|
|
15
|
+
|
|
16
|
+
On direct loopback access, the initial HTML embeds the serving process's revision. The browser polls the same listener sequentially, with a two-second request deadline and one second between completed attempts. Only a valid, different revision triggers refresh. Failed builds, unavailable listeners, malformed responses and redirects do not cause reload loops. A restart before the external script finishes loading is still detected against the revision embedded in the original document.
|
|
17
|
+
|
|
18
|
+
Clean pages reload automatically after a new revision appears. The edit guard conservatively keeps the current document if it observes input/change events, sees differing form defaults when it starts, encounters an editable element with focus, or finds contenteditable content. A native, keyboard-operable notice offers **Reload and discard drafts**. Ordinary untouched selects are compared against the browser's actual reset defaults, not simply their `selected` attributes. Once confirmation is required, resetting or submitting a form does not silently permit automatic reload.
|
|
19
|
+
|
|
20
|
+
This is not autosave or a precise unsaved-change detector. False positives are intentional; custom editors or programmatic changes without input/change events may not be detected. The helper does not persist or transmit form contents, write browser storage, replay actions, or restore files/passwords after reload. It keeps the current DOM while waiting for your decision. Manual navigation, browser termination, and confirmed reload can discard drafts.
|
|
21
|
+
|
|
22
|
+
The notice uses a shadow root so ordinary reactive root updates preserve it and the application controls' existing focus/draft behavior. Its script, stylesheet and revision fetch are same-origin external resources; your CSP must allow those resources. Navigation away stops polling. History restoration resumes it, including back-forward-cache restoration where the browser supports and chooses it.
|
|
23
|
+
|
|
24
|
+
### Connections and server state
|
|
25
|
+
|
|
26
|
+
Keeping the old document does **not** make its old page token/session valid on a replacement process. Existing reconnect rules still apply to a temporary connection outage on the same server. A process restart resets in-memory page/chat/counter state; persistent application data remains the application's responsibility. Actions in flight may have uncertain outcomes. Refresh does not retry them, guarantee completion or migrate state. Reload creates a new page session.
|
|
27
|
+
|
|
28
|
+
### Access and resource boundaries
|
|
29
|
+
|
|
30
|
+
Refresh is for direct `localhost`, literal `127.x.x.x`, or `[::1]` URLs at the listener's actual port. It verifies the actual loopback peer, Host, any supplied Origin, and Fetch Metadata. It does not trust forwarding headers or support custom hostnames, tunnels or reverse-proxy origins. Rejected requests receive no refresh bootstrap; this restriction does not make the rest of your application private. An application can still bind publicly unless you separately set `bind`.
|
|
31
|
+
|
|
32
|
+
Enabled refresh reserves `/__redweb/development`, `/__redweb/development.js` and `/__redweb/development.css`. They reveal only a boot revision, fixed client code and styling—not inspection snapshots, application state or compiler output. Responses and decorated HTML are no-store. There is no extra listener or server timer. Served `live: false` pages support refresh without a live socket client; `exportStatic()` output remains script-free and never enables it from the environment. Raw `SocketServer`/`SecureSocketServer` do not accept the HTML `refresh` option.
|
|
33
|
+
|
|
34
|
+
The injected `rw-dev-refresh` element and `__redweb_dev` ID belong to this helper; do not reuse them in application markup. The repository's `npm run verify:development:browser` gate runs actual generated watchers and Chromium against real HTTP/WebSocket listeners, including edited-document confirmation and failed-build recovery. CI runs it separately from the production browser regression gate.
|
|
35
|
+
|
|
36
|
+
## Inspection
|
|
37
|
+
|
|
38
|
+
Enable inspection explicitly when starting a development application:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const app = defineApp({
|
|
42
|
+
pages: [CounterPage],
|
|
43
|
+
development: { inspect: true },
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Await startup before reading live metadata (ESM entry point).
|
|
47
|
+
await app.run();
|
|
48
|
+
console.dir(app.inspect(), { depth: null });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`SocketServer` and `SecureSocketServer` accept the same option and expose the same `inspect()` method. Without the option, `inspect()` returns `null`. Merely setting `NODE_ENV=development` does not enable inspection. Explicitly enabling it while `NODE_ENV=production` throws before routes or listeners are attached. The environment check occurs at construction; changing environment variables afterward is not a runtime mode switch.
|
|
52
|
+
|
|
53
|
+
Inspection itself is an in-process, read-only API. It does not create an HTTP/debugging route, listener, browser script, background timer, or automatic logger; browser refresh is a separate option and never exposes inspection data. Do not expose inspection results through an application endpoint in production. Your existing `redweb doctor --json` command remains the source/configuration checker; it does not inspect a running process.
|
|
54
|
+
|
|
55
|
+
## What the snapshot means
|
|
56
|
+
|
|
57
|
+
`inspect()` returns immutable, versioned JSON-compatible data:
|
|
58
|
+
|
|
59
|
+
- `pages.registrations`: configured route paths, live/shared flags, class names, decorated action/state names, and descriptions of currently available owned components. It never constructs a page to discover its members. Standard decorator metadata and component fields may not exist before first construction, so `instanceMetadata: "unobserved"` means an incomplete inventory, not “this page has no actions.” Static or expired pages can have no current instance even when class metadata was observed previously.
|
|
60
|
+
- `pages.connections`: separate counts for pending HTTP-created sessions, connected sockets, closing transports or disconnect hooks in progress (`detaching`), and disconnected sessions retained for reconnect. An attached socket that is no longer open is not yet a retained/reconnectable session. Shared pages appear once in each registration's instance list even when several visitors use them.
|
|
61
|
+
- `pages.sessions`: bounded per-render descriptions using inspector-local numeric IDs. These are not page tokens, credentials, principal IDs or socket IDs. A reconnect to the same retained page session keeps its renderer ID.
|
|
62
|
+
- `sockets.routes`: registered socket paths and handler names, registered connection counts, draining status, and room/session counts. Runtime-added routes and handlers appear on the next read. `pendingUpgrades` counts currently tracked handshakes. Room names, session identifiers and stored data are omitted. A registered raw connection is not a promise that every transport is currently open; lifecycle cleanup may be in progress.
|
|
63
|
+
- `history`: the latest reactive state invalidations and flush attempts. A state invalidation lists the member/component name and affected render-owner IDs. An empty affected-owner list means no current reactive owner read that member. Several invalidations can be batched into one flush.
|
|
64
|
+
|
|
65
|
+
Page-session and underlying socket counts describe overlapping resources—do not add them together as independent visitors. Counts remain available independently of truncated detail lists. A description failure yields `available: false` for its section without reflecting exception text.
|
|
66
|
+
|
|
67
|
+
## Render history is not delivery tracing
|
|
68
|
+
|
|
69
|
+
`flush-started` reports whether the attempt is a reconnect/attach snapshot and which owners were dirty. `flush-completed`, `flush-superseded`, and `flush-failed` describe that attempt, with elapsed milliseconds. Completion can mean unchanged HTML, no transport write, or a transport write that the peer did not receive. It is **not** a delivery acknowledgement. Supersession means a disconnect, disposal or generation change made the attempt obsolete.
|
|
70
|
+
|
|
71
|
+
History does not attribute an invalidation to a particular action: timers, services and application code can also assign state. It does not retain action arguments, state values, HTML, request headers, cookies, query parameters, identities, socket contexts, or exception messages. It does not serialize getters or call action/render/lifecycle callbacks to produce a snapshot. Application accessors replacing declaration fields are skipped; standard action metadata excludes replaced accessor methods. This is not a security sandbox against hostile JavaScript Proxies or global monkey-patching.
|
|
72
|
+
|
|
73
|
+
The history observes the reactive TSX renderer's connected update path, starting with socket attachment. Initial HTTP rendering, static output, changes with no connected reactive renderer, and nonreactive template `redweb:state` transport messages are not traced. `pages.sessions[].reactive` distinguishes those sessions. Existing runtime failure diagnostics and application tests remain necessary.
|
|
74
|
+
|
|
75
|
+
## Bounds and overhead
|
|
76
|
+
|
|
77
|
+
History retains at most 256 entries per inspected server and only primitive metadata; each event's owner list retains at most 100 names. Current page/socket description lists retain at most 100 items each and share a separate 1,000-item budget per snapshot. Lists include `total` and `truncated`. Labels are limited to 128 UTF-16 code units. Names and route paths are application-defined declarations: do not put secrets in them. The local history's `total` is its sequence counter, including any contained recording failure; retained entries may therefore have gaps.
|
|
78
|
+
|
|
79
|
+
Inspection selects a specialized renderer once for the enabled server. Ordinary reactive invalidation, flush, and socket-message paths have no inspector callbacks or added inspection branches. Disabled servers retain the original renderer class. The only ordinary rendering seam is renderer-class selection when a page session is constructed. Enabled inspection deliberately does extra metadata work and allocations; its timings are diagnostic observations, not production benchmarks.
|
|
80
|
+
|
|
81
|
+
Shutdown removes page sessions and connections through the existing lifecycle. The bounded primitive history remains readable while you retain the server object; it does not keep disposed page instances alive. The inspector's local ID table uses weak keys.
|
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -1,58 +1,78 @@
|
|
|
1
|
-
# Build a site and its realtime backend together
|
|
2
|
-
|
|
3
|
-
Redweb renders TypeScript/TSX on Node.js and connects server-owned state and actions to the browser through WebSockets. Use it for live dashboards, chat, collaboration, documentation sites, and socket services. You can use HTTP or WebSockets independently.
|
|
4
|
-
|
|
5
|
-
It is not React, a browser component framework, a database, an identity provider, or a managed multiplayer platform. Do not use React hooks or import `react/jsx-runtime`. An edge-only host without Node listeners cannot run a live Redweb server; exported static pages need no Node runtime.
|
|
6
|
-
|
|
7
|
-
## Start
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
1
|
+
# Build a site and its realtime backend together
|
|
2
|
+
|
|
3
|
+
Redweb renders TypeScript/TSX on Node.js and connects server-owned state and actions to the browser through WebSockets. Use it for live dashboards, chat, collaboration, documentation sites, and socket services. You can use HTTP or WebSockets independently.
|
|
4
|
+
|
|
5
|
+
It is not React, a browser component framework, a database, an identity provider, or a managed multiplayer platform. Do not use React hooks or import `react/jsx-runtime`. An edge-only host without Node listeners cannot run a live Redweb server; exported static pages need no Node runtime.
|
|
6
|
+
|
|
7
|
+
## Start a project
|
|
8
|
+
|
|
9
|
+
Create the same neutral foundation whether you are following a tutorial or starting your own application:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx --yes redweb@0.16.3 init my-app
|
|
13
|
+
cd my-app
|
|
14
|
+
npm install --save-exact redweb@0.16.3
|
|
15
|
+
npm test
|
|
16
|
+
npm run dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Add capability dependencies without importing example-domain code:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npx --yes redweb@0.16.3 init my-game --with auth,multiplayer
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The default includes its real tests. `--bare` omits only the test directory, test scripts, and test-only coverage dependency; it retains the runnable application, CSS, compiler configuration, build scripts, and development watcher.
|
|
26
|
+
|
|
27
|
+
## Explore complete recipes
|
|
28
|
+
|
|
29
|
+
Choose one of these complete applications:
|
|
30
|
+
|
|
31
|
+
- [Realtime counter](../recipes/realtime/README.md): the smallest live website, sharing server-owned state between visitors.
|
|
32
|
+
- [Chatroom](../recipes/chat/README.md): reusable stateful components, messages, and live presence.
|
|
33
|
+
- [Site](../recipes/site/README.md): non-live pages with a shared layout and stylesheet.
|
|
34
|
+
- [Socket service](../recipes/socket/README.md): a typed `/match` route with separate join/move/resume handlers.
|
|
35
|
+
- [HTTP and WebSockets](../recipes/http-ws/README.md): one listener, an HTTP health endpoint and a raw `/chat` route with an explicit cleanup owner.
|
|
36
|
+
- [Private dashboard](../recipes/dashboard/README.md): persistent SQLite cards, account sessions and private live updates (Node 22.13+).
|
|
37
|
+
|
|
38
|
+
Named templates are finished examples rather than the default project structure. Each generated recipe page contains its exact files, commands, limitations, and real HTTP/WebSocket acceptance tests. Follow that recipe's version-specific setup instructions rather than mixing an unreleased example with a published npm version.
|
|
39
|
+
|
|
40
|
+
Requirements: Node.js satisfying the package's `engines` field and npm. Use a supported Node.js release in production. TypeScript and the development watcher are installed by the starter. No React, frontend bundler or broker is required. Only the dashboard starter uses a database; its native SQLite requirement is recipe-local.
|
|
41
|
+
|
|
42
|
+
The installation floor is not a security-support promise for old Node releases. See [runtime compatibility, release verification and provenance](RELEASE_TRUST.md) before choosing a production version.
|
|
43
|
+
|
|
44
|
+
## One development loop
|
|
45
|
+
|
|
46
|
+
After initialization and installation, `npm test` compiles the project, copies assets, and runs the shipped network tests. `npm run dev` watches source/configuration files, rebuilds, and restarts. Served HTML pages on direct localhost access refresh when the replacement server is ready; detected edits instead produce a confirmation notice that keeps the current document until explicit reload. This is not autosave or browser hot-module replacement; in-memory state and old socket sessions reset on restart. See [development refresh and its guarantees](DEVELOPMENT.md#browser-refresh).
|
|
47
|
+
|
|
48
|
+
Use `.tsx` for markup and extend `redweb/tsconfig.json`. Colocate CSS with the decorated page/component or declare an explicit asset root. `npm run build` prepares `dist/`; `npm start` runs that compiled application.
|
|
49
|
+
|
|
50
|
+
When setup fails, run `npx --no-install redweb doctor --json` from the application directory. Fix reported errors and examine unresolved warnings, then rerun the build and real tests. Doctor does not execute application code or prove application correctness. See [diagnostics and boundaries](CLI.md).
|
|
51
|
+
|
|
52
|
+
## The mental model
|
|
53
|
+
|
|
54
|
+
- A page is a decorated class. Its `render()` returns server-side TSX.
|
|
55
|
+
- State is server-owned data. An ordinary TSX expression reading `@state()` updates automatically when that property is assigned. Replace arrays/objects rather than mutating them in place.
|
|
56
|
+
- Ordinary live pages expose decorated actions. Socket-bound pages expose their registered typed handlers through TSX controls. Validate and authorize every untrusted input; hiding a button is not access control.
|
|
57
|
+
- A class component owns reusable state/actions and has its own update boundary. Function components are convenient presentation helpers.
|
|
58
|
+
- Pages are connection-scoped by default. `shared: true` intentionally shares one page instance; do not put private visitor data there.
|
|
59
|
+
- Shared in-memory state survives visitors and reloads, not server restarts. Durable cards/history require application-owned persistence. Multiple processes do not automatically share memory.
|
|
60
|
+
- Socket URLs select routes; message `type` selects a handler. Do not add a second `message.action` dispatcher inside a catch-all handler.
|
|
61
|
+
|
|
62
|
+
See [rendering and lifecycle](LIVE_HTML.md) and [shared socket contracts](SOCKET_CONTRACTS.md) for exact semantics.
|
|
63
|
+
|
|
64
|
+
For private raw socket subscriptions, see [room authorization and shared request identity](ROOM_AUTHORIZATION.md). Keep authentication, subscription permission, and application-specific write permission explicit.
|
|
65
|
+
|
|
66
|
+
## Deploy deliberately
|
|
67
|
+
|
|
68
|
+
Build first. Deploy `dist/`, the package manifest, and the lockfile, then install runtime dependencies with `npm ci --omit=dev`. The starters are tested with `src/` unavailable after compilation. Configure HTTPS/WSS and a proxy that supports WebSocket upgrades when using a reverse proxy.
|
|
69
|
+
|
|
70
|
+
These deployment commands require a verified release pair. `redweb@0.16.3` installs published `redweb-client@0.3.1` automatically through its dependency. Future unreleased Redweb changes require their matching tested tarball until a release containing them is published. The `npm link` workflow is local development only: a clean production install does not preserve that link.
|
|
71
|
+
|
|
72
|
+
Before public access, add authentication, authorization, trusted-origin policy, input/rate limits, application persistence where needed, and bounded shutdown. Treat reconnect/session tokens as credentials. Do not promise exactly-once delivery or durable sessions from an in-memory starter. See [operations](MULTIPLAYER_OPERATIONS.md) and [guarantees and limits](PRODUCTION_READINESS.md).
|
|
73
|
+
|
|
74
|
+
## Evidence and compatibility
|
|
75
|
+
|
|
76
|
+
Complete recipe files are executable applications; shorter API snippets explain individual methods and may require surrounding application code. Type-check and test a complete recipe before adapting it. The package verifier runs the documented recipe files against an extracted tarball and actual listeners, not mocks.
|
|
77
|
+
|
|
78
|
+
Coverage reports refer to instrumented library code. They do not prove exhaustive browser behavior, application security, all generated-example branches, or production capacity. Historical [verification evidence](VERIFICATION_EVIDENCE.md) applies only to its recorded revision/environment; consult the [current release checklist](AGENT_READY_ACCEPTANCE.md) for remaining work.
|