@hypen-space/core 0.2.12 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) 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 +862 -81
  11. package/dist/src/index.browser.js.map +10 -6
  12. package/dist/src/index.js +1590 -124
  13. package/dist/src/index.js.map +16 -9
  14. package/dist/src/remote/client.js +525 -35
  15. package/dist/src/remote/client.js.map +7 -4
  16. package/dist/src/remote/index.js +1796 -35
  17. package/dist/src/remote/index.js.map +13 -4
  18. package/dist/src/router.js +55 -29
  19. package/dist/src/router.js.map +3 -3
  20. package/dist/src/state.js +57 -29
  21. package/dist/src/state.js.map +3 -3
  22. package/package.json +8 -2
  23. package/src/app.ts +292 -13
  24. package/src/discovery.ts +123 -18
  25. package/src/disposable.ts +281 -0
  26. package/src/engine.ts +29 -10
  27. package/src/hypen.ts +209 -0
  28. package/src/index.ts +147 -11
  29. package/src/logger.ts +338 -0
  30. package/src/remote/client.ts +263 -56
  31. package/src/remote/index.ts +25 -1
  32. package/src/remote/server.ts +652 -0
  33. package/src/remote/session.ts +256 -0
  34. package/src/remote/types.ts +68 -1
  35. package/src/result.ts +260 -0
  36. package/src/retry.ts +306 -0
  37. package/src/state.ts +103 -45
  38. package/wasm-browser/README.md +4 -0
  39. package/wasm-browser/hypen_engine_bg.wasm +0 -0
  40. package/wasm-browser/package.json +1 -1
  41. package/wasm-node/README.md +4 -0
  42. package/wasm-node/hypen_engine_bg.wasm +0 -0
  43. package/wasm-node/package.json +1 -1
  44. package/wasm-browser/hypen_engine_bg.js +0 -736
  45. package/wasm-node/hypen_engine_bg.js +0 -736
@@ -15,39 +15,34 @@ function deepClone(obj) {
15
15
  if (obj === null || typeof obj !== "object") {
16
16
  return obj;
17
17
  }
18
+ if (typeof obj === "function") {
19
+ return obj;
20
+ }
21
+ if (typeof obj.__getSnapshot === "function") {
22
+ return obj.__getSnapshot();
23
+ }
24
+ if (obj instanceof WeakMap || obj instanceof WeakSet) {
25
+ return obj;
26
+ }
18
27
  const visited = new WeakMap;
19
28
  function cloneInternal(value) {
20
29
  if (value === null || typeof value !== "object") {
21
30
  return value;
22
31
  }
32
+ if (typeof value === "function") {
33
+ return value;
34
+ }
23
35
  if (visited.has(value)) {
24
36
  return visited.get(value);
25
37
  }
26
- if (value instanceof Date) {
27
- return new Date(value.getTime());
28
- }
29
- if (value instanceof RegExp) {
30
- return new RegExp(value.source, value.flags);
31
- }
32
- if (value instanceof Map) {
33
- const mapClone = new Map;
34
- visited.set(value, mapClone);
35
- for (const [k, v] of value.entries()) {
36
- mapClone.set(cloneInternal(k), cloneInternal(v));
37
- }
38
- return mapClone;
39
- }
40
- if (value instanceof Set) {
41
- const setClone = new Set;
42
- visited.set(value, setClone);
43
- for (const item of value.values()) {
44
- setClone.add(cloneInternal(item));
45
- }
46
- return setClone;
47
- }
48
38
  if (value instanceof WeakMap || value instanceof WeakSet) {
49
39
  return value;
50
40
  }
41
+ if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
42
+ try {
43
+ return structuredClone(value);
44
+ } catch {}
45
+ }
51
46
  if (Array.isArray(value)) {
52
47
  const arrClone = [];
53
48
  visited.set(value, arrClone);
@@ -59,7 +54,7 @@ function deepClone(obj) {
59
54
  const objClone = {};
60
55
  visited.set(value, objClone);
61
56
  for (const key in value) {
62
- if (value.hasOwnProperty(key)) {
57
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
63
58
  objClone[key] = cloneInternal(value[key]);
64
59
  }
65
60
  }
@@ -161,12 +156,15 @@ function createObservableState(initialState, options) {
161
156
  }
162
157
  const proxyCache = new WeakMap;
163
158
  function createProxy(target, basePath) {
164
- if (proxyCache.has(target)) {
165
- return proxyCache.get(target);
166
- }
159
+ const cached = proxyCache.get(target);
160
+ if (cached)
161
+ return cached;
167
162
  const proxy = new Proxy(target, {
168
163
  get(obj, prop) {
169
- const value = obj[prop];
164
+ if (prop === IS_PROXY)
165
+ return true;
166
+ if (prop === RAW_TARGET)
167
+ return obj;
170
168
  if (prop === "__beginBatch") {
171
169
  return () => {
172
170
  batchDepth++;
@@ -183,16 +181,28 @@ function createObservableState(initialState, options) {
183
181
  if (prop === "__getSnapshot") {
184
182
  return () => deepClone(obj);
185
183
  }
184
+ const value = obj[prop];
186
185
  if (value && typeof value === "object") {
186
+ if (value[IS_PROXY]) {
187
+ return value;
188
+ }
187
189
  if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
188
190
  return value;
189
191
  }
190
- return createProxy(value, basePath ? `${basePath}.${String(prop)}` : String(prop));
192
+ const cachedNested = proxyCache.get(value);
193
+ if (cachedNested) {
194
+ return cachedNested;
195
+ }
196
+ const nestedProxy = createProxy(value, basePath ? `${basePath}.${String(prop)}` : String(prop));
197
+ return nestedProxy;
191
198
  }
192
199
  return value;
193
200
  },
194
201
  set(obj, prop, value) {
195
202
  const oldValue = obj[prop];
203
+ if (value && typeof value === "object" && value[IS_PROXY]) {
204
+ value = value[RAW_TARGET];
205
+ }
196
206
  obj[prop] = value;
197
207
  if (oldValue !== value) {
198
208
  scheduleBatch();
@@ -233,6 +243,344 @@ function getStateSnapshot(state) {
233
243
  }
234
244
  return deepClone(state);
235
245
  }
246
+ function isStateProxy(value) {
247
+ return value !== null && typeof value === "object" && value[IS_PROXY] === true;
248
+ }
249
+ function unwrapProxy(value) {
250
+ if (value !== null && typeof value === "object" && value[IS_PROXY]) {
251
+ return value[RAW_TARGET];
252
+ }
253
+ return value;
254
+ }
255
+ var IS_PROXY, RAW_TARGET;
256
+ var init_state = __esm(() => {
257
+ IS_PROXY = Symbol.for("hypen.isProxy");
258
+ RAW_TARGET = Symbol.for("hypen.rawTarget");
259
+ });
260
+
261
+ // src/result.ts
262
+ function Ok(value) {
263
+ return { ok: true, value };
264
+ }
265
+ function Err(error) {
266
+ return { ok: false, error };
267
+ }
268
+ function isOk(result) {
269
+ return result.ok;
270
+ }
271
+ function isErr(result) {
272
+ return !result.ok;
273
+ }
274
+ async function fromPromise(promise, mapError) {
275
+ try {
276
+ const value = await promise;
277
+ return Ok(value);
278
+ } catch (e) {
279
+ if (mapError) {
280
+ return Err(mapError(e));
281
+ }
282
+ return Err(e);
283
+ }
284
+ }
285
+ function fromTry(fn, mapError) {
286
+ try {
287
+ return Ok(fn());
288
+ } catch (e) {
289
+ if (mapError) {
290
+ return Err(mapError(e));
291
+ }
292
+ return Err(e);
293
+ }
294
+ }
295
+ function map(result, fn) {
296
+ if (result.ok) {
297
+ return Ok(fn(result.value));
298
+ }
299
+ return result;
300
+ }
301
+ function mapErr(result, fn) {
302
+ if (!result.ok) {
303
+ return Err(fn(result.error));
304
+ }
305
+ return result;
306
+ }
307
+ function flatMap(result, fn) {
308
+ if (result.ok) {
309
+ return fn(result.value);
310
+ }
311
+ return result;
312
+ }
313
+ function unwrap(result) {
314
+ if (result.ok) {
315
+ return result.value;
316
+ }
317
+ throw result.error;
318
+ }
319
+ function unwrapOr(result, defaultValue) {
320
+ if (result.ok) {
321
+ return result.value;
322
+ }
323
+ return defaultValue;
324
+ }
325
+ function unwrapOrElse(result, fn) {
326
+ if (result.ok) {
327
+ return result.value;
328
+ }
329
+ return fn(result.error);
330
+ }
331
+ function match(result, handlers) {
332
+ if (result.ok) {
333
+ return handlers.ok(result.value);
334
+ }
335
+ return handlers.err(result.error);
336
+ }
337
+ function all(results) {
338
+ const values = [];
339
+ for (const result of results) {
340
+ if (!result.ok) {
341
+ return result;
342
+ }
343
+ values.push(result.value);
344
+ }
345
+ return Ok(values);
346
+ }
347
+ var HypenError, ActionError, ConnectionError, StateError;
348
+ var init_result = __esm(() => {
349
+ HypenError = class HypenError extends Error {
350
+ code;
351
+ context;
352
+ cause;
353
+ constructor(code, message, options) {
354
+ super(message);
355
+ this.name = "HypenError";
356
+ this.code = code;
357
+ this.context = options?.context;
358
+ this.cause = options?.cause;
359
+ Object.setPrototypeOf(this, new.target.prototype);
360
+ }
361
+ };
362
+ ActionError = class ActionError extends HypenError {
363
+ actionName;
364
+ constructor(actionName, cause) {
365
+ super("ACTION_ERROR", `Action handler "${actionName}" failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
366
+ context: { actionName },
367
+ cause: cause instanceof Error ? cause : undefined
368
+ });
369
+ this.name = "ActionError";
370
+ this.actionName = actionName;
371
+ }
372
+ };
373
+ ConnectionError = class ConnectionError extends HypenError {
374
+ url;
375
+ attempt;
376
+ constructor(url, cause, attempt) {
377
+ super("CONNECTION_ERROR", `Connection to "${url}" failed${attempt ? ` (attempt ${attempt})` : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, {
378
+ context: { url, attempt },
379
+ cause: cause instanceof Error ? cause : undefined
380
+ });
381
+ this.name = "ConnectionError";
382
+ this.url = url;
383
+ this.attempt = attempt;
384
+ }
385
+ };
386
+ StateError = class StateError extends HypenError {
387
+ path;
388
+ constructor(message, path, cause) {
389
+ super("STATE_ERROR", message, {
390
+ context: { path },
391
+ cause: cause instanceof Error ? cause : undefined
392
+ });
393
+ this.name = "StateError";
394
+ this.path = path;
395
+ }
396
+ };
397
+ });
398
+
399
+ // src/logger.ts
400
+ function isProduction() {
401
+ if (typeof process !== "undefined" && process.env) {
402
+ return false;
403
+ }
404
+ return false;
405
+ }
406
+ function setLogLevel(level) {
407
+ config.level = level;
408
+ }
409
+ function getLogLevel() {
410
+ return config.level;
411
+ }
412
+ function configureLogger(options) {
413
+ config = { ...config, ...options };
414
+ }
415
+ function enableLogging() {
416
+ config.level = "debug";
417
+ }
418
+ function disableLogging() {
419
+ config.level = "none";
420
+ }
421
+ function shouldLog(level) {
422
+ return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
423
+ }
424
+ function formatTag(tag, level) {
425
+ const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
426
+ if (config.colors && level !== "none") {
427
+ const color = LOG_LEVEL_COLORS[level];
428
+ return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
429
+ }
430
+ return `${timestamp}[${tag}]`;
431
+ }
432
+
433
+ class Logger {
434
+ tag;
435
+ constructor(tag) {
436
+ this.tag = tag;
437
+ }
438
+ debug(...args) {
439
+ if (!shouldLog("debug"))
440
+ return;
441
+ if (config.handler) {
442
+ config.handler.debug(this.tag, ...args);
443
+ } else {
444
+ console.log(formatTag(this.tag, "debug"), ...args);
445
+ }
446
+ }
447
+ info(...args) {
448
+ if (!shouldLog("info"))
449
+ return;
450
+ if (config.handler) {
451
+ config.handler.info(this.tag, ...args);
452
+ } else {
453
+ console.info(formatTag(this.tag, "info"), ...args);
454
+ }
455
+ }
456
+ warn(...args) {
457
+ if (!shouldLog("warn"))
458
+ return;
459
+ if (config.handler) {
460
+ config.handler.warn(this.tag, ...args);
461
+ } else {
462
+ console.warn(formatTag(this.tag, "warn"), ...args);
463
+ }
464
+ }
465
+ error(...args) {
466
+ if (!shouldLog("error"))
467
+ return;
468
+ if (config.handler) {
469
+ config.handler.error(this.tag, ...args);
470
+ } else {
471
+ console.error(formatTag(this.tag, "error"), ...args);
472
+ }
473
+ }
474
+ time(label, fn) {
475
+ if (!shouldLog("debug")) {
476
+ return fn();
477
+ }
478
+ const start = performance.now();
479
+ try {
480
+ return fn();
481
+ } finally {
482
+ const duration = performance.now() - start;
483
+ this.debug(`${label}: ${duration.toFixed(2)}ms`);
484
+ }
485
+ }
486
+ async timeAsync(label, fn) {
487
+ if (!shouldLog("debug")) {
488
+ return fn();
489
+ }
490
+ const start = performance.now();
491
+ try {
492
+ return await fn();
493
+ } finally {
494
+ const duration = performance.now() - start;
495
+ this.debug(`${label}: ${duration.toFixed(2)}ms`);
496
+ }
497
+ }
498
+ child(subTag) {
499
+ return new Logger(`${this.tag}:${subTag}`);
500
+ }
501
+ debugIf(condition, ...args) {
502
+ if (condition)
503
+ this.debug(...args);
504
+ }
505
+ warnIf(condition, ...args) {
506
+ if (condition)
507
+ this.warn(...args);
508
+ }
509
+ errorIf(condition, ...args) {
510
+ if (condition)
511
+ this.error(...args);
512
+ }
513
+ loggedOnce = new Set;
514
+ warnOnce(key, ...args) {
515
+ if (this.loggedOnce.has(key))
516
+ return;
517
+ this.loggedOnce.add(key);
518
+ this.warn(...args);
519
+ }
520
+ debugOnce(key, ...args) {
521
+ if (this.loggedOnce.has(key))
522
+ return;
523
+ this.loggedOnce.add(key);
524
+ this.debug(...args);
525
+ }
526
+ }
527
+ function createLogger(tag) {
528
+ return new Logger(tag);
529
+ }
530
+ var LOG_LEVEL_ORDER, LOG_LEVEL_COLORS, RESET_COLOR = "\x1B[0m", config, logger, log, frameworkLoggers;
531
+ var init_logger = __esm(() => {
532
+ LOG_LEVEL_ORDER = {
533
+ debug: 0,
534
+ info: 1,
535
+ warn: 2,
536
+ error: 3,
537
+ none: 4
538
+ };
539
+ LOG_LEVEL_COLORS = {
540
+ debug: "\x1B[36m",
541
+ info: "\x1B[32m",
542
+ warn: "\x1B[33m",
543
+ error: "\x1B[31m"
544
+ };
545
+ config = {
546
+ level: isProduction() ? "error" : "debug",
547
+ colors: true,
548
+ timestamps: false
549
+ };
550
+ logger = createLogger("Hypen");
551
+ log = {
552
+ debug: (tag, ...args) => {
553
+ if (!shouldLog("debug"))
554
+ return;
555
+ console.log(formatTag(tag, "debug"), ...args);
556
+ },
557
+ info: (tag, ...args) => {
558
+ if (!shouldLog("info"))
559
+ return;
560
+ console.info(formatTag(tag, "info"), ...args);
561
+ },
562
+ warn: (tag, ...args) => {
563
+ if (!shouldLog("warn"))
564
+ return;
565
+ console.warn(formatTag(tag, "warn"), ...args);
566
+ },
567
+ error: (tag, ...args) => {
568
+ if (!shouldLog("error"))
569
+ return;
570
+ console.error(formatTag(tag, "error"), ...args);
571
+ }
572
+ };
573
+ frameworkLoggers = {
574
+ engine: createLogger("Engine"),
575
+ router: createLogger("Router"),
576
+ state: createLogger("State"),
577
+ events: createLogger("Events"),
578
+ remote: createLogger("Remote"),
579
+ renderer: createLogger("Renderer"),
580
+ module: createLogger("Module"),
581
+ lifecycle: createLogger("Lifecycle")
582
+ };
583
+ });
236
584
 
237
585
  // src/app.ts
238
586
  var exports_app = {};
@@ -249,6 +597,11 @@ class HypenAppBuilder {
249
597
  createdHandler;
250
598
  actionHandlers = new Map;
251
599
  destroyedHandler;
600
+ disconnectHandler;
601
+ reconnectHandler;
602
+ expireHandler;
603
+ errorHandler;
604
+ template;
252
605
  constructor(initialState, options) {
253
606
  this.initialState = initialState;
254
607
  this.options = options || {};
@@ -265,6 +618,26 @@ class HypenAppBuilder {
265
618
  this.destroyedHandler = fn;
266
619
  return this;
267
620
  }
621
+ onDisconnect(fn) {
622
+ this.disconnectHandler = fn;
623
+ return this;
624
+ }
625
+ onReconnect(fn) {
626
+ this.reconnectHandler = fn;
627
+ return this;
628
+ }
629
+ onExpire(fn) {
630
+ this.expireHandler = fn;
631
+ return this;
632
+ }
633
+ onError(fn) {
634
+ this.errorHandler = fn;
635
+ return this;
636
+ }
637
+ ui(template) {
638
+ this.template = template;
639
+ return this.build();
640
+ }
268
641
  build() {
269
642
  const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
270
643
  return {
@@ -274,10 +647,15 @@ class HypenAppBuilder {
274
647
  persist: this.options.persist,
275
648
  version: this.options.version,
276
649
  initialState: this.initialState,
650
+ template: this.template,
277
651
  handlers: {
278
652
  onCreated: this.createdHandler,
279
653
  onAction: this.actionHandlers,
280
- onDestroyed: this.destroyedHandler
654
+ onDestroyed: this.destroyedHandler,
655
+ onDisconnect: this.disconnectHandler,
656
+ onReconnect: this.reconnectHandler,
657
+ onExpire: this.expireHandler,
658
+ onError: this.errorHandler
281
659
  }
282
660
  };
283
661
  }
@@ -310,9 +688,9 @@ class HypenModuleInstance {
310
688
  });
311
689
  this.engine.setModule(definition.name || "AnonymousModule", definition.actions, definition.stateKeys, getStateSnapshot(this.state));
312
690
  for (const [actionName, handler] of definition.handlers.onAction) {
313
- console.log(`[ModuleInstance] Registering action handler: ${actionName} for module ${definition.name}`);
691
+ log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
314
692
  this.engine.onAction(actionName, async (action) => {
315
- console.log(`[ModuleInstance] Action handler fired: ${actionName}`, action);
693
+ log2.debug(`Action handler fired: ${actionName}`, action);
316
694
  const actionCtx = {
317
695
  name: action.name,
318
696
  payload: action.payload,
@@ -322,17 +700,19 @@ class HypenModuleInstance {
322
700
  router: this.routerContext?.root || null
323
701
  };
324
702
  const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
325
- try {
326
- await handler({
327
- action: actionCtx,
328
- state: this.state,
329
- next,
330
- context
331
- });
332
- console.log(`[ModuleInstance] Action handler completed: ${actionName}`);
333
- } catch (error) {
334
- console.error(`[ModuleInstance] Action handler error for ${actionName}:`, error);
335
- throw error;
703
+ const result = await this.executeAction(actionName, handler, {
704
+ action: actionCtx,
705
+ state: this.state,
706
+ next,
707
+ context
708
+ });
709
+ if (!result.ok) {
710
+ const shouldRethrow = await this.handleError(result.error, { actionName });
711
+ if (shouldRethrow) {
712
+ throw result.error;
713
+ }
714
+ } else {
715
+ log2.debug(`Action handler completed: ${actionName}`);
336
716
  }
337
717
  });
338
718
  }
@@ -359,6 +739,48 @@ class HypenModuleInstance {
359
739
  }
360
740
  return api;
361
741
  }
742
+ async executeAction(actionName, handler, ctx) {
743
+ try {
744
+ const result = handler(ctx);
745
+ await result;
746
+ return Ok(undefined);
747
+ } catch (e) {
748
+ return Err(new ActionError(actionName, e));
749
+ }
750
+ }
751
+ async handleError(error, context) {
752
+ const errorCtx = {
753
+ error,
754
+ state: this.state,
755
+ actionName: context.actionName,
756
+ lifecycle: context.lifecycle
757
+ };
758
+ if (this.definition.handlers.onError) {
759
+ try {
760
+ const result = await this.definition.handlers.onError(errorCtx);
761
+ if (result && typeof result === "object") {
762
+ if ("handled" in result && result.handled) {
763
+ return false;
764
+ }
765
+ if ("rethrow" in result && result.rethrow) {
766
+ return true;
767
+ }
768
+ }
769
+ } catch (handlerError) {
770
+ log2.error("Error in onError handler:", handlerError);
771
+ }
772
+ }
773
+ if (this.globalContext) {
774
+ const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
775
+ this.globalContext.emit("error", {
776
+ message: error.message,
777
+ error,
778
+ context: eventContext
779
+ });
780
+ }
781
+ log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
782
+ return false;
783
+ }
362
784
  async callCreatedHandler() {
363
785
  if (this.definition.handlers.onCreated) {
364
786
  const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
@@ -386,8 +808,12 @@ class HypenModuleInstance {
386
808
  Object.assign(this.state, patch);
387
809
  }
388
810
  }
389
- var app;
811
+ var log2, app;
390
812
  var init_app = __esm(() => {
813
+ init_result();
814
+ init_state();
815
+ init_logger();
816
+ log2 = createLogger("ModuleInstance");
391
817
  app = new HypenApp;
392
818
  });
393
819
 
@@ -472,4 +898,4 @@ export {
472
898
  Link
473
899
  };
474
900
 
475
- //# debugId=40114FB1C5B4505364756E2164756E21
901
+ //# debugId=7E3505DBDD2EEAEE64756E2164756E21