@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/dist/index.js CHANGED
@@ -1,4 +1,20 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
1
4
  var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
2
18
  var __export = (target, all) => {
3
19
  for (var name in all)
4
20
  __defProp(target, name, {
@@ -9,205 +25,7 @@ var __export = (target, all) => {
9
25
  });
10
26
  };
11
27
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
12
-
13
- // src/logger.ts
14
- function isProduction() {
15
- if (typeof process !== "undefined" && process.env) {
16
- return false;
17
- }
18
- return false;
19
- }
20
- function setLogLevel(level) {
21
- config.level = level;
22
- }
23
- function getLogLevel() {
24
- return config.level;
25
- }
26
- function configureLogger(options) {
27
- config = { ...config, ...options };
28
- }
29
- function enableLogging() {
30
- config.level = "debug";
31
- }
32
- function disableLogging() {
33
- config.level = "none";
34
- }
35
- function setDebugMode(enabled) {
36
- config.level = enabled ? "debug" : "error";
37
- }
38
- function isDebugMode() {
39
- return config.level === "debug";
40
- }
41
- function shouldLog(level) {
42
- return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
43
- }
44
- function formatTag(tag, level) {
45
- const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
46
- if (config.colors && level !== "none") {
47
- const color = LOG_LEVEL_COLORS[level];
48
- return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
49
- }
50
- return `${timestamp}[${tag}]`;
51
- }
52
-
53
- class Logger {
54
- tag;
55
- constructor(tag) {
56
- this.tag = tag;
57
- }
58
- debug(...args) {
59
- if (!shouldLog("debug"))
60
- return;
61
- if (config.handler) {
62
- config.handler.debug(this.tag, ...args);
63
- } else {
64
- console.log(formatTag(this.tag, "debug"), ...args);
65
- }
66
- }
67
- info(...args) {
68
- if (!shouldLog("info"))
69
- return;
70
- if (config.handler) {
71
- config.handler.info(this.tag, ...args);
72
- } else {
73
- console.info(formatTag(this.tag, "info"), ...args);
74
- }
75
- }
76
- warn(...args) {
77
- if (!shouldLog("warn"))
78
- return;
79
- if (config.handler) {
80
- config.handler.warn(this.tag, ...args);
81
- } else {
82
- console.warn(formatTag(this.tag, "warn"), ...args);
83
- }
84
- }
85
- error(...args) {
86
- if (!shouldLog("error"))
87
- return;
88
- if (config.handler) {
89
- config.handler.error(this.tag, ...args);
90
- } else {
91
- console.error(formatTag(this.tag, "error"), ...args);
92
- }
93
- }
94
- time(label, fn) {
95
- if (!shouldLog("debug")) {
96
- return fn();
97
- }
98
- const start = performance.now();
99
- try {
100
- return fn();
101
- } finally {
102
- const duration = performance.now() - start;
103
- this.debug(`${label}: ${duration.toFixed(2)}ms`);
104
- }
105
- }
106
- async timeAsync(label, fn) {
107
- if (!shouldLog("debug")) {
108
- return fn();
109
- }
110
- const start = performance.now();
111
- try {
112
- return await fn();
113
- } finally {
114
- const duration = performance.now() - start;
115
- this.debug(`${label}: ${duration.toFixed(2)}ms`);
116
- }
117
- }
118
- child(subTag) {
119
- return new Logger(`${this.tag}:${subTag}`);
120
- }
121
- debugIf(condition, ...args) {
122
- if (condition)
123
- this.debug(...args);
124
- }
125
- warnIf(condition, ...args) {
126
- if (condition)
127
- this.warn(...args);
128
- }
129
- errorIf(condition, ...args) {
130
- if (condition)
131
- this.error(...args);
132
- }
133
- loggedOnce = new Set;
134
- warnOnce(key, ...args) {
135
- if (this.loggedOnce.has(key))
136
- return;
137
- this.loggedOnce.add(key);
138
- this.warn(...args);
139
- }
140
- debugOnce(key, ...args) {
141
- if (this.loggedOnce.has(key))
142
- return;
143
- this.loggedOnce.add(key);
144
- this.debug(...args);
145
- }
146
- }
147
- function createLogger(tag) {
148
- return new Logger(tag);
149
- }
150
- var LOG_LEVEL_ORDER, LOG_LEVEL_COLORS, RESET_COLOR = "\x1B[0m", config, logger, log, frameworkLoggers;
151
- var init_logger = __esm(() => {
152
- LOG_LEVEL_ORDER = {
153
- debug: 0,
154
- info: 1,
155
- warn: 2,
156
- error: 3,
157
- none: 4
158
- };
159
- LOG_LEVEL_COLORS = {
160
- debug: "\x1B[36m",
161
- info: "\x1B[32m",
162
- warn: "\x1B[33m",
163
- error: "\x1B[31m"
164
- };
165
- config = {
166
- level: isProduction() ? "error" : "debug",
167
- colors: true,
168
- timestamps: false
169
- };
170
- logger = createLogger("Hypen");
171
- log = {
172
- debug: (tag, ...args) => {
173
- if (!shouldLog("debug"))
174
- return;
175
- console.log(formatTag(tag, "debug"), ...args);
176
- },
177
- info: (tag, ...args) => {
178
- if (!shouldLog("info"))
179
- return;
180
- console.info(formatTag(tag, "info"), ...args);
181
- },
182
- warn: (tag, ...args) => {
183
- if (!shouldLog("warn"))
184
- return;
185
- console.warn(formatTag(tag, "warn"), ...args);
186
- },
187
- error: (tag, ...args) => {
188
- if (!shouldLog("error"))
189
- return;
190
- console.error(formatTag(tag, "error"), ...args);
191
- }
192
- };
193
- frameworkLoggers = {
194
- hypen: createLogger("Hypen"),
195
- engine: createLogger("Engine"),
196
- router: createLogger("Router"),
197
- state: createLogger("State"),
198
- events: createLogger("Events"),
199
- remote: createLogger("Remote"),
200
- renderer: createLogger("Renderer"),
201
- module: createLogger("Module"),
202
- lifecycle: createLogger("Lifecycle"),
203
- loader: createLogger("Loader"),
204
- context: createLogger("Context"),
205
- discovery: createLogger("Discovery"),
206
- plugin: createLogger("Plugin"),
207
- canvas: createLogger("Canvas"),
208
- debug: createLogger("Debug")
209
- };
210
- });
28
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
211
29
 
212
30
  // src/state.ts
213
31
  function deepClone(obj) {
@@ -595,57 +413,256 @@ var init_result = __esm(() => {
595
413
  };
596
414
  });
597
415
 
598
- // src/app.ts
599
- var exports_app = {};
600
- __export(exports_app, {
601
- app: () => app,
602
- HypenModuleInstance: () => HypenModuleInstance,
603
- HypenAppBuilder: () => HypenAppBuilder,
604
- HypenApp: () => HypenApp
605
- });
606
-
607
- class HypenAppBuilder {
608
- initialState;
609
- options;
610
- createdHandler;
611
- actionHandlers = new Map;
612
- destroyedHandler;
613
- disconnectHandler;
614
- reconnectHandler;
615
- expireHandler;
616
- errorHandler;
617
- template;
618
- constructor(initialState, options) {
619
- this.initialState = initialState;
620
- this.options = options || {};
621
- }
622
- onCreated(fn) {
623
- this.createdHandler = fn;
624
- return this;
625
- }
626
- onAction(name, fn) {
627
- this.actionHandlers.set(name, fn);
628
- return this;
629
- }
630
- onDestroyed(fn) {
631
- this.destroyedHandler = fn;
632
- return this;
633
- }
634
- onDisconnect(fn) {
635
- this.disconnectHandler = fn;
636
- return this;
637
- }
638
- onReconnect(fn) {
639
- this.reconnectHandler = fn;
640
- return this;
641
- }
642
- onExpire(fn) {
643
- this.expireHandler = fn;
644
- return this;
416
+ // src/logger.ts
417
+ function isProduction() {
418
+ if (typeof process !== "undefined" && process.env) {
419
+ return false;
645
420
  }
646
- onError(fn) {
647
- this.errorHandler = fn;
648
- return this;
421
+ return false;
422
+ }
423
+ function setLogLevel(level) {
424
+ config.level = level;
425
+ }
426
+ function getLogLevel() {
427
+ return config.level;
428
+ }
429
+ function configureLogger(options) {
430
+ config = { ...config, ...options };
431
+ }
432
+ function enableLogging() {
433
+ config.level = "debug";
434
+ }
435
+ function disableLogging() {
436
+ config.level = "none";
437
+ }
438
+ function setDebugMode(enabled) {
439
+ config.level = enabled ? "debug" : "error";
440
+ }
441
+ function isDebugMode() {
442
+ return config.level === "debug";
443
+ }
444
+ function shouldLog(level) {
445
+ return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
446
+ }
447
+ function formatTag(tag, level) {
448
+ const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
449
+ if (config.colors && level !== "none") {
450
+ const color = LOG_LEVEL_COLORS[level];
451
+ return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
452
+ }
453
+ return `${timestamp}[${tag}]`;
454
+ }
455
+
456
+ class Logger {
457
+ tag;
458
+ constructor(tag) {
459
+ this.tag = tag;
460
+ }
461
+ debug(...args) {
462
+ if (!shouldLog("debug"))
463
+ return;
464
+ if (config.handler) {
465
+ config.handler.debug(this.tag, ...args);
466
+ } else {
467
+ console.log(formatTag(this.tag, "debug"), ...args);
468
+ }
469
+ }
470
+ info(...args) {
471
+ if (!shouldLog("info"))
472
+ return;
473
+ if (config.handler) {
474
+ config.handler.info(this.tag, ...args);
475
+ } else {
476
+ console.info(formatTag(this.tag, "info"), ...args);
477
+ }
478
+ }
479
+ warn(...args) {
480
+ if (!shouldLog("warn"))
481
+ return;
482
+ if (config.handler) {
483
+ config.handler.warn(this.tag, ...args);
484
+ } else {
485
+ console.warn(formatTag(this.tag, "warn"), ...args);
486
+ }
487
+ }
488
+ error(...args) {
489
+ if (!shouldLog("error"))
490
+ return;
491
+ if (config.handler) {
492
+ config.handler.error(this.tag, ...args);
493
+ } else {
494
+ console.error(formatTag(this.tag, "error"), ...args);
495
+ }
496
+ }
497
+ time(label, fn) {
498
+ if (!shouldLog("debug")) {
499
+ return fn();
500
+ }
501
+ const start = performance.now();
502
+ try {
503
+ return fn();
504
+ } finally {
505
+ const duration = performance.now() - start;
506
+ this.debug(`${label}: ${duration.toFixed(2)}ms`);
507
+ }
508
+ }
509
+ async timeAsync(label, fn) {
510
+ if (!shouldLog("debug")) {
511
+ return fn();
512
+ }
513
+ const start = performance.now();
514
+ try {
515
+ return await fn();
516
+ } finally {
517
+ const duration = performance.now() - start;
518
+ this.debug(`${label}: ${duration.toFixed(2)}ms`);
519
+ }
520
+ }
521
+ child(subTag) {
522
+ return new Logger(`${this.tag}:${subTag}`);
523
+ }
524
+ debugIf(condition, ...args) {
525
+ if (condition)
526
+ this.debug(...args);
527
+ }
528
+ warnIf(condition, ...args) {
529
+ if (condition)
530
+ this.warn(...args);
531
+ }
532
+ errorIf(condition, ...args) {
533
+ if (condition)
534
+ this.error(...args);
535
+ }
536
+ loggedOnce = new Set;
537
+ warnOnce(key, ...args) {
538
+ if (this.loggedOnce.has(key))
539
+ return;
540
+ this.loggedOnce.add(key);
541
+ this.warn(...args);
542
+ }
543
+ debugOnce(key, ...args) {
544
+ if (this.loggedOnce.has(key))
545
+ return;
546
+ this.loggedOnce.add(key);
547
+ this.debug(...args);
548
+ }
549
+ }
550
+ function createLogger(tag) {
551
+ return new Logger(tag);
552
+ }
553
+ var LOG_LEVEL_ORDER, LOG_LEVEL_COLORS, RESET_COLOR = "\x1B[0m", config, logger, log, frameworkLoggers;
554
+ var init_logger = __esm(() => {
555
+ LOG_LEVEL_ORDER = {
556
+ debug: 0,
557
+ info: 1,
558
+ warn: 2,
559
+ error: 3,
560
+ none: 4
561
+ };
562
+ LOG_LEVEL_COLORS = {
563
+ debug: "\x1B[36m",
564
+ info: "\x1B[32m",
565
+ warn: "\x1B[33m",
566
+ error: "\x1B[31m"
567
+ };
568
+ config = {
569
+ level: isProduction() ? "error" : "debug",
570
+ colors: true,
571
+ timestamps: false
572
+ };
573
+ logger = createLogger("Hypen");
574
+ log = {
575
+ debug: (tag, ...args) => {
576
+ if (!shouldLog("debug"))
577
+ return;
578
+ console.log(formatTag(tag, "debug"), ...args);
579
+ },
580
+ info: (tag, ...args) => {
581
+ if (!shouldLog("info"))
582
+ return;
583
+ console.info(formatTag(tag, "info"), ...args);
584
+ },
585
+ warn: (tag, ...args) => {
586
+ if (!shouldLog("warn"))
587
+ return;
588
+ console.warn(formatTag(tag, "warn"), ...args);
589
+ },
590
+ error: (tag, ...args) => {
591
+ if (!shouldLog("error"))
592
+ return;
593
+ console.error(formatTag(tag, "error"), ...args);
594
+ }
595
+ };
596
+ frameworkLoggers = {
597
+ hypen: createLogger("Hypen"),
598
+ engine: createLogger("Engine"),
599
+ router: createLogger("Router"),
600
+ state: createLogger("State"),
601
+ events: createLogger("Events"),
602
+ remote: createLogger("Remote"),
603
+ renderer: createLogger("Renderer"),
604
+ module: createLogger("Module"),
605
+ lifecycle: createLogger("Lifecycle"),
606
+ loader: createLogger("Loader"),
607
+ context: createLogger("Context"),
608
+ discovery: createLogger("Discovery"),
609
+ plugin: createLogger("Plugin"),
610
+ canvas: createLogger("Canvas"),
611
+ debug: createLogger("Debug")
612
+ };
613
+ });
614
+
615
+ // src/app.ts
616
+ var exports_app = {};
617
+ __export(exports_app, {
618
+ app: () => app,
619
+ HypenModuleInstance: () => HypenModuleInstance,
620
+ HypenAppBuilder: () => HypenAppBuilder,
621
+ HypenApp: () => HypenApp
622
+ });
623
+
624
+ class HypenAppBuilder {
625
+ initialState;
626
+ options;
627
+ createdHandler;
628
+ actionHandlers = new Map;
629
+ destroyedHandler;
630
+ disconnectHandler;
631
+ reconnectHandler;
632
+ expireHandler;
633
+ errorHandler;
634
+ template;
635
+ constructor(initialState, options) {
636
+ this.initialState = initialState;
637
+ this.options = options || {};
638
+ }
639
+ onCreated(fn) {
640
+ this.createdHandler = fn;
641
+ return this;
642
+ }
643
+ onAction(name, fn) {
644
+ this.actionHandlers.set(name, fn);
645
+ return this;
646
+ }
647
+ onDestroyed(fn) {
648
+ this.destroyedHandler = fn;
649
+ return this;
650
+ }
651
+ onDisconnect(fn) {
652
+ this.disconnectHandler = fn;
653
+ return this;
654
+ }
655
+ onReconnect(fn) {
656
+ this.reconnectHandler = fn;
657
+ return this;
658
+ }
659
+ onExpire(fn) {
660
+ this.expireHandler = fn;
661
+ return this;
662
+ }
663
+ onError(fn) {
664
+ this.errorHandler = fn;
665
+ return this;
649
666
  }
650
667
  ui(template) {
651
668
  this.template = template;
@@ -693,17 +710,21 @@ class HypenModuleInstance {
693
710
  this.definition = definition;
694
711
  this.routerContext = routerContext;
695
712
  this.globalContext = globalContext;
713
+ const statePrefix = (definition.name || "").toLowerCase();
696
714
  this.state = createObservableState(definition.initialState, {
697
715
  onChange: (change) => {
698
716
  this.engine.notifyStateChange(change.paths, change.newValues);
699
717
  this.stateChangeCallbacks.forEach((cb) => cb());
700
- }
718
+ },
719
+ pathPrefix: statePrefix || undefined
701
720
  });
702
- this.engine.setModule(definition.name || "AnonymousModule", definition.actions, definition.stateKeys, getStateSnapshot(this.state));
721
+ const snapshot = getStateSnapshot(this.state);
722
+ const prefixedState = statePrefix ? { [statePrefix]: snapshot } : snapshot;
723
+ this.engine.setModule(definition.name || "AnonymousModule", definition.actions, definition.stateKeys, prefixedState);
703
724
  for (const [actionName, handler] of definition.handlers.onAction) {
704
- log4.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
725
+ log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
705
726
  this.engine.onAction(actionName, async (action) => {
706
- log4.debug(`Action handler fired: ${actionName}`, action);
727
+ log2.debug(`Action handler fired: ${actionName}`, action);
707
728
  const actionCtx = {
708
729
  name: action.name,
709
730
  payload: action.payload,
@@ -725,7 +746,7 @@ class HypenModuleInstance {
725
746
  throw result.error;
726
747
  }
727
748
  } else {
728
- log4.debug(`Action handler completed: ${actionName}`);
749
+ log2.debug(`Action handler completed: ${actionName}`);
729
750
  }
730
751
  });
731
752
  }
@@ -735,341 +756,104 @@ class HypenModuleInstance {
735
756
  if (!this.globalContext) {
736
757
  throw new Error("Global context not available");
737
758
  }
738
- const ctx = this.globalContext;
739
- const api = {
740
- getModule: (id) => ctx.getModule(id),
741
- hasModule: (id) => ctx.hasModule(id),
742
- getModuleIds: () => ctx.getModuleIds(),
743
- getGlobalState: () => ctx.getGlobalState(),
744
- emit: (event, payload) => ctx.emit(event, payload),
745
- on: (event, handler) => ctx.on(event, handler)
746
- };
747
- if (ctx.__router) {
748
- api.__router = ctx.__router;
749
- }
750
- if (ctx.__hypenEngine) {
751
- api.__hypenEngine = ctx.__hypenEngine;
752
- }
753
- return api;
754
- }
755
- async executeAction(actionName, handler, ctx) {
756
- try {
757
- const result = handler(ctx);
758
- await result;
759
- return Ok(undefined);
760
- } catch (e) {
761
- return Err(new ActionError(actionName, e));
762
- }
763
- }
764
- async handleError(error, context) {
765
- const errorCtx = {
766
- error,
767
- state: this.state,
768
- actionName: context.actionName,
769
- lifecycle: context.lifecycle
770
- };
771
- if (this.definition.handlers.onError) {
772
- try {
773
- const result = await this.definition.handlers.onError(errorCtx);
774
- if (result && typeof result === "object") {
775
- if ("handled" in result && result.handled) {
776
- return false;
777
- }
778
- if ("rethrow" in result && result.rethrow) {
779
- return true;
780
- }
781
- }
782
- } catch (handlerError) {
783
- log4.error("Error in onError handler:", handlerError);
784
- }
785
- }
786
- if (this.globalContext) {
787
- const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
788
- this.globalContext.emit("error", {
789
- message: error.message,
790
- error,
791
- context: eventContext
792
- });
793
- }
794
- log4.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
795
- return false;
796
- }
797
- async callCreatedHandler() {
798
- if (this.definition.handlers.onCreated) {
799
- const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
800
- await this.definition.handlers.onCreated(this.state, context);
801
- }
802
- }
803
- onStateChange(callback) {
804
- this.stateChangeCallbacks.push(callback);
805
- }
806
- async destroy() {
807
- if (this.isDestroyed)
808
- return;
809
- if (this.definition.handlers.onDestroyed) {
810
- await this.definition.handlers.onDestroyed(this.state);
811
- }
812
- this.isDestroyed = true;
813
- }
814
- getState() {
815
- return getStateSnapshot(this.state);
816
- }
817
- getLiveState() {
818
- return this.state;
819
- }
820
- updateState(patch) {
821
- Object.assign(this.state, patch);
822
- }
823
- }
824
- var log4, app;
825
- var init_app = __esm(() => {
826
- init_result();
827
- init_state();
828
- init_logger();
829
- log4 = createLogger("ModuleInstance");
830
- app = new HypenApp;
831
- });
832
-
833
- // src/engine.ts
834
- init_logger();
835
- import { WasmEngine } from "../wasm-node/hypen_engine.js";
836
- var log2 = frameworkLoggers.engine;
837
- function unwrapForWasm(value) {
838
- if (value === null || typeof value !== "object") {
839
- return value;
840
- }
841
- if (typeof value.__getSnapshot === "function") {
842
- return value.__getSnapshot();
843
- }
844
- try {
845
- return structuredClone(value);
846
- } catch {
847
- return JSON.parse(JSON.stringify(value));
848
- }
849
- }
850
-
851
- class Engine {
852
- wasmEngine = null;
853
- initialized = false;
854
- async init() {
855
- if (this.initialized)
856
- return;
857
- this.wasmEngine = new WasmEngine;
858
- this.initialized = true;
859
- }
860
- ensureInitialized() {
861
- if (!this.wasmEngine) {
862
- throw new Error("Engine not initialized. Call init() first.");
863
- }
864
- return this.wasmEngine;
865
- }
866
- setRenderCallback(callback) {
867
- const engine = this.ensureInitialized();
868
- engine.setRenderCallback((patches) => {
869
- callback(patches);
870
- });
871
- }
872
- setComponentResolver(resolver) {
873
- const engine = this.ensureInitialized();
874
- engine.setComponentResolver((componentName, contextPath) => {
875
- const result = resolver(componentName, contextPath);
876
- return result;
877
- });
878
- }
879
- renderSource(source) {
880
- const engine = this.ensureInitialized();
881
- engine.renderSource(source);
882
- }
883
- renderLazyComponent(source) {
884
- const engine = this.ensureInitialized();
885
- engine.renderLazyComponent(source);
886
- }
887
- renderInto(source, parentNodeId, state) {
888
- const engine = this.ensureInitialized();
889
- engine.renderInto(source, parentNodeId, unwrapForWasm(state));
890
- }
891
- notifyStateChange(paths, values) {
892
- const engine = this.ensureInitialized();
893
- if (paths.length === 0) {
894
- return;
895
- }
896
- engine.updateStateSparse(paths, unwrapForWasm(values));
897
- log2.debug("State changed (sparse):", paths);
898
- }
899
- notifyStateChangeFull(paths, currentState) {
900
- const engine = this.ensureInitialized();
901
- engine.updateState(unwrapForWasm(currentState));
902
- if (paths.length > 0) {
903
- log2.debug("State changed (full):", paths);
904
- }
905
- }
906
- updateState(statePatch) {
907
- const engine = this.ensureInitialized();
908
- engine.updateState(unwrapForWasm(statePatch));
909
- }
910
- dispatchAction(name, payload) {
911
- const engine = this.ensureInitialized();
912
- engine.dispatchAction(name, payload ?? null);
913
- }
914
- onAction(actionName, handler) {
915
- const engine = this.ensureInitialized();
916
- engine.onAction(actionName, (action) => {
917
- Promise.resolve(handler(action)).catch((err) => log2.error("Action handler error:", err));
918
- });
919
- }
920
- setModule(name, actions, stateKeys, initialState) {
921
- const engine = this.ensureInitialized();
922
- engine.setModule(name, actions, stateKeys, initialState);
923
- }
924
- getRevision() {
925
- const engine = this.ensureInitialized();
926
- return Number(engine.getRevision());
927
- }
928
- clearTree() {
929
- const engine = this.ensureInitialized();
930
- engine.clearTree();
931
- }
932
- debugParseComponent(source) {
933
- const engine = this.ensureInitialized();
934
- return engine.debugParseComponent(source);
935
- }
936
- }
937
-
938
- // src/engine.browser.ts
939
- init_logger();
940
- var log3 = frameworkLoggers.engine;
941
- var wasmInit = null;
942
- var WasmEngineClass = null;
943
- function mapToObject(value) {
944
- if (value instanceof Map) {
945
- const obj = {};
946
- for (const [key, val] of value.entries()) {
947
- obj[key] = mapToObject(val);
948
- }
949
- return obj;
950
- } else if (Array.isArray(value)) {
951
- return value.map(mapToObject);
952
- } else if (value && typeof value === "object" && value.constructor === Object) {
953
- const obj = {};
954
- for (const [key, val] of Object.entries(value)) {
955
- obj[key] = mapToObject(val);
759
+ const ctx = this.globalContext;
760
+ const api = {
761
+ getModule: (id) => ctx.getModule(id),
762
+ hasModule: (id) => ctx.hasModule(id),
763
+ getModuleIds: () => ctx.getModuleIds(),
764
+ getGlobalState: () => ctx.getGlobalState(),
765
+ emit: (event, payload) => ctx.emit(event, payload),
766
+ on: (event, handler) => ctx.on(event, handler)
767
+ };
768
+ if (ctx.__router) {
769
+ api.__router = ctx.__router;
956
770
  }
957
- return obj;
771
+ if (ctx.__hypenEngine) {
772
+ api.__hypenEngine = ctx.__hypenEngine;
773
+ }
774
+ return api;
958
775
  }
959
- return value;
960
- }
961
-
962
- class Engine2 {
963
- wasmEngine = null;
964
- initialized = false;
965
- async init(options = {}) {
966
- if (this.initialized)
967
- return;
968
- const cdnBase = "https://unpkg.com/@hypen-space/core@latest/wasm-browser";
969
- const jsUrl = options.jsUrl ?? `${cdnBase}/hypen_engine.js`;
970
- const wasmUrl = options.wasmUrl ?? `${cdnBase}/hypen_engine_bg.wasm`;
776
+ async executeAction(actionName, handler, ctx) {
971
777
  try {
972
- const wasmModule = await import(jsUrl);
973
- wasmInit = wasmModule.default;
974
- WasmEngineClass = wasmModule.WasmEngine;
975
- await wasmInit(wasmUrl);
976
- this.wasmEngine = new WasmEngineClass;
977
- this.initialized = true;
978
- } catch (error) {
979
- log3.error("Failed to initialize WASM engine:", error);
980
- throw error;
778
+ const result = handler(ctx);
779
+ await result;
780
+ return Ok(undefined);
781
+ } catch (e) {
782
+ return Err(new ActionError(actionName, e));
981
783
  }
982
784
  }
983
- ensureInitialized() {
984
- if (!this.wasmEngine) {
985
- throw new Error("Engine not initialized. Call init() first.");
785
+ async handleError(error, context) {
786
+ const errorCtx = {
787
+ error,
788
+ state: this.state,
789
+ actionName: context.actionName,
790
+ lifecycle: context.lifecycle
791
+ };
792
+ if (this.definition.handlers.onError) {
793
+ try {
794
+ const result = await this.definition.handlers.onError(errorCtx);
795
+ if (result && typeof result === "object") {
796
+ if ("handled" in result && result.handled) {
797
+ return false;
798
+ }
799
+ if ("rethrow" in result && result.rethrow) {
800
+ return true;
801
+ }
802
+ }
803
+ } catch (handlerError) {
804
+ log2.error("Error in onError handler:", handlerError);
805
+ }
986
806
  }
987
- return this.wasmEngine;
988
- }
989
- setRenderCallback(callback) {
990
- const engine = this.ensureInitialized();
991
- engine.setRenderCallback((patches) => {
992
- callback(patches);
993
- });
994
- }
995
- setComponentResolver(resolver) {
996
- const engine = this.ensureInitialized();
997
- engine.setComponentResolver((componentName, contextPath) => {
998
- const result = resolver(componentName, contextPath);
999
- return result;
1000
- });
1001
- }
1002
- renderSource(source) {
1003
- const engine = this.ensureInitialized();
1004
- engine.renderSource(source);
1005
- }
1006
- renderLazyComponent(source) {
1007
- const engine = this.ensureInitialized();
1008
- engine.renderLazyComponent(source);
1009
- }
1010
- renderInto(source, parentNodeId, state) {
1011
- const engine = this.ensureInitialized();
1012
- const safeState = JSON.parse(JSON.stringify(state));
1013
- engine.renderInto(source, parentNodeId, safeState);
1014
- }
1015
- notifyStateChange(paths, values) {
1016
- const engine = this.ensureInitialized();
1017
- if (paths.length === 0) {
1018
- return;
807
+ if (this.globalContext) {
808
+ const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
809
+ this.globalContext.emit("error", {
810
+ message: error.message,
811
+ error,
812
+ context: eventContext
813
+ });
1019
814
  }
1020
- const plainValues = JSON.parse(JSON.stringify(values));
1021
- engine.updateStateSparse(paths, plainValues);
1022
- log3.debug("State changed (sparse):", paths);
815
+ log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
816
+ return false;
1023
817
  }
1024
- notifyStateChangeFull(paths, currentState) {
1025
- const engine = this.ensureInitialized();
1026
- const plainObject = JSON.parse(JSON.stringify(currentState));
1027
- engine.updateState(plainObject);
1028
- if (paths.length > 0) {
1029
- log3.debug("State changed (full):", paths);
818
+ async callCreatedHandler() {
819
+ if (this.definition.handlers.onCreated) {
820
+ const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
821
+ await this.definition.handlers.onCreated(this.state, context);
1030
822
  }
1031
823
  }
1032
- updateState(statePatch) {
1033
- const engine = this.ensureInitialized();
1034
- const plainObject = JSON.parse(JSON.stringify(statePatch));
1035
- engine.updateState(plainObject);
1036
- }
1037
- dispatchAction(name, payload) {
1038
- const engine = this.ensureInitialized();
1039
- log3.debug(`Action dispatched: ${name}`);
1040
- engine.dispatchAction(name, payload ?? null);
1041
- }
1042
- onAction(actionName, handler) {
1043
- const engine = this.ensureInitialized();
1044
- engine.onAction(actionName, (action) => {
1045
- const normalizedAction = {
1046
- ...action,
1047
- payload: action.payload ? mapToObject(action.payload) : action.payload
1048
- };
1049
- Promise.resolve(handler(normalizedAction)).catch((err) => log3.error("Action handler error:", err));
1050
- });
824
+ onStateChange(callback) {
825
+ this.stateChangeCallbacks.push(callback);
1051
826
  }
1052
- setModule(name, actions, stateKeys, initialState) {
1053
- const engine = this.ensureInitialized();
1054
- engine.setModule(name, actions, stateKeys, initialState);
827
+ async destroy() {
828
+ if (this.isDestroyed)
829
+ return;
830
+ if (this.definition.handlers.onDestroyed) {
831
+ await this.definition.handlers.onDestroyed(this.state);
832
+ }
833
+ this.isDestroyed = true;
1055
834
  }
1056
- getRevision() {
1057
- const engine = this.ensureInitialized();
1058
- return engine.getRevision();
835
+ getState() {
836
+ return getStateSnapshot(this.state);
1059
837
  }
1060
- clearTree() {
1061
- const engine = this.ensureInitialized();
1062
- engine.clearTree();
838
+ getLiveState() {
839
+ return this.state;
1063
840
  }
1064
- debugParseComponent(source) {
1065
- const engine = this.ensureInitialized();
1066
- return engine.debugParseComponent(source);
841
+ updateState(patch) {
842
+ Object.assign(this.state, patch);
1067
843
  }
1068
844
  }
845
+ var log2, app;
846
+ var init_app = __esm(() => {
847
+ init_result();
848
+ init_state();
849
+ init_logger();
850
+ log2 = createLogger("ModuleInstance");
851
+ app = new HypenApp;
852
+ });
1069
853
 
1070
854
  // src/renderer.ts
1071
855
  init_logger();
1072
- var log5 = frameworkLoggers.renderer;
856
+ var log3 = frameworkLoggers.renderer;
1073
857
 
1074
858
  class BaseRenderer {
1075
859
  nodes = new Map;
@@ -1111,14 +895,14 @@ class BaseRenderer {
1111
895
 
1112
896
  class ConsoleRenderer {
1113
897
  applyPatches(patches) {
1114
- log5.debug("Patches:", patches);
898
+ log3.debug("Patches:", patches);
1115
899
  }
1116
900
  }
1117
901
 
1118
902
  // src/router.ts
1119
903
  init_state();
1120
904
  init_logger();
1121
- var log6 = frameworkLoggers.router;
905
+ var log4 = frameworkLoggers.router;
1122
906
 
1123
907
  class HypenRouter {
1124
908
  state;
@@ -1156,7 +940,7 @@ class HypenRouter {
1156
940
  this.updatePath(newPath, false);
1157
941
  });
1158
942
  this.isInitialized = true;
1159
- log6.debug("Router initialized at:", initialPath);
943
+ log4.debug("Router initialized at:", initialPath);
1160
944
  }
1161
945
  getPathFromBrowser() {
1162
946
  if (typeof window === "undefined")
@@ -1177,21 +961,21 @@ class HypenRouter {
1177
961
  return query;
1178
962
  }
1179
963
  push(path) {
1180
- log6.debug("push:", path);
964
+ log4.debug("push:", path);
1181
965
  this.updatePath(path, true);
1182
966
  }
1183
967
  replace(path) {
1184
- log6.debug("replace:", path);
968
+ log4.debug("replace:", path);
1185
969
  this.updatePath(path, true, true);
1186
970
  }
1187
971
  back() {
1188
- log6.debug("back");
972
+ log4.debug("back");
1189
973
  if (typeof window !== "undefined") {
1190
974
  window.history.back();
1191
975
  }
1192
976
  }
1193
977
  forward() {
1194
- log6.debug("forward");
978
+ log4.debug("forward");
1195
979
  if (typeof window !== "undefined") {
1196
980
  window.history.forward();
1197
981
  }
@@ -1287,7 +1071,7 @@ class HypenRouter {
1287
1071
  try {
1288
1072
  callback(this.getState());
1289
1073
  } catch (error) {
1290
- log6.error("Error in route subscriber:", error);
1074
+ log4.error("Error in route subscriber:", error);
1291
1075
  }
1292
1076
  return () => {
1293
1077
  this.subscribers.delete(callback);
@@ -1299,7 +1083,7 @@ class HypenRouter {
1299
1083
  try {
1300
1084
  callback(routeState);
1301
1085
  } catch (error) {
1302
- log6.error("Error in route subscriber:", error);
1086
+ log4.error("Error in route subscriber:", error);
1303
1087
  }
1304
1088
  });
1305
1089
  }
@@ -1317,7 +1101,7 @@ class HypenRouter {
1317
1101
 
1318
1102
  // src/events.ts
1319
1103
  init_logger();
1320
- var log7 = frameworkLoggers.events;
1104
+ var log5 = frameworkLoggers.events;
1321
1105
 
1322
1106
  class TypedEventEmitter {
1323
1107
  eventBus = new Map;
@@ -1330,7 +1114,7 @@ class TypedEventEmitter {
1330
1114
  try {
1331
1115
  handler(payload);
1332
1116
  } catch (error) {
1333
- log7.error(`Error in event handler for "${String(event)}":`, error);
1117
+ log5.error(`Error in event handler for "${String(event)}":`, error);
1334
1118
  }
1335
1119
  });
1336
1120
  }
@@ -1383,7 +1167,7 @@ function createEventEmitter() {
1383
1167
 
1384
1168
  // src/context.ts
1385
1169
  init_logger();
1386
- var log8 = frameworkLoggers.context;
1170
+ var log6 = frameworkLoggers.context;
1387
1171
 
1388
1172
  class HypenGlobalContext {
1389
1173
  modules = new Map;
@@ -1397,14 +1181,14 @@ class HypenGlobalContext {
1397
1181
  }
1398
1182
  registerModule(id, instance) {
1399
1183
  if (this.modules.has(id)) {
1400
- log8.warn(`Module "${id}" is already registered. Overwriting.`);
1184
+ log6.warn(`Module "${id}" is already registered. Overwriting.`);
1401
1185
  }
1402
1186
  this.modules.set(id, instance);
1403
- log8.debug(`Registered module: ${id}`);
1187
+ log6.debug(`Registered module: ${id}`);
1404
1188
  }
1405
1189
  unregisterModule(id) {
1406
1190
  this.modules.delete(id);
1407
- log8.debug(`Unregistered module: ${id}`);
1191
+ log6.debug(`Unregistered module: ${id}`);
1408
1192
  }
1409
1193
  getModule(id) {
1410
1194
  const module = this.modules.get(id);
@@ -1433,14 +1217,14 @@ class HypenGlobalContext {
1433
1217
  emit(event, payload) {
1434
1218
  const handlers = this.legacyEventBus.get(event);
1435
1219
  if (!handlers || handlers.size === 0) {
1436
- log8.debug(`Event "${event}" emitted but no listeners`);
1220
+ log6.debug(`Event "${event}" emitted but no listeners`);
1437
1221
  } else {
1438
- log8.debug(`Emitting event: ${event}`, payload);
1222
+ log6.debug(`Emitting event: ${event}`, payload);
1439
1223
  handlers.forEach((handler) => {
1440
1224
  try {
1441
1225
  handler(payload);
1442
1226
  } catch (error) {
1443
- log8.error(`Error in event handler for "${event}":`, error);
1227
+ log6.error(`Error in event handler for "${event}":`, error);
1444
1228
  }
1445
1229
  });
1446
1230
  }
@@ -1454,7 +1238,7 @@ class HypenGlobalContext {
1454
1238
  }
1455
1239
  const handlers = this.legacyEventBus.get(event);
1456
1240
  handlers.add(handler);
1457
- log8.debug(`Listening to event: ${event}`);
1241
+ log6.debug(`Listening to event: ${event}`);
1458
1242
  return () => {
1459
1243
  handlers.delete(handler);
1460
1244
  if (handlers.size === 0) {
@@ -1489,7 +1273,7 @@ class HypenGlobalContext {
1489
1273
 
1490
1274
  // src/disposable.ts
1491
1275
  init_logger();
1492
- var log9 = frameworkLoggers.lifecycle;
1276
+ var log7 = frameworkLoggers.lifecycle;
1493
1277
  function isDisposable(obj) {
1494
1278
  return obj !== null && typeof obj === "object" && "dispose" in obj && typeof obj.dispose === "function";
1495
1279
  }
@@ -1521,7 +1305,7 @@ class DisposableStack {
1521
1305
  try {
1522
1306
  item.dispose();
1523
1307
  } catch (error) {
1524
- log9.error("Error during dispose:", error);
1308
+ log7.error("Error during dispose:", error);
1525
1309
  }
1526
1310
  }
1527
1311
  }
@@ -1612,7 +1396,7 @@ function compositeDisposable(...disposables) {
1612
1396
  try {
1613
1397
  d.dispose();
1614
1398
  } catch (error) {
1615
- log9.error("Error during dispose:", error);
1399
+ log7.error("Error during dispose:", error);
1616
1400
  }
1617
1401
  }
1618
1402
  }
@@ -1766,7 +1550,7 @@ var RetryPresets = {
1766
1550
 
1767
1551
  // src/remote/client.ts
1768
1552
  init_logger();
1769
- var log10 = frameworkLoggers.remote;
1553
+ var log8 = frameworkLoggers.remote;
1770
1554
 
1771
1555
  class RemoteEngine {
1772
1556
  ws = null;
@@ -1874,7 +1658,7 @@ class RemoteEngine {
1874
1658
  }
1875
1659
  dispatchAction(action, payload) {
1876
1660
  if (this.state !== "connected" || !this.ws) {
1877
- log10.warn("Cannot dispatch action: not connected");
1661
+ log8.warn("Cannot dispatch action: not connected");
1878
1662
  return;
1879
1663
  }
1880
1664
  const message = {
@@ -1947,7 +1731,7 @@ class RemoteEngine {
1947
1731
  break;
1948
1732
  }
1949
1733
  } catch (e) {
1950
- log10.error("Error handling remote message:", e);
1734
+ log8.error("Error handling remote message:", e);
1951
1735
  const error = e instanceof Error ? e : new Error(String(e));
1952
1736
  this.errorCallbacks.forEach((cb) => cb(error));
1953
1737
  }
@@ -1976,7 +1760,7 @@ class RemoteEngine {
1976
1760
  }
1977
1761
  handlePatch(message) {
1978
1762
  if (message.revision <= this.currentRevision) {
1979
- log10.warn(`Out of order patch: expected > ${this.currentRevision}, got ${message.revision}`);
1763
+ log8.warn(`Out of order patch: expected > ${this.currentRevision}, got ${message.revision}`);
1980
1764
  return;
1981
1765
  }
1982
1766
  this.currentRevision = message.revision;
@@ -2002,16 +1786,121 @@ class RemoteEngine {
2002
1786
  maxDelayMs: 30000,
2003
1787
  jitter: 0.1,
2004
1788
  onRetry: (attempt, error) => {
2005
- log10.debug(`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`);
1789
+ log8.debug(`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`);
2006
1790
  }
2007
1791
  }).catch((error) => {
2008
- log10.error("Max reconnection attempts reached:", error.message);
1792
+ log8.error("Max reconnection attempts reached:", error.message);
2009
1793
  this.errorCallbacks.forEach((cb) => cb(new ConnectionError(this.url, error, this.options.maxReconnectAttempts)));
2010
1794
  });
2011
1795
  }, this.options.reconnectInterval);
2012
1796
  }
2013
1797
  }
2014
1798
 
1799
+ // src/engine.ts
1800
+ init_logger();
1801
+ import { WasmEngine } from "../wasm-node/hypen_engine.js";
1802
+ var log9 = frameworkLoggers.engine;
1803
+ function unwrapForWasm(value) {
1804
+ if (value === null || typeof value !== "object") {
1805
+ return value;
1806
+ }
1807
+ if (typeof value.__getSnapshot === "function") {
1808
+ return value.__getSnapshot();
1809
+ }
1810
+ try {
1811
+ return structuredClone(value);
1812
+ } catch {
1813
+ return JSON.parse(JSON.stringify(value));
1814
+ }
1815
+ }
1816
+
1817
+ class Engine {
1818
+ wasmEngine = null;
1819
+ initialized = false;
1820
+ async init() {
1821
+ if (this.initialized)
1822
+ return;
1823
+ this.wasmEngine = new WasmEngine;
1824
+ this.initialized = true;
1825
+ }
1826
+ ensureInitialized() {
1827
+ if (!this.wasmEngine) {
1828
+ throw new Error("Engine not initialized. Call init() first.");
1829
+ }
1830
+ return this.wasmEngine;
1831
+ }
1832
+ setRenderCallback(callback) {
1833
+ const engine = this.ensureInitialized();
1834
+ engine.setRenderCallback((patches) => {
1835
+ callback(patches);
1836
+ });
1837
+ }
1838
+ setComponentResolver(resolver) {
1839
+ const engine = this.ensureInitialized();
1840
+ engine.setComponentResolver((componentName, contextPath) => {
1841
+ const result = resolver(componentName, contextPath);
1842
+ return result;
1843
+ });
1844
+ }
1845
+ renderSource(source) {
1846
+ const engine = this.ensureInitialized();
1847
+ engine.renderSource(source);
1848
+ }
1849
+ renderLazyComponent(source) {
1850
+ const engine = this.ensureInitialized();
1851
+ engine.renderLazyComponent(source);
1852
+ }
1853
+ renderInto(source, parentNodeId, state) {
1854
+ const engine = this.ensureInitialized();
1855
+ engine.renderInto(source, parentNodeId, unwrapForWasm(state));
1856
+ }
1857
+ notifyStateChange(paths, values) {
1858
+ const engine = this.ensureInitialized();
1859
+ if (paths.length === 0) {
1860
+ return;
1861
+ }
1862
+ engine.updateStateSparse(paths, unwrapForWasm(values));
1863
+ log9.debug("State changed (sparse):", paths);
1864
+ }
1865
+ notifyStateChangeFull(paths, currentState) {
1866
+ const engine = this.ensureInitialized();
1867
+ engine.updateState(unwrapForWasm(currentState));
1868
+ if (paths.length > 0) {
1869
+ log9.debug("State changed (full):", paths);
1870
+ }
1871
+ }
1872
+ updateState(statePatch) {
1873
+ const engine = this.ensureInitialized();
1874
+ engine.updateState(unwrapForWasm(statePatch));
1875
+ }
1876
+ dispatchAction(name, payload) {
1877
+ const engine = this.ensureInitialized();
1878
+ engine.dispatchAction(name, payload ?? null);
1879
+ }
1880
+ onAction(actionName, handler) {
1881
+ const engine = this.ensureInitialized();
1882
+ engine.onAction(actionName, (action) => {
1883
+ Promise.resolve(handler(action)).catch((err) => log9.error("Action handler error:", err));
1884
+ });
1885
+ }
1886
+ setModule(name, actions, stateKeys, initialState) {
1887
+ const engine = this.ensureInitialized();
1888
+ engine.setModule(name, actions, stateKeys, initialState);
1889
+ }
1890
+ getRevision() {
1891
+ const engine = this.ensureInitialized();
1892
+ return Number(engine.getRevision());
1893
+ }
1894
+ clearTree() {
1895
+ const engine = this.ensureInitialized();
1896
+ engine.clearTree();
1897
+ }
1898
+ debugParseComponent(source) {
1899
+ const engine = this.ensureInitialized();
1900
+ return engine.debugParseComponent(source);
1901
+ }
1902
+ }
1903
+
2015
1904
  // src/remote/server.ts
2016
1905
  init_app();
2017
1906
 
@@ -2141,7 +2030,7 @@ class SessionManager {
2141
2030
 
2142
2031
  // src/remote/server.ts
2143
2032
  init_logger();
2144
- var log11 = frameworkLoggers.remote;
2033
+ var log10 = frameworkLoggers.remote;
2145
2034
 
2146
2035
  class RemoteServer {
2147
2036
  _module = null;
@@ -2219,7 +2108,7 @@ class RemoteServer {
2219
2108
  return new Response("Hypen Remote Server", { status: 200 });
2220
2109
  }
2221
2110
  });
2222
- log11.info(`Hypen app streaming on ws://${hostname}:${finalPort}`);
2111
+ log10.info(`Hypen app streaming on ws://${hostname}:${finalPort}`);
2223
2112
  return this;
2224
2113
  }
2225
2114
  stop() {
@@ -2262,7 +2151,7 @@ class RemoteServer {
2262
2151
  }
2263
2152
  }, 1000);
2264
2153
  } catch (error) {
2265
- log11.error("Error handling WebSocket open:", error);
2154
+ log10.error("Error handling WebSocket open:", error);
2266
2155
  ws.close(1011, "Internal server error");
2267
2156
  }
2268
2157
  }
@@ -2433,7 +2322,7 @@ class RemoteServer {
2433
2322
  break;
2434
2323
  }
2435
2324
  } catch (error) {
2436
- log11.error("Error handling WebSocket message:", error);
2325
+ log10.error("Error handling WebSocket message:", error);
2437
2326
  }
2438
2327
  }
2439
2328
  async handleClose(ws) {
@@ -2516,7 +2405,7 @@ function serve(options) {
2516
2405
  init_logger();
2517
2406
  import { existsSync, readdirSync, readFileSync } from "fs";
2518
2407
  import { join } from "path";
2519
- var log12 = frameworkLoggers.loader;
2408
+ var log11 = frameworkLoggers.loader;
2520
2409
 
2521
2410
  class ComponentLoader {
2522
2411
  components = new Map;
@@ -2551,16 +2440,16 @@ class ComponentLoader {
2551
2440
  const templatePath = join(dirPath, "component.hypen");
2552
2441
  const template = readFileSync(templatePath, "utf-8");
2553
2442
  this.register(name, module, template, dirPath);
2554
- log12.debug(`Loaded component: ${name} from ${dirPath}`);
2443
+ log11.debug(`Loaded component: ${name} from ${dirPath}`);
2555
2444
  } catch (error) {
2556
- log12.error(`Failed to load component ${name} from ${dirPath}:`, error);
2445
+ log11.error(`Failed to load component ${name} from ${dirPath}:`, error);
2557
2446
  throw error;
2558
2447
  }
2559
2448
  }
2560
2449
  async loadFromComponentsDir(baseDir) {
2561
2450
  try {
2562
2451
  if (!existsSync(baseDir)) {
2563
- log12.warn(`Components directory not found: ${baseDir}`);
2452
+ log11.warn(`Components directory not found: ${baseDir}`);
2564
2453
  return;
2565
2454
  }
2566
2455
  const entries = readdirSync(baseDir, { withFileTypes: true });
@@ -2573,9 +2462,9 @@ class ComponentLoader {
2573
2462
  await this.loadFromDirectory(entry.name, componentDir);
2574
2463
  }
2575
2464
  }
2576
- log12.debug(`Loaded ${this.components.size} components from ${baseDir}`);
2465
+ log11.debug(`Loaded ${this.components.size} components from ${baseDir}`);
2577
2466
  } catch (error) {
2578
- log12.error(`Failed to load components from ${baseDir}:`, error);
2467
+ log11.error(`Failed to load components from ${baseDir}:`, error);
2579
2468
  throw error;
2580
2469
  }
2581
2470
  }
@@ -2586,14 +2475,32 @@ var componentLoader = new ComponentLoader;
2586
2475
  class ComponentResolver {
2587
2476
  cache = new Map;
2588
2477
  options;
2478
+ moduleRegistry;
2589
2479
  constructor(options = {}) {
2590
2480
  this.options = {
2591
2481
  baseDir: options.baseDir || process.cwd(),
2592
2482
  cache: options.cache ?? true,
2593
- customFetch: options.customFetch || this.defaultFetch.bind(this)
2483
+ customFetch: options.customFetch || this.defaultFetch.bind(this),
2484
+ moduleRegistry: options.moduleRegistry
2594
2485
  };
2486
+ this.moduleRegistry = options.moduleRegistry;
2595
2487
  }
2596
2488
  async resolve(importStmt) {
2489
+ if (this.moduleRegistry) {
2490
+ const names = importStmt.clause.type === "named" ? importStmt.clause.names : [importStmt.clause.name];
2491
+ const allFound = names.every((name) => this.moduleRegistry.has(name));
2492
+ if (allFound) {
2493
+ const result = {};
2494
+ for (const name of names) {
2495
+ const def = this.moduleRegistry.get(name);
2496
+ result[name] = {
2497
+ module: def,
2498
+ template: def?.template || ""
2499
+ };
2500
+ }
2501
+ return result;
2502
+ }
2503
+ }
2597
2504
  const sourcePath = this.getSourcePath(importStmt.source);
2598
2505
  if (this.options.cache && this.cache.has(sourcePath)) {
2599
2506
  const cached = this.cache.get(sourcePath);
@@ -2611,8 +2518,17 @@ class ComponentResolver {
2611
2518
  return this.extractComponents(importStmt.clause, component);
2612
2519
  }
2613
2520
  async resolveLocal(path) {
2614
- throw new Error(`Dynamic local component resolution not yet implemented: ${path}
2615
- ` + `Please use the build-components.ts script to generate component imports.`);
2521
+ const { resolve, join: join2 } = await import("path");
2522
+ const { readFile } = await import("fs/promises");
2523
+ const basePath = resolve(this.options.baseDir, path);
2524
+ const hypenPath = basePath.endsWith(".hypen") ? basePath : `${basePath}.hypen`;
2525
+ const template = await readFile(hypenPath, "utf-8");
2526
+ const modulePath = hypenPath.replace(/\.hypen$/, ".ts");
2527
+ let module = {};
2528
+ try {
2529
+ module = await import(modulePath);
2530
+ } catch {}
2531
+ return { module, template };
2616
2532
  }
2617
2533
  async resolveUrl(url) {
2618
2534
  try {
@@ -2680,29 +2596,26 @@ class ComponentResolver {
2680
2596
  init_logger();
2681
2597
  import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, watch } from "fs";
2682
2598
  import { join as join2, basename, resolve } from "path";
2683
- function removeImports(text) {
2684
- return text.replace(/import\s+(?:\{[^}]+\}|\w+)\s+from\s+["'][^"']+["']\s*/g, "");
2685
- }
2686
2599
  async function discoverComponents(baseDir, options = {}) {
2687
2600
  const {
2688
2601
  patterns = ["single-file", "folder", "sibling", "index"],
2689
2602
  recursive = false,
2690
2603
  debug = false
2691
2604
  } = options;
2692
- const log13 = debug ? (...args) => frameworkLoggers.discovery.debug(...args) : () => {};
2605
+ const log12 = debug ? (...args) => frameworkLoggers.discovery.debug(...args) : () => {};
2693
2606
  const resolvedDir = resolve(baseDir);
2694
2607
  const components = [];
2695
2608
  const seen = new Set;
2696
- log13("Scanning directory:", resolvedDir);
2697
- log13("Patterns:", patterns);
2609
+ log12("Scanning directory:", resolvedDir);
2610
+ log12("Patterns:", patterns);
2698
2611
  const addTwoFileComponent = (name, hypenPath, modulePath) => {
2699
2612
  if (seen.has(name)) {
2700
- log13(`Skipping duplicate: ${name}`);
2613
+ log12(`Skipping duplicate: ${name}`);
2701
2614
  return;
2702
2615
  }
2703
2616
  seen.add(name);
2704
2617
  const templateRaw = readFileSync2(hypenPath, "utf-8");
2705
- const template = removeImports(templateRaw).trim();
2618
+ const template = templateRaw.trim();
2706
2619
  components.push({
2707
2620
  name,
2708
2621
  hypenPath,
@@ -2711,11 +2624,11 @@ async function discoverComponents(baseDir, options = {}) {
2711
2624
  hasModule: modulePath !== null,
2712
2625
  isSingleFile: false
2713
2626
  });
2714
- log13(`Found: ${name} (two-file, ${modulePath ? "with module" : "stateless"})`);
2627
+ log12(`Found: ${name} (two-file, ${modulePath ? "with module" : "stateless"})`);
2715
2628
  };
2716
2629
  const addSingleFileComponent = (name, modulePath, template) => {
2717
2630
  if (seen.has(name)) {
2718
- log13(`Skipping duplicate: ${name}`);
2631
+ log12(`Skipping duplicate: ${name}`);
2719
2632
  return;
2720
2633
  }
2721
2634
  seen.add(name);
@@ -2727,7 +2640,7 @@ async function discoverComponents(baseDir, options = {}) {
2727
2640
  hasModule: true,
2728
2641
  isSingleFile: true
2729
2642
  });
2730
- log13(`Found: ${name} (single-file with inline template)`);
2643
+ log12(`Found: ${name} (single-file with inline template)`);
2731
2644
  };
2732
2645
  const scanForFolderComponents = (dir) => {
2733
2646
  if (!existsSync2(dir))
@@ -2811,7 +2724,7 @@ async function discoverComponents(baseDir, options = {}) {
2811
2724
  addSingleFileComponent(baseName, modulePath, module.template);
2812
2725
  }
2813
2726
  } catch (e) {
2814
- log13(`Failed to import potential single-file component: ${entry.name}`, e);
2727
+ log12(`Failed to import potential single-file component: ${entry.name}`, e);
2815
2728
  }
2816
2729
  }
2817
2730
  }
@@ -2825,7 +2738,7 @@ async function discoverComponents(baseDir, options = {}) {
2825
2738
  if (patterns.includes("single-file")) {
2826
2739
  await scanForSingleFileComponents(resolvedDir);
2827
2740
  }
2828
- log13(`Discovered ${components.length} components`);
2741
+ log12(`Discovered ${components.length} components`);
2829
2742
  return components;
2830
2743
  }
2831
2744
  async function loadDiscoveredComponents(components) {
@@ -2861,7 +2774,7 @@ function watchComponents(baseDir, options = {}) {
2861
2774
  debug = false,
2862
2775
  ...discoveryOptions
2863
2776
  } = options;
2864
- const log13 = debug ? (...args) => frameworkLoggers.discovery.debug("[watch]", ...args) : () => {};
2777
+ const log12 = debug ? (...args) => frameworkLoggers.discovery.debug("[watch]", ...args) : () => {};
2865
2778
  let currentComponents = new Map;
2866
2779
  let debounceTimer = null;
2867
2780
  const initialScan = async () => {
@@ -2874,22 +2787,22 @@ function watchComponents(baseDir, options = {}) {
2874
2787
  clearTimeout(debounceTimer);
2875
2788
  }
2876
2789
  debounceTimer = setTimeout(async () => {
2877
- log13("Rescanning...");
2790
+ log12("Rescanning...");
2878
2791
  const newComponents = await discoverComponents(resolvedDir, discoveryOptions);
2879
2792
  const newMap = new Map(newComponents.map((c) => [c.name, c]));
2880
2793
  for (const [name, component] of newMap) {
2881
2794
  const existing = currentComponents.get(name);
2882
2795
  if (!existing) {
2883
- log13("Added:", name);
2796
+ log12("Added:", name);
2884
2797
  onAdd?.(component);
2885
2798
  } else if (existing.template !== component.template || existing.modulePath !== component.modulePath) {
2886
- log13("Updated:", name);
2799
+ log12("Updated:", name);
2887
2800
  onUpdate?.(component);
2888
2801
  }
2889
2802
  }
2890
2803
  for (const name of currentComponents.keys()) {
2891
2804
  if (!newMap.has(name)) {
2892
- log13("Removed:", name);
2805
+ log12("Removed:", name);
2893
2806
  onRemove?.(name);
2894
2807
  }
2895
2808
  }
@@ -2901,7 +2814,7 @@ function watchComponents(baseDir, options = {}) {
2901
2814
  if (!filename)
2902
2815
  return;
2903
2816
  if (filename.endsWith(".hypen") || filename.endsWith(".ts")) {
2904
- log13("File changed:", filename);
2817
+ log12("File changed:", filename);
2905
2818
  rescan();
2906
2819
  }
2907
2820
  });
@@ -3024,28 +2937,28 @@ function parseImports(text) {
3024
2937
  }
3025
2938
  return imports;
3026
2939
  }
3027
- function removeImports2(text) {
2940
+ function removeImports(text) {
3028
2941
  return text.replace(/import\s+(?:\{[^}]+\}|\w+)\s+from\s+["'][^"']+["']\s*/g, "");
3029
2942
  }
3030
2943
  function hypenPlugin(options = {}) {
3031
2944
  const { debug = false, patterns = ["sibling", "component", "index"] } = options;
3032
- const log13 = debug ? (...args) => frameworkLoggers.plugin.debug(...args) : () => {};
2945
+ const log12 = debug ? (...args) => frameworkLoggers.plugin.debug(...args) : () => {};
3033
2946
  return {
3034
2947
  name: "hypen-loader",
3035
2948
  async setup(build) {
3036
2949
  build.onLoad({ filter: /\.hypen$/ }, async (args) => {
3037
2950
  const hypenPath = resolve2(args.path);
3038
- log13("Loading:", hypenPath);
2951
+ log12("Loading:", hypenPath);
3039
2952
  const templateRaw = readFileSync3(hypenPath, "utf-8");
3040
2953
  const imports = parseImports(templateRaw);
3041
- const template = removeImports2(templateRaw).trim();
2954
+ const template = removeImports(templateRaw).trim();
3042
2955
  if (imports.length > 0) {
3043
- log13("Found imports:", imports);
2956
+ log12("Found imports:", imports);
3044
2957
  }
3045
2958
  const componentName = getComponentName(hypenPath);
3046
- log13("Component name:", componentName);
2959
+ log12("Component name:", componentName);
3047
2960
  const modulePath = findModulePath(hypenPath, patterns);
3048
- log13("Module path:", modulePath);
2961
+ log12("Module path:", modulePath);
3049
2962
  let contents;
3050
2963
  if (modulePath) {
3051
2964
  const relativeModulePath = modulePath.replace(/\.ts$/, ".js");
@@ -3057,7 +2970,7 @@ export const name = ${JSON.stringify(componentName)};
3057
2970
  export default { module: _module, template: ${JSON.stringify(template)}, name: ${JSON.stringify(componentName)} };
3058
2971
  `;
3059
2972
  } else {
3060
- log13("No module found, creating stateless component");
2973
+ log12("No module found, creating stateless component");
3061
2974
  contents = `
3062
2975
  import { app } from "@hypen-space/core";
3063
2976
  const _module = app.defineState({}).build();
@@ -3084,19 +2997,19 @@ var plugin_default = hypenPlugin;
3084
2997
  // src/components/builtin.ts
3085
2998
  init_app();
3086
2999
  init_logger();
3087
- var log13 = frameworkLoggers.router;
3000
+ var log12 = frameworkLoggers.router;
3088
3001
  var Router = app.defineState({
3089
3002
  currentPath: "/",
3090
3003
  matchedRoute: null,
3091
3004
  routeParams: {}
3092
3005
  }, { name: "__Router" }).onCreated((state, context) => {
3093
3006
  if (!context) {
3094
- log13.error("Requires global context");
3007
+ log12.error("Requires global context");
3095
3008
  return;
3096
3009
  }
3097
3010
  const router = context.__router;
3098
3011
  if (!router) {
3099
- log13.error("Router not found in context");
3012
+ log12.error("Router not found in context");
3100
3013
  return;
3101
3014
  }
3102
3015
  const hypenEngine = context.__hypenEngine;
@@ -3118,13 +3031,13 @@ var Router = app.defineState({
3118
3031
  try {
3119
3032
  await hypenEngine.renderLazyRoute(routePath, componentName, htmlEl);
3120
3033
  } catch (err) {
3121
- log13.error(`Failed to render route ${routePath}:`, err);
3034
+ log12.error(`Failed to render route ${routePath}:`, err);
3122
3035
  }
3123
3036
  }
3124
3037
  }
3125
3038
  }
3126
3039
  if (!matchFound) {
3127
- log13.warn(`No route matched path: ${currentPath}. Available routes:`, Array.from(routeElements).map((el) => el.dataset.routePath));
3040
+ log12.warn(`No route matched path: ${currentPath}. Available routes:`, Array.from(routeElements).map((el) => el.dataset.routePath));
3128
3041
  }
3129
3042
  };
3130
3043
  setTimeout(() => {
@@ -3143,7 +3056,7 @@ var Link = app.defineState({
3143
3056
  }, { name: "__Link" }).onAction("navigate", ({ state, context }) => {
3144
3057
  const router = context?.__router;
3145
3058
  if (!router) {
3146
- log13.error("Link requires router context");
3059
+ log12.error("Link requires router context");
3147
3060
  return;
3148
3061
  }
3149
3062
  const targetPath = state.to;
@@ -3293,16 +3206,14 @@ export {
3293
3206
  HypenAppBuilder,
3294
3207
  HypenApp,
3295
3208
  Err,
3296
- Engine,
3297
3209
  DisposableStack,
3298
3210
  DisposableMixin,
3299
3211
  ConsoleRenderer,
3300
3212
  ConnectionError,
3301
3213
  ComponentResolver,
3302
3214
  ComponentLoader,
3303
- Engine2 as BrowserEngine,
3304
3215
  BaseRenderer,
3305
3216
  ActionError
3306
3217
  };
3307
3218
 
3308
- //# debugId=1C99977DF3016DFC64756E2164756E21
3219
+ //# debugId=C03FB9EC8278859464756E2164756E21