@constela/start 1.8.25 → 1.8.27

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.
@@ -1,5 +1,104 @@
1
1
  // src/runtime/entry-server.ts
2
2
  import { renderToString } from "@constela/server";
3
+
4
+ // src/runtime/theme.ts
5
+ function escapeCssValue(value) {
6
+ return value.replace(/[;<>{}]/g, "");
7
+ }
8
+ function escapeJsString(str) {
9
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/<\/script>/gi, "<\\/script>");
10
+ }
11
+ function generateThemeCss(options) {
12
+ const { config, cssPrefix: optionsPrefix } = options;
13
+ if (!config) {
14
+ return "";
15
+ }
16
+ const { colors, darkColors, fonts, cssPrefix: configPrefix } = config;
17
+ const prefix = optionsPrefix ?? configPrefix ?? "";
18
+ const hasColors = colors && Object.keys(colors).length > 0;
19
+ const hasDarkColors = darkColors && Object.keys(darkColors).length > 0;
20
+ const hasFonts = fonts && Object.keys(fonts).length > 0;
21
+ if (!hasColors && !hasDarkColors && !hasFonts) {
22
+ return "";
23
+ }
24
+ const lines = [];
25
+ const rootVars = [];
26
+ const separator = prefix && !prefix.endsWith("-") ? "-" : "";
27
+ if (colors) {
28
+ for (const [key, value] of Object.entries(colors)) {
29
+ if (value !== void 0) {
30
+ rootVars.push(` --${prefix}${separator}${key}: ${escapeCssValue(value)};`);
31
+ }
32
+ }
33
+ }
34
+ if (fonts) {
35
+ for (const [key, value] of Object.entries(fonts)) {
36
+ if (value !== void 0) {
37
+ rootVars.push(` --${prefix}${separator}font-${key}: ${escapeCssValue(value)};`);
38
+ }
39
+ }
40
+ }
41
+ if (rootVars.length > 0) {
42
+ lines.push(":root {");
43
+ lines.push(...rootVars);
44
+ lines.push("}");
45
+ }
46
+ if (darkColors) {
47
+ const darkVars = [];
48
+ for (const [key, value] of Object.entries(darkColors)) {
49
+ if (value !== void 0) {
50
+ darkVars.push(` --${prefix}${separator}${key}: ${escapeCssValue(value)};`);
51
+ }
52
+ }
53
+ if (darkVars.length > 0) {
54
+ lines.push(".dark {");
55
+ lines.push(...darkVars);
56
+ lines.push("}");
57
+ }
58
+ }
59
+ return lines.join("\n");
60
+ }
61
+ function generateThemeScript(storageKey = "theme") {
62
+ if (storageKey === "") {
63
+ storageKey = "theme";
64
+ }
65
+ const escapedKey = escapeJsString(storageKey);
66
+ return `(function() {
67
+ try {
68
+ var theme;
69
+ var cookies = document.cookie.split(';');
70
+ for (var i = 0; i < cookies.length; i++) {
71
+ var cookie = cookies[i].trim();
72
+ if (cookie.indexOf('${escapedKey}=') === 0) {
73
+ theme = decodeURIComponent(cookie.substring('${escapedKey}='.length));
74
+ break;
75
+ }
76
+ }
77
+ if (!theme) {
78
+ theme = localStorage.getItem('${escapedKey}');
79
+ }
80
+ if (theme === 'dark') {
81
+ document.documentElement.classList.add('dark');
82
+ } else if (theme === 'light') {
83
+ document.documentElement.classList.remove('dark');
84
+ }
85
+ } catch (e) {}
86
+ })();`;
87
+ }
88
+ function getHtmlThemeClass(config, cookieMode) {
89
+ if (cookieMode === "dark") {
90
+ return "dark";
91
+ }
92
+ if (cookieMode === "light" || cookieMode === "system") {
93
+ return "";
94
+ }
95
+ if (config?.mode === "dark") {
96
+ return "dark";
97
+ }
98
+ return "";
99
+ }
100
+
101
+ // src/runtime/entry-server.ts
3
102
  async function renderPage(program, ctx) {
4
103
  const stateOverrides = {};
5
104
  if (ctx.cookies?.["theme"] && program.state?.["theme"]) {
@@ -26,7 +125,7 @@ async function renderPage(program, ctx) {
26
125
  }
27
126
  return await renderToString(program, options);
28
127
  }
29
- function escapeJsString(str) {
128
+ function escapeJsString2(str) {
30
129
  return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
31
130
  }
32
131
  function escapeJsonForScript(json) {
@@ -70,7 +169,7 @@ function generateHydrationScript(program, widgets, route, hmrUrl) {
70
169
  }).join("\n") : "";
71
170
  const widgetMounting = hasWidgets ? widgets.map((widget) => {
72
171
  const jsId = toJsIdentifier(widget.id);
73
- const escapedId = escapeJsString(widget.id);
172
+ const escapedId = escapeJsString2(widget.id);
74
173
  return `
75
174
  const container_${jsId} = document.getElementById('${escapedId}');
76
175
  if (container_${jsId}) {
@@ -93,14 +192,16 @@ if (container_${jsId}) {
93
192
  }`;
94
193
  let hmrSetup = "";
95
194
  if (enableHmr) {
96
- const escapedHmrUrl = escapeJsString(hmrUrl);
195
+ const escapedHmrUrl = escapeJsString2(hmrUrl);
97
196
  const handlerOptions = route ? `{
98
197
  container: document.getElementById('app'),
99
198
  program,
100
- route
199
+ route,
200
+ skipInitialRender: true
101
201
  }` : `{
102
202
  container: document.getElementById('app'),
103
- program
203
+ program,
204
+ skipInitialRender: true
104
205
  }`;
105
206
  hmrSetup = `
106
207
 
@@ -128,7 +229,15 @@ function wrapHtml(content, hydrationScript, head, options) {
128
229
  }
129
230
  langAttr = ` lang="${options.lang}"`;
130
231
  }
131
- const htmlClass = options?.defaultTheme === "dark" || options?.theme === "dark" ? ' class="dark"' : "";
232
+ let htmlClass = "";
233
+ if (options?.themeConfig) {
234
+ const themeClass = getHtmlThemeClass(options.themeConfig, options.themeCookie);
235
+ if (themeClass) {
236
+ htmlClass = ` class="${themeClass}"`;
237
+ }
238
+ } else if (options?.defaultTheme === "dark" || options?.theme === "dark") {
239
+ htmlClass = ' class="dark"';
240
+ }
132
241
  let processedScript = hydrationScript;
133
242
  let importMapScript = "";
134
243
  if (options?.runtimePath) {
@@ -153,8 +262,20 @@ ${importMapJson}
153
262
  </script>
154
263
  `;
155
264
  }
265
+ let themeCssStyle = "";
266
+ if (options?.themeConfig) {
267
+ const themeCss = generateThemeCss({ config: options.themeConfig });
268
+ if (themeCss) {
269
+ themeCssStyle = `<style>${themeCss}</style>
270
+ `;
271
+ }
272
+ }
156
273
  let themeScript = "";
157
- if (options?.themeStorageKey) {
274
+ if (options?.themeConfig) {
275
+ const storageKey = options.themeStorageKey ?? "theme";
276
+ themeScript = `<script>${generateThemeScript(storageKey)}</script>
277
+ `;
278
+ } else if (options?.themeStorageKey) {
158
279
  if (!/^[a-zA-Z0-9_-]+$/.test(options.themeStorageKey)) {
159
280
  throw new Error(`Invalid themeStorageKey: ${options.themeStorageKey}. Only alphanumeric characters, underscores, and hyphens are allowed.`);
160
281
  }
@@ -193,7 +314,7 @@ ${importMapJson}
193
314
  <head>
194
315
  <meta charset="utf-8">
195
316
  <meta name="viewport" content="width=device-width, initial-scale=1">
196
- ${themeScript}${importMapScript}${head ?? ""}
317
+ ${themeCssStyle}${themeScript}${importMapScript}${head ?? ""}
197
318
  </head>
198
319
  <body>
199
320
  <div id="app">${content}</div>
@@ -3,7 +3,7 @@ import {
3
3
  generateMetaTags,
4
4
  renderPage,
5
5
  wrapHtml
6
- } from "./chunk-IZPFJSX3.js";
6
+ } from "./chunk-C7LIB2RS.js";
7
7
 
8
8
  // src/router/file-router.ts
9
9
  import fg from "fast-glob";
@@ -770,6 +770,8 @@ async function mdxContentToNode(content, options) {
770
770
  import { existsSync as existsSync3, readFileSync } from "fs";
771
771
  import { basename, dirname, extname as extname2, join as join3 } from "path";
772
772
  import fg2 from "fast-glob";
773
+ import { isAiDataSource } from "@constela/core";
774
+ import { createDslGenerator } from "@constela/ai";
773
775
  var mdxContentToNode2 = mdxContentToNode;
774
776
  function resolveJsonRefs(json) {
775
777
  const cloned = JSON.parse(JSON.stringify(json));
@@ -1099,6 +1101,19 @@ async function loadApi(url, transform) {
1099
1101
  throw new Error(`Network error: ${error.message}`);
1100
1102
  }
1101
1103
  }
1104
+ async function loadAi(dataSource, generator) {
1105
+ const gen = generator ?? createDslGenerator({
1106
+ provider: dataSource.provider
1107
+ });
1108
+ const result = await gen.generate({
1109
+ prompt: dataSource.prompt,
1110
+ output: dataSource.output
1111
+ });
1112
+ if (!result.validated && result.errors && result.errors.length > 0) {
1113
+ throw new Error(`AI generated DSL validation failed: ${result.errors.join(", ")}`);
1114
+ }
1115
+ return result.dsl;
1116
+ }
1102
1117
  function evaluateParamExpression(expr, item) {
1103
1118
  switch (expr.expr) {
1104
1119
  case "lit":
@@ -1211,6 +1226,12 @@ var DataLoader = class {
1211
1226
  }
1212
1227
  data = await loadApi(dataSource.url, dataSource.transform);
1213
1228
  break;
1229
+ case "ai":
1230
+ if (!isAiDataSource(dataSource)) {
1231
+ throw new Error(`Invalid AI data source '${name}'`);
1232
+ }
1233
+ data = await loadAi(dataSource);
1234
+ break;
1214
1235
  default:
1215
1236
  throw new Error(`Unknown data source type: ${dataSource.type}`);
1216
1237
  }
@@ -1373,8 +1394,65 @@ function parseCookies(cookieHeader) {
1373
1394
  }
1374
1395
  return cookies;
1375
1396
  }
1397
+ function wrapHtmlStream(contentStream, hydrationScript, options) {
1398
+ const encoder = new TextEncoder();
1399
+ const lang = options?.lang ?? "en";
1400
+ const themeClass = options?.theme === "dark" ? 'class="dark"' : "";
1401
+ const docStart = `<!DOCTYPE html>
1402
+ <html lang="${lang}" ${themeClass}>
1403
+ <head>
1404
+ <meta charset="UTF-8">
1405
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1406
+ ${options?.head ?? ""}
1407
+ </head>
1408
+ <body>
1409
+ <div id="app">`;
1410
+ const docEnd = `</div>
1411
+ ${options?.runtimePath ? `<script type="module" src="${options.runtimePath}"></script>` : ""}
1412
+ <script type="module">
1413
+ ${hydrationScript}
1414
+ </script>
1415
+ </body>
1416
+ </html>`;
1417
+ let contentReader = null;
1418
+ let startSent = false;
1419
+ let contentDone = false;
1420
+ return new ReadableStream({
1421
+ async start() {
1422
+ contentReader = contentStream.getReader();
1423
+ },
1424
+ async pull(controller) {
1425
+ try {
1426
+ if (!startSent) {
1427
+ controller.enqueue(encoder.encode(docStart));
1428
+ startSent = true;
1429
+ return;
1430
+ }
1431
+ if (!contentDone && contentReader) {
1432
+ const { done, value } = await contentReader.read();
1433
+ if (done) {
1434
+ contentDone = true;
1435
+ controller.enqueue(encoder.encode(docEnd));
1436
+ controller.close();
1437
+ return;
1438
+ }
1439
+ controller.enqueue(value);
1440
+ return;
1441
+ }
1442
+ controller.close();
1443
+ } catch (error) {
1444
+ controller.error(error);
1445
+ }
1446
+ },
1447
+ cancel(reason) {
1448
+ if (contentReader) {
1449
+ contentReader.cancel(reason);
1450
+ }
1451
+ }
1452
+ });
1453
+ }
1376
1454
  function createAdapter(options) {
1377
- const { routes, loadModule = defaultLoadModule } = options;
1455
+ const { routes, loadModule = defaultLoadModule, streaming = false } = options;
1378
1456
  async function fetch2(request) {
1379
1457
  try {
1380
1458
  const url = new URL(request.url);
@@ -1435,6 +1513,29 @@ function createAdapter(options) {
1435
1513
  initialTheme = themeState.initial;
1436
1514
  }
1437
1515
  }
1516
+ if (streaming) {
1517
+ const encoder = new TextEncoder();
1518
+ const contentStream = new ReadableStream({
1519
+ start(controller) {
1520
+ controller.enqueue(encoder.encode(content));
1521
+ controller.close();
1522
+ }
1523
+ });
1524
+ const streamOptions = {
1525
+ lang: "en"
1526
+ };
1527
+ if (initialTheme) {
1528
+ streamOptions.theme = initialTheme;
1529
+ }
1530
+ const htmlStream = wrapHtmlStream(contentStream, hydrationScript, streamOptions);
1531
+ return new Response(htmlStream, {
1532
+ status: 200,
1533
+ headers: {
1534
+ "Content-Type": "text/html; charset=utf-8",
1535
+ "Transfer-Encoding": "chunked"
1536
+ }
1537
+ });
1538
+ }
1438
1539
  const html = wrapHtml(content, hydrationScript, void 0, initialTheme ? {
1439
1540
  theme: initialTheme,
1440
1541
  defaultTheme: initialTheme,
@@ -2948,14 +3049,14 @@ async function resolveConfig(fileConfig, cliOptions) {
2948
3049
 
2949
3050
  // src/build/index.ts
2950
3051
  import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
2951
- import { mkdir as mkdir2, writeFile, cp, readdir } from "fs/promises";
3052
+ import { mkdir as mkdir2, writeFile as writeFile2, cp, readdir } from "fs/promises";
2952
3053
  import { join as join11, dirname as dirname6, relative as relative5, basename as basename4, isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
2953
3054
  import { isCookieInitialExpr as isCookieInitialExpr3 } from "@constela/core";
2954
3055
 
2955
3056
  // src/build/bundler.ts
2956
3057
  import * as esbuild from "esbuild";
2957
3058
  import { existsSync as existsSync8 } from "fs";
2958
- import { mkdir, readFile } from "fs/promises";
3059
+ import { mkdir, readFile, writeFile } from "fs/promises";
2959
3060
  import { createRequire } from "module";
2960
3061
  import { join as join10, dirname as dirname5, isAbsolute as isAbsolute2, relative as relative4 } from "path";
2961
3062
  import { fileURLToPath } from "url";
@@ -3703,14 +3804,14 @@ async function build2(options) {
3703
3804
  const program = await convertToCompiledProgram(processedPageInfo);
3704
3805
  const html = await renderPageToHtml(program, params, routePath, runtimePath, cssPath, processedPageInfo.page.externalImports, processedPageInfo.widgets, seoLang);
3705
3806
  await mkdir2(dirname6(outputPath), { recursive: true });
3706
- await writeFile(outputPath, html, "utf-8");
3807
+ await writeFile2(outputPath, html, "utf-8");
3707
3808
  generatedFiles.push(outputPath);
3708
3809
  const slugValue = params["slug"];
3709
3810
  if (slugValue && (slugValue === "index" || slugValue.endsWith("/index"))) {
3710
3811
  const parentOutputPath = join11(dirname6(dirname6(outputPath)), "index.html");
3711
3812
  if (!generatedFiles.includes(parentOutputPath)) {
3712
3813
  await mkdir2(dirname6(parentOutputPath), { recursive: true });
3713
- await writeFile(parentOutputPath, html, "utf-8");
3814
+ await writeFile2(parentOutputPath, html, "utf-8");
3714
3815
  generatedFiles.push(parentOutputPath);
3715
3816
  }
3716
3817
  }
@@ -3737,7 +3838,7 @@ async function build2(options) {
3737
3838
  const program = await convertToCompiledProgram(pageInfo);
3738
3839
  const html = await renderPageToHtml(program, {}, routePath, runtimePath, cssPath, pageInfo.page.externalImports, pageInfo.widgets, seoLang);
3739
3840
  await mkdir2(dirname6(outputPath), { recursive: true });
3740
- await writeFile(outputPath, html, "utf-8");
3841
+ await writeFile2(outputPath, html, "utf-8");
3741
3842
  generatedFiles.push(outputPath);
3742
3843
  }
3743
3844
  }
package/dist/cli/index.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  hyperlink,
5
5
  loadConfig,
6
6
  resolveConfig
7
- } from "../chunk-E4SJFPTB.js";
8
- import "../chunk-IZPFJSX3.js";
7
+ } from "../chunk-VL6BMHBL.js";
8
+ import "../chunk-C7LIB2RS.js";
9
9
 
10
10
  // src/cli/index.ts
11
11
  import { Command } from "commander";
package/dist/index.d.ts CHANGED
@@ -80,6 +80,10 @@ interface ConstelaConfig {
80
80
  edge?: {
81
81
  adapter?: 'cloudflare' | 'vercel' | 'deno' | 'node';
82
82
  };
83
+ /** Enable streaming SSR */
84
+ streaming?: boolean;
85
+ /** Flush strategy for streaming SSR */
86
+ streamingFlushStrategy?: 'immediate' | 'batched' | 'manual';
83
87
  }
84
88
  /**
85
89
  * Development server options
@@ -97,6 +101,8 @@ interface DevServerOptions {
97
101
  seo?: {
98
102
  lang?: string;
99
103
  };
104
+ /** Enable streaming SSR */
105
+ streaming?: boolean;
100
106
  }
101
107
  /**
102
108
  * Build options
@@ -115,6 +121,8 @@ interface BuildOptions {
115
121
  seo?: {
116
122
  lang?: string;
117
123
  };
124
+ /** Enable streaming SSR */
125
+ streaming?: boolean;
118
126
  }
119
127
 
120
128
  /**
@@ -279,6 +287,10 @@ interface AdapterOptions {
279
287
  platform: PlatformAdapter;
280
288
  routes: ScannedRoute[];
281
289
  loadModule?: (file: string) => Promise<unknown>;
290
+ /** Enable streaming SSR */
291
+ streaming?: boolean;
292
+ /** Flush strategy for streaming SSR */
293
+ streamingFlushStrategy?: 'immediate' | 'batched' | 'manual';
282
294
  }
283
295
  interface EdgeAdapter {
284
296
  fetch: (request: Request) => Promise<Response>;
package/dist/index.js CHANGED
@@ -28,14 +28,14 @@ import {
28
28
  transformCsv,
29
29
  transformMdx,
30
30
  transformYaml
31
- } from "./chunk-E4SJFPTB.js";
31
+ } from "./chunk-VL6BMHBL.js";
32
32
  import {
33
33
  evaluateMetaExpression,
34
34
  generateHydrationScript,
35
35
  generateMetaTags,
36
36
  renderPage,
37
37
  wrapHtml
38
- } from "./chunk-IZPFJSX3.js";
38
+ } from "./chunk-C7LIB2RS.js";
39
39
 
40
40
  // src/build/ssg.ts
41
41
  import { mkdir, writeFile } from "fs/promises";
@@ -5,6 +5,58 @@ import {
5
5
  createHMRHandler,
6
6
  createErrorOverlay
7
7
  } from "@constela/runtime";
8
+
9
+ // src/runtime/theme-provider.ts
10
+ import { createThemeProvider } from "@constela/runtime";
11
+ var instance = null;
12
+ var ThemeProvider = {
13
+ /**
14
+ * Initializes the ThemeProvider with the given configuration.
15
+ * Creates a new instance if one doesn't exist.
16
+ */
17
+ init(config) {
18
+ if (instance) {
19
+ instance.destroy();
20
+ }
21
+ instance = createThemeProvider({ config });
22
+ },
23
+ /**
24
+ * Sets the color scheme mode.
25
+ */
26
+ setMode(mode) {
27
+ if (instance) {
28
+ instance.setMode(mode);
29
+ }
30
+ },
31
+ /**
32
+ * Applies CSS variables to the document.
33
+ * Called internally by init, but can be called manually if needed.
34
+ */
35
+ applyCssVariables() {
36
+ if (instance) {
37
+ const currentMode = instance.getMode();
38
+ instance.setMode(currentMode);
39
+ }
40
+ },
41
+ /**
42
+ * Destroys the ThemeProvider instance and cleans up resources.
43
+ */
44
+ destroy() {
45
+ if (instance) {
46
+ instance.destroy();
47
+ instance = null;
48
+ }
49
+ },
50
+ /**
51
+ * Gets the underlying ThemeProvider instance.
52
+ * Returns null if not initialized.
53
+ */
54
+ getInstance() {
55
+ return instance;
56
+ }
57
+ };
58
+
59
+ // src/runtime/entry-client.ts
8
60
  function initClient(options) {
9
61
  const { program, container, escapeHandlers = [], route } = options;
10
62
  const appInstance = hydrateApp({ program, container, ...route && { route } });
@@ -36,6 +88,16 @@ function initClient(options) {
36
88
  } catch {
37
89
  }
38
90
  }
91
+ const programTheme = program.theme;
92
+ const hasThemeProvider = !!programTheme;
93
+ if (hasThemeProvider) {
94
+ ThemeProvider.init(programTheme);
95
+ ThemeProvider.applyCssVariables();
96
+ const currentTheme = appInstance.getState?.("theme");
97
+ if (currentTheme === "dark") {
98
+ document.documentElement.classList.add("dark");
99
+ }
100
+ }
39
101
  if (program.state?.["theme"]) {
40
102
  const updateThemeClass = (value) => {
41
103
  if (value === "dark") {
@@ -44,11 +106,18 @@ function initClient(options) {
44
106
  document.documentElement.classList.remove("dark");
45
107
  }
46
108
  };
47
- const currentTheme = appInstance.getState?.("theme");
48
- if (currentTheme) {
49
- updateThemeClass(currentTheme);
109
+ if (!hasThemeProvider) {
110
+ const currentTheme = appInstance.getState?.("theme");
111
+ if (currentTheme) {
112
+ updateThemeClass(currentTheme);
113
+ }
50
114
  }
51
- const unsubscribeTheme = appInstance.subscribe("theme", updateThemeClass);
115
+ const unsubscribeTheme = appInstance.subscribe("theme", (value) => {
116
+ updateThemeClass(value);
117
+ if (hasThemeProvider && typeof value === "string") {
118
+ ThemeProvider.setMode(value);
119
+ }
120
+ });
52
121
  cleanupFns.push(unsubscribeTheme);
53
122
  }
54
123
  let destroyed = false;
@@ -59,6 +128,9 @@ function initClient(options) {
59
128
  for (const cleanup of cleanupFns) {
60
129
  cleanup();
61
130
  }
131
+ if (hasThemeProvider) {
132
+ ThemeProvider.destroy();
133
+ }
62
134
  appInstance.destroy();
63
135
  },
64
136
  setState(name, value) {
@@ -1,4 +1,5 @@
1
1
  import { CompiledRouteDefinition, CompiledExpression, CompiledProgram } from '@constela/compiler';
2
+ import { ThemeConfig, ColorScheme } from '@constela/core';
2
3
 
3
4
  /**
4
5
  * Server-side entry point for Constela applications
@@ -23,6 +24,10 @@ interface WrapHtmlOptions {
23
24
  themeStorageKey?: string;
24
25
  /** Default theme to use when no stored preference exists */
25
26
  defaultTheme?: 'dark' | 'light';
27
+ /** Full theme configuration from program */
28
+ themeConfig?: ThemeConfig;
29
+ /** Theme from cookie (takes precedence over themeConfig.mode) */
30
+ themeCookie?: ColorScheme;
26
31
  }
27
32
  interface WidgetConfig {
28
33
  /** The DOM element ID where the widget should be mounted */
@@ -4,7 +4,7 @@ import {
4
4
  generateMetaTags,
5
5
  renderPage,
6
6
  wrapHtml
7
- } from "../chunk-IZPFJSX3.js";
7
+ } from "../chunk-C7LIB2RS.js";
8
8
  export {
9
9
  evaluateMetaExpression,
10
10
  generateHydrationScript,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/start",
3
- "version": "1.8.25",
3
+ "version": "1.8.27",
4
4
  "description": "Meta-framework for Constela applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,11 +44,12 @@
44
44
  "@tailwindcss/postcss": "^4.0.0",
45
45
  "tailwindcss": "^4.0.0",
46
46
  "ws": "^8.18.0",
47
- "@constela/compiler": "0.14.6",
48
- "@constela/core": "0.16.1",
49
- "@constela/server": "12.0.1",
50
- "@constela/router": "18.0.0",
51
- "@constela/runtime": "0.19.5"
47
+ "@constela/compiler": "0.14.7",
48
+ "@constela/core": "0.16.2",
49
+ "@constela/ai": "1.0.2",
50
+ "@constela/server": "12.0.2",
51
+ "@constela/router": "18.0.1",
52
+ "@constela/runtime": "0.19.7"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@types/ws": "^8.5.0",