@hypen-space/core 0.4.956 → 0.5.2

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 (96) hide show
  1. package/README.md +5 -8
  2. package/dist/app.js +13 -1168
  3. package/dist/app.js.map +3 -9
  4. package/dist/chunk-00zt3w6e.js +58 -0
  5. package/dist/chunk-00zt3w6e.js.map +10 -0
  6. package/dist/chunk-3zvyth50.js +87 -0
  7. package/dist/chunk-3zvyth50.js.map +10 -0
  8. package/dist/chunk-5gtx3pza.js +11 -0
  9. package/dist/chunk-5gtx3pza.js.map +9 -0
  10. package/dist/chunk-7222131a.js +191 -0
  11. package/dist/chunk-7222131a.js.map +10 -0
  12. package/dist/chunk-avchpwhf.js +200 -0
  13. package/dist/chunk-avchpwhf.js.map +10 -0
  14. package/dist/chunk-ec0tgy9a.js +154 -0
  15. package/dist/chunk-ec0tgy9a.js.map +10 -0
  16. package/dist/chunk-fnss4dm2.js +479 -0
  17. package/dist/chunk-fnss4dm2.js.map +10 -0
  18. package/dist/chunk-g2gz9pgg.js +133 -0
  19. package/dist/chunk-g2gz9pgg.js.map +10 -0
  20. package/dist/chunk-g72v6mmf.js +341 -0
  21. package/dist/chunk-g72v6mmf.js.map +10 -0
  22. package/dist/chunk-g7g3jxty.js +351 -0
  23. package/dist/chunk-g7g3jxty.js.map +10 -0
  24. package/dist/chunk-g830re2c.js +50 -0
  25. package/dist/chunk-g830re2c.js.map +10 -0
  26. package/dist/chunk-gtwh1sg7.js +127 -0
  27. package/dist/chunk-gtwh1sg7.js.map +10 -0
  28. package/dist/chunk-k12cwfst.js +72 -0
  29. package/dist/chunk-k12cwfst.js.map +10 -0
  30. package/dist/chunk-mbqqsz6r.js +128 -0
  31. package/dist/chunk-mbqqsz6r.js.map +10 -0
  32. package/dist/chunk-ne632swm.js +209 -0
  33. package/dist/chunk-ne632swm.js.map +10 -0
  34. package/dist/chunk-p3pj1bax.js +206 -0
  35. package/dist/chunk-p3pj1bax.js.map +10 -0
  36. package/dist/chunk-vshdk2pw.js +56 -0
  37. package/dist/chunk-vshdk2pw.js.map +10 -0
  38. package/dist/chunk-wp2ebn2m.js +51 -0
  39. package/dist/chunk-wp2ebn2m.js.map +10 -0
  40. package/dist/chunk-x4qv4jm9.js +88 -0
  41. package/dist/chunk-x4qv4jm9.js.map +10 -0
  42. package/dist/chunk-ycxp4drd.js +211 -0
  43. package/dist/chunk-ycxp4drd.js.map +10 -0
  44. package/dist/components/builtin.js +14 -1246
  45. package/dist/components/builtin.js.map +3 -10
  46. package/dist/context.js +7 -362
  47. package/dist/context.js.map +3 -6
  48. package/dist/datasource.js +5 -76
  49. package/dist/datasource.js.map +3 -4
  50. package/dist/disposable.js +20 -367
  51. package/dist/disposable.js.map +3 -5
  52. package/dist/engine-base.d.ts +38 -2
  53. package/dist/engine-base.js +45 -412
  54. package/dist/engine-base.js.map +4 -6
  55. package/dist/events.js +7 -285
  56. package/dist/events.js.map +3 -5
  57. package/dist/hypen.js +8 -71
  58. package/dist/hypen.js.map +3 -4
  59. package/dist/index.browser.js +58 -2152
  60. package/dist/index.browser.js.map +3 -16
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +132 -3058
  63. package/dist/index.js.map +4 -22
  64. package/dist/logger.js +16 -220
  65. package/dist/logger.js.map +3 -4
  66. package/dist/managed-router.js +12 -1367
  67. package/dist/managed-router.js.map +3 -10
  68. package/dist/portable.js +6 -70
  69. package/dist/portable.js.map +3 -4
  70. package/dist/remote/client.js +9 -1010
  71. package/dist/remote/client.js.map +3 -8
  72. package/dist/remote/index.d.ts +2 -0
  73. package/dist/remote/index.js +511 -1066
  74. package/dist/remote/index.js.map +4 -9
  75. package/dist/remote/remote-session.d.ts +245 -0
  76. package/dist/remote/session.js +5 -147
  77. package/dist/remote/session.js.map +3 -4
  78. package/dist/renderer.js +7 -271
  79. package/dist/renderer.js.map +3 -5
  80. package/dist/resolver.js +5 -484
  81. package/dist/resolver.js.map +3 -5
  82. package/dist/result.js +25 -211
  83. package/dist/result.js.map +3 -4
  84. package/dist/retry.js +10 -336
  85. package/dist/retry.js.map +3 -5
  86. package/dist/router.js +9 -807
  87. package/dist/router.js.map +3 -8
  88. package/dist/state.js +10 -274
  89. package/dist/state.js.map +3 -5
  90. package/dist/validate.d.ts +19 -0
  91. package/package.json +13 -1
  92. package/src/engine-base.ts +87 -4
  93. package/src/index.ts +5 -0
  94. package/src/remote/index.ts +16 -0
  95. package/src/remote/remote-session.ts +998 -0
  96. package/src/validate.ts +37 -0
@@ -0,0 +1,479 @@
1
+ import {
2
+ DataSourceManager
3
+ } from "./chunk-vshdk2pw.js";
4
+ import {
5
+ ActionError,
6
+ Err,
7
+ HypenError,
8
+ Ok
9
+ } from "./chunk-7222131a.js";
10
+ import {
11
+ createObservableState,
12
+ getStateSnapshot
13
+ } from "./chunk-ycxp4drd.js";
14
+ import {
15
+ createLogger
16
+ } from "./chunk-avchpwhf.js";
17
+ import {
18
+ __require
19
+ } from "./chunk-5gtx3pza.js";
20
+
21
+ // src/app.ts
22
+ var log = createLogger("ModuleInstance");
23
+
24
+ class HypenAppBuilder {
25
+ initialState;
26
+ options;
27
+ createdHandler;
28
+ activatedHandler;
29
+ deactivatedHandler;
30
+ actionHandlers = new Map;
31
+ destroyedHandler;
32
+ disconnectHandler;
33
+ reconnectHandler;
34
+ expireHandler;
35
+ errorHandler;
36
+ template;
37
+ _registry;
38
+ dataSourceEntries = [];
39
+ _stateStore;
40
+ constructor(initialState, options, registry) {
41
+ this.initialState = initialState;
42
+ this.options = options || {};
43
+ this._registry = registry;
44
+ }
45
+ onCreated(fn) {
46
+ this.createdHandler = fn;
47
+ return this;
48
+ }
49
+ onAction(name, fn) {
50
+ this.actionHandlers.set(name, fn);
51
+ return this;
52
+ }
53
+ onActivated(fn) {
54
+ this.activatedHandler = fn;
55
+ return this;
56
+ }
57
+ onDeactivated(fn) {
58
+ this.deactivatedHandler = fn;
59
+ return this;
60
+ }
61
+ onDestroyed(fn) {
62
+ this.destroyedHandler = fn;
63
+ return this;
64
+ }
65
+ onDisconnect(fn) {
66
+ this.disconnectHandler = fn;
67
+ return this;
68
+ }
69
+ onReconnect(fn) {
70
+ this.reconnectHandler = fn;
71
+ return this;
72
+ }
73
+ onExpire(fn) {
74
+ this.expireHandler = fn;
75
+ return this;
76
+ }
77
+ onError(fn) {
78
+ this.errorHandler = fn;
79
+ return this;
80
+ }
81
+ persist(store) {
82
+ this._stateStore = store;
83
+ return this;
84
+ }
85
+ useDataSource(plugin, config) {
86
+ this.dataSourceEntries.push({ plugin, config });
87
+ return this;
88
+ }
89
+ ui(template) {
90
+ this.template = template;
91
+ return this.build();
92
+ }
93
+ uiFile(path) {
94
+ const fs = (() => ({}));
95
+ this.template = fs.readFileSync(path, "utf-8").trim();
96
+ return this.build();
97
+ }
98
+ build() {
99
+ const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
100
+ const definition = {
101
+ name: this.options.name,
102
+ actions: Array.from(this.actionHandlers.keys()),
103
+ stateKeys,
104
+ persist: this.options.persist,
105
+ version: this.options.version,
106
+ initialState: this.initialState,
107
+ template: this.template,
108
+ dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
109
+ stateStore: this._stateStore,
110
+ handlers: {
111
+ onCreated: this.createdHandler,
112
+ onActivated: this.activatedHandler,
113
+ onDeactivated: this.deactivatedHandler,
114
+ onAction: this.actionHandlers,
115
+ onDestroyed: this.destroyedHandler,
116
+ onDisconnect: this.disconnectHandler,
117
+ onReconnect: this.reconnectHandler,
118
+ onExpire: this.expireHandler,
119
+ onError: this.errorHandler
120
+ }
121
+ };
122
+ if (this.options.name && this._registry) {
123
+ this._registry.set(this.options.name, definition);
124
+ }
125
+ return definition;
126
+ }
127
+ }
128
+
129
+ class HypenApp {
130
+ _registry = new Map;
131
+ defineState(initial, options) {
132
+ return new HypenAppBuilder(initial, options, this._registry);
133
+ }
134
+ module(name) {
135
+ const registry = this._registry;
136
+ return {
137
+ defineState: (initial, options) => {
138
+ return new HypenAppBuilder(initial, { ...options, name }, registry);
139
+ }
140
+ };
141
+ }
142
+ get(name) {
143
+ return this._registry.get(name);
144
+ }
145
+ has(name) {
146
+ return this._registry.has(name);
147
+ }
148
+ get components() {
149
+ return this._registry;
150
+ }
151
+ getNames() {
152
+ return Array.from(this._registry.keys());
153
+ }
154
+ get size() {
155
+ return this._registry.size;
156
+ }
157
+ unregister(name) {
158
+ this._registry.delete(name);
159
+ }
160
+ clear() {
161
+ this._registry.clear();
162
+ }
163
+ }
164
+ var app = new HypenApp;
165
+
166
+ class HypenModuleInstance {
167
+ engine;
168
+ definition;
169
+ state;
170
+ isDestroyed = false;
171
+ isActive = false;
172
+ router;
173
+ globalContext;
174
+ stateChangeCallbacks = [];
175
+ dataSourceManager;
176
+ dataSourceAccessor = {};
177
+ stateStore;
178
+ currentPersistKey = null;
179
+ persistDebounceTimer;
180
+ sessionId;
181
+ moduleKey = "";
182
+ constructor(engine, definition, router, globalContext, sessionId) {
183
+ this.engine = engine;
184
+ this.definition = definition;
185
+ this.router = router ?? null;
186
+ this.globalContext = globalContext;
187
+ this.sessionId = sessionId ?? crypto.randomUUID();
188
+ this.stateStore = definition.stateStore;
189
+ const moduleKey = (definition.name || "").toLowerCase();
190
+ this.moduleKey = moduleKey;
191
+ this.state = createObservableState(definition.initialState, {
192
+ onChange: (change) => {
193
+ this.engine.updateStateSparse(moduleKey || null, change.paths, change.newValues);
194
+ this.stateChangeCallbacks.forEach((cb) => cb());
195
+ this.persistIfNeeded();
196
+ }
197
+ });
198
+ const snapshot = getStateSnapshot(this.state);
199
+ if (moduleKey) {
200
+ this.engine.registerModule(moduleKey, definition.actions, definition.stateKeys, snapshot);
201
+ } else {
202
+ this.engine.setModule("AnonymousModule", definition.actions, definition.stateKeys, snapshot);
203
+ }
204
+ for (const [actionName, handler] of definition.handlers.onAction) {
205
+ log.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
206
+ this.engine.onAction(actionName, async (action) => {
207
+ log.debug(`Action handler fired: ${actionName}`, action);
208
+ const actionCtx = {
209
+ name: action.name,
210
+ payload: action.payload,
211
+ sender: action.sender
212
+ };
213
+ const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
214
+ const result = await this.executeAction(actionName, handler, {
215
+ action: actionCtx,
216
+ state: this.state,
217
+ context,
218
+ dataSources: this.dataSourceAccessor
219
+ });
220
+ if (!result.ok) {
221
+ const shouldRethrow = await this.handleError(result.error, { actionName });
222
+ if (shouldRethrow) {
223
+ throw result.error;
224
+ }
225
+ } else {
226
+ log.debug(`Action handler completed: ${actionName}`);
227
+ }
228
+ });
229
+ }
230
+ this.engine.onAction("__hypen_bind", (action) => {
231
+ const payload = action.payload;
232
+ if (!payload?.path)
233
+ return;
234
+ const segments = payload.path.split(".");
235
+ let target = this.state;
236
+ for (let i = 0;i < segments.length - 1; i++) {
237
+ const seg = segments[i];
238
+ target = target?.[seg];
239
+ if (target == null)
240
+ return;
241
+ }
242
+ const lastSeg = segments[segments.length - 1];
243
+ target[lastSeg] = payload.value;
244
+ });
245
+ this._readyPromise = this.callCreatedHandler();
246
+ }
247
+ _readyPromise = Promise.resolve();
248
+ async waitForReady() {
249
+ await this._readyPromise;
250
+ }
251
+ async activate() {
252
+ if (this.isDestroyed || this.isActive)
253
+ return;
254
+ await this._readyPromise;
255
+ if (this.isDestroyed || this.isActive)
256
+ return;
257
+ this.isActive = true;
258
+ if (this.definition.handlers.onActivated) {
259
+ const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
260
+ try {
261
+ await this.definition.handlers.onActivated(this.state, context);
262
+ } catch (e) {
263
+ const error = e instanceof HypenError ? e : new ActionError("onActivated", e);
264
+ const shouldRethrow = await this.handleError(error, { lifecycle: "activated" });
265
+ if (shouldRethrow) {
266
+ throw error;
267
+ }
268
+ }
269
+ }
270
+ }
271
+ async deactivate() {
272
+ if (this.isDestroyed || !this.isActive)
273
+ return;
274
+ this.isActive = false;
275
+ if (this.definition.handlers.onDeactivated) {
276
+ const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
277
+ try {
278
+ await this.definition.handlers.onDeactivated(this.state, context);
279
+ } catch (e) {
280
+ const error = e instanceof HypenError ? e : new ActionError("onDeactivated", e);
281
+ const shouldRethrow = await this.handleError(error, { lifecycle: "deactivated" });
282
+ if (shouldRethrow) {
283
+ throw error;
284
+ }
285
+ }
286
+ }
287
+ }
288
+ createGlobalContextAPI() {
289
+ if (!this.globalContext) {
290
+ throw new Error("Global context not available");
291
+ }
292
+ const ctx = this.globalContext;
293
+ const api = {
294
+ getModule: (id) => ctx.getModule(id),
295
+ hasModule: (id) => ctx.hasModule(id),
296
+ getModuleIds: () => ctx.getModuleIds(),
297
+ getGlobalState: () => ctx.getGlobalState(),
298
+ emit: (event, payload) => ctx.emit(event, payload),
299
+ on: (event, handler) => ctx.on(event, handler),
300
+ router: this.router
301
+ };
302
+ const ctxRecord = ctx;
303
+ if (ctxRecord.__hypenEngine) {
304
+ api.__hypenEngine = ctxRecord.__hypenEngine;
305
+ }
306
+ return api;
307
+ }
308
+ async executeAction(actionName, handler, ctx) {
309
+ try {
310
+ const result = handler(ctx);
311
+ await result;
312
+ return Ok(undefined);
313
+ } catch (e) {
314
+ return Err(new ActionError(actionName, e));
315
+ }
316
+ }
317
+ async handleError(error, context) {
318
+ const errorCtx = {
319
+ error,
320
+ state: this.state,
321
+ actionName: context.actionName,
322
+ lifecycle: context.lifecycle
323
+ };
324
+ if (this.definition.handlers.onError) {
325
+ try {
326
+ const result = await this.definition.handlers.onError(errorCtx);
327
+ if (result && typeof result === "object") {
328
+ if ("handled" in result && result.handled) {
329
+ return false;
330
+ }
331
+ if ("rethrow" in result && result.rethrow) {
332
+ return true;
333
+ }
334
+ }
335
+ } catch (handlerError) {
336
+ log.error("Error in onError handler:", handlerError);
337
+ }
338
+ }
339
+ if (this.globalContext) {
340
+ const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
341
+ this.globalContext.emit("error", {
342
+ message: error.message,
343
+ error,
344
+ context: eventContext
345
+ });
346
+ }
347
+ log.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
348
+ return false;
349
+ }
350
+ persistIfNeeded() {
351
+ if (!this.stateStore)
352
+ return;
353
+ const store = this.stateStore;
354
+ const newKey = store.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
355
+ if (newKey && !this.currentPersistKey) {
356
+ this.activatePersistence(newKey);
357
+ return;
358
+ }
359
+ if (!newKey && this.currentPersistKey) {
360
+ this.currentPersistKey = null;
361
+ return;
362
+ }
363
+ if (newKey && newKey !== this.currentPersistKey) {
364
+ this.activatePersistence(newKey);
365
+ return;
366
+ }
367
+ if (!this.currentPersistKey)
368
+ return;
369
+ clearTimeout(this.persistDebounceTimer);
370
+ this.persistDebounceTimer = setTimeout(() => {
371
+ const snapshot = getStateSnapshot(this.state);
372
+ store.save(this.currentPersistKey, snapshot);
373
+ }, 50);
374
+ }
375
+ async activatePersistence(key) {
376
+ this.currentPersistKey = key;
377
+ const stored = await this.stateStore.load(key);
378
+ if (stored) {
379
+ const merged = { ...this.definition.initialState, ...stored };
380
+ Object.assign(this.state, merged);
381
+ } else {
382
+ const snapshot = getStateSnapshot(this.state);
383
+ await this.stateStore.save(key, snapshot);
384
+ }
385
+ }
386
+ async callCreatedHandler() {
387
+ if (this.stateStore) {
388
+ const key = this.stateStore.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
389
+ if (key) {
390
+ this.currentPersistKey = key;
391
+ const stored = await this.stateStore.load(key);
392
+ if (stored) {
393
+ const merged = { ...this.definition.initialState, ...stored };
394
+ Object.assign(this.state, merged);
395
+ }
396
+ }
397
+ }
398
+ if (this.definition.dataSources?.length) {
399
+ const dsEngine = this.engine;
400
+ this.dataSourceManager = new DataSourceManager(dsEngine);
401
+ for (const { plugin, config } of this.definition.dataSources) {
402
+ try {
403
+ await this.dataSourceManager.use(plugin, config);
404
+ this.dataSourceAccessor[plugin.name] = new Proxy(plugin, {
405
+ get(target, prop) {
406
+ if (typeof prop === "string" && !(prop in target)) {
407
+ return (...args) => target.call(prop, ...args);
408
+ }
409
+ return target[prop];
410
+ }
411
+ });
412
+ } catch (e) {
413
+ log.error(`Failed to connect data source "${plugin.name}":`, e);
414
+ }
415
+ }
416
+ }
417
+ if (this.definition.handlers.onCreated) {
418
+ const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
419
+ try {
420
+ await this.definition.handlers.onCreated(this.state, context);
421
+ } catch (e) {
422
+ const error = e instanceof HypenError ? e : new ActionError("onCreated", e);
423
+ const shouldRethrow = await this.handleError(error, { lifecycle: "created" });
424
+ if (shouldRethrow) {
425
+ throw error;
426
+ }
427
+ }
428
+ }
429
+ }
430
+ onStateChange(callback) {
431
+ this.stateChangeCallbacks.push(callback);
432
+ }
433
+ async destroy() {
434
+ if (this.isDestroyed)
435
+ return;
436
+ if (this.isActive) {
437
+ await this.deactivate();
438
+ }
439
+ if (this.currentPersistKey && this.stateStore) {
440
+ clearTimeout(this.persistDebounceTimer);
441
+ const snapshot = getStateSnapshot(this.state);
442
+ await this.stateStore.save(this.currentPersistKey, snapshot);
443
+ }
444
+ if (this.dataSourceManager) {
445
+ try {
446
+ await this.dataSourceManager.disconnectAll();
447
+ } catch (e) {
448
+ log.error("Error disconnecting data sources:", e);
449
+ }
450
+ this.dataSourceManager = undefined;
451
+ this.dataSourceAccessor = {};
452
+ }
453
+ if (this.definition.handlers.onDestroyed) {
454
+ try {
455
+ await this.definition.handlers.onDestroyed(this.state);
456
+ } catch (e) {
457
+ const error = e instanceof HypenError ? e : new ActionError("onDestroyed", e);
458
+ const shouldRethrow = await this.handleError(error, { lifecycle: "destroyed" });
459
+ if (shouldRethrow) {
460
+ throw error;
461
+ }
462
+ }
463
+ }
464
+ this.isDestroyed = true;
465
+ }
466
+ getState() {
467
+ return getStateSnapshot(this.state);
468
+ }
469
+ getLiveState() {
470
+ return this.state;
471
+ }
472
+ updateState(patch) {
473
+ Object.assign(this.state, patch);
474
+ }
475
+ }
476
+
477
+ export { HypenAppBuilder, HypenApp, app, HypenModuleInstance };
478
+
479
+ //# debugId=CE88A330FC69253464756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/app.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Hypen App Builder API\n * Implements the stateful module system from RFC-0001\n */\n\nimport type { Action } from \"./types.js\";\nimport type { Session } from \"./remote/types.js\";\nimport { type Result, Ok, Err, fromPromise, ActionError, HypenError } from \"./result.js\";\nimport { DataSourceManager, type DataSourcePlugin, type IDataSourceEngine } from \"./datasource.js\";\nimport type { StateStore } from \"./persistence.js\";\n\n// Interface for engine compatibility (works with both engine.js and engine.browser.js)\nexport interface IEngine {\n setModule(name: string, actions: string[], stateKeys: string[], initialState: unknown): void;\n registerModule(name: string, actions: string[], stateKeys: string[], initialState: unknown): void;\n onAction(actionName: string, handler: (action: Action) => void | Promise<void>): void;\n /**\n * Apply a sparse state update.\n *\n * @param scope Lowercase module name to target a named module registered\n * via `registerModule`. Pass `null` (or empty string) to\n * target the primary module set via `setModule`.\n * @param paths Changed state paths (relative to the targeted module).\n * @param values Map of `path -> new value`.\n */\n updateStateSparse(\n scope: string | null,\n paths: string[],\n values: Record<string, unknown>\n ): void;\n}\n\nimport { createObservableState, type StateChange, getStateSnapshot } from \"./state.js\";\nimport type { HypenRouter } from \"./router.js\";\nimport type { HypenGlobalContext, ModuleReference } from \"./context.js\";\nimport { createLogger } from \"./logger.js\";\n\nconst log = createLogger(\"ModuleInstance\");\n\nexport type ActionContext<P = unknown> = {\n name: string;\n payload?: P;\n sender?: string;\n};\n\nexport type GlobalContext = {\n getModule: <T = unknown>(id: string) => ModuleReference<T>;\n hasModule: (id: string) => boolean;\n getModuleIds: () => string[];\n getGlobalState: () => Record<string, unknown>;\n emit: (event: string, payload?: unknown) => void;\n on: (event: string, handler: (payload?: unknown) => void) => () => void;\n router: HypenRouter | null;\n};\n\nexport type LifecycleHandler<T> = (\n state: T,\n context?: GlobalContext\n) => void | Promise<void>;\n\n/**\n * Action handler context - all parameters available explicitly\n */\nexport interface ActionHandlerContext<T, P = unknown> {\n action: ActionContext<P>;\n state: T;\n context: GlobalContext;\n /** Access to registered data source plugins for mutations */\n dataSources: DataSourceAccessor;\n}\n\n/**\n * Provides typed access to registered data source plugins.\n * Unknown method calls are forwarded to `plugin.call()`:\n * `dataSources.spacetime.sendMessage(text)` → `plugin.call(\"sendMessage\", text)`\n *\n * You can also call `.call()` directly if you prefer:\n * `dataSources.spacetime.call(\"sendMessage\", text)`\n */\nexport interface DataSourceAccessor {\n [providerName: string]: DataSourcePlugin & Record<string, (...args: unknown[]) => Promise<unknown>>;\n}\n\n/**\n * Action handler - receives all context in a single object\n */\nexport type ActionHandler<T, P = unknown> = (ctx: ActionHandlerContext<T, P>) => void | Promise<void>;\n\n/**\n * Context passed to onDisconnect handler\n */\nexport interface DisconnectContext<T> {\n /** Current state snapshot (ready to serialize and save) */\n state: T;\n /** Session information */\n session: Session;\n}\n\n/**\n * Context passed to onReconnect handler\n */\nexport interface ReconnectContext<T> {\n /** Session information */\n session: Session;\n /** Call this with saved state to restore it */\n restore: (savedState: T) => void;\n}\n\n/**\n * Context passed to onExpire handler\n */\nexport interface ExpireContext {\n /** Session information */\n session: Session;\n}\n\n/**\n * Handler called when client disconnects (session still alive for TTL)\n */\nexport type DisconnectHandler<T> = (ctx: DisconnectContext<T>) => void | Promise<void>;\n\n/**\n * Handler called when client reconnects with existing session\n */\nexport type ReconnectHandler<T> = (ctx: ReconnectContext<T>) => void | Promise<void>;\n\n/**\n * Handler called when session TTL expires (client never reconnected)\n */\nexport type ExpireHandler = (ctx: ExpireContext) => void | Promise<void>;\n\n/**\n * Context passed to onError handler\n */\nexport interface ErrorContext<T> {\n /** The error that occurred */\n error: HypenError;\n /** Current state (for inspection, not mutation during error handling) */\n state: T;\n /** The action name if error occurred in an action handler */\n actionName?: string;\n /** The lifecycle phase if error occurred in a lifecycle handler */\n lifecycle?: \"created\" | \"activated\" | \"deactivated\" | \"destroyed\" | \"disconnect\" | \"reconnect\" | \"expire\";\n}\n\n/**\n * Error handler return type - controls error propagation\n */\nexport type ErrorHandlerResult =\n | void // Continue with default behavior (log + emit)\n | { handled: true } // Error was handled, skip default behavior\n | { retry: true } // Retry the operation (only for actions)\n | { rethrow: true }; // Re-throw the error\n\n/**\n * Handler called when an error occurs in the module\n */\nexport type ErrorHandler<T> = (ctx: ErrorContext<T>) => ErrorHandlerResult | Promise<ErrorHandlerResult>;\n\nexport interface HypenModuleDefinition<T = unknown> {\n name?: string;\n actions: string[];\n stateKeys: string[];\n persist?: boolean;\n version?: number;\n initialState: T;\n /**\n * Inline UI template for single-file components.\n * Set via the `.ui(hypen`...`)` method.\n */\n template?: string;\n /**\n * Data source plugins registered via `.useDataSource()`.\n * Each entry contains the plugin and its configuration.\n */\n dataSources?: Array<{ plugin: DataSourcePlugin; config: unknown }>;\n /**\n * Pluggable state store for persistence across sessions.\n * Set via the `.persist(store)` builder method.\n */\n stateStore?: StateStore<T>;\n handlers: {\n onCreated?: LifecycleHandler<T>;\n /**\n * Called every time the module becomes the active route target.\n * Runs after `onCreated` on first mount, and again on each re-mount\n * from the ManagedRouter's persistence cache. Use for data refresh,\n * subscription (re)connects, or any \"screen became visible\" work.\n */\n onActivated?: LifecycleHandler<T>;\n /**\n * Called every time the module stops being the active route target.\n * Runs before the module is cached for persistence OR before\n * `onDestroyed` if the module is being torn down. Use for pausing\n * timers, tearing down ephemeral subscriptions, etc.\n */\n onDeactivated?: LifecycleHandler<T>;\n onAction: Map<string, ActionHandler<T, any>>;\n onDestroyed?: LifecycleHandler<T>;\n /** Called when client disconnects (session persists for TTL) */\n onDisconnect?: DisconnectHandler<T>;\n /** Called when client reconnects with existing session */\n onReconnect?: ReconnectHandler<T>;\n /** Called when session TTL expires */\n onExpire?: ExpireHandler;\n /** Called when any error occurs in the module */\n onError?: ErrorHandler<T>;\n };\n}\n\n/**\n * Alias for HypenModuleDefinition for backward compatibility\n */\nexport type HypenModule<T = unknown> = HypenModuleDefinition<T>;\n\n/**\n * Builder for creating Hypen app modules\n */\nexport class HypenAppBuilder<T> {\n private initialState: T;\n private options: { persist?: boolean; version?: number; name?: string };\n private createdHandler?: LifecycleHandler<T>;\n private activatedHandler?: LifecycleHandler<T>;\n private deactivatedHandler?: LifecycleHandler<T>;\n private actionHandlers: Map<string, ActionHandler<T, any>> = new Map();\n private destroyedHandler?: LifecycleHandler<T>;\n private disconnectHandler?: DisconnectHandler<T>;\n private reconnectHandler?: ReconnectHandler<T>;\n private expireHandler?: ExpireHandler;\n private errorHandler?: ErrorHandler<T>;\n private template?: string;\n private _registry?: Map<string, HypenModuleDefinition>;\n private dataSourceEntries: Array<{ plugin: DataSourcePlugin; config: unknown }> = [];\n private _stateStore?: StateStore<T>;\n\n constructor(\n initialState: T,\n options?: { persist?: boolean; version?: number; name?: string },\n registry?: Map<string, HypenModuleDefinition>\n ) {\n this.initialState = initialState;\n this.options = options || {};\n this._registry = registry;\n }\n\n /**\n * Register a handler for module creation\n */\n onCreated(fn: LifecycleHandler<T>): this {\n this.createdHandler = fn;\n return this;\n }\n\n /**\n * Register a handler for a specific action.\n * Optionally provide a payload type parameter for typed action payloads.\n *\n * @example\n * ```typescript\n * // Typed payload:\n * .onAction<{ amount: number }>(\"add\", ({ action, state }) => {\n * action.payload.amount; // fully typed\n * })\n *\n * // Untyped (payload is unknown):\n * .onAction(\"add\", ({ action, state }) => {\n * action.payload; // unknown\n * })\n * ```\n */\n onAction<P = unknown>(name: string, fn: ActionHandler<T, P>): this {\n this.actionHandlers.set(name, fn as ActionHandler<T, any>);\n return this;\n }\n\n /**\n * Register a handler that runs every time the module becomes the active\n * route target.\n *\n * Unlike `onCreated`, which only runs once per module instance, `onActivated`\n * runs on **every** mount — the first one (right after `onCreated`) and\n * every subsequent re-entry when a cached module is restored by\n * `ManagedRouter`. Use this hook for data refresh, (re)connecting\n * subscriptions, starting timers, and any \"screen became visible\" work\n * that should happen on every navigation to this route.\n *\n * @example\n * ```typescript\n * app\n * .defineState({ items: [], loading: false })\n * .onActivated(async (state) => {\n * state.loading = true;\n * state.items = await fetchItems();\n * state.loading = false;\n * });\n * ```\n */\n onActivated(fn: LifecycleHandler<T>): this {\n this.activatedHandler = fn;\n return this;\n }\n\n /**\n * Register a handler that runs every time the module stops being the\n * active route target.\n *\n * Runs before the module is cached for persistence (when the user\n * navigates to another route) OR before `onDestroyed` if the module is\n * being torn down. Use this for pausing timers, unsubscribing from\n * ephemeral streams, and any \"screen became hidden\" cleanup.\n */\n onDeactivated(fn: LifecycleHandler<T>): this {\n this.deactivatedHandler = fn;\n return this;\n }\n\n /**\n * Register a handler for module destruction\n */\n onDestroyed(fn: LifecycleHandler<T>): this {\n this.destroyedHandler = fn;\n return this;\n }\n\n /**\n * Register a handler for client disconnection\n * Called when client disconnects; session persists for TTL\n * Use this to save state for later restoration\n */\n onDisconnect(fn: DisconnectHandler<T>): this {\n this.disconnectHandler = fn;\n return this;\n }\n\n /**\n * Register a handler for session reconnection\n * Called when client reconnects with an existing session ID\n * Use restore() to hydrate state from saved data\n */\n onReconnect(fn: ReconnectHandler<T>): this {\n this.reconnectHandler = fn;\n return this;\n }\n\n /**\n * Register a handler for session expiration\n * Called when TTL expires and client never reconnected\n * Use this to clean up stored state\n */\n onExpire(fn: ExpireHandler): this {\n this.expireHandler = fn;\n return this;\n }\n\n /**\n * Register an error handler for the module\n * Called when any error occurs in action handlers or lifecycle hooks\n *\n * @example\n * ```typescript\n * app\n * .defineState({ count: 0 })\n * .onAction(\"increment\", ({ state }) => {\n * if (state.count > 100) throw new Error(\"Count too high\");\n * state.count += 1;\n * })\n * .onError(({ error, actionName, state }) => {\n * console.error(`Error in ${actionName}:`, error.message);\n * // Optionally recover\n * if (error.message.includes(\"too high\")) {\n * return { handled: true }; // Don't propagate\n * }\n * // Or retry\n * // return { retry: true };\n * })\n * .build();\n * ```\n *\n * @param fn - Error handler function\n * @returns Builder for chaining\n */\n onError(fn: ErrorHandler<T>): this {\n this.errorHandler = fn;\n return this;\n }\n\n /**\n * Attach a persistent state store.\n * The store handles save/restore lifecycle automatically.\n *\n * @example\n * ```typescript\n * import { durableObjectStore, withKey } from \"@hypen-space/cf\";\n *\n * app\n * .defineState({ user: null, todos: [] }, { name: \"TodoList\" })\n * .persist(durableObjectStore(withKey(state => state.user?.id)))\n * .build();\n * ```\n */\n persist(store: StateStore<T>): this {\n this._stateStore = store;\n return this;\n }\n\n /**\n * Register a data source plugin for live database subscriptions.\n *\n * @example\n * ```typescript\n * import { SpacetimeDBPlugin } from \"@hypen-space/plugin-spacetimedb\";\n *\n * export default app\n * .defineState({ messageText: \"\" })\n * .useDataSource(new SpacetimeDBPlugin(), {\n * uri: \"ws://localhost:3000\",\n * moduleName: \"chat\",\n * tables: [\"user\", \"message\"],\n * })\n * .onAction(\"sendMessage\", async ({ state, dataSources }) => {\n * await dataSources.spacetime.sendMessage(state.messageText);\n * state.messageText = \"\";\n * })\n * .build();\n * ```\n *\n * In Hypen DSL, bind to data source tables with `@provider.table`:\n * ```hypen\n * ForEach(items: @spacetime.message, key: \"id\") {\n * Text(\"@{item.text}\")\n * }\n * ```\n */\n useDataSource<C>(plugin: DataSourcePlugin<C>, config: C): this {\n this.dataSourceEntries.push({ plugin: plugin as DataSourcePlugin, config });\n return this;\n }\n\n /**\n * Define the component's UI template inline (single-file component).\n *\n * Use with the `hypen` tagged template literal and binding proxies:\n *\n * @example\n * ```typescript\n * import { app, hypen, state } from \"@hypen-space/core\";\n *\n * export default app\n * .defineState({ count: 0 })\n * .onAction(\"increment\", ({ state }) => {\n * state.count += 1;\n * })\n * .ui(hypen`\n * Column {\n * Text(\"Count: @{state.count}\")\n * Button { Text(\"+\") }\n * .onClick(\"@actions.increment\")\n * }\n * `);\n * ```\n *\n * @param template - The Hypen DSL template string\n * @returns The built module definition (calls build() internally)\n */\n ui(template: string): HypenModuleDefinition<T> {\n this.template = template;\n return this.build();\n }\n\n /**\n * Load a UI template from a .hypen file on disk.\n *\n * @example\n * ```typescript\n * import { app } from \"@hypen-space/core\";\n *\n * export default app\n * .defineState({ count: 0 })\n * .onAction(\"increment\", ({ state }) => { state.count += 1; })\n * .uiFile(\"./counter.hypen\");\n * ```\n *\n * @param path - Path to a .hypen template file\n * @returns The built module definition (calls build() internally)\n */\n uiFile(path: string): HypenModuleDefinition<T> {\n const fs = require(\"fs\");\n this.template = fs.readFileSync(path, \"utf-8\").trim();\n return this.build();\n }\n\n /**\n * Build the module definition\n */\n build(): HypenModuleDefinition<T> {\n // Safe way to get keys from initialState\n const stateKeys = this.initialState !== null && typeof this.initialState === 'object'\n ? Object.keys(this.initialState)\n : [];\n\n const definition: HypenModuleDefinition<T> = {\n name: this.options.name,\n actions: Array.from(this.actionHandlers.keys()),\n stateKeys,\n persist: this.options.persist,\n version: this.options.version,\n initialState: this.initialState,\n template: this.template,\n dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,\n stateStore: this._stateStore,\n handlers: {\n onCreated: this.createdHandler,\n onActivated: this.activatedHandler,\n onDeactivated: this.deactivatedHandler,\n onAction: this.actionHandlers,\n onDestroyed: this.destroyedHandler,\n onDisconnect: this.disconnectHandler,\n onReconnect: this.reconnectHandler,\n onExpire: this.expireHandler,\n onError: this.errorHandler,\n },\n };\n\n // Auto-register in the app registry when the module has a name\n if (this.options.name && this._registry) {\n this._registry.set(this.options.name, definition as HypenModuleDefinition);\n }\n\n return definition;\n }\n}\n\n/**\n * Hypen App API — singleton factory and component registry.\n *\n * Modules built with a `name` are automatically registered here.\n * Consumers (ManagedRouter, RemoteServer, ComponentResolver) read from this\n * registry instead of requiring a separate ModuleRegistry instance.\n */\nexport class HypenApp {\n /** @internal */\n readonly _registry = new Map<string, HypenModuleDefinition>();\n\n /**\n * Define the initial state for a module\n */\n defineState<T>(\n initial: T,\n options?: { persist?: boolean; version?: number; name?: string }\n ): HypenAppBuilder<T> {\n return new HypenAppBuilder(initial, options, this._registry);\n }\n\n /**\n * Convenience: start a module builder with a name pre-set.\n *\n * @example\n * ```typescript\n * export default app\n * .module(\"Settings\")\n * .defineState({ theme: \"dark\" })\n * .ui(hypen`...`);\n * ```\n */\n module(name: string) {\n const registry = this._registry;\n return {\n defineState: <T>(\n initial: T,\n options?: { persist?: boolean; version?: number }\n ): HypenAppBuilder<T> => {\n return new HypenAppBuilder(initial, { ...options, name }, registry);\n },\n };\n }\n\n // ---------------------------------------------------------------------------\n // Registry API\n // ---------------------------------------------------------------------------\n\n /**\n * Get a registered module definition by component name.\n */\n get(name: string): HypenModuleDefinition | undefined {\n return this._registry.get(name);\n }\n\n /**\n * Check if a module definition is registered.\n */\n has(name: string): boolean {\n return this._registry.has(name);\n }\n\n /**\n * Read-only view of all registered component definitions.\n */\n get components(): ReadonlyMap<string, HypenModuleDefinition> {\n return this._registry;\n }\n\n /**\n * Get all registered component names.\n */\n getNames(): string[] {\n return Array.from(this._registry.keys());\n }\n\n /**\n * Number of registered definitions.\n */\n get size(): number {\n return this._registry.size;\n }\n\n /**\n * Unregister a module definition.\n */\n unregister(name: string): void {\n this._registry.delete(name);\n }\n\n /**\n * Clear all registered definitions.\n */\n clear(): void {\n this._registry.clear();\n }\n}\n\n/**\n * The main app instance for creating modules\n */\nexport const app = new HypenApp();\n\n/**\n * Module Instance - manages a running module with typed state\n */\nexport class HypenModuleInstance<T extends object = any> {\n private engine: IEngine;\n private definition: HypenModuleDefinition<T>;\n private state: T;\n private isDestroyed = false;\n /**\n * True when the module is currently the active route target (i.e.\n * `onActivated` has fired more recently than `onDeactivated`).\n * Used to make `activate()` / `deactivate()` idempotent so the\n * `ManagedRouter` can call them safely regardless of state.\n */\n private isActive = false;\n private router: HypenRouter | null;\n private globalContext?: HypenGlobalContext;\n private stateChangeCallbacks: Array<() => void> = [];\n private dataSourceManager?: DataSourceManager;\n private dataSourceAccessor: DataSourceAccessor = {};\n private stateStore?: StateStore<T>;\n private currentPersistKey: string | null = null;\n private persistDebounceTimer: any;\n private sessionId: string;\n /**\n * Lowercase name used to address this module in the engine. Empty string\n * for anonymous modules (which back the engine's primary module slot).\n */\n private moduleKey: string = \"\";\n\n constructor(\n engine: IEngine,\n definition: HypenModuleDefinition<T>,\n router?: HypenRouter | null,\n globalContext?: HypenGlobalContext,\n sessionId?: string\n ) {\n this.engine = engine;\n this.definition = definition;\n this.router = router ?? null;\n this.globalContext = globalContext;\n this.sessionId = sessionId ?? crypto.randomUUID();\n this.stateStore = definition.stateStore;\n\n // Lowercase module name — used as the engine-side scope key. Empty for\n // anonymous modules (which become the engine's primary module).\n const moduleKey = (definition.name || \"\").toLowerCase();\n this.moduleKey = moduleKey;\n\n // Create observable state that forwards changed paths to the engine.\n // Paths are passed through raw — the engine handles module scoping via\n // the IR's `module_scope` field derived from `module <Name> { ... }` in\n // the DSL.\n this.state = createObservableState<T>(definition.initialState as T & object, {\n onChange: (change: StateChange) => {\n this.engine.updateStateSparse(\n moduleKey || null,\n change.paths,\n change.newValues\n );\n this.stateChangeCallbacks.forEach(cb => cb());\n this.persistIfNeeded();\n },\n });\n\n // Register the module with the engine. Named modules go into the\n // secondary `engine.modules` slot under their lowercase name; anonymous\n // modules become the engine's primary module.\n const snapshot = getStateSnapshot(this.state);\n if (moduleKey) {\n this.engine.registerModule(\n moduleKey,\n definition.actions,\n definition.stateKeys,\n snapshot\n );\n } else {\n this.engine.setModule(\n \"AnonymousModule\",\n definition.actions,\n definition.stateKeys,\n snapshot\n );\n }\n\n // Register action handlers with flexible parameter support\n for (const [actionName, handler] of definition.handlers.onAction) {\n log.debug(`Registering action handler: ${actionName} for module ${definition.name}`);\n this.engine.onAction(actionName, async (action: Action) => {\n log.debug(`Action handler fired: ${actionName}`, action);\n\n const actionCtx: ActionContext = {\n name: action.name,\n payload: action.payload,\n sender: action.sender,\n };\n\n const context: GlobalContext | undefined = this.globalContext\n ? this.createGlobalContextAPI()\n : undefined;\n\n // Use Result type for error handling\n const result = await this.executeAction(actionName, handler, {\n action: actionCtx,\n state: this.state,\n context: context!,\n dataSources: this.dataSourceAccessor,\n });\n\n if (!result.ok) {\n const shouldRethrow = await this.handleError(result.error, { actionName });\n if (shouldRethrow) {\n throw result.error;\n }\n } else {\n log.debug(`Action handler completed: ${actionName}`);\n }\n });\n }\n\n // Auto-register __hypen_bind for .bind() two-way binding support\n this.engine.onAction(\"__hypen_bind\", (action: Action) => {\n const payload = action.payload as { path?: string; value?: unknown } | null;\n if (!payload?.path) return;\n\n const segments = payload.path.split(\".\");\n let target: any = this.state;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!;\n target = target?.[seg];\n if (target == null) return;\n }\n const lastSeg = segments[segments.length - 1]!;\n target[lastSeg] = payload.value;\n });\n\n // Call onCreated — store the promise so callers can await initialization\n this._readyPromise = this.callCreatedHandler();\n }\n\n /** Promise that resolves when onCreated handler has completed */\n private _readyPromise: Promise<void> = Promise.resolve();\n\n /**\n * Wait for the module's onCreated handler to complete.\n * Call this before renderSource to ensure state is fully initialized.\n */\n async waitForReady(): Promise<void> {\n await this._readyPromise;\n }\n\n /**\n * Mark the module as the active route target and fire `onActivated`.\n *\n * Idempotent: calling `activate()` on an already-active module is a\n * no-op. Internally awaits `waitForReady()` so that `onCreated` always\n * runs to completion before `onActivated` fires, regardless of who\n * calls this method and when.\n *\n * Called by `ManagedRouter` on every route mount — both fresh\n * constructions and re-mounts from the persistence cache.\n */\n async activate(): Promise<void> {\n if (this.isDestroyed || this.isActive) return;\n // Ensure onCreated finishes before onActivated — critical on fresh\n // mounts where the constructor kicks off onCreated asynchronously.\n await this._readyPromise;\n if (this.isDestroyed || this.isActive) return;\n this.isActive = true;\n if (this.definition.handlers.onActivated) {\n const context = this.globalContext ? this.createGlobalContextAPI() : undefined;\n try {\n await this.definition.handlers.onActivated(this.state, context);\n } catch (e) {\n const error = e instanceof HypenError ? e : new ActionError(\"onActivated\", e);\n const shouldRethrow = await this.handleError(error, { lifecycle: \"activated\" });\n if (shouldRethrow) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Mark the module as no longer the active route target and fire\n * `onDeactivated`.\n *\n * Idempotent: calling `deactivate()` on an inactive module is a no-op.\n * Called by `ManagedRouter` before persisting a module for later reuse\n * OR before destroying it.\n */\n async deactivate(): Promise<void> {\n if (this.isDestroyed || !this.isActive) return;\n this.isActive = false;\n if (this.definition.handlers.onDeactivated) {\n const context = this.globalContext ? this.createGlobalContextAPI() : undefined;\n try {\n await this.definition.handlers.onDeactivated(this.state, context);\n } catch (e) {\n const error = e instanceof HypenError ? e : new ActionError(\"onDeactivated\", e);\n const shouldRethrow = await this.handleError(error, { lifecycle: \"deactivated\" });\n if (shouldRethrow) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Create the global context API for this module\n */\n private createGlobalContextAPI(): GlobalContext {\n if (!this.globalContext) {\n throw new Error(\"Global context not available\");\n }\n\n const ctx = this.globalContext;\n const api: GlobalContext = {\n getModule: (id: string) => ctx.getModule(id),\n hasModule: (id: string) => ctx.hasModule(id),\n getModuleIds: () => ctx.getModuleIds(),\n getGlobalState: () => ctx.getGlobalState(),\n emit: (event: string, payload?: any) => ctx.emit(event, payload),\n on: (event: string, handler: (payload?: any) => void) =>\n ctx.on(event, handler),\n router: this.router,\n };\n\n // Expose hypen engine for built-in components (if available)\n const ctxRecord = ctx as unknown as Record<string, unknown>;\n if (ctxRecord.__hypenEngine) {\n (api as Record<string, unknown>).__hypenEngine = ctxRecord.__hypenEngine;\n }\n\n return api;\n }\n\n /**\n * Execute an action handler with Result-based error handling\n * Handles both synchronous throws and async rejections\n */\n private async executeAction(\n actionName: string,\n handler: ActionHandler<T, any>,\n ctx: ActionHandlerContext<T>\n ): Promise<Result<void, ActionError>> {\n try {\n // Wrap in try-catch to handle synchronous throws\n const result = handler(ctx);\n // Await in case handler returns a promise\n await result;\n return Ok(undefined);\n } catch (e) {\n return Err(new ActionError(actionName, e));\n }\n }\n\n /**\n * Handle an error with module-level error handler support\n * Falls back to default behavior (emit + log) if no handler or handler doesn't suppress\n * @returns true if the error should be rethrown\n */\n private async handleError(\n error: HypenError,\n context: { actionName?: string; lifecycle?: ErrorContext<T>[\"lifecycle\"] }\n ): Promise<boolean> {\n const errorCtx: ErrorContext<T> = {\n error,\n state: this.state,\n actionName: context.actionName,\n lifecycle: context.lifecycle,\n };\n\n // Call module-level error handler if defined\n if (this.definition.handlers.onError) {\n try {\n const result = await this.definition.handlers.onError(errorCtx);\n\n // Check if error was handled\n if (result && typeof result === \"object\") {\n if (\"handled\" in result && result.handled) {\n // Error was handled, skip default behavior\n return false;\n }\n if (\"rethrow\" in result && result.rethrow) {\n // Signal caller to rethrow\n return true;\n }\n // Note: 'retry' would need to be handled at the action execution level\n // For now, we just continue with default behavior\n }\n } catch (handlerError) {\n // Error in error handler - log and continue with default behavior\n log.error(\"Error in onError handler:\", handlerError);\n }\n }\n\n // Default behavior: emit to global context and log\n if (this.globalContext) {\n const eventContext = context.actionName\n ? `action:${context.actionName}`\n : context.lifecycle\n ? `lifecycle:${context.lifecycle}`\n : \"unknown\";\n\n this.globalContext.emit(\"error\", {\n message: error.message,\n error,\n context: eventContext,\n });\n }\n\n log.error(\n `${context.actionName ? `Action \"${context.actionName}\"` : `Lifecycle \"${context.lifecycle}\"`} error:`,\n error\n );\n\n return false;\n }\n\n /**\n * Re-evaluate the persistence key and debounce saves on state mutation.\n * Handles key transitions (null->value, value->null, value->different value).\n */\n private persistIfNeeded(): void {\n if (!this.stateStore) return;\n\n const store = this.stateStore;\n const newKey = store.resolveKey(\n this.state,\n this.definition.name || \"AnonymousModule\",\n this.sessionId\n );\n\n // Key transition: null -> value (e.g. login)\n if (newKey && !this.currentPersistKey) {\n this.activatePersistence(newKey);\n return;\n }\n\n // Key transition: value -> null (e.g. logout)\n if (!newKey && this.currentPersistKey) {\n this.currentPersistKey = null;\n return; // stop persisting, but don't delete stored data\n }\n\n // Key transition: value -> different value (e.g. account switch)\n if (newKey && newKey !== this.currentPersistKey) {\n this.activatePersistence(newKey);\n return;\n }\n\n // No key = no persistence\n if (!this.currentPersistKey) return;\n\n // Same key — debounced save\n clearTimeout(this.persistDebounceTimer);\n this.persistDebounceTimer = setTimeout(() => {\n const snapshot = getStateSnapshot(this.state);\n store.save(this.currentPersistKey!, snapshot);\n }, 50);\n }\n\n /**\n * Activate persistence for the given key: load stored state and merge.\n */\n private async activatePersistence(key: string): Promise<void> {\n this.currentPersistKey = key;\n const stored = await this.stateStore!.load(key);\n if (stored) {\n // Merge stored over initialState, then apply to live state\n const merged = { ...this.definition.initialState, ...stored };\n Object.assign(this.state, merged); // triggers re-render via proxy\n } else {\n // First time with this key — save current state\n const snapshot = getStateSnapshot(this.state);\n await this.stateStore!.save(key, snapshot);\n }\n }\n\n /**\n * Call the onCreated handler and connect data source plugins\n */\n private async callCreatedHandler(): Promise<void> {\n // Load persisted state before anything else\n if (this.stateStore) {\n const key = this.stateStore.resolveKey(\n this.state,\n this.definition.name || \"AnonymousModule\",\n this.sessionId\n );\n if (key) {\n this.currentPersistKey = key;\n const stored = await this.stateStore.load(key);\n if (stored) {\n const merged = { ...this.definition.initialState, ...stored };\n Object.assign(this.state, merged);\n }\n }\n }\n\n // Initialize data source plugins if any are registered\n if (this.definition.dataSources?.length) {\n // The engine must implement IDataSourceEngine methods\n const dsEngine = this.engine as unknown as IDataSourceEngine;\n this.dataSourceManager = new DataSourceManager(dsEngine);\n\n for (const { plugin, config } of this.definition.dataSources) {\n try {\n await this.dataSourceManager.use(plugin, config);\n // Wrap plugin in a Proxy so unknown method calls forward to plugin.call():\n // dataSources.spacetime.sendMessage(text) → plugin.call(\"sendMessage\", text)\n this.dataSourceAccessor[plugin.name] = new Proxy(plugin, {\n get(target, prop) {\n if (typeof prop === 'string' && !(prop in target)) {\n return (...args: unknown[]) => target.call(prop, ...args);\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop];\n },\n }) as typeof plugin & Record<string, (...args: unknown[]) => Promise<unknown>>;\n } catch (e) {\n log.error(`Failed to connect data source \"${plugin.name}\":`, e);\n }\n }\n }\n\n if (this.definition.handlers.onCreated) {\n const context = this.globalContext ? this.createGlobalContextAPI() : undefined;\n try {\n await this.definition.handlers.onCreated(this.state, context);\n } catch (e) {\n const error = e instanceof HypenError ? e : new ActionError(\"onCreated\", e);\n const shouldRethrow = await this.handleError(error, { lifecycle: \"created\" });\n if (shouldRethrow) {\n throw error;\n }\n }\n }\n }\n\n /**\n * Register a callback to be notified when state changes\n */\n onStateChange(callback: () => void): void {\n this.stateChangeCallbacks.push(callback);\n }\n\n /**\n * Destroy the module instance and disconnect all data source plugins\n */\n async destroy(): Promise<void> {\n if (this.isDestroyed) return;\n\n // If this module is still marked as active (e.g. destroy() was called\n // without a preceding deactivate()), fire onDeactivated first so the\n // lifecycle order is always: ...onActivated → onDeactivated → onDestroyed.\n if (this.isActive) {\n await this.deactivate();\n }\n\n // Flush any pending persistence writes before shutdown\n if (this.currentPersistKey && this.stateStore) {\n clearTimeout(this.persistDebounceTimer);\n const snapshot = getStateSnapshot(this.state);\n await this.stateStore.save(this.currentPersistKey, snapshot);\n }\n\n // Disconnect all data source plugins\n if (this.dataSourceManager) {\n try {\n await this.dataSourceManager.disconnectAll();\n } catch (e) {\n log.error(\"Error disconnecting data sources:\", e);\n }\n this.dataSourceManager = undefined;\n this.dataSourceAccessor = {};\n }\n\n if (this.definition.handlers.onDestroyed) {\n try {\n await this.definition.handlers.onDestroyed(this.state);\n } catch (e) {\n const error = e instanceof HypenError ? e : new ActionError(\"onDestroyed\", e);\n const shouldRethrow = await this.handleError(error, { lifecycle: \"destroyed\" });\n if (shouldRethrow) {\n throw error;\n }\n }\n }\n\n this.isDestroyed = true;\n }\n\n /**\n * Get the current state (returns a snapshot)\n */\n getState(): T {\n return getStateSnapshot(this.state);\n }\n\n /**\n * Get the live observable state\n */\n getLiveState(): T {\n return this.state;\n }\n\n /**\n * Update state directly (merges with existing state)\n */\n updateState(patch: Partial<T>): void {\n Object.assign(this.state, patch);\n }\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "CE88A330FC69253464756E2164756E21",
9
+ "names": []
10
+ }