vue-ssr-lite 0.2.15 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +223 -970
  2. package/dist/SsrApplicationRuntime.d.ts +2 -2
  3. package/dist/SsrApplicationRuntime.d.ts.map +1 -1
  4. package/dist/SsrBrowserRuntime.d.ts +4 -4
  5. package/dist/SsrBrowserRuntime.d.ts.map +1 -1
  6. package/dist/SsrCanonicalOrigin.d.ts +21 -0
  7. package/dist/SsrCanonicalOrigin.d.ts.map +1 -0
  8. package/dist/SsrConfigCompileRuntime.d.ts +47 -24
  9. package/dist/SsrConfigCompileRuntime.d.ts.map +1 -1
  10. package/dist/SsrConfigTypes.d.ts +54 -22
  11. package/dist/SsrConfigTypes.d.ts.map +1 -1
  12. package/dist/SsrDomainRuntime.d.ts.map +1 -1
  13. package/dist/SsrEscape.d.ts +3 -0
  14. package/dist/SsrEscape.d.ts.map +1 -0
  15. package/dist/SsrManagedHead.d.ts +69 -0
  16. package/dist/SsrManagedHead.d.ts.map +1 -0
  17. package/dist/SsrPublicConfig.d.ts +5 -0
  18. package/dist/SsrPublicConfig.d.ts.map +1 -0
  19. package/dist/SsrRenderRuntime.d.ts +2 -2
  20. package/dist/SsrRenderRuntime.d.ts.map +1 -1
  21. package/dist/SsrRequestContext.d.ts +7 -2
  22. package/dist/SsrRequestContext.d.ts.map +1 -1
  23. package/dist/SsrResponseStatus.d.ts +19 -0
  24. package/dist/SsrResponseStatus.d.ts.map +1 -0
  25. package/dist/SsrRuntimeTypes.d.ts +49 -46
  26. package/dist/SsrRuntimeTypes.d.ts.map +1 -1
  27. package/dist/SsrSerialization.d.ts +2 -4
  28. package/dist/SsrSerialization.d.ts.map +1 -1
  29. package/dist/chunks/SsrApplicationRuntime-B9RwpVZI.mjs +398 -0
  30. package/dist/chunks/SsrConfigCompileRuntime-CdltAG8u.mjs +765 -0
  31. package/dist/chunks/SsrPublicConfig-DpPUzyp7.mjs +42 -0
  32. package/dist/chunks/SsrResponseStatus-BFKcIZXi.mjs +109 -0
  33. package/dist/chunks/SsrSerialization-DBzNdvSc.mjs +179 -0
  34. package/dist/chunks/SsrServerRuntime-wTuqOAdQ.mjs +760 -0
  35. package/dist/chunks/state-C1EVbCVi.mjs +177 -0
  36. package/dist/cli/SsrCliOptions.d.ts +3 -3
  37. package/dist/cli.mjs +2 -2
  38. package/dist/client.mjs +100 -71
  39. package/dist/core/extensions/ExtensionContext.d.ts +31 -0
  40. package/dist/core/extensions/ExtensionContext.d.ts.map +1 -0
  41. package/dist/core/extensions/ExtensionDefinition.d.ts +17 -0
  42. package/dist/core/extensions/ExtensionDefinition.d.ts.map +1 -0
  43. package/dist/core/extensions/ExtensionRuntime.d.ts +26 -0
  44. package/dist/core/extensions/ExtensionRuntime.d.ts.map +1 -0
  45. package/dist/core/extensions/defineExtension.d.ts +4 -0
  46. package/dist/core/extensions/defineExtension.d.ts.map +1 -0
  47. package/dist/core/extensions/index.d.ts +5 -0
  48. package/dist/core/extensions/index.d.ts.map +1 -0
  49. package/dist/extensions/resolveBuiltInExtensions.d.ts +4 -0
  50. package/dist/extensions/resolveBuiltInExtensions.d.ts.map +1 -0
  51. package/dist/extensions/seo/SeoEndpoints.d.ts +15 -0
  52. package/dist/extensions/seo/SeoEndpoints.d.ts.map +1 -0
  53. package/dist/extensions/seo/client.d.ts +4 -0
  54. package/dist/extensions/seo/client.d.ts.map +1 -0
  55. package/dist/extensions/seo/index.d.ts +9 -0
  56. package/dist/extensions/seo/index.d.ts.map +1 -0
  57. package/dist/extensions/seo/normalize.d.ts +34 -0
  58. package/dist/extensions/seo/normalize.d.ts.map +1 -0
  59. package/dist/extensions/seo/robots.d.ts +3 -0
  60. package/dist/extensions/seo/robots.d.ts.map +1 -0
  61. package/dist/extensions/seo/server.d.ts +4 -0
  62. package/dist/extensions/seo/server.d.ts.map +1 -0
  63. package/dist/extensions/seo/sitemap.d.ts +14 -0
  64. package/dist/extensions/seo/sitemap.d.ts.map +1 -0
  65. package/dist/extensions/seo/state.d.ts +21 -0
  66. package/dist/extensions/seo/state.d.ts.map +1 -0
  67. package/dist/extensions/seo/types.d.ts +89 -0
  68. package/dist/extensions/seo/types.d.ts.map +1 -0
  69. package/dist/extensions/seo/useSeo.d.ts +3 -0
  70. package/dist/extensions/seo/useSeo.d.ts.map +1 -0
  71. package/dist/index.d.ts +11 -13
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.mjs +54 -34
  74. package/dist/server/SsrHtmlRuntime.d.ts +3 -2
  75. package/dist/server/SsrHtmlRuntime.d.ts.map +1 -1
  76. package/dist/server/SsrServerRuntime.d.ts.map +1 -1
  77. package/dist/server/SsrSiteOriginRuntime.d.ts +19 -0
  78. package/dist/server/SsrSiteOriginRuntime.d.ts.map +1 -0
  79. package/dist/server/SsrSitemapConfig.d.ts +7 -0
  80. package/dist/server/SsrSitemapConfig.d.ts.map +1 -0
  81. package/dist/server.d.ts +3 -0
  82. package/dist/server.d.ts.map +1 -1
  83. package/dist/server.mjs +95 -87
  84. package/dist/vite/SsrVitePlugin.d.ts.map +1 -1
  85. package/dist/vite.mjs +81 -64
  86. package/docs/refactorplan.md +2252 -0
  87. package/package.json +5 -8
  88. package/dist/chunks/SsrApplicationRuntime-CKjoPpvD.mjs +0 -272
  89. package/dist/chunks/SsrConfigRuntime-D0DkOrVQ.mjs +0 -4
  90. package/dist/chunks/SsrDiagnosticsRuntime-B5VbSgsA.mjs +0 -28
  91. package/dist/chunks/SsrHtmlRuntime-BJUxjM2F.mjs +0 -484
  92. package/dist/chunks/SsrReactivityRuntime-Cug-AFwk.mjs +0 -28
  93. package/dist/chunks/SsrSerialization-DeKozeIm.mjs +0 -80
  94. package/dist/chunks/SsrServerRuntime-BbWemkgs.mjs +0 -716
package/README.md CHANGED
@@ -1,360 +1,103 @@
1
1
  # vue-ssr-lite
2
2
 
3
- A simple, production-ready SSR runtime for Vue 3 and Vite.
4
-
5
- Use it to run:
6
-
7
- - A server-rendered Vue website
8
- - A normal Vue SPA
9
- - Multiple SSR applications
10
- - SPA and SSR applications together
11
- - Applications on different domains or subdomains
12
-
13
- It includes routing, browser hydration, production builds, a managed Node server, health checks, custom endpoints, caching, timeouts, and Vue plugin support.
14
-
15
- ## Features
16
-
17
- - Vue 3 server-side rendering
18
- - Normal Vue SPA support
19
- - SPA and SSR in one project
20
- - Vue Router support
21
- - Automatic browser hydration
22
- - Multiple applications and HTML entries
23
- - Domain and subdomain routing
24
- - Runtime roles for one image / many process modes
25
- - Generic `publicConfig` transport (Apollo, REST, Pinia, i18n stay in the app)
26
- - SEO metadata and status codes
27
- - Custom server endpoints
28
- - Health and readiness checks
29
- - Request timeouts
30
- - Optional response caching
31
- - Cookie allow / deny filtering (including deny-only)
32
- - Proxy-aware host and protocol resolution
33
- - Graceful shutdown
34
- - TypeScript support
35
-
36
- ## Package Entry Points
3
+ Convention-first SSR for Vue 3 and Vite that scales from one normal Vue app to
4
+ many independent applications in one repository. A website, admin, dashboard,
5
+ shop, or customer portal can each choose SSR or SPA and be selected by domain
6
+ or subdomain—without separate repositories or manual server/browser bootstraps.
7
+ `vue-ssr-lite` owns the unified runtime, host routing, router history,
8
+ hydration, request isolation, and Node lifecycle while your applications remain
9
+ normal Vue applications.
37
10
 
38
- | Import | Purpose |
39
- | --- | --- |
40
- | `vue-ssr-lite` | `defineSsrConfig`, `defineSsrApplication`, `useSsrDomain`, request context |
41
- | `vue-ssr-lite/client` | Browser hydration and SPA mount |
42
- | `vue-ssr-lite/server` | Managed Node server, config compile, host matching, cookies |
43
- | `vue-ssr-lite/vite` | Vite plugin that wires HTML templates and virtual client entries |
44
-
45
- CLI binary:
11
+ ## Install
46
12
 
47
13
  ```bash
48
- vue-ssr-lite <dev|build|start> [--root .] [--config ssr.config.ts] [--server-output dist/server/SsrRuntime.js] [--hmr-port 31001]
14
+ yarn add vue-ssr-lite
49
15
  ```
50
16
 
51
- - `dev` / `build` auto-discover `ssr.config.ts|.mts|.js|.mjs` in the project root
52
- (or take `--config`).
53
- - `start` loads the baked production runtime
54
- (`dist/server/SsrRuntime.js` or `--server-output`) and does **not** require
55
- source `ssr.config.*` in the working directory — slim Docker images only need
56
- `package.json`, production `node_modules`, and `dist/`.
57
-
58
- Requires Node.js 20 or newer.
59
-
60
- Development servers use an operating-system-assigned HMR WebSocket port, so
61
- multiple `vue-ssr-lite dev` processes can run concurrently. Use `--hmr-port`
62
- or `VUE_SSR_LITE_HMR_PORT` when a proxy or container requires a fixed port.
63
-
64
- ## Installation
17
+ or:
65
18
 
66
19
  ```bash
67
- npm install vue-ssr-lite vue vue-router @vue/server-renderer
68
- npm install --save-dev vite @vitejs/plugin-vue
20
+ npm install vue-ssr-lite
69
21
  ```
70
22
 
71
- Using Yarn:
72
-
73
- ```bash
74
- yarn add vue-ssr-lite vue vue-router @vue/server-renderer
75
- yarn add --dev vite @vitejs/plugin-vue
76
- ```
77
-
78
- ## Clean consumer (canonical path)
79
-
80
- Every project follows the same five pieces. `ssr.config` is the single source of
81
- truth — there is no `spaEntries`, no `kind`, no manual `main.ts` mount, and no
82
- `server.role`.
83
-
84
- | Piece | Role |
85
- | --- | --- |
86
- | `ssr.config.ts` | Apps, domains, `runtime`, cookies, endpoints |
87
- | `defineSsrApplication()` modules | One module per app (`application.module`) |
88
- | HTML shells | Mount node only — **no** bootstrap `<script type="module" src>` |
89
- | `vite.config.ts` | `vue()` + `vueSsrLite()` |
90
- | CLI scripts | `vue-ssr-lite dev \| build \| start` |
91
-
92
- Render modes (declared as `render` on each application):
93
-
94
- | `render` | Behavior |
95
- | --- | --- |
96
- | `spa` | Serves the HTML shell; the plugin generates the browser mount entry |
97
- | `ssr` | Server-renders Vue and generates the hydration client entry |
98
-
99
- `vueSsrLite()` defaults the client Vite `build.outDir` to `dist/client` so
100
- `vue-ssr-lite start` finds assets without a consumer override. Set
101
- `build.outDir` or `server.clientOutDir` only when you need a different path.
102
-
103
- Production compile requires top-level `runtime` (typically `APP_RUNTIME`) and
104
- each app’s `domain.production`. `publicConfig` is opaque — validating API URLs
105
- is the consumer’s job.
106
-
107
- For async config, export a factory that *returns* `defineSsrConfig(...)`:
108
-
109
- ```ts
110
- export default async () =>
111
- defineSsrConfig({
112
- name: 'my-platform',
113
- runtime: process.env.APP_RUNTIME,
114
- applications: { /* ... */ },
115
- })
116
- ```
117
-
118
- `defineSsrConfig` itself is synchronous and accepts a plain object.
119
-
120
- See `fixtures/clean-consumer` in this repo for a minimal SPA project that
121
- matches this contract.
122
-
123
- ## Quick Start: SPA and SSR Together
124
-
125
- The following example creates:
126
-
127
- - A dashboard SPA on `app.example.com`
128
- - A server-rendered website on all other hosts
129
-
130
- ## 1. Create the SPA Application
23
+ `vue-ssr-lite` is designed to be added to an existing Vue 3 + Vite
24
+ application. It uses the application's existing Vue and Vite installation; no
25
+ separate `@vue/server-renderer` installation is required. `vue-router` is
26
+ provided by `vue-ssr-lite` for the simple `routes` API because the runtime
27
+ creates that router. If application code directly imports Vue Router APIs for
28
+ the advanced router factory below, keep `vue-router` as a direct dependency of
29
+ the host application (existing routed applications should keep their current
30
+ dependency).
131
31
 
132
- Define the application once. The library owns router creation, plugin
133
- installation, public-config delivery, and mounting via the generated virtual
134
- client — do **not** add a `main.ts` bootstrap.
135
-
136
- ```ts
137
- // src/dashboard/DashboardBootstrap.ts
138
- import { defineSsrApplication } from 'vue-ssr-lite'
139
- import App from './App.vue'
140
- import routes from './routes'
141
-
142
- export const createDashboardApplication = defineSsrApplication({
143
- id: 'dashboard',
144
- rootComponent: App,
145
- routes,
146
- plugins: [/* your Vue plugins */],
147
- })
148
- ```
149
-
150
- Create the SPA root component:
32
+ Requires Node.js 20 or newer.
151
33
 
152
- ```vue
153
- <!-- src/dashboard/App.vue -->
154
- <script setup lang="ts">
155
- import { RouterView } from 'vue-router'
156
- </script>
34
+ ## One repo, one app or many
157
35
 
158
- <template>
159
- <RouterView />
160
- </template>
161
- ```
36
+ Start with the zero-config path when the repository contains one application.
37
+ When a product grows, add `applications` to keep multiple independently routed
38
+ Vue applications in the same codebase:
162
39
 
163
- Create the SPA HTML shell (no bootstrap script):
40
+ ```text
41
+ my-platform/
42
+ ├── src/
43
+ │ ├── website/main.ts → example.com → SSR
44
+ │ ├── admin/main.ts → admin.example.com → SPA
45
+ │ ├── shop/main.ts → shop.example.com → SSR
46
+ │ └── dashboard/main.ts → app.example.com → SPA
47
+ ├── index.html
48
+ ├── ssr.config.ts
49
+ ├── vite.config.ts
50
+ └── package.json
51
+ ```
52
+
53
+ One unified `vue-ssr-lite` runtime/server selects the application from the
54
+ incoming host; different subdomains do not require separate repositories or
55
+ separate SSR implementations:
164
56
 
165
- ```html
166
- <!-- index.html -->
167
- <!doctype html>
168
- <html lang="en">
169
- <head>
170
- <meta charset="UTF-8" />
171
- <meta
172
- name="viewport"
173
- content="width=device-width, initial-scale=1" />
174
- <title>Dashboard</title>
175
- </head>
176
-
177
- <body>
178
- <div id="app"></div>
179
- </body>
180
- </html>
57
+ ```text
58
+ one repository → vue-ssr-lite runtime
59
+ example.com → website → SSR
60
+ admin.example.com → admin → SPA
61
+ shop.example.com → shop → SSR
62
+ app.example.com → dashboard → SPA
181
63
  ```
182
64
 
183
- ## 2. Create the SSR Application
184
-
185
- Create the SSR root component:
65
+ ## Zero-config single application
186
66
 
187
- ```vue
188
- <!-- src/website/App.vue -->
189
- <script setup lang="ts">
190
- import { RouterView } from 'vue-router'
191
- </script>
67
+ Start with the structure Vite already gives you:
192
68
 
193
- <template>
194
- <RouterView />
195
- </template>
196
- ```
197
-
198
- Create the SSR routes:
199
-
200
- ```ts
201
- // src/website/routes.ts
202
- import type { RouteRecordRaw } from 'vue-router'
203
-
204
- import HomePage from './pages/HomePage.vue'
205
- import AboutPage from './pages/AboutPage.vue'
206
- import NotFoundPage from './pages/NotFoundPage.vue'
207
-
208
- export const websiteRoutes: RouteRecordRaw[] = [
209
- {
210
- path: '/',
211
- component: HomePage,
212
- },
213
- {
214
- path: '/about',
215
- component: AboutPage,
216
- },
217
- {
218
- path: '/:pathMatch(.*)*',
219
- component: NotFoundPage,
220
- },
221
- ]
69
+ ```text
70
+ my-vue-app/
71
+ ├── src/
72
+ │ ├── main.ts
73
+ │ ├── App.vue
74
+ │ ├── router/
75
+ │ └── ...
76
+ ├── index.html
77
+ ├── vite.config.ts
78
+ └── package.json
222
79
  ```
223
80
 
224
- Define the SSR application:
81
+ Rewrite the existing browser bootstrap as a universal application definition:
225
82
 
226
83
  ```ts
227
- // src/website/SsrApplication.ts
228
- import { defineSsrApplication } from 'vue-ssr-lite'
229
-
84
+ // src/main.ts
85
+ import { defineApplication } from 'vue-ssr-lite'
230
86
  import App from './App.vue'
231
- import { websiteRoutes } from './routes'
232
-
233
- export const websiteApplication = defineSsrApplication({
234
- id: 'website',
235
- rootComponent: App,
236
- routes: websiteRoutes,
237
- })
238
- ```
239
-
240
- Create its HTML template:
241
-
242
- ```html
243
- <!-- site.html -->
244
- <!doctype html>
245
- <html lang="en">
246
- <head>
247
- <meta charset="UTF-8" />
248
- <meta
249
- name="viewport"
250
- content="width=device-width, initial-scale=1" />
251
- </head>
252
-
253
- <body>
254
- <div id="app"></div>
255
- </body>
256
- </html>
257
- ```
258
-
259
- The SSR browser entry and hydration setup are added automatically.
260
-
261
- ## 3. Create `ssr.config.ts`
87
+ import routes from './router/routes'
262
88
 
263
- Every application is one self-contained object: runtime, domain, GraphQL,
264
- cookies, endpoints, and roles. The library expands domains, matches hosts by
265
- specificity, and exposes context through `useSsrDomain()`.
266
-
267
- ```ts
268
- // ssr.config.ts — the only application / domain registration source
269
- import { defineSsrConfig } from 'vue-ssr-lite'
270
-
271
- export default defineSsrConfig({
272
- name: 'my-platform',
273
- runtime: process.env.APP_RUNTIME, // required in production
274
- server: {
275
- host: '0.0.0.0',
276
- port: Number(process.env.PORT || 4173),
277
- trustProxy: true,
278
- },
279
- applications: {
280
- dashboard: {
281
- render: 'spa',
282
- application: {
283
- module: './src/dashboard/DashboardBootstrap.ts',
284
- exportName: 'createDashboardApplication',
285
- },
286
- template: 'index.html',
287
- roles: ['unified', 'dashboard'],
288
- domain: {
289
- development: 'localhost',
290
- production: process.env.VITE_ROOT_DOMAIN!,
291
- mode: 'root-and-subdomains',
292
- localAliases: true,
293
- params: {
294
- workspace: { source: 'last-subdomain-label' },
295
- },
296
- },
297
- publicConfig: {
298
- api: {
299
- endpoint: process.env.VITE_GRAPHQL_ENDPOINT!,
300
- timeout: 8_000,
301
- },
302
- },
303
- },
304
- website: {
305
- render: 'ssr',
306
- application: {
307
- module: './src/website/SsrApplication.ts',
308
- exportName: 'websiteApplication',
309
- },
310
- template: 'site.html',
311
- roles: ['unified', 'website'],
312
- domain: {
313
- development: 'shop.localhost',
314
- production: process.env.VITE_SHOP_BASE_DOMAIN!,
315
- mode: 'root-and-subdomains',
316
- customDomains: true,
317
- params: {
318
- storeDomain: { source: 'subdomain-or-hostname' },
319
- },
320
- },
321
- publicConfig: {
322
- api: {
323
- endpoint: process.env.VITE_GRAPHQL_ENDPOINT!,
324
- timeout: 8_000,
325
- },
326
- },
327
- },
328
- },
89
+ export default defineApplication({
90
+ root: App,
91
+ routes,
329
92
  })
330
93
  ```
331
94
 
332
- The object key under `applications` is the canonical application ID everywhere
333
- (routing, Apollo `applicationId`, hydration state).
334
-
335
- Host ownership is resolved by **specificity**, not application declaration order:
336
-
337
- 1. Exact hostname
338
- 2. Longest matching wildcard suffix (for example `*.shop.example.com` beats `*.example.com`)
339
- 3. Shorter matching wildcard suffix
340
- 4. Catch-all `*` (from `customDomains: true`)
341
- 5. `defaultApplicationId` when no host pattern matches
342
-
343
- Overlapping wildcards are valid. Duplicate exact or identical wildcard patterns across applications are rejected at startup.
344
-
345
- When the server starts, the console prints a ready message with a clickable local URL and the active role:
346
-
347
- ```text
348
- ✓ Server Ready
349
-
350
- ➜ Local: http://localhost:4173/
351
- ➜ Role: unified
352
- ```
353
-
354
- ## 4. Configure Vite
95
+ The application definition describes the Vue application. The runtime chooses
96
+ `createSSRApp`, request-safe memory history, web history, hydration, and plugin
97
+ installation for the active environment. Do not call `createApp().mount()` in
98
+ this file.
355
99
 
356
- Keep Vite-only concerns here (Vue, CSS, codegen, aliases). Application wiring
357
- stays in `ssr.config.ts`:
100
+ The normal Vite integration is one plugin line:
358
101
 
359
102
  ```ts
360
103
  // vite.config.ts
@@ -363,719 +106,229 @@ import vue from '@vitejs/plugin-vue'
363
106
  import { vueSsrLite } from 'vue-ssr-lite/vite'
364
107
 
365
108
  export default defineConfig({
366
- plugins: [
367
- vueSsrLite(),
368
- vue(),
369
- ],
109
+ plugins: [vue(), vueSsrLite()],
370
110
  })
371
111
  ```
372
112
 
373
- `vueSsrLite()` generates:
374
-
375
- - HTML Rollup inputs from each `template`
376
- - Client `build.outDir` defaulting to `dist/client` (aligned with the Node server)
377
- - `virtual:vue-ssr-lite/client/<id>` SPA mount / SSR hydrate entries
378
- - `virtual:vue-ssr-lite/runtime` for the Node server (SSR apps only; SPA modules are omitted)
379
-
380
- HTML templates should not include a manual `<script type="module" src="...">`
381
- bootstrap — the plugin strips those tags from matched templates and injects the
382
- generated client entry. Editing `ssr.config.*` invalidates the cached config and
383
- triggers a full reload in dev.
384
-
385
- ## 5. Add Commands
113
+ There is no `ssr.config.ts`, `app.ts`, `entry-client.ts`, `entry-server.ts`,
114
+ `site.html`, duplicate application ID, manual hydration, or explicit domain
115
+ configuration in this path. Add the normal lifecycle scripts:
386
116
 
387
117
  ```json
388
118
  {
389
- "scripts": {
390
- "dev": "vue-ssr-lite dev",
391
- "build": "vue-ssr-lite build",
392
- "start": "vue-ssr-lite start"
393
- }
119
+ "scripts": {
120
+ "dev": "vue-ssr-lite dev",
121
+ "build": "vue-ssr-lite build",
122
+ "start": "vue-ssr-lite start"
123
+ }
394
124
  }
395
125
  ```
396
126
 
397
- ## 6. Start Development
398
-
399
- ```bash
400
- npm run dev
401
- ```
402
-
403
- Build and run production:
127
+ The defaults are:
404
128
 
405
- ```bash
406
- npm run build
407
- npm run start
408
- ```
129
+ | Concern | Convention |
130
+ | --- | --- |
131
+ | Application | `./src/main.ts` |
132
+ | HTML template | `./index.html` |
133
+ | Mount target | `#app` |
134
+ | Render mode | `ssr` |
135
+ | Single-app host | incoming `Host` header (`*`) |
136
+ | Server port | `PORT`, then the library default |
137
+ | Runtime role | `unified` |
409
138
 
410
- ## SSR-Only Application
139
+ ## Existing `index.html` stays yours
411
140
 
412
- ```ts
413
- export default defineSsrConfig({
414
- name: 'my-website',
415
- runtime: process.env.APP_RUNTIME,
416
- applications: {
417
- website: {
418
- render: 'ssr',
419
- application: {
420
- module: './src/website/SsrApplication.ts',
421
- exportName: 'websiteApplication',
422
- },
423
- template: 'site.html',
424
- domain: {
425
- development: 'localhost',
426
- production: process.env.VITE_ROOT_DOMAIN!,
427
- customDomains: true,
428
- },
429
- publicConfig: {
430
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
431
- },
432
- },
433
- },
434
- })
435
- ```
141
+ Keep the existing Vite HTML, including metadata, favicon links, styles,
142
+ analytics, verification tags, and unrelated module scripts:
436
143
 
437
- ## SPA-Only Application
438
-
439
- ```ts
440
- export default defineSsrConfig({
441
- name: 'my-dashboard',
442
- runtime: process.env.APP_RUNTIME,
443
- applications: {
444
- dashboard: {
445
- render: 'spa',
446
- application: {
447
- module: './src/dashboard/DashboardBootstrap.ts',
448
- exportName: 'createDashboardApplication',
449
- },
450
- template: 'index.html',
451
- domain: {
452
- development: 'localhost',
453
- production: process.env.VITE_ROOT_DOMAIN!,
454
- localAliases: true,
455
- },
456
- publicConfig: {
457
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
458
- },
459
- },
460
- },
461
- })
144
+ ```html
145
+ <body>
146
+ <div id="app"></div>
147
+ <script type="module" src="/src/main.ts"></script>
148
+ <script type="module" src="/analytics.ts"></script>
149
+ </body>
462
150
  ```
463
151
 
464
- The library generates the SPA client entry from `application.module`. Keep
465
- `index.html` free of manual bootstrap scripts.
466
-
467
- ## Using the Same Vue Plugins
152
+ The plugin replaces only the configured application bootstrap and preserves the
153
+ other module scripts. It adds the generated hydration entry and the internal
154
+ SSR markers without requiring a second HTML file.
468
155
 
469
- Plugins read opaque `publicConfig` — the library does not know about GraphQL:
156
+ ## Optional `ssr.config.ts` overrides
470
157
 
471
- ```ts
472
- // src/config/apollo.ts
473
- import { defineApollo } from 'vue-apollo-client'
474
-
475
- export default defineApollo(({ publicConfig }) => ({
476
- endPoints: {
477
- default: publicConfig?.api?.endpoint,
478
- },
479
- requestTimeoutMs: publicConfig?.api?.timeout,
480
- }))
481
- ```
158
+ Configuration is an override layer. Unspecified values continue to use the
159
+ conventions above.
482
160
 
483
- Use the same configuration in SSR:
161
+ Custom application entry:
484
162
 
485
163
  ```ts
486
- // src/website/SsrApplication.ts
487
- import { defineSsrApplication } from 'vue-ssr-lite'
488
-
489
- import apollo from '../config/apollo'
490
- import App from './App.vue'
491
- import { websiteRoutes } from './routes'
164
+ import { defineSsrConfig } from 'vue-ssr-lite'
492
165
 
493
- export const websiteApplication = defineSsrApplication({
494
- id: 'website',
495
- rootComponent: App,
496
- routes: websiteRoutes,
497
- plugins: [apollo],
166
+ export default defineSsrConfig({
167
+ app: './src/platform/main.ts',
498
168
  })
499
169
  ```
500
170
 
501
- This allows one plugin configuration to support both application types.
502
-
503
- You can use the same approach with:
504
-
505
- - Apollo and GraphQL
506
- - Pinia
507
- - i18n
508
- - Theme providers
509
- - REST clients
510
- - Authentication plugins
511
- - Analytics plugins
512
- - Custom Vue plugins
513
-
514
- ## Using GraphQL in SSR
515
-
516
- Keep GraphQL operations in `.graphql` files:
517
-
518
- ```graphql
519
- query GetPosts {
520
- posts {
521
- id
522
- title
523
- }
524
- }
525
- ```
526
-
527
- Use the generated composable in the Vue page:
528
-
529
- ```vue
530
- <script setup lang="ts">
531
- import { useGetPostsQuery } from '../graphql'
532
-
533
- const { result, loading, error } = useGetPostsQuery(
534
- {},
535
- {
536
- ssr: true,
537
- fetchPolicy: 'cache-first',
538
- },
539
- )
540
- </script>
541
-
542
- <template>
543
- <main>
544
- <p v-if="loading">Loading...</p>
545
-
546
- <p v-else-if="error">
547
- {{ error.message }}
548
- </p>
549
-
550
- <article
551
- v-for="post in result?.posts ?? []"
552
- :key="post.id">
553
- <h2>{{ post.title }}</h2>
554
- </article>
555
- </main>
556
- </template>
557
- ```
558
-
559
- The same generated composable can also be used inside the SPA.
560
-
561
- Use `ssr: true` for public data that should be included in server-rendered HTML.
562
-
563
- Use `ssr: false` for browser-only or private queries.
564
-
565
- ## Using REST APIs
566
-
567
- REST APIs can be used through:
568
-
569
- - Native `fetch`
570
- - Axios
571
- - Ky
572
- - A custom Vue plugin
573
- - Another SSR-compatible data package
574
-
575
- Example:
576
-
577
- ```vue
578
- <script setup lang="ts">
579
- import { onServerPrefetch, ref } from 'vue'
580
-
581
- interface Post {
582
- id: string
583
- title: string
584
- }
585
-
586
- const posts = ref<Post[]>([])
587
-
588
- const loadPosts = async () => {
589
- const response = await fetch('https://api.example.com/posts')
590
-
591
- posts.value = await response.json()
592
- }
593
-
594
- onServerPrefetch(loadPosts)
595
- </script>
596
-
597
- <template>
598
- <article
599
- v-for="post in posts"
600
- :key="post.id">
601
- <h2>{{ post.title }}</h2>
602
- </article>
603
- </template>
604
- ```
605
-
606
- For reusable REST caching and hydration, register an SSR-compatible Vue plugin in the `plugins` array.
607
-
608
- ## Public Configuration
609
-
610
- Pass opaque, browser-safe values per application in `ssr.config`:
171
+ Custom template or mount target:
611
172
 
612
173
  ```ts
613
- applications: {
614
- website: {
615
- // ...
616
- publicConfig: {
617
- apiUrl: 'https://api.example.com',
618
- graphqlEndpoint: 'https://api.example.com/graphql',
619
- },
620
- },
621
- }
622
- ```
623
-
624
- Access them in a component:
625
-
626
- ```ts
627
- import { useSsrRequestContext } from 'vue-ssr-lite'
628
-
629
- const context = useSsrRequestContext()
630
- const apiUrl = context.publicConfig.apiUrl
631
- ```
632
-
633
- The library does not interpret `publicConfig` (no GraphQL/REST assumptions).
634
- Validate transport URLs in your own plugins or env checks.
635
-
636
- ## SEO
637
-
638
- Set page metadata from a component:
639
-
640
- ```ts
641
- import { useSsrRequestContext } from 'vue-ssr-lite'
642
-
643
- const context = useSsrRequestContext()
644
-
645
- context.head.value = {
646
- title: 'Products',
647
- description: 'Browse our latest products.',
648
- robots: 'index, follow',
649
- canonicalUrl: 'https://example.com/products',
650
- ogTitle: 'Products',
651
- ogDescription: 'Browse our latest products.',
652
- ogImage: 'https://example.com/social.jpg',
653
- twitterCard: 'summary_large_image',
654
- }
655
- ```
656
-
657
- ## Status Codes
658
-
659
- ```ts
660
- const context = useSsrRequestContext()
661
-
662
- context.response.statusCode = 404
663
- ```
664
-
665
- ## Redirects
666
-
667
- ```ts
668
- const context = useSsrRequestContext()
669
-
670
- context.response.redirect = {
671
- location: '/new-page',
672
- statusCode: 307,
673
- }
674
- ```
675
-
676
- ## Domain Routing
677
-
678
- Domain configuration lives on each application. The library owns normalization
679
- (ports, trailing dots, IPv4/IPv6 aliases), proxy header handling, environment
680
- selection (`development` vs `production`), host specificity matching, custom
681
- domains, context serialization, and hydration.
682
-
683
- ```ts
684
- import { defineSsrConfig, useSsrDomain } from 'vue-ssr-lite'
685
-
686
174
  export default defineSsrConfig({
687
- name: 'my-platform',
688
- runtime: process.env.APP_RUNTIME,
689
- server: { trustProxy: true },
690
- applications: {
691
- admin: {
692
- render: 'spa',
693
- application: {
694
- module: './src/admin/AdminBootstrap.ts',
695
- exportName: 'createAdminApplication',
696
- },
697
- template: 'index.html',
698
- domain: {
699
- development: 'localhost',
700
- production: process.env.VITE_ROOT_DOMAIN!,
701
- mode: 'root-and-subdomains',
702
- localAliases: true,
703
- params: {
704
- workspace: { source: 'last-subdomain-label' },
705
- },
706
- },
707
- publicConfig: {
708
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
709
- },
710
- },
711
- website: {
712
- render: 'ssr',
713
- application: {
714
- module: './src/website/SsrApplication.ts',
715
- exportName: 'websiteApplication',
716
- },
717
- template: 'site.html',
718
- domain: {
719
- development: 'shop.localhost',
720
- production: process.env.VITE_SHOP_BASE_DOMAIN!,
721
- mode: 'root-and-subdomains',
722
- customDomains: true,
723
- params: {
724
- storeDomain: { source: 'subdomain-or-hostname' },
725
- },
726
- },
727
- publicConfig: {
728
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
729
- },
730
- },
731
- },
175
+ template: './website.html',
176
+ mount: '#website',
732
177
  })
733
178
  ```
734
179
 
735
- Consume the resolved context anywhere (SSR, SPA, endpoints, hydration):
180
+ Flat advanced single-app options stay flat:
736
181
 
737
182
  ```ts
738
- const domain = useSsrDomain()
739
-
740
- domain.entry
741
- domain.hostname
742
- domain.baseDomain
743
- domain.subdomain
744
- domain.isCustomDomain
745
- domain.params.workspace
746
- domain.params.storeDomain
747
- domain.createUrl({ subdomain: 'department.acme', path: '/projects', query: { page: 2 } })
183
+ export default defineSsrConfig({
184
+ server: {
185
+ trustProxy: true,
186
+ },
187
+ publicConfig: {
188
+ apiUrl: process.env.API_URL,
189
+ },
190
+ cookies: {
191
+ allow: ['session'],
192
+ },
193
+ })
748
194
  ```
749
195
 
750
- ### Precedence
751
-
752
- | Priority | Pattern | Example winner |
753
- | --- | --- | --- |
754
- | 1 | Exact hostname | `shop.localhost` over `*.localhost` |
755
- | 2 | Longer wildcard suffix | `*.shop.localhost` over `*.localhost` |
756
- | 3 | Shorter wildcard suffix | `*.localhost` over `*` |
757
- | 4 | Catch-all `*` | custom domains with no specific rule |
758
- | 5 | `defaultApplicationId` | only when no pattern matches |
759
-
760
- Overlapping roots such as `*.localhost` and `*.shop.localhost` are supported
761
- because the longer suffix wins. Duplicate exact/wildcard ownership and multiple
762
- catch-all (`*`) applications are rejected at startup.
763
-
764
- Do not include protocols, paths, query strings, or ports in domain values.
765
-
766
- `localAliases: true` adds bare loopback hosts (`localhost`, `127.0.0.1`, …) only
767
- when the app’s development base is itself a loopback hostname. Subdomain bases
768
- such as `shop.localhost` already expand to `*.shop.localhost` and do not claim
769
- bare `localhost`, so multiple apps can enable `localAliases` safely.
196
+ Do not put single-application fields beside `applications`. Mixed configuration
197
+ is rejected early; move the field into the relevant application entry instead.
770
198
 
771
- Set `server.trustProxy` to `true` only when the Node process sits behind a trusted reverse proxy. Then `X-Forwarded-Host` and `X-Forwarded-Proto` are used for host matching and absolute URLs. Leave it `false` for direct local traffic.
199
+ ## Plugins and advanced routers
772
200
 
773
- ## Runtime Roles
774
-
775
- Roles let one codebase and one Docker image run as different process modes.
776
-
777
- - Set top-level `runtime` to the active mode for this process.
778
- - Set `roles` on each application to list which modes may serve it.
779
- - Omit `roles` (or omit `runtime`) to keep an application available in every mode.
780
-
781
- Role names are application-defined strings. Common patterns:
782
-
783
- | Mode | Typical use |
784
- | --- | --- |
785
- | `unified` | Local development or a single process that serves every application |
786
- | A private role | SPA / admin / back-office only |
787
- | A public role | SSR website / marketing / storefront only |
788
-
789
- Example:
201
+ Use a plugin factory for stateful integrations so each SSR request receives a
202
+ fresh Pinia, i18n, Apollo, or other request-sensitive instance:
790
203
 
791
204
  ```ts
792
- export default defineSsrConfig({
793
- name: 'my-platform',
794
- runtime: process.env.APP_RUNTIME, // required in production
795
- applications: {
796
- admin: {
797
- render: 'spa',
798
- application: {
799
- module: './src/admin/AdminBootstrap.ts',
800
- exportName: 'createAdminApplication',
801
- },
802
- template: 'index.html',
803
- roles: ['unified', 'admin'],
804
- domain: {
805
- development: 'localhost',
806
- production: process.env.VITE_ROOT_DOMAIN!,
807
- mode: 'root-and-subdomains',
808
- localAliases: true,
809
- },
810
- publicConfig: {
811
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
812
- },
813
- },
814
- website: {
815
- render: 'ssr',
816
- application: {
817
- module: './src/website/SsrApplication.ts',
818
- exportName: 'websiteApplication',
819
- },
820
- template: 'site.html',
821
- roles: ['unified', 'website'],
822
- domain: {
823
- development: 'shop.localhost',
824
- production: process.env.VITE_SHOP_BASE_DOMAIN!,
825
- mode: 'root-and-subdomains',
826
- customDomains: true,
827
- },
828
- publicConfig: {
829
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
830
- },
831
- },
832
- },
205
+ export default defineApplication({
206
+ root: App,
207
+ routes,
208
+ plugins: () => [
209
+ createPinia(),
210
+ createI18n(),
211
+ createApollo(),
212
+ ],
833
213
  })
834
214
  ```
835
215
 
836
- With that setup:
837
-
838
- - `APP_RUNTIME=unified` serves both applications (host routing still applies).
839
- - `APP_RUNTIME=admin` serves only the SPA application.
840
- - `APP_RUNTIME=website` serves only the SSR application.
841
-
842
- If a host matches an application that the current role does not allow, the server responds with `421`.
843
-
844
- Development may omit `runtime` (the compiler defaults the process role to
845
- `unified`). Production compilation rejects a missing `runtime` or
846
- `domain.production`. It does **not** validate `publicConfig` shape — pass
847
- whatever your plugins need and validate API URLs in the consumer.
848
-
849
- For async loading, export `async () => defineSsrConfig({ ... })`.
850
-
851
- ## Custom Endpoints
216
+ Stateless global-safe plugin arrays remain supported. For mature router setups,
217
+ the library supplies the environment-appropriate history while the application
218
+ keeps ownership of router options. If this factory directly imports
219
+ `createRouter` or other Vue Router APIs, the host application must declare
220
+ `vue-router` as a direct dependency; do not rely on another package's
221
+ transitive dependency under strict/non-hoisting package managers:
852
222
 
853
223
  ```ts
854
- endpoints: [
855
- {
856
- id: 'robots',
857
-
858
- match(request) {
859
- return request.pathname === '/robots.txt'
860
- },
861
-
862
- handle() {
863
- return {
864
- statusCode: 200,
865
- body: 'User-agent: *\nAllow: /',
866
- headers: {
867
- 'content-type': 'text/plain; charset=utf-8',
868
- },
869
- }
870
- },
871
- },
872
- ]
224
+ import { createRouter } from 'vue-router'
225
+
226
+ export default defineApplication({
227
+ root: App,
228
+ router: ({ history }) => createRouter({
229
+ history,
230
+ routes,
231
+ scrollBehavior,
232
+ }),
233
+ })
873
234
  ```
874
235
 
875
- Custom endpoints can be used for:
876
-
877
- - `robots.txt`
878
- - `sitemap.xml`
879
- - Verification files
880
- - Public JSON endpoints
236
+ `routes` and `router` are mutually exclusive. Router-less applications do not
237
+ create Vue Router history at all.
881
238
 
882
- ## Health Checks
239
+ ## Multi-application projects
883
240
 
884
- The server includes:
885
-
886
- ```text
887
- /healthz
888
- /readyz
889
- ```
890
-
891
- Add readiness checks:
241
+ Multi-app mode is for products where several Vue applications live in the same
242
+ repository. They can share components, packages, types, services, and
243
+ infrastructure while keeping independent roots, routes, rendering modes,
244
+ templates, and host routing. Introduce `applications` only when there are
245
+ actually multiple applications; a single app does not need this configuration.
246
+ Each object key is the canonical application ID; do not repeat it in
247
+ `src/*/main.ts`.
892
248
 
893
249
  ```ts
894
- readiness: [
895
- {
896
- id: 'api',
897
-
898
- async run() {
899
- const response = await fetch('https://api.example.com/health')
900
-
901
- if (!response.ok) {
902
- throw new Error('API is unavailable.')
903
- }
904
- },
905
- },
906
- ]
250
+ import { defineSsrConfig } from 'vue-ssr-lite'
251
+
252
+ export default defineSsrConfig({
253
+ applications: {
254
+ website: {
255
+ app: './src/website/main.ts',
256
+ host: 'example.com',
257
+ },
258
+ admin: {
259
+ app: './src/admin/main.ts',
260
+ render: 'spa',
261
+ host: 'admin.example.com',
262
+ },
263
+ shop: {
264
+ app: './src/shop/main.ts',
265
+ host: 'shop.example.com',
266
+ },
267
+ dashboard: {
268
+ app: './src/dashboard/main.ts',
269
+ render: 'spa',
270
+ host: 'app.example.com',
271
+ },
272
+ },
273
+ })
907
274
  ```
908
275
 
909
- ## Response Caching
276
+ Omitted `render` means SSR, so `website` and `shop` are SSR applications while
277
+ `admin` and `dashboard` are SPAs. Rendering mode belongs to each application;
278
+ one SPA does not change the mode of the others. Each application module
279
+ default-exports `defineApplication(...)`:
910
280
 
911
281
  ```ts
912
- import { createSsrMemoryResponseCache } from 'vue-ssr-lite/server'
282
+ // src/website/main.ts
283
+ import { defineApplication } from 'vue-ssr-lite'
284
+ import App from './App.vue'
913
285
 
914
- const responseCache = createSsrMemoryResponseCache({
915
- maxEntries: 500,
286
+ export default defineApplication({
287
+ root: App,
916
288
  })
917
289
  ```
918
290
 
919
- Add it on the application object:
291
+ For application host routing, use exact hosts such as `example.com`,
292
+ subdomains such as `admin.example.com`, wildcard hosts such as
293
+ `*.shop.example.com`, or the advanced `domain` options. Host specificity
294
+ determines the winner; duplicate ownership and ambiguous routing fail during
295
+ startup.
920
296
 
921
- ```ts
922
- website: {
923
- render: 'ssr',
924
- application: {
925
- module: './src/website/SsrApplication.ts',
926
- exportName: 'websiteApplication',
927
- },
928
- template: 'site.html',
929
- domain: {
930
- development: 'localhost',
931
- production: process.env.VITE_ROOT_DOMAIN!,
932
- customDomains: true,
933
- },
934
- responseCache: {
935
- store: responseCache,
936
- ttlMs: 60_000,
937
- },
938
- publicConfig: {
939
- api: { endpoint: process.env.VITE_API_ENDPOINT!, timeout: 8_000 },
940
- },
941
- }
942
- ```
297
+ ## Advanced capabilities
943
298
 
944
- ## Cookie Filtering
299
+ The normalized runtime still supports roles, domains and subdomains,
300
+ `publicConfig`, cookies, endpoints, response caching, readiness probes,
301
+ diagnostics, metrics, redirects, status codes, and proxy-aware host handling.
302
+ These belong in optional configuration and application code rather than in the
303
+ normal consumer bootstrap.
945
304
 
946
- SSR requests can forward a filtered `Cookie` header to upstream APIs. Configure
947
- cookies per application:
305
+ Request context and domain context are available to advanced integrations:
948
306
 
949
307
  ```ts
950
- cookies: {
951
- allow: ['session'],
952
- deny: ['admin_token', 'refresh_token'],
953
- }
954
- ```
955
-
956
- - Both empty → forward nothing (secure default when SSR needs no cookies).
957
- - `allow` empty and `deny` non-empty → forward all cookies except `deny`.
958
- - `allow` non-empty → forward `allow ∩ ¬deny`.
308
+ import { useSsrDomain, useSsrRequestContext } from 'vue-ssr-lite'
959
309
 
960
- ## Server Configuration
961
-
962
- ```ts
963
- server: {
964
- host: '0.0.0.0',
965
- port: 4173,
966
- trustProxy: false,
967
- requestTimeoutMs: 15_000,
968
- shutdownTimeoutMs: 10_000,
969
- healthPath: '/healthz',
970
- readinessPath: '/readyz',
971
- logger: {
972
- info: (event, details) => console.info(event, details ?? ''),
973
- warn: (event, details) => console.warn(event, details ?? ''),
974
- error: (event, details) => console.error(event, details ?? ''),
975
- },
976
- }
310
+ const domain = useSsrDomain()
311
+ const request = useSsrRequestContext()
977
312
  ```
978
313
 
979
- The active process role comes from top-level `runtime`, not `server.role`.
980
- `/healthz` and `/readyz` include that role in their JSON responses.
981
-
982
- ## Server-side data resolution
983
-
984
- `renderToString` awaits each component's native `onServerPrefetch`, which covers
985
- data consumed reactively inside the component that declared it. For work started
986
- OUTSIDE that lifecycle — a store action, an i18n loader, a lazy query — the
987
- package exposes a generic, API-client-neutral **resolution contract**.
988
-
989
- An installed plugin obtains it by injecting `SSR_REQUEST_RESOLUTION`
990
- (`Symbol.for('vue-ssr:request-resolution')`, resolvable without importing this
991
- package). It registers in-flight work with `track(promise)` and may call
992
- `requestAdditionalPass()`. After each render pass the renderer awaits registered
993
- work and re-renders when a plugin asked for another pass, up to
994
- `server.maxResolutionPasses` (default 4) and bounded by
995
- `server.resolutionDeadlineMs` and the request abort signal.
996
-
997
- A fully resolvable page completes in **one pass** — extra passes occur only when
998
- a plugin left work pending or requested one. `vue-ssr-lite` never inspects the
999
- work; it only awaits it.
1000
-
1001
- ### Deferred parent → child dependencies
314
+ `publicConfig` is opaque and browser-safe; validate API URLs and integration
315
+ details in the consuming application or plugin.
1002
316
 
1003
- A common pattern is: a parent query resolves, its result determines which child
1004
- components mount, and each child runs its own async work. When a child's data is
1005
- consumed DIRECTLY inside that child, a single pass suffices — Vue awaits the
1006
- child's `onServerPrefetch` before rendering it.
317
+ ## Failure messages
1007
318
 
1008
- It does NOT suffice when the child's data is consumed INDIRECTLY — the child
1009
- writes into a shared store that a **sibling** component reads — because Vue does
1010
- not block a sibling's render on an earlier sibling's `onServerPrefetch`. The
1011
- sibling renders before the store is populated.
319
+ Convention discovery fails early with actionable guidance when `src/main.ts`,
320
+ `index.html`, or the configured mount target is missing. The compiler also
321
+ reports invalid application exports, mixed single-/multi-app configuration, and
322
+ unresolved multi-app host routing before the server handles traffic.
1012
323
 
1013
- **Applications write no orchestration for this.** Reconcile resolved data into
1014
- the store with `ssrWatch` exactly as you already would:
324
+ ## Package entry points
1015
325
 
1016
- ```ts
1017
- const { result } = useMyQuery(vars, { ssr: true })
1018
- ssrWatch(() => result.value, (data) => {
1019
- if (data) store.set(key, data)
1020
- }, { immediate: true })
1021
- ```
1022
-
1023
- `ssrWatch` automatically requests one more render pass when it fires from the
1024
- awaited prefetch (after a sibling already rendered). On the resumed pass the
1025
- data is warm in the API client's request cache — `vue-apollo-client` settles it
1026
- synchronously at setup — so the same `ssrWatch` runs on its immediate tick, the
1027
- sibling sees the store, and no further pass is requested. Bounded, and each
1028
- operation runs exactly once. Nothing app-specific, and nothing to repeat per
1029
- component or per project.
1030
-
1031
- ## SSR-safe reactivity
1032
-
1033
- During SSR, Vue does not flush the scheduler, so `watch(src, cb)` and
1034
- `watchEffect` run at most once and are then discarded — the most common cause of
1035
- "the shell renders but the content is missing" bugs. Server-safe APIs:
1036
-
1037
- | API | Server behaviour |
326
+ | Import | Purpose |
1038
327
  | --- | --- |
1039
- | `computed` | ✅ Lazily evaluated during render |
1040
- | `ssrWatch(src, cb)` / `ssrWatchEffect(fn)` | ✅ Sync flush, active during render |
1041
- | `watch(src, cb)` / `watchEffect(fn)` | ⚠️ Runs once (or never), then discarded |
1042
- | `onServerPrefetch(async fn)` | ✅ Awaited before the component renders |
1043
- | `onMounted` / `onUpdated` | ❌ Browser only |
1044
-
1045
- Use a `computed` to derive a value for the template; reach for `ssrWatch` /
1046
- `ssrWatchEffect` (exported from `vue-ssr-lite` and `vue-ssr-lite/client`) when
1047
- resolved data must drive an imperative side effect during the server render.
1048
-
1049
- ## Runtime configuration helpers
1050
-
1051
- `vue-ssr-lite/server` provides declarative helpers so a runtime definition is
1052
- configuration, not boilerplate: `ssrEnvBoolean`, `ssrEnvNumber`, `ssrEnvList`,
1053
- `requireSsrEnv`, `requireSsrHostname`, `requireSsrEnum`, `createSsrConsoleLogger`
1054
- and `createSsrSeoEndpoints` (`robots.txt` / `sitemap.xml` — `served`, `proxy` or
1055
- `disallow` by option). Host helpers such as `normalizeSsrHost` remain exported;
1056
- reuse them rather than re-implementing.
1057
-
1058
- ## Development diagnostics
1059
-
1060
- When diagnostics are enabled (`server.diagnostics`, default on outside
1061
- production) the renderer reports, with actionable messages: a loading
1062
- placeholder or `aria-busy="true"` still present after resolution, a route that
1063
- matched no component, and a body that resolved to nothing. These are dev-only
1064
- warnings; production render paths are untouched.
1065
-
1066
- ## Summary
1067
-
1068
- 1. Add `ssr.config.ts` with `defineSsrConfig({ name, runtime, applications })`.
1069
- 2. For each app: `defineSsrApplication()` module, HTML shell (no bootstrap script),
1070
- `render: 'spa' | 'ssr'`, and `application: { module, exportName }`.
1071
- 3. Wire Vite with `vue()` + `vueSsrLite()` (client assets land in `dist/client`).
1072
- 4. Run `vue-ssr-lite dev|build|start`.
1073
- 5. In production set `APP_RUNTIME` / `runtime` and every `domain.production`.
1074
-
1075
- Optional: restrict apps with `roles`, filter cookies, add endpoints, or pass
1076
- `server.onMetrics` / `server.renderError` through `ssr.config`.
1077
-
1078
- SPA and SSR apps share one project, one build, and one production server.
328
+ | `vue-ssr-lite` | `defineApplication`, `defineSsrConfig`, request/domain context |
329
+ | `vue-ssr-lite/client` | Internal browser hydration and SPA mounting |
330
+ | `vue-ssr-lite/server` | Managed server, compilation, host matching, endpoints |
331
+ | `vue-ssr-lite/vite` | Vite HTML and generated client/server entry integration |
1079
332
 
1080
333
  ## License
1081
334