@constela/runtime 0.10.1 → 0.10.2

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.
Files changed (3) hide show
  1. package/README.md +306 -0
  2. package/dist/index.js +3 -0
  3. package/package.json +4 -4
package/README.md ADDED
@@ -0,0 +1,306 @@
1
+ # @constela/runtime
2
+
3
+ Runtime DOM renderer for the Constela UI framework with fine-grained reactivity.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @constela/runtime
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This package provides the client-side rendering engine for Constela applications. Key features:
14
+
15
+ - **Fine-grained Reactivity** - Signal-based updates without virtual DOM
16
+ - **Hydration** - Rehydrate server-rendered HTML
17
+ - **Markdown & Code** - Built-in Markdown and syntax highlighting support
18
+
19
+ ## API Reference
20
+
21
+ ### createApp
22
+
23
+ Creates and mounts a Constela application.
24
+
25
+ ```typescript
26
+ import { createApp } from '@constela/runtime';
27
+
28
+ const app = createApp(compiledProgram, document.getElementById('app'));
29
+
30
+ // Later: cleanup
31
+ app.destroy();
32
+ ```
33
+
34
+ **Parameters:**
35
+ - `program: CompiledProgram` - Compiled program from `@constela/compiler`
36
+ - `mount: HTMLElement` - DOM element to mount to
37
+
38
+ **Returns:** `AppInstance`
39
+
40
+ ### hydrateApp
41
+
42
+ Hydrates server-rendered HTML without DOM reconstruction.
43
+
44
+ ```typescript
45
+ import { hydrateApp } from '@constela/runtime';
46
+
47
+ const app = hydrateApp({
48
+ program: compiledProgram,
49
+ mount: document.getElementById('app'),
50
+ route: {
51
+ params: { id: '123' },
52
+ query: { tab: 'details' },
53
+ path: '/users/123'
54
+ },
55
+ imports: {
56
+ config: { apiUrl: 'https://api.example.com' }
57
+ }
58
+ });
59
+ ```
60
+
61
+ **HydrateOptions:**
62
+ - `program: CompiledProgram` - Compiled program
63
+ - `mount: HTMLElement` - Container element
64
+ - `route?: RouteContext` - Route parameters
65
+ - `imports?: Record<string, unknown>` - Import data
66
+
67
+ ### AppInstance
68
+
69
+ ```typescript
70
+ interface AppInstance {
71
+ destroy(): void;
72
+ setState(name: string, value: unknown): void;
73
+ getState(name: string): unknown;
74
+ subscribe(name: string, fn: (value: unknown) => void): () => void;
75
+ }
76
+ ```
77
+
78
+ #### destroy()
79
+
80
+ Cleans up the application, removes event listeners, and clears state.
81
+
82
+ #### setState(name, value)
83
+
84
+ Updates a state field programmatically.
85
+
86
+ ```typescript
87
+ app.setState('count', 10);
88
+ app.setState('user', { name: 'John', email: 'john@example.com' });
89
+ ```
90
+
91
+ #### getState(name)
92
+
93
+ Reads current state value.
94
+
95
+ ```typescript
96
+ const count = app.getState('count');
97
+ ```
98
+
99
+ #### subscribe(name, fn)
100
+
101
+ Subscribes to state changes. Returns an unsubscribe function.
102
+
103
+ ```typescript
104
+ const unsubscribe = app.subscribe('count', (value) => {
105
+ console.log('count changed:', value);
106
+ });
107
+
108
+ // Later: stop listening
109
+ unsubscribe();
110
+ ```
111
+
112
+ ## Reactive Primitives
113
+
114
+ Low-level reactive APIs for advanced usage.
115
+
116
+ ### createSignal
117
+
118
+ Creates a reactive signal with fine-grained dependency tracking.
119
+
120
+ ```typescript
121
+ import { createSignal } from '@constela/runtime';
122
+
123
+ const count = createSignal(0);
124
+
125
+ // Read value (auto-tracks in effects)
126
+ console.log(count.get()); // 0
127
+
128
+ // Update value
129
+ count.set(1);
130
+
131
+ // Subscribe to changes
132
+ const unsubscribe = count.subscribe((value) => {
133
+ console.log('Value:', value);
134
+ });
135
+ ```
136
+
137
+ ### createEffect
138
+
139
+ Creates a reactive side effect that auto-tracks dependencies.
140
+
141
+ ```typescript
142
+ import { createSignal, createEffect } from '@constela/runtime';
143
+
144
+ const name = createSignal('World');
145
+
146
+ const cleanup = createEffect(() => {
147
+ console.log(`Hello, ${name.get()}!`);
148
+
149
+ // Optional cleanup function
150
+ return () => {
151
+ console.log('Effect cleaned up');
152
+ };
153
+ });
154
+
155
+ name.set('Constela'); // Logs: "Hello, Constela!"
156
+
157
+ cleanup(); // Stops the effect
158
+ ```
159
+
160
+ ### createStateStore
161
+
162
+ Centralized state management with signal-based reactivity.
163
+
164
+ ```typescript
165
+ import { createStateStore } from '@constela/runtime';
166
+
167
+ const store = createStateStore({
168
+ count: { type: 'number', initial: 0 },
169
+ name: { type: 'string', initial: '' }
170
+ });
171
+
172
+ store.get('count'); // 0
173
+ store.set('count', 5);
174
+ store.subscribe('count', (value) => console.log(value));
175
+ ```
176
+
177
+ ## Expression Evaluation
178
+
179
+ ### evaluate
180
+
181
+ Evaluates compiled expressions.
182
+
183
+ ```typescript
184
+ import { evaluate } from '@constela/runtime';
185
+
186
+ const result = evaluate(expression, {
187
+ state: stateStore,
188
+ locals: { item: { id: 1, name: 'Test' } },
189
+ route: { params: { id: '123' }, query: new URLSearchParams(), path: '/items/123' },
190
+ imports: { config: { apiUrl: '...' } },
191
+ data: { posts: [...] },
192
+ refs: { inputEl: document.querySelector('#input') }
193
+ });
194
+ ```
195
+
196
+ **Supported Expressions:**
197
+ - Literals, state reads, variables
198
+ - Binary operations, logical not, conditionals
199
+ - Property access, array indexing
200
+ - Route parameters/query/path
201
+ - Imports and loaded data
202
+ - DOM refs
203
+
204
+ ## Action Execution
205
+
206
+ ### executeAction
207
+
208
+ Executes compiled actions.
209
+
210
+ ```typescript
211
+ import { executeAction } from '@constela/runtime';
212
+
213
+ await executeAction(action, {
214
+ state: stateStore,
215
+ locals: {},
216
+ route: { ... },
217
+ imports: { ... },
218
+ refs: { ... },
219
+ subscriptions: []
220
+ });
221
+ ```
222
+
223
+ **Supported Steps:**
224
+ - `set`, `update` - State mutations
225
+ - `fetch` - HTTP requests
226
+ - `storage` - localStorage/sessionStorage
227
+ - `clipboard` - Clipboard operations
228
+ - `navigate` - Page navigation
229
+ - `import`, `call` - Dynamic imports and function calls
230
+ - `subscribe`, `dispose` - Event subscriptions
231
+ - `dom` - DOM manipulation
232
+ - `if` - Conditional execution
233
+
234
+ ## Rendering
235
+
236
+ ### render
237
+
238
+ Renders compiled nodes to DOM.
239
+
240
+ ```typescript
241
+ import { render } from '@constela/runtime';
242
+
243
+ const domNode = render(compiledNode, {
244
+ state: stateStore,
245
+ actions: compiledProgram.actions,
246
+ components: {},
247
+ locals: {},
248
+ route: { ... },
249
+ imports: { ... },
250
+ refs: {},
251
+ subscriptions: [],
252
+ cleanups: []
253
+ });
254
+ ```
255
+
256
+ **Supported Nodes:**
257
+ - Element nodes with props and event handlers
258
+ - Text nodes with reactive updates
259
+ - Conditional rendering (`if/else`)
260
+ - List rendering (`each`)
261
+ - Markdown with sanitization
262
+ - Code blocks with Shiki highlighting
263
+
264
+ ## Markdown & Code Blocks
265
+
266
+ The runtime includes built-in support for rendering Markdown and syntax-highlighted code.
267
+
268
+ ### Markdown
269
+
270
+ Rendered using [marked](https://marked.js.org/) with [DOMPurify](https://github.com/cure53/DOMPurify) sanitization.
271
+
272
+ ```json
273
+ {
274
+ "kind": "markdown",
275
+ "content": { "expr": "state", "name": "markdownContent" }
276
+ }
277
+ ```
278
+
279
+ ### Code Blocks
280
+
281
+ Rendered with [Shiki](https://shiki.style/) syntax highlighting.
282
+
283
+ ```json
284
+ {
285
+ "kind": "code",
286
+ "code": { "expr": "lit", "value": "const x = 1;" },
287
+ "language": { "expr": "lit", "value": "typescript" }
288
+ }
289
+ ```
290
+
291
+ **Features:**
292
+ - Dual theme support (light/dark)
293
+ - Copy button with feedback
294
+ - Dynamic language loading
295
+
296
+ ## Security
297
+
298
+ The runtime includes security measures:
299
+
300
+ - **Prototype Pollution Prevention** - Blocks `__proto__`, `constructor`, `prototype`
301
+ - **Safe Globals** - Only exposes `JSON`, `Math`, `Date`, `Object`, `Array`, `String`, `Number`, `Boolean`, `console`
302
+ - **HTML Sanitization** - DOMPurify for Markdown content
303
+
304
+ ## License
305
+
306
+ MIT
package/dist/index.js CHANGED
@@ -248,6 +248,9 @@ function evaluate(expr, ctx) {
248
248
  }
249
249
  return dataValue;
250
250
  }
251
+ case "param": {
252
+ return void 0;
253
+ }
251
254
  default: {
252
255
  const _exhaustiveCheck = expr;
253
256
  throw new Error(`Unknown expression type: ${JSON.stringify(_exhaustiveCheck)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/runtime",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Runtime DOM renderer for Constela UI framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,8 +18,8 @@
18
18
  "dompurify": "^3.3.1",
19
19
  "marked": "^17.0.1",
20
20
  "shiki": "^3.20.0",
21
- "@constela/compiler": "0.7.0",
22
- "@constela/core": "0.7.0"
21
+ "@constela/core": "0.7.0",
22
+ "@constela/compiler": "0.7.1"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/dompurify": "^3.2.0",
@@ -29,7 +29,7 @@
29
29
  "tsup": "^8.0.0",
30
30
  "typescript": "^5.3.0",
31
31
  "vitest": "^2.0.0",
32
- "@constela/server": "3.0.0"
32
+ "@constela/server": "3.0.1"
33
33
  },
34
34
  "engines": {
35
35
  "node": ">=20.0.0"