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
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Gonia
2
+
3
+ A lightweight, SSR-first reactive UI library for building web applications with HTML-based templates and declarative directives.
4
+
5
+ ## Features
6
+
7
+ - **SSR-First Architecture** - Server-side rendering with seamless client hydration
8
+ - **Declarative Directives** - Vue-inspired template syntax (`c-text`, `c-for`, `c-if`, etc.)
9
+ - **Fine-Grained Reactivity** - Efficient updates without virtual DOM diffing
10
+ - **Zero Dependencies** - Core library has no runtime dependencies (linkedom for SSR only)
11
+ - **TypeScript Native** - Full type safety with excellent IDE support
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add gonia
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### Server-Side Rendering
22
+
23
+ ```typescript
24
+ import { render, registerDirective } from 'gonia/server';
25
+ import { text, show, cfor, cif, cclass } from 'gonia';
26
+
27
+ // Create directive registry
28
+ const registry = new Map();
29
+ registerDirective(registry, 'text', text);
30
+ registerDirective(registry, 'show', show);
31
+ registerDirective(registry, 'for', cfor);
32
+ registerDirective(registry, 'if', cif);
33
+ registerDirective(registry, 'class', cclass);
34
+
35
+ // Render HTML with state
36
+ const html = await render(
37
+ '<ul><li c-for="item in items" c-text="item"></li></ul>',
38
+ { items: ['Apple', 'Banana', 'Cherry'] },
39
+ registry
40
+ );
41
+ ```
42
+
43
+ ### Client-Side Hydration
44
+
45
+ ```typescript
46
+ import { hydrate } from 'gonia/client';
47
+ import { directive } from 'gonia';
48
+
49
+ // Import directives (registers globally)
50
+ import './directives/my-app.js';
51
+
52
+ // Hydrate when DOM is ready
53
+ hydrate();
54
+ ```
55
+
56
+ ### Creating a Component Directive
57
+
58
+ ```typescript
59
+ import { directive, Directive } from 'gonia';
60
+
61
+ const myApp: Directive = ($element, $state) => {
62
+ // Initialize state
63
+ $state.count = 0;
64
+
65
+ // Define methods
66
+ $state.increment = () => {
67
+ $state.count++;
68
+ };
69
+ };
70
+
71
+ myApp.$inject = ['$element', '$state'];
72
+
73
+ // Register with scope: true to create isolated state
74
+ directive('my-app', myApp, { scope: true });
75
+ ```
76
+
77
+ ```html
78
+ <my-app>
79
+ <p c-text="count"></p>
80
+ <button c-on="click: increment">+1</button>
81
+ </my-app>
82
+ ```
83
+
84
+ ## Directives
85
+
86
+ | Directive | Description | Example |
87
+ |-----------|-------------|---------|
88
+ | `c-text` | Set text content | `<span c-text="message"></span>` |
89
+ | `c-show` | Toggle visibility | `<div c-show="isVisible">...</div>` |
90
+ | `c-if` | Conditional render | `<p c-if="hasError">Error!</p>` |
91
+ | `c-for` | Loop iteration | `<li c-for="item in items">...</li>` |
92
+ | `c-class` | Dynamic classes | `<div c-class="{ active: isActive }">` |
93
+ | `c-model` | Two-way binding | `<input c-model="name">` |
94
+ | `c-on` | Event handling | `<button c-on="click: handleClick">` |
95
+
96
+ ## Vite Integration
97
+
98
+ ```typescript
99
+ // vite.config.ts
100
+ import { defineConfig } from 'vite';
101
+ import { gonia } from 'gonia/vite';
102
+
103
+ export default defineConfig({
104
+ plugins: [gonia()]
105
+ });
106
+ ```
107
+
108
+ ## Documentation
109
+
110
+ See the [docs](./docs) folder for detailed documentation:
111
+
112
+ - [Getting Started](./docs/getting-started.md)
113
+ - [Directives Reference](./docs/directives.md)
114
+ - [SSR Guide](./docs/ssr.md)
115
+ - [Reactivity](./docs/reactivity.md)
116
+
117
+ ## License
118
+
119
+ MIT
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Client-side hydration and runtime directive binding.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Directive, Context } from '../types.js';
7
+ /**
8
+ * Registry of directives by name.
9
+ */
10
+ export type DirectiveRegistry = Map<string, Directive<any>>;
11
+ /**
12
+ * Service registry for dependency injection.
13
+ */
14
+ export type ServiceRegistry = Map<string, unknown>;
15
+ /**
16
+ * Set context for an element (used by directives that create child contexts).
17
+ *
18
+ * @internal
19
+ */
20
+ export declare function setElementContext(el: Element, ctx: Context): void;
21
+ /**
22
+ * Register a directive in the registry.
23
+ *
24
+ * @remarks
25
+ * If called after hydration, scans the DOM for any elements using
26
+ * this directive and processes them immediately.
27
+ *
28
+ * @param registry - The directive registry
29
+ * @param name - Directive name (without c- prefix)
30
+ * @param fn - The directive function
31
+ */
32
+ export declare function registerDirective(registry: DirectiveRegistry, name: string, fn: Directive): void;
33
+ /**
34
+ * Register a service for dependency injection.
35
+ *
36
+ * @param name - Service name (used in $inject arrays)
37
+ * @param service - The service instance
38
+ */
39
+ export declare function registerService(name: string, service: unknown): void;
40
+ export declare function init(registry?: DirectiveRegistry): Promise<void>;
41
+ /**
42
+ * Hydrate server-rendered HTML.
43
+ *
44
+ * @remarks
45
+ * Alias for {@link init}. Use when hydrating SSR output.
46
+ */
47
+ export declare const hydrate: typeof init;
48
+ /**
49
+ * Mount the framework on client-rendered HTML.
50
+ *
51
+ * @remarks
52
+ * Alias for {@link init}. Use for pure client-side rendering.
53
+ */
54
+ export declare const mount: typeof init;
@@ -0,0 +1,445 @@
1
+ /**
2
+ * Client-side hydration and runtime directive binding.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Mode, DirectivePriority, getDirective, getDirectiveNames } from '../types.js';
7
+ import { createContext } from '../context.js';
8
+ import { processNativeSlot } from '../directives/slot.js';
9
+ import { getLocalState, registerProvider, resolveFromProviders, registerDIProviders, resolveFromDIProviders } from '../providers.js';
10
+ import { FOR_PROCESSED_ATTR } from '../directives/for.js';
11
+ import { findParentScope, createElementScope, getElementScope } from '../scope.js';
12
+ import { getInjectables } from '../inject.js';
13
+ // Built-in directives
14
+ import { text } from '../directives/text.js';
15
+ import { show } from '../directives/show.js';
16
+ import { cclass } from '../directives/class.js';
17
+ import { model } from '../directives/model.js';
18
+ import { on } from '../directives/on.js';
19
+ import { cfor } from '../directives/for.js';
20
+ import { cif } from '../directives/if.js';
21
+ /** Cached selector string */
22
+ let cachedSelector = null;
23
+ /** Whether init() has been called */
24
+ let initialized = false;
25
+ /** Registered services */
26
+ let services = new Map();
27
+ /** Context cache by element */
28
+ const contextCache = new WeakMap();
29
+ /** Default registry with built-in directives */
30
+ let defaultRegistry = null;
31
+ /**
32
+ * Get the default directive registry with built-in directives.
33
+ *
34
+ * @internal
35
+ */
36
+ function getDefaultRegistry() {
37
+ if (!defaultRegistry) {
38
+ defaultRegistry = new Map();
39
+ defaultRegistry.set('text', text);
40
+ defaultRegistry.set('show', show);
41
+ defaultRegistry.set('class', cclass);
42
+ defaultRegistry.set('model', model);
43
+ defaultRegistry.set('on', on);
44
+ defaultRegistry.set('for', cfor);
45
+ defaultRegistry.set('if', cif);
46
+ }
47
+ return defaultRegistry;
48
+ }
49
+ /**
50
+ * Build a CSS selector for all registered directives.
51
+ *
52
+ * @internal
53
+ */
54
+ function getSelector(registry) {
55
+ if (!cachedSelector) {
56
+ const directiveSelectors = [];
57
+ for (const name of registry.keys()) {
58
+ directiveSelectors.push(`[c-${name}]`);
59
+ }
60
+ // Also match native <slot> elements
61
+ directiveSelectors.push('slot');
62
+ cachedSelector = directiveSelectors.join(',');
63
+ }
64
+ return cachedSelector;
65
+ }
66
+ /**
67
+ * Get directives for an element, sorted by priority (highest first).
68
+ *
69
+ * @internal
70
+ */
71
+ function getDirectivesForElement(el, registry) {
72
+ const directives = [];
73
+ for (const [name, directive] of registry) {
74
+ const attr = el.getAttribute(`c-${name}`);
75
+ if (attr !== null) {
76
+ directives.push({ name, directive, expr: attr });
77
+ }
78
+ }
79
+ // Sort by priority (higher first)
80
+ directives.sort((a, b) => {
81
+ const priorityA = a.directive.priority ?? DirectivePriority.NORMAL;
82
+ const priorityB = b.directive.priority ?? DirectivePriority.NORMAL;
83
+ return priorityB - priorityA;
84
+ });
85
+ return directives;
86
+ }
87
+ /**
88
+ * Get or create context for an element.
89
+ *
90
+ * @remarks
91
+ * Walks up the DOM to find the nearest ancestor with a cached context,
92
+ * then creates a context using the nearest scope.
93
+ *
94
+ * @internal
95
+ */
96
+ function getContextForElement(el) {
97
+ // Check cache first
98
+ const cached = contextCache.get(el);
99
+ if (cached)
100
+ return cached;
101
+ // Walk up to find nearest context
102
+ let current = el.parentElement;
103
+ while (current) {
104
+ const parentCtx = contextCache.get(current);
105
+ if (parentCtx) {
106
+ const ctx = parentCtx.child({});
107
+ contextCache.set(el, ctx);
108
+ return ctx;
109
+ }
110
+ current = current.parentElement;
111
+ }
112
+ // Find nearest scope or create empty one
113
+ const scope = findParentScope(el, true) ?? createElementScope(el);
114
+ const ctx = createContext(Mode.CLIENT, scope);
115
+ contextCache.set(el, ctx);
116
+ return ctx;
117
+ }
118
+ /**
119
+ * Set context for an element (used by directives that create child contexts).
120
+ *
121
+ * @internal
122
+ */
123
+ export function setElementContext(el, ctx) {
124
+ contextCache.set(el, ctx);
125
+ }
126
+ /**
127
+ * Resolve dependencies for a directive based on its $inject array.
128
+ *
129
+ * @internal
130
+ */
131
+ function resolveDependencies(directive, expr, el, ctx) {
132
+ const inject = getInjectables(directive);
133
+ return inject.map(name => {
134
+ switch (name) {
135
+ case '$expr':
136
+ return expr;
137
+ case '$element':
138
+ return el;
139
+ case '$eval':
140
+ return ctx.eval.bind(ctx);
141
+ case '$state':
142
+ // Find nearest ancestor scope (including self)
143
+ return findParentScope(el, true) ?? getLocalState(el);
144
+ case '$rootState':
145
+ // Deprecated: same as $state now (scoped state)
146
+ return findParentScope(el, true) ?? getLocalState(el);
147
+ case '$mode':
148
+ return Mode.CLIENT;
149
+ default: {
150
+ // Look up in ancestor DI providers first (provide option)
151
+ const diProvided = resolveFromDIProviders(el, name);
152
+ if (diProvided !== undefined) {
153
+ return diProvided;
154
+ }
155
+ // Look up in global services registry
156
+ const service = services.get(name);
157
+ if (service !== undefined) {
158
+ return service;
159
+ }
160
+ // Look up in ancestor context providers ($context)
161
+ const contextProvided = resolveFromProviders(el, name);
162
+ if (contextProvided !== undefined) {
163
+ return contextProvided;
164
+ }
165
+ throw new Error(`Unknown injectable: ${name}`);
166
+ }
167
+ }
168
+ });
169
+ }
170
+ /**
171
+ * Process directives on a single element.
172
+ * Returns a promise if any directive is async, otherwise void.
173
+ * Directives on the same element are processed sequentially to handle dependencies.
174
+ *
175
+ * @internal
176
+ */
177
+ function processElement(el, registry) {
178
+ // Skip elements already processed by c-for (they have their own child scope)
179
+ if (el.hasAttribute(FOR_PROCESSED_ATTR)) {
180
+ return;
181
+ }
182
+ // Handle native <slot> elements
183
+ if (el.tagName === 'SLOT') {
184
+ processNativeSlot(el);
185
+ return;
186
+ }
187
+ const directives = getDirectivesForElement(el, registry);
188
+ if (directives.length === 0)
189
+ return;
190
+ const ctx = getContextForElement(el);
191
+ // Process directives sequentially, handling async ones properly
192
+ let chain;
193
+ for (const { directive, expr } of directives) {
194
+ const processDirective = () => {
195
+ const args = resolveDependencies(directive, expr, el, ctx);
196
+ const result = directive(...args);
197
+ // Register as provider if directive declares $context
198
+ if (directive.$context?.length) {
199
+ const state = getLocalState(el);
200
+ registerProvider(el, directive, state);
201
+ }
202
+ return result;
203
+ };
204
+ // STRUCTURAL directives (like c-for) take ownership of the element.
205
+ // They remove the original and handle other directives on clones themselves.
206
+ const isStructural = directive.priority === DirectivePriority.STRUCTURAL;
207
+ if (chain instanceof Promise) {
208
+ // Previous directive was async, chain this one after it
209
+ chain = chain.then(() => {
210
+ const result = processDirective();
211
+ return result instanceof Promise ? result : undefined;
212
+ });
213
+ }
214
+ else {
215
+ // Previous directive was sync (or this is the first)
216
+ const result = processDirective();
217
+ if (result instanceof Promise) {
218
+ chain = result;
219
+ }
220
+ }
221
+ if (isStructural) {
222
+ break;
223
+ }
224
+ }
225
+ return chain;
226
+ }
227
+ /**
228
+ * Process a node and its descendants for directives.
229
+ * Returns a promise that resolves when all async directives complete.
230
+ *
231
+ * @internal
232
+ */
233
+ function processNode(node, selector, registry) {
234
+ const matches = node.matches?.(selector) ? [node] : [];
235
+ const descendants = [...(node.querySelectorAll?.(selector) ?? [])];
236
+ const promises = [];
237
+ for (const el of [...matches, ...descendants]) {
238
+ const result = processElement(el, registry);
239
+ if (result instanceof Promise) {
240
+ promises.push(result);
241
+ }
242
+ }
243
+ if (promises.length > 0) {
244
+ return Promise.all(promises).then(() => { });
245
+ }
246
+ }
247
+ /**
248
+ * Register a directive in the registry.
249
+ *
250
+ * @remarks
251
+ * If called after hydration, scans the DOM for any elements using
252
+ * this directive and processes them immediately.
253
+ *
254
+ * @param registry - The directive registry
255
+ * @param name - Directive name (without c- prefix)
256
+ * @param fn - The directive function
257
+ */
258
+ export function registerDirective(registry, name, fn) {
259
+ registry.set(name, fn);
260
+ cachedSelector = null;
261
+ if (document.body && initialized) {
262
+ const selector = `[c-${name}]`;
263
+ for (const el of document.querySelectorAll(selector)) {
264
+ processElement(el, registry);
265
+ }
266
+ }
267
+ }
268
+ /**
269
+ * Register a service for dependency injection.
270
+ *
271
+ * @param name - Service name (used in $inject arrays)
272
+ * @param service - The service instance
273
+ */
274
+ export function registerService(name, service) {
275
+ services.set(name, service);
276
+ }
277
+ /**
278
+ * Initialize the client-side framework.
279
+ *
280
+ * @remarks
281
+ * Processes all existing elements with directive attributes, then
282
+ * sets up a MutationObserver to handle dynamically added elements.
283
+ * Works for both SSR hydration and pure client-side rendering.
284
+ *
285
+ * State is now scoped per custom element. Each element with `scope: true`
286
+ * creates its own state that child elements inherit via prototype chain.
287
+ *
288
+ * @param registry - The directive registry
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * const registry = new Map();
293
+ * registry.set('text', textDirective);
294
+ *
295
+ * init(registry);
296
+ * ```
297
+ */
298
+ /**
299
+ * Extract template attributes from an element.
300
+ *
301
+ * @internal
302
+ */
303
+ function getTemplateAttrs(el) {
304
+ const attrs = {
305
+ children: el.innerHTML
306
+ };
307
+ for (const attr of el.attributes) {
308
+ attrs[attr.name] = attr.value;
309
+ }
310
+ return attrs;
311
+ }
312
+ /**
313
+ * Process custom element directives (those with templates).
314
+ * Directives with templates are web components and must be processed before
315
+ * attribute directives so their content is rendered first.
316
+ *
317
+ * Order for each element:
318
+ * 1. Create scope (if scope: true)
319
+ * 2. Call directive function (if fn exists) - initializes state
320
+ * 3. Render template with (props, state) - can use initialized state
321
+ * 4. Child directives are processed later by main hydration
322
+ *
323
+ * @internal
324
+ */
325
+ async function processDirectiveElements() {
326
+ for (const name of getDirectiveNames()) {
327
+ const registration = getDirective(name);
328
+ if (!registration) {
329
+ continue;
330
+ }
331
+ const { fn, options } = registration;
332
+ // Only process directives with templates (web components),
333
+ // scope: true, or provide (DI overrides)
334
+ if (!options.template && !options.scope && !options.provide) {
335
+ continue;
336
+ }
337
+ // Find all elements matching this directive's tag name
338
+ const elements = document.querySelectorAll(name);
339
+ for (const el of elements) {
340
+ // Skip if already processed
341
+ if (getElementScope(el)) {
342
+ continue;
343
+ }
344
+ // 1. Create scope if needed
345
+ let scope = {};
346
+ if (options.scope) {
347
+ const parentScope = findParentScope(el);
348
+ scope = createElementScope(el, parentScope);
349
+ }
350
+ else {
351
+ scope = findParentScope(el, true) ?? {};
352
+ }
353
+ // 2. Register DI providers if present (for descendants)
354
+ if (options.provide) {
355
+ registerDIProviders(el, options.provide);
356
+ }
357
+ // 3. Call directive function if present (initializes state)
358
+ if (fn) {
359
+ const ctx = createContext(Mode.CLIENT, scope);
360
+ const inject = getInjectables(fn);
361
+ const args = inject.map((dep) => {
362
+ switch (dep) {
363
+ case '$element':
364
+ return el;
365
+ case '$state':
366
+ return scope;
367
+ case '$eval':
368
+ return ctx.eval.bind(ctx);
369
+ default:
370
+ return undefined;
371
+ }
372
+ });
373
+ const result = fn(...args);
374
+ if (result instanceof Promise) {
375
+ await result;
376
+ }
377
+ }
378
+ // 4. Render template if present (can query DOM for <template> elements etc)
379
+ if (options.template) {
380
+ const attrs = getTemplateAttrs(el);
381
+ let html;
382
+ if (typeof options.template === 'string') {
383
+ html = options.template;
384
+ }
385
+ else {
386
+ const result = options.template(attrs, el);
387
+ html = result instanceof Promise ? await result : result;
388
+ }
389
+ el.innerHTML = html;
390
+ }
391
+ }
392
+ }
393
+ }
394
+ export async function init(registry) {
395
+ const reg = registry ?? getDefaultRegistry();
396
+ cachedSelector = null;
397
+ // Process custom element directives first (those with templates or scope)
398
+ // This ensures templates are rendered and scopes exist before child directives run
399
+ await processDirectiveElements();
400
+ const selector = getSelector(reg);
401
+ // Process existing elements synchronously, collecting promises from async directives.
402
+ // Note: We collect elements first, but some may be removed during processing
403
+ // (e.g., c-for removes its template). Check isConnected before processing.
404
+ const elements = document.querySelectorAll(selector);
405
+ const promises = [];
406
+ for (const el of elements) {
407
+ if (!el.isConnected) {
408
+ continue;
409
+ }
410
+ const result = processElement(el, reg);
411
+ if (result instanceof Promise) {
412
+ promises.push(result);
413
+ }
414
+ }
415
+ // Wait for any async directives to complete
416
+ if (promises.length > 0) {
417
+ await Promise.all(promises);
418
+ }
419
+ // Set up MutationObserver for dynamic elements
420
+ const observer = new MutationObserver((mutations) => {
421
+ for (const mutation of mutations) {
422
+ for (const node of mutation.addedNodes) {
423
+ if (node.nodeType !== Node.ELEMENT_NODE)
424
+ continue;
425
+ processNode(node, selector, reg);
426
+ }
427
+ }
428
+ });
429
+ observer.observe(document.body, { childList: true, subtree: true });
430
+ initialized = true;
431
+ }
432
+ /**
433
+ * Hydrate server-rendered HTML.
434
+ *
435
+ * @remarks
436
+ * Alias for {@link init}. Use when hydrating SSR output.
437
+ */
438
+ export const hydrate = init;
439
+ /**
440
+ * Mount the framework on client-rendered HTML.
441
+ *
442
+ * @remarks
443
+ * Alias for {@link init}. Use for pure client-side rendering.
444
+ */
445
+ export const mount = init;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Client-side hydration and mounting utilities.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { init, hydrate, mount, registerDirective, setElementContext } from './hydrate.js';
7
+ export type { DirectiveRegistry } from './hydrate.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Client-side hydration and mounting utilities.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { init, hydrate, mount, registerDirective, setElementContext } from './hydrate.js';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Context creation and management.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Mode, Context } from './types.js';
7
+ export type { Context };
8
+ /**
9
+ * Create an evaluation context for directives.
10
+ *
11
+ * @remarks
12
+ * Uses {@link findRoots} to only access state keys that the expression
13
+ * actually references, enabling precise dependency tracking with the
14
+ * reactive system.
15
+ *
16
+ * Supports scoped values via `get()` for things like $component, $renderingChain.
17
+ * Create child contexts with `child()` for nested scopes.
18
+ *
19
+ * @param mode - Execution mode (server or client)
20
+ * @param state - The reactive state object
21
+ * @param scope - Optional scoped values (for context-specific data)
22
+ * @returns A new context
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const state = reactive({ user: { name: 'Alice' } });
27
+ * const ctx = createContext(Mode.CLIENT, state);
28
+ * ctx.eval('user.name' as Expression); // 'Alice'
29
+ *
30
+ * const childCtx = ctx.child({ $component: el });
31
+ * childCtx.get('$component'); // el
32
+ * ```
33
+ */
34
+ export declare function createContext(mode: Mode, state: Record<string, unknown>, scope?: Record<string, unknown>): Context;
35
+ /**
36
+ * Create a child context with additional bindings.
37
+ *
38
+ * @deprecated Use ctx.child() instead
39
+ */
40
+ export declare function createChildContext(parent: Context, additions: Record<string, unknown>): Context;