@constela/server 17.0.1 → 18.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/dist/index.d.ts CHANGED
@@ -1,23 +1,13 @@
1
1
  import { CompiledProgram } from '@constela/compiler';
2
- import { StreamingRenderOptions } from '@constela/core';
2
+ import { StylePreset, StreamingRenderOptions } from '@constela/core';
3
3
 
4
4
  /**
5
5
  * SSR Renderer
6
6
  *
7
7
  * Renders CompiledProgram to HTML string for Server-Side Rendering.
8
+ * Uses the unified evaluate module from @constela/core.
8
9
  */
9
10
 
10
- /**
11
- * Style preset definition for SSR
12
- */
13
- interface StylePreset$1 {
14
- base: string;
15
- variants?: Record<string, Record<string, string>>;
16
- defaultVariants?: Record<string, string>;
17
- compoundVariants?: Array<Record<string, string> & {
18
- class: string;
19
- }>;
20
- }
21
11
  /**
22
12
  * Options for renderToString
23
13
  */
@@ -28,7 +18,7 @@ interface RenderOptions {
28
18
  path?: string;
29
19
  };
30
20
  imports?: Record<string, unknown>;
31
- styles?: Record<string, StylePreset$1>;
21
+ styles?: Record<string, StylePreset>;
32
22
  stateOverrides?: Record<string, unknown>;
33
23
  cookies?: Record<string, string>;
34
24
  }
@@ -46,6 +36,7 @@ declare function renderToString(program: CompiledProgram, options?: RenderOption
46
36
  *
47
37
  * Renders CompiledProgram to a ReadableStream for Server-Side Rendering.
48
38
  * Uses Web Streams API for Edge Runtime compatibility.
39
+ * Uses the unified evaluate module from @constela/core.
49
40
  *
50
41
  * Features:
51
42
  * - Three flush strategies: immediate, batched, manual
@@ -54,17 +45,6 @@ declare function renderToString(program: CompiledProgram, options?: RenderOption
54
45
  * - Suspense boundary support for async content
55
46
  */
56
47
 
57
- /**
58
- * Style preset definition for SSR
59
- */
60
- interface StylePreset {
61
- base: string;
62
- variants?: Record<string, Record<string, string>>;
63
- defaultVariants?: Record<string, string>;
64
- compoundVariants?: Array<Record<string, string> & {
65
- class: string;
66
- }>;
67
- }
68
48
  /**
69
49
  * Extended options for streaming render
70
50
  */
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/renderer.ts
2
- import { isCookieInitialExpr, callGlobalFunction } from "@constela/core";
2
+ import { isCookieInitialExpr, evaluate as coreEvaluate } from "@constela/core";
3
3
 
4
4
  // src/markdown.ts
5
5
  import { marked } from "marked";
@@ -108,7 +108,37 @@ async function parseMarkdownSSRAsync(content) {
108
108
  });
109
109
  }
110
110
 
111
- // src/renderer.ts
111
+ // src/shared.ts
112
+ import { GLOBAL_FUNCTIONS } from "@constela/core";
113
+ var ssrAdapter = {
114
+ resolveRef: () => null,
115
+ resolveValidity: () => false,
116
+ resolveGlobal(name) {
117
+ const safeGlobals = {
118
+ JSON,
119
+ Math,
120
+ Date,
121
+ Object,
122
+ Array,
123
+ String,
124
+ Number,
125
+ Boolean,
126
+ console,
127
+ ...GLOBAL_FUNCTIONS
128
+ };
129
+ return safeGlobals[name];
130
+ }
131
+ };
132
+ function toCoreContext(ctx) {
133
+ return {
134
+ state: ctx.state,
135
+ locals: ctx.locals,
136
+ route: ctx.route,
137
+ imports: ctx.imports,
138
+ styles: ctx.styles,
139
+ env: ssrAdapter
140
+ };
141
+ }
112
142
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
113
143
  "area",
114
144
  "base",
@@ -125,557 +155,6 @@ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
125
155
  "track",
126
156
  "wbr"
127
157
  ]);
128
- var SAFE_ARRAY_METHODS = /* @__PURE__ */ new Set([
129
- "length",
130
- "at",
131
- "includes",
132
- "slice",
133
- "indexOf",
134
- "join",
135
- "filter",
136
- "map",
137
- "find",
138
- "findIndex",
139
- "some",
140
- "every"
141
- ]);
142
- var SAFE_STRING_METHODS = /* @__PURE__ */ new Set([
143
- "length",
144
- "charAt",
145
- "substring",
146
- "slice",
147
- "split",
148
- "trim",
149
- "toUpperCase",
150
- "toLowerCase",
151
- "replace",
152
- "includes",
153
- "startsWith",
154
- "endsWith",
155
- "indexOf"
156
- ]);
157
- var SAFE_MATH_METHODS = /* @__PURE__ */ new Set([
158
- "min",
159
- "max",
160
- "round",
161
- "floor",
162
- "ceil",
163
- "abs",
164
- "sqrt",
165
- "pow",
166
- "random",
167
- "sin",
168
- "cos",
169
- "tan"
170
- ]);
171
- var SAFE_DATE_STATIC_METHODS = /* @__PURE__ */ new Set(["now", "parse"]);
172
- var SAFE_DATE_INSTANCE_METHODS = /* @__PURE__ */ new Set([
173
- "toISOString",
174
- "toDateString",
175
- "toTimeString",
176
- "getTime",
177
- "getFullYear",
178
- "getMonth",
179
- "getDate",
180
- "getHours",
181
- "getMinutes",
182
- "getSeconds",
183
- "getMilliseconds"
184
- ]);
185
- function isEventHandler(value) {
186
- return typeof value === "object" && value !== null && "event" in value && "action" in value;
187
- }
188
- function createLambdaFunction(lambda, ctx) {
189
- return (item, index) => {
190
- const lambdaLocals = {
191
- ...ctx.locals,
192
- [lambda.param]: item
193
- };
194
- if (lambda.index !== void 0) {
195
- lambdaLocals[lambda.index] = index;
196
- }
197
- return evaluate(lambda.body, { ...ctx, locals: lambdaLocals });
198
- };
199
- }
200
- function callArrayMethod(target, method, args, ctx, rawArgs) {
201
- if (!SAFE_ARRAY_METHODS.has(method)) return void 0;
202
- switch (method) {
203
- case "length":
204
- return target.length;
205
- case "at": {
206
- const index = typeof args[0] === "number" ? args[0] : 0;
207
- return target.at(index);
208
- }
209
- case "includes": {
210
- const searchElement = args[0];
211
- const fromIndex = typeof args[1] === "number" ? args[1] : void 0;
212
- return target.includes(searchElement, fromIndex);
213
- }
214
- case "slice": {
215
- const start = typeof args[0] === "number" ? args[0] : void 0;
216
- const end = typeof args[1] === "number" ? args[1] : void 0;
217
- return target.slice(start, end);
218
- }
219
- case "indexOf": {
220
- const searchElement = args[0];
221
- const fromIndex = typeof args[1] === "number" ? args[1] : void 0;
222
- return target.indexOf(searchElement, fromIndex);
223
- }
224
- case "join": {
225
- const separator = typeof args[0] === "string" ? args[0] : ",";
226
- return target.join(separator);
227
- }
228
- case "filter": {
229
- const lambdaExpr = rawArgs?.[0];
230
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
231
- const fn = createLambdaFunction(lambdaExpr, ctx);
232
- return target.filter((item, index) => !!fn(item, index));
233
- }
234
- case "map": {
235
- const lambdaExpr = rawArgs?.[0];
236
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
237
- const fn = createLambdaFunction(lambdaExpr, ctx);
238
- return target.map((item, index) => fn(item, index));
239
- }
240
- case "find": {
241
- const lambdaExpr = rawArgs?.[0];
242
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
243
- const fn = createLambdaFunction(lambdaExpr, ctx);
244
- return target.find((item, index) => !!fn(item, index));
245
- }
246
- case "findIndex": {
247
- const lambdaExpr = rawArgs?.[0];
248
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
249
- const fn = createLambdaFunction(lambdaExpr, ctx);
250
- return target.findIndex((item, index) => !!fn(item, index));
251
- }
252
- case "some": {
253
- const lambdaExpr = rawArgs?.[0];
254
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
255
- const fn = createLambdaFunction(lambdaExpr, ctx);
256
- return target.some((item, index) => !!fn(item, index));
257
- }
258
- case "every": {
259
- const lambdaExpr = rawArgs?.[0];
260
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
261
- const fn = createLambdaFunction(lambdaExpr, ctx);
262
- return target.every((item, index) => !!fn(item, index));
263
- }
264
- default:
265
- return void 0;
266
- }
267
- }
268
- function callStringMethod(target, method, args) {
269
- if (!SAFE_STRING_METHODS.has(method)) return void 0;
270
- switch (method) {
271
- case "length":
272
- return target.length;
273
- case "charAt": {
274
- const index = typeof args[0] === "number" ? args[0] : 0;
275
- return target.charAt(index);
276
- }
277
- case "substring": {
278
- const start = typeof args[0] === "number" ? args[0] : 0;
279
- const end = typeof args[1] === "number" ? args[1] : void 0;
280
- return target.substring(start, end);
281
- }
282
- case "slice": {
283
- const start = typeof args[0] === "number" ? args[0] : void 0;
284
- const end = typeof args[1] === "number" ? args[1] : void 0;
285
- return target.slice(start, end);
286
- }
287
- case "split": {
288
- const separator = typeof args[0] === "string" ? args[0] : "";
289
- return target.split(separator);
290
- }
291
- case "trim":
292
- return target.trim();
293
- case "toUpperCase":
294
- return target.toUpperCase();
295
- case "toLowerCase":
296
- return target.toLowerCase();
297
- case "replace": {
298
- const search = typeof args[0] === "string" ? args[0] : "";
299
- const replace = typeof args[1] === "string" ? args[1] : "";
300
- return target.replace(search, replace);
301
- }
302
- case "includes": {
303
- const search = typeof args[0] === "string" ? args[0] : "";
304
- const position = typeof args[1] === "number" ? args[1] : void 0;
305
- return target.includes(search, position);
306
- }
307
- case "startsWith": {
308
- const search = typeof args[0] === "string" ? args[0] : "";
309
- const position = typeof args[1] === "number" ? args[1] : void 0;
310
- return target.startsWith(search, position);
311
- }
312
- case "endsWith": {
313
- const search = typeof args[0] === "string" ? args[0] : "";
314
- const length = typeof args[1] === "number" ? args[1] : void 0;
315
- return target.endsWith(search, length);
316
- }
317
- case "indexOf": {
318
- const search = typeof args[0] === "string" ? args[0] : "";
319
- const position = typeof args[1] === "number" ? args[1] : void 0;
320
- return target.indexOf(search, position);
321
- }
322
- default:
323
- return void 0;
324
- }
325
- }
326
- function callMathMethod(method, args) {
327
- if (!SAFE_MATH_METHODS.has(method)) return void 0;
328
- const numbers = args.filter((a) => typeof a === "number");
329
- switch (method) {
330
- case "min":
331
- return numbers.length > 0 ? Math.min(...numbers) : void 0;
332
- case "max":
333
- return numbers.length > 0 ? Math.max(...numbers) : void 0;
334
- case "round":
335
- return numbers[0] !== void 0 ? Math.round(numbers[0]) : void 0;
336
- case "floor":
337
- return numbers[0] !== void 0 ? Math.floor(numbers[0]) : void 0;
338
- case "ceil":
339
- return numbers[0] !== void 0 ? Math.ceil(numbers[0]) : void 0;
340
- case "abs":
341
- return numbers[0] !== void 0 ? Math.abs(numbers[0]) : void 0;
342
- case "sqrt":
343
- return numbers[0] !== void 0 ? Math.sqrt(numbers[0]) : void 0;
344
- case "pow":
345
- return numbers[0] !== void 0 && numbers[1] !== void 0 ? Math.pow(numbers[0], numbers[1]) : void 0;
346
- case "random":
347
- return Math.random();
348
- case "sin":
349
- return numbers[0] !== void 0 ? Math.sin(numbers[0]) : void 0;
350
- case "cos":
351
- return numbers[0] !== void 0 ? Math.cos(numbers[0]) : void 0;
352
- case "tan":
353
- return numbers[0] !== void 0 ? Math.tan(numbers[0]) : void 0;
354
- default:
355
- return void 0;
356
- }
357
- }
358
- function callDateStaticMethod(method, args) {
359
- if (!SAFE_DATE_STATIC_METHODS.has(method)) return void 0;
360
- switch (method) {
361
- case "now":
362
- return Date.now();
363
- case "parse": {
364
- const dateString = args[0];
365
- return typeof dateString === "string" ? Date.parse(dateString) : void 0;
366
- }
367
- default:
368
- return void 0;
369
- }
370
- }
371
- function callDateInstanceMethod(target, method) {
372
- if (!SAFE_DATE_INSTANCE_METHODS.has(method)) return void 0;
373
- switch (method) {
374
- case "toISOString":
375
- return target.toISOString();
376
- case "toDateString":
377
- return target.toDateString();
378
- case "toTimeString":
379
- return target.toTimeString();
380
- case "getTime":
381
- return target.getTime();
382
- case "getFullYear":
383
- return target.getFullYear();
384
- case "getMonth":
385
- return target.getMonth();
386
- case "getDate":
387
- return target.getDate();
388
- case "getHours":
389
- return target.getHours();
390
- case "getMinutes":
391
- return target.getMinutes();
392
- case "getSeconds":
393
- return target.getSeconds();
394
- case "getMilliseconds":
395
- return target.getMilliseconds();
396
- default:
397
- return void 0;
398
- }
399
- }
400
- function evaluate(expr, ctx) {
401
- switch (expr.expr) {
402
- case "lit":
403
- return expr.value;
404
- case "state":
405
- return ctx.state.get(expr.name);
406
- case "local":
407
- return ctx.locals[expr.name];
408
- case "var": {
409
- let varName = expr.name;
410
- let pathParts = [];
411
- if (varName.includes(".")) {
412
- const parts = varName.split(".");
413
- varName = parts[0];
414
- pathParts = parts.slice(1);
415
- }
416
- if (expr.path) {
417
- pathParts = pathParts.concat(expr.path.split("."));
418
- }
419
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
420
- for (const part of pathParts) {
421
- if (forbiddenKeys.has(part)) {
422
- return void 0;
423
- }
424
- }
425
- let value = ctx.locals[varName];
426
- for (const part of pathParts) {
427
- if (value == null) break;
428
- value = value[part];
429
- }
430
- return value;
431
- }
432
- case "bin":
433
- return evaluateBinary(expr.op, expr.left, expr.right, ctx);
434
- case "not":
435
- return !evaluate(expr.operand, ctx);
436
- case "cond":
437
- return evaluate(expr.if, ctx) ? evaluate(expr.then, ctx) : evaluate(expr.else, ctx);
438
- case "get": {
439
- const baseValue = evaluate(expr.base, ctx);
440
- if (baseValue == null) return void 0;
441
- const pathParts = expr.path.split(".");
442
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
443
- let value = baseValue;
444
- for (const part of pathParts) {
445
- if (forbiddenKeys.has(part)) return void 0;
446
- if (value == null) return void 0;
447
- value = value[part];
448
- }
449
- return value;
450
- }
451
- case "route": {
452
- const source = expr.source ?? "param";
453
- const routeCtx = ctx.route;
454
- if (!routeCtx) return "";
455
- switch (source) {
456
- case "param":
457
- return routeCtx.params[expr.name] ?? "";
458
- case "query":
459
- return routeCtx.query[expr.name] ?? "";
460
- case "path":
461
- return routeCtx.path;
462
- }
463
- }
464
- case "import": {
465
- const importData = ctx.imports?.[expr.name];
466
- if (importData === void 0) return void 0;
467
- if (expr.path) {
468
- return getNestedValue(importData, expr.path);
469
- }
470
- return importData;
471
- }
472
- case "data": {
473
- const dataValue = ctx.imports?.[expr.name];
474
- if (dataValue === void 0) return void 0;
475
- if (expr.path) {
476
- return getNestedValue(dataValue, expr.path);
477
- }
478
- return dataValue;
479
- }
480
- case "ref":
481
- return null;
482
- case "index": {
483
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
484
- const base = evaluate(expr.base, ctx);
485
- const key = evaluate(expr.key, ctx);
486
- if (base == null || key == null) return void 0;
487
- if (typeof key === "string" && forbiddenKeys.has(key)) return void 0;
488
- return base[key];
489
- }
490
- case "param": {
491
- return void 0;
492
- }
493
- case "style": {
494
- return evaluateStyle(expr, ctx);
495
- }
496
- case "concat": {
497
- return expr.items.map((item) => {
498
- const val = evaluate(item, ctx);
499
- return val == null ? "" : String(val);
500
- }).join("");
501
- }
502
- case "validity": {
503
- return false;
504
- }
505
- case "call": {
506
- const callExpr = expr;
507
- if (callExpr.target === null) {
508
- const globalArgs = callExpr.args?.map((arg) => {
509
- if (arg.expr === "lambda") return arg;
510
- return evaluate(arg, ctx);
511
- }) ?? [];
512
- return callGlobalFunction(callExpr.method, globalArgs);
513
- }
514
- const target = evaluate(callExpr.target, ctx);
515
- if (target == null) return void 0;
516
- const args = callExpr.args?.map((arg) => {
517
- if (arg.expr === "lambda") return arg;
518
- return evaluate(arg, ctx);
519
- }) ?? [];
520
- if (Array.isArray(target)) {
521
- return callArrayMethod(target, callExpr.method, args, ctx, callExpr.args);
522
- }
523
- if (typeof target === "string") {
524
- return callStringMethod(target, callExpr.method, args);
525
- }
526
- if (target === Math) {
527
- return callMathMethod(callExpr.method, args);
528
- }
529
- if (target === Date) {
530
- return callDateStaticMethod(callExpr.method, args);
531
- }
532
- if (target instanceof Date) {
533
- return callDateInstanceMethod(target, callExpr.method);
534
- }
535
- return void 0;
536
- }
537
- case "lambda": {
538
- return void 0;
539
- }
540
- case "array": {
541
- const arrayExpr = expr;
542
- return arrayExpr.elements.map((elem) => evaluate(elem, ctx));
543
- }
544
- case "obj": {
545
- const objExpr = expr;
546
- const result = {};
547
- for (const [key, value] of Object.entries(objExpr.props)) {
548
- result[key] = evaluate(value, ctx);
549
- }
550
- return result;
551
- }
552
- default: {
553
- const _exhaustiveCheck = expr;
554
- throw new Error(`Unknown expression type: ${JSON.stringify(_exhaustiveCheck)}`);
555
- }
556
- }
557
- }
558
- function getNestedValue(obj, path) {
559
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
560
- const parts = path.split(".");
561
- let value = obj;
562
- for (const part of parts) {
563
- if (forbiddenKeys.has(part)) {
564
- return void 0;
565
- }
566
- if (value == null) {
567
- return void 0;
568
- }
569
- if (Array.isArray(value)) {
570
- const index = Number(part);
571
- if (Number.isInteger(index) && index >= 0) {
572
- value = value[index];
573
- } else {
574
- value = value[part];
575
- }
576
- } else if (typeof value === "object") {
577
- value = value[part];
578
- } else {
579
- return void 0;
580
- }
581
- }
582
- return value;
583
- }
584
- function evaluateBinary(op, left, right, ctx) {
585
- if (op === "&&") {
586
- const leftVal2 = evaluate(left, ctx);
587
- if (!leftVal2) return leftVal2;
588
- return evaluate(right, ctx);
589
- }
590
- if (op === "||") {
591
- const leftVal2 = evaluate(left, ctx);
592
- if (leftVal2) return leftVal2;
593
- return evaluate(right, ctx);
594
- }
595
- const leftVal = evaluate(left, ctx);
596
- const rightVal = evaluate(right, ctx);
597
- switch (op) {
598
- case "+":
599
- if (typeof leftVal === "number" && typeof rightVal === "number") {
600
- return leftVal + rightVal;
601
- }
602
- return String(leftVal) + String(rightVal);
603
- case "-":
604
- return (typeof leftVal === "number" ? leftVal : 0) - (typeof rightVal === "number" ? rightVal : 0);
605
- case "*":
606
- return (typeof leftVal === "number" ? leftVal : 0) * (typeof rightVal === "number" ? rightVal : 0);
607
- case "/": {
608
- const dividend = typeof leftVal === "number" ? leftVal : 0;
609
- const divisor = typeof rightVal === "number" ? rightVal : 0;
610
- if (divisor === 0) {
611
- return dividend === 0 ? NaN : dividend > 0 ? Infinity : -Infinity;
612
- }
613
- return dividend / divisor;
614
- }
615
- case "%": {
616
- const dividend = typeof leftVal === "number" ? leftVal : 0;
617
- const divisor = typeof rightVal === "number" ? rightVal : 0;
618
- if (divisor === 0) return NaN;
619
- return dividend % divisor;
620
- }
621
- case "==":
622
- return leftVal === rightVal;
623
- case "!=":
624
- return leftVal !== rightVal;
625
- case "<":
626
- if (typeof leftVal === "number" && typeof rightVal === "number") {
627
- return leftVal < rightVal;
628
- }
629
- return String(leftVal) < String(rightVal);
630
- case "<=":
631
- if (typeof leftVal === "number" && typeof rightVal === "number") {
632
- return leftVal <= rightVal;
633
- }
634
- return String(leftVal) <= String(rightVal);
635
- case ">":
636
- if (typeof leftVal === "number" && typeof rightVal === "number") {
637
- return leftVal > rightVal;
638
- }
639
- return String(leftVal) > String(rightVal);
640
- case ">=":
641
- if (typeof leftVal === "number" && typeof rightVal === "number") {
642
- return leftVal >= rightVal;
643
- }
644
- return String(leftVal) >= String(rightVal);
645
- default:
646
- throw new Error("Unknown binary operator: " + op);
647
- }
648
- }
649
- function evaluateStyle(expr, ctx) {
650
- const preset = ctx.styles?.[expr.name];
651
- if (!preset) return "";
652
- let classes = preset.base;
653
- if (preset.variants) {
654
- for (const variantKey of Object.keys(preset.variants)) {
655
- let variantValueStr = null;
656
- if (expr.variants?.[variantKey]) {
657
- let variantValue;
658
- try {
659
- variantValue = evaluate(expr.variants[variantKey], ctx);
660
- } catch {
661
- continue;
662
- }
663
- if (variantValue != null) {
664
- variantValueStr = String(variantValue);
665
- }
666
- } else if (preset.defaultVariants?.[variantKey] !== void 0) {
667
- variantValueStr = preset.defaultVariants[variantKey];
668
- }
669
- if (variantValueStr !== null) {
670
- const variantClasses = preset.variants[variantKey]?.[variantValueStr];
671
- if (variantClasses) {
672
- classes += " " + variantClasses;
673
- }
674
- }
675
- }
676
- }
677
- return classes.trim();
678
- }
679
158
  function formatValue(value) {
680
159
  if (value === null || value === void 0) {
681
160
  return "";
@@ -685,6 +164,11 @@ function formatValue(value) {
685
164
  }
686
165
  return String(value);
687
166
  }
167
+
168
+ // src/renderer.ts
169
+ function isEventHandler(value) {
170
+ return typeof value === "object" && value !== null && "event" in value && "action" in value;
171
+ }
688
172
  async function renderNode(node, ctx) {
689
173
  switch (node.kind) {
690
174
  case "element":
@@ -726,7 +210,7 @@ async function renderElement(node, ctx) {
726
210
  if (isEventHandler(propValue)) {
727
211
  continue;
728
212
  }
729
- const value = evaluate(propValue, ctx);
213
+ const value = coreEvaluate(propValue, toCoreContext(ctx));
730
214
  if (value === false) {
731
215
  continue;
732
216
  }
@@ -752,11 +236,11 @@ async function renderElement(node, ctx) {
752
236
  return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
753
237
  }
754
238
  function renderText(node, ctx) {
755
- const value = evaluate(node.value, ctx);
239
+ const value = coreEvaluate(node.value, toCoreContext(ctx));
756
240
  return escapeHtml(formatValue(value));
757
241
  }
758
242
  async function renderIf(node, ctx) {
759
- const condition = evaluate(node.condition, ctx);
243
+ const condition = coreEvaluate(node.condition, toCoreContext(ctx));
760
244
  if (condition) {
761
245
  const content = await renderNode(node.then, ctx);
762
246
  return `<!--if:then-->${content}`;
@@ -768,7 +252,7 @@ async function renderIf(node, ctx) {
768
252
  return "<!--if:none-->";
769
253
  }
770
254
  async function renderEach(node, ctx) {
771
- const items = evaluate(node.items, ctx);
255
+ const items = coreEvaluate(node.items, toCoreContext(ctx));
772
256
  if (!Array.isArray(items)) {
773
257
  return "";
774
258
  }
@@ -791,13 +275,14 @@ async function renderEach(node, ctx) {
791
275
  return result;
792
276
  }
793
277
  async function renderMarkdown(node, ctx) {
794
- const content = evaluate(node.content, ctx);
278
+ const content = coreEvaluate(node.content, toCoreContext(ctx));
795
279
  const html = await parseMarkdownSSRAsync(formatValue(content));
796
280
  return `<div class="constela-markdown">${html}</div>`;
797
281
  }
798
282
  async function renderCode(node, ctx) {
799
- const language = formatValue(evaluate(node.language, ctx));
800
- const content = formatValue(evaluate(node.content, ctx));
283
+ const coreCtx = toCoreContext(ctx);
284
+ const language = formatValue(coreEvaluate(node.language, coreCtx));
285
+ const content = formatValue(coreEvaluate(node.content, coreCtx));
801
286
  const highlightedCode = await renderCodeSSR(content, language);
802
287
  const languageBadge = language ? `<div class="absolute right-12 top-3 z-10 rounded bg-muted-foreground/20 px-2 py-0.5 text-xs font-medium text-muted-foreground">${escapeHtml(language)}</div>` : "";
803
288
  const copyButton = `<button class="constela-copy-btn absolute right-3 top-3 z-10 flex h-8 w-8 items-center justify-center rounded-md border border-border bg-background/80 opacity-0 transition-opacity hover:bg-muted group-hover:opacity-100" data-copy-target="code" aria-label="Copy code"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></button>`;
@@ -816,7 +301,7 @@ async function renderLocalState(node, ctx) {
816
301
  const initial = field.initial;
817
302
  if (initial && typeof initial === "object" && "expr" in initial) {
818
303
  const evalCtx = { ...ctx, locals: progressiveLocals };
819
- localStateValues[name] = evaluate(initial, evalCtx);
304
+ localStateValues[name] = coreEvaluate(initial, toCoreContext(evalCtx));
820
305
  } else {
821
306
  localStateValues[name] = initial;
822
307
  }
@@ -887,581 +372,11 @@ async function renderToString(program, options) {
887
372
  }
888
373
 
889
374
  // src/streaming.ts
890
- import { isCookieInitialExpr as isCookieInitialExpr2 } from "@constela/core";
891
- var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
892
- "area",
893
- "base",
894
- "br",
895
- "col",
896
- "embed",
897
- "hr",
898
- "img",
899
- "input",
900
- "link",
901
- "meta",
902
- "param",
903
- "source",
904
- "track",
905
- "wbr"
906
- ]);
907
- var SAFE_ARRAY_METHODS2 = /* @__PURE__ */ new Set([
908
- "length",
909
- "at",
910
- "includes",
911
- "slice",
912
- "indexOf",
913
- "join",
914
- "filter",
915
- "map",
916
- "find",
917
- "findIndex",
918
- "some",
919
- "every"
920
- ]);
921
- var SAFE_STRING_METHODS2 = /* @__PURE__ */ new Set([
922
- "length",
923
- "charAt",
924
- "substring",
925
- "slice",
926
- "split",
927
- "trim",
928
- "toUpperCase",
929
- "toLowerCase",
930
- "replace",
931
- "includes",
932
- "startsWith",
933
- "endsWith",
934
- "indexOf"
935
- ]);
936
- var SAFE_MATH_METHODS2 = /* @__PURE__ */ new Set([
937
- "min",
938
- "max",
939
- "round",
940
- "floor",
941
- "ceil",
942
- "abs",
943
- "sqrt",
944
- "pow",
945
- "random",
946
- "sin",
947
- "cos",
948
- "tan"
949
- ]);
950
- var SAFE_DATE_STATIC_METHODS2 = /* @__PURE__ */ new Set(["now", "parse"]);
951
- var SAFE_DATE_INSTANCE_METHODS2 = /* @__PURE__ */ new Set([
952
- "toISOString",
953
- "toDateString",
954
- "toTimeString",
955
- "getTime",
956
- "getFullYear",
957
- "getMonth",
958
- "getDate",
959
- "getHours",
960
- "getMinutes",
961
- "getSeconds",
962
- "getMilliseconds"
963
- ]);
375
+ import { isCookieInitialExpr as isCookieInitialExpr2, evaluate as coreEvaluate2 } from "@constela/core";
964
376
  var CHUNK_SIZE_THRESHOLD = 1024;
965
377
  function isEventHandler2(value) {
966
378
  return typeof value === "object" && value !== null && "event" in value && "action" in value;
967
379
  }
968
- function createLambdaFunction2(lambda, ctx) {
969
- return (item, index) => {
970
- const lambdaLocals = {
971
- ...ctx.locals,
972
- [lambda.param]: item
973
- };
974
- if (lambda.index !== void 0) {
975
- lambdaLocals[lambda.index] = index;
976
- }
977
- return evaluate2(lambda.body, { ...ctx, locals: lambdaLocals });
978
- };
979
- }
980
- function callArrayMethod2(target, method, args, ctx, rawArgs) {
981
- if (!SAFE_ARRAY_METHODS2.has(method)) return void 0;
982
- switch (method) {
983
- case "length":
984
- return target.length;
985
- case "at": {
986
- const index = typeof args[0] === "number" ? args[0] : 0;
987
- return target.at(index);
988
- }
989
- case "includes": {
990
- const searchElement = args[0];
991
- const fromIndex = typeof args[1] === "number" ? args[1] : void 0;
992
- return target.includes(searchElement, fromIndex);
993
- }
994
- case "slice": {
995
- const start = typeof args[0] === "number" ? args[0] : void 0;
996
- const end = typeof args[1] === "number" ? args[1] : void 0;
997
- return target.slice(start, end);
998
- }
999
- case "indexOf": {
1000
- const searchElement = args[0];
1001
- const fromIndex = typeof args[1] === "number" ? args[1] : void 0;
1002
- return target.indexOf(searchElement, fromIndex);
1003
- }
1004
- case "join": {
1005
- const separator = typeof args[0] === "string" ? args[0] : ",";
1006
- return target.join(separator);
1007
- }
1008
- case "filter": {
1009
- const lambdaExpr = rawArgs?.[0];
1010
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
1011
- const fn = createLambdaFunction2(lambdaExpr, ctx);
1012
- return target.filter((item, index) => !!fn(item, index));
1013
- }
1014
- case "map": {
1015
- const lambdaExpr = rawArgs?.[0];
1016
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
1017
- const fn = createLambdaFunction2(lambdaExpr, ctx);
1018
- return target.map((item, index) => fn(item, index));
1019
- }
1020
- case "find": {
1021
- const lambdaExpr = rawArgs?.[0];
1022
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
1023
- const fn = createLambdaFunction2(lambdaExpr, ctx);
1024
- return target.find((item, index) => !!fn(item, index));
1025
- }
1026
- case "findIndex": {
1027
- const lambdaExpr = rawArgs?.[0];
1028
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
1029
- const fn = createLambdaFunction2(lambdaExpr, ctx);
1030
- return target.findIndex((item, index) => !!fn(item, index));
1031
- }
1032
- case "some": {
1033
- const lambdaExpr = rawArgs?.[0];
1034
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
1035
- const fn = createLambdaFunction2(lambdaExpr, ctx);
1036
- return target.some((item, index) => !!fn(item, index));
1037
- }
1038
- case "every": {
1039
- const lambdaExpr = rawArgs?.[0];
1040
- if (!lambdaExpr || lambdaExpr.expr !== "lambda") return void 0;
1041
- const fn = createLambdaFunction2(lambdaExpr, ctx);
1042
- return target.every((item, index) => !!fn(item, index));
1043
- }
1044
- default:
1045
- return void 0;
1046
- }
1047
- }
1048
- function callStringMethod2(target, method, args) {
1049
- if (!SAFE_STRING_METHODS2.has(method)) return void 0;
1050
- switch (method) {
1051
- case "length":
1052
- return target.length;
1053
- case "charAt": {
1054
- const index = typeof args[0] === "number" ? args[0] : 0;
1055
- return target.charAt(index);
1056
- }
1057
- case "substring": {
1058
- const start = typeof args[0] === "number" ? args[0] : 0;
1059
- const end = typeof args[1] === "number" ? args[1] : void 0;
1060
- return target.substring(start, end);
1061
- }
1062
- case "slice": {
1063
- const start = typeof args[0] === "number" ? args[0] : void 0;
1064
- const end = typeof args[1] === "number" ? args[1] : void 0;
1065
- return target.slice(start, end);
1066
- }
1067
- case "split": {
1068
- const separator = typeof args[0] === "string" ? args[0] : "";
1069
- return target.split(separator);
1070
- }
1071
- case "trim":
1072
- return target.trim();
1073
- case "toUpperCase":
1074
- return target.toUpperCase();
1075
- case "toLowerCase":
1076
- return target.toLowerCase();
1077
- case "replace": {
1078
- const search = typeof args[0] === "string" ? args[0] : "";
1079
- const replace = typeof args[1] === "string" ? args[1] : "";
1080
- return target.replace(search, replace);
1081
- }
1082
- case "includes": {
1083
- const search = typeof args[0] === "string" ? args[0] : "";
1084
- const position = typeof args[1] === "number" ? args[1] : void 0;
1085
- return target.includes(search, position);
1086
- }
1087
- case "startsWith": {
1088
- const search = typeof args[0] === "string" ? args[0] : "";
1089
- const position = typeof args[1] === "number" ? args[1] : void 0;
1090
- return target.startsWith(search, position);
1091
- }
1092
- case "endsWith": {
1093
- const search = typeof args[0] === "string" ? args[0] : "";
1094
- const length = typeof args[1] === "number" ? args[1] : void 0;
1095
- return target.endsWith(search, length);
1096
- }
1097
- case "indexOf": {
1098
- const search = typeof args[0] === "string" ? args[0] : "";
1099
- const position = typeof args[1] === "number" ? args[1] : void 0;
1100
- return target.indexOf(search, position);
1101
- }
1102
- default:
1103
- return void 0;
1104
- }
1105
- }
1106
- function callMathMethod2(method, args) {
1107
- if (!SAFE_MATH_METHODS2.has(method)) return void 0;
1108
- const numbers = args.filter((a) => typeof a === "number");
1109
- switch (method) {
1110
- case "min":
1111
- return numbers.length > 0 ? Math.min(...numbers) : void 0;
1112
- case "max":
1113
- return numbers.length > 0 ? Math.max(...numbers) : void 0;
1114
- case "round":
1115
- return numbers[0] !== void 0 ? Math.round(numbers[0]) : void 0;
1116
- case "floor":
1117
- return numbers[0] !== void 0 ? Math.floor(numbers[0]) : void 0;
1118
- case "ceil":
1119
- return numbers[0] !== void 0 ? Math.ceil(numbers[0]) : void 0;
1120
- case "abs":
1121
- return numbers[0] !== void 0 ? Math.abs(numbers[0]) : void 0;
1122
- case "sqrt":
1123
- return numbers[0] !== void 0 ? Math.sqrt(numbers[0]) : void 0;
1124
- case "pow":
1125
- return numbers[0] !== void 0 && numbers[1] !== void 0 ? Math.pow(numbers[0], numbers[1]) : void 0;
1126
- case "random":
1127
- return Math.random();
1128
- case "sin":
1129
- return numbers[0] !== void 0 ? Math.sin(numbers[0]) : void 0;
1130
- case "cos":
1131
- return numbers[0] !== void 0 ? Math.cos(numbers[0]) : void 0;
1132
- case "tan":
1133
- return numbers[0] !== void 0 ? Math.tan(numbers[0]) : void 0;
1134
- default:
1135
- return void 0;
1136
- }
1137
- }
1138
- function callDateStaticMethod2(method, args) {
1139
- if (!SAFE_DATE_STATIC_METHODS2.has(method)) return void 0;
1140
- switch (method) {
1141
- case "now":
1142
- return Date.now();
1143
- case "parse": {
1144
- const dateString = args[0];
1145
- return typeof dateString === "string" ? Date.parse(dateString) : void 0;
1146
- }
1147
- default:
1148
- return void 0;
1149
- }
1150
- }
1151
- function callDateInstanceMethod2(target, method) {
1152
- if (!SAFE_DATE_INSTANCE_METHODS2.has(method)) return void 0;
1153
- switch (method) {
1154
- case "toISOString":
1155
- return target.toISOString();
1156
- case "toDateString":
1157
- return target.toDateString();
1158
- case "toTimeString":
1159
- return target.toTimeString();
1160
- case "getTime":
1161
- return target.getTime();
1162
- case "getFullYear":
1163
- return target.getFullYear();
1164
- case "getMonth":
1165
- return target.getMonth();
1166
- case "getDate":
1167
- return target.getDate();
1168
- case "getHours":
1169
- return target.getHours();
1170
- case "getMinutes":
1171
- return target.getMinutes();
1172
- case "getSeconds":
1173
- return target.getSeconds();
1174
- case "getMilliseconds":
1175
- return target.getMilliseconds();
1176
- default:
1177
- return void 0;
1178
- }
1179
- }
1180
- function evaluate2(expr, ctx) {
1181
- switch (expr.expr) {
1182
- case "lit":
1183
- return expr.value;
1184
- case "state":
1185
- return ctx.state.get(expr.name);
1186
- case "local":
1187
- return ctx.locals[expr.name];
1188
- case "var": {
1189
- let varName = expr.name;
1190
- let pathParts = [];
1191
- if (varName.includes(".")) {
1192
- const parts = varName.split(".");
1193
- varName = parts[0];
1194
- pathParts = parts.slice(1);
1195
- }
1196
- if (expr.path) {
1197
- pathParts = pathParts.concat(expr.path.split("."));
1198
- }
1199
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1200
- for (const part of pathParts) {
1201
- if (forbiddenKeys.has(part)) {
1202
- return void 0;
1203
- }
1204
- }
1205
- let value = ctx.locals[varName];
1206
- for (const part of pathParts) {
1207
- if (value == null) break;
1208
- value = value[part];
1209
- }
1210
- return value;
1211
- }
1212
- case "bin":
1213
- return evaluateBinary2(expr.op, expr.left, expr.right, ctx);
1214
- case "not":
1215
- return !evaluate2(expr.operand, ctx);
1216
- case "cond":
1217
- return evaluate2(expr.if, ctx) ? evaluate2(expr.then, ctx) : evaluate2(expr.else, ctx);
1218
- case "get": {
1219
- const baseValue = evaluate2(expr.base, ctx);
1220
- if (baseValue == null) return void 0;
1221
- const pathParts = expr.path.split(".");
1222
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1223
- let value = baseValue;
1224
- for (const part of pathParts) {
1225
- if (forbiddenKeys.has(part)) return void 0;
1226
- if (value == null) return void 0;
1227
- value = value[part];
1228
- }
1229
- return value;
1230
- }
1231
- case "route": {
1232
- const source = expr.source ?? "param";
1233
- const routeCtx = ctx.route;
1234
- if (!routeCtx) return "";
1235
- switch (source) {
1236
- case "param":
1237
- return routeCtx.params[expr.name] ?? "";
1238
- case "query":
1239
- return routeCtx.query[expr.name] ?? "";
1240
- case "path":
1241
- return routeCtx.path;
1242
- default:
1243
- return "";
1244
- }
1245
- }
1246
- case "import": {
1247
- const importData = ctx.imports?.[expr.name];
1248
- if (importData === void 0) return void 0;
1249
- if (expr.path) {
1250
- return getNestedValue2(importData, expr.path);
1251
- }
1252
- return importData;
1253
- }
1254
- case "data": {
1255
- const dataValue = ctx.imports?.[expr.name];
1256
- if (dataValue === void 0) return void 0;
1257
- if (expr.path) {
1258
- return getNestedValue2(dataValue, expr.path);
1259
- }
1260
- return dataValue;
1261
- }
1262
- case "ref":
1263
- return null;
1264
- case "index": {
1265
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1266
- const base = evaluate2(expr.base, ctx);
1267
- const key = evaluate2(expr.key, ctx);
1268
- if (base == null || key == null) return void 0;
1269
- if (typeof key === "string" && forbiddenKeys.has(key)) return void 0;
1270
- return base[key];
1271
- }
1272
- case "param": {
1273
- return void 0;
1274
- }
1275
- case "style": {
1276
- return evaluateStyle2(expr, ctx);
1277
- }
1278
- case "concat": {
1279
- return expr.items.map((item) => {
1280
- const val = evaluate2(item, ctx);
1281
- return val == null ? "" : String(val);
1282
- }).join("");
1283
- }
1284
- case "validity": {
1285
- return false;
1286
- }
1287
- case "call": {
1288
- const callExpr = expr;
1289
- if (callExpr.target === null) {
1290
- return void 0;
1291
- }
1292
- const target = evaluate2(callExpr.target, ctx);
1293
- if (target == null) return void 0;
1294
- const args = callExpr.args?.map((arg) => {
1295
- if (arg.expr === "lambda") return arg;
1296
- return evaluate2(arg, ctx);
1297
- }) ?? [];
1298
- if (Array.isArray(target)) {
1299
- return callArrayMethod2(target, callExpr.method, args, ctx, callExpr.args);
1300
- }
1301
- if (typeof target === "string") {
1302
- return callStringMethod2(target, callExpr.method, args);
1303
- }
1304
- if (target === Math) {
1305
- return callMathMethod2(callExpr.method, args);
1306
- }
1307
- if (target === Date) {
1308
- return callDateStaticMethod2(callExpr.method, args);
1309
- }
1310
- if (target instanceof Date) {
1311
- return callDateInstanceMethod2(target, callExpr.method);
1312
- }
1313
- return void 0;
1314
- }
1315
- case "lambda": {
1316
- return void 0;
1317
- }
1318
- case "array": {
1319
- const arrayExpr = expr;
1320
- return arrayExpr.elements.map((elem) => evaluate2(elem, ctx));
1321
- }
1322
- case "obj": {
1323
- const objExpr = expr;
1324
- const result = {};
1325
- for (const [key, value] of Object.entries(objExpr.props)) {
1326
- result[key] = evaluate2(value, ctx);
1327
- }
1328
- return result;
1329
- }
1330
- default: {
1331
- return void 0;
1332
- }
1333
- }
1334
- }
1335
- function getNestedValue2(obj, path) {
1336
- const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1337
- const parts = path.split(".");
1338
- let value = obj;
1339
- for (const part of parts) {
1340
- if (forbiddenKeys.has(part)) {
1341
- return void 0;
1342
- }
1343
- if (value == null) {
1344
- return void 0;
1345
- }
1346
- if (Array.isArray(value)) {
1347
- const index = Number(part);
1348
- if (Number.isInteger(index) && index >= 0) {
1349
- value = value[index];
1350
- } else {
1351
- value = value[part];
1352
- }
1353
- } else if (typeof value === "object") {
1354
- value = value[part];
1355
- } else {
1356
- return void 0;
1357
- }
1358
- }
1359
- return value;
1360
- }
1361
- function evaluateBinary2(op, left, right, ctx) {
1362
- if (op === "&&") {
1363
- const leftVal2 = evaluate2(left, ctx);
1364
- if (!leftVal2) return leftVal2;
1365
- return evaluate2(right, ctx);
1366
- }
1367
- if (op === "||") {
1368
- const leftVal2 = evaluate2(left, ctx);
1369
- if (leftVal2) return leftVal2;
1370
- return evaluate2(right, ctx);
1371
- }
1372
- const leftVal = evaluate2(left, ctx);
1373
- const rightVal = evaluate2(right, ctx);
1374
- switch (op) {
1375
- case "+":
1376
- if (typeof leftVal === "number" && typeof rightVal === "number") {
1377
- return leftVal + rightVal;
1378
- }
1379
- return String(leftVal) + String(rightVal);
1380
- case "-":
1381
- return (typeof leftVal === "number" ? leftVal : 0) - (typeof rightVal === "number" ? rightVal : 0);
1382
- case "*":
1383
- return (typeof leftVal === "number" ? leftVal : 0) * (typeof rightVal === "number" ? rightVal : 0);
1384
- case "/": {
1385
- const dividend = typeof leftVal === "number" ? leftVal : 0;
1386
- const divisor = typeof rightVal === "number" ? rightVal : 0;
1387
- if (divisor === 0) {
1388
- return dividend === 0 ? NaN : dividend > 0 ? Infinity : -Infinity;
1389
- }
1390
- return dividend / divisor;
1391
- }
1392
- case "%": {
1393
- const dividend = typeof leftVal === "number" ? leftVal : 0;
1394
- const divisor = typeof rightVal === "number" ? rightVal : 0;
1395
- if (divisor === 0) return NaN;
1396
- return dividend % divisor;
1397
- }
1398
- case "==":
1399
- return leftVal === rightVal;
1400
- case "!=":
1401
- return leftVal !== rightVal;
1402
- case "<":
1403
- if (typeof leftVal === "number" && typeof rightVal === "number") {
1404
- return leftVal < rightVal;
1405
- }
1406
- return String(leftVal) < String(rightVal);
1407
- case "<=":
1408
- if (typeof leftVal === "number" && typeof rightVal === "number") {
1409
- return leftVal <= rightVal;
1410
- }
1411
- return String(leftVal) <= String(rightVal);
1412
- case ">":
1413
- if (typeof leftVal === "number" && typeof rightVal === "number") {
1414
- return leftVal > rightVal;
1415
- }
1416
- return String(leftVal) > String(rightVal);
1417
- case ">=":
1418
- if (typeof leftVal === "number" && typeof rightVal === "number") {
1419
- return leftVal >= rightVal;
1420
- }
1421
- return String(leftVal) >= String(rightVal);
1422
- default:
1423
- throw new Error("Unknown binary operator: " + op);
1424
- }
1425
- }
1426
- function evaluateStyle2(expr, ctx) {
1427
- const preset = ctx.styles?.[expr.name];
1428
- if (!preset) return "";
1429
- let classes = preset.base;
1430
- if (preset.variants) {
1431
- for (const variantKey of Object.keys(preset.variants)) {
1432
- let variantValueStr = null;
1433
- if (expr.variants?.[variantKey]) {
1434
- let variantValue;
1435
- try {
1436
- variantValue = evaluate2(expr.variants[variantKey], ctx);
1437
- } catch {
1438
- continue;
1439
- }
1440
- if (variantValue != null) {
1441
- variantValueStr = String(variantValue);
1442
- }
1443
- } else if (preset.defaultVariants?.[variantKey] !== void 0) {
1444
- variantValueStr = preset.defaultVariants[variantKey];
1445
- }
1446
- if (variantValueStr !== null) {
1447
- const variantClasses = preset.variants[variantKey]?.[variantValueStr];
1448
- if (variantClasses) {
1449
- classes += " " + variantClasses;
1450
- }
1451
- }
1452
- }
1453
- }
1454
- return classes.trim();
1455
- }
1456
- function formatValue2(value) {
1457
- if (value === null || value === void 0) {
1458
- return "";
1459
- }
1460
- if (typeof value === "object") {
1461
- return JSON.stringify(value);
1462
- }
1463
- return String(value);
1464
- }
1465
380
  function flush(ctx, force = false) {
1466
381
  if (ctx.aborted) return;
1467
382
  const { buffer, options, controller } = ctx;
@@ -1547,14 +462,14 @@ async function renderSuspenseToStream(node, ctx) {
1547
462
  async function renderElementToStream(node, ctx) {
1548
463
  if (checkAbort(ctx)) return;
1549
464
  const tag = node.tag;
1550
- const isVoid = VOID_ELEMENTS2.has(tag);
465
+ const isVoid = VOID_ELEMENTS.has(tag);
1551
466
  let attrs = "";
1552
467
  if (node.props) {
1553
468
  for (const [propName, propValue] of Object.entries(node.props)) {
1554
469
  if (isEventHandler2(propValue)) {
1555
470
  continue;
1556
471
  }
1557
- const value = evaluate2(propValue, ctx);
472
+ const value = coreEvaluate2(propValue, toCoreContext(ctx));
1558
473
  if (value === false) {
1559
474
  continue;
1560
475
  }
@@ -1581,12 +496,12 @@ async function renderElementToStream(node, ctx) {
1581
496
  write(ctx, "</" + tag + ">");
1582
497
  }
1583
498
  function renderTextToStream(node, ctx) {
1584
- const value = evaluate2(node.value, ctx);
1585
- write(ctx, escapeHtml(formatValue2(value)));
499
+ const value = coreEvaluate2(node.value, toCoreContext(ctx));
500
+ write(ctx, escapeHtml(formatValue(value)));
1586
501
  }
1587
502
  async function renderIfToStream(node, ctx) {
1588
503
  if (checkAbort(ctx)) return;
1589
- const condition = evaluate2(node.condition, ctx);
504
+ const condition = coreEvaluate2(node.condition, toCoreContext(ctx));
1590
505
  if (condition) {
1591
506
  write(ctx, "<!--if:then-->");
1592
507
  await renderNodeToStream(node.then, ctx);
@@ -1599,7 +514,7 @@ async function renderIfToStream(node, ctx) {
1599
514
  }
1600
515
  async function renderEachToStream(node, ctx) {
1601
516
  if (checkAbort(ctx)) return;
1602
- const items = evaluate2(node.items, ctx);
517
+ const items = coreEvaluate2(node.items, toCoreContext(ctx));
1603
518
  if (!Array.isArray(items)) {
1604
519
  return;
1605
520
  }
@@ -1624,12 +539,13 @@ async function renderEachToStream(node, ctx) {
1624
539
  }
1625
540
  }
1626
541
  async function renderMarkdownToStream(node, ctx) {
1627
- const content = evaluate2(node.content, ctx);
1628
- write(ctx, '<div class="constela-markdown">' + escapeHtml(formatValue2(content)) + "</div>");
542
+ const content = coreEvaluate2(node.content, toCoreContext(ctx));
543
+ write(ctx, '<div class="constela-markdown">' + escapeHtml(formatValue(content)) + "</div>");
1629
544
  }
1630
545
  async function renderCodeToStream(node, ctx) {
1631
- const language = formatValue2(evaluate2(node.language, ctx));
1632
- const content = formatValue2(evaluate2(node.content, ctx));
546
+ const coreCtx = toCoreContext(ctx);
547
+ const language = formatValue(coreEvaluate2(node.language, coreCtx));
548
+ const content = formatValue(coreEvaluate2(node.content, coreCtx));
1633
549
  const languageBadge = language ? '<div class="absolute right-12 top-3 z-10 rounded bg-muted-foreground/20 px-2 py-0.5 text-xs font-medium text-muted-foreground">' + escapeHtml(language) + "</div>" : "";
1634
550
  const copyButton = '<button class="constela-copy-btn absolute right-3 top-3 z-10 flex h-8 w-8 items-center justify-center rounded-md border border-border bg-background/80 opacity-0 transition-opacity hover:bg-muted group-hover:opacity-100" data-copy-target="code" aria-label="Copy code"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></button>';
1635
551
  write(ctx, '<div class="constela-code" data-code-content="' + escapeHtml(content) + '"><div class="group relative">' + languageBadge + copyButton + "<pre><code>" + escapeHtml(content) + "</code></pre></div></div>");
@@ -1648,7 +564,7 @@ async function renderLocalStateToStream(node, ctx) {
1648
564
  const initial = field.initial;
1649
565
  if (initial && typeof initial === "object" && "expr" in initial) {
1650
566
  const evalCtx = { ...ctx, locals: progressiveLocals };
1651
- localStateValues[name] = evaluate2(initial, evalCtx);
567
+ localStateValues[name] = coreEvaluate2(initial, toCoreContext(evalCtx));
1652
568
  } else {
1653
569
  localStateValues[name] = initial;
1654
570
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/server",
3
- "version": "17.0.1",
3
+ "version": "18.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.16",
19
- "@constela/core": "^0.21.0"
18
+ "@constela/compiler": "^0.15.21",
19
+ "@constela/core": "^0.22.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.16",
33
- "@constela/core": "0.21.0"
32
+ "@constela/compiler": "0.15.21",
33
+ "@constela/core": "0.22.0"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=20.0.0"