@fictjs/ssr 0.26.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/README.md +194 -59
- package/package.json +15 -5
- package/dist/experimental.cjs +0 -3
- package/dist/experimental.d.cts +0 -2
- package/dist/experimental.d.ts +0 -2
- package/dist/experimental.js +0 -2
- package/dist/fict-stream-runtime.js +0 -1
- package/dist/index.cjs +0 -8
- package/dist/index.d.cts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/render-core-BCYvLfHF.d.cts +0 -166
- package/dist/render-core-BCYvLfHF.d.cts.map +0 -1
- package/dist/render-core-BCYvLfHF.d.ts +0 -166
- package/dist/render-core-BCYvLfHF.d.ts.map +0 -1
- package/dist/render-core-BLLUhkYy.js +0 -969
- package/dist/render-core-BLLUhkYy.js.map +0 -1
- package/dist/render-core-CWudV9yi.cjs +0 -1008
- package/dist/stream-runtime.cjs +0 -15
- package/dist/stream-runtime.d.cts +0 -18
- package/dist/stream-runtime.d.cts.map +0 -1
- package/dist/stream-runtime.d.ts +0 -18
- package/dist/stream-runtime.d.ts.map +0 -1
- package/dist/stream-runtime.js +0 -15
- package/dist/stream-runtime.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# @fictjs/ssr
|
|
2
2
|
|
|
3
|
-
Fict's Server-Side Rendering (SSR) package, providing
|
|
3
|
+
Fict's Satellite Server-Side Rendering (SSR) package, providing supported
|
|
4
|
+
string and streaming rendering plus opt-in client resumability.
|
|
5
|
+
|
|
6
|
+
> **Preview** — resumability, snapshot schema v2, and partial prerendering have no
|
|
7
|
+
> semver guarantee and are excluded from Core 1.0. They require
|
|
8
|
+
> `includeSnapshot: true`, compiler `resumable: true`, and the
|
|
9
|
+
> `fict/experimental/loader` entrypoint. Basic string and streaming SSR does not
|
|
10
|
+
> enable them by default.
|
|
4
11
|
|
|
5
12
|
## Table of Contents
|
|
6
13
|
|
|
@@ -20,7 +27,9 @@ Fict's Server-Side Rendering (SSR) package, providing high-performance server-si
|
|
|
20
27
|
|
|
21
28
|
## Overview
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
Supported SSR produces HTML without a resumability snapshot by default. The
|
|
31
|
+
optional **Preview Resumability** architecture is fundamentally different from
|
|
32
|
+
traditional hydration:
|
|
24
33
|
|
|
25
34
|
| Feature | Traditional Hydration | Fict Resumability |
|
|
26
35
|
| ------------------------- | --------------------------------- | --------------------------------- |
|
|
@@ -29,7 +38,7 @@ Fict SSR adopts a **Resumability** architecture, which is fundamentally differen
|
|
|
29
38
|
| Handler Loading | All preloaded | Lazy loaded on demand |
|
|
30
39
|
| State Restoration | Re-calculated | Restored from serialized snapshot |
|
|
31
40
|
|
|
32
|
-
### How
|
|
41
|
+
### How Preview Resumability Works
|
|
33
42
|
|
|
34
43
|
```
|
|
35
44
|
┌─────────────────────────────────────────────────────────────────┐
|
|
@@ -79,7 +88,7 @@ import { renderToString } from '@fictjs/ssr'
|
|
|
79
88
|
import { App } from './App'
|
|
80
89
|
|
|
81
90
|
const html = renderToString(() => <App />, {
|
|
82
|
-
includeSnapshot: true,
|
|
91
|
+
includeSnapshot: true, // Explicit Preview snapshot opt-in (default false)
|
|
83
92
|
containerId: 'app',
|
|
84
93
|
manifest: './dist/client/fict.manifest.json',
|
|
85
94
|
})
|
|
@@ -89,11 +98,15 @@ const html = renderToString(() => <App />, {
|
|
|
89
98
|
|
|
90
99
|
```typescript
|
|
91
100
|
// entry-client.tsx
|
|
92
|
-
import {
|
|
101
|
+
import { render } from 'fict'
|
|
102
|
+
import { installResumableLoader } from 'fict/experimental/loader'
|
|
103
|
+
import { App } from './App'
|
|
93
104
|
|
|
94
105
|
// Load manifest (production)
|
|
95
106
|
async function loadManifest() {
|
|
96
|
-
|
|
107
|
+
// Prefer a build-scoped URL. If the manifest has a fixed name, revalidate it
|
|
108
|
+
// instead of allowing it to become stale independently from the HTML.
|
|
109
|
+
const res = await fetch('/fict.manifest.json', { cache: 'no-store' })
|
|
97
110
|
if (res.ok) {
|
|
98
111
|
globalThis.__FICT_MANIFEST__ = await res.json()
|
|
99
112
|
}
|
|
@@ -104,6 +117,12 @@ async function init() {
|
|
|
104
117
|
|
|
105
118
|
installResumableLoader({
|
|
106
119
|
events: ['click', 'input', 'change', 'submit'],
|
|
120
|
+
onSnapshotIssue: issue => reportSnapshotIssue(issue),
|
|
121
|
+
onSnapshotRejected: () => {
|
|
122
|
+
const root = document.getElementById('app')!
|
|
123
|
+
root.replaceChildren()
|
|
124
|
+
render(() => <App />, root)
|
|
125
|
+
},
|
|
107
126
|
prefetch: {
|
|
108
127
|
visibility: true,
|
|
109
128
|
visibilityMargin: '200px',
|
|
@@ -123,18 +142,22 @@ init()
|
|
|
123
142
|
QRL is the URL format Fict uses for lazy loading handlers:
|
|
124
143
|
|
|
125
144
|
```
|
|
126
|
-
virtual:fict-handler
|
|
127
|
-
│
|
|
128
|
-
│
|
|
129
|
-
│
|
|
130
|
-
│
|
|
145
|
+
virtual:fict-handler:h0123456789abcdef0123456789abcdef$$__fict_e0#default
|
|
146
|
+
│ │ │ │
|
|
147
|
+
│ │ │ └─ Export Name
|
|
148
|
+
│ │ └─ Handler Export
|
|
149
|
+
│ └─ Opaque Source Identity
|
|
131
150
|
└─ Virtual Module Prefix
|
|
132
151
|
```
|
|
133
152
|
|
|
153
|
+
Production identities are checkout-independent and do not contain source paths.
|
|
154
|
+
|
|
134
155
|
**Representation in HTML:**
|
|
135
156
|
|
|
136
157
|
```html
|
|
137
|
-
<button on:click="virtual:fict-handler
|
|
158
|
+
<button on:click="virtual:fict-handler:h0123456789abcdef0123456789abcdef$$__fict_e0#default">
|
|
159
|
+
Click me
|
|
160
|
+
</button>
|
|
138
161
|
```
|
|
139
162
|
|
|
140
163
|
### 2. State Snapshot
|
|
@@ -144,10 +167,10 @@ During server-side rendering, component state is serialized into JSON and inject
|
|
|
144
167
|
```html
|
|
145
168
|
<script id="__FICT_SNAPSHOT__" type="application/json">
|
|
146
169
|
{
|
|
147
|
-
"v":
|
|
170
|
+
"v": 2,
|
|
148
171
|
"scopes": {
|
|
149
|
-
"s1": {
|
|
150
|
-
"id": "s1",
|
|
172
|
+
"account_scope:s1": {
|
|
173
|
+
"id": "account_scope:s1",
|
|
151
174
|
"slots": [
|
|
152
175
|
[0, "sig", 10], // Index 0: signal, value 10
|
|
153
176
|
[1, "store", {...}], // Index 1: store
|
|
@@ -162,16 +185,25 @@ During server-side rendering, component state is serialized into JSON and inject
|
|
|
162
185
|
|
|
163
186
|
**Supported Serialization Types:**
|
|
164
187
|
|
|
165
|
-
| Type
|
|
166
|
-
|
|
|
167
|
-
| Date
|
|
168
|
-
| Map
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
188
|
+
| Type / shape | Tag | Preserved detail |
|
|
189
|
+
| ----------------------------- | --------------------------- | ------------------------------------------------- |
|
|
190
|
+
| Date | `__t: 'd'` | Timestamp or invalid-date marker |
|
|
191
|
+
| Map / Set | `__t: 'm'` / `__t: 's'` | Recursively encoded entries/items |
|
|
192
|
+
| RegExp | `__t: 'r'` | Source, flags, and `lastIndex` |
|
|
193
|
+
| Global/well-known Symbol | `__t: 'sym'` | Symbol registry kind and name |
|
|
194
|
+
| Special object representation | `__t: 'o'` | Symbol keys, null prototype, or literal `__t` key |
|
|
195
|
+
| Array hole | `__t: 'h'` | Distinguishes a hole from `undefined` |
|
|
196
|
+
| undefined / NaN / negative 0 | `__t: 'u'` / `'n'` / `'-0'` | Values JSON cannot represent faithfully |
|
|
197
|
+
| Infinity | `__t: '+i'` / `__t: '-i'` | Positive/negative infinity |
|
|
198
|
+
| BigInt | `__t: 'b'` | Decimal string |
|
|
199
|
+
| Shared/circular reference | `__t: 'ref'` | Path to an already encoded object |
|
|
200
|
+
|
|
201
|
+
Schema v2 escapes plain objects that themselves contain `__t`, so literal user
|
|
202
|
+
data cannot be confused with a serialization marker. Unsupported object shapes
|
|
203
|
+
and functions stored inside value-bearing containers fail serialization rather
|
|
204
|
+
than being silently corrupted. Function-valued component-prop properties and
|
|
205
|
+
function-valued raw slots are intentionally omitted by the existing compiler
|
|
206
|
+
ABI.
|
|
175
207
|
|
|
176
208
|
### 3. Scope Registration
|
|
177
209
|
|
|
@@ -179,14 +211,31 @@ Each resumable component instance has a unique scope ID:
|
|
|
179
211
|
|
|
180
212
|
```html
|
|
181
213
|
<fict-host
|
|
182
|
-
data-fict-s="s1"
|
|
214
|
+
data-fict-s="account_scope:s1" <!-- scope ID -->
|
|
183
215
|
data-fict-h="/assets/index.js#__fict_r0" <!-- resume handler -->
|
|
184
|
-
data-fict-t="Counter@
|
|
216
|
+
data-fict-t="Counter@fict:module:m0123456789abcdef0123456789abcdef" <!-- Component Type -->
|
|
185
217
|
>
|
|
186
218
|
...
|
|
187
219
|
</fict-host>
|
|
188
220
|
```
|
|
189
221
|
|
|
222
|
+
Every SSR render receives an automatic unique namespace for these host and
|
|
223
|
+
snapshot scope IDs. For deterministic cached output, or when fragments from
|
|
224
|
+
multiple services can share a document, pass a stable `scopeIdentifierPrefix`
|
|
225
|
+
that is unique within the final document:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
renderToString(() => <App />, {
|
|
229
|
+
scopeIdentifierPrefix: 'account_scope',
|
|
230
|
+
})
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
This option is shared by string, document, Web Stream, pipeable, async-string,
|
|
234
|
+
and partial render entry points. It does not affect Suspense patch IDs; use
|
|
235
|
+
`streamIdentifierPrefix` for those. Reusing a scope prefix in one document can
|
|
236
|
+
make loader state ambiguous. Prefixes accept 1-128 ASCII letters, digits, `_`,
|
|
237
|
+
`.`, `:`, or `-`, but may not contain `--`.
|
|
238
|
+
|
|
190
239
|
### 4. Automatic Handler Extraction
|
|
191
240
|
|
|
192
241
|
The Fict compiler supports two ways to extract handlers:
|
|
@@ -245,7 +294,7 @@ interface RenderToStringOptions {
|
|
|
245
294
|
doctype?: string | null
|
|
246
295
|
|
|
247
296
|
// Resumability Configuration
|
|
248
|
-
includeSnapshot?: boolean //
|
|
297
|
+
includeSnapshot?: boolean // Preview opt-in; default: false
|
|
249
298
|
snapshotScriptId?: string // Default: '__FICT_SNAPSHOT__'
|
|
250
299
|
snapshotTarget?: 'container' | 'body' | 'head'
|
|
251
300
|
|
|
@@ -259,7 +308,22 @@ By default SSR does not write `window`, `document`, `Node`, or related DOM const
|
|
|
259
308
|
`globalThis`. This keeps concurrent renders from racing over process-global DOM state. Components
|
|
260
309
|
should use Fict's render-provided document/ownerDocument paths; set `exposeGlobals: true` only for
|
|
261
310
|
legacy code that still reads DOM globals during server render. That compatibility mode is restored
|
|
262
|
-
on `dispose()
|
|
311
|
+
on `dispose()`. Default renders may overlap each other, but a compatibility render is exclusive
|
|
312
|
+
with every other SSR render so no request can observe another request's process-global DOM. On a
|
|
313
|
+
non-extensible hardened global object, ordinary render-local SSR remains available without a
|
|
314
|
+
process marker; compatibility mode remains unavailable because it cannot acquire its exclusive
|
|
315
|
+
lease or install DOM globals.
|
|
316
|
+
|
|
317
|
+
### renderToStringAsync
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
function renderToStringAsync(view: () => FictNode, options?: RenderToStringOptions): Promise<string>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Waits until all Suspense boundaries have resolved, then serializes their final content. Its output
|
|
324
|
+
options and defaults match `renderToString`; in particular, it returns the rendered container's
|
|
325
|
+
children unless `fullDocument` or `includeContainer` is requested. Use `renderToString` when a
|
|
326
|
+
synchronous fallback shell is desired instead.
|
|
263
327
|
|
|
264
328
|
### renderToDocument
|
|
265
329
|
|
|
@@ -316,6 +380,28 @@ Trusted Types deployments should use this external observer runtime. The patch
|
|
|
316
380
|
runtime moves `<template>` content with DOM APIs (`content` + `insertBefore`) and
|
|
317
381
|
does not call `innerHTML`, `insertAdjacentHTML`, `eval`, or `Function`.
|
|
318
382
|
|
|
383
|
+
When Preview snapshots are enabled on a nonce-free strict-CSP route, keep
|
|
384
|
+
`snapshotTarget` at `container` or `body`. Incremental `head` snapshots require a
|
|
385
|
+
small executable mover; external runtime mode rejects that combination unless
|
|
386
|
+
`scriptNonce` is non-empty.
|
|
387
|
+
|
|
388
|
+
Each shell stream receives an automatic unique namespace for its Suspense patch
|
|
389
|
+
identifiers. When independently cached fragments or streams from multiple
|
|
390
|
+
services can be composed into one document, pass a stable
|
|
391
|
+
`streamIdentifierPrefix` that is unique within that final document:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
renderToStream(() => <App />, {
|
|
395
|
+
mode: 'shell',
|
|
396
|
+
streamIdentifierPrefix: 'account_shell',
|
|
397
|
+
})
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The prefix only affects streaming patch identifiers; resumable scope IDs are
|
|
401
|
+
unchanged. Reusing a prefix for two live streams in the same document can make
|
|
402
|
+
their patches ambiguous. Prefixes accept 1-128 ASCII letters, digits, `_`, `.`,
|
|
403
|
+
`:`, or `-`, but may not contain `--`.
|
|
404
|
+
|
|
319
405
|
### renderToPipeableStream
|
|
320
406
|
|
|
321
407
|
Node.js-style stream variant (compatible with `pipe()`).
|
|
@@ -372,21 +458,47 @@ interface ResumableLoaderOptions {
|
|
|
372
458
|
document?: Document
|
|
373
459
|
snapshotScriptId?: string
|
|
374
460
|
events?: string[] // Default: DelegatedEvents
|
|
461
|
+
snapshotMigrations?: Record<number, SnapshotMigration>
|
|
375
462
|
onSnapshotIssue?: (issue: SnapshotIssue) => void
|
|
463
|
+
onSnapshotRejected?: (issue: SnapshotIssue) => void | Promise<void>
|
|
376
464
|
prefetch?: PrefetchStrategy | false
|
|
377
465
|
}
|
|
378
466
|
|
|
467
|
+
type SnapshotMigration = (
|
|
468
|
+
snapshot: Record<string, unknown>,
|
|
469
|
+
context: SnapshotMigrationContext,
|
|
470
|
+
) => unknown
|
|
471
|
+
|
|
472
|
+
interface SnapshotMigrationContext {
|
|
473
|
+
fromVersion: number
|
|
474
|
+
toVersion: number
|
|
475
|
+
source: string
|
|
476
|
+
}
|
|
477
|
+
|
|
379
478
|
interface SnapshotIssue {
|
|
380
479
|
code:
|
|
381
480
|
| 'snapshot_parse_error'
|
|
382
481
|
| 'snapshot_invalid_shape'
|
|
383
482
|
| 'snapshot_unsupported_version'
|
|
483
|
+
| 'snapshot_migration_failed'
|
|
484
|
+
| 'snapshot_fallback_failed'
|
|
384
485
|
| 'scope_snapshot_missing'
|
|
486
|
+
| 'resume_import_failed'
|
|
487
|
+
| 'resume_function_missing'
|
|
488
|
+
| 'resume_failed'
|
|
489
|
+
| 'handler_import_failed'
|
|
490
|
+
| 'handler_missing'
|
|
491
|
+
| 'handler_failed'
|
|
385
492
|
message: string
|
|
386
493
|
source: string
|
|
387
494
|
expectedVersion: number
|
|
388
495
|
actualVersion?: number
|
|
389
496
|
scopeId?: string
|
|
497
|
+
qrl?: string
|
|
498
|
+
url?: string
|
|
499
|
+
exportName?: string
|
|
500
|
+
eventType?: string
|
|
501
|
+
error?: unknown
|
|
390
502
|
}
|
|
391
503
|
|
|
392
504
|
interface PrefetchStrategy {
|
|
@@ -395,8 +507,23 @@ interface PrefetchStrategy {
|
|
|
395
507
|
hover?: boolean // Default: true
|
|
396
508
|
hoverDelay?: number // Default: 50
|
|
397
509
|
}
|
|
510
|
+
|
|
511
|
+
type LegacySnapshotFormat = 'raw-props' | 'encoded-props'
|
|
512
|
+
const UNVERSIONED_SNAPSHOT_MIGRATION_KEY = 0
|
|
513
|
+
function createLegacySnapshotMigration(format: LegacySnapshotFormat): SnapshotMigration
|
|
398
514
|
```
|
|
399
515
|
|
|
516
|
+
The current writer emits v2. Missing `v` and v1 fail closed unless the
|
|
517
|
+
application explicitly selects the matching historical writer dialect. The
|
|
518
|
+
loader cannot infer whether v1 `{ "__t": "u" }` bytes mean literal data or an
|
|
519
|
+
encoded `undefined` value. See the
|
|
520
|
+
[SSR / Resume Stability Contract](../../docs/ssr-resume-stability-contract.md)
|
|
521
|
+
for the version map and migration examples.
|
|
522
|
+
|
|
523
|
+
`onSnapshotIssue` is telemetry-only. `onSnapshotRejected` runs once after the
|
|
524
|
+
loader disengages; the application must mount the CSR root. Fict does not mount
|
|
525
|
+
CSR automatically, and it does not route QRL failures to an ErrorBoundary.
|
|
526
|
+
|
|
400
527
|
## Architecture Design
|
|
401
528
|
|
|
402
529
|
### Build Time
|
|
@@ -417,26 +544,12 @@ interface PrefetchStrategy {
|
|
|
417
544
|
|
|
418
545
|
**Generated Code Structure:**
|
|
419
546
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
$effect(() => {
|
|
428
|
-
/* Bind DOM update */
|
|
429
|
-
})
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
__fictRegisterResume('__fict_r0', __fict_r0)
|
|
433
|
-
|
|
434
|
-
// Handler chunk (separate file)
|
|
435
|
-
export default (scopeId, event, el) => {
|
|
436
|
-
const [count] = __fictUseLexicalScope(scopeId, ['count'])
|
|
437
|
-
count++ // Trigger signal update
|
|
438
|
-
}
|
|
439
|
-
```
|
|
547
|
+
The exact generated ABI is compiler-owned and may change while resumability is
|
|
548
|
+
Preview. Conceptually, the main bundle registers a resume function that restores
|
|
549
|
+
the scope and reconnects reactive bindings. Each extracted handler chunk exports
|
|
550
|
+
the function named by its QRL; the loader restores the lexical scope before it
|
|
551
|
+
invokes that handler. Use compiler fixture output and package tests—not copied
|
|
552
|
+
generated snippets—as the compatibility source of truth.
|
|
440
553
|
|
|
441
554
|
### Runtime
|
|
442
555
|
|
|
@@ -470,12 +583,20 @@ Generated detailed `fict.manifest.json` during production build, mapping virtual
|
|
|
470
583
|
|
|
471
584
|
```json
|
|
472
585
|
{
|
|
473
|
-
"
|
|
474
|
-
"virtual:fict-handler
|
|
475
|
-
"
|
|
586
|
+
"fict:module:m0123456789abcdef0123456789abcdef": "/assets/index-xyz789.js",
|
|
587
|
+
"virtual:fict-handler:h0123456789abcdef0123456789abcdef$$__fict_e0": "/assets/handler-e0-abc123.js",
|
|
588
|
+
"virtual:fict-handler:h0123456789abcdef0123456789abcdef$$__fict_e1": "/assets/handler-e1-def456.js"
|
|
476
589
|
}
|
|
477
590
|
```
|
|
478
591
|
|
|
592
|
+
Only modules that own a resumable QRL are listed. Physical filenames and
|
|
593
|
+
unrelated Rollup modules are deliberately excluded.
|
|
594
|
+
|
|
595
|
+
Treat the manifest, SSR server, HTML snapshots, client loader, QRL chunks, and
|
|
596
|
+
external stream runtime as one build. Prefer a build-scoped manifest URL. If a
|
|
597
|
+
fixed `/fict.manifest.json` is unavoidable, serve it with `no-store` or mandatory
|
|
598
|
+
revalidation; do not let it use stale-while-revalidate independently from HTML.
|
|
599
|
+
|
|
479
600
|
## Integration with Vite
|
|
480
601
|
|
|
481
602
|
## Partial Prerendering
|
|
@@ -486,6 +607,8 @@ Generated detailed `fict.manifest.json` during production build, mapping virtual
|
|
|
486
607
|
2. **Deferred phase**: deliver `stream` patches for resolved Suspense boundaries.
|
|
487
608
|
|
|
488
609
|
This keeps shell TTFB low while still allowing server-resolved dynamic islands.
|
|
610
|
+
The shell and every deferred patch must come from the same build and snapshot
|
|
611
|
+
schema; never combine a cached shell with a newer server's patch stream.
|
|
489
612
|
|
|
490
613
|
## Edge Runtime
|
|
491
614
|
|
|
@@ -706,13 +829,25 @@ console.log(__fictGetResume('__fict_r0')) // Should return function
|
|
|
706
829
|
|
|
707
830
|
**Solution:**
|
|
708
831
|
|
|
709
|
-
```
|
|
710
|
-
//
|
|
711
|
-
let data = $state(null)
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
832
|
+
```tsx
|
|
833
|
+
// Serialize only lightweight state, then load details from a resumable client interaction.
|
|
834
|
+
let data = $state(null)
|
|
835
|
+
|
|
836
|
+
return (
|
|
837
|
+
<button
|
|
838
|
+
onClick$={async () => {
|
|
839
|
+
data = await fetchData(itemId)
|
|
840
|
+
}}
|
|
841
|
+
>
|
|
842
|
+
Load details
|
|
843
|
+
</button>
|
|
844
|
+
)
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
`onMount` is synchronous and also runs during server rendering. Do not use an async `onMount`
|
|
848
|
+
callback for client bootstrapping: returned promises are not awaited, and a cleanup resolved from a
|
|
849
|
+
promise is not registered. If data must load automatically on the client, use an explicit
|
|
850
|
+
client-only/CSR bootstrap instead.
|
|
716
851
|
|
|
717
852
|
### Debugging Tips
|
|
718
853
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fictjs/ssr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Fict server-side rendering",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -12,17 +12,25 @@
|
|
|
12
12
|
"directory": "packages/ssr"
|
|
13
13
|
},
|
|
14
14
|
"type": "module",
|
|
15
|
-
"main": "dist/index.cjs",
|
|
15
|
+
"main": "dist/index.node.cjs",
|
|
16
16
|
"module": "dist/index.js",
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
20
|
"types": "./dist/index.d.ts",
|
|
21
|
+
"node": {
|
|
22
|
+
"import": "./dist/index.node.js",
|
|
23
|
+
"require": "./dist/index.node.cjs"
|
|
24
|
+
},
|
|
21
25
|
"import": "./dist/index.js",
|
|
22
26
|
"require": "./dist/index.cjs"
|
|
23
27
|
},
|
|
24
28
|
"./experimental": {
|
|
25
29
|
"types": "./dist/experimental.d.ts",
|
|
30
|
+
"node": {
|
|
31
|
+
"import": "./dist/experimental.node.js",
|
|
32
|
+
"require": "./dist/experimental.node.cjs"
|
|
33
|
+
},
|
|
26
34
|
"import": "./dist/experimental.js",
|
|
27
35
|
"require": "./dist/experimental.cjs"
|
|
28
36
|
},
|
|
@@ -38,11 +46,11 @@
|
|
|
38
46
|
],
|
|
39
47
|
"dependencies": {
|
|
40
48
|
"linkedom": "^0.18.12",
|
|
41
|
-
"@fictjs/runtime": "0.
|
|
49
|
+
"@fictjs/runtime": "0.28.0"
|
|
42
50
|
},
|
|
43
51
|
"devDependencies": {
|
|
44
52
|
"tsdown": "^0.22.3",
|
|
45
|
-
"fict": "0.
|
|
53
|
+
"fict": "0.28.0"
|
|
46
54
|
},
|
|
47
55
|
"keywords": [
|
|
48
56
|
"fict",
|
|
@@ -58,8 +66,10 @@
|
|
|
58
66
|
"build:stream-runtime": "node scripts/write-stream-runtime-asset.mjs",
|
|
59
67
|
"dev": "tsdown --watch --on-success \"pnpm run build:stream-runtime\"",
|
|
60
68
|
"test": "vitest run",
|
|
69
|
+
"test:node": "node test/node-runtime.smoke.mjs",
|
|
61
70
|
"test:edge": "node test/edge-runtime.smoke.mjs",
|
|
62
|
-
"test:
|
|
71
|
+
"test:cjs": "node test/cjs-runtime.smoke.cjs",
|
|
72
|
+
"test:matrix": "pnpm run test && pnpm run test:node && pnpm run test:edge && pnpm run test:cjs",
|
|
63
73
|
"test:coverage": "vitest run --coverage",
|
|
64
74
|
"lint": "eslint src",
|
|
65
75
|
"typecheck": "tsc --noEmit",
|
package/dist/experimental.cjs
DELETED
package/dist/experimental.d.cts
DELETED
package/dist/experimental.d.ts
DELETED
package/dist/experimental.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(){if(window.__FICT_STREAM)return;var cache=new Map();function find(id){var hit=cache.get(id);if(hit)return hit;var start=null,end=null;var w=document.createTreeWalker(document,NodeFilter.SHOW_COMMENT);while(w.nextNode()){var n=w.currentNode;var d=n.data;if(d==="fict:suspense-start:"+id)start=n;else if(d==="fict:suspense-end:"+id)end=n;if(start&&end)break;}if(start&&end){hit={start:start,end:end};cache.set(id,hit);}return hit;}function apply(id){var tpl=document.querySelector('template[data-fict-suspense="' + id + '"]');if(!tpl)return;var b=find(id);if(!b)return;var node=b.start.nextSibling;while(node&&node!==b.end){var next=node.nextSibling;node.parentNode&&node.parentNode.removeChild(node);node=next;}b.end.parentNode&&b.end.parentNode.insertBefore(tpl.content,b.end);tpl.parentNode&&tpl.parentNode.removeChild(tpl);}window.__FICT_STREAM={apply:apply};function scan(root){var list=(root&&root.querySelectorAll?root:document).querySelectorAll("template[data-fict-suspense]");for(var i=0;i<list.length;i++){apply(list[i].getAttribute("data-fict-suspense"));}}if(typeof MutationObserver==="function"){new MutationObserver(function(muts){for(var i=0;i<muts.length;i++){for(var j=0;j<muts[i].addedNodes.length;j++){var n=muts[i].addedNodes[j];if(n.nodeType===1){if(n.matches&&n.matches("template[data-fict-suspense]"))apply(n.getAttribute("data-fict-suspense"));scan(n);}}}}).observe(document.documentElement||document,{childList:true,subtree:true});}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",function(){scan(document);},{once:true});}else{scan(document);}})();
|
package/dist/index.cjs
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_render_core = require("./render-core-CWudV9yi.cjs");
|
|
3
|
-
exports.createSSRDocument = require_render_core.createSSRDocument;
|
|
4
|
-
exports.renderToDocument = require_render_core.renderToDocument;
|
|
5
|
-
exports.renderToPipeableStream = require_render_core.renderToPipeableStream;
|
|
6
|
-
exports.renderToStream = require_render_core.renderToStream;
|
|
7
|
-
exports.renderToString = require_render_core.renderToString;
|
|
8
|
-
exports.renderToStringAsync = require_render_core.renderToStringAsync;
|
package/dist/index.d.cts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as RenderToStringOptions, c as renderToDocument, d as renderToStream, f as renderToString, i as RenderToStreamOptions, n as PipeableStream, o as SSRDom, p as renderToStringAsync, r as RenderToDocumentResult, s as createSSRDocument, u as renderToPipeableStream } from "./render-core-BCYvLfHF.cjs";
|
|
2
|
-
export { type PipeableStream, type RenderToDocumentResult, type RenderToStreamOptions, type RenderToStringOptions, type SSRDom, createSSRDocument, renderToDocument, renderToPipeableStream, renderToStream, renderToString, renderToStringAsync };
|
package/dist/index.d.ts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as RenderToStringOptions, c as renderToDocument, d as renderToStream, f as renderToString, i as RenderToStreamOptions, n as PipeableStream, o as SSRDom, p as renderToStringAsync, r as RenderToDocumentResult, s as createSSRDocument, u as renderToPipeableStream } from "./render-core-BCYvLfHF.js";
|
|
2
|
-
export { type PipeableStream, type RenderToDocumentResult, type RenderToStreamOptions, type RenderToStringOptions, type SSRDom, createSSRDocument, renderToDocument, renderToPipeableStream, renderToStream, renderToString, renderToStringAsync };
|
package/dist/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as renderToStream, i as renderToPipeableStream, n as renderToDocument, o as renderToString, s as renderToStringAsync, t as createSSRDocument } from "./render-core-BLLUhkYy.js";
|
|
2
|
-
export { createSSRDocument, renderToDocument, renderToPipeableStream, renderToStream, renderToString, renderToStringAsync };
|