gonia 0.0.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.
Files changed (54) hide show
  1. package/README.md +119 -0
  2. package/dist/client/hydrate.d.ts +54 -0
  3. package/dist/client/hydrate.js +445 -0
  4. package/dist/client/index.d.ts +7 -0
  5. package/dist/client/index.js +6 -0
  6. package/dist/context.d.ts +40 -0
  7. package/dist/context.js +69 -0
  8. package/dist/directives/class.d.ts +21 -0
  9. package/dist/directives/class.js +42 -0
  10. package/dist/directives/for.d.ts +29 -0
  11. package/dist/directives/for.js +265 -0
  12. package/dist/directives/html.d.ts +16 -0
  13. package/dist/directives/html.js +19 -0
  14. package/dist/directives/if.d.ts +25 -0
  15. package/dist/directives/if.js +133 -0
  16. package/dist/directives/index.d.ts +15 -0
  17. package/dist/directives/index.js +15 -0
  18. package/dist/directives/model.d.ts +27 -0
  19. package/dist/directives/model.js +134 -0
  20. package/dist/directives/on.d.ts +21 -0
  21. package/dist/directives/on.js +54 -0
  22. package/dist/directives/show.d.ts +15 -0
  23. package/dist/directives/show.js +19 -0
  24. package/dist/directives/slot.d.ts +48 -0
  25. package/dist/directives/slot.js +99 -0
  26. package/dist/directives/template.d.ts +55 -0
  27. package/dist/directives/template.js +147 -0
  28. package/dist/directives/text.d.ts +15 -0
  29. package/dist/directives/text.js +18 -0
  30. package/dist/expression.d.ts +60 -0
  31. package/dist/expression.js +96 -0
  32. package/dist/index.d.ts +19 -0
  33. package/dist/index.js +16 -0
  34. package/dist/inject.d.ts +42 -0
  35. package/dist/inject.js +63 -0
  36. package/dist/providers.d.ts +96 -0
  37. package/dist/providers.js +146 -0
  38. package/dist/reactivity.d.ts +95 -0
  39. package/dist/reactivity.js +219 -0
  40. package/dist/scope.d.ts +43 -0
  41. package/dist/scope.js +112 -0
  42. package/dist/server/index.d.ts +7 -0
  43. package/dist/server/index.js +6 -0
  44. package/dist/server/render.d.ts +61 -0
  45. package/dist/server/render.js +243 -0
  46. package/dist/templates.d.ts +92 -0
  47. package/dist/templates.js +124 -0
  48. package/dist/types.d.ts +362 -0
  49. package/dist/types.js +110 -0
  50. package/dist/vite/index.d.ts +6 -0
  51. package/dist/vite/index.js +6 -0
  52. package/dist/vite/plugin.d.ts +30 -0
  53. package/dist/vite/plugin.js +127 -0
  54. package/package.json +67 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Two-way binding directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { directive } from '../types.js';
7
+ import { effect } from '../reactivity.js';
8
+ /**
9
+ * Parse a property path and return getter/setter functions.
10
+ *
11
+ * @param path - Dot-separated property path (e.g., 'user.name')
12
+ * @param state - The state object to operate on
13
+ * @returns Object with get and set functions
14
+ */
15
+ function createAccessor(path, $eval, state) {
16
+ const parts = path.trim().split('.');
17
+ return {
18
+ get: () => $eval(path),
19
+ set: (value) => {
20
+ let target = state;
21
+ for (let i = 0; i < parts.length - 1; i++) {
22
+ const part = parts[i];
23
+ if (target[part] === undefined || target[part] === null) {
24
+ target[part] = {};
25
+ }
26
+ target = target[part];
27
+ }
28
+ target[parts[parts.length - 1]] = value;
29
+ }
30
+ };
31
+ }
32
+ /**
33
+ * Get the element type for determining binding behavior.
34
+ */
35
+ function getInputType(el) {
36
+ const tagName = el.tagName.toLowerCase();
37
+ if (tagName === 'select') {
38
+ return 'select';
39
+ }
40
+ if (tagName === 'textarea') {
41
+ return 'textarea';
42
+ }
43
+ if (tagName === 'input') {
44
+ const type = el.type?.toLowerCase() || 'text';
45
+ if (type === 'checkbox') {
46
+ return 'checkbox';
47
+ }
48
+ if (type === 'radio') {
49
+ return 'radio';
50
+ }
51
+ if (type === 'number' || type === 'range') {
52
+ return 'number';
53
+ }
54
+ }
55
+ return 'text';
56
+ }
57
+ /**
58
+ * Bind form element value to state with two-way data binding.
59
+ *
60
+ * @remarks
61
+ * Updates the element value when state changes, and updates state
62
+ * when the user modifies the element. Handles different input types:
63
+ * - text/textarea: binds to value, uses input event
64
+ * - checkbox: binds to checked, uses change event
65
+ * - radio: binds to checked, uses change event
66
+ * - select: binds to value, uses change event
67
+ * - number: binds to value (as number), uses input event
68
+ *
69
+ * @example
70
+ * ```html
71
+ * <input c-model="name">
72
+ * <input type="checkbox" c-model="isActive">
73
+ * <select c-model="selectedOption">
74
+ * <textarea c-model="description"></textarea>
75
+ * ```
76
+ */
77
+ export const model = function model($expr, $element, $eval, $rootState) {
78
+ const inputType = getInputType($element);
79
+ const accessor = createAccessor($expr, $eval, $rootState);
80
+ const el = $element;
81
+ if (inputType === 'checkbox') {
82
+ effect(() => {
83
+ el.checked = Boolean(accessor.get());
84
+ });
85
+ el.addEventListener('change', () => {
86
+ accessor.set(el.checked);
87
+ });
88
+ }
89
+ else if (inputType === 'radio') {
90
+ effect(() => {
91
+ const value = accessor.get();
92
+ el.checked = el.value === String(value);
93
+ });
94
+ el.addEventListener('change', () => {
95
+ if (el.checked) {
96
+ accessor.set(el.value);
97
+ }
98
+ });
99
+ }
100
+ else if (inputType === 'select') {
101
+ effect(() => {
102
+ el.value = String(accessor.get() ?? '');
103
+ });
104
+ el.addEventListener('change', () => {
105
+ accessor.set(el.value);
106
+ });
107
+ }
108
+ else if (inputType === 'number') {
109
+ effect(() => {
110
+ const value = accessor.get();
111
+ el.value = value === null || value === undefined ? '' : String(value);
112
+ });
113
+ el.addEventListener('input', () => {
114
+ const value = el.value;
115
+ if (value === '') {
116
+ accessor.set(null);
117
+ }
118
+ else {
119
+ const num = Number(value);
120
+ accessor.set(isNaN(num) ? value : num);
121
+ }
122
+ });
123
+ }
124
+ else {
125
+ effect(() => {
126
+ el.value = String(accessor.get() ?? '');
127
+ });
128
+ el.addEventListener('input', () => {
129
+ accessor.set(el.value);
130
+ });
131
+ }
132
+ };
133
+ model.$inject = ['$expr', '$element', '$eval', '$rootState'];
134
+ directive('c-model', model);
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Event handling directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Directive } from '../types.js';
7
+ /**
8
+ * Bind event handler to element.
9
+ *
10
+ * @remarks
11
+ * The handler receives the event object and can call preventDefault(),
12
+ * stopPropagation(), etc. as needed.
13
+ *
14
+ * @example
15
+ * ```html
16
+ * <button c-on="click: handleClick">Click me</button>
17
+ * <form c-on="submit: save">
18
+ * <input c-on="keydown: onKey">
19
+ * ```
20
+ */
21
+ export declare const on: Directive<['$expr', '$element', '$eval', '$rootState']>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Event handling directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { directive } from '../types.js';
7
+ /**
8
+ * Parse c-on expression: "event: handler" or "event: handler()"
9
+ */
10
+ function parseOnExpression(expr) {
11
+ const colonIdx = expr.indexOf(':');
12
+ if (colonIdx === -1) {
13
+ return null;
14
+ }
15
+ const event = expr.slice(0, colonIdx).trim();
16
+ const handler = expr.slice(colonIdx + 1).trim();
17
+ if (!event || !handler) {
18
+ return null;
19
+ }
20
+ return { event, handler };
21
+ }
22
+ /**
23
+ * Bind event handler to element.
24
+ *
25
+ * @remarks
26
+ * The handler receives the event object and can call preventDefault(),
27
+ * stopPropagation(), etc. as needed.
28
+ *
29
+ * @example
30
+ * ```html
31
+ * <button c-on="click: handleClick">Click me</button>
32
+ * <form c-on="submit: save">
33
+ * <input c-on="keydown: onKey">
34
+ * ```
35
+ */
36
+ export const on = function on($expr, $element, $eval, $rootState) {
37
+ const parsed = parseOnExpression($expr);
38
+ if (!parsed) {
39
+ console.error(`Invalid c-on expression: ${$expr}. Expected "event: handler"`);
40
+ return;
41
+ }
42
+ const { event: eventName, handler: handlerExpr } = parsed;
43
+ const handler = (event) => {
44
+ // Evaluate the expression. If it returns a function (e.g., "addTodo" instead
45
+ // of "addTodo()"), call it with the state as 'this' context and event as arg.
46
+ const result = $eval(handlerExpr);
47
+ if (typeof result === 'function') {
48
+ result.call($rootState, event);
49
+ }
50
+ };
51
+ $element.addEventListener(eventName, handler);
52
+ };
53
+ on.$inject = ['$expr', '$element', '$eval', '$rootState'];
54
+ directive('c-on', on);
@@ -0,0 +1,15 @@
1
+ import { Directive } from '../types.js';
2
+ /**
3
+ * Toggle element visibility based on an expression.
4
+ *
5
+ * @remarks
6
+ * Sets `display: none` when the expression is falsy,
7
+ * removes inline display style when truthy.
8
+ *
9
+ * @example
10
+ * ```html
11
+ * <div c-show="isVisible">Visible content</div>
12
+ * <div c-show="items.length > 0">Has items</div>
13
+ * ```
14
+ */
15
+ export declare const show: Directive<['$expr', '$element', '$eval']>;
@@ -0,0 +1,19 @@
1
+ import { directive } from '../types.js';
2
+ /**
3
+ * Toggle element visibility based on an expression.
4
+ *
5
+ * @remarks
6
+ * Sets `display: none` when the expression is falsy,
7
+ * removes inline display style when truthy.
8
+ *
9
+ * @example
10
+ * ```html
11
+ * <div c-show="isVisible">Visible content</div>
12
+ * <div c-show="items.length > 0">Has items</div>
13
+ * ```
14
+ */
15
+ export const show = function show($expr, $element, $eval) {
16
+ const value = $eval($expr);
17
+ $element.style.display = value ? '' : 'none';
18
+ };
19
+ directive('c-show', show);
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Slot directive for content transclusion.
3
+ *
4
+ * @remarks
5
+ * A slot is a placeholder in a template that receives content
6
+ * from the element using the template. Slots enable composition
7
+ * by allowing parent content to be projected into child templates.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import { Directive } from '../types.js';
12
+ /**
13
+ * Slot directive for content transclusion.
14
+ *
15
+ * @remarks
16
+ * Finds the nearest template ancestor and transcludes the
17
+ * matching slot content into itself.
18
+ *
19
+ * If the slot name is an expression, wraps in an effect
20
+ * for reactivity.
21
+ *
22
+ * @example
23
+ * Static slot name:
24
+ * ```html
25
+ * <slot name="header"></slot>
26
+ * ```
27
+ *
28
+ * Dynamic slot name:
29
+ * ```html
30
+ * <slot c-slot="activeTab"></slot>
31
+ * ```
32
+ *
33
+ * Default slot (no name):
34
+ * ```html
35
+ * <slot></slot>
36
+ * ```
37
+ */
38
+ export declare const slot: Directive<['$expr', '$element', '$eval']>;
39
+ /**
40
+ * Process native <slot> elements.
41
+ *
42
+ * @remarks
43
+ * This handles native `<slot>` elements in templates,
44
+ * treating them the same as `c-slot` directives.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function processNativeSlot(el: Element): void;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Slot directive for content transclusion.
3
+ *
4
+ * @remarks
5
+ * A slot is a placeholder in a template that receives content
6
+ * from the element using the template. Slots enable composition
7
+ * by allowing parent content to be projected into child templates.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import { directive } from '../types.js';
12
+ import { effect } from '../reactivity.js';
13
+ import { findTemplateAncestor, getSavedContent } from './template.js';
14
+ /**
15
+ * Slot directive for content transclusion.
16
+ *
17
+ * @remarks
18
+ * Finds the nearest template ancestor and transcludes the
19
+ * matching slot content into itself.
20
+ *
21
+ * If the slot name is an expression, wraps in an effect
22
+ * for reactivity.
23
+ *
24
+ * @example
25
+ * Static slot name:
26
+ * ```html
27
+ * <slot name="header"></slot>
28
+ * ```
29
+ *
30
+ * Dynamic slot name:
31
+ * ```html
32
+ * <slot c-slot="activeTab"></slot>
33
+ * ```
34
+ *
35
+ * Default slot (no name):
36
+ * ```html
37
+ * <slot></slot>
38
+ * ```
39
+ */
40
+ export const slot = function slot($expr, $element, $eval) {
41
+ // Determine slot name
42
+ // If expr is empty, check for name attribute, otherwise use 'default'
43
+ const getName = () => {
44
+ if ($expr && String($expr).trim()) {
45
+ // Dynamic slot name from expression
46
+ return String($eval($expr));
47
+ }
48
+ // Static slot name from attribute or default
49
+ return $element.getAttribute('name') ?? 'default';
50
+ };
51
+ const transclude = () => {
52
+ const name = getName();
53
+ const templateEl = findTemplateAncestor($element);
54
+ if (!templateEl) {
55
+ // No template ancestor - leave slot as-is or clear it
56
+ return;
57
+ }
58
+ const content = getSavedContent(templateEl);
59
+ const slotContent = content.slots.get(name);
60
+ if (slotContent) {
61
+ $element.innerHTML = slotContent;
62
+ // MutationObserver will process the new content
63
+ }
64
+ else {
65
+ // No content for this slot - could show fallback
66
+ // For now, leave any default content in the slot
67
+ }
68
+ };
69
+ // If expression is provided, make it reactive
70
+ if ($expr && String($expr).trim()) {
71
+ effect(transclude);
72
+ }
73
+ else {
74
+ // Static slot name - just run once
75
+ transclude();
76
+ }
77
+ };
78
+ directive('c-slot', slot);
79
+ /**
80
+ * Process native <slot> elements.
81
+ *
82
+ * @remarks
83
+ * This handles native `<slot>` elements in templates,
84
+ * treating them the same as `c-slot` directives.
85
+ *
86
+ * @internal
87
+ */
88
+ export function processNativeSlot(el) {
89
+ const name = el.getAttribute('name') ?? 'default';
90
+ const templateEl = findTemplateAncestor(el);
91
+ if (!templateEl) {
92
+ return;
93
+ }
94
+ const content = getSavedContent(templateEl);
95
+ const slotContent = content.slots.get(name);
96
+ if (slotContent) {
97
+ el.outerHTML = slotContent;
98
+ }
99
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Template directive for rendering reusable templates.
3
+ *
4
+ * @remarks
5
+ * Saves the element's children for slot transclusion,
6
+ * fetches and renders the template, and sets up context
7
+ * for nested slots to access the saved content.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import { Directive } from '../types.js';
12
+ import { EffectScope } from '../reactivity.js';
13
+ /**
14
+ * Saved slot content for an element.
15
+ *
16
+ * @internal
17
+ */
18
+ export interface SlotContent {
19
+ /** Content by slot name. 'default' for unnamed content. */
20
+ slots: Map<string, string>;
21
+ }
22
+ /**
23
+ * Get saved slot content for an element.
24
+ *
25
+ * @internal
26
+ */
27
+ export declare function getSavedContent(el: Element): SlotContent | undefined;
28
+ /**
29
+ * Find the nearest ancestor with saved content (the template element).
30
+ *
31
+ * @internal
32
+ */
33
+ export declare function findTemplateAncestor(el: Element): Element | null;
34
+ /**
35
+ * Template directive for rendering reusable templates.
36
+ *
37
+ * @remarks
38
+ * Fetches a template by name and replaces the element's content.
39
+ * Children are saved for slot transclusion before replacement.
40
+ *
41
+ * @example
42
+ * ```html
43
+ * <div c-template="dialog">
44
+ * <span slot="header">Title</span>
45
+ * <p>Body content</p>
46
+ * </div>
47
+ * ```
48
+ */
49
+ export declare const template: Directive<['$expr', '$element', '$templates']>;
50
+ /**
51
+ * Get the effect scope for an element.
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function getElementScope(el: Element): EffectScope | undefined;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Template directive for rendering reusable templates.
3
+ *
4
+ * @remarks
5
+ * Saves the element's children for slot transclusion,
6
+ * fetches and renders the template, and sets up context
7
+ * for nested slots to access the saved content.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import { directive, DirectivePriority } from '../types.js';
12
+ import { createEffectScope } from '../reactivity.js';
13
+ /** WeakMap storing saved children per element. */
14
+ const savedContent = new WeakMap();
15
+ /** WeakMap storing effect scopes per element for cleanup. */
16
+ const elementScopes = new WeakMap();
17
+ /** Set tracking which templates are currently rendering (cycle detection). */
18
+ const renderingChain = new WeakMap();
19
+ /**
20
+ * Get saved slot content for an element.
21
+ *
22
+ * @internal
23
+ */
24
+ export function getSavedContent(el) {
25
+ return savedContent.get(el);
26
+ }
27
+ /**
28
+ * Find the nearest ancestor with saved content (the template element).
29
+ *
30
+ * @internal
31
+ */
32
+ export function findTemplateAncestor(el) {
33
+ let current = el.parentElement;
34
+ while (current) {
35
+ if (savedContent.has(current)) {
36
+ return current;
37
+ }
38
+ current = current.parentElement;
39
+ }
40
+ return null;
41
+ }
42
+ /**
43
+ * Check if a node is an Element.
44
+ *
45
+ * @internal
46
+ */
47
+ function isElement(node) {
48
+ return node.nodeType === 1;
49
+ }
50
+ /**
51
+ * Extract slot content from an element's children.
52
+ *
53
+ * @internal
54
+ */
55
+ function extractSlotContent(el) {
56
+ const slots = new Map();
57
+ const defaultParts = [];
58
+ for (const child of Array.from(el.childNodes)) {
59
+ if (isElement(child) && child.hasAttribute('slot')) {
60
+ const slotName = child.getAttribute('slot');
61
+ const existing = slots.get(slotName) ?? '';
62
+ slots.set(slotName, existing + child.outerHTML);
63
+ }
64
+ else if (isElement(child)) {
65
+ defaultParts.push(child.outerHTML);
66
+ }
67
+ else if (child.nodeType === 3) { // TEXT_NODE
68
+ const text = child.textContent;
69
+ if (text.trim()) {
70
+ defaultParts.push(text);
71
+ }
72
+ }
73
+ }
74
+ if (defaultParts.length > 0) {
75
+ slots.set('default', defaultParts.join(''));
76
+ }
77
+ return slots;
78
+ }
79
+ /**
80
+ * Template directive for rendering reusable templates.
81
+ *
82
+ * @remarks
83
+ * Fetches a template by name and replaces the element's content.
84
+ * Children are saved for slot transclusion before replacement.
85
+ *
86
+ * @example
87
+ * ```html
88
+ * <div c-template="dialog">
89
+ * <span slot="header">Title</span>
90
+ * <p>Body content</p>
91
+ * </div>
92
+ * ```
93
+ */
94
+ export const template = async function template($expr, $element, $templates) {
95
+ const templateName = String($expr);
96
+ // Clean up previous render if re-rendering
97
+ const prevScope = elementScopes.get($element);
98
+ if (prevScope) {
99
+ prevScope.stop();
100
+ // Clear the element's own chain since we're starting fresh
101
+ renderingChain.delete($element);
102
+ }
103
+ // Cycle detection - only inherit from ancestors
104
+ let chain;
105
+ let parent = $element.parentElement;
106
+ while (parent) {
107
+ const parentChain = renderingChain.get(parent);
108
+ if (parentChain) {
109
+ chain = new Set(parentChain);
110
+ break;
111
+ }
112
+ parent = parent.parentElement;
113
+ }
114
+ chain = chain ?? new Set();
115
+ if (chain.has(templateName)) {
116
+ console.error(`Cycle detected: template "${templateName}" is already being rendered`);
117
+ return;
118
+ }
119
+ // Save children for slots
120
+ const slotContent = {
121
+ slots: extractSlotContent($element)
122
+ };
123
+ savedContent.set($element, slotContent);
124
+ // Track this template in the chain
125
+ const newChain = new Set(chain);
126
+ newChain.add(templateName);
127
+ renderingChain.set($element, newChain);
128
+ // Create effect scope for this element's descendants
129
+ const scope = createEffectScope();
130
+ elementScopes.set($element, scope);
131
+ // Fetch and render template
132
+ const html = await $templates.get(templateName);
133
+ $element.innerHTML = html;
134
+ // Note: MutationObserver will process the new children
135
+ };
136
+ template.$inject = ['$expr', '$element', '$templates'];
137
+ template.transclude = true;
138
+ template.priority = DirectivePriority.TEMPLATE;
139
+ directive('c-template', template);
140
+ /**
141
+ * Get the effect scope for an element.
142
+ *
143
+ * @internal
144
+ */
145
+ export function getElementScope(el) {
146
+ return elementScopes.get(el);
147
+ }
@@ -0,0 +1,15 @@
1
+ import { Directive } from '../types.js';
2
+ /**
3
+ * Set element's text content from an expression.
4
+ *
5
+ * @remarks
6
+ * Evaluates the expression and sets the element's `textContent`.
7
+ * Safe from XSS as it doesn't interpret HTML.
8
+ *
9
+ * @example
10
+ * ```html
11
+ * <span c-text="user.name"></span>
12
+ * <span c-text="'Hello, ' + user.name + '!'"></span>
13
+ * ```
14
+ */
15
+ export declare const text: Directive<['$expr', '$element', '$eval']>;
@@ -0,0 +1,18 @@
1
+ import { directive } from '../types.js';
2
+ /**
3
+ * Set element's text content from an expression.
4
+ *
5
+ * @remarks
6
+ * Evaluates the expression and sets the element's `textContent`.
7
+ * Safe from XSS as it doesn't interpret HTML.
8
+ *
9
+ * @example
10
+ * ```html
11
+ * <span c-text="user.name"></span>
12
+ * <span c-text="'Hello, ' + user.name + '!'"></span>
13
+ * ```
14
+ */
15
+ export const text = function text($expr, $element, $eval) {
16
+ $element.textContent = String($eval($expr) ?? '');
17
+ };
18
+ directive('c-text', text);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Expression parsing utilities.
3
+ *
4
+ * @remarks
5
+ * Parses expression strings (from HTML attributes) to find root identifiers.
6
+ * Used for reactive dependency tracking - only access state keys that
7
+ * the expression actually references.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /**
12
+ * Find root identifiers in an expression.
13
+ *
14
+ * @remarks
15
+ * These are the top-level variable references that need to come from state.
16
+ * Used to enable precise reactive dependency tracking.
17
+ *
18
+ * @param expr - The expression string to parse
19
+ * @returns Array of root identifier names
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * findRoots('user.name') // ['user']
24
+ * findRoots('user.name + item.count') // ['user', 'item']
25
+ * findRoots('"hello.world"') // []
26
+ * findRoots('items.filter(x => x.active)') // ['items', 'x']
27
+ * ```
28
+ */
29
+ export declare function findRoots(expr: string): string[];
30
+ /**
31
+ * A segment of parsed interpolation template.
32
+ */
33
+ export interface Segment {
34
+ /** Segment type: static text or expression */
35
+ type: 'static' | 'expr';
36
+ /** The segment value */
37
+ value: string;
38
+ }
39
+ /**
40
+ * Parse interpolation syntax in a string.
41
+ *
42
+ * @remarks
43
+ * Splits a template string with `{{ expr }}` markers into segments
44
+ * of static text and expressions. Used by directives like c-href
45
+ * that support inline interpolation.
46
+ *
47
+ * @param template - The template string with interpolation markers
48
+ * @returns Array of segments
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * parseInterpolation('/users/{{ user.id }}/profile')
53
+ * // [
54
+ * // { type: 'static', value: '/users/' },
55
+ * // { type: 'expr', value: 'user.id' },
56
+ * // { type: 'static', value: '/profile' }
57
+ * // ]
58
+ * ```
59
+ */
60
+ export declare function parseInterpolation(template: string): Segment[];