aktion-runtime 0.5.0 → 0.5.1
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 +48 -14
- package/dist/aktion.iife.js +86 -44
- package/dist/aktion.iife.js.map +1 -1
- package/dist/aktion.js +1543 -1429
- package/dist/aktion.js.map +1 -1
- package/dist/aktion.umd.cjs +86 -44
- package/dist/aktion.umd.cjs.map +1 -1
- package/dist/system_prompt.txt +42 -0
- package/dist/types/renderer/renderer.d.ts +22 -0
- package/dist/types/runtime/effects.d.ts +22 -3
- package/dist/types/runtime/evaluator.d.ts +26 -2
- package/package.json +1 -1
package/dist/system_prompt.txt
CHANGED
|
@@ -230,6 +230,48 @@ brackets doesn't matter.
|
|
|
230
230
|
`effect { ... }` (no brackets) is equivalent to `effect [on:mount] { ... }` —
|
|
231
231
|
both run the body once on mount.
|
|
232
232
|
|
|
233
|
+
### Scope — top-level vs. component-local
|
|
234
|
+
An effect can live at the program top level OR inside a
|
|
235
|
+
`component Name() { … }` body. The syntax is identical; only the
|
|
236
|
+
lifecycle differs:
|
|
237
|
+
|
|
238
|
+
- **Top-level** — mounted once when the program parses, torn down on
|
|
239
|
+
`setResponse` / `clear()`. Use for global concerns (analytics,
|
|
240
|
+
app-wide shortcuts, hydration of shared atoms).
|
|
241
|
+
- **Component-local** — mounted once per component instance on its
|
|
242
|
+
first render, torn down when the instance disappears from the tree.
|
|
243
|
+
Each instance gets its own timers, watched-atom subscriptions, and
|
|
244
|
+
`cleanup(fn)` registrations. Use for per-instance work (per-row
|
|
245
|
+
polling, modal focus management, observers attached to a widget).
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
_app_ = App()
|
|
249
|
+
$value = 10
|
|
250
|
+
|
|
251
|
+
# Top-level — one shared interval for the whole program.
|
|
252
|
+
effect [on:every(1000)] {
|
|
253
|
+
$value = $value + 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
component App() {
|
|
257
|
+
return Box([Text("Value: " + $value)])
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
_app_ = App()
|
|
263
|
+
$value = 10
|
|
264
|
+
|
|
265
|
+
component App() {
|
|
266
|
+
# Component-local — interval starts on first render and is cleared
|
|
267
|
+
# automatically when the App instance leaves the tree.
|
|
268
|
+
effect [on:every(1000)] {
|
|
269
|
+
$value = $value + 1
|
|
270
|
+
}
|
|
271
|
+
return Box([Text("Value: " + $value)])
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
233
275
|
### Examples
|
|
234
276
|
|
|
235
277
|
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EvaluationContext } from '../runtime/evaluator.js';
|
|
2
|
+
import { EffectDeclaration } from '../parser/types.js';
|
|
2
3
|
import { StateStore } from '../runtime/state.js';
|
|
3
4
|
import { Router } from '../runtime/router.js';
|
|
4
5
|
import { ComponentLibrary } from '../library/types.js';
|
|
@@ -18,6 +19,20 @@ export interface RenderOptions {
|
|
|
18
19
|
* render as `[unknown component: <Name>]` so the failure is visible.
|
|
19
20
|
*/
|
|
20
21
|
evaluationContext?: () => EvaluationContext;
|
|
22
|
+
/**
|
|
23
|
+
* Mount `effect [ ...deps ] { … }` declarations discovered inside a
|
|
24
|
+
* `component { … }` body. Called by the renderer after every render of
|
|
25
|
+
* the instance; the implementation is expected to be idempotent so
|
|
26
|
+
* re-renders are no-ops once the effects are mounted. The host wires
|
|
27
|
+
* this to the same `EffectRunner` that handles top-level effects.
|
|
28
|
+
*/
|
|
29
|
+
mountInstanceEffects?: (instanceKey: string, decls: ReadonlyArray<EffectDeclaration>, getCtx: () => EvaluationContext) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Tear down every per-instance effect mounted under `instanceKey`.
|
|
32
|
+
* Invoked when the component instance disappears from the render tree
|
|
33
|
+
* (between two `beginRender`/`endRender` passes).
|
|
34
|
+
*/
|
|
35
|
+
unmountInstanceEffects?: (instanceKey: string) => void;
|
|
21
36
|
}
|
|
22
37
|
export declare class Renderer {
|
|
23
38
|
private options;
|
|
@@ -36,6 +51,13 @@ export declare class Renderer {
|
|
|
36
51
|
private readonly instanceDisposers;
|
|
37
52
|
/** Instance paths seen during the current render — used to GC stale state. */
|
|
38
53
|
private aliveInstances;
|
|
54
|
+
/**
|
|
55
|
+
* User-declared component instances that currently hold per-instance
|
|
56
|
+
* effects (mounted via `mountInstanceEffects`). Tracked separately from
|
|
57
|
+
* `instanceStates` so the renderer can fire `unmountInstanceEffects` on
|
|
58
|
+
* GC even when an instance never registered `useInstanceState`.
|
|
59
|
+
*/
|
|
60
|
+
private readonly instancesWithEffects;
|
|
39
61
|
constructor(options: RenderOptions);
|
|
40
62
|
/**
|
|
41
63
|
* Swap the component library backing this renderer. Used when the host
|
|
@@ -25,11 +25,30 @@ export declare class EffectRunner {
|
|
|
25
25
|
/** Get any errors raised at mount-time (denied capabilities, parse issues). */
|
|
26
26
|
getErrors(): ReadonlyArray<string>;
|
|
27
27
|
/**
|
|
28
|
-
* Mount every effect declaration in `decls`. Idempotent:
|
|
29
|
-
* that are already mounted under the same name are left
|
|
30
|
-
* that vanish from the new program are torn down.
|
|
28
|
+
* Mount every top-level effect declaration in `decls`. Idempotent:
|
|
29
|
+
* declarations that are already mounted under the same name are left
|
|
30
|
+
* alone, those that vanish from the new program are torn down.
|
|
31
|
+
*
|
|
32
|
+
* Only touches global (top-level) effects. Per-instance effects mounted
|
|
33
|
+
* inside `component { … }` bodies are managed via `syncInstanceEffects`
|
|
34
|
+
* / `unmountInstance` and are not affected by this call.
|
|
31
35
|
*/
|
|
32
36
|
syncEffects(decls: ReadonlyArray<EffectDeclaration>, getCtx: () => EvaluationContext): void;
|
|
37
|
+
/**
|
|
38
|
+
* Mount per-instance effects discovered inside a `component { … }` body.
|
|
39
|
+
* Idempotent: re-rendering the same instance with the same effect set is
|
|
40
|
+
* a no-op; effects that vanished from the body since the last render are
|
|
41
|
+
* torn down. Effects belonging to other instances are untouched.
|
|
42
|
+
*/
|
|
43
|
+
syncInstanceEffects(instanceKey: string, decls: ReadonlyArray<EffectDeclaration>, getCtx: () => EvaluationContext): void;
|
|
44
|
+
/**
|
|
45
|
+
* Tear down every effect that belongs to the given component instance
|
|
46
|
+
* (i.e. mounted via `syncInstanceEffects(instanceKey, …)`). Called by
|
|
47
|
+
* the renderer when an instance disappears from the tree so timers,
|
|
48
|
+
* interval handles, and state subscriptions don't outlive the
|
|
49
|
+
* component the user can see.
|
|
50
|
+
*/
|
|
51
|
+
unmountInstance(instanceKey: string): void;
|
|
33
52
|
reset(): void;
|
|
34
53
|
private mount;
|
|
35
54
|
private unmount;
|
|
@@ -85,6 +85,17 @@ export interface EvaluationContext {
|
|
|
85
85
|
componentDecls: Map<string, ComponentDeclaration>;
|
|
86
86
|
/** Effect declarations (`effect [ ...deps ] { ... }`), keyed by auto-generated name. */
|
|
87
87
|
effectDecls: Map<string, EffectDeclaration>;
|
|
88
|
+
/**
|
|
89
|
+
* Stack of per-component-invocation effect collection frames.
|
|
90
|
+
*
|
|
91
|
+
* When this stack is non-empty, an `EffectDeclaration` encountered while
|
|
92
|
+
* walking a block body is appended to the top frame instead of being
|
|
93
|
+
* registered globally on `effectDecls`. The renderer drains the frame
|
|
94
|
+
* immediately after `evaluateUserComponent` returns so it can mount the
|
|
95
|
+
* declarations on a per-instance scope (instead of globally, once per
|
|
96
|
+
* program).
|
|
97
|
+
*/
|
|
98
|
+
componentEffectStack: EffectDeclaration[][];
|
|
88
99
|
/** Action declarations (`action Foo() { ... }`). */
|
|
89
100
|
actionDecls: Map<string, ActionDeclaration>;
|
|
90
101
|
/** HTTP runtime (`http({...})` calls + interceptor configuration). */
|
|
@@ -135,6 +146,18 @@ export declare function resolveStateAlias(ctx: EvaluationContext, name: string):
|
|
|
135
146
|
*/
|
|
136
147
|
export declare function planProgram(program: Program, ctx: EvaluationContext): void;
|
|
137
148
|
export declare function evaluate(expr: Expression, ctx: EvaluationContext): unknown;
|
|
149
|
+
/**
|
|
150
|
+
* Result of `evaluateUserComponent`. `value` is the body's last
|
|
151
|
+
* expression value (a `ComponentNode`, another `UserComponentNode`, or a
|
|
152
|
+
* primitive) that the renderer will materialise. `effects` is the list of
|
|
153
|
+
* `effect [ ...deps ] { … }` declarations discovered inside the body —
|
|
154
|
+
* the renderer hands them to the host's `EffectRunner` so they mount on
|
|
155
|
+
* a per-instance scope and tear down when the instance unmounts.
|
|
156
|
+
*/
|
|
157
|
+
export interface EvaluatedUserComponent {
|
|
158
|
+
value: unknown;
|
|
159
|
+
effects: ReadonlyArray<EffectDeclaration>;
|
|
160
|
+
}
|
|
138
161
|
/**
|
|
139
162
|
* Evaluate a user-declared component body in a fresh per-instance scope.
|
|
140
163
|
* Called by the renderer once the stable instance key is known so
|
|
@@ -146,6 +169,7 @@ export declare function evaluate(expr: Expression, ctx: EvaluationContext): unkn
|
|
|
146
169
|
*
|
|
147
170
|
* Returns the body's last-expression value (typically a `ComponentNode`
|
|
148
171
|
* the renderer can hand to the library, or another `UserComponentNode`
|
|
149
|
-
* to expand recursively)
|
|
172
|
+
* to expand recursively) plus any `effect [ ...deps ] { … }` declarations
|
|
173
|
+
* discovered inside the body that the renderer must mount per-instance.
|
|
150
174
|
*/
|
|
151
|
-
export declare function evaluateUserComponent(node: UserComponentNode, ctx: EvaluationContext, instanceKey: string):
|
|
175
|
+
export declare function evaluateUserComponent(node: UserComponentNode, ctx: EvaluationContext, instanceKey: string): EvaluatedUserComponent;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aktion-runtime",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Aktion is a single web component that turns a compact, streaming-first DSL into a rich, interactive UI inside its shadow DOM. Works in React, Vue, Angular, Svelte, plain HTML — or no framework at all.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|