@hypen-space/core 0.4.3 → 0.4.11
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.
- package/README.md +1 -1
- package/dist/app.d.ts +79 -22
- package/dist/app.js +248 -155
- package/dist/app.js.map +5 -5
- package/dist/components/builtin.js +251 -158
- package/dist/components/builtin.js.map +6 -6
- package/dist/context.js +1 -1
- package/dist/context.js.map +1 -1
- package/dist/discovery.d.ts +1 -1
- package/dist/discovery.js +249 -156
- package/dist/discovery.js.map +6 -6
- package/dist/disposable.js +1 -1
- package/dist/disposable.js.map +1 -1
- package/dist/engine.browser.d.ts +6 -0
- package/dist/engine.browser.js +192 -5
- package/dist/engine.browser.js.map +5 -4
- package/dist/engine.d.ts +6 -0
- package/dist/engine.js +192 -5
- package/dist/engine.js.map +5 -4
- package/dist/events.js +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +248 -155
- package/dist/index.browser.js.map +6 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +254 -158
- package/dist/index.js.map +7 -7
- package/dist/loader.js +1 -1
- package/dist/loader.js.map +1 -1
- package/dist/logger.js +245 -0
- package/dist/logger.js.map +10 -0
- package/dist/managed-router.d.ts +3 -4
- package/dist/plugin.js +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/remote/client.js +34 -2
- package/dist/remote/client.js.map +3 -3
- package/dist/remote/index.js +609 -163
- package/dist/remote/index.js.map +8 -7
- package/dist/remote/server.d.ts +46 -15
- package/dist/remote/server.js +609 -163
- package/dist/remote/server.js.map +8 -7
- package/dist/renderer.js +1 -1
- package/dist/renderer.js.map +1 -1
- package/dist/result.d.ts +18 -0
- package/dist/router.js +1 -1
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +9 -0
- package/package.json +1 -1
- package/src/app.ts +162 -41
- package/src/components/builtin.ts +3 -4
- package/src/discovery.ts +2 -2
- package/src/engine.browser.ts +27 -4
- package/src/engine.ts +27 -4
- package/src/index.browser.ts +0 -2
- package/src/index.ts +3 -2
- package/src/managed-router.ts +5 -6
- package/src/remote/server.ts +116 -21
- package/src/result.ts +48 -0
- package/src/types.ts +1 -1
- package/wasm-browser/hypen_engine.d.ts +3 -3
- package/wasm-browser/hypen_engine.js +73 -66
- package/wasm-browser/hypen_engine_bg.wasm +0 -0
- package/wasm-browser/hypen_engine_bg.wasm.d.ts +3 -3
- package/wasm-browser/package.json +1 -1
- package/wasm-node/hypen_engine.js +74 -66
- package/wasm-node/hypen_engine_bg.wasm +0 -0
- package/wasm-node/hypen_engine_bg.wasm.d.ts +3 -3
- package/wasm-node/package.json +1 -1
- package/dist/module-registry.d.ts +0 -42
- package/src/module-registry.ts +0 -76
package/README.md
CHANGED
|
@@ -136,7 +136,7 @@ const userModule = app
|
|
|
136
136
|
state.user = await fetchUser(userId);
|
|
137
137
|
state.loading = false;
|
|
138
138
|
// State changes are auto-synced via Proxy
|
|
139
|
-
//
|
|
139
|
+
// context.router is available for programmatic navigation
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
.onAction("logout", ({ state }) => {
|
package/dist/app.d.ts
CHANGED
|
@@ -12,19 +12,11 @@ export interface IEngine {
|
|
|
12
12
|
}
|
|
13
13
|
import type { HypenRouter } from "./router.js";
|
|
14
14
|
import type { HypenGlobalContext, ModuleReference } from "./context.js";
|
|
15
|
-
export type
|
|
16
|
-
root: HypenRouter;
|
|
17
|
-
current?: HypenRouter;
|
|
18
|
-
parent?: HypenRouter;
|
|
19
|
-
};
|
|
20
|
-
export type ActionContext = {
|
|
15
|
+
export type ActionContext<P = unknown> = {
|
|
21
16
|
name: string;
|
|
22
|
-
payload?:
|
|
17
|
+
payload?: P;
|
|
23
18
|
sender?: string;
|
|
24
19
|
};
|
|
25
|
-
export type ActionNext = {
|
|
26
|
-
router: HypenRouter;
|
|
27
|
-
};
|
|
28
20
|
export type GlobalContext = {
|
|
29
21
|
getModule: <T = unknown>(id: string) => ModuleReference<T>;
|
|
30
22
|
hasModule: (id: string) => boolean;
|
|
@@ -32,22 +24,21 @@ export type GlobalContext = {
|
|
|
32
24
|
getGlobalState: () => Record<string, unknown>;
|
|
33
25
|
emit: (event: string, payload?: unknown) => void;
|
|
34
26
|
on: (event: string, handler: (payload?: unknown) => void) => () => void;
|
|
35
|
-
|
|
27
|
+
router: HypenRouter | null;
|
|
36
28
|
};
|
|
37
29
|
export type LifecycleHandler<T> = (state: T, context?: GlobalContext) => void | Promise<void>;
|
|
38
30
|
/**
|
|
39
31
|
* Action handler context - all parameters available explicitly
|
|
40
32
|
*/
|
|
41
|
-
export interface ActionHandlerContext<T> {
|
|
42
|
-
action: ActionContext
|
|
33
|
+
export interface ActionHandlerContext<T, P = unknown> {
|
|
34
|
+
action: ActionContext<P>;
|
|
43
35
|
state: T;
|
|
44
|
-
next: ActionNext;
|
|
45
36
|
context: GlobalContext;
|
|
46
37
|
}
|
|
47
38
|
/**
|
|
48
39
|
* Action handler - receives all context in a single object
|
|
49
40
|
*/
|
|
50
|
-
export type ActionHandler<T> = (ctx: ActionHandlerContext<T>) => void | Promise<void>;
|
|
41
|
+
export type ActionHandler<T, P = unknown> = (ctx: ActionHandlerContext<T, P>) => void | Promise<void>;
|
|
51
42
|
/**
|
|
52
43
|
* Context passed to onDisconnect handler
|
|
53
44
|
*/
|
|
@@ -126,7 +117,7 @@ export interface HypenModuleDefinition<T = unknown> {
|
|
|
126
117
|
template?: string;
|
|
127
118
|
handlers: {
|
|
128
119
|
onCreated?: LifecycleHandler<T>;
|
|
129
|
-
onAction: Map<string, ActionHandler<T>>;
|
|
120
|
+
onAction: Map<string, ActionHandler<T, any>>;
|
|
130
121
|
onDestroyed?: LifecycleHandler<T>;
|
|
131
122
|
/** Called when client disconnects (session persists for TTL) */
|
|
132
123
|
onDisconnect?: DisconnectHandler<T>;
|
|
@@ -156,19 +147,34 @@ export declare class HypenAppBuilder<T> {
|
|
|
156
147
|
private expireHandler?;
|
|
157
148
|
private errorHandler?;
|
|
158
149
|
private template?;
|
|
150
|
+
private _registry?;
|
|
159
151
|
constructor(initialState: T, options?: {
|
|
160
152
|
persist?: boolean;
|
|
161
153
|
version?: number;
|
|
162
154
|
name?: string;
|
|
163
|
-
});
|
|
155
|
+
}, registry?: Map<string, HypenModuleDefinition>);
|
|
164
156
|
/**
|
|
165
157
|
* Register a handler for module creation
|
|
166
158
|
*/
|
|
167
159
|
onCreated(fn: LifecycleHandler<T>): this;
|
|
168
160
|
/**
|
|
169
|
-
* Register a handler for a specific action
|
|
161
|
+
* Register a handler for a specific action.
|
|
162
|
+
* Optionally provide a payload type parameter for typed action payloads.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Typed payload:
|
|
167
|
+
* .onAction<{ amount: number }>("add", ({ action, state }) => {
|
|
168
|
+
* action.payload.amount; // fully typed
|
|
169
|
+
* })
|
|
170
|
+
*
|
|
171
|
+
* // Untyped (payload is unknown):
|
|
172
|
+
* .onAction("add", ({ action, state }) => {
|
|
173
|
+
* action.payload; // unknown
|
|
174
|
+
* })
|
|
175
|
+
* ```
|
|
170
176
|
*/
|
|
171
|
-
onAction(name: string, fn: ActionHandler<T>): this;
|
|
177
|
+
onAction<P = unknown>(name: string, fn: ActionHandler<T, P>): this;
|
|
172
178
|
/**
|
|
173
179
|
* Register a handler for module destruction
|
|
174
180
|
*/
|
|
@@ -252,9 +258,15 @@ export declare class HypenAppBuilder<T> {
|
|
|
252
258
|
build(): HypenModuleDefinition<T>;
|
|
253
259
|
}
|
|
254
260
|
/**
|
|
255
|
-
* Hypen App API
|
|
261
|
+
* Hypen App API — singleton factory and component registry.
|
|
262
|
+
*
|
|
263
|
+
* Modules built with a `name` are automatically registered here.
|
|
264
|
+
* Consumers (ManagedRouter, RemoteServer, ComponentResolver) read from this
|
|
265
|
+
* registry instead of requiring a separate ModuleRegistry instance.
|
|
256
266
|
*/
|
|
257
267
|
export declare class HypenApp {
|
|
268
|
+
/** @internal */
|
|
269
|
+
readonly _registry: Map<string, HypenModuleDefinition<unknown>>;
|
|
258
270
|
/**
|
|
259
271
|
* Define the initial state for a module
|
|
260
272
|
*/
|
|
@@ -263,6 +275,51 @@ export declare class HypenApp {
|
|
|
263
275
|
version?: number;
|
|
264
276
|
name?: string;
|
|
265
277
|
}): HypenAppBuilder<T>;
|
|
278
|
+
/**
|
|
279
|
+
* Convenience: start a module builder with a name pre-set.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* export default app
|
|
284
|
+
* .module("Settings")
|
|
285
|
+
* .defineState({ theme: "dark" })
|
|
286
|
+
* .ui(hypen`...`);
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
module(name: string): {
|
|
290
|
+
defineState: <T>(initial: T, options?: {
|
|
291
|
+
persist?: boolean;
|
|
292
|
+
version?: number;
|
|
293
|
+
}) => HypenAppBuilder<T>;
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Get a registered module definition by component name.
|
|
297
|
+
*/
|
|
298
|
+
get(name: string): HypenModuleDefinition | undefined;
|
|
299
|
+
/**
|
|
300
|
+
* Check if a module definition is registered.
|
|
301
|
+
*/
|
|
302
|
+
has(name: string): boolean;
|
|
303
|
+
/**
|
|
304
|
+
* Read-only view of all registered component definitions.
|
|
305
|
+
*/
|
|
306
|
+
get components(): ReadonlyMap<string, HypenModuleDefinition>;
|
|
307
|
+
/**
|
|
308
|
+
* Get all registered component names.
|
|
309
|
+
*/
|
|
310
|
+
getNames(): string[];
|
|
311
|
+
/**
|
|
312
|
+
* Number of registered definitions.
|
|
313
|
+
*/
|
|
314
|
+
get size(): number;
|
|
315
|
+
/**
|
|
316
|
+
* Unregister a module definition.
|
|
317
|
+
*/
|
|
318
|
+
unregister(name: string): void;
|
|
319
|
+
/**
|
|
320
|
+
* Clear all registered definitions.
|
|
321
|
+
*/
|
|
322
|
+
clear(): void;
|
|
266
323
|
}
|
|
267
324
|
/**
|
|
268
325
|
* The main app instance for creating modules
|
|
@@ -276,10 +333,10 @@ export declare class HypenModuleInstance<T extends object = any> {
|
|
|
276
333
|
private definition;
|
|
277
334
|
private state;
|
|
278
335
|
private isDestroyed;
|
|
279
|
-
private
|
|
336
|
+
private router;
|
|
280
337
|
private globalContext?;
|
|
281
338
|
private stateChangeCallbacks;
|
|
282
|
-
constructor(engine: IEngine, definition: HypenModuleDefinition<T>,
|
|
339
|
+
constructor(engine: IEngine, definition: HypenModuleDefinition<T>, router?: HypenRouter | null, globalContext?: HypenGlobalContext);
|
|
283
340
|
/**
|
|
284
341
|
* Create the global context API for this module
|
|
285
342
|
*/
|
package/dist/app.js
CHANGED
|
@@ -275,144 +275,6 @@ var init_state = __esm(() => {
|
|
|
275
275
|
RAW_TARGET = Symbol.for("hypen.rawTarget");
|
|
276
276
|
});
|
|
277
277
|
|
|
278
|
-
// src/result.ts
|
|
279
|
-
function Ok(value) {
|
|
280
|
-
return { ok: true, value };
|
|
281
|
-
}
|
|
282
|
-
function Err(error) {
|
|
283
|
-
return { ok: false, error };
|
|
284
|
-
}
|
|
285
|
-
function isOk(result) {
|
|
286
|
-
return result.ok;
|
|
287
|
-
}
|
|
288
|
-
function isErr(result) {
|
|
289
|
-
return !result.ok;
|
|
290
|
-
}
|
|
291
|
-
async function fromPromise(promise, mapError) {
|
|
292
|
-
try {
|
|
293
|
-
const value = await promise;
|
|
294
|
-
return Ok(value);
|
|
295
|
-
} catch (e) {
|
|
296
|
-
if (mapError) {
|
|
297
|
-
return Err(mapError(e));
|
|
298
|
-
}
|
|
299
|
-
return Err(e);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
function fromTry(fn, mapError) {
|
|
303
|
-
try {
|
|
304
|
-
return Ok(fn());
|
|
305
|
-
} catch (e) {
|
|
306
|
-
if (mapError) {
|
|
307
|
-
return Err(mapError(e));
|
|
308
|
-
}
|
|
309
|
-
return Err(e);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function map(result, fn) {
|
|
313
|
-
if (result.ok) {
|
|
314
|
-
return Ok(fn(result.value));
|
|
315
|
-
}
|
|
316
|
-
return result;
|
|
317
|
-
}
|
|
318
|
-
function mapErr(result, fn) {
|
|
319
|
-
if (!result.ok) {
|
|
320
|
-
return Err(fn(result.error));
|
|
321
|
-
}
|
|
322
|
-
return result;
|
|
323
|
-
}
|
|
324
|
-
function flatMap(result, fn) {
|
|
325
|
-
if (result.ok) {
|
|
326
|
-
return fn(result.value);
|
|
327
|
-
}
|
|
328
|
-
return result;
|
|
329
|
-
}
|
|
330
|
-
function unwrap(result) {
|
|
331
|
-
if (result.ok) {
|
|
332
|
-
return result.value;
|
|
333
|
-
}
|
|
334
|
-
throw result.error;
|
|
335
|
-
}
|
|
336
|
-
function unwrapOr(result, defaultValue) {
|
|
337
|
-
if (result.ok) {
|
|
338
|
-
return result.value;
|
|
339
|
-
}
|
|
340
|
-
return defaultValue;
|
|
341
|
-
}
|
|
342
|
-
function unwrapOrElse(result, fn) {
|
|
343
|
-
if (result.ok) {
|
|
344
|
-
return result.value;
|
|
345
|
-
}
|
|
346
|
-
return fn(result.error);
|
|
347
|
-
}
|
|
348
|
-
function match(result, handlers) {
|
|
349
|
-
if (result.ok) {
|
|
350
|
-
return handlers.ok(result.value);
|
|
351
|
-
}
|
|
352
|
-
return handlers.err(result.error);
|
|
353
|
-
}
|
|
354
|
-
function all(results) {
|
|
355
|
-
const values = [];
|
|
356
|
-
for (const result of results) {
|
|
357
|
-
if (!result.ok) {
|
|
358
|
-
return result;
|
|
359
|
-
}
|
|
360
|
-
values.push(result.value);
|
|
361
|
-
}
|
|
362
|
-
return Ok(values);
|
|
363
|
-
}
|
|
364
|
-
var HypenError, ActionError, ConnectionError, StateError;
|
|
365
|
-
var init_result = __esm(() => {
|
|
366
|
-
HypenError = class HypenError extends Error {
|
|
367
|
-
code;
|
|
368
|
-
context;
|
|
369
|
-
cause;
|
|
370
|
-
constructor(code, message, options) {
|
|
371
|
-
super(message);
|
|
372
|
-
this.name = "HypenError";
|
|
373
|
-
this.code = code;
|
|
374
|
-
this.context = options?.context;
|
|
375
|
-
this.cause = options?.cause;
|
|
376
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
ActionError = class ActionError extends HypenError {
|
|
380
|
-
actionName;
|
|
381
|
-
constructor(actionName, cause) {
|
|
382
|
-
super("ACTION_ERROR", `Action handler "${actionName}" failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
383
|
-
context: { actionName },
|
|
384
|
-
cause: cause instanceof Error ? cause : undefined
|
|
385
|
-
});
|
|
386
|
-
this.name = "ActionError";
|
|
387
|
-
this.actionName = actionName;
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
ConnectionError = class ConnectionError extends HypenError {
|
|
391
|
-
url;
|
|
392
|
-
attempt;
|
|
393
|
-
constructor(url, cause, attempt) {
|
|
394
|
-
super("CONNECTION_ERROR", `Connection to "${url}" failed${attempt ? ` (attempt ${attempt})` : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
395
|
-
context: { url, attempt },
|
|
396
|
-
cause: cause instanceof Error ? cause : undefined
|
|
397
|
-
});
|
|
398
|
-
this.name = "ConnectionError";
|
|
399
|
-
this.url = url;
|
|
400
|
-
this.attempt = attempt;
|
|
401
|
-
}
|
|
402
|
-
};
|
|
403
|
-
StateError = class StateError extends HypenError {
|
|
404
|
-
path;
|
|
405
|
-
constructor(message, path, cause) {
|
|
406
|
-
super("STATE_ERROR", message, {
|
|
407
|
-
context: { path },
|
|
408
|
-
cause: cause instanceof Error ? cause : undefined
|
|
409
|
-
});
|
|
410
|
-
this.name = "StateError";
|
|
411
|
-
this.path = path;
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
});
|
|
415
|
-
|
|
416
278
|
// src/logger.ts
|
|
417
279
|
function isProduction() {
|
|
418
280
|
if (typeof process !== "undefined" && process.env) {
|
|
@@ -612,6 +474,176 @@ var init_logger = __esm(() => {
|
|
|
612
474
|
};
|
|
613
475
|
});
|
|
614
476
|
|
|
477
|
+
// src/result.ts
|
|
478
|
+
function Ok(value) {
|
|
479
|
+
return { ok: true, value };
|
|
480
|
+
}
|
|
481
|
+
function Err(error) {
|
|
482
|
+
return { ok: false, error };
|
|
483
|
+
}
|
|
484
|
+
function isOk(result) {
|
|
485
|
+
return result.ok;
|
|
486
|
+
}
|
|
487
|
+
function isErr(result) {
|
|
488
|
+
return !result.ok;
|
|
489
|
+
}
|
|
490
|
+
async function fromPromise(promise, mapError) {
|
|
491
|
+
try {
|
|
492
|
+
const value = await promise;
|
|
493
|
+
return Ok(value);
|
|
494
|
+
} catch (e) {
|
|
495
|
+
if (mapError) {
|
|
496
|
+
return Err(mapError(e));
|
|
497
|
+
}
|
|
498
|
+
return Err(e);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function fromTry(fn, mapError) {
|
|
502
|
+
try {
|
|
503
|
+
return Ok(fn());
|
|
504
|
+
} catch (e) {
|
|
505
|
+
if (mapError) {
|
|
506
|
+
return Err(mapError(e));
|
|
507
|
+
}
|
|
508
|
+
return Err(e);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function map(result, fn) {
|
|
512
|
+
if (result.ok) {
|
|
513
|
+
return Ok(fn(result.value));
|
|
514
|
+
}
|
|
515
|
+
return result;
|
|
516
|
+
}
|
|
517
|
+
function mapErr(result, fn) {
|
|
518
|
+
if (!result.ok) {
|
|
519
|
+
return Err(fn(result.error));
|
|
520
|
+
}
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
function flatMap(result, fn) {
|
|
524
|
+
if (result.ok) {
|
|
525
|
+
return fn(result.value);
|
|
526
|
+
}
|
|
527
|
+
return result;
|
|
528
|
+
}
|
|
529
|
+
function unwrap(result) {
|
|
530
|
+
if (result.ok) {
|
|
531
|
+
return result.value;
|
|
532
|
+
}
|
|
533
|
+
throw result.error;
|
|
534
|
+
}
|
|
535
|
+
function unwrapOr(result, defaultValue) {
|
|
536
|
+
if (result.ok) {
|
|
537
|
+
return result.value;
|
|
538
|
+
}
|
|
539
|
+
return defaultValue;
|
|
540
|
+
}
|
|
541
|
+
function unwrapOrElse(result, fn) {
|
|
542
|
+
if (result.ok) {
|
|
543
|
+
return result.value;
|
|
544
|
+
}
|
|
545
|
+
return fn(result.error);
|
|
546
|
+
}
|
|
547
|
+
function match(result, handlers) {
|
|
548
|
+
if (result.ok) {
|
|
549
|
+
return handlers.ok(result.value);
|
|
550
|
+
}
|
|
551
|
+
return handlers.err(result.error);
|
|
552
|
+
}
|
|
553
|
+
function all(results) {
|
|
554
|
+
const values = [];
|
|
555
|
+
for (const result of results) {
|
|
556
|
+
if (!result.ok) {
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
values.push(result.value);
|
|
560
|
+
}
|
|
561
|
+
return Ok(values);
|
|
562
|
+
}
|
|
563
|
+
function classifyEngineError(err) {
|
|
564
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
565
|
+
if (message.startsWith("Parse error:")) {
|
|
566
|
+
return new ParseError(message);
|
|
567
|
+
}
|
|
568
|
+
if (message.startsWith("Invalid state:") || message.startsWith("Invalid state patch:")) {
|
|
569
|
+
return new StateError(message);
|
|
570
|
+
}
|
|
571
|
+
if (message.startsWith("Parent node not found:")) {
|
|
572
|
+
return new RenderError(message);
|
|
573
|
+
}
|
|
574
|
+
return new RenderError(message);
|
|
575
|
+
}
|
|
576
|
+
var HypenError, ActionError, ConnectionError, StateError, ParseError, RenderError;
|
|
577
|
+
var init_result = __esm(() => {
|
|
578
|
+
HypenError = class HypenError extends Error {
|
|
579
|
+
code;
|
|
580
|
+
context;
|
|
581
|
+
cause;
|
|
582
|
+
constructor(code, message, options) {
|
|
583
|
+
super(message);
|
|
584
|
+
this.name = "HypenError";
|
|
585
|
+
this.code = code;
|
|
586
|
+
this.context = options?.context;
|
|
587
|
+
this.cause = options?.cause;
|
|
588
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
ActionError = class ActionError extends HypenError {
|
|
592
|
+
actionName;
|
|
593
|
+
constructor(actionName, cause) {
|
|
594
|
+
super("ACTION_ERROR", `Action handler "${actionName}" failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
595
|
+
context: { actionName },
|
|
596
|
+
cause: cause instanceof Error ? cause : undefined
|
|
597
|
+
});
|
|
598
|
+
this.name = "ActionError";
|
|
599
|
+
this.actionName = actionName;
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
ConnectionError = class ConnectionError extends HypenError {
|
|
603
|
+
url;
|
|
604
|
+
attempt;
|
|
605
|
+
constructor(url, cause, attempt) {
|
|
606
|
+
super("CONNECTION_ERROR", `Connection to "${url}" failed${attempt ? ` (attempt ${attempt})` : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
607
|
+
context: { url, attempt },
|
|
608
|
+
cause: cause instanceof Error ? cause : undefined
|
|
609
|
+
});
|
|
610
|
+
this.name = "ConnectionError";
|
|
611
|
+
this.url = url;
|
|
612
|
+
this.attempt = attempt;
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
StateError = class StateError extends HypenError {
|
|
616
|
+
path;
|
|
617
|
+
constructor(message, path, cause) {
|
|
618
|
+
super("STATE_ERROR", message, {
|
|
619
|
+
context: { path },
|
|
620
|
+
cause: cause instanceof Error ? cause : undefined
|
|
621
|
+
});
|
|
622
|
+
this.name = "StateError";
|
|
623
|
+
this.path = path;
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
ParseError = class ParseError extends HypenError {
|
|
627
|
+
source;
|
|
628
|
+
constructor(message, source, cause) {
|
|
629
|
+
super("PARSE_ERROR", message, {
|
|
630
|
+
context: { source },
|
|
631
|
+
cause: cause instanceof Error ? cause : undefined
|
|
632
|
+
});
|
|
633
|
+
this.name = "ParseError";
|
|
634
|
+
this.source = source;
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
RenderError = class RenderError extends HypenError {
|
|
638
|
+
constructor(message, cause) {
|
|
639
|
+
super("RENDER_ERROR", message, {
|
|
640
|
+
cause: cause instanceof Error ? cause : undefined
|
|
641
|
+
});
|
|
642
|
+
this.name = "RenderError";
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
});
|
|
646
|
+
|
|
615
647
|
// src/app.ts
|
|
616
648
|
var exports_app = {};
|
|
617
649
|
__export(exports_app, {
|
|
@@ -632,9 +664,11 @@ class HypenAppBuilder {
|
|
|
632
664
|
expireHandler;
|
|
633
665
|
errorHandler;
|
|
634
666
|
template;
|
|
635
|
-
|
|
667
|
+
_registry;
|
|
668
|
+
constructor(initialState, options, registry) {
|
|
636
669
|
this.initialState = initialState;
|
|
637
670
|
this.options = options || {};
|
|
671
|
+
this._registry = registry;
|
|
638
672
|
}
|
|
639
673
|
onCreated(fn) {
|
|
640
674
|
this.createdHandler = fn;
|
|
@@ -670,7 +704,7 @@ class HypenAppBuilder {
|
|
|
670
704
|
}
|
|
671
705
|
build() {
|
|
672
706
|
const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
|
|
673
|
-
|
|
707
|
+
const definition = {
|
|
674
708
|
name: this.options.name,
|
|
675
709
|
actions: Array.from(this.actionHandlers.keys()),
|
|
676
710
|
stateKeys,
|
|
@@ -688,12 +722,46 @@ class HypenAppBuilder {
|
|
|
688
722
|
onError: this.errorHandler
|
|
689
723
|
}
|
|
690
724
|
};
|
|
725
|
+
if (this.options.name && this._registry) {
|
|
726
|
+
this._registry.set(this.options.name, definition);
|
|
727
|
+
}
|
|
728
|
+
return definition;
|
|
691
729
|
}
|
|
692
730
|
}
|
|
693
731
|
|
|
694
732
|
class HypenApp {
|
|
733
|
+
_registry = new Map;
|
|
695
734
|
defineState(initial, options) {
|
|
696
|
-
return new HypenAppBuilder(initial, options);
|
|
735
|
+
return new HypenAppBuilder(initial, options, this._registry);
|
|
736
|
+
}
|
|
737
|
+
module(name) {
|
|
738
|
+
const registry = this._registry;
|
|
739
|
+
return {
|
|
740
|
+
defineState: (initial, options) => {
|
|
741
|
+
return new HypenAppBuilder(initial, { ...options, name }, registry);
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
get(name) {
|
|
746
|
+
return this._registry.get(name);
|
|
747
|
+
}
|
|
748
|
+
has(name) {
|
|
749
|
+
return this._registry.has(name);
|
|
750
|
+
}
|
|
751
|
+
get components() {
|
|
752
|
+
return this._registry;
|
|
753
|
+
}
|
|
754
|
+
getNames() {
|
|
755
|
+
return Array.from(this._registry.keys());
|
|
756
|
+
}
|
|
757
|
+
get size() {
|
|
758
|
+
return this._registry.size;
|
|
759
|
+
}
|
|
760
|
+
unregister(name) {
|
|
761
|
+
this._registry.delete(name);
|
|
762
|
+
}
|
|
763
|
+
clear() {
|
|
764
|
+
this._registry.clear();
|
|
697
765
|
}
|
|
698
766
|
}
|
|
699
767
|
|
|
@@ -702,13 +770,13 @@ class HypenModuleInstance {
|
|
|
702
770
|
definition;
|
|
703
771
|
state;
|
|
704
772
|
isDestroyed = false;
|
|
705
|
-
|
|
773
|
+
router;
|
|
706
774
|
globalContext;
|
|
707
775
|
stateChangeCallbacks = [];
|
|
708
|
-
constructor(engine, definition,
|
|
776
|
+
constructor(engine, definition, router, globalContext) {
|
|
709
777
|
this.engine = engine;
|
|
710
778
|
this.definition = definition;
|
|
711
|
-
this.
|
|
779
|
+
this.router = router ?? null;
|
|
712
780
|
this.globalContext = globalContext;
|
|
713
781
|
const statePrefix = (definition.name || "").toLowerCase();
|
|
714
782
|
this.state = createObservableState(definition.initialState, {
|
|
@@ -730,14 +798,10 @@ class HypenModuleInstance {
|
|
|
730
798
|
payload: action.payload,
|
|
731
799
|
sender: action.sender
|
|
732
800
|
};
|
|
733
|
-
const next = {
|
|
734
|
-
router: this.routerContext?.root || null
|
|
735
|
-
};
|
|
736
801
|
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
737
802
|
const result = await this.executeAction(actionName, handler, {
|
|
738
803
|
action: actionCtx,
|
|
739
804
|
state: this.state,
|
|
740
|
-
next,
|
|
741
805
|
context
|
|
742
806
|
});
|
|
743
807
|
if (!result.ok) {
|
|
@@ -750,6 +814,21 @@ class HypenModuleInstance {
|
|
|
750
814
|
}
|
|
751
815
|
});
|
|
752
816
|
}
|
|
817
|
+
this.engine.onAction("__hypen_bind", (action) => {
|
|
818
|
+
const payload = action.payload;
|
|
819
|
+
if (!payload?.path)
|
|
820
|
+
return;
|
|
821
|
+
const segments = payload.path.split(".");
|
|
822
|
+
let target = this.state;
|
|
823
|
+
for (let i = 0;i < segments.length - 1; i++) {
|
|
824
|
+
const seg = segments[i];
|
|
825
|
+
target = target?.[seg];
|
|
826
|
+
if (target == null)
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
const lastSeg = segments[segments.length - 1];
|
|
830
|
+
target[lastSeg] = payload.value;
|
|
831
|
+
});
|
|
753
832
|
this.callCreatedHandler();
|
|
754
833
|
}
|
|
755
834
|
createGlobalContextAPI() {
|
|
@@ -763,11 +842,9 @@ class HypenModuleInstance {
|
|
|
763
842
|
getModuleIds: () => ctx.getModuleIds(),
|
|
764
843
|
getGlobalState: () => ctx.getGlobalState(),
|
|
765
844
|
emit: (event, payload) => ctx.emit(event, payload),
|
|
766
|
-
on: (event, handler) => ctx.on(event, handler)
|
|
845
|
+
on: (event, handler) => ctx.on(event, handler),
|
|
846
|
+
router: this.router
|
|
767
847
|
};
|
|
768
|
-
if (ctx.__router) {
|
|
769
|
-
api.__router = ctx.__router;
|
|
770
|
-
}
|
|
771
848
|
if (ctx.__hypenEngine) {
|
|
772
849
|
api.__hypenEngine = ctx.__hypenEngine;
|
|
773
850
|
}
|
|
@@ -818,7 +895,15 @@ class HypenModuleInstance {
|
|
|
818
895
|
async callCreatedHandler() {
|
|
819
896
|
if (this.definition.handlers.onCreated) {
|
|
820
897
|
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
821
|
-
|
|
898
|
+
try {
|
|
899
|
+
await this.definition.handlers.onCreated(this.state, context);
|
|
900
|
+
} catch (e) {
|
|
901
|
+
const error = e instanceof HypenError ? e : new ActionError("onCreated", e);
|
|
902
|
+
const shouldRethrow = await this.handleError(error, { lifecycle: "created" });
|
|
903
|
+
if (shouldRethrow) {
|
|
904
|
+
throw error;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
822
907
|
}
|
|
823
908
|
}
|
|
824
909
|
onStateChange(callback) {
|
|
@@ -828,7 +913,15 @@ class HypenModuleInstance {
|
|
|
828
913
|
if (this.isDestroyed)
|
|
829
914
|
return;
|
|
830
915
|
if (this.definition.handlers.onDestroyed) {
|
|
831
|
-
|
|
916
|
+
try {
|
|
917
|
+
await this.definition.handlers.onDestroyed(this.state);
|
|
918
|
+
} catch (e) {
|
|
919
|
+
const error = e instanceof HypenError ? e : new ActionError("onDestroyed", e);
|
|
920
|
+
const shouldRethrow = await this.handleError(error, { lifecycle: "destroyed" });
|
|
921
|
+
if (shouldRethrow) {
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
832
925
|
}
|
|
833
926
|
this.isDestroyed = true;
|
|
834
927
|
}
|
|
@@ -859,4 +952,4 @@ export {
|
|
|
859
952
|
HypenApp
|
|
860
953
|
};
|
|
861
954
|
|
|
862
|
-
//# debugId=
|
|
955
|
+
//# debugId=901B51DDE158736764756E2164756E21
|