@hypen-space/core 0.3.11 → 0.4.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 (64) hide show
  1. package/dist/app.js +24 -3
  2. package/dist/app.js.map +3 -3
  3. package/dist/components/builtin.js +24 -3
  4. package/dist/components/builtin.js.map +3 -3
  5. package/dist/context.js +18 -1
  6. package/dist/context.js.map +2 -2
  7. package/dist/discovery.js +25 -7
  8. package/dist/discovery.js.map +4 -4
  9. package/dist/disposable.js +18 -1
  10. package/dist/disposable.js.map +2 -2
  11. package/dist/engine.browser.js +18 -1
  12. package/dist/engine.browser.js.map +3 -3
  13. package/dist/engine.js +18 -1
  14. package/dist/engine.js.map +3 -3
  15. package/dist/events.js +18 -1
  16. package/dist/events.js.map +2 -2
  17. package/dist/index.browser.js +259 -371
  18. package/dist/index.browser.js.map +8 -9
  19. package/dist/index.js +558 -647
  20. package/dist/index.js.map +12 -13
  21. package/dist/loader.js +18 -1
  22. package/dist/loader.js.map +2 -2
  23. package/dist/plugin.js +18 -1
  24. package/dist/plugin.js.map +2 -2
  25. package/dist/remote/client.js +18 -1
  26. package/dist/remote/client.js.map +3 -3
  27. package/dist/remote/index.js +353 -332
  28. package/dist/remote/index.js.map +8 -8
  29. package/dist/remote/server.js +234 -213
  30. package/dist/remote/server.js.map +7 -7
  31. package/dist/renderer.js +18 -1
  32. package/dist/renderer.js.map +3 -3
  33. package/dist/resolver.js +48 -4
  34. package/dist/resolver.js.map +3 -3
  35. package/dist/router.js +18 -1
  36. package/dist/router.js.map +2 -2
  37. package/dist/state.js +18 -1
  38. package/dist/state.js.map +2 -2
  39. package/package.json +7 -1
  40. package/src/app.ts +14 -2
  41. package/src/discovery.ts +3 -1
  42. package/src/engine.browser.ts +4 -32
  43. package/src/engine.ts +4 -32
  44. package/src/index.browser.ts +6 -4
  45. package/src/index.ts +6 -6
  46. package/src/managed-router.ts +195 -0
  47. package/src/module-registry.ts +76 -0
  48. package/src/remote/client.ts +1 -1
  49. package/src/remote/index.ts +2 -2
  50. package/src/remote/server.ts +1 -1
  51. package/src/remote/types.ts +1 -1
  52. package/src/renderer.ts +1 -1
  53. package/src/resolver.ts +56 -9
  54. package/src/types.ts +38 -0
  55. package/wasm-browser/README.md +7 -1
  56. package/wasm-browser/hypen_engine.d.ts +1 -0
  57. package/wasm-browser/hypen_engine.js +1 -0
  58. package/wasm-browser/hypen_engine_bg.wasm +0 -0
  59. package/wasm-browser/package.json +1 -1
  60. package/wasm-node/README.md +7 -1
  61. package/wasm-node/hypen_engine.d.ts +1 -0
  62. package/wasm-node/hypen_engine.js +1 -0
  63. package/wasm-node/hypen_engine_bg.wasm +0 -0
  64. package/wasm-node/package.json +1 -1
package/src/engine.ts CHANGED
@@ -6,6 +6,10 @@
6
6
  // WASM module types
7
7
  import { WasmEngine } from "../wasm-node/hypen_engine.js";
8
8
  import { frameworkLoggers } from "./logger.js";
9
+ import type { Patch, Action, RenderCallback, ActionHandler, ResolvedComponent, ComponentResolver } from "./types.js";
10
+
11
+ // Re-export types so existing consumers of "./engine.js" still work
12
+ export type { Patch, Action, RenderCallback, ActionHandler, ResolvedComponent, ComponentResolver } from "./types.js";
9
13
 
10
14
  const log = frameworkLoggers.engine;
11
15
 
@@ -34,38 +38,6 @@ function unwrapForWasm<T>(value: T): T {
34
38
  }
35
39
  }
36
40
 
37
- export type Patch = {
38
- type: "create" | "setProp" | "setText" | "insert" | "move" | "remove" | "attachEvent" | "detachEvent";
39
- id?: string;
40
- elementType?: string;
41
- props?: Record<string, any>;
42
- name?: string;
43
- value?: any;
44
- text?: string;
45
- parentId?: string;
46
- beforeId?: string;
47
- eventName?: string;
48
- };
49
-
50
- export type Action = {
51
- name: string;
52
- payload?: any;
53
- sender?: string;
54
- };
55
-
56
- export type RenderCallback = (patches: Patch[]) => void;
57
- export type ActionHandler = (action: Action) => void | Promise<void>;
58
-
59
- export type ResolvedComponent = {
60
- source: string;
61
- path: string;
62
- };
63
-
64
- export type ComponentResolver = (
65
- componentName: string,
66
- contextPath: string | null
67
- ) => ResolvedComponent | null;
68
-
69
41
  /**
70
42
  * Engine wraps the WASM engine and provides a TypeScript-friendly API
71
43
  */
@@ -5,10 +5,9 @@
5
5
  */
6
6
 
7
7
  // ============================================================================
8
- // ENGINE API (Browser version)
8
+ // CORE TYPES (WASM-free)
9
9
  // ============================================================================
10
10
 
11
- export { Engine } from "./engine.browser.js";
12
11
  export type {
13
12
  Patch,
14
13
  Action,
@@ -16,8 +15,11 @@ export type {
16
15
  ActionHandler as EngineActionHandler,
17
16
  ResolvedComponent,
18
17
  ComponentResolver,
19
- EngineInitOptions,
20
- } from "./engine.browser.js";
18
+ } from "./types.js";
19
+
20
+ // NOTE: Engine (BrowserEngine) is NOT exported from the barrel.
21
+ // Import it explicitly from its subpath to avoid pulling in WASM:
22
+ // import { Engine } from "@hypen-space/core/engine/browser"
21
23
 
22
24
  // ============================================================================
23
25
  // APP / MODULE SYSTEM
package/src/index.ts CHANGED
@@ -42,10 +42,9 @@
42
42
  */
43
43
 
44
44
  // ============================================================================
45
- // ENGINE API
45
+ // CORE TYPES (WASM-free)
46
46
  // ============================================================================
47
47
 
48
- export { Engine } from "./engine.js";
49
48
  export type {
50
49
  Patch,
51
50
  Action,
@@ -53,11 +52,12 @@ export type {
53
52
  ActionHandler as EngineActionHandler,
54
53
  ResolvedComponent,
55
54
  ComponentResolver as EngineComponentResolver,
56
- } from "./engine.js";
55
+ } from "./types.js";
57
56
 
58
- // Browser engine (explicit WASM init)
59
- export { Engine as BrowserEngine } from "./engine.browser.js";
60
- export type { EngineInitOptions } from "./engine.browser.js";
57
+ // NOTE: Engine and BrowserEngine are NOT exported from the barrel.
58
+ // Import them explicitly from their subpaths to avoid pulling in WASM:
59
+ // import { Engine } from "@hypen-space/core/engine"
60
+ // import { Engine } from "@hypen-space/core/engine/browser"
61
61
 
62
62
  // ============================================================================
63
63
  // APP / MODULE SYSTEM
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Managed Router — orchestrates module mount/unmount on route changes.
3
+ *
4
+ * When the router navigates to a route:
5
+ * 1. Unmounts the previous module (destroy instance, unregister from GlobalContext)
6
+ * 2. Mounts the new module (create instance with namespaced state, register)
7
+ *
8
+ * Module names are used as state prefixes (lowercased) for isolation.
9
+ */
10
+
11
+ import type { IEngine, HypenModuleDefinition } from "./app.js";
12
+ import { HypenModuleInstance } from "./app.js";
13
+ import type { HypenRouter, RouteState } from "./router.js";
14
+ import type { HypenGlobalContext } from "./context.js";
15
+ import { ModuleRegistry } from "./module-registry.js";
16
+ import { createLogger } from "./logger.js";
17
+
18
+ const log = createLogger("ManagedRouter");
19
+
20
+ export interface RouteDefinition {
21
+ /** Route path pattern (e.g., "/", "/profile/:id") */
22
+ path: string;
23
+ /** Component name — used to look up in ModuleRegistry */
24
+ component: string;
25
+ /** Inline module definition (alternative to registry lookup) */
26
+ module?: HypenModuleDefinition;
27
+ }
28
+
29
+ export class ManagedRouter {
30
+ private router: HypenRouter;
31
+ private engine: IEngine;
32
+ private registry: ModuleRegistry;
33
+ private globalContext: HypenGlobalContext;
34
+ private routes: RouteDefinition[] = [];
35
+ private activeModule: HypenModuleInstance | null = null;
36
+ private activeRoute: RouteDefinition | null = null;
37
+ private unsubscribe: (() => void) | null = null;
38
+ /** Cached instances for modules with persist=true */
39
+ private persistedModules = new Map<string, HypenModuleInstance>();
40
+
41
+ constructor(
42
+ router: HypenRouter,
43
+ engine: IEngine,
44
+ registry: ModuleRegistry,
45
+ globalContext: HypenGlobalContext
46
+ ) {
47
+ this.router = router;
48
+ this.engine = engine;
49
+ this.registry = registry;
50
+ this.globalContext = globalContext;
51
+ }
52
+
53
+ /**
54
+ * Add a route definition.
55
+ */
56
+ addRoute(route: RouteDefinition): this {
57
+ this.routes.push(route);
58
+ return this;
59
+ }
60
+
61
+ /**
62
+ * Start listening for route changes and mount the initial route.
63
+ */
64
+ start(): void {
65
+ this.unsubscribe = this.router.onNavigate((routeState: RouteState) => {
66
+ this.handleRouteChange(routeState);
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Stop listening and unmount the active module.
72
+ * Also destroys all persisted modules.
73
+ */
74
+ stop(): void {
75
+ if (this.unsubscribe) {
76
+ this.unsubscribe();
77
+ this.unsubscribe = null;
78
+ }
79
+ this.unmountActive();
80
+
81
+ // Destroy all persisted modules on full stop
82
+ for (const [moduleId, instance] of this.persistedModules) {
83
+ log.debug(`Destroying persisted module on stop: ${moduleId}`);
84
+ instance.destroy();
85
+ this.globalContext.unregisterModule(moduleId);
86
+ }
87
+ this.persistedModules.clear();
88
+ }
89
+
90
+ /**
91
+ * Get the currently active module instance.
92
+ */
93
+ getActiveModule(): HypenModuleInstance | null {
94
+ return this.activeModule;
95
+ }
96
+
97
+ /**
98
+ * Get the currently active route.
99
+ */
100
+ getActiveRoute(): RouteDefinition | null {
101
+ return this.activeRoute;
102
+ }
103
+
104
+ private handleRouteChange(routeState: RouteState): void {
105
+ const path = routeState.currentPath;
106
+ const matched = this.matchRoute(path);
107
+
108
+ if (!matched) {
109
+ log.debug(`No route matched for path: ${path}`);
110
+ this.unmountActive();
111
+ return;
112
+ }
113
+
114
+ // If same route, no need to remount
115
+ if (this.activeRoute && this.activeRoute.path === matched.path) {
116
+ return;
117
+ }
118
+
119
+ // Unmount old, mount new
120
+ this.unmountActive();
121
+ this.mount(matched);
122
+ }
123
+
124
+ private matchRoute(path: string): RouteDefinition | null {
125
+ for (const route of this.routes) {
126
+ if (this.router.matchPath(route.path, path) !== null) {
127
+ return route;
128
+ }
129
+ }
130
+ return null;
131
+ }
132
+
133
+ private mount(route: RouteDefinition): void {
134
+ // Look up module definition: inline first, then registry
135
+ const definition = route.module || this.registry.get(route.component);
136
+ if (!definition) {
137
+ log.debug(`No module definition found for component: ${route.component}`);
138
+ this.activeRoute = route;
139
+ return;
140
+ }
141
+
142
+ // Ensure the definition has a name for state namespacing
143
+ const namedDef = {
144
+ ...definition,
145
+ name: definition.name || route.component.toLowerCase(),
146
+ };
147
+
148
+ const moduleId = namedDef.name!.toLowerCase();
149
+
150
+ // Check for a persisted instance first
151
+ const persisted = this.persistedModules.get(moduleId);
152
+ if (persisted) {
153
+ log.debug(`Restoring persisted module: ${moduleId} for route: ${route.path}`);
154
+ this.activeModule = persisted;
155
+ this.activeRoute = route;
156
+ return;
157
+ }
158
+
159
+ log.debug(`Mounting module: ${namedDef.name} for route: ${route.path}`);
160
+
161
+ const instance = new HypenModuleInstance(
162
+ this.engine,
163
+ namedDef as HypenModuleDefinition<object>,
164
+ { root: this.router },
165
+ this.globalContext
166
+ );
167
+
168
+ // Register in global context under the module name (lowercase)
169
+ this.globalContext.registerModule(moduleId, instance);
170
+
171
+ this.activeModule = instance;
172
+ this.activeRoute = route;
173
+ }
174
+
175
+ private unmountActive(): void {
176
+ if (this.activeModule && this.activeRoute) {
177
+ const definition = this.activeRoute.module || this.registry.get(this.activeRoute.component);
178
+ const moduleId = (definition?.name || this.activeRoute.component).toLowerCase();
179
+ const persist = definition?.persist === true;
180
+
181
+ if (persist) {
182
+ log.debug(`Persisting module: ${moduleId} (persist=true)`);
183
+ this.persistedModules.set(moduleId, this.activeModule);
184
+ // Keep registered in GlobalContext so other modules can still access its state
185
+ } else {
186
+ log.debug(`Unmounting module: ${moduleId}`);
187
+ this.activeModule.destroy();
188
+ this.globalContext.unregisterModule(moduleId);
189
+ }
190
+ }
191
+
192
+ this.activeModule = null;
193
+ this.activeRoute = null;
194
+ }
195
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Module Registry — maps component names to module definitions.
3
+ *
4
+ * Used by the import resolution pipeline and ManagedRouter to look up
5
+ * module definitions by their component name (e.g., "HomePage", "ProfilePage").
6
+ */
7
+
8
+ import type { HypenModuleDefinition } from "./app.js";
9
+ import { createLogger } from "./logger.js";
10
+
11
+ const log = createLogger("ModuleRegistry");
12
+
13
+ export class ModuleRegistry {
14
+ private definitions = new Map<string, HypenModuleDefinition>();
15
+
16
+ /**
17
+ * Register a module definition under a component name.
18
+ */
19
+ register(name: string, definition: HypenModuleDefinition): void {
20
+ if (this.definitions.has(name)) {
21
+ log.warn(`Module "${name}" is already registered. Overwriting.`);
22
+ }
23
+ this.definitions.set(name, definition);
24
+ log.debug(`Registered module definition: ${name}`);
25
+ }
26
+
27
+ /**
28
+ * Get a module definition by component name.
29
+ */
30
+ get(name: string): HypenModuleDefinition | undefined {
31
+ return this.definitions.get(name);
32
+ }
33
+
34
+ /**
35
+ * Check if a module definition exists.
36
+ */
37
+ has(name: string): boolean {
38
+ return this.definitions.has(name);
39
+ }
40
+
41
+ /**
42
+ * Get all registered definitions.
43
+ */
44
+ getAll(): Map<string, HypenModuleDefinition> {
45
+ return new Map(this.definitions);
46
+ }
47
+
48
+ /**
49
+ * Get all registered component names.
50
+ */
51
+ getNames(): string[] {
52
+ return Array.from(this.definitions.keys());
53
+ }
54
+
55
+ /**
56
+ * Unregister a module definition.
57
+ */
58
+ unregister(name: string): void {
59
+ this.definitions.delete(name);
60
+ log.debug(`Unregistered module definition: ${name}`);
61
+ }
62
+
63
+ /**
64
+ * Number of registered definitions.
65
+ */
66
+ get size(): number {
67
+ return this.definitions.size;
68
+ }
69
+
70
+ /**
71
+ * Clear all registered definitions.
72
+ */
73
+ clear(): void {
74
+ this.definitions.clear();
75
+ }
76
+ }
@@ -36,7 +36,7 @@ import type {
36
36
  SessionAckMessage,
37
37
  SessionExpiredMessage,
38
38
  } from "./types.js";
39
- import type { Patch } from "../engine.js";
39
+ import type { Patch } from "../types.js";
40
40
  import {
41
41
  type Result,
42
42
  Ok,
@@ -37,5 +37,5 @@ export type {
37
37
  RemoteServerConfig,
38
38
  } from "./types.js";
39
39
 
40
- // Re-export Patch type from engine
41
- export type { Patch } from "../engine.js";
40
+ // Re-export Patch type
41
+ export type { Patch } from "../types.js";
@@ -44,7 +44,7 @@ import type {
44
44
  Session,
45
45
  SessionConfig,
46
46
  } from "./types.js";
47
- import type { Patch } from "../engine.js";
47
+ import type { Patch } from "../types.js";
48
48
  import type { ServerWebSocket } from "bun";
49
49
  import { SessionManager } from "./session.js";
50
50
  import { frameworkLoggers } from "../logger.js";
@@ -2,7 +2,7 @@
2
2
  * Remote UI Protocol Types
3
3
  */
4
4
 
5
- import type { Patch } from "../engine.js";
5
+ import type { Patch } from "../types.js";
6
6
 
7
7
  export type RemoteMessage =
8
8
  | InitialTreeMessage
package/src/renderer.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * (DOM, Canvas, Native, etc.)
6
6
  */
7
7
 
8
- import type { Patch } from "./engine.js";
8
+ import type { Patch } from "./types.js";
9
9
  import { frameworkLoggers } from "./logger.js";
10
10
 
11
11
  const log = frameworkLoggers.renderer;
package/src/resolver.ts CHANGED
@@ -39,6 +39,12 @@ export interface ResolverOptions {
39
39
  * Useful for adding authentication, custom headers, etc.
40
40
  */
41
41
  customFetch?: (url: string) => Promise<string>;
42
+
43
+ /**
44
+ * Module registry for looking up pre-registered module definitions.
45
+ * When set, the resolver will check the registry before doing file I/O.
46
+ */
47
+ moduleRegistry?: { has: (name: string) => boolean; get: (name: string) => any };
42
48
  }
43
49
 
44
50
  /**
@@ -47,22 +53,48 @@ export interface ResolverOptions {
47
53
  */
48
54
  export class ComponentResolver {
49
55
  private cache = new Map<string, ComponentDefinition>();
50
- private options: Required<ResolverOptions>;
56
+ private options: Required<Pick<ResolverOptions, 'baseDir' | 'cache' | 'customFetch'>> & Pick<ResolverOptions, 'moduleRegistry'>;
57
+
58
+ private moduleRegistry?: { has: (name: string) => boolean; get: (name: string) => any };
51
59
 
52
60
  constructor(options: ResolverOptions = {}) {
53
61
  this.options = {
54
62
  baseDir: options.baseDir || process.cwd(),
55
63
  cache: options.cache ?? true,
56
64
  customFetch: options.customFetch || this.defaultFetch.bind(this),
65
+ moduleRegistry: options.moduleRegistry,
57
66
  };
67
+ this.moduleRegistry = options.moduleRegistry;
58
68
  }
59
69
 
60
70
  /**
61
- * Resolve a component from an import statement
71
+ * Resolve a component from an import statement.
72
+ * Checks the module registry first (if available), then falls back to file I/O.
62
73
  */
63
74
  async resolve(
64
75
  importStmt: ImportStatement
65
76
  ): Promise<Record<string, ComponentDefinition>> {
77
+ // Check module registry first — if a component is pre-registered,
78
+ // use its template directly (it will be mounted as a full module)
79
+ if (this.moduleRegistry) {
80
+ const names = importStmt.clause.type === "named"
81
+ ? importStmt.clause.names
82
+ : [importStmt.clause.name];
83
+
84
+ const allFound = names.every(name => this.moduleRegistry!.has(name));
85
+ if (allFound) {
86
+ const result: Record<string, ComponentDefinition> = {};
87
+ for (const name of names) {
88
+ const def = this.moduleRegistry!.get(name);
89
+ result[name] = {
90
+ module: def,
91
+ template: def?.template || "",
92
+ };
93
+ }
94
+ return result;
95
+ }
96
+ }
97
+
66
98
  const sourcePath = this.getSourcePath(importStmt.source);
67
99
 
68
100
  // Check cache first
@@ -91,13 +123,28 @@ export class ComponentResolver {
91
123
  * Resolve a component from a local file path
92
124
  */
93
125
  private async resolveLocal(path: string): Promise<ComponentDefinition> {
94
- // For local paths, we expect the build system to have already
95
- // processed them into the generated components file
96
- // This is a fallback for dynamic resolution
97
- throw new Error(
98
- `Dynamic local component resolution not yet implemented: ${path}\n` +
99
- `Please use the build-components.ts script to generate component imports.`
100
- );
126
+ const { resolve, join } = await import("path");
127
+ const { readFile } = await import("fs/promises");
128
+
129
+ const basePath = resolve(this.options.baseDir, path);
130
+
131
+ // Try .hypen extension
132
+ const hypenPath = basePath.endsWith(".hypen")
133
+ ? basePath
134
+ : `${basePath}.hypen`;
135
+
136
+ const template = await readFile(hypenPath, "utf-8");
137
+
138
+ // Try loading sibling .ts module (stateless components have no module)
139
+ const modulePath = hypenPath.replace(/\.hypen$/, ".ts");
140
+ let module = {};
141
+ try {
142
+ module = await import(modulePath);
143
+ } catch {
144
+ // Stateless component — no module file
145
+ }
146
+
147
+ return { module, template };
101
148
  }
102
149
 
103
150
  /**
package/src/types.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Core types shared across the engine and runtime.
3
+ *
4
+ * These types are intentionally decoupled from the WASM engine so that
5
+ * packages can import them without pulling in any WASM code.
6
+ */
7
+
8
+ export type Patch = {
9
+ type: "create" | "setProp" | "setText" | "insert" | "move" | "remove" | "attachEvent" | "detachEvent";
10
+ id?: string;
11
+ elementType?: string;
12
+ props?: Record<string, any>;
13
+ name?: string;
14
+ value?: any;
15
+ text?: string;
16
+ parentId?: string;
17
+ beforeId?: string;
18
+ eventName?: string;
19
+ };
20
+
21
+ export type Action = {
22
+ name: string;
23
+ payload?: any;
24
+ sender?: string;
25
+ };
26
+
27
+ export type RenderCallback = (patches: Patch[]) => void;
28
+ export type ActionHandler = (action: Action) => void | Promise<void>;
29
+
30
+ export type ResolvedComponent = {
31
+ source: string;
32
+ path: string;
33
+ };
34
+
35
+ export type ComponentResolver = (
36
+ componentName: string,
37
+ contextPath: string | null
38
+ ) => ResolvedComponent | null;
@@ -583,6 +583,12 @@ pub use state::StateChange;
583
583
 
584
584
  ## Related Documentation
585
585
 
586
+ - [Documentation Index](./docs/README.md) - Full documentation index
587
+ - [Control Flow Components](./docs/control-flow.md) - ForEach, When/If, Map usage guide
588
+ - [Router](./docs/router.md) - Declarative routing and navigation
589
+ - [Architecture Internals](./docs/architecture.md) - Reactive system, reconciliation, IR
590
+ - [Advanced SDK Usage](./docs/advanced-sdk-usage.md) - Custom renderers, component resolvers, Remote UI
591
+ - [Glossary](./docs/glossary.md) - Key terms and binding syntax reference
592
+ - [Browser Compatibility](./docs/browser-compatibility.md) - Browser, platform, and WASM target support
586
593
  - [BUILD_WASM.md](./BUILD_WASM.md) - Detailed WASM build instructions
587
594
  - [../parser/README.md](../parser/README.md) - Hypen parser documentation
588
- - [../hypen-render-bun/README.md](../hypen-render-bun/README.md) - Bun renderer implementation
@@ -22,6 +22,7 @@ export class WasmEngine {
22
22
  constructor();
23
23
  /**
24
24
  * Parse and render Hypen DSL source code
25
+ * Supports documents with import statements - imports are resolved via the component resolver
25
26
  */
26
27
  renderSource(source: string): void;
27
28
  /**
@@ -282,6 +282,7 @@ export class WasmEngine {
282
282
  }
283
283
  /**
284
284
  * Parse and render Hypen DSL source code
285
+ * Supports documents with import statements - imports are resolved via the component resolver
285
286
  * @param {string} source
286
287
  */
287
288
  renderSource(source) {
Binary file
@@ -5,7 +5,7 @@
5
5
  "Hypen Contributors"
6
6
  ],
7
7
  "description": "A Rust implementation of the Hypen engine",
8
- "version": "0.3.3",
8
+ "version": "0.4.0",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
@@ -583,6 +583,12 @@ pub use state::StateChange;
583
583
 
584
584
  ## Related Documentation
585
585
 
586
+ - [Documentation Index](./docs/README.md) - Full documentation index
587
+ - [Control Flow Components](./docs/control-flow.md) - ForEach, When/If, Map usage guide
588
+ - [Router](./docs/router.md) - Declarative routing and navigation
589
+ - [Architecture Internals](./docs/architecture.md) - Reactive system, reconciliation, IR
590
+ - [Advanced SDK Usage](./docs/advanced-sdk-usage.md) - Custom renderers, component resolvers, Remote UI
591
+ - [Glossary](./docs/glossary.md) - Key terms and binding syntax reference
592
+ - [Browser Compatibility](./docs/browser-compatibility.md) - Browser, platform, and WASM target support
586
593
  - [BUILD_WASM.md](./BUILD_WASM.md) - Detailed WASM build instructions
587
594
  - [../parser/README.md](../parser/README.md) - Hypen parser documentation
588
- - [../hypen-render-bun/README.md](../hypen-render-bun/README.md) - Bun renderer implementation
@@ -22,6 +22,7 @@ export class WasmEngine {
22
22
  constructor();
23
23
  /**
24
24
  * Parse and render Hypen DSL source code
25
+ * Supports documents with import statements - imports are resolved via the component resolver
25
26
  */
26
27
  renderSource(source: string): void;
27
28
  /**
@@ -276,6 +276,7 @@ class WasmEngine {
276
276
  }
277
277
  /**
278
278
  * Parse and render Hypen DSL source code
279
+ * Supports documents with import statements - imports are resolved via the component resolver
279
280
  * @param {string} source
280
281
  */
281
282
  renderSource(source) {
Binary file
@@ -4,7 +4,7 @@
4
4
  "Hypen Contributors"
5
5
  ],
6
6
  "description": "A Rust implementation of the Hypen engine",
7
- "version": "0.3.3",
7
+ "version": "0.4.0",
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",