@constela/start 1.8.20 → 1.8.22

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
@@ -166,6 +166,63 @@ Define reusable layouts:
166
166
  }
167
167
  ```
168
168
 
169
+ ### Components with Local State in Layouts
170
+
171
+ Layout components can use `localState` and `localActions` for instance-scoped state:
172
+
173
+ ```json
174
+ {
175
+ "version": "1.0",
176
+ "type": "layout",
177
+ "components": {
178
+ "Sidebar": {
179
+ "params": { "items": { "type": "list" } },
180
+ "localState": {
181
+ "expandedCategory": { "type": "string", "initial": "" }
182
+ },
183
+ "localActions": [
184
+ {
185
+ "name": "toggleCategory",
186
+ "steps": [{ "do": "set", "target": "expandedCategory", "value": { "expr": "var", "name": "payload" } }]
187
+ }
188
+ ],
189
+ "view": {
190
+ "kind": "each",
191
+ "items": { "expr": "param", "name": "items" },
192
+ "as": "item",
193
+ "body": {
194
+ "kind": "element",
195
+ "tag": "div",
196
+ "children": [
197
+ {
198
+ "kind": "element",
199
+ "tag": "button",
200
+ "props": {
201
+ "onClick": {
202
+ "event": "click",
203
+ "action": "toggleCategory",
204
+ "payload": { "expr": "var", "name": "item", "path": "name" }
205
+ }
206
+ },
207
+ "children": [
208
+ { "kind": "text", "value": { "expr": "var", "name": "item", "path": "name" } }
209
+ ]
210
+ }
211
+ ]
212
+ }
213
+ }
214
+ }
215
+ },
216
+ "view": { ... }
217
+ }
218
+ ```
219
+
220
+ Key features:
221
+ - Components in layouts support `localState` and `localActions`
222
+ - `param` expressions inside components are substituted with prop values
223
+ - Works with dynamic props from `each` loops
224
+ - Each component instance maintains independent state
225
+
169
226
  Use layout in pages:
170
227
 
171
228
  ```json
@@ -3,7 +3,7 @@ import {
3
3
  generateMetaTags,
4
4
  renderPage,
5
5
  wrapHtml
6
- } from "./chunk-OXGKHULH.js";
6
+ } from "./chunk-IZPFJSX3.js";
7
7
 
8
8
  // src/router/file-router.ts
9
9
  import fg from "fast-glob";
@@ -2728,15 +2728,15 @@ async function createDevServer(options = {}) {
2728
2728
  query: Object.fromEntries(url.searchParams.entries()),
2729
2729
  path: pathname
2730
2730
  };
2731
- const hydrationScript = generateHydrationScript(composedProgram, widgets, routeContext);
2731
+ const hmrUrl = hmrServer ? `ws://${host}:${hmrServer.port}` : void 0;
2732
+ const hydrationScript = generateHydrationScript(composedProgram, widgets, routeContext, hmrUrl);
2732
2733
  const metaTags = generateMetaTags(composedProgram.route, {
2733
2734
  params: match.params,
2734
2735
  query: Object.fromEntries(url.searchParams.entries()),
2735
2736
  path: pathname
2736
2737
  });
2737
2738
  const cssHead = css ? (Array.isArray(css) ? css : [css]).map((p) => `<link rel="stylesheet" href="/${p}">`).join("\n") : "";
2738
- const hmrScript = hmrServer ? `<script>window.__CONSTELA_HMR_URL__ = "ws://${host}:${hmrServer.port}";</script>` : "";
2739
- const head = [metaTags, cssHead, hmrScript].filter(Boolean).join("\n");
2739
+ const head = [metaTags, cssHead].filter(Boolean).join("\n");
2740
2740
  const themeState = composedProgram.state?.["theme"];
2741
2741
  let initialTheme;
2742
2742
  if (themeState) {
@@ -47,10 +47,20 @@ function toJsIdentifier(id) {
47
47
  }
48
48
  return result;
49
49
  }
50
- function generateHydrationScript(program, widgets, route) {
50
+ function generateHydrationScript(program, widgets, route, hmrUrl) {
51
51
  const serializedProgram = escapeJsonForScript(serializeProgram(program));
52
52
  const hasWidgets = widgets && widgets.length > 0;
53
- const imports = hasWidgets ? `import { hydrateApp, createApp } from '@constela/runtime';` : `import { hydrateApp } from '@constela/runtime';`;
53
+ const enableHmr = hmrUrl && hmrUrl.length > 0;
54
+ let imports;
55
+ if (enableHmr) {
56
+ const baseImports = ["hydrateApp", "createHMRClient", "createHMRHandler", "createErrorOverlay"];
57
+ if (hasWidgets) {
58
+ baseImports.push("createApp");
59
+ }
60
+ imports = `import { ${baseImports.join(", ")} } from '@constela/runtime';`;
61
+ } else {
62
+ imports = hasWidgets ? `import { hydrateApp, createApp } from '@constela/runtime';` : `import { hydrateApp } from '@constela/runtime';`;
63
+ }
54
64
  const widgetDeclarations = hasWidgets ? widgets.map((widget) => {
55
65
  const jsId = toJsIdentifier(widget.id);
56
66
  const serializedWidget = escapeJsonForScript(
@@ -81,11 +91,34 @@ if (container_${jsId}) {
81
91
  program,
82
92
  container: document.getElementById('app')
83
93
  }`;
94
+ let hmrSetup = "";
95
+ if (enableHmr) {
96
+ const escapedHmrUrl = escapeJsString(hmrUrl);
97
+ const handlerOptions = route ? `{
98
+ container: document.getElementById('app'),
99
+ program,
100
+ route
101
+ }` : `{
102
+ container: document.getElementById('app'),
103
+ program
104
+ }`;
105
+ hmrSetup = `
106
+
107
+ const overlay = createErrorOverlay();
108
+ const handler = createHMRHandler(${handlerOptions});
109
+ const client = createHMRClient({
110
+ url: '${escapedHmrUrl}',
111
+ onUpdate: (file, newProgram) => { handler.handleUpdate(newProgram); },
112
+ onError: (file, errors) => { overlay.show(errors); },
113
+ onConnect: () => { console.log('[HMR] Connected'); }
114
+ });
115
+ client.connect();`;
116
+ }
84
117
  return `${imports}
85
118
 
86
119
  const program = ${serializedProgram};
87
120
  ${routeDeclaration ? "\n" + routeDeclaration : ""}${widgetDeclarations ? "\n" + widgetDeclarations : ""}
88
- hydrateApp(${hydrateOptions});${widgetMounting}`;
121
+ hydrateApp(${hydrateOptions});${hmrSetup}${widgetMounting}`;
89
122
  }
90
123
  function wrapHtml(content, hydrationScript, head, options) {
91
124
  let langAttr = "";
package/dist/cli/index.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  hyperlink,
5
5
  loadConfig,
6
6
  resolveConfig
7
- } from "../chunk-L3BK4NOH.js";
8
- import "../chunk-OXGKHULH.js";
7
+ } from "../chunk-E4SJFPTB.js";
8
+ import "../chunk-IZPFJSX3.js";
9
9
 
10
10
  // src/cli/index.ts
11
11
  import { Command } from "commander";
package/dist/index.js CHANGED
@@ -28,14 +28,14 @@ import {
28
28
  transformCsv,
29
29
  transformMdx,
30
30
  transformYaml
31
- } from "./chunk-L3BK4NOH.js";
31
+ } from "./chunk-E4SJFPTB.js";
32
32
  import {
33
33
  evaluateMetaExpression,
34
34
  generateHydrationScript,
35
35
  generateMetaTags,
36
36
  renderPage,
37
37
  wrapHtml
38
- } from "./chunk-OXGKHULH.js";
38
+ } from "./chunk-IZPFJSX3.js";
39
39
 
40
40
  // src/build/ssg.ts
41
41
  import { mkdir, writeFile } from "fs/promises";
@@ -54,13 +54,15 @@ interface HydrationRouteContext {
54
54
  * - Serializes the program data
55
55
  * - Calls hydrateApp with the program and container element
56
56
  * - Optionally mounts widgets using createApp
57
+ * - When hmrUrl is provided, sets up HMR client, handler, and error overlay
57
58
  *
58
59
  * @param program - The compiled program to hydrate
59
60
  * @param widgets - Optional array of widget configurations to mount after hydration
60
61
  * @param route - Optional route context for dynamic routes
62
+ * @param hmrUrl - Optional WebSocket URL for HMR connection (development mode)
61
63
  * @returns JavaScript module code as string
62
64
  */
63
- declare function generateHydrationScript(program: CompiledProgram, widgets?: WidgetConfig[], route?: HydrationRouteContext): string;
65
+ declare function generateHydrationScript(program: CompiledProgram, widgets?: WidgetConfig[], route?: HydrationRouteContext, hmrUrl?: string): string;
64
66
  /**
65
67
  * Wraps rendered content in a complete HTML document.
66
68
  *
@@ -4,7 +4,7 @@ import {
4
4
  generateMetaTags,
5
5
  renderPage,
6
6
  wrapHtml
7
- } from "../chunk-OXGKHULH.js";
7
+ } from "../chunk-IZPFJSX3.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.20",
3
+ "version": "1.8.22",
4
4
  "description": "Meta-framework for Constela applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,11 +44,11 @@
44
44
  "@tailwindcss/postcss": "^4.0.0",
45
45
  "tailwindcss": "^4.0.0",
46
46
  "ws": "^8.18.0",
47
+ "@constela/compiler": "0.14.4",
47
48
  "@constela/core": "0.15.2",
48
- "@constela/compiler": "0.14.3",
49
49
  "@constela/router": "18.0.0",
50
- "@constela/server": "11.0.1",
51
- "@constela/runtime": "0.19.1"
50
+ "@constela/runtime": "0.19.2",
51
+ "@constela/server": "11.0.1"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/ws": "^8.5.0",