@constela/server 13.0.0 → 14.0.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 CHANGED
@@ -201,6 +201,104 @@ Pass style presets via `RenderOptions.styles` for evaluation.
201
201
  .dark .shiki span { color: var(--shiki-dark); }
202
202
  ```
203
203
 
204
+ ## Streaming SSR
205
+
206
+ Render to a ReadableStream for progressive HTML delivery:
207
+
208
+ ```typescript
209
+ import { renderToStream, createHtmlTransformStream } from '@constela/server';
210
+
211
+ // Render program to stream
212
+ const contentStream = renderToStream(compiledProgram, {
213
+ flushStrategy: 'batched',
214
+ }, {
215
+ route: { params: { id: '123' }, query: {}, path: '/posts/123' },
216
+ imports: { config: siteConfig },
217
+ });
218
+
219
+ // Wrap with HTML document structure
220
+ const htmlStream = contentStream.pipeThrough(
221
+ createHtmlTransformStream({
222
+ title: 'My Page',
223
+ lang: 'en',
224
+ stylesheets: ['/styles.css'],
225
+ scripts: ['/client.js'],
226
+ })
227
+ );
228
+
229
+ // Use with Response (Edge/Workers)
230
+ return new Response(htmlStream, {
231
+ headers: { 'Content-Type': 'text/html; charset=utf-8' },
232
+ });
233
+ ```
234
+
235
+ **Flush Strategies:**
236
+
237
+ | Strategy | Description |
238
+ |----------|-------------|
239
+ | `immediate` | Flush each chunk as soon as it's ready |
240
+ | `batched` | Flush when buffer exceeds 1KB threshold |
241
+ | `manual` | Only flush at the end (for small pages) |
242
+
243
+ **StreamingRenderOptions:**
244
+
245
+ ```typescript
246
+ interface StreamingRenderOptions {
247
+ flushStrategy: 'immediate' | 'batched' | 'manual';
248
+ }
249
+ ```
250
+
251
+ ### Abort Signal Support
252
+
253
+ Cancel streaming when the client disconnects:
254
+
255
+ ```typescript
256
+ const controller = new AbortController();
257
+
258
+ const stream = renderToStream(program, { flushStrategy: 'batched' }, {
259
+ signal: controller.signal,
260
+ });
261
+
262
+ // Cancel on client disconnect
263
+ request.signal.addEventListener('abort', () => {
264
+ controller.abort();
265
+ });
266
+ ```
267
+
268
+ ## Suspense Boundaries
269
+
270
+ Server-side suspense for async content:
271
+
272
+ ```json
273
+ {
274
+ "view": {
275
+ "kind": "suspense",
276
+ "id": "user-data",
277
+ "fallback": {
278
+ "kind": "element",
279
+ "tag": "div",
280
+ "props": { "className": { "expr": "lit", "value": "skeleton" } },
281
+ "children": []
282
+ },
283
+ "content": {
284
+ "kind": "component",
285
+ "name": "UserProfile",
286
+ "props": { "user": { "expr": "data", "name": "user" } }
287
+ }
288
+ }
289
+ }
290
+ ```
291
+
292
+ Renders with markers for client-side hydration:
293
+
294
+ ```html
295
+ <div data-suspense-id="user-data">
296
+ <!-- Fallback content first -->
297
+ <div class="skeleton"></div>
298
+ </div>
299
+ <!-- Resolved content follows -->
300
+ ```
301
+
204
302
  ## Security
205
303
 
206
304
  - **HTML Escaping** - All text output is escaped
package/dist/index.js CHANGED
@@ -502,6 +502,9 @@ function evaluate(expr, ctx) {
502
502
  }
503
503
  case "call": {
504
504
  const callExpr = expr;
505
+ if (callExpr.target === null) {
506
+ return void 0;
507
+ }
505
508
  const target = evaluate(callExpr.target, ctx);
506
509
  if (target == null) return void 0;
507
510
  const args = callExpr.args?.map((arg) => {
@@ -1253,6 +1256,9 @@ function evaluate2(expr, ctx) {
1253
1256
  }
1254
1257
  case "call": {
1255
1258
  const callExpr = expr;
1259
+ if (callExpr.target === null) {
1260
+ return void 0;
1261
+ }
1256
1262
  const target = evaluate2(callExpr.target, ctx);
1257
1263
  if (target == null) return void 0;
1258
1264
  const args = callExpr.args?.map((arg) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/server",
3
- "version": "13.0.0",
3
+ "version": "14.0.0",
4
4
  "description": "Server-side rendering for Constela UI framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,8 +15,8 @@
15
15
  "dist"
16
16
  ],
17
17
  "peerDependencies": {
18
- "@constela/compiler": "^0.15.0",
19
- "@constela/core": "^0.17.0"
18
+ "@constela/compiler": "^0.15.6",
19
+ "@constela/core": "^0.18.0"
20
20
  },
21
21
  "dependencies": {
22
22
  "isomorphic-dompurify": "^2.35.0",
@@ -29,8 +29,8 @@
29
29
  "tsup": "^8.0.0",
30
30
  "typescript": "^5.3.0",
31
31
  "vitest": "^2.0.0",
32
- "@constela/compiler": "0.15.0",
33
- "@constela/core": "0.17.0"
32
+ "@constela/core": "0.18.0",
33
+ "@constela/compiler": "0.15.6"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=20.0.0"