@hypen-space/core 0.4.2 → 0.4.3
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/dist/app.d.ts +322 -0
- package/dist/components/builtin.d.ts +50 -0
- package/dist/context.d.ts +90 -0
- package/dist/discovery.d.ts +90 -0
- package/dist/discovery.js +10 -5
- package/dist/discovery.js.map +3 -3
- package/dist/disposable.d.ts +122 -0
- package/dist/engine.browser.d.ts +95 -0
- package/dist/engine.d.ts +79 -0
- package/dist/events.d.ts +78 -0
- package/dist/hypen.d.ts +127 -0
- package/dist/index.browser.d.ts +25 -0
- package/dist/index.browser.js +6 -2
- package/dist/index.browser.js.map +3 -3
- package/dist/index.d.ts +76 -0
- package/dist/index.js +238 -1196
- package/dist/index.js.map +7 -12
- package/dist/loader.d.ts +51 -0
- package/dist/logger.d.ts +141 -0
- package/dist/managed-router.d.ts +60 -0
- package/dist/module-registry.d.ts +42 -0
- package/dist/plugin.d.ts +39 -0
- package/dist/remote/client.d.ts +154 -0
- package/dist/remote/index.d.ts +13 -0
- package/dist/remote/server.d.ts +142 -0
- package/dist/remote/session.d.ts +115 -0
- package/dist/remote/types.d.ts +98 -0
- package/dist/renderer.d.ts +54 -0
- package/dist/renderer.js +6 -2
- package/dist/renderer.js.map +3 -3
- package/dist/resolver.d.ts +95 -0
- package/dist/result.d.ts +116 -0
- package/dist/retry.d.ts +150 -0
- package/dist/router.d.ts +94 -0
- package/dist/state.d.ts +42 -0
- package/dist/types.d.ts +30 -0
- package/package.json +2 -1
- package/wasm-browser/hypen_engine_bg.wasm +0 -0
- package/wasm-node/package.json +4 -2
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads and registers Hypen components from file system.
|
|
5
|
+
* Works in both Node.js and Bun environments.
|
|
6
|
+
*/
|
|
7
|
+
import type { HypenModuleDefinition } from "./app.js";
|
|
8
|
+
export interface ComponentDefinition {
|
|
9
|
+
name: string;
|
|
10
|
+
module: HypenModuleDefinition<any>;
|
|
11
|
+
template: string;
|
|
12
|
+
path: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class ComponentLoader {
|
|
15
|
+
private components;
|
|
16
|
+
/**
|
|
17
|
+
* Register a component with its module and template
|
|
18
|
+
*/
|
|
19
|
+
register(name: string, module: HypenModuleDefinition<any>, template: string, path?: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get a registered component by name
|
|
22
|
+
*/
|
|
23
|
+
get(name: string): ComponentDefinition | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a component is registered
|
|
26
|
+
*/
|
|
27
|
+
has(name: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Get all registered component names
|
|
30
|
+
*/
|
|
31
|
+
getNames(): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Get all registered components
|
|
34
|
+
*/
|
|
35
|
+
getAll(): ComponentDefinition[];
|
|
36
|
+
/**
|
|
37
|
+
* Clear all registered components
|
|
38
|
+
*/
|
|
39
|
+
clear(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Load a component from a directory
|
|
42
|
+
* Expects: component.ts and component.hypen in the same directory
|
|
43
|
+
*/
|
|
44
|
+
loadFromDirectory(name: string, dirPath: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Auto-load all components from a directory
|
|
47
|
+
* Scans for subdirectories containing component.ts and component.hypen
|
|
48
|
+
*/
|
|
49
|
+
loadFromComponentsDir(baseDir: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export declare const componentLoader: ComponentLoader;
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configurable Debug Logger
|
|
3
|
+
*
|
|
4
|
+
* Provides environment-aware logging that can be disabled in production.
|
|
5
|
+
* Supports log levels, tagged output, and performance timing.
|
|
6
|
+
*/
|
|
7
|
+
export type LogLevel = "debug" | "info" | "warn" | "error" | "none";
|
|
8
|
+
export interface LoggerConfig {
|
|
9
|
+
/** Minimum log level (default: "debug" in dev, "error" in prod) */
|
|
10
|
+
level: LogLevel;
|
|
11
|
+
/** Enable colored output (default: true) */
|
|
12
|
+
colors: boolean;
|
|
13
|
+
/** Include timestamps (default: false) */
|
|
14
|
+
timestamps: boolean;
|
|
15
|
+
/** Custom log handler (default: console) */
|
|
16
|
+
handler?: LogHandler;
|
|
17
|
+
}
|
|
18
|
+
export interface LogHandler {
|
|
19
|
+
debug(tag: string, ...args: unknown[]): void;
|
|
20
|
+
info(tag: string, ...args: unknown[]): void;
|
|
21
|
+
warn(tag: string, ...args: unknown[]): void;
|
|
22
|
+
error(tag: string, ...args: unknown[]): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Set the global log level
|
|
26
|
+
*/
|
|
27
|
+
export declare function setLogLevel(level: LogLevel): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get the current log level
|
|
30
|
+
*/
|
|
31
|
+
export declare function getLogLevel(): LogLevel;
|
|
32
|
+
/**
|
|
33
|
+
* Configure the logger
|
|
34
|
+
*/
|
|
35
|
+
export declare function configureLogger(options: Partial<LoggerConfig>): void;
|
|
36
|
+
/**
|
|
37
|
+
* Enable all logging (sets level to "debug")
|
|
38
|
+
*/
|
|
39
|
+
export declare function enableLogging(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Disable all logging (sets level to "none")
|
|
42
|
+
*/
|
|
43
|
+
export declare function disableLogging(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Enable debug mode - alias for enableLogging()
|
|
46
|
+
* Call this at app startup to see all debug logs
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* import { setDebugMode } from "@hypen-space/core";
|
|
51
|
+
* setDebugMode(true);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function setDebugMode(enabled: boolean): void;
|
|
55
|
+
/**
|
|
56
|
+
* Check if debug mode is enabled
|
|
57
|
+
*/
|
|
58
|
+
export declare function isDebugMode(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Tagged logger instance
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const log = createLogger("MyComponent");
|
|
65
|
+
* log.debug("initialized with", { props });
|
|
66
|
+
* log.error("failed to load", error);
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare class Logger {
|
|
70
|
+
private readonly tag;
|
|
71
|
+
constructor(tag: string);
|
|
72
|
+
debug(...args: unknown[]): void;
|
|
73
|
+
info(...args: unknown[]): void;
|
|
74
|
+
warn(...args: unknown[]): void;
|
|
75
|
+
error(...args: unknown[]): void;
|
|
76
|
+
/**
|
|
77
|
+
* Time a function execution
|
|
78
|
+
*/
|
|
79
|
+
time<T>(label: string, fn: () => T): T;
|
|
80
|
+
/**
|
|
81
|
+
* Time an async function execution
|
|
82
|
+
*/
|
|
83
|
+
timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Create a child logger with additional context
|
|
86
|
+
*/
|
|
87
|
+
child(subTag: string): Logger;
|
|
88
|
+
/**
|
|
89
|
+
* Conditionally log based on a condition
|
|
90
|
+
*/
|
|
91
|
+
debugIf(condition: boolean, ...args: unknown[]): void;
|
|
92
|
+
warnIf(condition: boolean, ...args: unknown[]): void;
|
|
93
|
+
errorIf(condition: boolean, ...args: unknown[]): void;
|
|
94
|
+
/**
|
|
95
|
+
* Log once (useful for deprecation warnings)
|
|
96
|
+
*/
|
|
97
|
+
private loggedOnce;
|
|
98
|
+
warnOnce(key: string, ...args: unknown[]): void;
|
|
99
|
+
debugOnce(key: string, ...args: unknown[]): void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a tagged logger
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const log = createLogger("Router");
|
|
107
|
+
* log.info("navigating to", path);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare function createLogger(tag: string): Logger;
|
|
111
|
+
/**
|
|
112
|
+
* Default logger for general use
|
|
113
|
+
*/
|
|
114
|
+
export declare const logger: Logger;
|
|
115
|
+
/**
|
|
116
|
+
* Shorthand logging functions for quick use
|
|
117
|
+
* Prefer createLogger() for component-specific logging
|
|
118
|
+
*/
|
|
119
|
+
export declare const log: {
|
|
120
|
+
debug: (tag: string, ...args: unknown[]) => void;
|
|
121
|
+
info: (tag: string, ...args: unknown[]) => void;
|
|
122
|
+
warn: (tag: string, ...args: unknown[]) => void;
|
|
123
|
+
error: (tag: string, ...args: unknown[]) => void;
|
|
124
|
+
};
|
|
125
|
+
export declare const frameworkLoggers: {
|
|
126
|
+
hypen: Logger;
|
|
127
|
+
engine: Logger;
|
|
128
|
+
router: Logger;
|
|
129
|
+
state: Logger;
|
|
130
|
+
events: Logger;
|
|
131
|
+
remote: Logger;
|
|
132
|
+
renderer: Logger;
|
|
133
|
+
module: Logger;
|
|
134
|
+
lifecycle: Logger;
|
|
135
|
+
loader: Logger;
|
|
136
|
+
context: Logger;
|
|
137
|
+
discovery: Logger;
|
|
138
|
+
plugin: Logger;
|
|
139
|
+
canvas: Logger;
|
|
140
|
+
debug: Logger;
|
|
141
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed Router — orchestrates module mount/unmount on route changes.
|
|
3
|
+
*
|
|
4
|
+
* When the router navigates to a route:
|
|
5
|
+
* 1. Unmounts the previous module (destroy instance, unregister from GlobalContext)
|
|
6
|
+
* 2. Mounts the new module (create instance with namespaced state, register)
|
|
7
|
+
*
|
|
8
|
+
* Module names are used as state prefixes (lowercased) for isolation.
|
|
9
|
+
*/
|
|
10
|
+
import type { IEngine, HypenModuleDefinition } from "./app.js";
|
|
11
|
+
import { HypenModuleInstance } from "./app.js";
|
|
12
|
+
import type { HypenRouter } from "./router.js";
|
|
13
|
+
import type { HypenGlobalContext } from "./context.js";
|
|
14
|
+
import { ModuleRegistry } from "./module-registry.js";
|
|
15
|
+
export interface RouteDefinition {
|
|
16
|
+
/** Route path pattern (e.g., "/", "/profile/:id") */
|
|
17
|
+
path: string;
|
|
18
|
+
/** Component name — used to look up in ModuleRegistry */
|
|
19
|
+
component: string;
|
|
20
|
+
/** Inline module definition (alternative to registry lookup) */
|
|
21
|
+
module?: HypenModuleDefinition;
|
|
22
|
+
}
|
|
23
|
+
export declare class ManagedRouter {
|
|
24
|
+
private router;
|
|
25
|
+
private engine;
|
|
26
|
+
private registry;
|
|
27
|
+
private globalContext;
|
|
28
|
+
private routes;
|
|
29
|
+
private activeModule;
|
|
30
|
+
private activeRoute;
|
|
31
|
+
private unsubscribe;
|
|
32
|
+
/** Cached instances for modules with persist=true */
|
|
33
|
+
private persistedModules;
|
|
34
|
+
constructor(router: HypenRouter, engine: IEngine, registry: ModuleRegistry, globalContext: HypenGlobalContext);
|
|
35
|
+
/**
|
|
36
|
+
* Add a route definition.
|
|
37
|
+
*/
|
|
38
|
+
addRoute(route: RouteDefinition): this;
|
|
39
|
+
/**
|
|
40
|
+
* Start listening for route changes and mount the initial route.
|
|
41
|
+
*/
|
|
42
|
+
start(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Stop listening and unmount the active module.
|
|
45
|
+
* Also destroys all persisted modules.
|
|
46
|
+
*/
|
|
47
|
+
stop(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Get the currently active module instance.
|
|
50
|
+
*/
|
|
51
|
+
getActiveModule(): HypenModuleInstance | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get the currently active route.
|
|
54
|
+
*/
|
|
55
|
+
getActiveRoute(): RouteDefinition | null;
|
|
56
|
+
private handleRouteChange;
|
|
57
|
+
private matchRoute;
|
|
58
|
+
private mount;
|
|
59
|
+
private unmountActive;
|
|
60
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Registry — maps component names to module definitions.
|
|
3
|
+
*
|
|
4
|
+
* Used by the import resolution pipeline and ManagedRouter to look up
|
|
5
|
+
* module definitions by their component name (e.g., "HomePage", "ProfilePage").
|
|
6
|
+
*/
|
|
7
|
+
import type { HypenModuleDefinition } from "./app.js";
|
|
8
|
+
export declare class ModuleRegistry {
|
|
9
|
+
private definitions;
|
|
10
|
+
/**
|
|
11
|
+
* Register a module definition under a component name.
|
|
12
|
+
*/
|
|
13
|
+
register(name: string, definition: HypenModuleDefinition): void;
|
|
14
|
+
/**
|
|
15
|
+
* Get a module definition by component name.
|
|
16
|
+
*/
|
|
17
|
+
get(name: string): HypenModuleDefinition | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a module definition exists.
|
|
20
|
+
*/
|
|
21
|
+
has(name: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get all registered definitions.
|
|
24
|
+
*/
|
|
25
|
+
getAll(): Map<string, HypenModuleDefinition>;
|
|
26
|
+
/**
|
|
27
|
+
* Get all registered component names.
|
|
28
|
+
*/
|
|
29
|
+
getNames(): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Unregister a module definition.
|
|
32
|
+
*/
|
|
33
|
+
unregister(name: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Number of registered definitions.
|
|
36
|
+
*/
|
|
37
|
+
get size(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Clear all registered definitions.
|
|
40
|
+
*/
|
|
41
|
+
clear(): void;
|
|
42
|
+
}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Bun Plugin for Hypen
|
|
3
|
+
*
|
|
4
|
+
* Automatically pairs .hypen templates with their TypeScript modules.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import Counter from "./Counter.hypen";
|
|
8
|
+
* // Returns: { module, template, name: "Counter" }
|
|
9
|
+
*
|
|
10
|
+
* Supported conventions:
|
|
11
|
+
* 1. Sibling: Name.ts + Name.hypen
|
|
12
|
+
* 2. Folder: Name/component.ts + Name/component.hypen
|
|
13
|
+
* 3. Index: Name/index.ts + Name/index.hypen
|
|
14
|
+
*/
|
|
15
|
+
import type { BunPlugin } from "bun";
|
|
16
|
+
export interface HypenPluginOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Enable debug logging
|
|
19
|
+
*/
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Custom patterns for finding module files
|
|
23
|
+
* Default: ["sibling", "component", "index"]
|
|
24
|
+
*/
|
|
25
|
+
patterns?: ("sibling" | "component" | "index")[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create the enhanced Hypen plugin for Bun
|
|
29
|
+
*/
|
|
30
|
+
export declare function hypenPlugin(options?: HypenPluginOptions): BunPlugin;
|
|
31
|
+
/**
|
|
32
|
+
* Default plugin instance with standard options
|
|
33
|
+
*/
|
|
34
|
+
export declare const defaultHypenPlugin: BunPlugin;
|
|
35
|
+
/**
|
|
36
|
+
* Register the plugin globally (call this in your preload file)
|
|
37
|
+
*/
|
|
38
|
+
export declare function registerHypenPlugin(options?: HypenPluginOptions): void;
|
|
39
|
+
export default hypenPlugin;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemoteEngine - Connect to a remote Hypen app over WebSocket
|
|
3
|
+
* Platform-agnostic client (uses standard WebSocket API)
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const engine = new RemoteEngine("ws://localhost:3000", {
|
|
8
|
+
* session: {
|
|
9
|
+
* id: localStorage.getItem("sessionId") ?? undefined,
|
|
10
|
+
* props: { platform: "web", version: "1.0" }
|
|
11
|
+
* }
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* engine.onSessionEstablished(({ sessionId, isNew, isRestored }) => {
|
|
15
|
+
* localStorage.setItem("sessionId", sessionId);
|
|
16
|
+
* console.log(isRestored ? "Welcome back!" : "New session");
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* engine.onSessionExpired((reason) => {
|
|
20
|
+
* localStorage.removeItem("sessionId");
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const result = await engine.connect();
|
|
24
|
+
* if (!result.ok) {
|
|
25
|
+
* console.error("Connection failed:", result.error);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
import type { Patch } from "../types.js";
|
|
30
|
+
import { type Result, ConnectionError } from "../result.js";
|
|
31
|
+
import { type Disposable } from "../disposable.js";
|
|
32
|
+
export type RemoteConnectionState = "disconnected" | "connecting" | "connected" | "error";
|
|
33
|
+
/**
|
|
34
|
+
* Session configuration for the client
|
|
35
|
+
*/
|
|
36
|
+
export interface SessionOptions {
|
|
37
|
+
/** Session ID to resume (omit for new session) */
|
|
38
|
+
id?: string;
|
|
39
|
+
/** Client metadata (platform, version, userId, etc.) */
|
|
40
|
+
props?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Session information received from server
|
|
44
|
+
*/
|
|
45
|
+
export interface SessionInfo {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
isNew: boolean;
|
|
48
|
+
isRestored: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface RemoteEngineOptions {
|
|
51
|
+
autoReconnect?: boolean;
|
|
52
|
+
reconnectInterval?: number;
|
|
53
|
+
maxReconnectAttempts?: number;
|
|
54
|
+
/** Session configuration */
|
|
55
|
+
session?: SessionOptions;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Client-side engine that connects to a remote Hypen app
|
|
59
|
+
*/
|
|
60
|
+
export declare class RemoteEngine implements Disposable {
|
|
61
|
+
private ws;
|
|
62
|
+
private readonly url;
|
|
63
|
+
private state;
|
|
64
|
+
private readonly options;
|
|
65
|
+
private reconnectAttempts;
|
|
66
|
+
private readonly disposables;
|
|
67
|
+
private reconnectDisposable;
|
|
68
|
+
private currentSessionId;
|
|
69
|
+
private readonly sessionOptions?;
|
|
70
|
+
private readonly patchCallbacks;
|
|
71
|
+
private readonly stateCallbacks;
|
|
72
|
+
private readonly connectionCallbacks;
|
|
73
|
+
private readonly disconnectionCallbacks;
|
|
74
|
+
private readonly errorCallbacks;
|
|
75
|
+
private readonly sessionEstablishedCallbacks;
|
|
76
|
+
private readonly sessionExpiredCallbacks;
|
|
77
|
+
private currentState;
|
|
78
|
+
private currentRevision;
|
|
79
|
+
private moduleName;
|
|
80
|
+
constructor(url: string, options?: RemoteEngineOptions);
|
|
81
|
+
/**
|
|
82
|
+
* Connect to the remote server
|
|
83
|
+
* Returns a Result indicating success or failure
|
|
84
|
+
*/
|
|
85
|
+
connect(): Promise<Result<void, ConnectionError>>;
|
|
86
|
+
/**
|
|
87
|
+
* Send hello message to establish session
|
|
88
|
+
*/
|
|
89
|
+
private sendHello;
|
|
90
|
+
/**
|
|
91
|
+
* Disconnect from the remote server and clean up resources
|
|
92
|
+
*/
|
|
93
|
+
disconnect(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Dispose all resources (alias for disconnect)
|
|
96
|
+
*/
|
|
97
|
+
dispose(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Dispatch an action to the remote server
|
|
100
|
+
*/
|
|
101
|
+
dispatchAction(action: string, payload?: unknown): void;
|
|
102
|
+
/**
|
|
103
|
+
* Register callback for patches
|
|
104
|
+
*/
|
|
105
|
+
onPatches(callback: (patches: Patch[]) => void): this;
|
|
106
|
+
/**
|
|
107
|
+
* Register callback for state updates
|
|
108
|
+
*/
|
|
109
|
+
onStateUpdate(callback: (state: unknown) => void): this;
|
|
110
|
+
/**
|
|
111
|
+
* Register callback for connection
|
|
112
|
+
*/
|
|
113
|
+
onConnect(callback: () => void): this;
|
|
114
|
+
/**
|
|
115
|
+
* Register callback for disconnection
|
|
116
|
+
*/
|
|
117
|
+
onDisconnect(callback: () => void): this;
|
|
118
|
+
/**
|
|
119
|
+
* Register callback for errors
|
|
120
|
+
*/
|
|
121
|
+
onError(callback: (error: Error) => void): this;
|
|
122
|
+
/**
|
|
123
|
+
* Register callback for session establishment
|
|
124
|
+
* Called when server confirms session (new or resumed)
|
|
125
|
+
*/
|
|
126
|
+
onSessionEstablished(callback: (info: SessionInfo) => void): this;
|
|
127
|
+
/**
|
|
128
|
+
* Register callback for session expiration
|
|
129
|
+
* Called when session is kicked or expires
|
|
130
|
+
*/
|
|
131
|
+
onSessionExpired(callback: (reason: string) => void): this;
|
|
132
|
+
/**
|
|
133
|
+
* Get current connection state
|
|
134
|
+
*/
|
|
135
|
+
getConnectionState(): RemoteConnectionState;
|
|
136
|
+
/**
|
|
137
|
+
* Get current app state
|
|
138
|
+
*/
|
|
139
|
+
getCurrentState(): unknown;
|
|
140
|
+
/**
|
|
141
|
+
* Get current revision
|
|
142
|
+
*/
|
|
143
|
+
getRevision(): number;
|
|
144
|
+
/**
|
|
145
|
+
* Get current session ID
|
|
146
|
+
*/
|
|
147
|
+
getSessionId(): string | null;
|
|
148
|
+
private handleMessage;
|
|
149
|
+
private handleSessionAck;
|
|
150
|
+
private handleSessionExpired;
|
|
151
|
+
private handleInitialTree;
|
|
152
|
+
private handlePatch;
|
|
153
|
+
private attemptReconnect;
|
|
154
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote UI streaming for Hypen
|
|
3
|
+
*
|
|
4
|
+
* Client-side: Connect to remote Hypen apps
|
|
5
|
+
* Server-side: Stream Hypen apps over WebSocket
|
|
6
|
+
*/
|
|
7
|
+
export { RemoteEngine } from "./client.js";
|
|
8
|
+
export type { RemoteConnectionState, RemoteEngineOptions, SessionOptions, SessionInfo, } from "./client.js";
|
|
9
|
+
export { RemoteServer, serve } from "./server.js";
|
|
10
|
+
export { SessionManager } from "./session.js";
|
|
11
|
+
export type { SessionExpireCallback } from "./session.js";
|
|
12
|
+
export type { RemoteMessage, InitialTreeMessage, PatchMessage, DispatchActionMessage, StateUpdateMessage, HelloMessage, SessionAckMessage, SessionExpiredMessage, Session, SessionConfig, RemoteClient, RemoteServerConfig, } from "./types.js";
|
|
13
|
+
export type { Patch } from "../types.js";
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RemoteServer - Stream Hypen apps over WebSocket with Session Management
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { RemoteServer, app } from "@hypen-space/core";
|
|
7
|
+
*
|
|
8
|
+
* const counter = app
|
|
9
|
+
* .defineState({ count: 0 })
|
|
10
|
+
* .onAction("increment", ({ state }) => state.count++)
|
|
11
|
+
* .onDisconnect(async ({ state, session }) => {
|
|
12
|
+
* await redis.set(`session:${session.id}`, JSON.stringify(state));
|
|
13
|
+
* })
|
|
14
|
+
* .onReconnect(async ({ session, restore }) => {
|
|
15
|
+
* const saved = await redis.get(`session:${session.id}`);
|
|
16
|
+
* if (saved) restore(JSON.parse(saved));
|
|
17
|
+
* })
|
|
18
|
+
* .onExpire(async ({ session }) => {
|
|
19
|
+
* await redis.del(`session:${session.id}`);
|
|
20
|
+
* })
|
|
21
|
+
* .build();
|
|
22
|
+
*
|
|
23
|
+
* new RemoteServer()
|
|
24
|
+
* .module("Counter", counter)
|
|
25
|
+
* .ui(`Column { Text("Count: \${state.count}") }`)
|
|
26
|
+
* .session({ ttl: 3600, concurrent: "kick-old" })
|
|
27
|
+
* .listen(3000);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import type { HypenModule } from "../app.js";
|
|
31
|
+
import type { RemoteMessage, RemoteClient, RemoteServerConfig, SessionConfig } from "./types.js";
|
|
32
|
+
/**
|
|
33
|
+
* Builder pattern for hosting Hypen apps over WebSocket
|
|
34
|
+
*/
|
|
35
|
+
export declare class RemoteServer {
|
|
36
|
+
private _module;
|
|
37
|
+
private _moduleName;
|
|
38
|
+
private _ui;
|
|
39
|
+
private _config;
|
|
40
|
+
private _sessionConfig;
|
|
41
|
+
private _onConnectionCallbacks;
|
|
42
|
+
private _onDisconnectionCallbacks;
|
|
43
|
+
private clients;
|
|
44
|
+
private nextClientId;
|
|
45
|
+
private server;
|
|
46
|
+
private sessionManager;
|
|
47
|
+
/**
|
|
48
|
+
* Set the module for this app
|
|
49
|
+
*/
|
|
50
|
+
module(name: string, module: HypenModule<any>): this;
|
|
51
|
+
/**
|
|
52
|
+
* Set the UI DSL string
|
|
53
|
+
*/
|
|
54
|
+
ui(dsl: string): this;
|
|
55
|
+
/**
|
|
56
|
+
* Set server configuration
|
|
57
|
+
*/
|
|
58
|
+
config(config: RemoteServerConfig): this;
|
|
59
|
+
/**
|
|
60
|
+
* Configure session management
|
|
61
|
+
*/
|
|
62
|
+
session(config: SessionConfig): this;
|
|
63
|
+
/**
|
|
64
|
+
* Register connection callback
|
|
65
|
+
*/
|
|
66
|
+
onConnection(callback: (client: RemoteClient) => void): this;
|
|
67
|
+
/**
|
|
68
|
+
* Register disconnection callback
|
|
69
|
+
*/
|
|
70
|
+
onDisconnection(callback: (client: RemoteClient) => void): this;
|
|
71
|
+
/**
|
|
72
|
+
* Start the WebSocket server
|
|
73
|
+
*/
|
|
74
|
+
listen(port?: number): this;
|
|
75
|
+
/**
|
|
76
|
+
* Stop the server
|
|
77
|
+
*/
|
|
78
|
+
stop(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Get the server URL
|
|
81
|
+
*/
|
|
82
|
+
get url(): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* Handle new WebSocket connection
|
|
85
|
+
* Waits for hello message before fully initializing
|
|
86
|
+
*/
|
|
87
|
+
private handleOpen;
|
|
88
|
+
/**
|
|
89
|
+
* Initialize session for a client (new or resumed)
|
|
90
|
+
*/
|
|
91
|
+
private initializeSession;
|
|
92
|
+
/**
|
|
93
|
+
* Set up the render callback for streaming patches
|
|
94
|
+
*/
|
|
95
|
+
private setupRenderCallback;
|
|
96
|
+
/**
|
|
97
|
+
* Handle concurrent connection based on policy
|
|
98
|
+
* Returns true if connection is allowed, false if rejected
|
|
99
|
+
*/
|
|
100
|
+
private handleConcurrentConnection;
|
|
101
|
+
/**
|
|
102
|
+
* Trigger onReconnect hook
|
|
103
|
+
*/
|
|
104
|
+
private triggerReconnect;
|
|
105
|
+
/**
|
|
106
|
+
* Handle incoming WebSocket message
|
|
107
|
+
*/
|
|
108
|
+
private handleMessage;
|
|
109
|
+
/**
|
|
110
|
+
* Handle WebSocket close - suspend session instead of destroying
|
|
111
|
+
*/
|
|
112
|
+
private handleClose;
|
|
113
|
+
/**
|
|
114
|
+
* Get current client count
|
|
115
|
+
*/
|
|
116
|
+
getClientCount(): number;
|
|
117
|
+
/**
|
|
118
|
+
* Get session stats
|
|
119
|
+
*/
|
|
120
|
+
getSessionStats(): {
|
|
121
|
+
activeSessions: number;
|
|
122
|
+
pendingSessions: number;
|
|
123
|
+
totalConnections: number;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Broadcast a message to all connected clients
|
|
127
|
+
*/
|
|
128
|
+
broadcast(message: RemoteMessage): void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Convenience function to create and start a RemoteServer
|
|
132
|
+
*/
|
|
133
|
+
export declare function serve(options: {
|
|
134
|
+
module: HypenModule<any>;
|
|
135
|
+
moduleName?: string;
|
|
136
|
+
ui: string;
|
|
137
|
+
port?: number;
|
|
138
|
+
hostname?: string;
|
|
139
|
+
session?: SessionConfig;
|
|
140
|
+
onConnection?: (client: RemoteClient) => void;
|
|
141
|
+
onDisconnection?: (client: RemoteClient) => void;
|
|
142
|
+
}): RemoteServer;
|