@hypen-space/core 0.2.12 → 0.3.1

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 (49) hide show
  1. package/README.md +182 -11
  2. package/dist/src/app.js +470 -44
  3. package/dist/src/app.js.map +7 -5
  4. package/dist/src/components/builtin.js +470 -44
  5. package/dist/src/components/builtin.js.map +7 -5
  6. package/dist/src/discovery.js +559 -65
  7. package/dist/src/discovery.js.map +8 -6
  8. package/dist/src/engine.js +18 -9
  9. package/dist/src/engine.js.map +3 -3
  10. package/dist/src/index.browser.js +870 -81
  11. package/dist/src/index.browser.js.map +11 -7
  12. package/dist/src/index.js +1591 -125
  13. package/dist/src/index.js.map +17 -10
  14. package/dist/src/plugin.js +2 -2
  15. package/dist/src/plugin.js.map +2 -2
  16. package/dist/src/remote/client.js +525 -35
  17. package/dist/src/remote/client.js.map +7 -4
  18. package/dist/src/remote/index.js +1796 -35
  19. package/dist/src/remote/index.js.map +13 -4
  20. package/dist/src/router.js +55 -29
  21. package/dist/src/router.js.map +3 -3
  22. package/dist/src/state.js +57 -29
  23. package/dist/src/state.js.map +3 -3
  24. package/package.json +8 -2
  25. package/src/app.ts +292 -13
  26. package/src/discovery.ts +123 -18
  27. package/src/disposable.ts +281 -0
  28. package/src/engine.ts +29 -10
  29. package/src/hypen.ts +209 -0
  30. package/src/index.browser.ts +17 -1
  31. package/src/index.ts +148 -12
  32. package/src/logger.ts +338 -0
  33. package/src/plugin.ts +1 -1
  34. package/src/remote/client.ts +263 -56
  35. package/src/remote/index.ts +25 -1
  36. package/src/remote/server.ts +652 -0
  37. package/src/remote/session.ts +256 -0
  38. package/src/remote/types.ts +68 -1
  39. package/src/result.ts +260 -0
  40. package/src/retry.ts +306 -0
  41. package/src/state.ts +103 -45
  42. package/wasm-browser/README.md +4 -0
  43. package/wasm-browser/hypen_engine_bg.wasm +0 -0
  44. package/wasm-browser/package.json +1 -1
  45. package/wasm-node/README.md +4 -0
  46. package/wasm-node/hypen_engine_bg.wasm +0 -0
  47. package/wasm-node/package.json +1 -1
  48. package/wasm-browser/hypen_engine_bg.js +0 -736
  49. package/wasm-node/hypen_engine_bg.js +0 -736
package/src/index.ts CHANGED
@@ -1,25 +1,43 @@
1
1
  /**
2
- * @hypen/core - Hypen Core Engine
2
+ * @hypen-space/core - Hypen Core Engine
3
3
  *
4
4
  * Platform-agnostic reactive UI runtime.
5
5
  * Use with @hypen/web for browser rendering, or build your own renderer.
6
6
  *
7
- * ## Quick Start
7
+ * ## Quick Start (Single-File Component)
8
8
  *
9
9
  * ```typescript
10
- * import { Engine, app } from "@hypen/core";
10
+ * import { app, hypen, state } from "@hypen-space/core";
11
11
  *
12
- * // Create a stateful module
13
- * const counter = app
12
+ * export default app
13
+ * .defineState({ count: 0 })
14
+ * .onAction("increment", ({ state }) => {
15
+ * state.count += 1;
16
+ * })
17
+ * .ui(hypen`
18
+ * Column {
19
+ * Text("Count: ${state.count}")
20
+ * Button { Text("+") }
21
+ * .onClick("@actions.increment")
22
+ * }
23
+ * `);
24
+ * ```
25
+ *
26
+ * ## Two-File Component (Legacy)
27
+ *
28
+ * ```typescript
29
+ * // component.ts
30
+ * import { app } from "@hypen-space/core";
31
+ *
32
+ * export default app
14
33
  * .defineState({ count: 0 })
15
34
  * .onAction("increment", ({ state }) => {
16
35
  * state.count++;
17
36
  * })
18
37
  * .build();
19
38
  *
20
- * // Initialize the engine
21
- * const engine = new Engine();
22
- * await engine.init();
39
+ * // component.hypen (separate file)
40
+ * // Column { Text("Count: ${state.count}") ... }
23
41
  * ```
24
42
  */
25
43
 
@@ -46,6 +64,14 @@ export type { EngineInitOptions } from "./engine.browser.js";
46
64
  // ============================================================================
47
65
 
48
66
  export { app, HypenApp, HypenAppBuilder, HypenModuleInstance } from "./app.js";
67
+
68
+ // ============================================================================
69
+ // TEMPLATE SYSTEM (Single-File Components)
70
+ // ============================================================================
71
+
72
+ export { hypen, state, item, index } from "./hypen.js";
73
+ export type { StateProxy, ItemProxy } from "./hypen.js";
74
+
49
75
  export type {
50
76
  IEngine,
51
77
  RouterContext,
@@ -57,6 +83,17 @@ export type {
57
83
  ActionHandler,
58
84
  HypenModuleDefinition,
59
85
  HypenModule,
86
+ // Session lifecycle hooks
87
+ DisconnectContext,
88
+ ReconnectContext,
89
+ ExpireContext,
90
+ DisconnectHandler,
91
+ ReconnectHandler,
92
+ ExpireHandler,
93
+ // Error handling
94
+ ErrorContext,
95
+ ErrorHandler,
96
+ ErrorHandlerResult,
60
97
  } from "./app.js";
61
98
 
62
99
  // ============================================================================
@@ -67,6 +104,8 @@ export {
67
104
  createObservableState,
68
105
  batchStateUpdates,
69
106
  getStateSnapshot,
107
+ isStateProxy,
108
+ unwrapProxy,
70
109
  } from "./state.js";
71
110
  export type {
72
111
  StatePath,
@@ -110,20 +149,37 @@ export type { ModuleReference } from "./context.js";
110
149
  // REMOTE UI
111
150
  // ============================================================================
112
151
 
152
+ // Client
113
153
  export { RemoteEngine } from "./remote/client.js";
154
+ export type {
155
+ RemoteConnectionState,
156
+ RemoteEngineOptions,
157
+ SessionOptions,
158
+ SessionInfo,
159
+ } from "./remote/client.js";
160
+
161
+ // Server
162
+ export { RemoteServer, serve } from "./remote/server.js";
163
+
164
+ // Session Management
165
+ export { SessionManager } from "./remote/session.js";
166
+ export type { SessionExpireCallback } from "./remote/session.js";
167
+
168
+ // Types
114
169
  export type {
115
170
  RemoteMessage,
116
171
  InitialTreeMessage,
117
172
  PatchMessage,
118
173
  DispatchActionMessage,
119
174
  StateUpdateMessage,
175
+ HelloMessage,
176
+ SessionAckMessage,
177
+ SessionExpiredMessage,
178
+ Session,
179
+ SessionConfig,
120
180
  RemoteClient,
121
181
  RemoteServerConfig,
122
182
  } from "./remote/types.js";
123
- export type {
124
- RemoteConnectionState,
125
- RemoteEngineOptions,
126
- } from "./remote/client.js";
127
183
 
128
184
  // ============================================================================
129
185
  // COMPONENT LOADER
@@ -173,3 +229,83 @@ export type { HypenPluginOptions } from "./plugin.js";
173
229
  // ============================================================================
174
230
 
175
231
  export { Router, Route, Link } from "./components/builtin.js";
232
+
233
+ // ============================================================================
234
+ // RESULT TYPE (Error Handling)
235
+ // ============================================================================
236
+
237
+ export {
238
+ Ok,
239
+ Err,
240
+ isOk,
241
+ isErr,
242
+ fromPromise,
243
+ fromTry,
244
+ map,
245
+ mapErr,
246
+ flatMap,
247
+ unwrap,
248
+ unwrapOr,
249
+ unwrapOrElse,
250
+ match,
251
+ all,
252
+ HypenError,
253
+ ActionError,
254
+ ConnectionError,
255
+ StateError,
256
+ } from "./result.js";
257
+ export type { Result } from "./result.js";
258
+
259
+ // ============================================================================
260
+ // DISPOSABLE PATTERN (Resource Management)
261
+ // ============================================================================
262
+
263
+ export {
264
+ DisposableStack,
265
+ DisposableMixin,
266
+ isDisposable,
267
+ disposableListener,
268
+ disposableTimeout,
269
+ disposableInterval,
270
+ disposableWebSocket,
271
+ disposableAbortController,
272
+ disposableSubscription,
273
+ getElementDisposables,
274
+ disposeElement,
275
+ hasElementDisposables,
276
+ compositeDisposable,
277
+ using,
278
+ usingSync,
279
+ } from "./disposable.js";
280
+ export type { Disposable } from "./disposable.js";
281
+
282
+ // ============================================================================
283
+ // Retry Utility
284
+ // ============================================================================
285
+
286
+ export {
287
+ retry,
288
+ retryResult,
289
+ withRetry,
290
+ RetryConditions,
291
+ RetryPresets,
292
+ } from "./retry.js";
293
+ export type { RetryOptions } from "./retry.js";
294
+
295
+ // ============================================================================
296
+ // Logger
297
+ // ============================================================================
298
+
299
+ export {
300
+ Logger,
301
+ createLogger,
302
+ logger,
303
+ log,
304
+ frameworkLoggers,
305
+ setLogLevel,
306
+ getLogLevel,
307
+ configureLogger,
308
+ enableLogging,
309
+ disableLogging,
310
+ } from "./logger.js";
311
+ export type { LogLevel, LoggerConfig, LogHandler } from "./logger.js";
package/src/logger.ts ADDED
@@ -0,0 +1,338 @@
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
+
8
+ // ============================================================================
9
+ // Types
10
+ // ============================================================================
11
+
12
+ export type LogLevel = "debug" | "info" | "warn" | "error" | "none";
13
+
14
+ export interface LoggerConfig {
15
+ /** Minimum log level (default: "debug" in dev, "error" in prod) */
16
+ level: LogLevel;
17
+ /** Enable colored output (default: true) */
18
+ colors: boolean;
19
+ /** Include timestamps (default: false) */
20
+ timestamps: boolean;
21
+ /** Custom log handler (default: console) */
22
+ handler?: LogHandler;
23
+ }
24
+
25
+ export interface LogHandler {
26
+ debug(tag: string, ...args: unknown[]): void;
27
+ info(tag: string, ...args: unknown[]): void;
28
+ warn(tag: string, ...args: unknown[]): void;
29
+ error(tag: string, ...args: unknown[]): void;
30
+ }
31
+
32
+ // ============================================================================
33
+ // Constants
34
+ // ============================================================================
35
+
36
+ const LOG_LEVEL_ORDER: Record<LogLevel, number> = {
37
+ debug: 0,
38
+ info: 1,
39
+ warn: 2,
40
+ error: 3,
41
+ none: 4,
42
+ };
43
+
44
+ const LOG_LEVEL_COLORS: Record<Exclude<LogLevel, "none">, string> = {
45
+ debug: "\x1b[36m", // Cyan
46
+ info: "\x1b[32m", // Green
47
+ warn: "\x1b[33m", // Yellow
48
+ error: "\x1b[31m", // Red
49
+ };
50
+
51
+ const RESET_COLOR = "\x1b[0m";
52
+
53
+ // ============================================================================
54
+ // Global Configuration
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Detect if running in production environment
59
+ */
60
+ function isProduction(): boolean {
61
+ if (typeof process !== "undefined" && process.env) {
62
+ return process.env.NODE_ENV === "production";
63
+ }
64
+ return false;
65
+ }
66
+
67
+ /**
68
+ * Default configuration
69
+ */
70
+ let config: LoggerConfig = {
71
+ level: isProduction() ? "error" : "debug",
72
+ colors: true,
73
+ timestamps: false,
74
+ };
75
+
76
+ // ============================================================================
77
+ // Configuration API
78
+ // ============================================================================
79
+
80
+ /**
81
+ * Set the global log level
82
+ */
83
+ export function setLogLevel(level: LogLevel): void {
84
+ config.level = level;
85
+ }
86
+
87
+ /**
88
+ * Get the current log level
89
+ */
90
+ export function getLogLevel(): LogLevel {
91
+ return config.level;
92
+ }
93
+
94
+ /**
95
+ * Configure the logger
96
+ */
97
+ export function configureLogger(options: Partial<LoggerConfig>): void {
98
+ config = { ...config, ...options };
99
+ }
100
+
101
+ /**
102
+ * Enable all logging (sets level to "debug")
103
+ */
104
+ export function enableLogging(): void {
105
+ config.level = "debug";
106
+ }
107
+
108
+ /**
109
+ * Disable all logging (sets level to "none")
110
+ */
111
+ export function disableLogging(): void {
112
+ config.level = "none";
113
+ }
114
+
115
+ // ============================================================================
116
+ // Utility Functions
117
+ // ============================================================================
118
+
119
+ function shouldLog(level: LogLevel): boolean {
120
+ return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
121
+ }
122
+
123
+ function formatTag(tag: string, level: LogLevel): string {
124
+ const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
125
+
126
+ if (config.colors && level !== "none") {
127
+ const color = LOG_LEVEL_COLORS[level as Exclude<LogLevel, "none">];
128
+ return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
129
+ }
130
+
131
+ return `${timestamp}[${tag}]`;
132
+ }
133
+
134
+ // ============================================================================
135
+ // Logger Class
136
+ // ============================================================================
137
+
138
+ /**
139
+ * Tagged logger instance
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const log = createLogger("MyComponent");
144
+ * log.debug("initialized with", { props });
145
+ * log.error("failed to load", error);
146
+ * ```
147
+ */
148
+ export class Logger {
149
+ private readonly tag: string;
150
+
151
+ constructor(tag: string) {
152
+ this.tag = tag;
153
+ }
154
+
155
+ debug(...args: unknown[]): void {
156
+ if (!shouldLog("debug")) return;
157
+
158
+ if (config.handler) {
159
+ config.handler.debug(this.tag, ...args);
160
+ } else {
161
+ console.log(formatTag(this.tag, "debug"), ...args);
162
+ }
163
+ }
164
+
165
+ info(...args: unknown[]): void {
166
+ if (!shouldLog("info")) return;
167
+
168
+ if (config.handler) {
169
+ config.handler.info(this.tag, ...args);
170
+ } else {
171
+ console.info(formatTag(this.tag, "info"), ...args);
172
+ }
173
+ }
174
+
175
+ warn(...args: unknown[]): void {
176
+ if (!shouldLog("warn")) return;
177
+
178
+ if (config.handler) {
179
+ config.handler.warn(this.tag, ...args);
180
+ } else {
181
+ console.warn(formatTag(this.tag, "warn"), ...args);
182
+ }
183
+ }
184
+
185
+ error(...args: unknown[]): void {
186
+ if (!shouldLog("error")) return;
187
+
188
+ if (config.handler) {
189
+ config.handler.error(this.tag, ...args);
190
+ } else {
191
+ console.error(formatTag(this.tag, "error"), ...args);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Time a function execution
197
+ */
198
+ time<T>(label: string, fn: () => T): T {
199
+ if (!shouldLog("debug")) {
200
+ return fn();
201
+ }
202
+
203
+ const start = performance.now();
204
+ try {
205
+ return fn();
206
+ } finally {
207
+ const duration = performance.now() - start;
208
+ this.debug(`${label}: ${duration.toFixed(2)}ms`);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Time an async function execution
214
+ */
215
+ async timeAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {
216
+ if (!shouldLog("debug")) {
217
+ return fn();
218
+ }
219
+
220
+ const start = performance.now();
221
+ try {
222
+ return await fn();
223
+ } finally {
224
+ const duration = performance.now() - start;
225
+ this.debug(`${label}: ${duration.toFixed(2)}ms`);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Create a child logger with additional context
231
+ */
232
+ child(subTag: string): Logger {
233
+ return new Logger(`${this.tag}:${subTag}`);
234
+ }
235
+
236
+ /**
237
+ * Conditionally log based on a condition
238
+ */
239
+ debugIf(condition: boolean, ...args: unknown[]): void {
240
+ if (condition) this.debug(...args);
241
+ }
242
+
243
+ warnIf(condition: boolean, ...args: unknown[]): void {
244
+ if (condition) this.warn(...args);
245
+ }
246
+
247
+ errorIf(condition: boolean, ...args: unknown[]): void {
248
+ if (condition) this.error(...args);
249
+ }
250
+
251
+ /**
252
+ * Log once (useful for deprecation warnings)
253
+ */
254
+ private loggedOnce = new Set<string>();
255
+
256
+ warnOnce(key: string, ...args: unknown[]): void {
257
+ if (this.loggedOnce.has(key)) return;
258
+ this.loggedOnce.add(key);
259
+ this.warn(...args);
260
+ }
261
+
262
+ debugOnce(key: string, ...args: unknown[]): void {
263
+ if (this.loggedOnce.has(key)) return;
264
+ this.loggedOnce.add(key);
265
+ this.debug(...args);
266
+ }
267
+ }
268
+
269
+ // ============================================================================
270
+ // Factory Functions
271
+ // ============================================================================
272
+
273
+ /**
274
+ * Create a tagged logger
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * const log = createLogger("Router");
279
+ * log.info("navigating to", path);
280
+ * ```
281
+ */
282
+ export function createLogger(tag: string): Logger {
283
+ return new Logger(tag);
284
+ }
285
+
286
+ // ============================================================================
287
+ // Default Logger Instance
288
+ // ============================================================================
289
+
290
+ /**
291
+ * Default logger for general use
292
+ */
293
+ export const logger = createLogger("Hypen");
294
+
295
+ // ============================================================================
296
+ // Shorthand Functions (untagged)
297
+ // ============================================================================
298
+
299
+ /**
300
+ * Shorthand logging functions for quick use
301
+ * Prefer createLogger() for component-specific logging
302
+ */
303
+ export const log = {
304
+ debug: (tag: string, ...args: unknown[]): void => {
305
+ if (!shouldLog("debug")) return;
306
+ console.log(formatTag(tag, "debug"), ...args);
307
+ },
308
+
309
+ info: (tag: string, ...args: unknown[]): void => {
310
+ if (!shouldLog("info")) return;
311
+ console.info(formatTag(tag, "info"), ...args);
312
+ },
313
+
314
+ warn: (tag: string, ...args: unknown[]): void => {
315
+ if (!shouldLog("warn")) return;
316
+ console.warn(formatTag(tag, "warn"), ...args);
317
+ },
318
+
319
+ error: (tag: string, ...args: unknown[]): void => {
320
+ if (!shouldLog("error")) return;
321
+ console.error(formatTag(tag, "error"), ...args);
322
+ },
323
+ };
324
+
325
+ // ============================================================================
326
+ // Predefined Loggers for Framework Components
327
+ // ============================================================================
328
+
329
+ export const frameworkLoggers = {
330
+ engine: createLogger("Engine"),
331
+ router: createLogger("Router"),
332
+ state: createLogger("State"),
333
+ events: createLogger("Events"),
334
+ remote: createLogger("Remote"),
335
+ renderer: createLogger("Renderer"),
336
+ module: createLogger("Module"),
337
+ lifecycle: createLogger("Lifecycle"),
338
+ };
package/src/plugin.ts CHANGED
@@ -186,7 +186,7 @@ export default { module: _module, template: ${JSON.stringify(template)}, name: $
186
186
  // No TypeScript module - create stateless component
187
187
  log("No module found, creating stateless component");
188
188
  contents = `
189
- import { app } from "@hypen/core";
189
+ import { app } from "@hypen-space/core";
190
190
  const _module = app.defineState({}).build();
191
191
  export const module = _module;
192
192
  export const template = ${JSON.stringify(template)};