@hypen-space/core 0.4.956 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +5 -8
  2. package/dist/app.js +13 -1168
  3. package/dist/app.js.map +3 -9
  4. package/dist/chunk-00zt3w6e.js +58 -0
  5. package/dist/chunk-00zt3w6e.js.map +10 -0
  6. package/dist/chunk-3zvyth50.js +87 -0
  7. package/dist/chunk-3zvyth50.js.map +10 -0
  8. package/dist/chunk-5gtx3pza.js +11 -0
  9. package/dist/chunk-5gtx3pza.js.map +9 -0
  10. package/dist/chunk-7222131a.js +191 -0
  11. package/dist/chunk-7222131a.js.map +10 -0
  12. package/dist/chunk-avchpwhf.js +200 -0
  13. package/dist/chunk-avchpwhf.js.map +10 -0
  14. package/dist/chunk-ec0tgy9a.js +154 -0
  15. package/dist/chunk-ec0tgy9a.js.map +10 -0
  16. package/dist/chunk-fnss4dm2.js +479 -0
  17. package/dist/chunk-fnss4dm2.js.map +10 -0
  18. package/dist/chunk-g2gz9pgg.js +133 -0
  19. package/dist/chunk-g2gz9pgg.js.map +10 -0
  20. package/dist/chunk-g72v6mmf.js +341 -0
  21. package/dist/chunk-g72v6mmf.js.map +10 -0
  22. package/dist/chunk-g7g3jxty.js +351 -0
  23. package/dist/chunk-g7g3jxty.js.map +10 -0
  24. package/dist/chunk-g830re2c.js +50 -0
  25. package/dist/chunk-g830re2c.js.map +10 -0
  26. package/dist/chunk-gtwh1sg7.js +127 -0
  27. package/dist/chunk-gtwh1sg7.js.map +10 -0
  28. package/dist/chunk-k12cwfst.js +72 -0
  29. package/dist/chunk-k12cwfst.js.map +10 -0
  30. package/dist/chunk-mbqqsz6r.js +128 -0
  31. package/dist/chunk-mbqqsz6r.js.map +10 -0
  32. package/dist/chunk-ne632swm.js +209 -0
  33. package/dist/chunk-ne632swm.js.map +10 -0
  34. package/dist/chunk-p3pj1bax.js +206 -0
  35. package/dist/chunk-p3pj1bax.js.map +10 -0
  36. package/dist/chunk-vshdk2pw.js +56 -0
  37. package/dist/chunk-vshdk2pw.js.map +10 -0
  38. package/dist/chunk-wp2ebn2m.js +51 -0
  39. package/dist/chunk-wp2ebn2m.js.map +10 -0
  40. package/dist/chunk-x4qv4jm9.js +88 -0
  41. package/dist/chunk-x4qv4jm9.js.map +10 -0
  42. package/dist/chunk-ycxp4drd.js +211 -0
  43. package/dist/chunk-ycxp4drd.js.map +10 -0
  44. package/dist/components/builtin.js +14 -1246
  45. package/dist/components/builtin.js.map +3 -10
  46. package/dist/context.js +7 -362
  47. package/dist/context.js.map +3 -6
  48. package/dist/datasource.js +5 -76
  49. package/dist/datasource.js.map +3 -4
  50. package/dist/disposable.js +20 -367
  51. package/dist/disposable.js.map +3 -5
  52. package/dist/engine-base.d.ts +38 -2
  53. package/dist/engine-base.js +45 -412
  54. package/dist/engine-base.js.map +4 -6
  55. package/dist/events.js +7 -285
  56. package/dist/events.js.map +3 -5
  57. package/dist/hypen.js +8 -71
  58. package/dist/hypen.js.map +3 -4
  59. package/dist/index.browser.js +58 -2152
  60. package/dist/index.browser.js.map +3 -16
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +132 -3058
  63. package/dist/index.js.map +4 -22
  64. package/dist/logger.js +16 -220
  65. package/dist/logger.js.map +3 -4
  66. package/dist/managed-router.js +12 -1367
  67. package/dist/managed-router.js.map +3 -10
  68. package/dist/portable.js +6 -70
  69. package/dist/portable.js.map +3 -4
  70. package/dist/remote/client.js +9 -1010
  71. package/dist/remote/client.js.map +3 -8
  72. package/dist/remote/index.d.ts +2 -0
  73. package/dist/remote/index.js +511 -1066
  74. package/dist/remote/index.js.map +4 -9
  75. package/dist/remote/remote-session.d.ts +245 -0
  76. package/dist/remote/session.js +5 -147
  77. package/dist/remote/session.js.map +3 -4
  78. package/dist/renderer.js +7 -271
  79. package/dist/renderer.js.map +3 -5
  80. package/dist/resolver.js +5 -484
  81. package/dist/resolver.js.map +3 -5
  82. package/dist/result.js +25 -211
  83. package/dist/result.js.map +3 -4
  84. package/dist/retry.js +10 -336
  85. package/dist/retry.js.map +3 -5
  86. package/dist/router.js +9 -807
  87. package/dist/router.js.map +3 -8
  88. package/dist/state.js +10 -274
  89. package/dist/state.js.map +3 -5
  90. package/dist/validate.d.ts +19 -0
  91. package/package.json +13 -1
  92. package/src/engine-base.ts +87 -4
  93. package/src/index.ts +5 -0
  94. package/src/remote/index.ts +16 -0
  95. package/src/remote/remote-session.ts +998 -0
  96. package/src/validate.ts +37 -0
@@ -46,6 +46,35 @@ import type {
46
46
 
47
47
  const log = frameworkLoggers.engine;
48
48
 
49
+ /**
50
+ * Recursively convert wasm-bindgen `Map` values into plain objects.
51
+ *
52
+ * The web-target WASM returns action payloads (and nested values) as `Map`
53
+ * instances; user handlers expect plain objects. Only plain objects
54
+ * (`constructor === Object`), arrays, and Maps are descended into — class
55
+ * instances, `Date`, etc. are passed through untouched.
56
+ */
57
+ function mapToPlainObject(value: any): any {
58
+ if (value instanceof Map) {
59
+ const obj: Record<string, any> = {};
60
+ for (const [key, val] of value.entries()) {
61
+ obj[key] = mapToPlainObject(val);
62
+ }
63
+ return obj;
64
+ }
65
+ if (Array.isArray(value)) {
66
+ return value.map(mapToPlainObject);
67
+ }
68
+ if (value && typeof value === "object" && value.constructor === Object) {
69
+ const obj: Record<string, any> = {};
70
+ for (const [key, val] of Object.entries(value)) {
71
+ obj[key] = mapToPlainObject(val);
72
+ }
73
+ return obj;
74
+ }
75
+ return value;
76
+ }
77
+
49
78
  /**
50
79
  * Shared base wrapping a wasm-bindgen `WasmEngine`. `any` here is load-
51
80
  * bearing: the concrete type is `WasmEngine` from either the `wasm-node`
@@ -57,6 +86,16 @@ export abstract class BaseEngine {
57
86
  protected wasmEngine: any = null;
58
87
  protected initialized = false;
59
88
 
89
+ /**
90
+ * Component names the resolver was asked for and returned `null` on — i.e.
91
+ * names that are neither a registered primitive (the engine checks those
92
+ * first and never consults the resolver for them) nor resolvable to a
93
+ * template. Each such name produces an opaque `Create` patch the renderer
94
+ * drops. Tracked so a miss is warned about once and can be asserted on in
95
+ * tests. See `getUnresolvedComponents`.
96
+ */
97
+ private unresolvedComponents = new Set<string>();
98
+
60
99
  /**
61
100
  * Initialize the WASM module. Platform-specific.
62
101
  *
@@ -98,16 +137,47 @@ export abstract class BaseEngine {
98
137
 
99
138
  /**
100
139
  * Set the component resolver for dynamic component composition.
140
+ *
141
+ * The resolver is wrapped to detect "resolver misses": the engine only
142
+ * consults the resolver for element types that are NOT registered
143
+ * primitives, so a `null` return means the name resolves to nothing and
144
+ * the engine will emit an opaque `Create` the renderer silently drops
145
+ * (e.g. an anonymous component that never got registered). The first miss
146
+ * for each unique name is logged with actionable guidance; every miss is
147
+ * recorded for `getUnresolvedComponents()` / test assertions.
101
148
  */
102
149
  setComponentResolver(resolver: ComponentResolver): void {
103
150
  const engine = this.ensureInitialized();
104
151
  engine.setComponentResolver(
105
152
  (componentName: string, contextPath: string | null) => {
106
- return resolver(componentName, contextPath);
153
+ const resolved = resolver(componentName, contextPath);
154
+ if (resolved == null) {
155
+ if (!this.unresolvedComponents.has(componentName)) {
156
+ this.unresolvedComponents.add(componentName);
157
+ log.warn(
158
+ `Component "${componentName}" did not resolve: it is not a ` +
159
+ `registered primitive and the component resolver returned null. ` +
160
+ `The engine will emit an opaque Create for it and the renderer ` +
161
+ `will drop it. Did you forget to register/.module(...) the ` +
162
+ `component, or is its name misspelled in the template?`,
163
+ );
164
+ }
165
+ }
166
+ return resolved;
107
167
  },
108
168
  );
109
169
  }
110
170
 
171
+ /**
172
+ * Names the component resolver has been asked for and failed to resolve
173
+ * this session (see `setComponentResolver`). Useful as a CI / smoke-test
174
+ * assertion — an empty array means every referenced component resolved to
175
+ * a primitive or a template.
176
+ */
177
+ getUnresolvedComponents(): string[] {
178
+ return [...this.unresolvedComponents];
179
+ }
180
+
111
181
  /**
112
182
  * Parse and render Hypen DSL source code.
113
183
  * @throws {ParseError} if the source fails to parse
@@ -220,11 +290,24 @@ export abstract class BaseEngine {
220
290
 
221
291
  /**
222
292
  * Platform hook: normalize an incoming action before the user handler
223
- * sees it. Default identity; subclasses can override to unwrap native
224
- * values (e.g. browser Maps in action payloads).
293
+ * sees it.
294
+ *
295
+ * The default converts any `Map` instances in the payload into plain
296
+ * objects (deeply). This is the landmine the wasm-bindgen **web** target
297
+ * sets: it returns structured action payloads as JS `Map`s, so a handler
298
+ * reading `payload.to` gets `undefined` and `@router.push, to: "/x"`
299
+ * silently no-ops. Every consumer of the web-target WASM — the browser
300
+ * engine, a Cloudflare `CFEngine`, any future runtime — inherits the fix
301
+ * here instead of rediscovering it.
302
+ *
303
+ * The wasm-bindgen **bundler** (node) target already returns plain
304
+ * objects, so for it this is a harmless deep walk. Subclasses on that
305
+ * target may override with an identity to skip the walk (see
306
+ * `@hypen-space/server`).
225
307
  */
226
308
  protected normalizeAction(action: Action): Action {
227
- return action;
309
+ if (!action.payload) return action;
310
+ return { ...action, payload: mapToPlainObject(action.payload) };
228
311
  }
229
312
 
230
313
  /**
package/src/index.ts CHANGED
@@ -326,3 +326,8 @@ export {
326
326
  isDebugMode,
327
327
  } from "./logger.js";
328
328
  export type { LogLevel, LoggerConfig, LogHandler } from "./logger.js";
329
+
330
+ // Patch-stream validation — catch silent resolver misses (opaque component
331
+ // elementTypes) in CI / smoke tests off the wire.
332
+ export { validatePatches } from "./validate.js";
333
+ export type { PatchValidationResult } from "./validate.js";
@@ -44,3 +44,19 @@ export type {
44
44
 
45
45
  // Re-export Patch type
46
46
  export type { Patch } from "../types.js";
47
+
48
+ // Transport-agnostic remote session. The engine is injected via
49
+ // `SessionHost.createEngine`, so any runtime (Node/Bun, Cloudflare Durable
50
+ // Objects, etc.) can reuse the Hypen remote protocol envelope.
51
+ // `@hypen-space/server` re-exports these for back-compat.
52
+ export {
53
+ RemoteSession,
54
+ AsyncQueueTransport,
55
+ createBunWebSocketTransport,
56
+ } from "./remote-session.js";
57
+ export type {
58
+ SessionTransport,
59
+ SessionHost,
60
+ RemoteSessionOptions,
61
+ OutgoingMessage,
62
+ } from "./remote-session.js";