@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.
- package/README.md +5 -8
- package/dist/app.js +13 -1168
- package/dist/app.js.map +3 -9
- package/dist/chunk-00zt3w6e.js +58 -0
- package/dist/chunk-00zt3w6e.js.map +10 -0
- package/dist/chunk-3zvyth50.js +87 -0
- package/dist/chunk-3zvyth50.js.map +10 -0
- package/dist/chunk-5gtx3pza.js +11 -0
- package/dist/chunk-5gtx3pza.js.map +9 -0
- package/dist/chunk-7222131a.js +191 -0
- package/dist/chunk-7222131a.js.map +10 -0
- package/dist/chunk-avchpwhf.js +200 -0
- package/dist/chunk-avchpwhf.js.map +10 -0
- package/dist/chunk-ec0tgy9a.js +154 -0
- package/dist/chunk-ec0tgy9a.js.map +10 -0
- package/dist/chunk-fnss4dm2.js +479 -0
- package/dist/chunk-fnss4dm2.js.map +10 -0
- package/dist/chunk-g2gz9pgg.js +133 -0
- package/dist/chunk-g2gz9pgg.js.map +10 -0
- package/dist/chunk-g72v6mmf.js +341 -0
- package/dist/chunk-g72v6mmf.js.map +10 -0
- package/dist/chunk-g7g3jxty.js +351 -0
- package/dist/chunk-g7g3jxty.js.map +10 -0
- package/dist/chunk-g830re2c.js +50 -0
- package/dist/chunk-g830re2c.js.map +10 -0
- package/dist/chunk-gtwh1sg7.js +127 -0
- package/dist/chunk-gtwh1sg7.js.map +10 -0
- package/dist/chunk-k12cwfst.js +72 -0
- package/dist/chunk-k12cwfst.js.map +10 -0
- package/dist/chunk-mbqqsz6r.js +128 -0
- package/dist/chunk-mbqqsz6r.js.map +10 -0
- package/dist/chunk-ne632swm.js +209 -0
- package/dist/chunk-ne632swm.js.map +10 -0
- package/dist/chunk-p3pj1bax.js +206 -0
- package/dist/chunk-p3pj1bax.js.map +10 -0
- package/dist/chunk-vshdk2pw.js +56 -0
- package/dist/chunk-vshdk2pw.js.map +10 -0
- package/dist/chunk-wp2ebn2m.js +51 -0
- package/dist/chunk-wp2ebn2m.js.map +10 -0
- package/dist/chunk-x4qv4jm9.js +88 -0
- package/dist/chunk-x4qv4jm9.js.map +10 -0
- package/dist/chunk-ycxp4drd.js +211 -0
- package/dist/chunk-ycxp4drd.js.map +10 -0
- package/dist/components/builtin.js +14 -1246
- package/dist/components/builtin.js.map +3 -10
- package/dist/context.js +7 -362
- package/dist/context.js.map +3 -6
- package/dist/datasource.js +5 -76
- package/dist/datasource.js.map +3 -4
- package/dist/disposable.js +20 -367
- package/dist/disposable.js.map +3 -5
- package/dist/engine-base.d.ts +38 -2
- package/dist/engine-base.js +45 -412
- package/dist/engine-base.js.map +4 -6
- package/dist/events.js +7 -285
- package/dist/events.js.map +3 -5
- package/dist/hypen.js +8 -71
- package/dist/hypen.js.map +3 -4
- package/dist/index.browser.js +58 -2152
- package/dist/index.browser.js.map +3 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.js +132 -3058
- package/dist/index.js.map +4 -22
- package/dist/logger.js +16 -220
- package/dist/logger.js.map +3 -4
- package/dist/managed-router.js +12 -1367
- package/dist/managed-router.js.map +3 -10
- package/dist/portable.js +6 -70
- package/dist/portable.js.map +3 -4
- package/dist/remote/client.js +9 -1010
- package/dist/remote/client.js.map +3 -8
- package/dist/remote/index.d.ts +2 -0
- package/dist/remote/index.js +511 -1066
- package/dist/remote/index.js.map +4 -9
- package/dist/remote/remote-session.d.ts +245 -0
- package/dist/remote/session.js +5 -147
- package/dist/remote/session.js.map +3 -4
- package/dist/renderer.js +7 -271
- package/dist/renderer.js.map +3 -5
- package/dist/resolver.js +5 -484
- package/dist/resolver.js.map +3 -5
- package/dist/result.js +25 -211
- package/dist/result.js.map +3 -4
- package/dist/retry.js +10 -336
- package/dist/retry.js.map +3 -5
- package/dist/router.js +9 -807
- package/dist/router.js.map +3 -8
- package/dist/state.js +10 -274
- package/dist/state.js.map +3 -5
- package/dist/validate.d.ts +19 -0
- package/package.json +13 -1
- package/src/engine-base.ts +87 -4
- package/src/index.ts +5 -0
- package/src/remote/index.ts +16 -0
- package/src/remote/remote-session.ts +998 -0
- package/src/validate.ts +37 -0
package/dist/index.browser.js
CHANGED
|
@@ -1,2154 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function map(result, fn) {
|
|
59
|
-
if (result.ok) {
|
|
60
|
-
return Ok(fn(result.value));
|
|
61
|
-
}
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
64
|
-
function mapErr(result, fn) {
|
|
65
|
-
if (!result.ok) {
|
|
66
|
-
return Err(fn(result.error));
|
|
67
|
-
}
|
|
68
|
-
return result;
|
|
69
|
-
}
|
|
70
|
-
function flatMap(result, fn) {
|
|
71
|
-
if (result.ok) {
|
|
72
|
-
return fn(result.value);
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
function unwrap(result) {
|
|
77
|
-
if (result.ok) {
|
|
78
|
-
return result.value;
|
|
79
|
-
}
|
|
80
|
-
throw result.error;
|
|
81
|
-
}
|
|
82
|
-
function unwrapOr(result, defaultValue) {
|
|
83
|
-
if (result.ok) {
|
|
84
|
-
return result.value;
|
|
85
|
-
}
|
|
86
|
-
return defaultValue;
|
|
87
|
-
}
|
|
88
|
-
function unwrapOrElse(result, fn) {
|
|
89
|
-
if (result.ok) {
|
|
90
|
-
return result.value;
|
|
91
|
-
}
|
|
92
|
-
return fn(result.error);
|
|
93
|
-
}
|
|
94
|
-
function match(result, handlers) {
|
|
95
|
-
if (result.ok) {
|
|
96
|
-
return handlers.ok(result.value);
|
|
97
|
-
}
|
|
98
|
-
return handlers.err(result.error);
|
|
99
|
-
}
|
|
100
|
-
function all(results) {
|
|
101
|
-
const values = [];
|
|
102
|
-
for (const result of results) {
|
|
103
|
-
if (!result.ok) {
|
|
104
|
-
return result;
|
|
105
|
-
}
|
|
106
|
-
values.push(result.value);
|
|
107
|
-
}
|
|
108
|
-
return Ok(values);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
class HypenError extends Error {
|
|
112
|
-
code;
|
|
113
|
-
context;
|
|
114
|
-
cause;
|
|
115
|
-
constructor(code, message, options) {
|
|
116
|
-
super(message);
|
|
117
|
-
this.name = "HypenError";
|
|
118
|
-
this.code = code;
|
|
119
|
-
this.context = options?.context;
|
|
120
|
-
this.cause = options?.cause;
|
|
121
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
class ActionError extends HypenError {
|
|
126
|
-
actionName;
|
|
127
|
-
constructor(actionName, cause) {
|
|
128
|
-
super("ACTION_ERROR", `Action handler "${actionName}" failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
129
|
-
context: { actionName },
|
|
130
|
-
cause: cause instanceof Error ? cause : undefined
|
|
131
|
-
});
|
|
132
|
-
this.name = "ActionError";
|
|
133
|
-
this.actionName = actionName;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
class ConnectionError extends HypenError {
|
|
138
|
-
url;
|
|
139
|
-
attempt;
|
|
140
|
-
constructor(url, cause, attempt) {
|
|
141
|
-
super("CONNECTION_ERROR", `Connection to "${url}" failed${attempt ? ` (attempt ${attempt})` : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
142
|
-
context: { url, attempt },
|
|
143
|
-
cause: cause instanceof Error ? cause : undefined
|
|
144
|
-
});
|
|
145
|
-
this.name = "ConnectionError";
|
|
146
|
-
this.url = url;
|
|
147
|
-
this.attempt = attempt;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
class StateError extends HypenError {
|
|
152
|
-
path;
|
|
153
|
-
constructor(message, path, cause) {
|
|
154
|
-
super("STATE_ERROR", message, {
|
|
155
|
-
context: { path },
|
|
156
|
-
cause: cause instanceof Error ? cause : undefined
|
|
157
|
-
});
|
|
158
|
-
this.name = "StateError";
|
|
159
|
-
this.path = path;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
class ParseError extends HypenError {
|
|
164
|
-
source;
|
|
165
|
-
constructor(message, source, cause) {
|
|
166
|
-
super("PARSE_ERROR", message, {
|
|
167
|
-
context: { source },
|
|
168
|
-
cause: cause instanceof Error ? cause : undefined
|
|
169
|
-
});
|
|
170
|
-
this.name = "ParseError";
|
|
171
|
-
this.source = source;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
class RenderError extends HypenError {
|
|
176
|
-
constructor(message, cause) {
|
|
177
|
-
super("RENDER_ERROR", message, {
|
|
178
|
-
cause: cause instanceof Error ? cause : undefined
|
|
179
|
-
});
|
|
180
|
-
this.name = "RenderError";
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
function classifyEngineError(err) {
|
|
184
|
-
if (err && typeof err === "object" && "message" in err && "type" in err) {
|
|
185
|
-
const structured = err;
|
|
186
|
-
switch (structured.type) {
|
|
187
|
-
case "parseError":
|
|
188
|
-
return new ParseError(structured.message);
|
|
189
|
-
case "stateError":
|
|
190
|
-
return new StateError(structured.message);
|
|
191
|
-
case "renderError":
|
|
192
|
-
return new RenderError(structured.message);
|
|
193
|
-
case "actionError":
|
|
194
|
-
return new HypenError("ACTION_ERROR", structured.message);
|
|
195
|
-
default:
|
|
196
|
-
return new HypenError("UNKNOWN_ERROR", structured.message);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
200
|
-
if (message.startsWith("Parse error:")) {
|
|
201
|
-
return new ParseError(message);
|
|
202
|
-
}
|
|
203
|
-
if (message.startsWith("Invalid state:") || message.startsWith("Invalid state patch:")) {
|
|
204
|
-
return new StateError(message);
|
|
205
|
-
}
|
|
206
|
-
if (message.startsWith("Parent node not found:")) {
|
|
207
|
-
return new RenderError(message);
|
|
208
|
-
}
|
|
209
|
-
return new RenderError(message);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// src/datasource.ts
|
|
213
|
-
class DataSourceManager {
|
|
214
|
-
plugins = new Map;
|
|
215
|
-
configs = new Map;
|
|
216
|
-
state = new Map;
|
|
217
|
-
engine;
|
|
218
|
-
constructor(engine) {
|
|
219
|
-
this.engine = engine;
|
|
220
|
-
}
|
|
221
|
-
async use(plugin, config) {
|
|
222
|
-
const { name } = plugin;
|
|
223
|
-
this.state.set(name, {});
|
|
224
|
-
this.plugins.set(name, plugin);
|
|
225
|
-
this.configs.set(name, config);
|
|
226
|
-
await plugin.connect(config, (change) => {
|
|
227
|
-
const current = this.state.get(name) ?? {};
|
|
228
|
-
for (const path of change.paths) {
|
|
229
|
-
if (path in change.values) {
|
|
230
|
-
current[path] = change.values[path];
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
this.state.set(name, current);
|
|
234
|
-
this.engine.setContext(name, current);
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
get(name) {
|
|
238
|
-
return this.plugins.get(name);
|
|
239
|
-
}
|
|
240
|
-
has(name) {
|
|
241
|
-
return this.plugins.has(name);
|
|
242
|
-
}
|
|
243
|
-
getNames() {
|
|
244
|
-
return Array.from(this.plugins.keys());
|
|
245
|
-
}
|
|
246
|
-
getStatus(name) {
|
|
247
|
-
return this.plugins.get(name)?.status;
|
|
248
|
-
}
|
|
249
|
-
async remove(name) {
|
|
250
|
-
const plugin = this.plugins.get(name);
|
|
251
|
-
if (plugin) {
|
|
252
|
-
await plugin.disconnect();
|
|
253
|
-
this.plugins.delete(name);
|
|
254
|
-
this.configs.delete(name);
|
|
255
|
-
this.state.delete(name);
|
|
256
|
-
this.engine.removeContext(name);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
async disconnectAll() {
|
|
260
|
-
const names = Array.from(this.plugins.keys());
|
|
261
|
-
await Promise.all(names.map((name) => this.remove(name)));
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// src/portable.ts
|
|
266
|
-
function notInstalled(name) {
|
|
267
|
-
throw new Error(`[@hypen-space/core] Portable helper "${name}" called before the engine was installed. ` + `Import @hypen-space/server, @hypen-space/web-engine, or call setPortableImpl() before use.`);
|
|
268
|
-
}
|
|
269
|
-
var current = {
|
|
270
|
-
diffState: () => notInstalled("diffState"),
|
|
271
|
-
matchPath: () => notInstalled("matchPath"),
|
|
272
|
-
pathGet: () => notInstalled("pathGet"),
|
|
273
|
-
pathHas: () => notInstalled("pathHas"),
|
|
274
|
-
pathSet: () => notInstalled("pathSet"),
|
|
275
|
-
pathDelete: () => notInstalled("pathDelete"),
|
|
276
|
-
encodeUriComponent: () => notInstalled("encodeUriComponent"),
|
|
277
|
-
decodeUriComponent: () => notInstalled("decodeUriComponent"),
|
|
278
|
-
parseQuery: () => notInstalled("parseQuery"),
|
|
279
|
-
buildUrl: () => notInstalled("buildUrl")
|
|
280
|
-
};
|
|
281
|
-
function setPortableImpl(impl) {
|
|
282
|
-
if (impl === null) {
|
|
283
|
-
current = {
|
|
284
|
-
diffState: () => notInstalled("diffState"),
|
|
285
|
-
matchPath: () => notInstalled("matchPath"),
|
|
286
|
-
pathGet: () => notInstalled("pathGet"),
|
|
287
|
-
pathHas: () => notInstalled("pathHas"),
|
|
288
|
-
pathSet: () => notInstalled("pathSet"),
|
|
289
|
-
pathDelete: () => notInstalled("pathDelete"),
|
|
290
|
-
encodeUriComponent: () => notInstalled("encodeUriComponent"),
|
|
291
|
-
decodeUriComponent: () => notInstalled("decodeUriComponent"),
|
|
292
|
-
parseQuery: () => notInstalled("parseQuery"),
|
|
293
|
-
buildUrl: () => notInstalled("buildUrl")
|
|
294
|
-
};
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
current = impl;
|
|
298
|
-
}
|
|
299
|
-
var portable = {
|
|
300
|
-
diffState: (o, n, b) => current.diffState(o, n, b),
|
|
301
|
-
matchPath: (p, path) => current.matchPath(p, path),
|
|
302
|
-
pathGet: (v, p) => current.pathGet(v, p),
|
|
303
|
-
pathHas: (v, p) => current.pathHas(v, p),
|
|
304
|
-
pathSet: (v, p, nv) => current.pathSet(v, p, nv),
|
|
305
|
-
pathDelete: (v, p) => current.pathDelete(v, p),
|
|
306
|
-
encodeUriComponent: (s) => current.encodeUriComponent(s),
|
|
307
|
-
decodeUriComponent: (s) => current.decodeUriComponent(s),
|
|
308
|
-
parseQuery: (f) => current.parseQuery(f),
|
|
309
|
-
buildUrl: (p, q) => current.buildUrl(p, q)
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// src/state.ts
|
|
313
|
-
var IS_PROXY = Symbol.for("hypen.isProxy");
|
|
314
|
-
var RAW_TARGET = Symbol.for("hypen.rawTarget");
|
|
315
|
-
function deepClone(obj) {
|
|
316
|
-
if (obj === null || typeof obj !== "object") {
|
|
317
|
-
return obj;
|
|
318
|
-
}
|
|
319
|
-
if (typeof obj === "function") {
|
|
320
|
-
return obj;
|
|
321
|
-
}
|
|
322
|
-
if (typeof obj.__getSnapshot === "function") {
|
|
323
|
-
return obj.__getSnapshot();
|
|
324
|
-
}
|
|
325
|
-
if (obj instanceof WeakMap || obj instanceof WeakSet) {
|
|
326
|
-
return obj;
|
|
327
|
-
}
|
|
328
|
-
const visited = new WeakMap;
|
|
329
|
-
function cloneInternal(value) {
|
|
330
|
-
if (value === null || typeof value !== "object") {
|
|
331
|
-
return value;
|
|
332
|
-
}
|
|
333
|
-
if (typeof value === "function") {
|
|
334
|
-
return value;
|
|
335
|
-
}
|
|
336
|
-
if (visited.has(value)) {
|
|
337
|
-
return visited.get(value);
|
|
338
|
-
}
|
|
339
|
-
if (value instanceof WeakMap || value instanceof WeakSet) {
|
|
340
|
-
return value;
|
|
341
|
-
}
|
|
342
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
|
|
343
|
-
try {
|
|
344
|
-
return structuredClone(value);
|
|
345
|
-
} catch {}
|
|
346
|
-
}
|
|
347
|
-
if (Array.isArray(value)) {
|
|
348
|
-
const arrClone = [];
|
|
349
|
-
visited.set(value, arrClone);
|
|
350
|
-
for (let i = 0;i < value.length; i++) {
|
|
351
|
-
arrClone[i] = cloneInternal(value[i]);
|
|
352
|
-
}
|
|
353
|
-
return arrClone;
|
|
354
|
-
}
|
|
355
|
-
const objClone = {};
|
|
356
|
-
visited.set(value, objClone);
|
|
357
|
-
for (const key in value) {
|
|
358
|
-
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
359
|
-
objClone[key] = cloneInternal(value[key]);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
const symbolKeys = Object.getOwnPropertySymbols(value);
|
|
363
|
-
for (const sym of symbolKeys) {
|
|
364
|
-
objClone[sym] = cloneInternal(value[sym]);
|
|
365
|
-
}
|
|
366
|
-
return objClone;
|
|
367
|
-
}
|
|
368
|
-
return cloneInternal(obj);
|
|
369
|
-
}
|
|
370
|
-
function diffState(oldState, newState, basePath = "") {
|
|
371
|
-
return portable.diffState(oldState, newState, basePath);
|
|
372
|
-
}
|
|
373
|
-
function createObservableState(initialState, options) {
|
|
374
|
-
const opts = options || { onChange: () => {} };
|
|
375
|
-
if (initialState === null || initialState === undefined) {
|
|
376
|
-
initialState = {};
|
|
377
|
-
}
|
|
378
|
-
if (initialState instanceof Number || initialState instanceof String || initialState instanceof Boolean) {
|
|
379
|
-
throw new TypeError("Cannot create observable state from primitive wrapper objects (Number, String, Boolean). " + "Use plain primitives or regular objects instead.");
|
|
380
|
-
}
|
|
381
|
-
initialState = deepClone(initialState);
|
|
382
|
-
let lastSnapshot = deepClone(initialState);
|
|
383
|
-
const pathPrefix = opts.pathPrefix || "";
|
|
384
|
-
let batchDepth = 0;
|
|
385
|
-
let pendingChange = null;
|
|
386
|
-
function notifyChange() {
|
|
387
|
-
if (batchDepth > 0)
|
|
388
|
-
return;
|
|
389
|
-
const change = diffState(lastSnapshot, state, pathPrefix);
|
|
390
|
-
if (change.paths.length > 0) {
|
|
391
|
-
lastSnapshot = deepClone(state);
|
|
392
|
-
if (pendingChange) {
|
|
393
|
-
change.paths.push(...pendingChange.paths);
|
|
394
|
-
Object.assign(change.newValues, pendingChange.newValues);
|
|
395
|
-
pendingChange = null;
|
|
396
|
-
}
|
|
397
|
-
opts.onChange(change);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
let notificationPending = false;
|
|
401
|
-
function scheduleBatch() {
|
|
402
|
-
if (batchDepth === 0) {
|
|
403
|
-
if (!notificationPending) {
|
|
404
|
-
notificationPending = true;
|
|
405
|
-
queueMicrotask(() => {
|
|
406
|
-
notificationPending = false;
|
|
407
|
-
if (batchDepth === 0) {
|
|
408
|
-
notifyChange();
|
|
409
|
-
} else {}
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
} else {
|
|
413
|
-
pendingChange = pendingChange || { paths: [], newValues: {} };
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
const proxyCache = new WeakMap;
|
|
417
|
-
function createProxy(target, basePath) {
|
|
418
|
-
const cached = proxyCache.get(target);
|
|
419
|
-
if (cached)
|
|
420
|
-
return cached;
|
|
421
|
-
const proxy = new Proxy(target, {
|
|
422
|
-
get(obj, prop) {
|
|
423
|
-
if (prop === IS_PROXY)
|
|
424
|
-
return true;
|
|
425
|
-
if (prop === RAW_TARGET)
|
|
426
|
-
return obj;
|
|
427
|
-
if (prop === "__beginBatch") {
|
|
428
|
-
return () => {
|
|
429
|
-
batchDepth++;
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
if (prop === "__endBatch") {
|
|
433
|
-
return () => {
|
|
434
|
-
batchDepth--;
|
|
435
|
-
if (batchDepth === 0) {
|
|
436
|
-
notifyChange();
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
if (prop === "__getSnapshot") {
|
|
441
|
-
return () => deepClone(obj);
|
|
442
|
-
}
|
|
443
|
-
const value = obj[prop];
|
|
444
|
-
if (value && typeof value === "object") {
|
|
445
|
-
if (value[IS_PROXY]) {
|
|
446
|
-
return value;
|
|
447
|
-
}
|
|
448
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
|
|
449
|
-
return value;
|
|
450
|
-
}
|
|
451
|
-
const cachedNested = proxyCache.get(value);
|
|
452
|
-
if (cachedNested) {
|
|
453
|
-
return cachedNested;
|
|
454
|
-
}
|
|
455
|
-
const nestedProxy = createProxy(value, basePath ? `${basePath}.${String(prop)}` : String(prop));
|
|
456
|
-
return nestedProxy;
|
|
457
|
-
}
|
|
458
|
-
return value;
|
|
459
|
-
},
|
|
460
|
-
set(obj, prop, value) {
|
|
461
|
-
const oldValue = obj[prop];
|
|
462
|
-
if (value && typeof value === "object" && value[IS_PROXY]) {
|
|
463
|
-
value = value[RAW_TARGET];
|
|
464
|
-
}
|
|
465
|
-
obj[prop] = value;
|
|
466
|
-
if (oldValue !== value) {
|
|
467
|
-
scheduleBatch();
|
|
468
|
-
}
|
|
469
|
-
return true;
|
|
470
|
-
},
|
|
471
|
-
deleteProperty(obj, prop) {
|
|
472
|
-
const existed = Object.prototype.hasOwnProperty.call(obj, prop);
|
|
473
|
-
const result = delete obj[prop];
|
|
474
|
-
if (existed) {
|
|
475
|
-
scheduleBatch();
|
|
476
|
-
}
|
|
477
|
-
return result;
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
proxyCache.set(target, proxy);
|
|
481
|
-
return proxy;
|
|
482
|
-
}
|
|
483
|
-
const state = createProxy(initialState, pathPrefix);
|
|
484
|
-
return state;
|
|
485
|
-
}
|
|
486
|
-
function batchStateUpdates(state, fn) {
|
|
487
|
-
const s = state;
|
|
488
|
-
if (s.__beginBatch && s.__endBatch) {
|
|
489
|
-
s.__beginBatch();
|
|
490
|
-
try {
|
|
491
|
-
fn();
|
|
492
|
-
} finally {
|
|
493
|
-
s.__endBatch();
|
|
494
|
-
}
|
|
495
|
-
} else {
|
|
496
|
-
fn();
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function getStateSnapshot(state) {
|
|
500
|
-
const s = state;
|
|
501
|
-
if (s.__getSnapshot) {
|
|
502
|
-
return s.__getSnapshot();
|
|
503
|
-
}
|
|
504
|
-
return deepClone(state);
|
|
505
|
-
}
|
|
506
|
-
function isStateProxy(value) {
|
|
507
|
-
return value !== null && typeof value === "object" && value[IS_PROXY] === true;
|
|
508
|
-
}
|
|
509
|
-
function unwrapProxy(value) {
|
|
510
|
-
if (value !== null && typeof value === "object" && value[IS_PROXY]) {
|
|
511
|
-
return value[RAW_TARGET];
|
|
512
|
-
}
|
|
513
|
-
return value;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// src/logger.ts
|
|
517
|
-
var LOG_LEVEL_ORDER = {
|
|
518
|
-
debug: 0,
|
|
519
|
-
info: 1,
|
|
520
|
-
warn: 2,
|
|
521
|
-
error: 3,
|
|
522
|
-
none: 4
|
|
523
|
-
};
|
|
524
|
-
var LOG_LEVEL_COLORS = {
|
|
525
|
-
debug: "\x1B[36m",
|
|
526
|
-
info: "\x1B[32m",
|
|
527
|
-
warn: "\x1B[33m",
|
|
528
|
-
error: "\x1B[31m"
|
|
529
|
-
};
|
|
530
|
-
var RESET_COLOR = "\x1B[0m";
|
|
531
|
-
function isProduction() {
|
|
532
|
-
if (typeof process !== "undefined" && process.env) {
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
return false;
|
|
536
|
-
}
|
|
537
|
-
var config = {
|
|
538
|
-
level: isProduction() ? "error" : "info",
|
|
539
|
-
colors: true,
|
|
540
|
-
timestamps: false
|
|
541
|
-
};
|
|
542
|
-
function setLogLevel(level) {
|
|
543
|
-
config.level = level;
|
|
544
|
-
}
|
|
545
|
-
function getLogLevel() {
|
|
546
|
-
return config.level;
|
|
547
|
-
}
|
|
548
|
-
function configureLogger(options) {
|
|
549
|
-
config = { ...config, ...options };
|
|
550
|
-
}
|
|
551
|
-
function enableLogging() {
|
|
552
|
-
config.level = "debug";
|
|
553
|
-
}
|
|
554
|
-
function disableLogging() {
|
|
555
|
-
config.level = "none";
|
|
556
|
-
}
|
|
557
|
-
function setDebugMode(enabled) {
|
|
558
|
-
config.level = enabled ? "debug" : "error";
|
|
559
|
-
}
|
|
560
|
-
function isDebugMode() {
|
|
561
|
-
return config.level === "debug";
|
|
562
|
-
}
|
|
563
|
-
function shouldLog(level) {
|
|
564
|
-
return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
|
|
565
|
-
}
|
|
566
|
-
function formatTag(tag, level) {
|
|
567
|
-
const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
|
|
568
|
-
if (config.colors && level !== "none") {
|
|
569
|
-
const color = LOG_LEVEL_COLORS[level];
|
|
570
|
-
return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
|
|
571
|
-
}
|
|
572
|
-
return `${timestamp}[${tag}]`;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
class Logger {
|
|
576
|
-
tag;
|
|
577
|
-
constructor(tag) {
|
|
578
|
-
this.tag = tag;
|
|
579
|
-
}
|
|
580
|
-
debug(...args) {
|
|
581
|
-
if (!shouldLog("debug"))
|
|
582
|
-
return;
|
|
583
|
-
if (config.handler) {
|
|
584
|
-
config.handler.debug(this.tag, ...args);
|
|
585
|
-
} else {
|
|
586
|
-
console.log(formatTag(this.tag, "debug"), ...args);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
info(...args) {
|
|
590
|
-
if (!shouldLog("info"))
|
|
591
|
-
return;
|
|
592
|
-
if (config.handler) {
|
|
593
|
-
config.handler.info(this.tag, ...args);
|
|
594
|
-
} else {
|
|
595
|
-
console.info(formatTag(this.tag, "info"), ...args);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
warn(...args) {
|
|
599
|
-
if (!shouldLog("warn"))
|
|
600
|
-
return;
|
|
601
|
-
if (config.handler) {
|
|
602
|
-
config.handler.warn(this.tag, ...args);
|
|
603
|
-
} else {
|
|
604
|
-
console.warn(formatTag(this.tag, "warn"), ...args);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
error(...args) {
|
|
608
|
-
if (!shouldLog("error"))
|
|
609
|
-
return;
|
|
610
|
-
if (config.handler) {
|
|
611
|
-
config.handler.error(this.tag, ...args);
|
|
612
|
-
} else {
|
|
613
|
-
console.error(formatTag(this.tag, "error"), ...args);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
time(label, fn) {
|
|
617
|
-
if (!shouldLog("debug")) {
|
|
618
|
-
return fn();
|
|
619
|
-
}
|
|
620
|
-
const start = performance.now();
|
|
621
|
-
try {
|
|
622
|
-
return fn();
|
|
623
|
-
} finally {
|
|
624
|
-
const duration = performance.now() - start;
|
|
625
|
-
this.debug(`${label}: ${duration.toFixed(2)}ms`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
async timeAsync(label, fn) {
|
|
629
|
-
if (!shouldLog("debug")) {
|
|
630
|
-
return fn();
|
|
631
|
-
}
|
|
632
|
-
const start = performance.now();
|
|
633
|
-
try {
|
|
634
|
-
return await fn();
|
|
635
|
-
} finally {
|
|
636
|
-
const duration = performance.now() - start;
|
|
637
|
-
this.debug(`${label}: ${duration.toFixed(2)}ms`);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
child(subTag) {
|
|
641
|
-
return new Logger(`${this.tag}:${subTag}`);
|
|
642
|
-
}
|
|
643
|
-
debugIf(condition, ...args) {
|
|
644
|
-
if (condition)
|
|
645
|
-
this.debug(...args);
|
|
646
|
-
}
|
|
647
|
-
warnIf(condition, ...args) {
|
|
648
|
-
if (condition)
|
|
649
|
-
this.warn(...args);
|
|
650
|
-
}
|
|
651
|
-
errorIf(condition, ...args) {
|
|
652
|
-
if (condition)
|
|
653
|
-
this.error(...args);
|
|
654
|
-
}
|
|
655
|
-
loggedOnce = new Set;
|
|
656
|
-
warnOnce(key, ...args) {
|
|
657
|
-
if (this.loggedOnce.has(key))
|
|
658
|
-
return;
|
|
659
|
-
this.loggedOnce.add(key);
|
|
660
|
-
this.warn(...args);
|
|
661
|
-
}
|
|
662
|
-
debugOnce(key, ...args) {
|
|
663
|
-
if (this.loggedOnce.has(key))
|
|
664
|
-
return;
|
|
665
|
-
this.loggedOnce.add(key);
|
|
666
|
-
this.debug(...args);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
function createLogger(tag) {
|
|
670
|
-
return new Logger(tag);
|
|
671
|
-
}
|
|
672
|
-
var logger = createLogger("Hypen");
|
|
673
|
-
var log = {
|
|
674
|
-
debug: (tag, ...args) => {
|
|
675
|
-
if (!shouldLog("debug"))
|
|
676
|
-
return;
|
|
677
|
-
console.log(formatTag(tag, "debug"), ...args);
|
|
678
|
-
},
|
|
679
|
-
info: (tag, ...args) => {
|
|
680
|
-
if (!shouldLog("info"))
|
|
681
|
-
return;
|
|
682
|
-
console.info(formatTag(tag, "info"), ...args);
|
|
683
|
-
},
|
|
684
|
-
warn: (tag, ...args) => {
|
|
685
|
-
if (!shouldLog("warn"))
|
|
686
|
-
return;
|
|
687
|
-
console.warn(formatTag(tag, "warn"), ...args);
|
|
688
|
-
},
|
|
689
|
-
error: (tag, ...args) => {
|
|
690
|
-
if (!shouldLog("error"))
|
|
691
|
-
return;
|
|
692
|
-
console.error(formatTag(tag, "error"), ...args);
|
|
693
|
-
}
|
|
694
|
-
};
|
|
695
|
-
var frameworkLoggers = {
|
|
696
|
-
hypen: createLogger("Hypen"),
|
|
697
|
-
engine: createLogger("Engine"),
|
|
698
|
-
router: createLogger("Router"),
|
|
699
|
-
state: createLogger("State"),
|
|
700
|
-
events: createLogger("Events"),
|
|
701
|
-
remote: createLogger("Remote"),
|
|
702
|
-
renderer: createLogger("Renderer"),
|
|
703
|
-
module: createLogger("Module"),
|
|
704
|
-
lifecycle: createLogger("Lifecycle"),
|
|
705
|
-
loader: createLogger("Loader"),
|
|
706
|
-
context: createLogger("Context"),
|
|
707
|
-
discovery: createLogger("Discovery"),
|
|
708
|
-
plugin: createLogger("Plugin"),
|
|
709
|
-
canvas: createLogger("Canvas"),
|
|
710
|
-
debug: createLogger("Debug")
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
// src/app.ts
|
|
714
|
-
var log2 = createLogger("ModuleInstance");
|
|
715
|
-
|
|
716
|
-
class HypenAppBuilder {
|
|
717
|
-
initialState;
|
|
718
|
-
options;
|
|
719
|
-
createdHandler;
|
|
720
|
-
activatedHandler;
|
|
721
|
-
deactivatedHandler;
|
|
722
|
-
actionHandlers = new Map;
|
|
723
|
-
destroyedHandler;
|
|
724
|
-
disconnectHandler;
|
|
725
|
-
reconnectHandler;
|
|
726
|
-
expireHandler;
|
|
727
|
-
errorHandler;
|
|
728
|
-
template;
|
|
729
|
-
_registry;
|
|
730
|
-
dataSourceEntries = [];
|
|
731
|
-
_stateStore;
|
|
732
|
-
constructor(initialState, options, registry) {
|
|
733
|
-
this.initialState = initialState;
|
|
734
|
-
this.options = options || {};
|
|
735
|
-
this._registry = registry;
|
|
736
|
-
}
|
|
737
|
-
onCreated(fn) {
|
|
738
|
-
this.createdHandler = fn;
|
|
739
|
-
return this;
|
|
740
|
-
}
|
|
741
|
-
onAction(name, fn) {
|
|
742
|
-
this.actionHandlers.set(name, fn);
|
|
743
|
-
return this;
|
|
744
|
-
}
|
|
745
|
-
onActivated(fn) {
|
|
746
|
-
this.activatedHandler = fn;
|
|
747
|
-
return this;
|
|
748
|
-
}
|
|
749
|
-
onDeactivated(fn) {
|
|
750
|
-
this.deactivatedHandler = fn;
|
|
751
|
-
return this;
|
|
752
|
-
}
|
|
753
|
-
onDestroyed(fn) {
|
|
754
|
-
this.destroyedHandler = fn;
|
|
755
|
-
return this;
|
|
756
|
-
}
|
|
757
|
-
onDisconnect(fn) {
|
|
758
|
-
this.disconnectHandler = fn;
|
|
759
|
-
return this;
|
|
760
|
-
}
|
|
761
|
-
onReconnect(fn) {
|
|
762
|
-
this.reconnectHandler = fn;
|
|
763
|
-
return this;
|
|
764
|
-
}
|
|
765
|
-
onExpire(fn) {
|
|
766
|
-
this.expireHandler = fn;
|
|
767
|
-
return this;
|
|
768
|
-
}
|
|
769
|
-
onError(fn) {
|
|
770
|
-
this.errorHandler = fn;
|
|
771
|
-
return this;
|
|
772
|
-
}
|
|
773
|
-
persist(store) {
|
|
774
|
-
this._stateStore = store;
|
|
775
|
-
return this;
|
|
776
|
-
}
|
|
777
|
-
useDataSource(plugin, config2) {
|
|
778
|
-
this.dataSourceEntries.push({ plugin, config: config2 });
|
|
779
|
-
return this;
|
|
780
|
-
}
|
|
781
|
-
ui(template) {
|
|
782
|
-
this.template = template;
|
|
783
|
-
return this.build();
|
|
784
|
-
}
|
|
785
|
-
uiFile(path) {
|
|
786
|
-
const fs = (() => ({}));
|
|
787
|
-
this.template = fs.readFileSync(path, "utf-8").trim();
|
|
788
|
-
return this.build();
|
|
789
|
-
}
|
|
790
|
-
build() {
|
|
791
|
-
const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
|
|
792
|
-
const definition = {
|
|
793
|
-
name: this.options.name,
|
|
794
|
-
actions: Array.from(this.actionHandlers.keys()),
|
|
795
|
-
stateKeys,
|
|
796
|
-
persist: this.options.persist,
|
|
797
|
-
version: this.options.version,
|
|
798
|
-
initialState: this.initialState,
|
|
799
|
-
template: this.template,
|
|
800
|
-
dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
|
|
801
|
-
stateStore: this._stateStore,
|
|
802
|
-
handlers: {
|
|
803
|
-
onCreated: this.createdHandler,
|
|
804
|
-
onActivated: this.activatedHandler,
|
|
805
|
-
onDeactivated: this.deactivatedHandler,
|
|
806
|
-
onAction: this.actionHandlers,
|
|
807
|
-
onDestroyed: this.destroyedHandler,
|
|
808
|
-
onDisconnect: this.disconnectHandler,
|
|
809
|
-
onReconnect: this.reconnectHandler,
|
|
810
|
-
onExpire: this.expireHandler,
|
|
811
|
-
onError: this.errorHandler
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
if (this.options.name && this._registry) {
|
|
815
|
-
this._registry.set(this.options.name, definition);
|
|
816
|
-
}
|
|
817
|
-
return definition;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
class HypenApp {
|
|
822
|
-
_registry = new Map;
|
|
823
|
-
defineState(initial, options) {
|
|
824
|
-
return new HypenAppBuilder(initial, options, this._registry);
|
|
825
|
-
}
|
|
826
|
-
module(name) {
|
|
827
|
-
const registry = this._registry;
|
|
828
|
-
return {
|
|
829
|
-
defineState: (initial, options) => {
|
|
830
|
-
return new HypenAppBuilder(initial, { ...options, name }, registry);
|
|
831
|
-
}
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
get(name) {
|
|
835
|
-
return this._registry.get(name);
|
|
836
|
-
}
|
|
837
|
-
has(name) {
|
|
838
|
-
return this._registry.has(name);
|
|
839
|
-
}
|
|
840
|
-
get components() {
|
|
841
|
-
return this._registry;
|
|
842
|
-
}
|
|
843
|
-
getNames() {
|
|
844
|
-
return Array.from(this._registry.keys());
|
|
845
|
-
}
|
|
846
|
-
get size() {
|
|
847
|
-
return this._registry.size;
|
|
848
|
-
}
|
|
849
|
-
unregister(name) {
|
|
850
|
-
this._registry.delete(name);
|
|
851
|
-
}
|
|
852
|
-
clear() {
|
|
853
|
-
this._registry.clear();
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
var app = new HypenApp;
|
|
857
|
-
|
|
858
|
-
class HypenModuleInstance {
|
|
859
|
-
engine;
|
|
860
|
-
definition;
|
|
861
|
-
state;
|
|
862
|
-
isDestroyed = false;
|
|
863
|
-
isActive = false;
|
|
864
|
-
router;
|
|
865
|
-
globalContext;
|
|
866
|
-
stateChangeCallbacks = [];
|
|
867
|
-
dataSourceManager;
|
|
868
|
-
dataSourceAccessor = {};
|
|
869
|
-
stateStore;
|
|
870
|
-
currentPersistKey = null;
|
|
871
|
-
persistDebounceTimer;
|
|
872
|
-
sessionId;
|
|
873
|
-
moduleKey = "";
|
|
874
|
-
constructor(engine, definition, router, globalContext, sessionId) {
|
|
875
|
-
this.engine = engine;
|
|
876
|
-
this.definition = definition;
|
|
877
|
-
this.router = router ?? null;
|
|
878
|
-
this.globalContext = globalContext;
|
|
879
|
-
this.sessionId = sessionId ?? crypto.randomUUID();
|
|
880
|
-
this.stateStore = definition.stateStore;
|
|
881
|
-
const moduleKey = (definition.name || "").toLowerCase();
|
|
882
|
-
this.moduleKey = moduleKey;
|
|
883
|
-
this.state = createObservableState(definition.initialState, {
|
|
884
|
-
onChange: (change) => {
|
|
885
|
-
this.engine.updateStateSparse(moduleKey || null, change.paths, change.newValues);
|
|
886
|
-
this.stateChangeCallbacks.forEach((cb) => cb());
|
|
887
|
-
this.persistIfNeeded();
|
|
888
|
-
}
|
|
889
|
-
});
|
|
890
|
-
const snapshot = getStateSnapshot(this.state);
|
|
891
|
-
if (moduleKey) {
|
|
892
|
-
this.engine.registerModule(moduleKey, definition.actions, definition.stateKeys, snapshot);
|
|
893
|
-
} else {
|
|
894
|
-
this.engine.setModule("AnonymousModule", definition.actions, definition.stateKeys, snapshot);
|
|
895
|
-
}
|
|
896
|
-
for (const [actionName, handler] of definition.handlers.onAction) {
|
|
897
|
-
log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
|
|
898
|
-
this.engine.onAction(actionName, async (action) => {
|
|
899
|
-
log2.debug(`Action handler fired: ${actionName}`, action);
|
|
900
|
-
const actionCtx = {
|
|
901
|
-
name: action.name,
|
|
902
|
-
payload: action.payload,
|
|
903
|
-
sender: action.sender
|
|
904
|
-
};
|
|
905
|
-
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
906
|
-
const result = await this.executeAction(actionName, handler, {
|
|
907
|
-
action: actionCtx,
|
|
908
|
-
state: this.state,
|
|
909
|
-
context,
|
|
910
|
-
dataSources: this.dataSourceAccessor
|
|
911
|
-
});
|
|
912
|
-
if (!result.ok) {
|
|
913
|
-
const shouldRethrow = await this.handleError(result.error, { actionName });
|
|
914
|
-
if (shouldRethrow) {
|
|
915
|
-
throw result.error;
|
|
916
|
-
}
|
|
917
|
-
} else {
|
|
918
|
-
log2.debug(`Action handler completed: ${actionName}`);
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
this.engine.onAction("__hypen_bind", (action) => {
|
|
923
|
-
const payload = action.payload;
|
|
924
|
-
if (!payload?.path)
|
|
925
|
-
return;
|
|
926
|
-
const segments = payload.path.split(".");
|
|
927
|
-
let target = this.state;
|
|
928
|
-
for (let i = 0;i < segments.length - 1; i++) {
|
|
929
|
-
const seg = segments[i];
|
|
930
|
-
target = target?.[seg];
|
|
931
|
-
if (target == null)
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
const lastSeg = segments[segments.length - 1];
|
|
935
|
-
target[lastSeg] = payload.value;
|
|
936
|
-
});
|
|
937
|
-
this._readyPromise = this.callCreatedHandler();
|
|
938
|
-
}
|
|
939
|
-
_readyPromise = Promise.resolve();
|
|
940
|
-
async waitForReady() {
|
|
941
|
-
await this._readyPromise;
|
|
942
|
-
}
|
|
943
|
-
async activate() {
|
|
944
|
-
if (this.isDestroyed || this.isActive)
|
|
945
|
-
return;
|
|
946
|
-
await this._readyPromise;
|
|
947
|
-
if (this.isDestroyed || this.isActive)
|
|
948
|
-
return;
|
|
949
|
-
this.isActive = true;
|
|
950
|
-
if (this.definition.handlers.onActivated) {
|
|
951
|
-
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
952
|
-
try {
|
|
953
|
-
await this.definition.handlers.onActivated(this.state, context);
|
|
954
|
-
} catch (e) {
|
|
955
|
-
const error = e instanceof HypenError ? e : new ActionError("onActivated", e);
|
|
956
|
-
const shouldRethrow = await this.handleError(error, { lifecycle: "activated" });
|
|
957
|
-
if (shouldRethrow) {
|
|
958
|
-
throw error;
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
async deactivate() {
|
|
964
|
-
if (this.isDestroyed || !this.isActive)
|
|
965
|
-
return;
|
|
966
|
-
this.isActive = false;
|
|
967
|
-
if (this.definition.handlers.onDeactivated) {
|
|
968
|
-
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
969
|
-
try {
|
|
970
|
-
await this.definition.handlers.onDeactivated(this.state, context);
|
|
971
|
-
} catch (e) {
|
|
972
|
-
const error = e instanceof HypenError ? e : new ActionError("onDeactivated", e);
|
|
973
|
-
const shouldRethrow = await this.handleError(error, { lifecycle: "deactivated" });
|
|
974
|
-
if (shouldRethrow) {
|
|
975
|
-
throw error;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
createGlobalContextAPI() {
|
|
981
|
-
if (!this.globalContext) {
|
|
982
|
-
throw new Error("Global context not available");
|
|
983
|
-
}
|
|
984
|
-
const ctx = this.globalContext;
|
|
985
|
-
const api = {
|
|
986
|
-
getModule: (id) => ctx.getModule(id),
|
|
987
|
-
hasModule: (id) => ctx.hasModule(id),
|
|
988
|
-
getModuleIds: () => ctx.getModuleIds(),
|
|
989
|
-
getGlobalState: () => ctx.getGlobalState(),
|
|
990
|
-
emit: (event, payload) => ctx.emit(event, payload),
|
|
991
|
-
on: (event, handler) => ctx.on(event, handler),
|
|
992
|
-
router: this.router
|
|
993
|
-
};
|
|
994
|
-
const ctxRecord = ctx;
|
|
995
|
-
if (ctxRecord.__hypenEngine) {
|
|
996
|
-
api.__hypenEngine = ctxRecord.__hypenEngine;
|
|
997
|
-
}
|
|
998
|
-
return api;
|
|
999
|
-
}
|
|
1000
|
-
async executeAction(actionName, handler, ctx) {
|
|
1001
|
-
try {
|
|
1002
|
-
const result = handler(ctx);
|
|
1003
|
-
await result;
|
|
1004
|
-
return Ok(undefined);
|
|
1005
|
-
} catch (e) {
|
|
1006
|
-
return Err(new ActionError(actionName, e));
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
async handleError(error, context) {
|
|
1010
|
-
const errorCtx = {
|
|
1011
|
-
error,
|
|
1012
|
-
state: this.state,
|
|
1013
|
-
actionName: context.actionName,
|
|
1014
|
-
lifecycle: context.lifecycle
|
|
1015
|
-
};
|
|
1016
|
-
if (this.definition.handlers.onError) {
|
|
1017
|
-
try {
|
|
1018
|
-
const result = await this.definition.handlers.onError(errorCtx);
|
|
1019
|
-
if (result && typeof result === "object") {
|
|
1020
|
-
if ("handled" in result && result.handled) {
|
|
1021
|
-
return false;
|
|
1022
|
-
}
|
|
1023
|
-
if ("rethrow" in result && result.rethrow) {
|
|
1024
|
-
return true;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
} catch (handlerError) {
|
|
1028
|
-
log2.error("Error in onError handler:", handlerError);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
if (this.globalContext) {
|
|
1032
|
-
const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
|
|
1033
|
-
this.globalContext.emit("error", {
|
|
1034
|
-
message: error.message,
|
|
1035
|
-
error,
|
|
1036
|
-
context: eventContext
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
|
|
1040
|
-
return false;
|
|
1041
|
-
}
|
|
1042
|
-
persistIfNeeded() {
|
|
1043
|
-
if (!this.stateStore)
|
|
1044
|
-
return;
|
|
1045
|
-
const store = this.stateStore;
|
|
1046
|
-
const newKey = store.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
|
|
1047
|
-
if (newKey && !this.currentPersistKey) {
|
|
1048
|
-
this.activatePersistence(newKey);
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
if (!newKey && this.currentPersistKey) {
|
|
1052
|
-
this.currentPersistKey = null;
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
if (newKey && newKey !== this.currentPersistKey) {
|
|
1056
|
-
this.activatePersistence(newKey);
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
if (!this.currentPersistKey)
|
|
1060
|
-
return;
|
|
1061
|
-
clearTimeout(this.persistDebounceTimer);
|
|
1062
|
-
this.persistDebounceTimer = setTimeout(() => {
|
|
1063
|
-
const snapshot = getStateSnapshot(this.state);
|
|
1064
|
-
store.save(this.currentPersistKey, snapshot);
|
|
1065
|
-
}, 50);
|
|
1066
|
-
}
|
|
1067
|
-
async activatePersistence(key) {
|
|
1068
|
-
this.currentPersistKey = key;
|
|
1069
|
-
const stored = await this.stateStore.load(key);
|
|
1070
|
-
if (stored) {
|
|
1071
|
-
const merged = { ...this.definition.initialState, ...stored };
|
|
1072
|
-
Object.assign(this.state, merged);
|
|
1073
|
-
} else {
|
|
1074
|
-
const snapshot = getStateSnapshot(this.state);
|
|
1075
|
-
await this.stateStore.save(key, snapshot);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
async callCreatedHandler() {
|
|
1079
|
-
if (this.stateStore) {
|
|
1080
|
-
const key = this.stateStore.resolveKey(this.state, this.definition.name || "AnonymousModule", this.sessionId);
|
|
1081
|
-
if (key) {
|
|
1082
|
-
this.currentPersistKey = key;
|
|
1083
|
-
const stored = await this.stateStore.load(key);
|
|
1084
|
-
if (stored) {
|
|
1085
|
-
const merged = { ...this.definition.initialState, ...stored };
|
|
1086
|
-
Object.assign(this.state, merged);
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
if (this.definition.dataSources?.length) {
|
|
1091
|
-
const dsEngine = this.engine;
|
|
1092
|
-
this.dataSourceManager = new DataSourceManager(dsEngine);
|
|
1093
|
-
for (const { plugin, config: config2 } of this.definition.dataSources) {
|
|
1094
|
-
try {
|
|
1095
|
-
await this.dataSourceManager.use(plugin, config2);
|
|
1096
|
-
this.dataSourceAccessor[plugin.name] = new Proxy(plugin, {
|
|
1097
|
-
get(target, prop) {
|
|
1098
|
-
if (typeof prop === "string" && !(prop in target)) {
|
|
1099
|
-
return (...args) => target.call(prop, ...args);
|
|
1100
|
-
}
|
|
1101
|
-
return target[prop];
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
} catch (e) {
|
|
1105
|
-
log2.error(`Failed to connect data source "${plugin.name}":`, e);
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
if (this.definition.handlers.onCreated) {
|
|
1110
|
-
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
1111
|
-
try {
|
|
1112
|
-
await this.definition.handlers.onCreated(this.state, context);
|
|
1113
|
-
} catch (e) {
|
|
1114
|
-
const error = e instanceof HypenError ? e : new ActionError("onCreated", e);
|
|
1115
|
-
const shouldRethrow = await this.handleError(error, { lifecycle: "created" });
|
|
1116
|
-
if (shouldRethrow) {
|
|
1117
|
-
throw error;
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
onStateChange(callback) {
|
|
1123
|
-
this.stateChangeCallbacks.push(callback);
|
|
1124
|
-
}
|
|
1125
|
-
async destroy() {
|
|
1126
|
-
if (this.isDestroyed)
|
|
1127
|
-
return;
|
|
1128
|
-
if (this.isActive) {
|
|
1129
|
-
await this.deactivate();
|
|
1130
|
-
}
|
|
1131
|
-
if (this.currentPersistKey && this.stateStore) {
|
|
1132
|
-
clearTimeout(this.persistDebounceTimer);
|
|
1133
|
-
const snapshot = getStateSnapshot(this.state);
|
|
1134
|
-
await this.stateStore.save(this.currentPersistKey, snapshot);
|
|
1135
|
-
}
|
|
1136
|
-
if (this.dataSourceManager) {
|
|
1137
|
-
try {
|
|
1138
|
-
await this.dataSourceManager.disconnectAll();
|
|
1139
|
-
} catch (e) {
|
|
1140
|
-
log2.error("Error disconnecting data sources:", e);
|
|
1141
|
-
}
|
|
1142
|
-
this.dataSourceManager = undefined;
|
|
1143
|
-
this.dataSourceAccessor = {};
|
|
1144
|
-
}
|
|
1145
|
-
if (this.definition.handlers.onDestroyed) {
|
|
1146
|
-
try {
|
|
1147
|
-
await this.definition.handlers.onDestroyed(this.state);
|
|
1148
|
-
} catch (e) {
|
|
1149
|
-
const error = e instanceof HypenError ? e : new ActionError("onDestroyed", e);
|
|
1150
|
-
const shouldRethrow = await this.handleError(error, { lifecycle: "destroyed" });
|
|
1151
|
-
if (shouldRethrow) {
|
|
1152
|
-
throw error;
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
this.isDestroyed = true;
|
|
1157
|
-
}
|
|
1158
|
-
getState() {
|
|
1159
|
-
return getStateSnapshot(this.state);
|
|
1160
|
-
}
|
|
1161
|
-
getLiveState() {
|
|
1162
|
-
return this.state;
|
|
1163
|
-
}
|
|
1164
|
-
updateState(patch) {
|
|
1165
|
-
Object.assign(this.state, patch);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// src/renderer.ts
|
|
1170
|
-
var log3 = frameworkLoggers.renderer;
|
|
1171
|
-
|
|
1172
|
-
class BaseRenderer {
|
|
1173
|
-
nodes = new Map;
|
|
1174
|
-
getNode(id) {
|
|
1175
|
-
return this.nodes.get(id);
|
|
1176
|
-
}
|
|
1177
|
-
clear() {
|
|
1178
|
-
this.nodes.clear();
|
|
1179
|
-
}
|
|
1180
|
-
applyPatch(patch) {
|
|
1181
|
-
switch (patch.type) {
|
|
1182
|
-
case "create":
|
|
1183
|
-
this.onCreate(patch.id, patch.elementType, patch.props || {});
|
|
1184
|
-
break;
|
|
1185
|
-
case "setProp":
|
|
1186
|
-
this.onSetProp(patch.id, patch.name, patch.value);
|
|
1187
|
-
break;
|
|
1188
|
-
case "setText":
|
|
1189
|
-
this.onSetText(patch.id, patch.text);
|
|
1190
|
-
break;
|
|
1191
|
-
case "insert":
|
|
1192
|
-
this.onInsert(patch.parentId, patch.id, patch.beforeId);
|
|
1193
|
-
break;
|
|
1194
|
-
case "move":
|
|
1195
|
-
this.onMove(patch.parentId, patch.id, patch.beforeId);
|
|
1196
|
-
break;
|
|
1197
|
-
case "remove":
|
|
1198
|
-
this.onRemove(patch.id);
|
|
1199
|
-
break;
|
|
1200
|
-
case "attachEvent":
|
|
1201
|
-
this.onAttachEvent(patch.id, patch.eventName);
|
|
1202
|
-
break;
|
|
1203
|
-
case "detachEvent":
|
|
1204
|
-
this.onDetachEvent(patch.id, patch.eventName);
|
|
1205
|
-
break;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
class ConsoleRenderer {
|
|
1211
|
-
applyPatches(patches) {
|
|
1212
|
-
console.group("Hypen Patches");
|
|
1213
|
-
for (const patch of patches) {
|
|
1214
|
-
console.log(patch);
|
|
1215
|
-
}
|
|
1216
|
-
console.groupEnd();
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// src/disposable.ts
|
|
1221
|
-
var log4 = frameworkLoggers.lifecycle;
|
|
1222
|
-
function isDisposable(obj) {
|
|
1223
|
-
return obj !== null && typeof obj === "object" && "dispose" in obj && typeof obj.dispose === "function";
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
class DisposableStack {
|
|
1227
|
-
stack = [];
|
|
1228
|
-
disposed = false;
|
|
1229
|
-
add(disposable) {
|
|
1230
|
-
if (this.disposed) {
|
|
1231
|
-
disposable.dispose();
|
|
1232
|
-
return disposable;
|
|
1233
|
-
}
|
|
1234
|
-
this.stack.push(disposable);
|
|
1235
|
-
return disposable;
|
|
1236
|
-
}
|
|
1237
|
-
addCallback(callback) {
|
|
1238
|
-
this.add({ dispose: callback });
|
|
1239
|
-
}
|
|
1240
|
-
addValue(value, dispose) {
|
|
1241
|
-
this.add({ dispose: () => dispose(value) });
|
|
1242
|
-
return value;
|
|
1243
|
-
}
|
|
1244
|
-
dispose() {
|
|
1245
|
-
if (this.disposed)
|
|
1246
|
-
return;
|
|
1247
|
-
this.disposed = true;
|
|
1248
|
-
while (this.stack.length > 0) {
|
|
1249
|
-
const item = this.stack.pop();
|
|
1250
|
-
try {
|
|
1251
|
-
item.dispose();
|
|
1252
|
-
} catch (error) {
|
|
1253
|
-
log4.error("Error during dispose:", error);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
get isDisposed() {
|
|
1258
|
-
return this.disposed;
|
|
1259
|
-
}
|
|
1260
|
-
get size() {
|
|
1261
|
-
return this.stack.length;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
function disposableListener(target, event, handler, options) {
|
|
1265
|
-
target.addEventListener(event, handler, options);
|
|
1266
|
-
return {
|
|
1267
|
-
dispose: () => target.removeEventListener(event, handler, options)
|
|
1268
|
-
};
|
|
1269
|
-
}
|
|
1270
|
-
function disposableTimeout(callback, ms) {
|
|
1271
|
-
const id = setTimeout(callback, ms);
|
|
1272
|
-
return {
|
|
1273
|
-
id,
|
|
1274
|
-
dispose: () => clearTimeout(id)
|
|
1275
|
-
};
|
|
1276
|
-
}
|
|
1277
|
-
function disposableInterval(callback, ms) {
|
|
1278
|
-
const id = setInterval(callback, ms);
|
|
1279
|
-
return {
|
|
1280
|
-
id,
|
|
1281
|
-
dispose: () => clearInterval(id)
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
function disposableWebSocket(ws) {
|
|
1285
|
-
return {
|
|
1286
|
-
dispose: () => {
|
|
1287
|
-
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
1288
|
-
ws.close();
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
function disposableAbortController() {
|
|
1294
|
-
const controller = new AbortController;
|
|
1295
|
-
return {
|
|
1296
|
-
controller,
|
|
1297
|
-
signal: controller.signal,
|
|
1298
|
-
dispose: () => controller.abort()
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
function disposableSubscription(unsubscribe) {
|
|
1302
|
-
return { dispose: unsubscribe };
|
|
1303
|
-
}
|
|
1304
|
-
var ELEMENT_DISPOSABLES = Symbol("hypen.disposables");
|
|
1305
|
-
function getElementDisposables(element) {
|
|
1306
|
-
const existing = element[ELEMENT_DISPOSABLES];
|
|
1307
|
-
if (existing instanceof DisposableStack) {
|
|
1308
|
-
return existing;
|
|
1309
|
-
}
|
|
1310
|
-
const stack = new DisposableStack;
|
|
1311
|
-
element[ELEMENT_DISPOSABLES] = stack;
|
|
1312
|
-
return stack;
|
|
1313
|
-
}
|
|
1314
|
-
function disposeElement(element) {
|
|
1315
|
-
const stack = element[ELEMENT_DISPOSABLES];
|
|
1316
|
-
if (stack instanceof DisposableStack) {
|
|
1317
|
-
stack.dispose();
|
|
1318
|
-
delete element[ELEMENT_DISPOSABLES];
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
function hasElementDisposables(element) {
|
|
1322
|
-
return element[ELEMENT_DISPOSABLES] instanceof DisposableStack;
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
class DisposableMixin {
|
|
1326
|
-
disposables = new DisposableStack;
|
|
1327
|
-
track(disposable) {
|
|
1328
|
-
return this.disposables.add(disposable);
|
|
1329
|
-
}
|
|
1330
|
-
onDispose(callback) {
|
|
1331
|
-
this.disposables.addCallback(callback);
|
|
1332
|
-
}
|
|
1333
|
-
dispose() {
|
|
1334
|
-
this.disposables.dispose();
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
function compositeDisposable(...disposables) {
|
|
1338
|
-
return {
|
|
1339
|
-
dispose: () => {
|
|
1340
|
-
for (const d of disposables) {
|
|
1341
|
-
try {
|
|
1342
|
-
d.dispose();
|
|
1343
|
-
} catch (error) {
|
|
1344
|
-
log4.error("Error during dispose:", error);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
async function using(resource, fn) {
|
|
1351
|
-
const r = typeof resource === "function" ? resource() : resource;
|
|
1352
|
-
try {
|
|
1353
|
-
return await fn(r);
|
|
1354
|
-
} finally {
|
|
1355
|
-
r.dispose();
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
function usingSync(resource, fn) {
|
|
1359
|
-
const r = typeof resource === "function" ? resource() : resource;
|
|
1360
|
-
try {
|
|
1361
|
-
return fn(r);
|
|
1362
|
-
} finally {
|
|
1363
|
-
r.dispose();
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
// src/router.ts
|
|
1368
|
-
var log5 = frameworkLoggers.router;
|
|
1369
|
-
|
|
1370
|
-
class HypenRouter {
|
|
1371
|
-
state;
|
|
1372
|
-
subscribers = new Set;
|
|
1373
|
-
isInitialized = false;
|
|
1374
|
-
isUpdating = false;
|
|
1375
|
-
browserListeners = [];
|
|
1376
|
-
constructor() {
|
|
1377
|
-
this.state = createObservableState({
|
|
1378
|
-
currentPath: "/",
|
|
1379
|
-
params: {},
|
|
1380
|
-
query: {},
|
|
1381
|
-
previousPath: null
|
|
1382
|
-
}, {
|
|
1383
|
-
onChange: () => {
|
|
1384
|
-
this.notifySubscribers();
|
|
1385
|
-
}
|
|
1386
|
-
});
|
|
1387
|
-
if (typeof window !== "undefined") {
|
|
1388
|
-
this.initializeBrowserSync();
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
initializeBrowserSync() {
|
|
1392
|
-
const initialPath = this.getPathFromBrowser();
|
|
1393
|
-
this.state.currentPath = initialPath;
|
|
1394
|
-
this.state.params = {};
|
|
1395
|
-
this.state.query = this.parseQuery();
|
|
1396
|
-
this.browserListeners.push(disposableListener(window, "popstate", () => {
|
|
1397
|
-
const newPath = this.getPathFromBrowser();
|
|
1398
|
-
this.updatePath(newPath, false);
|
|
1399
|
-
}));
|
|
1400
|
-
this.browserListeners.push(disposableListener(window, "hashchange", () => {
|
|
1401
|
-
if (this.isUpdating)
|
|
1402
|
-
return;
|
|
1403
|
-
const newPath = this.getPathFromBrowser();
|
|
1404
|
-
this.updatePath(newPath, false);
|
|
1405
|
-
}));
|
|
1406
|
-
this.isInitialized = true;
|
|
1407
|
-
log5.debug("Router initialized at:", initialPath);
|
|
1408
|
-
}
|
|
1409
|
-
getPathFromBrowser() {
|
|
1410
|
-
if (typeof window === "undefined")
|
|
1411
|
-
return "/";
|
|
1412
|
-
const hash = window.location.hash.slice(1);
|
|
1413
|
-
if (hash)
|
|
1414
|
-
return hash;
|
|
1415
|
-
return window.location.pathname;
|
|
1416
|
-
}
|
|
1417
|
-
parseQuery() {
|
|
1418
|
-
if (typeof window === "undefined")
|
|
1419
|
-
return {};
|
|
1420
|
-
const query = {};
|
|
1421
|
-
const searchParams = new URLSearchParams(window.location.search);
|
|
1422
|
-
searchParams.forEach((value, key) => {
|
|
1423
|
-
query[key] = value;
|
|
1424
|
-
});
|
|
1425
|
-
return query;
|
|
1426
|
-
}
|
|
1427
|
-
push(path) {
|
|
1428
|
-
log5.debug("push:", path);
|
|
1429
|
-
this.updatePath(path, true);
|
|
1430
|
-
}
|
|
1431
|
-
replace(path) {
|
|
1432
|
-
log5.debug("replace:", path);
|
|
1433
|
-
this.updatePath(path, true, true);
|
|
1434
|
-
}
|
|
1435
|
-
back() {
|
|
1436
|
-
log5.debug("back");
|
|
1437
|
-
if (typeof window !== "undefined") {
|
|
1438
|
-
window.history.back();
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
forward() {
|
|
1442
|
-
log5.debug("forward");
|
|
1443
|
-
if (typeof window !== "undefined") {
|
|
1444
|
-
window.history.forward();
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
updatePath(path, updateBrowser, replace = false) {
|
|
1448
|
-
if (this.isUpdating)
|
|
1449
|
-
return;
|
|
1450
|
-
this.isUpdating = true;
|
|
1451
|
-
try {
|
|
1452
|
-
const oldPath = this.state.currentPath;
|
|
1453
|
-
this.state.previousPath = oldPath;
|
|
1454
|
-
this.state.currentPath = path;
|
|
1455
|
-
this.state.query = this.parseQuery();
|
|
1456
|
-
this.notifySubscribers();
|
|
1457
|
-
if (updateBrowser && typeof window !== "undefined") {
|
|
1458
|
-
const url = "#" + path;
|
|
1459
|
-
if (replace) {
|
|
1460
|
-
window.history.replaceState(null, "", url);
|
|
1461
|
-
} else {
|
|
1462
|
-
window.history.pushState(null, "", url);
|
|
1463
|
-
}
|
|
1464
|
-
try {
|
|
1465
|
-
const hashChangeEvent = new HashChangeEvent("hashchange", {
|
|
1466
|
-
oldURL: window.location.href.replace(window.location.hash, "#" + oldPath),
|
|
1467
|
-
newURL: window.location.href
|
|
1468
|
-
});
|
|
1469
|
-
window.dispatchEvent(hashChangeEvent);
|
|
1470
|
-
} catch {}
|
|
1471
|
-
}
|
|
1472
|
-
} finally {
|
|
1473
|
-
this.isUpdating = false;
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
getCurrentPath() {
|
|
1477
|
-
return this.state.currentPath;
|
|
1478
|
-
}
|
|
1479
|
-
getParams() {
|
|
1480
|
-
return { ...this.state.params };
|
|
1481
|
-
}
|
|
1482
|
-
getQuery() {
|
|
1483
|
-
return { ...this.state.query };
|
|
1484
|
-
}
|
|
1485
|
-
getState() {
|
|
1486
|
-
return getStateSnapshot(this.state);
|
|
1487
|
-
}
|
|
1488
|
-
matchPath(pattern, path) {
|
|
1489
|
-
if (!pattern || typeof pattern !== "string")
|
|
1490
|
-
return null;
|
|
1491
|
-
if (!path || typeof path !== "string")
|
|
1492
|
-
return null;
|
|
1493
|
-
const cleanPath = path.split("?")[0] ?? path;
|
|
1494
|
-
const result = portable.matchPath(pattern, cleanPath);
|
|
1495
|
-
if (!result)
|
|
1496
|
-
return null;
|
|
1497
|
-
const params = {};
|
|
1498
|
-
for (const [name, raw] of Object.entries(result.params)) {
|
|
1499
|
-
try {
|
|
1500
|
-
params[name] = decodeURIComponent(raw);
|
|
1501
|
-
} catch {
|
|
1502
|
-
params[name] = raw;
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
return {
|
|
1506
|
-
params,
|
|
1507
|
-
query: this.state.query,
|
|
1508
|
-
path
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
onNavigate(callback) {
|
|
1512
|
-
this.subscribers.add(callback);
|
|
1513
|
-
try {
|
|
1514
|
-
callback(this.getState());
|
|
1515
|
-
} catch (error) {
|
|
1516
|
-
log5.error("Error in route subscriber:", error);
|
|
1517
|
-
}
|
|
1518
|
-
return () => {
|
|
1519
|
-
this.subscribers.delete(callback);
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
|
-
notifySubscribers() {
|
|
1523
|
-
const routeState = this.getState();
|
|
1524
|
-
this.subscribers.forEach((callback) => {
|
|
1525
|
-
try {
|
|
1526
|
-
callback(routeState);
|
|
1527
|
-
} catch (error) {
|
|
1528
|
-
log5.error("Error in route subscriber:", error);
|
|
1529
|
-
}
|
|
1530
|
-
});
|
|
1531
|
-
}
|
|
1532
|
-
isActive(pattern) {
|
|
1533
|
-
return this.matchPath(pattern, this.state.currentPath) !== null;
|
|
1534
|
-
}
|
|
1535
|
-
buildUrl(path, query) {
|
|
1536
|
-
if (!query || Object.keys(query).length === 0) {
|
|
1537
|
-
return path;
|
|
1538
|
-
}
|
|
1539
|
-
const queryString = new URLSearchParams(query).toString();
|
|
1540
|
-
return `${path}?${queryString}`;
|
|
1541
|
-
}
|
|
1542
|
-
dispose() {
|
|
1543
|
-
for (const listener of this.browserListeners) {
|
|
1544
|
-
listener.dispose();
|
|
1545
|
-
}
|
|
1546
|
-
this.browserListeners = [];
|
|
1547
|
-
this.subscribers.clear();
|
|
1548
|
-
this.isInitialized = false;
|
|
1549
|
-
log5.debug("Router disposed");
|
|
1550
|
-
}
|
|
1551
|
-
[Symbol.dispose]() {
|
|
1552
|
-
this.dispose();
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
// src/events.ts
|
|
1557
|
-
var log6 = frameworkLoggers.events;
|
|
1558
|
-
|
|
1559
|
-
class TypedEventEmitter {
|
|
1560
|
-
eventBus = new Map;
|
|
1561
|
-
emit(event, payload) {
|
|
1562
|
-
const handlers = this.eventBus.get(event);
|
|
1563
|
-
if (!handlers || handlers.size === 0) {
|
|
1564
|
-
return;
|
|
1565
|
-
}
|
|
1566
|
-
handlers.forEach((handler) => {
|
|
1567
|
-
try {
|
|
1568
|
-
handler(payload);
|
|
1569
|
-
} catch (error) {
|
|
1570
|
-
log6.error(`Error in event handler for "${String(event)}":`, error);
|
|
1571
|
-
}
|
|
1572
|
-
});
|
|
1573
|
-
}
|
|
1574
|
-
on(event, handler) {
|
|
1575
|
-
if (!this.eventBus.has(event)) {
|
|
1576
|
-
this.eventBus.set(event, new Set);
|
|
1577
|
-
}
|
|
1578
|
-
const handlers = this.eventBus.get(event);
|
|
1579
|
-
handlers.add(handler);
|
|
1580
|
-
return () => {
|
|
1581
|
-
handlers.delete(handler);
|
|
1582
|
-
if (handlers.size === 0) {
|
|
1583
|
-
this.eventBus.delete(event);
|
|
1584
|
-
}
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
once(event, handler) {
|
|
1588
|
-
const wrappedHandler = (payload) => {
|
|
1589
|
-
handler(payload);
|
|
1590
|
-
unsubscribe();
|
|
1591
|
-
};
|
|
1592
|
-
const unsubscribe = this.on(event, wrappedHandler);
|
|
1593
|
-
return unsubscribe;
|
|
1594
|
-
}
|
|
1595
|
-
off(event, handler) {
|
|
1596
|
-
const handlers = this.eventBus.get(event);
|
|
1597
|
-
if (handlers) {
|
|
1598
|
-
handlers.delete(handler);
|
|
1599
|
-
if (handlers.size === 0) {
|
|
1600
|
-
this.eventBus.delete(event);
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
removeAllListeners(event) {
|
|
1605
|
-
this.eventBus.delete(event);
|
|
1606
|
-
}
|
|
1607
|
-
clearAll() {
|
|
1608
|
-
this.eventBus.clear();
|
|
1609
|
-
}
|
|
1610
|
-
listenerCount(event) {
|
|
1611
|
-
return this.eventBus.get(event)?.size ?? 0;
|
|
1612
|
-
}
|
|
1613
|
-
eventNames() {
|
|
1614
|
-
return Array.from(this.eventBus.keys());
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
function createEventEmitter() {
|
|
1618
|
-
return new TypedEventEmitter;
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
// src/context.ts
|
|
1622
|
-
var log7 = frameworkLoggers.context;
|
|
1623
|
-
|
|
1624
|
-
class HypenGlobalContext {
|
|
1625
|
-
modules = new Map;
|
|
1626
|
-
typedEvents;
|
|
1627
|
-
constructor() {
|
|
1628
|
-
this.typedEvents = new TypedEventEmitter;
|
|
1629
|
-
}
|
|
1630
|
-
get events() {
|
|
1631
|
-
return this.typedEvents;
|
|
1632
|
-
}
|
|
1633
|
-
registerModule(id, instance) {
|
|
1634
|
-
if (this.modules.has(id)) {
|
|
1635
|
-
log7.warn(`Module "${id}" is already registered. Overwriting.`);
|
|
1636
|
-
}
|
|
1637
|
-
this.modules.set(id, instance);
|
|
1638
|
-
log7.debug(`Registered module: ${id}`);
|
|
1639
|
-
}
|
|
1640
|
-
unregisterModule(id) {
|
|
1641
|
-
this.modules.delete(id);
|
|
1642
|
-
log7.debug(`Unregistered module: ${id}`);
|
|
1643
|
-
}
|
|
1644
|
-
getModule(id) {
|
|
1645
|
-
const module = this.modules.get(id);
|
|
1646
|
-
if (!module) {
|
|
1647
|
-
throw new Error(`Module "${id}" not found. Available modules: ${Array.from(this.modules.keys()).join(", ")}`);
|
|
1648
|
-
}
|
|
1649
|
-
return {
|
|
1650
|
-
state: module.getLiveState(),
|
|
1651
|
-
setState: (patch) => module.updateState(patch),
|
|
1652
|
-
getState: () => module.getState()
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
hasModule(id) {
|
|
1656
|
-
return this.modules.has(id);
|
|
1657
|
-
}
|
|
1658
|
-
getModuleIds() {
|
|
1659
|
-
return Array.from(this.modules.keys());
|
|
1660
|
-
}
|
|
1661
|
-
getGlobalState() {
|
|
1662
|
-
const state = {};
|
|
1663
|
-
this.modules.forEach((module, id) => {
|
|
1664
|
-
state[id] = module.getState();
|
|
1665
|
-
});
|
|
1666
|
-
return state;
|
|
1667
|
-
}
|
|
1668
|
-
emit(event, payload) {
|
|
1669
|
-
if (this.typedEvents.listenerCount(event) === 0) {
|
|
1670
|
-
log7.debug(`Event "${event}" emitted but no listeners`);
|
|
1671
|
-
return;
|
|
1672
|
-
}
|
|
1673
|
-
log7.debug(`Emitting event: ${event}`, payload);
|
|
1674
|
-
this.typedEvents.emit(event, payload);
|
|
1675
|
-
}
|
|
1676
|
-
on(event, handler) {
|
|
1677
|
-
log7.debug(`Listening to event: ${event}`);
|
|
1678
|
-
return this.typedEvents.on(event, handler);
|
|
1679
|
-
}
|
|
1680
|
-
off(event, handler) {
|
|
1681
|
-
this.typedEvents.off(event, handler);
|
|
1682
|
-
}
|
|
1683
|
-
clearEvent(event) {
|
|
1684
|
-
this.typedEvents.removeAllListeners(event);
|
|
1685
|
-
}
|
|
1686
|
-
clearAllEvents() {
|
|
1687
|
-
this.typedEvents.clearAll();
|
|
1688
|
-
}
|
|
1689
|
-
debug() {
|
|
1690
|
-
return {
|
|
1691
|
-
modules: this.getModuleIds(),
|
|
1692
|
-
events: this.typedEvents.eventNames(),
|
|
1693
|
-
state: this.getGlobalState()
|
|
1694
|
-
};
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
// src/retry.ts
|
|
1699
|
-
var DEFAULT_OPTIONS = {
|
|
1700
|
-
maxAttempts: 3,
|
|
1701
|
-
delayMs: 1000,
|
|
1702
|
-
backoff: "exponential",
|
|
1703
|
-
maxDelayMs: 30000,
|
|
1704
|
-
jitter: 0.1
|
|
1705
|
-
};
|
|
1706
|
-
function calculateDelay(attempt, options) {
|
|
1707
|
-
let delay;
|
|
1708
|
-
switch (options.backoff) {
|
|
1709
|
-
case "exponential":
|
|
1710
|
-
delay = options.delayMs * Math.pow(2, attempt - 1);
|
|
1711
|
-
break;
|
|
1712
|
-
case "linear":
|
|
1713
|
-
delay = options.delayMs * attempt;
|
|
1714
|
-
break;
|
|
1715
|
-
case "none":
|
|
1716
|
-
delay = options.delayMs;
|
|
1717
|
-
break;
|
|
1718
|
-
}
|
|
1719
|
-
if (options.jitter > 0) {
|
|
1720
|
-
const jitterRange = delay * options.jitter;
|
|
1721
|
-
delay += (Math.random() * 2 - 1) * jitterRange;
|
|
1722
|
-
}
|
|
1723
|
-
return Math.min(delay, options.maxDelayMs);
|
|
1724
|
-
}
|
|
1725
|
-
function sleep(ms, signal) {
|
|
1726
|
-
return new Promise((resolve, reject) => {
|
|
1727
|
-
if (signal?.aborted) {
|
|
1728
|
-
reject(new Error("Retry aborted"));
|
|
1729
|
-
return;
|
|
1730
|
-
}
|
|
1731
|
-
const timeoutId = setTimeout(resolve, ms);
|
|
1732
|
-
signal?.addEventListener("abort", () => {
|
|
1733
|
-
clearTimeout(timeoutId);
|
|
1734
|
-
reject(new Error("Retry aborted"));
|
|
1735
|
-
});
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
|
-
async function retry(fn, options = {}) {
|
|
1739
|
-
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1740
|
-
let lastError = new Error("No attempts made");
|
|
1741
|
-
for (let attempt = 1;attempt <= opts.maxAttempts; attempt++) {
|
|
1742
|
-
try {
|
|
1743
|
-
if (opts.signal?.aborted) {
|
|
1744
|
-
throw new Error("Retry aborted");
|
|
1745
|
-
}
|
|
1746
|
-
return await fn();
|
|
1747
|
-
} catch (e) {
|
|
1748
|
-
lastError = e instanceof Error ? e : new Error(String(e));
|
|
1749
|
-
if (opts.shouldRetry && !opts.shouldRetry(lastError)) {
|
|
1750
|
-
throw lastError;
|
|
1751
|
-
}
|
|
1752
|
-
if (attempt === opts.maxAttempts) {
|
|
1753
|
-
break;
|
|
1754
|
-
}
|
|
1755
|
-
const delayMs = calculateDelay(attempt, opts);
|
|
1756
|
-
opts.onRetry?.(attempt, lastError, delayMs);
|
|
1757
|
-
await sleep(delayMs, opts.signal);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
throw lastError;
|
|
1761
|
-
}
|
|
1762
|
-
async function retryResult(fn, options = {}) {
|
|
1763
|
-
try {
|
|
1764
|
-
const value = await retry(fn, options);
|
|
1765
|
-
return Ok(value);
|
|
1766
|
-
} catch (e) {
|
|
1767
|
-
return Err(e instanceof Error ? e : new Error(String(e)));
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
function withRetry(fn, options = {}) {
|
|
1771
|
-
return (...args) => retry(() => fn(...args), options);
|
|
1772
|
-
}
|
|
1773
|
-
var RetryConditions = {
|
|
1774
|
-
networkErrors: (error) => {
|
|
1775
|
-
const message = error.message.toLowerCase();
|
|
1776
|
-
return message.includes("network") || message.includes("fetch") || message.includes("timeout") || message.includes("econnrefused") || message.includes("econnreset") || message.includes("socket");
|
|
1777
|
-
},
|
|
1778
|
-
httpRetryable: (error) => {
|
|
1779
|
-
const status = error.status;
|
|
1780
|
-
if (!status)
|
|
1781
|
-
return false;
|
|
1782
|
-
return [408, 429, 500, 502, 503, 504].includes(status);
|
|
1783
|
-
},
|
|
1784
|
-
websocketErrors: (error) => {
|
|
1785
|
-
const message = error.message.toLowerCase();
|
|
1786
|
-
return message.includes("websocket") || message.includes("connection") || message.includes("close");
|
|
1787
|
-
},
|
|
1788
|
-
any: (...conditions) => (error) => conditions.some((c) => c(error)),
|
|
1789
|
-
all: (...conditions) => (error) => conditions.every((c) => c(error))
|
|
1790
|
-
};
|
|
1791
|
-
var RetryPresets = {
|
|
1792
|
-
aggressive: {
|
|
1793
|
-
maxAttempts: 10,
|
|
1794
|
-
delayMs: 500,
|
|
1795
|
-
backoff: "exponential",
|
|
1796
|
-
maxDelayMs: 60000,
|
|
1797
|
-
jitter: 0.2
|
|
1798
|
-
},
|
|
1799
|
-
conservative: {
|
|
1800
|
-
maxAttempts: 3,
|
|
1801
|
-
delayMs: 2000,
|
|
1802
|
-
backoff: "linear",
|
|
1803
|
-
maxDelayMs: 1e4,
|
|
1804
|
-
jitter: 0.1
|
|
1805
|
-
},
|
|
1806
|
-
fast: {
|
|
1807
|
-
maxAttempts: 5,
|
|
1808
|
-
delayMs: 100,
|
|
1809
|
-
backoff: "exponential",
|
|
1810
|
-
maxDelayMs: 2000,
|
|
1811
|
-
jitter: 0
|
|
1812
|
-
},
|
|
1813
|
-
websocket: {
|
|
1814
|
-
maxAttempts: 10,
|
|
1815
|
-
delayMs: 1000,
|
|
1816
|
-
backoff: "exponential",
|
|
1817
|
-
maxDelayMs: 30000,
|
|
1818
|
-
jitter: 0.1,
|
|
1819
|
-
shouldRetry: RetryConditions.websocketErrors
|
|
1820
|
-
}
|
|
1821
|
-
};
|
|
1822
|
-
|
|
1823
|
-
// src/remote/client.ts
|
|
1824
|
-
var log8 = frameworkLoggers.remote;
|
|
1825
|
-
|
|
1826
|
-
class RemoteEngine {
|
|
1827
|
-
ws = null;
|
|
1828
|
-
url;
|
|
1829
|
-
state = "disconnected";
|
|
1830
|
-
options;
|
|
1831
|
-
reconnectAttempts = 0;
|
|
1832
|
-
disposables = new DisposableStack;
|
|
1833
|
-
reconnectDisposable = null;
|
|
1834
|
-
currentSessionId = null;
|
|
1835
|
-
sessionOptions;
|
|
1836
|
-
patchCallbacks = [];
|
|
1837
|
-
stateCallbacks = [];
|
|
1838
|
-
connectionCallbacks = [];
|
|
1839
|
-
disconnectionCallbacks = [];
|
|
1840
|
-
errorCallbacks = [];
|
|
1841
|
-
sessionEstablishedCallbacks = [];
|
|
1842
|
-
sessionExpiredCallbacks = [];
|
|
1843
|
-
currentState = null;
|
|
1844
|
-
currentRevision = 0;
|
|
1845
|
-
moduleName = "";
|
|
1846
|
-
navigationDisposable = null;
|
|
1847
|
-
lastViewValue = null;
|
|
1848
|
-
handlingPopState = false;
|
|
1849
|
-
constructor(url, options = {}) {
|
|
1850
|
-
this.url = url;
|
|
1851
|
-
const navOpt = options.navigation;
|
|
1852
|
-
const navigation = navOpt ? {
|
|
1853
|
-
backAction: (typeof navOpt === "object" ? navOpt.backAction : undefined) ?? "navigateBack",
|
|
1854
|
-
viewStateKey: (typeof navOpt === "object" ? navOpt.viewStateKey : undefined) ?? "currentView"
|
|
1855
|
-
} : undefined;
|
|
1856
|
-
this.options = {
|
|
1857
|
-
autoReconnect: options.autoReconnect ?? true,
|
|
1858
|
-
reconnectInterval: options.reconnectInterval ?? 3000,
|
|
1859
|
-
maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
|
|
1860
|
-
session: options.session,
|
|
1861
|
-
navigation
|
|
1862
|
-
};
|
|
1863
|
-
this.sessionOptions = options.session;
|
|
1864
|
-
if (options.session?.id) {
|
|
1865
|
-
this.currentSessionId = options.session.id;
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
async connect() {
|
|
1869
|
-
if (this.state === "connected" || this.state === "connecting") {
|
|
1870
|
-
return Ok(undefined);
|
|
1871
|
-
}
|
|
1872
|
-
this.state = "connecting";
|
|
1873
|
-
return new Promise((resolve) => {
|
|
1874
|
-
try {
|
|
1875
|
-
this.ws = new WebSocket(this.url);
|
|
1876
|
-
this.disposables.add(disposableWebSocket(this.ws));
|
|
1877
|
-
const messageHandler = (event) => {
|
|
1878
|
-
this.handleMessage(event.data);
|
|
1879
|
-
};
|
|
1880
|
-
this.disposables.add(disposableListener(this.ws, "message", messageHandler));
|
|
1881
|
-
const errorHandler = () => {
|
|
1882
|
-
this.state = "error";
|
|
1883
|
-
const error = new ConnectionError(this.url, new Error("WebSocket error"));
|
|
1884
|
-
this.errorCallbacks.forEach((cb) => cb(error));
|
|
1885
|
-
resolve(Err(error));
|
|
1886
|
-
};
|
|
1887
|
-
this.disposables.add(disposableListener(this.ws, "error", errorHandler));
|
|
1888
|
-
const closeHandler = () => {
|
|
1889
|
-
this.state = "disconnected";
|
|
1890
|
-
this.disconnectionCallbacks.forEach((cb) => cb());
|
|
1891
|
-
this.attemptReconnect();
|
|
1892
|
-
};
|
|
1893
|
-
this.disposables.add(disposableListener(this.ws, "close", closeHandler));
|
|
1894
|
-
this.ws.onopen = () => {
|
|
1895
|
-
this.state = "connected";
|
|
1896
|
-
this.reconnectAttempts = 0;
|
|
1897
|
-
if (this.reconnectDisposable) {
|
|
1898
|
-
this.reconnectDisposable.dispose();
|
|
1899
|
-
this.reconnectDisposable = null;
|
|
1900
|
-
}
|
|
1901
|
-
this.sendHello();
|
|
1902
|
-
this.connectionCallbacks.forEach((cb) => cb());
|
|
1903
|
-
resolve(Ok(undefined));
|
|
1904
|
-
};
|
|
1905
|
-
} catch (e) {
|
|
1906
|
-
this.state = "error";
|
|
1907
|
-
const error = new ConnectionError(this.url, e);
|
|
1908
|
-
resolve(Err(error));
|
|
1909
|
-
}
|
|
1910
|
-
});
|
|
1911
|
-
}
|
|
1912
|
-
sendHello() {
|
|
1913
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
1914
|
-
return;
|
|
1915
|
-
const hello = {
|
|
1916
|
-
type: "hello",
|
|
1917
|
-
sessionId: this.currentSessionId ?? this.sessionOptions?.id,
|
|
1918
|
-
props: this.sessionOptions?.props,
|
|
1919
|
-
persistKey: this.sessionOptions?.persistKey
|
|
1920
|
-
};
|
|
1921
|
-
this.ws.send(JSON.stringify(hello));
|
|
1922
|
-
}
|
|
1923
|
-
disconnect() {
|
|
1924
|
-
if (this.reconnectDisposable) {
|
|
1925
|
-
this.reconnectDisposable.dispose();
|
|
1926
|
-
this.reconnectDisposable = null;
|
|
1927
|
-
}
|
|
1928
|
-
if (this.ws) {
|
|
1929
|
-
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
|
|
1930
|
-
this.ws.close();
|
|
1931
|
-
}
|
|
1932
|
-
this.ws = null;
|
|
1933
|
-
}
|
|
1934
|
-
this.state = "disconnected";
|
|
1935
|
-
}
|
|
1936
|
-
dispose() {
|
|
1937
|
-
this.disconnect();
|
|
1938
|
-
if (this.navigationDisposable) {
|
|
1939
|
-
this.navigationDisposable.dispose();
|
|
1940
|
-
this.navigationDisposable = null;
|
|
1941
|
-
}
|
|
1942
|
-
this.disposables.dispose();
|
|
1943
|
-
}
|
|
1944
|
-
setupNavigation() {
|
|
1945
|
-
const nav = this.options.navigation;
|
|
1946
|
-
if (!nav || typeof window === "undefined")
|
|
1947
|
-
return;
|
|
1948
|
-
if (this.navigationDisposable)
|
|
1949
|
-
return;
|
|
1950
|
-
this.navigationDisposable = disposableListener(window, "popstate", () => {
|
|
1951
|
-
if (this.handlingPopState)
|
|
1952
|
-
return;
|
|
1953
|
-
log8.debug("Back navigation detected, dispatching:", nav.backAction);
|
|
1954
|
-
this.handlingPopState = true;
|
|
1955
|
-
this.dispatchAction(nav.backAction);
|
|
1956
|
-
});
|
|
1957
|
-
}
|
|
1958
|
-
trackViewChange(state) {
|
|
1959
|
-
const nav = this.options.navigation;
|
|
1960
|
-
if (!nav || typeof window === "undefined")
|
|
1961
|
-
return;
|
|
1962
|
-
const viewValue = this.resolveStateKey(state, nav.viewStateKey);
|
|
1963
|
-
if (viewValue === undefined || viewValue === this.lastViewValue)
|
|
1964
|
-
return;
|
|
1965
|
-
if (this.handlingPopState) {
|
|
1966
|
-
this.handlingPopState = false;
|
|
1967
|
-
this.lastViewValue = viewValue;
|
|
1968
|
-
return;
|
|
1969
|
-
}
|
|
1970
|
-
if (this.lastViewValue !== null) {
|
|
1971
|
-
window.history.pushState({ view: viewValue }, "", "");
|
|
1972
|
-
}
|
|
1973
|
-
this.lastViewValue = viewValue;
|
|
1974
|
-
}
|
|
1975
|
-
resolveStateKey(state, key) {
|
|
1976
|
-
if (!state || typeof state !== "object")
|
|
1977
|
-
return;
|
|
1978
|
-
const parts = key.split(".");
|
|
1979
|
-
let current2 = state;
|
|
1980
|
-
for (const part of parts) {
|
|
1981
|
-
if (current2 == null || typeof current2 !== "object")
|
|
1982
|
-
return;
|
|
1983
|
-
current2 = current2[part];
|
|
1984
|
-
}
|
|
1985
|
-
return typeof current2 === "string" ? current2 : undefined;
|
|
1986
|
-
}
|
|
1987
|
-
dispatchAction(action, payload) {
|
|
1988
|
-
if (this.state !== "connected" || !this.ws) {
|
|
1989
|
-
log8.warn("Cannot dispatch action: not connected");
|
|
1990
|
-
return;
|
|
1991
|
-
}
|
|
1992
|
-
const message = {
|
|
1993
|
-
type: "dispatchAction",
|
|
1994
|
-
module: this.moduleName,
|
|
1995
|
-
action,
|
|
1996
|
-
payload
|
|
1997
|
-
};
|
|
1998
|
-
this.ws.send(JSON.stringify(message));
|
|
1999
|
-
}
|
|
2000
|
-
subscribeState() {
|
|
2001
|
-
if (this.state !== "connected" || !this.ws) {
|
|
2002
|
-
log8.warn("Cannot subscribe to state: not connected");
|
|
2003
|
-
return;
|
|
2004
|
-
}
|
|
2005
|
-
const message = { type: "subscribeState" };
|
|
2006
|
-
this.ws.send(JSON.stringify(message));
|
|
2007
|
-
}
|
|
2008
|
-
updateState(state) {
|
|
2009
|
-
if (this.state !== "connected" || !this.ws) {
|
|
2010
|
-
log8.warn("Cannot update state: not connected");
|
|
2011
|
-
return;
|
|
2012
|
-
}
|
|
2013
|
-
const message = {
|
|
2014
|
-
type: "updateState",
|
|
2015
|
-
module: this.moduleName,
|
|
2016
|
-
state
|
|
2017
|
-
};
|
|
2018
|
-
this.ws.send(JSON.stringify(message));
|
|
2019
|
-
}
|
|
2020
|
-
onPatches(callback) {
|
|
2021
|
-
this.patchCallbacks.push(callback);
|
|
2022
|
-
return this;
|
|
2023
|
-
}
|
|
2024
|
-
onStateUpdate(callback) {
|
|
2025
|
-
this.stateCallbacks.push(callback);
|
|
2026
|
-
return this;
|
|
2027
|
-
}
|
|
2028
|
-
onConnect(callback) {
|
|
2029
|
-
this.connectionCallbacks.push(callback);
|
|
2030
|
-
return this;
|
|
2031
|
-
}
|
|
2032
|
-
onDisconnect(callback) {
|
|
2033
|
-
this.disconnectionCallbacks.push(callback);
|
|
2034
|
-
return this;
|
|
2035
|
-
}
|
|
2036
|
-
onError(callback) {
|
|
2037
|
-
this.errorCallbacks.push(callback);
|
|
2038
|
-
return this;
|
|
2039
|
-
}
|
|
2040
|
-
onSessionEstablished(callback) {
|
|
2041
|
-
this.sessionEstablishedCallbacks.push(callback);
|
|
2042
|
-
return this;
|
|
2043
|
-
}
|
|
2044
|
-
onSessionExpired(callback) {
|
|
2045
|
-
this.sessionExpiredCallbacks.push(callback);
|
|
2046
|
-
return this;
|
|
2047
|
-
}
|
|
2048
|
-
getConnectionState() {
|
|
2049
|
-
return this.state;
|
|
2050
|
-
}
|
|
2051
|
-
getCurrentState() {
|
|
2052
|
-
return this.currentState;
|
|
2053
|
-
}
|
|
2054
|
-
getRevision() {
|
|
2055
|
-
return this.currentRevision;
|
|
2056
|
-
}
|
|
2057
|
-
getSessionId() {
|
|
2058
|
-
return this.currentSessionId;
|
|
2059
|
-
}
|
|
2060
|
-
handleMessage(data) {
|
|
2061
|
-
try {
|
|
2062
|
-
const message = JSON.parse(data);
|
|
2063
|
-
switch (message.type) {
|
|
2064
|
-
case "sessionAck":
|
|
2065
|
-
this.handleSessionAck(message);
|
|
2066
|
-
break;
|
|
2067
|
-
case "sessionExpired":
|
|
2068
|
-
this.handleSessionExpired(message);
|
|
2069
|
-
break;
|
|
2070
|
-
case "initialTree":
|
|
2071
|
-
this.handleInitialTree(message);
|
|
2072
|
-
break;
|
|
2073
|
-
case "patch":
|
|
2074
|
-
this.handlePatch(message);
|
|
2075
|
-
break;
|
|
2076
|
-
case "stateUpdate":
|
|
2077
|
-
this.currentState = message.state;
|
|
2078
|
-
this.trackViewChange(this.currentState);
|
|
2079
|
-
this.stateCallbacks.forEach((cb) => cb(this.currentState));
|
|
2080
|
-
break;
|
|
2081
|
-
}
|
|
2082
|
-
} catch (e) {
|
|
2083
|
-
log8.error("Error handling remote message:", e);
|
|
2084
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
2085
|
-
this.errorCallbacks.forEach((cb) => cb(error));
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
handleSessionAck(message) {
|
|
2089
|
-
this.currentSessionId = message.sessionId;
|
|
2090
|
-
const info = {
|
|
2091
|
-
sessionId: message.sessionId,
|
|
2092
|
-
isNew: message.isNew,
|
|
2093
|
-
isRestored: message.isRestored
|
|
2094
|
-
};
|
|
2095
|
-
this.sessionEstablishedCallbacks.forEach((cb) => cb(info));
|
|
2096
|
-
}
|
|
2097
|
-
handleSessionExpired(message) {
|
|
2098
|
-
this.currentSessionId = null;
|
|
2099
|
-
this.sessionExpiredCallbacks.forEach((cb) => cb(message.reason));
|
|
2100
|
-
}
|
|
2101
|
-
handleInitialTree(message) {
|
|
2102
|
-
this.moduleName = message.module;
|
|
2103
|
-
this.currentState = message.state;
|
|
2104
|
-
this.currentRevision = message.revision;
|
|
2105
|
-
if (message.patches.length > 0) {
|
|
2106
|
-
this.patchCallbacks.forEach((cb) => cb(message.patches));
|
|
2107
|
-
}
|
|
2108
|
-
this.trackViewChange(message.state);
|
|
2109
|
-
this.setupNavigation();
|
|
2110
|
-
if (this.options.navigation) {
|
|
2111
|
-
this.subscribeState();
|
|
2112
|
-
}
|
|
2113
|
-
this.stateCallbacks.forEach((cb) => cb(message.state));
|
|
2114
|
-
}
|
|
2115
|
-
handlePatch(message) {
|
|
2116
|
-
if (message.revision <= this.currentRevision) {
|
|
2117
|
-
log8.warn(`Out of order patch: expected > ${this.currentRevision}, got ${message.revision}`);
|
|
2118
|
-
return;
|
|
2119
|
-
}
|
|
2120
|
-
this.currentRevision = message.revision;
|
|
2121
|
-
if (message.patches.length > 0) {
|
|
2122
|
-
this.patchCallbacks.forEach((cb) => cb(message.patches));
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
attemptReconnect() {
|
|
2126
|
-
if (!this.options.autoReconnect) {
|
|
2127
|
-
return;
|
|
2128
|
-
}
|
|
2129
|
-
this.reconnectDisposable = disposableTimeout(() => {
|
|
2130
|
-
this.reconnectDisposable = null;
|
|
2131
|
-
retry(async () => {
|
|
2132
|
-
const result = await this.connect();
|
|
2133
|
-
if (!result.ok) {
|
|
2134
|
-
throw result.error;
|
|
2135
|
-
}
|
|
2136
|
-
}, {
|
|
2137
|
-
maxAttempts: this.options.maxReconnectAttempts,
|
|
2138
|
-
delayMs: this.options.reconnectInterval,
|
|
2139
|
-
backoff: "exponential",
|
|
2140
|
-
maxDelayMs: 30000,
|
|
2141
|
-
jitter: 0.1,
|
|
2142
|
-
onRetry: (attempt, error) => {
|
|
2143
|
-
log8.debug(`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`);
|
|
2144
|
-
}
|
|
2145
|
-
}).catch((error) => {
|
|
2146
|
-
log8.error("Max reconnection attempts reached:", error.message);
|
|
2147
|
-
this.errorCallbacks.forEach((cb) => cb(new ConnectionError(this.url, error, this.options.maxReconnectAttempts)));
|
|
2148
|
-
});
|
|
2149
|
-
}, this.options.reconnectInterval);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
HypenGlobalContext
|
|
3
|
+
} from "./chunk-3zvyth50.js";
|
|
4
|
+
import {
|
|
5
|
+
TypedEventEmitter,
|
|
6
|
+
createEventEmitter
|
|
7
|
+
} from "./chunk-k12cwfst.js";
|
|
8
|
+
import {
|
|
9
|
+
RemoteEngine
|
|
10
|
+
} from "./chunk-g7g3jxty.js";
|
|
11
|
+
import"./chunk-g2gz9pgg.js";
|
|
12
|
+
import {
|
|
13
|
+
BaseRenderer,
|
|
14
|
+
ConsoleRenderer
|
|
15
|
+
} from "./chunk-00zt3w6e.js";
|
|
16
|
+
import {
|
|
17
|
+
HypenRouter
|
|
18
|
+
} from "./chunk-p3pj1bax.js";
|
|
19
|
+
import {
|
|
20
|
+
DisposableStack,
|
|
21
|
+
disposableAbortController,
|
|
22
|
+
disposableInterval,
|
|
23
|
+
disposableListener,
|
|
24
|
+
disposableSubscription,
|
|
25
|
+
disposableTimeout,
|
|
26
|
+
disposableWebSocket,
|
|
27
|
+
getElementDisposables
|
|
28
|
+
} from "./chunk-ec0tgy9a.js";
|
|
29
|
+
import {
|
|
30
|
+
HypenApp,
|
|
31
|
+
HypenAppBuilder,
|
|
32
|
+
HypenModuleInstance,
|
|
33
|
+
app
|
|
34
|
+
} from "./chunk-fnss4dm2.js";
|
|
35
|
+
import"./chunk-vshdk2pw.js";
|
|
36
|
+
import"./chunk-7222131a.js";
|
|
37
|
+
import {
|
|
38
|
+
batchStateUpdates,
|
|
39
|
+
createObservableState,
|
|
40
|
+
getStateSnapshot
|
|
41
|
+
} from "./chunk-ycxp4drd.js";
|
|
42
|
+
import"./chunk-g830re2c.js";
|
|
43
|
+
import {
|
|
44
|
+
Logger,
|
|
45
|
+
configureLogger,
|
|
46
|
+
createLogger,
|
|
47
|
+
disableLogging,
|
|
48
|
+
enableLogging,
|
|
49
|
+
frameworkLoggers,
|
|
50
|
+
getLogLevel,
|
|
51
|
+
isDebugMode,
|
|
52
|
+
log,
|
|
53
|
+
logger,
|
|
54
|
+
setDebugMode,
|
|
55
|
+
setLogLevel
|
|
56
|
+
} from "./chunk-avchpwhf.js";
|
|
57
|
+
import"./chunk-5gtx3pza.js";
|
|
2152
58
|
export {
|
|
2153
59
|
setLogLevel,
|
|
2154
60
|
setDebugMode,
|
|
@@ -2186,4 +92,4 @@ export {
|
|
|
2186
92
|
BaseRenderer
|
|
2187
93
|
};
|
|
2188
94
|
|
|
2189
|
-
//# debugId=
|
|
95
|
+
//# debugId=C69EEEE2CFC1CAE464756E2164756E21
|