aktion-runtime 0.5.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.
Files changed (76) hide show
  1. package/README.md +1246 -0
  2. package/dist/aktion.iife.js +8431 -0
  3. package/dist/aktion.iife.js.map +1 -0
  4. package/dist/aktion.js +22594 -0
  5. package/dist/aktion.js.map +1 -0
  6. package/dist/aktion.umd.cjs +8431 -0
  7. package/dist/aktion.umd.cjs.map +1 -0
  8. package/dist/index.cjs +3 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +5 -0
  11. package/dist/system_prompt.txt +1095 -0
  12. package/dist/system_prompt_chat.txt +404 -0
  13. package/dist/types/element.d.ts +175 -0
  14. package/dist/types/icons/index.d.ts +45 -0
  15. package/dist/types/index.d.ts +15 -0
  16. package/dist/types/language/builtins.d.ts +33 -0
  17. package/dist/types/language/components.d.ts +28 -0
  18. package/dist/types/language/grammar.d.ts +121 -0
  19. package/dist/types/language/index.d.ts +41 -0
  20. package/dist/types/language/snippets.d.ts +17 -0
  21. package/dist/types/library/components/_internal.d.ts +56 -0
  22. package/dist/types/library/components/advanced-charts.d.ts +6 -0
  23. package/dist/types/library/components/advanced-data.d.ts +6 -0
  24. package/dist/types/library/components/advanced-forms.d.ts +12 -0
  25. package/dist/types/library/components/advanced-patterns.d.ts +13 -0
  26. package/dist/types/library/components/charts.d.ts +5 -0
  27. package/dist/types/library/components/chat.d.ts +6 -0
  28. package/dist/types/library/components/content.d.ts +33 -0
  29. package/dist/types/library/components/data.d.ts +9 -0
  30. package/dist/types/library/components/editors.d.ts +5 -0
  31. package/dist/types/library/components/feedback.d.ts +14 -0
  32. package/dist/types/library/components/forms-shared.d.ts +7 -0
  33. package/dist/types/library/components/forms.d.ts +21 -0
  34. package/dist/types/library/components/helpers.d.ts +33 -0
  35. package/dist/types/library/components/layout.d.ts +20 -0
  36. package/dist/types/library/components/media.d.ts +7 -0
  37. package/dist/types/library/components/menu.d.ts +5 -0
  38. package/dist/types/library/components/navigation.d.ts +6 -0
  39. package/dist/types/library/components/new-components.d.ts +13 -0
  40. package/dist/types/library/components/patterns.d.ts +39 -0
  41. package/dist/types/library/components/router.d.ts +2 -0
  42. package/dist/types/library/components/theme.d.ts +2 -0
  43. package/dist/types/library/index.d.ts +5 -0
  44. package/dist/types/library/registry.d.ts +15 -0
  45. package/dist/types/library/types.d.ts +140 -0
  46. package/dist/types/library/utils.d.ts +73 -0
  47. package/dist/types/library/validate.d.ts +27 -0
  48. package/dist/types/parser/frontier.d.ts +65 -0
  49. package/dist/types/parser/index.d.ts +4 -0
  50. package/dist/types/parser/lexer.d.ts +46 -0
  51. package/dist/types/parser/parser.d.ts +2 -0
  52. package/dist/types/parser/types.d.ts +349 -0
  53. package/dist/types/prompt/generator.d.ts +33 -0
  54. package/dist/types/prompt/index.d.ts +1 -0
  55. package/dist/types/renderer/index.d.ts +1 -0
  56. package/dist/types/renderer/morph.d.ts +42 -0
  57. package/dist/types/renderer/renderer.d.ts +73 -0
  58. package/dist/types/runtime/builtins.d.ts +27 -0
  59. package/dist/types/runtime/console.d.ts +21 -0
  60. package/dist/types/runtime/effects.d.ts +69 -0
  61. package/dist/types/runtime/evaluator.d.ts +151 -0
  62. package/dist/types/runtime/http.d.ts +85 -0
  63. package/dist/types/runtime/i18n.d.ts +40 -0
  64. package/dist/types/runtime/index.d.ts +9 -0
  65. package/dist/types/runtime/router.d.ts +105 -0
  66. package/dist/types/runtime/state.d.ts +84 -0
  67. package/dist/types/runtime/storage.d.ts +50 -0
  68. package/dist/types/theme/index.d.ts +175 -0
  69. package/dist/types/theme/styles.d.ts +9 -0
  70. package/dist/types/tooling/codemod.d.ts +36 -0
  71. package/dist/types/tooling/delta.d.ts +74 -0
  72. package/dist/types/tooling/formatter.d.ts +8 -0
  73. package/dist/types/tooling/index.d.ts +29 -0
  74. package/dist/types/tooling/inspector.d.ts +49 -0
  75. package/dist/types/tooling/language-service.d.ts +57 -0
  76. package/package.json +63 -0
@@ -0,0 +1,2 @@
1
+ import { ComponentSpec } from '../types.js';
2
+ export declare const NavLink: ComponentSpec;
@@ -0,0 +1,2 @@
1
+ import { ComponentSpec } from '../types.js';
2
+ export declare const Theme: ComponentSpec;
@@ -0,0 +1,5 @@
1
+ import { ComponentLibrary } from './types.js';
2
+ export * from './types.js';
3
+ export * from './registry.js';
4
+ export { validateProgramSchema, validateProgram } from './validate.js';
5
+ export declare const defaultLibrary: ComponentLibrary;
@@ -0,0 +1,15 @@
1
+ import { ComponentLibrary, ComponentSpec } from './types.js';
2
+ /**
3
+ * Combines two libraries by name. Components from `extra` win on collision.
4
+ * Useful when a consumer registers their own components alongside the
5
+ * built-ins via `<aktion-app>.registerComponents([...])`.
6
+ */
7
+ export declare function mergeLibraries(base: ComponentLibrary, extra: {
8
+ components: ReadonlyArray<ComponentSpec>;
9
+ root?: string;
10
+ }): ComponentLibrary;
11
+ /**
12
+ * Resolve a component spec by name. O(1) thanks to the lazily-built index
13
+ * cached against the library's `components` array.
14
+ */
15
+ export declare function findComponent(library: ComponentLibrary, name: string): ComponentSpec | undefined;
@@ -0,0 +1,140 @@
1
+ import { ComponentNode } from '../runtime/evaluator.js';
2
+ import { Router } from '../runtime/router.js';
3
+ export type PrimitiveType = "string" | "number" | "boolean" | "any" | "callable" | "Node" | "Node[]";
4
+ export interface PropSpec {
5
+ name: string;
6
+ type: PrimitiveType | string;
7
+ optional?: boolean;
8
+ /**
9
+ * Aktion 0.5 §19.1 — at most one prop per spec may be marked
10
+ * positional. Authors may pass exactly one positional argument at the
11
+ * call site; it lands in this prop's slot regardless of its position in
12
+ * the `props` array. Every other prop must be provided as a named
13
+ * argument (`prop: value`). Specs without any `positional: true` flag
14
+ * reject every positional argument at evaluation time.
15
+ */
16
+ positional?: boolean;
17
+ /**
18
+ * Marker only — recorded so the schema-driven prompt generator can render
19
+ * required props without an `?` suffix. Not enforced by the runtime.
20
+ */
21
+ required?: boolean;
22
+ description?: string;
23
+ /** Accepted enum values, formatted as a comma-separated list in the prompt. */
24
+ enum?: readonly string[];
25
+ /**
26
+ * Optional alternative names that route to this prop when used as a
27
+ * `name: value` named argument at the call site. Lets the same prop be
28
+ * called by its canonical name *and* a synonym so authors writing
29
+ * `Badge("Live", tone: "success")` and `Badge("Live", variant: "success")`
30
+ * both land in the same slot. Aliases never change the runtime prop name
31
+ * — renderers continue to read `props[spec.name]`.
32
+ */
33
+ aliases?: readonly string[];
34
+ }
35
+ export interface ComponentSpec {
36
+ name: string;
37
+ description: string;
38
+ props: readonly PropSpec[];
39
+ render: ComponentRenderFn;
40
+ }
41
+ /**
42
+ * Aktion 0.5 §19.1 — locate the canonical positional prop
43
+ * for a spec. A spec opts in by marking exactly one prop with
44
+ * `positional: true`. Specs that omit the marker fall back to "first prop
45
+ * is positional", which is the convention every legacy library component
46
+ * was authored with.
47
+ *
48
+ * `findPositionalIndex` is consumed by the evaluator (to route the single
49
+ * positional argument to the right slot regardless of where in `props`
50
+ * the positional prop lives — e.g. `Portal(target?, children)` keeps
51
+ * `children` as the positional but it lives at index 1).
52
+ *
53
+ * `findPositionalProp` returns the resolved prop or `undefined` for void
54
+ * components (zero props).
55
+ */
56
+ export declare function findPositionalIndex(spec: ComponentSpec): number;
57
+ export declare function findPositionalProp(spec: ComponentSpec): PropSpec | undefined;
58
+ /**
59
+ * Asserts that every spec in the library declares at most one positional
60
+ * prop. Surfaces inconsistencies as a `SyntaxError` at module load so the
61
+ * library never ships a contradictory spec.
62
+ */
63
+ export declare function assertOnePositionalMax(specs: ReadonlyArray<ComponentSpec>): void;
64
+ /**
65
+ * Stable, component-local state slot. Returned by
66
+ * `RenderHelpers.useInstanceState`. Keep the reference for the lifetime of
67
+ * a single render — the renderer wires it to a long-lived storage cell so
68
+ * subsequent renders read the value the previous click handler wrote.
69
+ */
70
+ export interface InstanceStateSlot<T> {
71
+ get(): T;
72
+ set(value: T): void;
73
+ }
74
+ export interface RenderHelpers {
75
+ /** Render a child node tree (a ComponentNode or array of nodes). */
76
+ renderNode: (node: unknown) => Node;
77
+ /**
78
+ * Invoke a user-supplied callable (e.g. a lambda passed as an `onClick`
79
+ * prop, or a bare `action` declaration reference). Safe to call with
80
+ * `undefined` / `null` — silently no-ops. Returned values are ignored.
81
+ *
82
+ * This is the single dispatch path for user-authored event handlers in
83
+ * Aktion 0.5. The legacy `Action([@Set, @Run, ...])` payload
84
+ * is no longer accepted.
85
+ */
86
+ invoke: (callable: unknown, ...args: unknown[]) => void;
87
+ /** Set a state value. Used by library primitives for their own internal state writes. */
88
+ setState: (name: string, value: unknown) => void;
89
+ /** Reset state values back to their initial declared value. */
90
+ resetState: (...names: string[]) => void;
91
+ /** Dispatch an `assistant-message` CustomEvent on the host element. */
92
+ sendToAssistant: (message: string) => void;
93
+ /** Open a URL (sanitised against `javascript:` payloads). */
94
+ openUrl: (url: string) => void;
95
+ /** Bind a `$variable` to an HTML form element. */
96
+ bindState: (element: HTMLElement, name: string, options?: {
97
+ event?: string;
98
+ getValue?: (el: HTMLElement) => unknown;
99
+ }) => void;
100
+ /**
101
+ * Persist component-local state across re-renders. The slot is keyed by
102
+ * the component's position in the tree (its source location plus its
103
+ * path through sibling iterations), so independent instances never share
104
+ * a value. Used by stateful primitives like `Tabs` so user-driven UI
105
+ * state (active tab, expanded row, …) survives a re-render triggered by
106
+ * unrelated state changes.
107
+ */
108
+ useInstanceState: <T>(key: string, initialValue: T) => InstanceStateSlot<T>;
109
+ /**
110
+ * Register a cleanup callback tied to this component instance. The renderer
111
+ * invokes the callback once the instance disappears from the tree (e.g. a
112
+ * Toast finishes auto-dismissing or the parent re-renders without it).
113
+ * Use for `setTimeout` / `setInterval` handles and external resources so we
114
+ * never accumulate work for components the user can no longer see.
115
+ *
116
+ * Calling this multiple times during a single render replaces any previous
117
+ * disposer for the same `key` (cancelling the prior cleanup runs immediately
118
+ * via that callback's caller). If `key` is omitted, each call registers an
119
+ * independent disposer.
120
+ */
121
+ registerDisposer: (cleanup: () => void, key?: string) => void;
122
+ /**
123
+ * Hash-based router instance, always provided. Components such as `NavLink`
124
+ * call `router.navigate(path)` directly to change the active route.
125
+ */
126
+ router: Router;
127
+ }
128
+ export type ComponentRenderFn = (node: ComponentNode, props: Record<string, unknown>, helpers: RenderHelpers) => Node;
129
+ export interface ComponentGroup {
130
+ name: string;
131
+ components: readonly string[];
132
+ notes?: readonly string[];
133
+ }
134
+ export interface ComponentLibrary {
135
+ root: string;
136
+ components: ReadonlyArray<ComponentSpec>;
137
+ componentGroups?: ReadonlyArray<ComponentGroup>;
138
+ }
139
+ /** Resolve positional args from Aktion into named props. */
140
+ export declare function mapPositionalArgs(spec: ComponentSpec, args: ReadonlyArray<unknown>): Record<string, unknown>;
@@ -0,0 +1,73 @@
1
+ import { IconSize } from '../icons/index.js';
2
+ export declare function el<K extends keyof HTMLElementTagNameMap>(tag: K, attrs?: Record<string, string | number | boolean | null | undefined>, children?: ReadonlyArray<Node | string | null | undefined>): HTMLElementTagNameMap[K];
3
+ export declare function text(value: unknown): string;
4
+ export declare function classNames(...parts: Array<string | false | null | undefined>): string;
5
+ export declare function asArray<T>(value: unknown): T[];
6
+ export declare function asString(value: unknown, fallback?: string): string;
7
+ export declare function asBoolean(value: unknown, fallback?: boolean): boolean;
8
+ export declare function asNumber(value: unknown, fallback?: number): number;
9
+ /**
10
+ * Breakpoint keys honoured by responsive prop maps (Grid columns, Stack
11
+ * direction, etc.). Ordered from narrow → wide so CSS only needs to chain
12
+ * one `min-width` per breakpoint and naturally cascades.
13
+ */
14
+ export declare const RESPONSIVE_BREAKPOINTS: readonly ["base", "sm", "md", "lg", "xl"];
15
+ export type Breakpoint = typeof RESPONSIVE_BREAKPOINTS[number];
16
+ /**
17
+ * Normalise a prop value that may be a single value or a responsive map
18
+ * like `{sm: 1, md: 2, lg: 4}`. Returns either:
19
+ * - `{kind: "single", value}` — caller should use the value directly
20
+ * - `{kind: "responsive", values}` — caller should emit CSS variables /
21
+ * data attributes for each breakpoint
22
+ *
23
+ * A bare key without a breakpoint prefix (e.g. `{value: 2}`) collapses to
24
+ * a single-value result. Unknown breakpoint keys are ignored so typos
25
+ * don't crash the page.
26
+ */
27
+ export type ResponsiveProp<T> = {
28
+ kind: "single";
29
+ value: T | null;
30
+ } | {
31
+ kind: "responsive";
32
+ values: Partial<Record<Breakpoint, T>>;
33
+ };
34
+ export declare function readResponsiveProp<T>(value: unknown): ResponsiveProp<T>;
35
+ export declare function sanitiseCssUrl(raw: string): string;
36
+ export declare function sanitiseCssLength(raw: unknown, fallback: string): string;
37
+ /**
38
+ * Sanitise an LLM-supplied `href` value before it lands on an anchor.
39
+ *
40
+ * - Fragments (`#anchor`), root-relative paths (`/about`), and pure query
41
+ * strings (`?q=foo`) are kept verbatim.
42
+ * - Absolute URLs with an allow-listed scheme (`http`, `https`, `mailto`,
43
+ * `tel`) are kept verbatim.
44
+ * - Anything else — `javascript:`, `vbscript:`, `data:text/html`,
45
+ * `file:`, protocol-relative `//host/path`, control characters — collapses
46
+ * to `fallback` (default `"#"`) so the click never navigates anywhere
47
+ * dangerous.
48
+ *
49
+ * This is the single chokepoint every component should call before assigning
50
+ * `href`. The Markdown renderer has its own (stricter, HTML-escaping)
51
+ * sibling, but the validation rules match so behaviour stays consistent.
52
+ */
53
+ export declare function sanitiseHref(raw: unknown, fallback?: string): string;
54
+ /**
55
+ * Sanitise an LLM-supplied image `src` before it lands on an `<img>`.
56
+ *
57
+ * Relative paths and same-origin shapes are kept as-is. Absolute URLs with
58
+ * an allow-listed scheme (`http`, `https`, `data`, `blob`) are kept. Anything
59
+ * else (`javascript:`, `vbscript:`, `file:`, …) returns the empty string so
60
+ * callers can render their own placeholder.
61
+ */
62
+ export declare function sanitiseImageSrc(raw: unknown): string;
63
+ /**
64
+ * Render an icon-typed prop into an `<i class="rui-icon fa-...">` element.
65
+ *
66
+ * Falls back to a `<span>` containing the raw string when the value is not
67
+ * a Font Awesome name (legacy emoji input). Returns `null` when the value
68
+ * is empty / nullish so callers can short-circuit.
69
+ */
70
+ export declare function renderIcon(value: unknown, options?: {
71
+ className?: string;
72
+ size?: IconSize | string;
73
+ }): HTMLElement | null;
@@ -0,0 +1,27 @@
1
+ import { ParseError, Program } from '../parser/types.js';
2
+ import { ComponentLibrary } from './types.js';
3
+ /**
4
+ * Combined entry point for hosts: parse the source and merge any
5
+ * schema-level violations into `program.errors`. The element calls
6
+ * this so the on-screen error banner surfaces *every* Aktion 0.5
7
+ * violation, not just the syntactic ones. Returning the
8
+ * parsed program lets the caller render the committed prefix when
9
+ * the input still parses cleanly.
10
+ */
11
+ export declare function validateProgram(source: string, library: ComponentLibrary): Program;
12
+ /**
13
+ * Schema-as-truth validation (§15).
14
+ *
15
+ * Walks a parsed `Program` and returns every Aktion 0.5
16
+ * schema violation as a `ParseError`. In 0.5 these are **fatal** — the
17
+ * host should merge them into `program.errors` (see
18
+ * `validateProgram(source, library)` for the combined entry point) and
19
+ * surface the error banner instead of rendering. There are no longer
20
+ * any "advisory warnings" — every legacy v1 surface either:
21
+ *
22
+ * - produces a parser-level migration error at parse time, or
23
+ * - produces a schema-validator error here when library knowledge is
24
+ * required (multi-positional calls, unknown props, enum mismatches,
25
+ * legacy Theme tokens, …).
26
+ */
27
+ export declare function validateProgramSchema(program: Program, library: ComponentLibrary): ParseError[];
@@ -0,0 +1,65 @@
1
+ import { ParseError, Program, Statement } from './types.js';
2
+ export interface FrontierResult {
3
+ /**
4
+ * Longest prefix of `source` (line-aligned) where every statement
5
+ * parsed without errors. Always ends on a newline boundary, or equals
6
+ * the empty string when no committed prefix exists yet.
7
+ */
8
+ committedSource: string;
9
+ /** Tail of `source` past the committed prefix — the "in-flight" lines. */
10
+ draftingSource: string;
11
+ /**
12
+ * Names declared inside the committed prefix. Top-level identifiers
13
+ * only — block-local bindings (component params, $state inside a
14
+ * component body) are not included.
15
+ */
16
+ committedBindings: ReadonlyArray<string>;
17
+ /**
18
+ * The set `U` (SCC-2): top-level names that appear inside the drafting
19
+ * tail but have not yet committed. Includes:
20
+ *
21
+ * - Identifiers on the left of an assignment (`foo = …`).
22
+ * - State assignments (`$x = …`).
23
+ * - Component / effect / action / router declarations.
24
+ *
25
+ * Names that only appear as call expressions (`Button(…)`) inside the
26
+ * drafting tail are not listed — those are dependencies, not bindings.
27
+ */
28
+ uncommittedBindings: ReadonlyArray<string>;
29
+ /**
30
+ * Statements that parsed cleanly in the committed prefix. Hosts can
31
+ * walk this list to enumerate the committed surface (components,
32
+ * effects, actions, endpoints, router declarations) without having to
33
+ * re-parse.
34
+ */
35
+ committedStatements: ReadonlyArray<Statement>;
36
+ /** Errors reported by the parser. Empty when the whole input parses cleanly. */
37
+ errors: ReadonlyArray<ParseError>;
38
+ }
39
+ /**
40
+ * Compute the streaming frontier for `source`. Cheap — runs a single
41
+ * `parse(source)` pass and walks the resulting statements once.
42
+ *
43
+ * Example:
44
+ *
45
+ * const f = computeFrontier(
46
+ * "$state count = 0\n" +
47
+ * "component Counter() {\n" +
48
+ * " Stack(Te" // mid-stream: half-written `Text`
49
+ * );
50
+ * // f.committedBindings = ["count"]
51
+ * // f.uncommittedBindings = ["Counter"]
52
+ * // f.committedSource ends just before "component Counter() {…"
53
+ */
54
+ export declare function computeFrontier(source: string): FrontierResult;
55
+ /**
56
+ * Build a frontier result from an already-parsed program. Exposed so
57
+ * hosts that already call `parse(...)` once don't have to repeat the
58
+ * work.
59
+ */
60
+ export declare function buildFrontier(source: string, program: Program): FrontierResult;
61
+ /**
62
+ * SCC-3 helper: returns `true` when every name in `deps` lives in the
63
+ * committed prefix (so a side effect depending on them is safe to fire).
64
+ */
65
+ export declare function isQuiescent(frontier: FrontierResult, deps: ReadonlyArray<string>): boolean;
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export { tokenize } from './lexer.js';
3
+ export { parse } from './parser.js';
4
+ export { computeFrontier, buildFrontier, isQuiescent, type FrontierResult, } from './frontier.js';
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Tokenizer for Aktion.
3
+ *
4
+ * The language is line-oriented. We tokenize into a flat stream of tokens
5
+ * including NEWLINE markers so that the parser can recover at line boundaries.
6
+ */
7
+ export type TokenType = "Identifier" | "Keyword" | "StateIdentifier" | "BuiltinIdentifier" | "Number" | "String"
8
+ /**
9
+ * Backtick-quoted template literal — carries alternating raw chunks and
10
+ * embedded expression source strings via `parts`.
11
+ * `parts[0]` is always a string chunk; positions then alternate between
12
+ * `{kind:"expr", source}` and `{kind:"str", text}`.
13
+ */
14
+ | "TemplateString"
15
+ /**
16
+ * `js{ … }` opaque block. The body is captured verbatim (with balanced
17
+ * brace tracking) so it can be passed through to the runtime without
18
+ * re-parsing.
19
+ */
20
+ | "JsBlock" | "Boolean" | "Null" | "Punctuation" | "Operator" | "Newline" | "EOF";
21
+ /**
22
+ * Keywords reserved by Aktion. The lexer recognises them so the
23
+ * parser can dispatch on `Keyword` tokens directly instead of second-guessing
24
+ * every identifier. `$name` is lexed as a `StateIdentifier` token whose
25
+ * `value` is the bare name; there is only one reactive atom kind so no
26
+ * additional disambiguation is needed.
27
+ */
28
+ export declare const KEYWORDS_AKTION: Set<string>;
29
+ export type TemplatePart = {
30
+ kind: "str";
31
+ text: string;
32
+ } | {
33
+ kind: "expr";
34
+ source: string;
35
+ line: number;
36
+ column: number;
37
+ };
38
+ export interface Token {
39
+ type: TokenType;
40
+ value: string;
41
+ line: number;
42
+ column: number;
43
+ /** Set on `TemplateString` tokens to carry the alternating parts. */
44
+ parts?: TemplatePart[];
45
+ }
46
+ export declare function tokenize(source: string): Token[];
@@ -0,0 +1,2 @@
1
+ import { Program } from './types.js';
2
+ export declare function parse(source: string): Program;