@hypen-space/core 0.4.36 → 0.4.38
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 +13 -14
- package/dist/app.js +289 -227
- package/dist/app.js.map +5 -5
- package/dist/components/builtin.js +289 -227
- package/dist/components/builtin.js.map +5 -5
- package/dist/context.js +60 -64
- package/dist/context.js.map +2 -2
- package/dist/datasource.js +80 -0
- package/dist/datasource.js.map +10 -0
- package/dist/disposable.js +60 -63
- package/dist/disposable.js.map +2 -2
- package/dist/events.js +60 -63
- package/dist/events.js.map +2 -2
- package/dist/hypen.js +78 -0
- package/dist/hypen.js.map +10 -0
- package/dist/index.browser.d.ts +2 -1
- package/dist/index.browser.js +303 -244
- package/dist/index.browser.js.map +6 -7
- package/dist/index.d.ts +0 -3
- package/dist/index.js +492 -180
- package/dist/index.js.map +6 -6
- package/dist/logger.js +60 -64
- package/dist/logger.js.map +2 -2
- package/dist/remote/client.js +243 -158
- package/dist/remote/client.js.map +6 -6
- package/dist/remote/index.d.ts +6 -5
- package/dist/remote/index.js +241 -1986
- package/dist/remote/index.js.map +6 -13
- package/dist/remote/session.js +151 -0
- package/dist/remote/session.js.map +10 -0
- package/dist/renderer.js +60 -63
- package/dist/renderer.js.map +2 -2
- package/dist/result.js +220 -0
- package/dist/result.js.map +10 -0
- package/dist/retry.js +329 -0
- package/dist/retry.js.map +11 -0
- package/dist/router.js +62 -70
- package/dist/router.js.map +2 -2
- package/dist/state.js +3 -8
- package/dist/state.js.map +2 -2
- package/package.json +11 -56
- package/src/index.browser.ts +5 -4
- package/src/index.ts +10 -23
- package/src/remote/index.ts +9 -5
- package/dist/discovery.d.ts +0 -90
- package/dist/discovery.js +0 -1334
- package/dist/discovery.js.map +0 -15
- package/dist/engine.browser.d.ts +0 -116
- package/dist/engine.browser.js +0 -479
- package/dist/engine.browser.js.map +0 -12
- package/dist/engine.d.ts +0 -107
- package/dist/engine.js +0 -543
- package/dist/engine.js.map +0 -12
- package/dist/loader.d.ts +0 -51
- package/dist/loader.js +0 -292
- package/dist/loader.js.map +0 -11
- package/dist/plugin.d.ts +0 -39
- package/dist/plugin.js +0 -685
- package/dist/plugin.js.map +0 -12
- package/dist/remote/server.d.ts +0 -188
- package/dist/remote/server.js +0 -2270
- package/dist/remote/server.js.map +0 -19
- package/src/discovery.ts +0 -527
- package/src/engine.browser.ts +0 -302
- package/src/engine.ts +0 -282
- package/src/loader.ts +0 -136
- package/src/plugin.ts +0 -220
- package/src/remote/server.ts +0 -879
- package/wasm-browser/README.md +0 -594
- package/wasm-browser/hypen_engine.d.ts +0 -389
- package/wasm-browser/hypen_engine.js +0 -1070
- package/wasm-browser/hypen_engine_bg.wasm +0 -0
- package/wasm-browser/hypen_engine_bg.wasm.d.ts +0 -37
- package/wasm-browser/package.json +0 -24
- package/wasm-node/README.md +0 -594
- package/wasm-node/hypen_engine.d.ts +0 -327
- package/wasm-node/hypen_engine.js +0 -979
- package/wasm-node/hypen_engine_bg.wasm +0 -0
- package/wasm-node/hypen_engine_bg.wasm.d.ts +0 -37
- package/wasm-node/package.json +0 -22
package/dist/remote/index.js
CHANGED
|
@@ -21,264 +21,205 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
21
21
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
// src/
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
// src/result.ts
|
|
25
|
+
function Ok(value) {
|
|
26
|
+
return { ok: true, value };
|
|
27
|
+
}
|
|
28
|
+
function Err(error) {
|
|
29
|
+
return { ok: false, error };
|
|
30
|
+
}
|
|
31
|
+
function isOk(result) {
|
|
32
|
+
return result.ok;
|
|
33
|
+
}
|
|
34
|
+
function isErr(result) {
|
|
35
|
+
return !result.ok;
|
|
36
|
+
}
|
|
37
|
+
async function fromPromise(promise, mapError) {
|
|
38
|
+
try {
|
|
39
|
+
const value = await promise;
|
|
40
|
+
return Ok(value);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
if (mapError) {
|
|
43
|
+
return Err(mapError(e));
|
|
44
|
+
}
|
|
45
|
+
return Err(e);
|
|
28
46
|
}
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
}
|
|
48
|
+
function fromTry(fn, mapError) {
|
|
49
|
+
try {
|
|
50
|
+
return Ok(fn());
|
|
51
|
+
} catch (e) {
|
|
52
|
+
if (mapError) {
|
|
53
|
+
return Err(mapError(e));
|
|
54
|
+
}
|
|
55
|
+
return Err(e);
|
|
31
56
|
}
|
|
32
|
-
|
|
33
|
-
|
|
57
|
+
}
|
|
58
|
+
function map(result, fn) {
|
|
59
|
+
if (result.ok) {
|
|
60
|
+
return Ok(fn(result.value));
|
|
34
61
|
}
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
function mapErr(result, fn) {
|
|
65
|
+
if (!result.ok) {
|
|
66
|
+
return Err(fn(result.error));
|
|
37
67
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (typeof value === "function") {
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
if (visited.has(value)) {
|
|
47
|
-
return visited.get(value);
|
|
48
|
-
}
|
|
49
|
-
if (value instanceof WeakMap || value instanceof WeakSet) {
|
|
50
|
-
return value;
|
|
51
|
-
}
|
|
52
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
|
|
53
|
-
try {
|
|
54
|
-
return structuredClone(value);
|
|
55
|
-
} catch {}
|
|
56
|
-
}
|
|
57
|
-
if (Array.isArray(value)) {
|
|
58
|
-
const arrClone = [];
|
|
59
|
-
visited.set(value, arrClone);
|
|
60
|
-
for (let i = 0;i < value.length; i++) {
|
|
61
|
-
arrClone[i] = cloneInternal(value[i]);
|
|
62
|
-
}
|
|
63
|
-
return arrClone;
|
|
64
|
-
}
|
|
65
|
-
const objClone = {};
|
|
66
|
-
visited.set(value, objClone);
|
|
67
|
-
for (const key in value) {
|
|
68
|
-
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
69
|
-
objClone[key] = cloneInternal(value[key]);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const symbolKeys = Object.getOwnPropertySymbols(value);
|
|
73
|
-
for (const sym of symbolKeys) {
|
|
74
|
-
objClone[sym] = cloneInternal(value[sym]);
|
|
75
|
-
}
|
|
76
|
-
return objClone;
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function flatMap(result, fn) {
|
|
71
|
+
if (result.ok) {
|
|
72
|
+
return fn(result.value);
|
|
77
73
|
}
|
|
78
|
-
return
|
|
74
|
+
return result;
|
|
79
75
|
}
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
function diff(oldVal, newVal, path) {
|
|
84
|
-
if (oldVal === newVal)
|
|
85
|
-
return;
|
|
86
|
-
if (typeof oldVal !== "object" || typeof newVal !== "object" || oldVal === null || newVal === null) {
|
|
87
|
-
if (oldVal !== newVal) {
|
|
88
|
-
paths.push(path);
|
|
89
|
-
newValues[path] = newVal;
|
|
90
|
-
}
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (Array.isArray(oldVal) || Array.isArray(newVal)) {
|
|
94
|
-
if (!Array.isArray(oldVal) || !Array.isArray(newVal) || oldVal.length !== newVal.length) {
|
|
95
|
-
paths.push(path);
|
|
96
|
-
newValues[path] = newVal;
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
for (let i = 0;i < newVal.length; i++) {
|
|
100
|
-
const itemPath = path ? `${path}.${i}` : `${i}`;
|
|
101
|
-
diff(oldVal[i], newVal[i], itemPath);
|
|
102
|
-
}
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const oldKeys = new Set(Object.keys(oldVal));
|
|
106
|
-
const newKeys = new Set(Object.keys(newVal));
|
|
107
|
-
for (const key of newKeys) {
|
|
108
|
-
const propPath = path ? `${path}.${key}` : key;
|
|
109
|
-
if (!oldKeys.has(key)) {
|
|
110
|
-
paths.push(propPath);
|
|
111
|
-
newValues[propPath] = newVal[key];
|
|
112
|
-
} else {
|
|
113
|
-
diff(oldVal[key], newVal[key], propPath);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
for (const key of oldKeys) {
|
|
117
|
-
if (!newKeys.has(key)) {
|
|
118
|
-
const propPath = path ? `${path}.${key}` : key;
|
|
119
|
-
paths.push(propPath);
|
|
120
|
-
newValues[propPath] = undefined;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
76
|
+
function unwrap(result) {
|
|
77
|
+
if (result.ok) {
|
|
78
|
+
return result.value;
|
|
123
79
|
}
|
|
124
|
-
|
|
125
|
-
return { paths, newValues };
|
|
80
|
+
throw result.error;
|
|
126
81
|
}
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
initialState = {};
|
|
131
|
-
}
|
|
132
|
-
if (initialState instanceof Number || initialState instanceof String || initialState instanceof Boolean) {
|
|
133
|
-
throw new TypeError("Cannot create observable state from primitive wrapper objects (Number, String, Boolean). " + "Use plain primitives or regular objects instead.");
|
|
134
|
-
}
|
|
135
|
-
initialState = deepClone(initialState);
|
|
136
|
-
let lastSnapshot = deepClone(initialState);
|
|
137
|
-
const pathPrefix = opts.pathPrefix || "";
|
|
138
|
-
let batchDepth = 0;
|
|
139
|
-
let pendingChange = null;
|
|
140
|
-
function notifyChange() {
|
|
141
|
-
if (batchDepth > 0)
|
|
142
|
-
return;
|
|
143
|
-
const change = diffState(lastSnapshot, state, pathPrefix);
|
|
144
|
-
if (change.paths.length > 0) {
|
|
145
|
-
lastSnapshot = deepClone(state);
|
|
146
|
-
if (pendingChange) {
|
|
147
|
-
change.paths.push(...pendingChange.paths);
|
|
148
|
-
Object.assign(change.newValues, pendingChange.newValues);
|
|
149
|
-
pendingChange = null;
|
|
150
|
-
}
|
|
151
|
-
opts.onChange(change);
|
|
152
|
-
}
|
|
82
|
+
function unwrapOr(result, defaultValue) {
|
|
83
|
+
if (result.ok) {
|
|
84
|
+
return result.value;
|
|
153
85
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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;
|
|
168
105
|
}
|
|
106
|
+
values.push(result.value);
|
|
169
107
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
if (prop === "__getSnapshot") {
|
|
195
|
-
return () => deepClone(obj);
|
|
196
|
-
}
|
|
197
|
-
const value = obj[prop];
|
|
198
|
-
if (value && typeof value === "object") {
|
|
199
|
-
if (value[IS_PROXY]) {
|
|
200
|
-
return value;
|
|
201
|
-
}
|
|
202
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
|
|
203
|
-
return value;
|
|
204
|
-
}
|
|
205
|
-
const cachedNested = proxyCache.get(value);
|
|
206
|
-
if (cachedNested) {
|
|
207
|
-
return cachedNested;
|
|
208
|
-
}
|
|
209
|
-
const nestedProxy = createProxy(value, basePath ? `${basePath}.${String(prop)}` : String(prop));
|
|
210
|
-
return nestedProxy;
|
|
211
|
-
}
|
|
212
|
-
return value;
|
|
213
|
-
},
|
|
214
|
-
set(obj, prop, value) {
|
|
215
|
-
const oldValue = obj[prop];
|
|
216
|
-
if (value && typeof value === "object" && value[IS_PROXY]) {
|
|
217
|
-
value = value[RAW_TARGET];
|
|
218
|
-
}
|
|
219
|
-
obj[prop] = value;
|
|
220
|
-
if (oldValue !== value) {
|
|
221
|
-
scheduleBatch();
|
|
222
|
-
}
|
|
223
|
-
return true;
|
|
224
|
-
},
|
|
225
|
-
deleteProperty(obj, prop) {
|
|
226
|
-
const existed = Object.prototype.hasOwnProperty.call(obj, prop);
|
|
227
|
-
const result = delete obj[prop];
|
|
228
|
-
if (existed) {
|
|
229
|
-
scheduleBatch();
|
|
230
|
-
}
|
|
231
|
-
return result;
|
|
232
|
-
}
|
|
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
|
|
233
131
|
});
|
|
234
|
-
|
|
235
|
-
|
|
132
|
+
this.name = "ActionError";
|
|
133
|
+
this.actionName = actionName;
|
|
236
134
|
}
|
|
237
|
-
const state = createProxy(initialState, pathPrefix);
|
|
238
|
-
return state;
|
|
239
135
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
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;
|
|
251
148
|
}
|
|
252
149
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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;
|
|
257
160
|
}
|
|
258
|
-
return deepClone(state);
|
|
259
161
|
}
|
|
260
|
-
|
|
261
|
-
|
|
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
|
+
}
|
|
262
173
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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";
|
|
266
181
|
}
|
|
267
|
-
return value;
|
|
268
182
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
183
|
+
function classifyEngineError(err) {
|
|
184
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
185
|
+
if (message.startsWith("Parse error:")) {
|
|
186
|
+
return new ParseError(message);
|
|
187
|
+
}
|
|
188
|
+
if (message.startsWith("Invalid state:") || message.startsWith("Invalid state patch:")) {
|
|
189
|
+
return new StateError(message);
|
|
190
|
+
}
|
|
191
|
+
if (message.startsWith("Parent node not found:")) {
|
|
192
|
+
return new RenderError(message);
|
|
193
|
+
}
|
|
194
|
+
return new RenderError(message);
|
|
195
|
+
}
|
|
274
196
|
|
|
275
197
|
// src/logger.ts
|
|
198
|
+
var LOG_LEVEL_ORDER = {
|
|
199
|
+
debug: 0,
|
|
200
|
+
info: 1,
|
|
201
|
+
warn: 2,
|
|
202
|
+
error: 3,
|
|
203
|
+
none: 4
|
|
204
|
+
};
|
|
205
|
+
var LOG_LEVEL_COLORS = {
|
|
206
|
+
debug: "\x1B[36m",
|
|
207
|
+
info: "\x1B[32m",
|
|
208
|
+
warn: "\x1B[33m",
|
|
209
|
+
error: "\x1B[31m"
|
|
210
|
+
};
|
|
211
|
+
var RESET_COLOR = "\x1B[0m";
|
|
276
212
|
function isProduction() {
|
|
277
213
|
if (typeof process !== "undefined" && process.env) {
|
|
278
214
|
return false;
|
|
279
215
|
}
|
|
280
216
|
return false;
|
|
281
217
|
}
|
|
218
|
+
var config = {
|
|
219
|
+
level: isProduction() ? "error" : "info",
|
|
220
|
+
colors: true,
|
|
221
|
+
timestamps: false
|
|
222
|
+
};
|
|
282
223
|
function setLogLevel(level) {
|
|
283
224
|
config.level = level;
|
|
284
225
|
}
|
|
@@ -399,908 +340,59 @@ class Logger {
|
|
|
399
340
|
this.loggedOnce.add(key);
|
|
400
341
|
this.warn(...args);
|
|
401
342
|
}
|
|
402
|
-
debugOnce(key, ...args) {
|
|
403
|
-
if (this.loggedOnce.has(key))
|
|
404
|
-
return;
|
|
405
|
-
this.loggedOnce.add(key);
|
|
406
|
-
this.debug(...args);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
function createLogger(tag) {
|
|
410
|
-
return new Logger(tag);
|
|
411
|
-
}
|
|
412
|
-
var LOG_LEVEL_ORDER, LOG_LEVEL_COLORS, RESET_COLOR = "\x1B[0m", config, logger, log, frameworkLoggers;
|
|
413
|
-
var init_logger = __esm(() => {
|
|
414
|
-
LOG_LEVEL_ORDER = {
|
|
415
|
-
debug: 0,
|
|
416
|
-
info: 1,
|
|
417
|
-
warn: 2,
|
|
418
|
-
error: 3,
|
|
419
|
-
none: 4
|
|
420
|
-
};
|
|
421
|
-
LOG_LEVEL_COLORS = {
|
|
422
|
-
debug: "\x1B[36m",
|
|
423
|
-
info: "\x1B[32m",
|
|
424
|
-
warn: "\x1B[33m",
|
|
425
|
-
error: "\x1B[31m"
|
|
426
|
-
};
|
|
427
|
-
config = {
|
|
428
|
-
level: isProduction() ? "error" : "info",
|
|
429
|
-
colors: true,
|
|
430
|
-
timestamps: false
|
|
431
|
-
};
|
|
432
|
-
logger = createLogger("Hypen");
|
|
433
|
-
log = {
|
|
434
|
-
debug: (tag, ...args) => {
|
|
435
|
-
if (!shouldLog("debug"))
|
|
436
|
-
return;
|
|
437
|
-
console.log(formatTag(tag, "debug"), ...args);
|
|
438
|
-
},
|
|
439
|
-
info: (tag, ...args) => {
|
|
440
|
-
if (!shouldLog("info"))
|
|
441
|
-
return;
|
|
442
|
-
console.info(formatTag(tag, "info"), ...args);
|
|
443
|
-
},
|
|
444
|
-
warn: (tag, ...args) => {
|
|
445
|
-
if (!shouldLog("warn"))
|
|
446
|
-
return;
|
|
447
|
-
console.warn(formatTag(tag, "warn"), ...args);
|
|
448
|
-
},
|
|
449
|
-
error: (tag, ...args) => {
|
|
450
|
-
if (!shouldLog("error"))
|
|
451
|
-
return;
|
|
452
|
-
console.error(formatTag(tag, "error"), ...args);
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
frameworkLoggers = {
|
|
456
|
-
hypen: createLogger("Hypen"),
|
|
457
|
-
engine: createLogger("Engine"),
|
|
458
|
-
router: createLogger("Router"),
|
|
459
|
-
state: createLogger("State"),
|
|
460
|
-
events: createLogger("Events"),
|
|
461
|
-
remote: createLogger("Remote"),
|
|
462
|
-
renderer: createLogger("Renderer"),
|
|
463
|
-
module: createLogger("Module"),
|
|
464
|
-
lifecycle: createLogger("Lifecycle"),
|
|
465
|
-
loader: createLogger("Loader"),
|
|
466
|
-
context: createLogger("Context"),
|
|
467
|
-
discovery: createLogger("Discovery"),
|
|
468
|
-
plugin: createLogger("Plugin"),
|
|
469
|
-
canvas: createLogger("Canvas"),
|
|
470
|
-
debug: createLogger("Debug")
|
|
471
|
-
};
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// src/result.ts
|
|
475
|
-
function Ok(value) {
|
|
476
|
-
return { ok: true, value };
|
|
477
|
-
}
|
|
478
|
-
function Err(error) {
|
|
479
|
-
return { ok: false, error };
|
|
480
|
-
}
|
|
481
|
-
function classifyEngineError(err) {
|
|
482
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
483
|
-
if (message.startsWith("Parse error:")) {
|
|
484
|
-
return new ParseError(message);
|
|
485
|
-
}
|
|
486
|
-
if (message.startsWith("Invalid state:") || message.startsWith("Invalid state patch:")) {
|
|
487
|
-
return new StateError(message);
|
|
488
|
-
}
|
|
489
|
-
if (message.startsWith("Parent node not found:")) {
|
|
490
|
-
return new RenderError(message);
|
|
491
|
-
}
|
|
492
|
-
return new RenderError(message);
|
|
493
|
-
}
|
|
494
|
-
var HypenError, ActionError, ConnectionError, StateError, ParseError, RenderError;
|
|
495
|
-
var init_result = __esm(() => {
|
|
496
|
-
HypenError = class HypenError extends Error {
|
|
497
|
-
code;
|
|
498
|
-
context;
|
|
499
|
-
cause;
|
|
500
|
-
constructor(code, message, options) {
|
|
501
|
-
super(message);
|
|
502
|
-
this.name = "HypenError";
|
|
503
|
-
this.code = code;
|
|
504
|
-
this.context = options?.context;
|
|
505
|
-
this.cause = options?.cause;
|
|
506
|
-
Object.setPrototypeOf(this, new.target.prototype);
|
|
507
|
-
}
|
|
508
|
-
};
|
|
509
|
-
ActionError = class ActionError extends HypenError {
|
|
510
|
-
actionName;
|
|
511
|
-
constructor(actionName, cause) {
|
|
512
|
-
super("ACTION_ERROR", `Action handler "${actionName}" failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
513
|
-
context: { actionName },
|
|
514
|
-
cause: cause instanceof Error ? cause : undefined
|
|
515
|
-
});
|
|
516
|
-
this.name = "ActionError";
|
|
517
|
-
this.actionName = actionName;
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
ConnectionError = class ConnectionError extends HypenError {
|
|
521
|
-
url;
|
|
522
|
-
attempt;
|
|
523
|
-
constructor(url, cause, attempt) {
|
|
524
|
-
super("CONNECTION_ERROR", `Connection to "${url}" failed${attempt ? ` (attempt ${attempt})` : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
525
|
-
context: { url, attempt },
|
|
526
|
-
cause: cause instanceof Error ? cause : undefined
|
|
527
|
-
});
|
|
528
|
-
this.name = "ConnectionError";
|
|
529
|
-
this.url = url;
|
|
530
|
-
this.attempt = attempt;
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
StateError = class StateError extends HypenError {
|
|
534
|
-
path;
|
|
535
|
-
constructor(message, path, cause) {
|
|
536
|
-
super("STATE_ERROR", message, {
|
|
537
|
-
context: { path },
|
|
538
|
-
cause: cause instanceof Error ? cause : undefined
|
|
539
|
-
});
|
|
540
|
-
this.name = "StateError";
|
|
541
|
-
this.path = path;
|
|
542
|
-
}
|
|
543
|
-
};
|
|
544
|
-
ParseError = class ParseError extends HypenError {
|
|
545
|
-
source;
|
|
546
|
-
constructor(message, source, cause) {
|
|
547
|
-
super("PARSE_ERROR", message, {
|
|
548
|
-
context: { source },
|
|
549
|
-
cause: cause instanceof Error ? cause : undefined
|
|
550
|
-
});
|
|
551
|
-
this.name = "ParseError";
|
|
552
|
-
this.source = source;
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
RenderError = class RenderError extends HypenError {
|
|
556
|
-
constructor(message, cause) {
|
|
557
|
-
super("RENDER_ERROR", message, {
|
|
558
|
-
cause: cause instanceof Error ? cause : undefined
|
|
559
|
-
});
|
|
560
|
-
this.name = "RenderError";
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
// src/datasource.ts
|
|
566
|
-
class DataSourceManager {
|
|
567
|
-
plugins = new Map;
|
|
568
|
-
configs = new Map;
|
|
569
|
-
state = new Map;
|
|
570
|
-
engine;
|
|
571
|
-
constructor(engine) {
|
|
572
|
-
this.engine = engine;
|
|
573
|
-
}
|
|
574
|
-
async use(plugin, config2) {
|
|
575
|
-
const { name } = plugin;
|
|
576
|
-
this.state.set(name, {});
|
|
577
|
-
this.plugins.set(name, plugin);
|
|
578
|
-
this.configs.set(name, config2);
|
|
579
|
-
await plugin.connect(config2, (change) => {
|
|
580
|
-
const current = this.state.get(name) ?? {};
|
|
581
|
-
for (const path of change.paths) {
|
|
582
|
-
if (path in change.values) {
|
|
583
|
-
current[path] = change.values[path];
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
this.state.set(name, current);
|
|
587
|
-
this.engine.setContext(name, current);
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
get(name) {
|
|
591
|
-
return this.plugins.get(name);
|
|
592
|
-
}
|
|
593
|
-
has(name) {
|
|
594
|
-
return this.plugins.has(name);
|
|
595
|
-
}
|
|
596
|
-
getNames() {
|
|
597
|
-
return Array.from(this.plugins.keys());
|
|
598
|
-
}
|
|
599
|
-
getStatus(name) {
|
|
600
|
-
return this.plugins.get(name)?.status;
|
|
601
|
-
}
|
|
602
|
-
async remove(name) {
|
|
603
|
-
const plugin = this.plugins.get(name);
|
|
604
|
-
if (plugin) {
|
|
605
|
-
await plugin.disconnect();
|
|
606
|
-
this.plugins.delete(name);
|
|
607
|
-
this.configs.delete(name);
|
|
608
|
-
this.state.delete(name);
|
|
609
|
-
this.engine.removeContext(name);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
async disconnectAll() {
|
|
613
|
-
const names = Array.from(this.plugins.keys());
|
|
614
|
-
await Promise.all(names.map((name) => this.remove(name)));
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
// src/app.ts
|
|
619
|
-
var exports_app = {};
|
|
620
|
-
__export(exports_app, {
|
|
621
|
-
app: () => app,
|
|
622
|
-
HypenModuleInstance: () => HypenModuleInstance,
|
|
623
|
-
HypenAppBuilder: () => HypenAppBuilder,
|
|
624
|
-
HypenApp: () => HypenApp
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
class HypenAppBuilder {
|
|
628
|
-
initialState;
|
|
629
|
-
options;
|
|
630
|
-
createdHandler;
|
|
631
|
-
actionHandlers = new Map;
|
|
632
|
-
destroyedHandler;
|
|
633
|
-
disconnectHandler;
|
|
634
|
-
reconnectHandler;
|
|
635
|
-
expireHandler;
|
|
636
|
-
errorHandler;
|
|
637
|
-
template;
|
|
638
|
-
_registry;
|
|
639
|
-
dataSourceEntries = [];
|
|
640
|
-
constructor(initialState, options, registry) {
|
|
641
|
-
this.initialState = initialState;
|
|
642
|
-
this.options = options || {};
|
|
643
|
-
this._registry = registry;
|
|
644
|
-
}
|
|
645
|
-
onCreated(fn) {
|
|
646
|
-
this.createdHandler = fn;
|
|
647
|
-
return this;
|
|
648
|
-
}
|
|
649
|
-
onAction(name, fn) {
|
|
650
|
-
this.actionHandlers.set(name, fn);
|
|
651
|
-
return this;
|
|
652
|
-
}
|
|
653
|
-
onDestroyed(fn) {
|
|
654
|
-
this.destroyedHandler = fn;
|
|
655
|
-
return this;
|
|
656
|
-
}
|
|
657
|
-
onDisconnect(fn) {
|
|
658
|
-
this.disconnectHandler = fn;
|
|
659
|
-
return this;
|
|
660
|
-
}
|
|
661
|
-
onReconnect(fn) {
|
|
662
|
-
this.reconnectHandler = fn;
|
|
663
|
-
return this;
|
|
664
|
-
}
|
|
665
|
-
onExpire(fn) {
|
|
666
|
-
this.expireHandler = fn;
|
|
667
|
-
return this;
|
|
668
|
-
}
|
|
669
|
-
onError(fn) {
|
|
670
|
-
this.errorHandler = fn;
|
|
671
|
-
return this;
|
|
672
|
-
}
|
|
673
|
-
useDataSource(plugin, config2) {
|
|
674
|
-
this.dataSourceEntries.push({ plugin, config: config2 });
|
|
675
|
-
return this;
|
|
676
|
-
}
|
|
677
|
-
ui(template) {
|
|
678
|
-
this.template = template;
|
|
679
|
-
return this.build();
|
|
680
|
-
}
|
|
681
|
-
uiFile(path) {
|
|
682
|
-
const fs = (() => ({}));
|
|
683
|
-
this.template = fs.readFileSync(path, "utf-8").trim();
|
|
684
|
-
return this.build();
|
|
685
|
-
}
|
|
686
|
-
build() {
|
|
687
|
-
const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
|
|
688
|
-
const definition = {
|
|
689
|
-
name: this.options.name,
|
|
690
|
-
actions: Array.from(this.actionHandlers.keys()),
|
|
691
|
-
stateKeys,
|
|
692
|
-
persist: this.options.persist,
|
|
693
|
-
version: this.options.version,
|
|
694
|
-
initialState: this.initialState,
|
|
695
|
-
template: this.template,
|
|
696
|
-
dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
|
|
697
|
-
handlers: {
|
|
698
|
-
onCreated: this.createdHandler,
|
|
699
|
-
onAction: this.actionHandlers,
|
|
700
|
-
onDestroyed: this.destroyedHandler,
|
|
701
|
-
onDisconnect: this.disconnectHandler,
|
|
702
|
-
onReconnect: this.reconnectHandler,
|
|
703
|
-
onExpire: this.expireHandler,
|
|
704
|
-
onError: this.errorHandler
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
if (this.options.name && this._registry) {
|
|
708
|
-
this._registry.set(this.options.name, definition);
|
|
709
|
-
}
|
|
710
|
-
return definition;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
class HypenApp {
|
|
715
|
-
_registry = new Map;
|
|
716
|
-
defineState(initial, options) {
|
|
717
|
-
return new HypenAppBuilder(initial, options, this._registry);
|
|
718
|
-
}
|
|
719
|
-
module(name) {
|
|
720
|
-
const registry = this._registry;
|
|
721
|
-
return {
|
|
722
|
-
defineState: (initial, options) => {
|
|
723
|
-
return new HypenAppBuilder(initial, { ...options, name }, registry);
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
get(name) {
|
|
728
|
-
return this._registry.get(name);
|
|
729
|
-
}
|
|
730
|
-
has(name) {
|
|
731
|
-
return this._registry.has(name);
|
|
732
|
-
}
|
|
733
|
-
get components() {
|
|
734
|
-
return this._registry;
|
|
735
|
-
}
|
|
736
|
-
getNames() {
|
|
737
|
-
return Array.from(this._registry.keys());
|
|
738
|
-
}
|
|
739
|
-
get size() {
|
|
740
|
-
return this._registry.size;
|
|
741
|
-
}
|
|
742
|
-
unregister(name) {
|
|
743
|
-
this._registry.delete(name);
|
|
744
|
-
}
|
|
745
|
-
clear() {
|
|
746
|
-
this._registry.clear();
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
class HypenModuleInstance {
|
|
751
|
-
engine;
|
|
752
|
-
definition;
|
|
753
|
-
state;
|
|
754
|
-
isDestroyed = false;
|
|
755
|
-
router;
|
|
756
|
-
globalContext;
|
|
757
|
-
stateChangeCallbacks = [];
|
|
758
|
-
dataSourceManager;
|
|
759
|
-
dataSourceAccessor = {};
|
|
760
|
-
constructor(engine, definition, router, globalContext) {
|
|
761
|
-
this.engine = engine;
|
|
762
|
-
this.definition = definition;
|
|
763
|
-
this.router = router ?? null;
|
|
764
|
-
this.globalContext = globalContext;
|
|
765
|
-
const statePrefix = (definition.name || "").toLowerCase();
|
|
766
|
-
this.state = createObservableState(definition.initialState, {
|
|
767
|
-
onChange: (change) => {
|
|
768
|
-
this.engine.notifyStateChange(change.paths, change.newValues);
|
|
769
|
-
this.stateChangeCallbacks.forEach((cb) => cb());
|
|
770
|
-
},
|
|
771
|
-
pathPrefix: statePrefix || undefined
|
|
772
|
-
});
|
|
773
|
-
const snapshot = getStateSnapshot(this.state);
|
|
774
|
-
const prefixedState = statePrefix ? { [statePrefix]: snapshot } : snapshot;
|
|
775
|
-
this.engine.setModule(definition.name || "AnonymousModule", definition.actions, definition.stateKeys, prefixedState);
|
|
776
|
-
for (const [actionName, handler] of definition.handlers.onAction) {
|
|
777
|
-
log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
|
|
778
|
-
this.engine.onAction(actionName, async (action) => {
|
|
779
|
-
log2.debug(`Action handler fired: ${actionName}`, action);
|
|
780
|
-
const actionCtx = {
|
|
781
|
-
name: action.name,
|
|
782
|
-
payload: action.payload,
|
|
783
|
-
sender: action.sender
|
|
784
|
-
};
|
|
785
|
-
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
786
|
-
const result = await this.executeAction(actionName, handler, {
|
|
787
|
-
action: actionCtx,
|
|
788
|
-
state: this.state,
|
|
789
|
-
context,
|
|
790
|
-
dataSources: this.dataSourceAccessor
|
|
791
|
-
});
|
|
792
|
-
if (!result.ok) {
|
|
793
|
-
const shouldRethrow = await this.handleError(result.error, { actionName });
|
|
794
|
-
if (shouldRethrow) {
|
|
795
|
-
throw result.error;
|
|
796
|
-
}
|
|
797
|
-
} else {
|
|
798
|
-
log2.debug(`Action handler completed: ${actionName}`);
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
this.engine.onAction("__hypen_bind", (action) => {
|
|
803
|
-
const payload = action.payload;
|
|
804
|
-
if (!payload?.path)
|
|
805
|
-
return;
|
|
806
|
-
const segments = payload.path.split(".");
|
|
807
|
-
let target = this.state;
|
|
808
|
-
for (let i = 0;i < segments.length - 1; i++) {
|
|
809
|
-
const seg = segments[i];
|
|
810
|
-
target = target?.[seg];
|
|
811
|
-
if (target == null)
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
const lastSeg = segments[segments.length - 1];
|
|
815
|
-
target[lastSeg] = payload.value;
|
|
816
|
-
});
|
|
817
|
-
this.callCreatedHandler();
|
|
818
|
-
}
|
|
819
|
-
createGlobalContextAPI() {
|
|
820
|
-
if (!this.globalContext) {
|
|
821
|
-
throw new Error("Global context not available");
|
|
822
|
-
}
|
|
823
|
-
const ctx = this.globalContext;
|
|
824
|
-
const api = {
|
|
825
|
-
getModule: (id) => ctx.getModule(id),
|
|
826
|
-
hasModule: (id) => ctx.hasModule(id),
|
|
827
|
-
getModuleIds: () => ctx.getModuleIds(),
|
|
828
|
-
getGlobalState: () => ctx.getGlobalState(),
|
|
829
|
-
emit: (event, payload) => ctx.emit(event, payload),
|
|
830
|
-
on: (event, handler) => ctx.on(event, handler),
|
|
831
|
-
router: this.router
|
|
832
|
-
};
|
|
833
|
-
const ctxRecord = ctx;
|
|
834
|
-
if (ctxRecord.__hypenEngine) {
|
|
835
|
-
api.__hypenEngine = ctxRecord.__hypenEngine;
|
|
836
|
-
}
|
|
837
|
-
return api;
|
|
838
|
-
}
|
|
839
|
-
async executeAction(actionName, handler, ctx) {
|
|
840
|
-
try {
|
|
841
|
-
const result = handler(ctx);
|
|
842
|
-
await result;
|
|
843
|
-
return Ok(undefined);
|
|
844
|
-
} catch (e) {
|
|
845
|
-
return Err(new ActionError(actionName, e));
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
async handleError(error, context) {
|
|
849
|
-
const errorCtx = {
|
|
850
|
-
error,
|
|
851
|
-
state: this.state,
|
|
852
|
-
actionName: context.actionName,
|
|
853
|
-
lifecycle: context.lifecycle
|
|
854
|
-
};
|
|
855
|
-
if (this.definition.handlers.onError) {
|
|
856
|
-
try {
|
|
857
|
-
const result = await this.definition.handlers.onError(errorCtx);
|
|
858
|
-
if (result && typeof result === "object") {
|
|
859
|
-
if ("handled" in result && result.handled) {
|
|
860
|
-
return false;
|
|
861
|
-
}
|
|
862
|
-
if ("rethrow" in result && result.rethrow) {
|
|
863
|
-
return true;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
} catch (handlerError) {
|
|
867
|
-
log2.error("Error in onError handler:", handlerError);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
if (this.globalContext) {
|
|
871
|
-
const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
|
|
872
|
-
this.globalContext.emit("error", {
|
|
873
|
-
message: error.message,
|
|
874
|
-
error,
|
|
875
|
-
context: eventContext
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
|
|
879
|
-
return false;
|
|
880
|
-
}
|
|
881
|
-
async callCreatedHandler() {
|
|
882
|
-
if (this.definition.dataSources?.length) {
|
|
883
|
-
const dsEngine = this.engine;
|
|
884
|
-
this.dataSourceManager = new DataSourceManager(dsEngine);
|
|
885
|
-
for (const { plugin, config: config2 } of this.definition.dataSources) {
|
|
886
|
-
try {
|
|
887
|
-
await this.dataSourceManager.use(plugin, config2);
|
|
888
|
-
this.dataSourceAccessor[plugin.name] = new Proxy(plugin, {
|
|
889
|
-
get(target, prop) {
|
|
890
|
-
if (typeof prop === "string" && !(prop in target)) {
|
|
891
|
-
return (...args) => target.call(prop, ...args);
|
|
892
|
-
}
|
|
893
|
-
return target[prop];
|
|
894
|
-
}
|
|
895
|
-
});
|
|
896
|
-
} catch (e) {
|
|
897
|
-
log2.error(`Failed to connect data source "${plugin.name}":`, e);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
if (this.definition.handlers.onCreated) {
|
|
902
|
-
const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
|
|
903
|
-
try {
|
|
904
|
-
await this.definition.handlers.onCreated(this.state, context);
|
|
905
|
-
} catch (e) {
|
|
906
|
-
const error = e instanceof HypenError ? e : new ActionError("onCreated", e);
|
|
907
|
-
const shouldRethrow = await this.handleError(error, { lifecycle: "created" });
|
|
908
|
-
if (shouldRethrow) {
|
|
909
|
-
throw error;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
onStateChange(callback) {
|
|
915
|
-
this.stateChangeCallbacks.push(callback);
|
|
916
|
-
}
|
|
917
|
-
async destroy() {
|
|
918
|
-
if (this.isDestroyed)
|
|
919
|
-
return;
|
|
920
|
-
if (this.dataSourceManager) {
|
|
921
|
-
try {
|
|
922
|
-
await this.dataSourceManager.disconnectAll();
|
|
923
|
-
} catch (e) {
|
|
924
|
-
log2.error("Error disconnecting data sources:", e);
|
|
925
|
-
}
|
|
926
|
-
this.dataSourceManager = undefined;
|
|
927
|
-
this.dataSourceAccessor = {};
|
|
928
|
-
}
|
|
929
|
-
if (this.definition.handlers.onDestroyed) {
|
|
930
|
-
try {
|
|
931
|
-
await this.definition.handlers.onDestroyed(this.state);
|
|
932
|
-
} catch (e) {
|
|
933
|
-
const error = e instanceof HypenError ? e : new ActionError("onDestroyed", e);
|
|
934
|
-
const shouldRethrow = await this.handleError(error, { lifecycle: "destroyed" });
|
|
935
|
-
if (shouldRethrow) {
|
|
936
|
-
throw error;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
this.isDestroyed = true;
|
|
941
|
-
}
|
|
942
|
-
getState() {
|
|
943
|
-
return getStateSnapshot(this.state);
|
|
944
|
-
}
|
|
945
|
-
getLiveState() {
|
|
946
|
-
return this.state;
|
|
947
|
-
}
|
|
948
|
-
updateState(patch) {
|
|
949
|
-
Object.assign(this.state, patch);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
var log2, app;
|
|
953
|
-
var init_app = __esm(() => {
|
|
954
|
-
init_result();
|
|
955
|
-
init_state();
|
|
956
|
-
init_logger();
|
|
957
|
-
log2 = createLogger("ModuleInstance");
|
|
958
|
-
app = new HypenApp;
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
// node:path
|
|
962
|
-
var exports_path = {};
|
|
963
|
-
__export(exports_path, {
|
|
964
|
-
sep: () => sep,
|
|
965
|
-
resolve: () => resolve,
|
|
966
|
-
relative: () => relative,
|
|
967
|
-
posix: () => posix,
|
|
968
|
-
parse: () => parse,
|
|
969
|
-
normalize: () => normalize,
|
|
970
|
-
join: () => join,
|
|
971
|
-
isAbsolute: () => isAbsolute,
|
|
972
|
-
format: () => format,
|
|
973
|
-
extname: () => extname,
|
|
974
|
-
dirname: () => dirname,
|
|
975
|
-
delimiter: () => delimiter,
|
|
976
|
-
default: () => path_default,
|
|
977
|
-
basename: () => basename,
|
|
978
|
-
_makeLong: () => _makeLong
|
|
979
|
-
});
|
|
980
|
-
function assertPath(path) {
|
|
981
|
-
if (typeof path !== "string")
|
|
982
|
-
throw TypeError("Path must be a string. Received " + JSON.stringify(path));
|
|
983
|
-
}
|
|
984
|
-
function normalizeStringPosix(path, allowAboveRoot) {
|
|
985
|
-
var res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, code;
|
|
986
|
-
for (var i = 0;i <= path.length; ++i) {
|
|
987
|
-
if (i < path.length)
|
|
988
|
-
code = path.charCodeAt(i);
|
|
989
|
-
else if (code === 47)
|
|
990
|
-
break;
|
|
991
|
-
else
|
|
992
|
-
code = 47;
|
|
993
|
-
if (code === 47) {
|
|
994
|
-
if (lastSlash === i - 1 || dots === 1)
|
|
995
|
-
;
|
|
996
|
-
else if (lastSlash !== i - 1 && dots === 2) {
|
|
997
|
-
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
|
|
998
|
-
if (res.length > 2) {
|
|
999
|
-
var lastSlashIndex = res.lastIndexOf("/");
|
|
1000
|
-
if (lastSlashIndex !== res.length - 1) {
|
|
1001
|
-
if (lastSlashIndex === -1)
|
|
1002
|
-
res = "", lastSegmentLength = 0;
|
|
1003
|
-
else
|
|
1004
|
-
res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
1005
|
-
lastSlash = i, dots = 0;
|
|
1006
|
-
continue;
|
|
1007
|
-
}
|
|
1008
|
-
} else if (res.length === 2 || res.length === 1) {
|
|
1009
|
-
res = "", lastSegmentLength = 0, lastSlash = i, dots = 0;
|
|
1010
|
-
continue;
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
if (allowAboveRoot) {
|
|
1014
|
-
if (res.length > 0)
|
|
1015
|
-
res += "/..";
|
|
1016
|
-
else
|
|
1017
|
-
res = "..";
|
|
1018
|
-
lastSegmentLength = 2;
|
|
1019
|
-
}
|
|
1020
|
-
} else {
|
|
1021
|
-
if (res.length > 0)
|
|
1022
|
-
res += "/" + path.slice(lastSlash + 1, i);
|
|
1023
|
-
else
|
|
1024
|
-
res = path.slice(lastSlash + 1, i);
|
|
1025
|
-
lastSegmentLength = i - lastSlash - 1;
|
|
1026
|
-
}
|
|
1027
|
-
lastSlash = i, dots = 0;
|
|
1028
|
-
} else if (code === 46 && dots !== -1)
|
|
1029
|
-
++dots;
|
|
1030
|
-
else
|
|
1031
|
-
dots = -1;
|
|
1032
|
-
}
|
|
1033
|
-
return res;
|
|
1034
|
-
}
|
|
1035
|
-
function _format(sep, pathObject) {
|
|
1036
|
-
var dir = pathObject.dir || pathObject.root, base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
|
|
1037
|
-
if (!dir)
|
|
1038
|
-
return base;
|
|
1039
|
-
if (dir === pathObject.root)
|
|
1040
|
-
return dir + base;
|
|
1041
|
-
return dir + sep + base;
|
|
1042
|
-
}
|
|
1043
|
-
function resolve() {
|
|
1044
|
-
var resolvedPath = "", resolvedAbsolute = false, cwd;
|
|
1045
|
-
for (var i = arguments.length - 1;i >= -1 && !resolvedAbsolute; i--) {
|
|
1046
|
-
var path;
|
|
1047
|
-
if (i >= 0)
|
|
1048
|
-
path = arguments[i];
|
|
1049
|
-
else {
|
|
1050
|
-
if (cwd === undefined)
|
|
1051
|
-
cwd = process.cwd();
|
|
1052
|
-
path = cwd;
|
|
1053
|
-
}
|
|
1054
|
-
if (assertPath(path), path.length === 0)
|
|
1055
|
-
continue;
|
|
1056
|
-
resolvedPath = path + "/" + resolvedPath, resolvedAbsolute = path.charCodeAt(0) === 47;
|
|
1057
|
-
}
|
|
1058
|
-
if (resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute), resolvedAbsolute)
|
|
1059
|
-
if (resolvedPath.length > 0)
|
|
1060
|
-
return "/" + resolvedPath;
|
|
1061
|
-
else
|
|
1062
|
-
return "/";
|
|
1063
|
-
else if (resolvedPath.length > 0)
|
|
1064
|
-
return resolvedPath;
|
|
1065
|
-
else
|
|
1066
|
-
return ".";
|
|
1067
|
-
}
|
|
1068
|
-
function normalize(path) {
|
|
1069
|
-
if (assertPath(path), path.length === 0)
|
|
1070
|
-
return ".";
|
|
1071
|
-
var isAbsolute = path.charCodeAt(0) === 47, trailingSeparator = path.charCodeAt(path.length - 1) === 47;
|
|
1072
|
-
if (path = normalizeStringPosix(path, !isAbsolute), path.length === 0 && !isAbsolute)
|
|
1073
|
-
path = ".";
|
|
1074
|
-
if (path.length > 0 && trailingSeparator)
|
|
1075
|
-
path += "/";
|
|
1076
|
-
if (isAbsolute)
|
|
1077
|
-
return "/" + path;
|
|
1078
|
-
return path;
|
|
1079
|
-
}
|
|
1080
|
-
function isAbsolute(path) {
|
|
1081
|
-
return assertPath(path), path.length > 0 && path.charCodeAt(0) === 47;
|
|
1082
|
-
}
|
|
1083
|
-
function join() {
|
|
1084
|
-
if (arguments.length === 0)
|
|
1085
|
-
return ".";
|
|
1086
|
-
var joined;
|
|
1087
|
-
for (var i = 0;i < arguments.length; ++i) {
|
|
1088
|
-
var arg = arguments[i];
|
|
1089
|
-
if (assertPath(arg), arg.length > 0)
|
|
1090
|
-
if (joined === undefined)
|
|
1091
|
-
joined = arg;
|
|
1092
|
-
else
|
|
1093
|
-
joined += "/" + arg;
|
|
1094
|
-
}
|
|
1095
|
-
if (joined === undefined)
|
|
1096
|
-
return ".";
|
|
1097
|
-
return normalize(joined);
|
|
1098
|
-
}
|
|
1099
|
-
function relative(from, to) {
|
|
1100
|
-
if (assertPath(from), assertPath(to), from === to)
|
|
1101
|
-
return "";
|
|
1102
|
-
if (from = resolve(from), to = resolve(to), from === to)
|
|
1103
|
-
return "";
|
|
1104
|
-
var fromStart = 1;
|
|
1105
|
-
for (;fromStart < from.length; ++fromStart)
|
|
1106
|
-
if (from.charCodeAt(fromStart) !== 47)
|
|
1107
|
-
break;
|
|
1108
|
-
var fromEnd = from.length, fromLen = fromEnd - fromStart, toStart = 1;
|
|
1109
|
-
for (;toStart < to.length; ++toStart)
|
|
1110
|
-
if (to.charCodeAt(toStart) !== 47)
|
|
1111
|
-
break;
|
|
1112
|
-
var toEnd = to.length, toLen = toEnd - toStart, length = fromLen < toLen ? fromLen : toLen, lastCommonSep = -1, i = 0;
|
|
1113
|
-
for (;i <= length; ++i) {
|
|
1114
|
-
if (i === length) {
|
|
1115
|
-
if (toLen > length) {
|
|
1116
|
-
if (to.charCodeAt(toStart + i) === 47)
|
|
1117
|
-
return to.slice(toStart + i + 1);
|
|
1118
|
-
else if (i === 0)
|
|
1119
|
-
return to.slice(toStart + i);
|
|
1120
|
-
} else if (fromLen > length) {
|
|
1121
|
-
if (from.charCodeAt(fromStart + i) === 47)
|
|
1122
|
-
lastCommonSep = i;
|
|
1123
|
-
else if (i === 0)
|
|
1124
|
-
lastCommonSep = 0;
|
|
1125
|
-
}
|
|
1126
|
-
break;
|
|
1127
|
-
}
|
|
1128
|
-
var fromCode = from.charCodeAt(fromStart + i), toCode = to.charCodeAt(toStart + i);
|
|
1129
|
-
if (fromCode !== toCode)
|
|
1130
|
-
break;
|
|
1131
|
-
else if (fromCode === 47)
|
|
1132
|
-
lastCommonSep = i;
|
|
1133
|
-
}
|
|
1134
|
-
var out = "";
|
|
1135
|
-
for (i = fromStart + lastCommonSep + 1;i <= fromEnd; ++i)
|
|
1136
|
-
if (i === fromEnd || from.charCodeAt(i) === 47)
|
|
1137
|
-
if (out.length === 0)
|
|
1138
|
-
out += "..";
|
|
1139
|
-
else
|
|
1140
|
-
out += "/..";
|
|
1141
|
-
if (out.length > 0)
|
|
1142
|
-
return out + to.slice(toStart + lastCommonSep);
|
|
1143
|
-
else {
|
|
1144
|
-
if (toStart += lastCommonSep, to.charCodeAt(toStart) === 47)
|
|
1145
|
-
++toStart;
|
|
1146
|
-
return to.slice(toStart);
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
function _makeLong(path) {
|
|
1150
|
-
return path;
|
|
1151
|
-
}
|
|
1152
|
-
function dirname(path) {
|
|
1153
|
-
if (assertPath(path), path.length === 0)
|
|
1154
|
-
return ".";
|
|
1155
|
-
var code = path.charCodeAt(0), hasRoot = code === 47, end = -1, matchedSlash = true;
|
|
1156
|
-
for (var i = path.length - 1;i >= 1; --i)
|
|
1157
|
-
if (code = path.charCodeAt(i), code === 47) {
|
|
1158
|
-
if (!matchedSlash) {
|
|
1159
|
-
end = i;
|
|
1160
|
-
break;
|
|
1161
|
-
}
|
|
1162
|
-
} else
|
|
1163
|
-
matchedSlash = false;
|
|
1164
|
-
if (end === -1)
|
|
1165
|
-
return hasRoot ? "/" : ".";
|
|
1166
|
-
if (hasRoot && end === 1)
|
|
1167
|
-
return "//";
|
|
1168
|
-
return path.slice(0, end);
|
|
1169
|
-
}
|
|
1170
|
-
function basename(path, ext) {
|
|
1171
|
-
if (ext !== undefined && typeof ext !== "string")
|
|
1172
|
-
throw TypeError('"ext" argument must be a string');
|
|
1173
|
-
assertPath(path);
|
|
1174
|
-
var start = 0, end = -1, matchedSlash = true, i;
|
|
1175
|
-
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
|
|
1176
|
-
if (ext.length === path.length && ext === path)
|
|
1177
|
-
return "";
|
|
1178
|
-
var extIdx = ext.length - 1, firstNonSlashEnd = -1;
|
|
1179
|
-
for (i = path.length - 1;i >= 0; --i) {
|
|
1180
|
-
var code = path.charCodeAt(i);
|
|
1181
|
-
if (code === 47) {
|
|
1182
|
-
if (!matchedSlash) {
|
|
1183
|
-
start = i + 1;
|
|
1184
|
-
break;
|
|
1185
|
-
}
|
|
1186
|
-
} else {
|
|
1187
|
-
if (firstNonSlashEnd === -1)
|
|
1188
|
-
matchedSlash = false, firstNonSlashEnd = i + 1;
|
|
1189
|
-
if (extIdx >= 0)
|
|
1190
|
-
if (code === ext.charCodeAt(extIdx)) {
|
|
1191
|
-
if (--extIdx === -1)
|
|
1192
|
-
end = i;
|
|
1193
|
-
} else
|
|
1194
|
-
extIdx = -1, end = firstNonSlashEnd;
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
if (start === end)
|
|
1198
|
-
end = firstNonSlashEnd;
|
|
1199
|
-
else if (end === -1)
|
|
1200
|
-
end = path.length;
|
|
1201
|
-
return path.slice(start, end);
|
|
1202
|
-
} else {
|
|
1203
|
-
for (i = path.length - 1;i >= 0; --i)
|
|
1204
|
-
if (path.charCodeAt(i) === 47) {
|
|
1205
|
-
if (!matchedSlash) {
|
|
1206
|
-
start = i + 1;
|
|
1207
|
-
break;
|
|
1208
|
-
}
|
|
1209
|
-
} else if (end === -1)
|
|
1210
|
-
matchedSlash = false, end = i + 1;
|
|
1211
|
-
if (end === -1)
|
|
1212
|
-
return "";
|
|
1213
|
-
return path.slice(start, end);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
function extname(path) {
|
|
1217
|
-
assertPath(path);
|
|
1218
|
-
var startDot = -1, startPart = 0, end = -1, matchedSlash = true, preDotState = 0;
|
|
1219
|
-
for (var i = path.length - 1;i >= 0; --i) {
|
|
1220
|
-
var code = path.charCodeAt(i);
|
|
1221
|
-
if (code === 47) {
|
|
1222
|
-
if (!matchedSlash) {
|
|
1223
|
-
startPart = i + 1;
|
|
1224
|
-
break;
|
|
1225
|
-
}
|
|
1226
|
-
continue;
|
|
1227
|
-
}
|
|
1228
|
-
if (end === -1)
|
|
1229
|
-
matchedSlash = false, end = i + 1;
|
|
1230
|
-
if (code === 46) {
|
|
1231
|
-
if (startDot === -1)
|
|
1232
|
-
startDot = i;
|
|
1233
|
-
else if (preDotState !== 1)
|
|
1234
|
-
preDotState = 1;
|
|
1235
|
-
} else if (startDot !== -1)
|
|
1236
|
-
preDotState = -1;
|
|
1237
|
-
}
|
|
1238
|
-
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
1239
|
-
return "";
|
|
1240
|
-
return path.slice(startDot, end);
|
|
1241
|
-
}
|
|
1242
|
-
function format(pathObject) {
|
|
1243
|
-
if (pathObject === null || typeof pathObject !== "object")
|
|
1244
|
-
throw TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
|
|
1245
|
-
return _format("/", pathObject);
|
|
343
|
+
debugOnce(key, ...args) {
|
|
344
|
+
if (this.loggedOnce.has(key))
|
|
345
|
+
return;
|
|
346
|
+
this.loggedOnce.add(key);
|
|
347
|
+
this.debug(...args);
|
|
348
|
+
}
|
|
1246
349
|
}
|
|
1247
|
-
function
|
|
1248
|
-
|
|
1249
|
-
var ret = { root: "", dir: "", base: "", ext: "", name: "" };
|
|
1250
|
-
if (path.length === 0)
|
|
1251
|
-
return ret;
|
|
1252
|
-
var code = path.charCodeAt(0), isAbsolute2 = code === 47, start;
|
|
1253
|
-
if (isAbsolute2)
|
|
1254
|
-
ret.root = "/", start = 1;
|
|
1255
|
-
else
|
|
1256
|
-
start = 0;
|
|
1257
|
-
var startDot = -1, startPart = 0, end = -1, matchedSlash = true, i = path.length - 1, preDotState = 0;
|
|
1258
|
-
for (;i >= start; --i) {
|
|
1259
|
-
if (code = path.charCodeAt(i), code === 47) {
|
|
1260
|
-
if (!matchedSlash) {
|
|
1261
|
-
startPart = i + 1;
|
|
1262
|
-
break;
|
|
1263
|
-
}
|
|
1264
|
-
continue;
|
|
1265
|
-
}
|
|
1266
|
-
if (end === -1)
|
|
1267
|
-
matchedSlash = false, end = i + 1;
|
|
1268
|
-
if (code === 46) {
|
|
1269
|
-
if (startDot === -1)
|
|
1270
|
-
startDot = i;
|
|
1271
|
-
else if (preDotState !== 1)
|
|
1272
|
-
preDotState = 1;
|
|
1273
|
-
} else if (startDot !== -1)
|
|
1274
|
-
preDotState = -1;
|
|
1275
|
-
}
|
|
1276
|
-
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
1277
|
-
if (end !== -1)
|
|
1278
|
-
if (startPart === 0 && isAbsolute2)
|
|
1279
|
-
ret.base = ret.name = path.slice(1, end);
|
|
1280
|
-
else
|
|
1281
|
-
ret.base = ret.name = path.slice(startPart, end);
|
|
1282
|
-
} else {
|
|
1283
|
-
if (startPart === 0 && isAbsolute2)
|
|
1284
|
-
ret.name = path.slice(1, startDot), ret.base = path.slice(1, end);
|
|
1285
|
-
else
|
|
1286
|
-
ret.name = path.slice(startPart, startDot), ret.base = path.slice(startPart, end);
|
|
1287
|
-
ret.ext = path.slice(startDot, end);
|
|
1288
|
-
}
|
|
1289
|
-
if (startPart > 0)
|
|
1290
|
-
ret.dir = path.slice(0, startPart - 1);
|
|
1291
|
-
else if (isAbsolute2)
|
|
1292
|
-
ret.dir = "/";
|
|
1293
|
-
return ret;
|
|
350
|
+
function createLogger(tag) {
|
|
351
|
+
return new Logger(tag);
|
|
1294
352
|
}
|
|
1295
|
-
var
|
|
1296
|
-
var
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
353
|
+
var logger = createLogger("Hypen");
|
|
354
|
+
var log = {
|
|
355
|
+
debug: (tag, ...args) => {
|
|
356
|
+
if (!shouldLog("debug"))
|
|
357
|
+
return;
|
|
358
|
+
console.log(formatTag(tag, "debug"), ...args);
|
|
359
|
+
},
|
|
360
|
+
info: (tag, ...args) => {
|
|
361
|
+
if (!shouldLog("info"))
|
|
362
|
+
return;
|
|
363
|
+
console.info(formatTag(tag, "info"), ...args);
|
|
364
|
+
},
|
|
365
|
+
warn: (tag, ...args) => {
|
|
366
|
+
if (!shouldLog("warn"))
|
|
367
|
+
return;
|
|
368
|
+
console.warn(formatTag(tag, "warn"), ...args);
|
|
369
|
+
},
|
|
370
|
+
error: (tag, ...args) => {
|
|
371
|
+
if (!shouldLog("error"))
|
|
372
|
+
return;
|
|
373
|
+
console.error(formatTag(tag, "error"), ...args);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
var frameworkLoggers = {
|
|
377
|
+
hypen: createLogger("Hypen"),
|
|
378
|
+
engine: createLogger("Engine"),
|
|
379
|
+
router: createLogger("Router"),
|
|
380
|
+
state: createLogger("State"),
|
|
381
|
+
events: createLogger("Events"),
|
|
382
|
+
remote: createLogger("Remote"),
|
|
383
|
+
renderer: createLogger("Renderer"),
|
|
384
|
+
module: createLogger("Module"),
|
|
385
|
+
lifecycle: createLogger("Lifecycle"),
|
|
386
|
+
loader: createLogger("Loader"),
|
|
387
|
+
context: createLogger("Context"),
|
|
388
|
+
discovery: createLogger("Discovery"),
|
|
389
|
+
plugin: createLogger("Plugin"),
|
|
390
|
+
canvas: createLogger("Canvas"),
|
|
391
|
+
debug: createLogger("Debug")
|
|
392
|
+
};
|
|
1300
393
|
|
|
1301
394
|
// src/disposable.ts
|
|
1302
|
-
|
|
1303
|
-
var log3 = frameworkLoggers.lifecycle;
|
|
395
|
+
var log2 = frameworkLoggers.lifecycle;
|
|
1304
396
|
function isDisposable(obj) {
|
|
1305
397
|
return obj !== null && typeof obj === "object" && "dispose" in obj && typeof obj.dispose === "function";
|
|
1306
398
|
}
|
|
@@ -1332,7 +424,7 @@ class DisposableStack {
|
|
|
1332
424
|
try {
|
|
1333
425
|
item.dispose();
|
|
1334
426
|
} catch (error) {
|
|
1335
|
-
|
|
427
|
+
log2.error("Error during dispose:", error);
|
|
1336
428
|
}
|
|
1337
429
|
}
|
|
1338
430
|
}
|
|
@@ -1423,7 +515,7 @@ function compositeDisposable(...disposables) {
|
|
|
1423
515
|
try {
|
|
1424
516
|
d.dispose();
|
|
1425
517
|
} catch (error) {
|
|
1426
|
-
|
|
518
|
+
log2.error("Error during dispose:", error);
|
|
1427
519
|
}
|
|
1428
520
|
}
|
|
1429
521
|
}
|
|
@@ -1446,11 +538,7 @@ function usingSync(resource, fn) {
|
|
|
1446
538
|
}
|
|
1447
539
|
}
|
|
1448
540
|
|
|
1449
|
-
// src/remote/client.ts
|
|
1450
|
-
init_result();
|
|
1451
|
-
|
|
1452
541
|
// src/retry.ts
|
|
1453
|
-
init_result();
|
|
1454
542
|
var DEFAULT_OPTIONS = {
|
|
1455
543
|
maxAttempts: 3,
|
|
1456
544
|
delayMs: 1000,
|
|
@@ -1514,6 +602,17 @@ async function retry(fn, options = {}) {
|
|
|
1514
602
|
}
|
|
1515
603
|
throw lastError;
|
|
1516
604
|
}
|
|
605
|
+
async function retryResult(fn, options = {}) {
|
|
606
|
+
try {
|
|
607
|
+
const value = await retry(fn, options);
|
|
608
|
+
return Ok(value);
|
|
609
|
+
} catch (e) {
|
|
610
|
+
return Err(e instanceof Error ? e : new Error(String(e)));
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function withRetry(fn, options = {}) {
|
|
614
|
+
return (...args) => retry(() => fn(...args), options);
|
|
615
|
+
}
|
|
1517
616
|
var RetryConditions = {
|
|
1518
617
|
networkErrors: (error) => {
|
|
1519
618
|
const message = error.message.toLowerCase();
|
|
@@ -1565,8 +664,7 @@ var RetryPresets = {
|
|
|
1565
664
|
};
|
|
1566
665
|
|
|
1567
666
|
// src/remote/client.ts
|
|
1568
|
-
|
|
1569
|
-
var log4 = frameworkLoggers.remote;
|
|
667
|
+
var log3 = frameworkLoggers.remote;
|
|
1570
668
|
|
|
1571
669
|
class RemoteEngine {
|
|
1572
670
|
ws = null;
|
|
@@ -1674,7 +772,7 @@ class RemoteEngine {
|
|
|
1674
772
|
}
|
|
1675
773
|
dispatchAction(action, payload) {
|
|
1676
774
|
if (this.state !== "connected" || !this.ws) {
|
|
1677
|
-
|
|
775
|
+
log3.warn("Cannot dispatch action: not connected");
|
|
1678
776
|
return;
|
|
1679
777
|
}
|
|
1680
778
|
const message = {
|
|
@@ -1687,7 +785,7 @@ class RemoteEngine {
|
|
|
1687
785
|
}
|
|
1688
786
|
subscribeState() {
|
|
1689
787
|
if (this.state !== "connected" || !this.ws) {
|
|
1690
|
-
|
|
788
|
+
log3.warn("Cannot subscribe to state: not connected");
|
|
1691
789
|
return;
|
|
1692
790
|
}
|
|
1693
791
|
const message = { type: "subscribeState" };
|
|
@@ -1695,7 +793,7 @@ class RemoteEngine {
|
|
|
1695
793
|
}
|
|
1696
794
|
updateState(state) {
|
|
1697
795
|
if (this.state !== "connected" || !this.ws) {
|
|
1698
|
-
|
|
796
|
+
log3.warn("Cannot update state: not connected");
|
|
1699
797
|
return;
|
|
1700
798
|
}
|
|
1701
799
|
const message = {
|
|
@@ -1767,7 +865,7 @@ class RemoteEngine {
|
|
|
1767
865
|
break;
|
|
1768
866
|
}
|
|
1769
867
|
} catch (e) {
|
|
1770
|
-
|
|
868
|
+
log3.error("Error handling remote message:", e);
|
|
1771
869
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
1772
870
|
this.errorCallbacks.forEach((cb) => cb(error));
|
|
1773
871
|
}
|
|
@@ -1796,7 +894,7 @@ class RemoteEngine {
|
|
|
1796
894
|
}
|
|
1797
895
|
handlePatch(message) {
|
|
1798
896
|
if (message.revision <= this.currentRevision) {
|
|
1799
|
-
|
|
897
|
+
log3.warn(`Out of order patch: expected > ${this.currentRevision}, got ${message.revision}`);
|
|
1800
898
|
return;
|
|
1801
899
|
}
|
|
1802
900
|
this.currentRevision = message.revision;
|
|
@@ -1822,171 +920,16 @@ class RemoteEngine {
|
|
|
1822
920
|
maxDelayMs: 30000,
|
|
1823
921
|
jitter: 0.1,
|
|
1824
922
|
onRetry: (attempt, error) => {
|
|
1825
|
-
|
|
923
|
+
log3.debug(`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`);
|
|
1826
924
|
}
|
|
1827
925
|
}).catch((error) => {
|
|
1828
|
-
|
|
926
|
+
log3.error("Max reconnection attempts reached:", error.message);
|
|
1829
927
|
this.errorCallbacks.forEach((cb) => cb(new ConnectionError(this.url, error, this.options.maxReconnectAttempts)));
|
|
1830
928
|
});
|
|
1831
929
|
}, this.options.reconnectInterval);
|
|
1832
930
|
}
|
|
1833
931
|
}
|
|
1834
932
|
|
|
1835
|
-
// src/engine.ts
|
|
1836
|
-
init_logger();
|
|
1837
|
-
init_result();
|
|
1838
|
-
import { WasmEngine } from "../wasm-node/hypen_engine.js";
|
|
1839
|
-
var log5 = frameworkLoggers.engine;
|
|
1840
|
-
function unwrapForWasm(value) {
|
|
1841
|
-
if (value === null || typeof value !== "object") {
|
|
1842
|
-
return value;
|
|
1843
|
-
}
|
|
1844
|
-
if (typeof value.__getSnapshot === "function") {
|
|
1845
|
-
return value.__getSnapshot();
|
|
1846
|
-
}
|
|
1847
|
-
try {
|
|
1848
|
-
return structuredClone(value);
|
|
1849
|
-
} catch {
|
|
1850
|
-
return JSON.parse(JSON.stringify(value));
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
class Engine {
|
|
1855
|
-
wasmEngine = null;
|
|
1856
|
-
initialized = false;
|
|
1857
|
-
async init() {
|
|
1858
|
-
if (this.initialized)
|
|
1859
|
-
return;
|
|
1860
|
-
this.wasmEngine = new WasmEngine;
|
|
1861
|
-
this.initialized = true;
|
|
1862
|
-
}
|
|
1863
|
-
ensureInitialized() {
|
|
1864
|
-
if (!this.wasmEngine) {
|
|
1865
|
-
throw new Error("Engine not initialized. Call init() first.");
|
|
1866
|
-
}
|
|
1867
|
-
return this.wasmEngine;
|
|
1868
|
-
}
|
|
1869
|
-
setRenderCallback(callback) {
|
|
1870
|
-
const engine = this.ensureInitialized();
|
|
1871
|
-
engine.setRenderCallback((patches) => {
|
|
1872
|
-
callback(patches);
|
|
1873
|
-
});
|
|
1874
|
-
}
|
|
1875
|
-
setComponentResolver(resolver) {
|
|
1876
|
-
const engine = this.ensureInitialized();
|
|
1877
|
-
engine.setComponentResolver((componentName, contextPath) => {
|
|
1878
|
-
const result = resolver(componentName, contextPath);
|
|
1879
|
-
return result;
|
|
1880
|
-
});
|
|
1881
|
-
}
|
|
1882
|
-
renderSource(source) {
|
|
1883
|
-
const engine = this.ensureInitialized();
|
|
1884
|
-
try {
|
|
1885
|
-
engine.renderSource(source);
|
|
1886
|
-
} catch (err) {
|
|
1887
|
-
throw classifyEngineError(err);
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
renderLazyComponent(source) {
|
|
1891
|
-
const engine = this.ensureInitialized();
|
|
1892
|
-
engine.renderLazyComponent(source);
|
|
1893
|
-
}
|
|
1894
|
-
renderInto(source, parentNodeId, state) {
|
|
1895
|
-
const engine = this.ensureInitialized();
|
|
1896
|
-
try {
|
|
1897
|
-
engine.renderInto(source, parentNodeId, unwrapForWasm(state));
|
|
1898
|
-
} catch (err) {
|
|
1899
|
-
throw classifyEngineError(err);
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
notifyStateChange(paths, values) {
|
|
1903
|
-
const engine = this.ensureInitialized();
|
|
1904
|
-
if (paths.length === 0) {
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
try {
|
|
1908
|
-
engine.updateStateSparse(paths, unwrapForWasm(values));
|
|
1909
|
-
} catch (err) {
|
|
1910
|
-
throw classifyEngineError(err);
|
|
1911
|
-
}
|
|
1912
|
-
log5.debug("State changed (sparse):", paths);
|
|
1913
|
-
}
|
|
1914
|
-
notifyStateChangeFull(paths, currentState) {
|
|
1915
|
-
const engine = this.ensureInitialized();
|
|
1916
|
-
engine.updateState(unwrapForWasm(currentState));
|
|
1917
|
-
if (paths.length > 0) {
|
|
1918
|
-
log5.debug("State changed (full):", paths);
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
updateState(statePatch) {
|
|
1922
|
-
const engine = this.ensureInitialized();
|
|
1923
|
-
engine.updateState(unwrapForWasm(statePatch));
|
|
1924
|
-
}
|
|
1925
|
-
dispatchAction(name, payload) {
|
|
1926
|
-
const engine = this.ensureInitialized();
|
|
1927
|
-
try {
|
|
1928
|
-
engine.dispatchAction(name, payload ?? null);
|
|
1929
|
-
} catch (err) {
|
|
1930
|
-
throw classifyEngineError(err);
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
_onErrorHandler;
|
|
1934
|
-
setOnError(handler) {
|
|
1935
|
-
this._onErrorHandler = handler;
|
|
1936
|
-
}
|
|
1937
|
-
onAction(actionName, handler) {
|
|
1938
|
-
const engine = this.ensureInitialized();
|
|
1939
|
-
const handleError = (err) => {
|
|
1940
|
-
log5.error("Action handler error:", err);
|
|
1941
|
-
if (this._onErrorHandler) {
|
|
1942
|
-
this._onErrorHandler(err instanceof Error ? err : new Error(String(err)));
|
|
1943
|
-
}
|
|
1944
|
-
};
|
|
1945
|
-
engine.onAction(actionName, (action) => {
|
|
1946
|
-
try {
|
|
1947
|
-
const result = handler(action);
|
|
1948
|
-
if (result && typeof result.catch === "function") {
|
|
1949
|
-
result.catch(handleError);
|
|
1950
|
-
}
|
|
1951
|
-
} catch (err) {
|
|
1952
|
-
handleError(err);
|
|
1953
|
-
}
|
|
1954
|
-
});
|
|
1955
|
-
}
|
|
1956
|
-
setModule(name, actions, stateKeys, initialState) {
|
|
1957
|
-
const engine = this.ensureInitialized();
|
|
1958
|
-
engine.setModule(name, actions, stateKeys, initialState);
|
|
1959
|
-
}
|
|
1960
|
-
getRevision() {
|
|
1961
|
-
const engine = this.ensureInitialized();
|
|
1962
|
-
return Number(engine.getRevision());
|
|
1963
|
-
}
|
|
1964
|
-
clearTree() {
|
|
1965
|
-
const engine = this.ensureInitialized();
|
|
1966
|
-
engine.clearTree();
|
|
1967
|
-
}
|
|
1968
|
-
debugParseComponent(source) {
|
|
1969
|
-
const engine = this.ensureInitialized();
|
|
1970
|
-
return engine.debugParseComponent(source);
|
|
1971
|
-
}
|
|
1972
|
-
setContext(name, data) {
|
|
1973
|
-
const engine = this.ensureInitialized();
|
|
1974
|
-
try {
|
|
1975
|
-
engine.setContext(name, unwrapForWasm(data));
|
|
1976
|
-
} catch (err) {
|
|
1977
|
-
throw classifyEngineError(err);
|
|
1978
|
-
}
|
|
1979
|
-
log5.debug(`Data source "${name}" context set`);
|
|
1980
|
-
}
|
|
1981
|
-
removeContext(name) {
|
|
1982
|
-
const engine = this.ensureInitialized();
|
|
1983
|
-
engine.removeContext(name);
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
// src/remote/server.ts
|
|
1988
|
-
init_app();
|
|
1989
|
-
|
|
1990
933
|
// src/remote/session.ts
|
|
1991
934
|
class SessionManager {
|
|
1992
935
|
activeSessions = new Map;
|
|
@@ -2110,697 +1053,9 @@ class SessionManager {
|
|
|
2110
1053
|
this.sessionConnections.clear();
|
|
2111
1054
|
}
|
|
2112
1055
|
}
|
|
2113
|
-
|
|
2114
|
-
// src/remote/server.ts
|
|
2115
|
-
init_logger();
|
|
2116
|
-
|
|
2117
|
-
// src/discovery.ts
|
|
2118
|
-
init_path();
|
|
2119
|
-
init_logger();
|
|
2120
|
-
var {existsSync, readdirSync, readFileSync, watch} = (() => ({}));
|
|
2121
|
-
async function discoverComponents(baseDir, options = {}) {
|
|
2122
|
-
const {
|
|
2123
|
-
patterns = ["single-file", "folder", "sibling", "index"],
|
|
2124
|
-
recursive = true,
|
|
2125
|
-
debug = false
|
|
2126
|
-
} = options;
|
|
2127
|
-
const log6 = debug ? (...args) => frameworkLoggers.discovery.debug(...args) : () => {};
|
|
2128
|
-
const resolvedDir = resolve(baseDir);
|
|
2129
|
-
const components = [];
|
|
2130
|
-
const seen = new Set;
|
|
2131
|
-
log6("Scanning directory:", resolvedDir);
|
|
2132
|
-
log6("Patterns:", patterns);
|
|
2133
|
-
const addTwoFileComponent = (name, hypenPath, modulePath) => {
|
|
2134
|
-
if (seen.has(name)) {
|
|
2135
|
-
log6(`Skipping duplicate: ${name}`);
|
|
2136
|
-
return;
|
|
2137
|
-
}
|
|
2138
|
-
seen.add(name);
|
|
2139
|
-
const templateRaw = readFileSync(hypenPath, "utf-8");
|
|
2140
|
-
const template = templateRaw.trim();
|
|
2141
|
-
components.push({
|
|
2142
|
-
name,
|
|
2143
|
-
hypenPath,
|
|
2144
|
-
modulePath,
|
|
2145
|
-
template,
|
|
2146
|
-
hasModule: modulePath !== null,
|
|
2147
|
-
isSingleFile: false
|
|
2148
|
-
});
|
|
2149
|
-
log6(`Found: ${name} (two-file, ${modulePath ? "with module" : "stateless"})`);
|
|
2150
|
-
};
|
|
2151
|
-
const addSingleFileComponent = (name, modulePath, template) => {
|
|
2152
|
-
if (seen.has(name)) {
|
|
2153
|
-
log6(`Skipping duplicate: ${name}`);
|
|
2154
|
-
return;
|
|
2155
|
-
}
|
|
2156
|
-
seen.add(name);
|
|
2157
|
-
components.push({
|
|
2158
|
-
name,
|
|
2159
|
-
hypenPath: null,
|
|
2160
|
-
modulePath,
|
|
2161
|
-
template,
|
|
2162
|
-
hasModule: true,
|
|
2163
|
-
isSingleFile: true
|
|
2164
|
-
});
|
|
2165
|
-
log6(`Found: ${name} (single-file with inline template)`);
|
|
2166
|
-
};
|
|
2167
|
-
const scanForFolderComponents = (dir) => {
|
|
2168
|
-
if (!existsSync(dir))
|
|
2169
|
-
return;
|
|
2170
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
2171
|
-
for (const entry of entries) {
|
|
2172
|
-
if (!entry.isDirectory())
|
|
2173
|
-
continue;
|
|
2174
|
-
const folderPath = join(dir, entry.name);
|
|
2175
|
-
const componentName = entry.name;
|
|
2176
|
-
if (patterns.includes("folder")) {
|
|
2177
|
-
const hypenPath = join(folderPath, "component.hypen");
|
|
2178
|
-
if (existsSync(hypenPath)) {
|
|
2179
|
-
const modulePath = join(folderPath, "component.ts");
|
|
2180
|
-
addTwoFileComponent(componentName, hypenPath, existsSync(modulePath) ? modulePath : null);
|
|
2181
|
-
continue;
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
if (patterns.includes("index")) {
|
|
2185
|
-
const hypenPath = join(folderPath, "index.hypen");
|
|
2186
|
-
if (existsSync(hypenPath)) {
|
|
2187
|
-
const modulePath = join(folderPath, "index.ts");
|
|
2188
|
-
addTwoFileComponent(componentName, hypenPath, existsSync(modulePath) ? modulePath : null);
|
|
2189
|
-
continue;
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
if (recursive) {
|
|
2193
|
-
scanForFolderComponents(folderPath);
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
};
|
|
2197
|
-
const scanForSiblingComponents = (dir) => {
|
|
2198
|
-
if (!existsSync(dir))
|
|
2199
|
-
return;
|
|
2200
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
2201
|
-
for (const entry of entries) {
|
|
2202
|
-
if (entry.isDirectory()) {
|
|
2203
|
-
if (recursive) {
|
|
2204
|
-
scanForSiblingComponents(join(dir, entry.name));
|
|
2205
|
-
}
|
|
2206
|
-
continue;
|
|
2207
|
-
}
|
|
2208
|
-
if (!entry.name.endsWith(".hypen"))
|
|
2209
|
-
continue;
|
|
2210
|
-
const hypenPath = join(dir, entry.name);
|
|
2211
|
-
const baseName = basename(entry.name, ".hypen");
|
|
2212
|
-
if (baseName === "component" || baseName === "index")
|
|
2213
|
-
continue;
|
|
2214
|
-
const modulePath = join(dir, `${baseName}.ts`);
|
|
2215
|
-
addTwoFileComponent(baseName, hypenPath, existsSync(modulePath) ? modulePath : null);
|
|
2216
|
-
}
|
|
2217
|
-
};
|
|
2218
|
-
const scanForSingleFileComponents = async (dir) => {
|
|
2219
|
-
if (!existsSync(dir))
|
|
2220
|
-
return;
|
|
2221
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
2222
|
-
for (const entry of entries) {
|
|
2223
|
-
if (entry.isDirectory()) {
|
|
2224
|
-
if (recursive) {
|
|
2225
|
-
await scanForSingleFileComponents(join(dir, entry.name));
|
|
2226
|
-
}
|
|
2227
|
-
continue;
|
|
2228
|
-
}
|
|
2229
|
-
if (!entry.name.endsWith(".ts"))
|
|
2230
|
-
continue;
|
|
2231
|
-
if (entry.name.startsWith(".") || entry.name.includes(".test.") || entry.name.includes(".spec."))
|
|
2232
|
-
continue;
|
|
2233
|
-
const baseName = basename(entry.name, ".ts");
|
|
2234
|
-
if (baseName === "component" || baseName === "index")
|
|
2235
|
-
continue;
|
|
2236
|
-
const hypenPath = join(dir, `${baseName}.hypen`);
|
|
2237
|
-
if (existsSync(hypenPath))
|
|
2238
|
-
continue;
|
|
2239
|
-
const modulePath = join(dir, entry.name);
|
|
2240
|
-
const content = readFileSync(modulePath, "utf-8");
|
|
2241
|
-
if (content.includes(".ui(") || content.includes(".ui(hypen")) {
|
|
2242
|
-
try {
|
|
2243
|
-
const moduleExport = await import(modulePath);
|
|
2244
|
-
const module = moduleExport.default;
|
|
2245
|
-
if (module && typeof module === "object" && module.template) {
|
|
2246
|
-
addSingleFileComponent(baseName, modulePath, module.template);
|
|
2247
|
-
} else if (module && typeof module === "object") {
|
|
2248
|
-
frameworkLoggers.discovery.warn(`Skipping "${entry.name}": has .ui() pattern but module has no .template property. Did you forget to call .ui(hypen\`...\`)?`);
|
|
2249
|
-
}
|
|
2250
|
-
} catch (e) {
|
|
2251
|
-
log6(`Failed to import potential single-file component: ${entry.name}`, e);
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
};
|
|
2256
|
-
if (patterns.includes("folder") || patterns.includes("index")) {
|
|
2257
|
-
scanForFolderComponents(resolvedDir);
|
|
2258
|
-
}
|
|
2259
|
-
if (patterns.includes("sibling")) {
|
|
2260
|
-
scanForSiblingComponents(resolvedDir);
|
|
2261
|
-
}
|
|
2262
|
-
if (patterns.includes("single-file")) {
|
|
2263
|
-
await scanForSingleFileComponents(resolvedDir);
|
|
2264
|
-
}
|
|
2265
|
-
log6(`Discovered ${components.length} components`);
|
|
2266
|
-
return components;
|
|
2267
|
-
}
|
|
2268
|
-
async function loadDiscoveredComponents(components) {
|
|
2269
|
-
const { app: app2 } = await Promise.resolve().then(() => (init_app(), exports_app));
|
|
2270
|
-
const loaded = new Map;
|
|
2271
|
-
for (const component of components) {
|
|
2272
|
-
let module;
|
|
2273
|
-
let template = component.template;
|
|
2274
|
-
if (component.modulePath) {
|
|
2275
|
-
const moduleExport = await import(component.modulePath);
|
|
2276
|
-
module = moduleExport.default;
|
|
2277
|
-
if (component.isSingleFile && module.template) {
|
|
2278
|
-
template = module.template;
|
|
2279
|
-
}
|
|
2280
|
-
} else {
|
|
2281
|
-
module = app2.defineState({}).build();
|
|
2282
|
-
}
|
|
2283
|
-
loaded.set(component.name, {
|
|
2284
|
-
name: component.name,
|
|
2285
|
-
module,
|
|
2286
|
-
template
|
|
2287
|
-
});
|
|
2288
|
-
}
|
|
2289
|
-
return loaded;
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
// src/remote/server.ts
|
|
2293
|
-
var log6 = frameworkLoggers.remote;
|
|
2294
|
-
|
|
2295
|
-
class RemoteServer {
|
|
2296
|
-
_module = null;
|
|
2297
|
-
_moduleName = "App";
|
|
2298
|
-
_ui = "";
|
|
2299
|
-
_sourceDir = null;
|
|
2300
|
-
_discoveredComponents = new Map;
|
|
2301
|
-
_config = {};
|
|
2302
|
-
_sessionConfig = {};
|
|
2303
|
-
_onConnectionCallbacks = [];
|
|
2304
|
-
_onDisconnectionCallbacks = [];
|
|
2305
|
-
clients = new Map;
|
|
2306
|
-
nextClientId = 1;
|
|
2307
|
-
server = null;
|
|
2308
|
-
sessionManager = null;
|
|
2309
|
-
_syncActions = false;
|
|
2310
|
-
module(name, module) {
|
|
2311
|
-
this._moduleName = name;
|
|
2312
|
-
this._module = module;
|
|
2313
|
-
return this;
|
|
2314
|
-
}
|
|
2315
|
-
ui(dsl) {
|
|
2316
|
-
this._ui = dsl;
|
|
2317
|
-
return this;
|
|
2318
|
-
}
|
|
2319
|
-
source(dir) {
|
|
2320
|
-
this._sourceDir = dir;
|
|
2321
|
-
return this;
|
|
2322
|
-
}
|
|
2323
|
-
config(config2) {
|
|
2324
|
-
this._config = { ...this._config, ...config2 };
|
|
2325
|
-
return this;
|
|
2326
|
-
}
|
|
2327
|
-
session(config2) {
|
|
2328
|
-
this._sessionConfig = config2;
|
|
2329
|
-
return this;
|
|
2330
|
-
}
|
|
2331
|
-
syncActions() {
|
|
2332
|
-
this._syncActions = true;
|
|
2333
|
-
return this;
|
|
2334
|
-
}
|
|
2335
|
-
onConnection(callback) {
|
|
2336
|
-
this._onConnectionCallbacks.push(callback);
|
|
2337
|
-
return this;
|
|
2338
|
-
}
|
|
2339
|
-
onDisconnection(callback) {
|
|
2340
|
-
this._onDisconnectionCallbacks.push(callback);
|
|
2341
|
-
return this;
|
|
2342
|
-
}
|
|
2343
|
-
async listen(port) {
|
|
2344
|
-
if (!this._module) {
|
|
2345
|
-
throw new Error("Module not set. Call .module() before .listen()");
|
|
2346
|
-
}
|
|
2347
|
-
if (this._sourceDir) {
|
|
2348
|
-
await this.discoverFromSource();
|
|
2349
|
-
}
|
|
2350
|
-
if (!this._ui) {
|
|
2351
|
-
throw new Error("UI not set. Call .ui() or .source() before .listen()");
|
|
2352
|
-
}
|
|
2353
|
-
this.sessionManager = new SessionManager(this._sessionConfig);
|
|
2354
|
-
const finalPort = port ?? this._config.port ?? 3000;
|
|
2355
|
-
const hostname = this._config.hostname ?? "0.0.0.0";
|
|
2356
|
-
this.server = Bun.serve({
|
|
2357
|
-
port: finalPort,
|
|
2358
|
-
hostname,
|
|
2359
|
-
websocket: {
|
|
2360
|
-
open: (ws) => this.handleOpen(ws),
|
|
2361
|
-
message: (ws, message) => this.handleMessage(ws, message),
|
|
2362
|
-
close: (ws) => this.handleClose(ws)
|
|
2363
|
-
},
|
|
2364
|
-
fetch: (req, server) => {
|
|
2365
|
-
const url = new URL(req.url);
|
|
2366
|
-
if (server.upgrade(req, { data: undefined })) {
|
|
2367
|
-
return;
|
|
2368
|
-
}
|
|
2369
|
-
if (url.pathname === "/health") {
|
|
2370
|
-
return new Response("OK", { status: 200 });
|
|
2371
|
-
}
|
|
2372
|
-
if (url.pathname === "/stats") {
|
|
2373
|
-
const stats = this.sessionManager?.getStats() ?? {
|
|
2374
|
-
activeSessions: 0,
|
|
2375
|
-
pendingSessions: 0,
|
|
2376
|
-
totalConnections: 0
|
|
2377
|
-
};
|
|
2378
|
-
return new Response(JSON.stringify(stats), {
|
|
2379
|
-
headers: { "Content-Type": "application/json" }
|
|
2380
|
-
});
|
|
2381
|
-
}
|
|
2382
|
-
return new Response("Hypen Remote Server", { status: 200 });
|
|
2383
|
-
}
|
|
2384
|
-
});
|
|
2385
|
-
log6.info(`Hypen app streaming on ws://${hostname}:${finalPort}`);
|
|
2386
|
-
return this;
|
|
2387
|
-
}
|
|
2388
|
-
stop() {
|
|
2389
|
-
if (this.server) {
|
|
2390
|
-
this.server.stop();
|
|
2391
|
-
this.server = null;
|
|
2392
|
-
}
|
|
2393
|
-
if (this.sessionManager) {
|
|
2394
|
-
this.sessionManager.destroy();
|
|
2395
|
-
this.sessionManager = null;
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
get url() {
|
|
2399
|
-
if (!this.server)
|
|
2400
|
-
return null;
|
|
2401
|
-
const hostname = this._config.hostname ?? "localhost";
|
|
2402
|
-
const port = this._config.port ?? 3000;
|
|
2403
|
-
return `ws://${hostname}:${port}`;
|
|
2404
|
-
}
|
|
2405
|
-
async discoverFromSource() {
|
|
2406
|
-
const dir = this._sourceDir;
|
|
2407
|
-
log6.info(`Discovering components from ${dir}...`);
|
|
2408
|
-
const discovered = await discoverComponents(dir);
|
|
2409
|
-
const loaded = await loadDiscoveredComponents(discovered);
|
|
2410
|
-
for (const [name, { module, template }] of loaded) {
|
|
2411
|
-
this._discoveredComponents.set(name, { template, module });
|
|
2412
|
-
}
|
|
2413
|
-
log6.info(`Discovered ${this._discoveredComponents.size} components: ${Array.from(this._discoveredComponents.keys()).join(", ")}`);
|
|
2414
|
-
if (!this._ui) {
|
|
2415
|
-
const entry = this._discoveredComponents.get(this._moduleName);
|
|
2416
|
-
if (entry) {
|
|
2417
|
-
this._ui = entry.template;
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
setupComponentResolver(engine) {
|
|
2422
|
-
if (this._discoveredComponents.size === 0)
|
|
2423
|
-
return;
|
|
2424
|
-
engine.setComponentResolver((componentName, _contextPath) => {
|
|
2425
|
-
const comp = this._discoveredComponents.get(componentName);
|
|
2426
|
-
if (!comp) {
|
|
2427
|
-
log6.warn(`Component "${componentName}" not found. Available: ${Array.from(this._discoveredComponents.keys()).join(", ") || "(none)"}`);
|
|
2428
|
-
return null;
|
|
2429
|
-
}
|
|
2430
|
-
return {
|
|
2431
|
-
source: comp.template,
|
|
2432
|
-
path: componentName
|
|
2433
|
-
};
|
|
2434
|
-
});
|
|
2435
|
-
}
|
|
2436
|
-
handleOpen(ws) {
|
|
2437
|
-
try {
|
|
2438
|
-
const clientId = `client_${this.nextClientId++}`;
|
|
2439
|
-
const connectedAt = new Date;
|
|
2440
|
-
const engine = new Engine;
|
|
2441
|
-
engine.init().catch((err) => log6.error("Engine init failed:", err));
|
|
2442
|
-
this.setupComponentResolver(engine);
|
|
2443
|
-
const moduleInstance = new HypenModuleInstance(engine, this._module);
|
|
2444
|
-
log6.info(`Client ${clientId} connected, engine initialized`);
|
|
2445
|
-
const clientData = {
|
|
2446
|
-
id: clientId,
|
|
2447
|
-
engine,
|
|
2448
|
-
moduleInstance,
|
|
2449
|
-
revision: 0,
|
|
2450
|
-
connectedAt,
|
|
2451
|
-
sessionId: "",
|
|
2452
|
-
helloReceived: false,
|
|
2453
|
-
stateSubscribed: false
|
|
2454
|
-
};
|
|
2455
|
-
this.clients.set(ws, clientData);
|
|
2456
|
-
clientData.helloTimeout = setTimeout(() => {
|
|
2457
|
-
if (!clientData.helloReceived) {
|
|
2458
|
-
this.initializeSession(ws, clientData, undefined, undefined).catch((err) => log6.error("Error initializing legacy session:", err));
|
|
2459
|
-
}
|
|
2460
|
-
}, 1000);
|
|
2461
|
-
} catch (error) {
|
|
2462
|
-
log6.error("Error handling WebSocket open:", error);
|
|
2463
|
-
ws.close(1011, "Internal server error");
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2466
|
-
async initializeSession(ws, clientData, requestedSessionId, props) {
|
|
2467
|
-
if (clientData.helloReceived)
|
|
2468
|
-
return;
|
|
2469
|
-
clientData.helloReceived = true;
|
|
2470
|
-
log6.info(`Initializing session for ${clientData.id} (sessionId: ${requestedSessionId ?? "new"})`);
|
|
2471
|
-
if (clientData.helloTimeout) {
|
|
2472
|
-
clearTimeout(clientData.helloTimeout);
|
|
2473
|
-
clientData.helloTimeout = undefined;
|
|
2474
|
-
}
|
|
2475
|
-
let session;
|
|
2476
|
-
let isNew = true;
|
|
2477
|
-
let isRestored = false;
|
|
2478
|
-
let restoredState = null;
|
|
2479
|
-
if (requestedSessionId && this.sessionManager) {
|
|
2480
|
-
const resumed = this.sessionManager.resumeSession(requestedSessionId);
|
|
2481
|
-
if (resumed) {
|
|
2482
|
-
session = resumed.session;
|
|
2483
|
-
restoredState = resumed.savedState;
|
|
2484
|
-
isNew = false;
|
|
2485
|
-
isRestored = true;
|
|
2486
|
-
await this.triggerReconnect(clientData, session, restoredState);
|
|
2487
|
-
} else {
|
|
2488
|
-
const activeSession = this.sessionManager.getActiveSession(requestedSessionId);
|
|
2489
|
-
if (activeSession) {
|
|
2490
|
-
const handled = await this.handleConcurrentConnection(ws, clientData, activeSession, props);
|
|
2491
|
-
if (!handled)
|
|
2492
|
-
return;
|
|
2493
|
-
session = activeSession;
|
|
2494
|
-
isNew = false;
|
|
2495
|
-
} else {
|
|
2496
|
-
session = this.sessionManager.createSession(props);
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
} else if (this.sessionManager) {
|
|
2500
|
-
session = this.sessionManager.createSession(props);
|
|
2501
|
-
} else {
|
|
2502
|
-
session = {
|
|
2503
|
-
id: crypto.randomUUID(),
|
|
2504
|
-
ttl: 3600,
|
|
2505
|
-
createdAt: new Date,
|
|
2506
|
-
lastConnectedAt: new Date,
|
|
2507
|
-
props
|
|
2508
|
-
};
|
|
2509
|
-
}
|
|
2510
|
-
clientData.sessionId = session.id;
|
|
2511
|
-
this.sessionManager?.trackConnection(session.id, ws);
|
|
2512
|
-
const sessionAck = {
|
|
2513
|
-
type: "sessionAck",
|
|
2514
|
-
sessionId: session.id,
|
|
2515
|
-
isNew,
|
|
2516
|
-
isRestored
|
|
2517
|
-
};
|
|
2518
|
-
ws.send(JSON.stringify(sessionAck));
|
|
2519
|
-
log6.info(`Sent sessionAck to ${clientData.id} (session: ${session.id})`);
|
|
2520
|
-
const initialPatches = [];
|
|
2521
|
-
clientData.engine.setRenderCallback((patches) => {
|
|
2522
|
-
initialPatches.push(...patches);
|
|
2523
|
-
});
|
|
2524
|
-
try {
|
|
2525
|
-
clientData.engine.renderSource(this._ui);
|
|
2526
|
-
} catch (renderError) {
|
|
2527
|
-
log6.error(`Failed to render UI for ${clientData.id}:`, renderError);
|
|
2528
|
-
ws.close(1011, "Render failed");
|
|
2529
|
-
return;
|
|
2530
|
-
}
|
|
2531
|
-
this.setupRenderCallback(ws, clientData);
|
|
2532
|
-
const initialMessage = {
|
|
2533
|
-
type: "initialTree",
|
|
2534
|
-
module: this._moduleName,
|
|
2535
|
-
state: clientData.moduleInstance.getState(),
|
|
2536
|
-
patches: initialPatches,
|
|
2537
|
-
revision: 0
|
|
2538
|
-
};
|
|
2539
|
-
try {
|
|
2540
|
-
ws.send(JSON.stringify(initialMessage));
|
|
2541
|
-
} catch (sendError) {
|
|
2542
|
-
log6.error(`Failed to send initialTree to ${clientData.id}:`, sendError);
|
|
2543
|
-
return;
|
|
2544
|
-
}
|
|
2545
|
-
log6.info(`Sent initialTree to ${clientData.id} (${initialPatches.length} patches)`);
|
|
2546
|
-
const client = {
|
|
2547
|
-
id: clientData.id,
|
|
2548
|
-
socket: ws,
|
|
2549
|
-
connectedAt: clientData.connectedAt
|
|
2550
|
-
};
|
|
2551
|
-
this._onConnectionCallbacks.forEach((cb) => cb(client));
|
|
2552
|
-
}
|
|
2553
|
-
setupRenderCallback(ws, clientData) {
|
|
2554
|
-
clientData.engine.setRenderCallback((patches) => {
|
|
2555
|
-
const data = this.clients.get(ws);
|
|
2556
|
-
if (!data)
|
|
2557
|
-
return;
|
|
2558
|
-
data.revision++;
|
|
2559
|
-
const patchMessage = {
|
|
2560
|
-
type: "patch",
|
|
2561
|
-
module: this._moduleName,
|
|
2562
|
-
patches,
|
|
2563
|
-
revision: data.revision
|
|
2564
|
-
};
|
|
2565
|
-
ws.send(JSON.stringify(patchMessage));
|
|
2566
|
-
if (data.stateSubscribed) {
|
|
2567
|
-
const stateMessage = {
|
|
2568
|
-
type: "stateUpdate",
|
|
2569
|
-
module: this._moduleName,
|
|
2570
|
-
state: data.moduleInstance.getState(),
|
|
2571
|
-
revision: data.revision
|
|
2572
|
-
};
|
|
2573
|
-
ws.send(JSON.stringify(stateMessage));
|
|
2574
|
-
}
|
|
2575
|
-
if (this.sessionManager?.getConcurrentPolicy() === "allow-multiple" && data.sessionId) {
|
|
2576
|
-
const connections = this.sessionManager.getConnections(data.sessionId);
|
|
2577
|
-
if (connections) {
|
|
2578
|
-
for (const conn of connections) {
|
|
2579
|
-
if (conn !== ws) {
|
|
2580
|
-
const otherWs = conn;
|
|
2581
|
-
const otherData = this.clients.get(otherWs);
|
|
2582
|
-
otherWs.send(JSON.stringify(patchMessage));
|
|
2583
|
-
if (otherData?.stateSubscribed) {
|
|
2584
|
-
const stateMessage = {
|
|
2585
|
-
type: "stateUpdate",
|
|
2586
|
-
module: this._moduleName,
|
|
2587
|
-
state: data.moduleInstance.getState(),
|
|
2588
|
-
revision: data.revision
|
|
2589
|
-
};
|
|
2590
|
-
otherWs.send(JSON.stringify(stateMessage));
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
});
|
|
2597
|
-
}
|
|
2598
|
-
async handleConcurrentConnection(ws, clientData, existingSession, props) {
|
|
2599
|
-
const policy = this.sessionManager?.getConcurrentPolicy() ?? "kick-old";
|
|
2600
|
-
switch (policy) {
|
|
2601
|
-
case "kick-old": {
|
|
2602
|
-
const existingConnections = this.sessionManager?.getConnections(existingSession.id);
|
|
2603
|
-
if (existingConnections) {
|
|
2604
|
-
for (const conn of existingConnections) {
|
|
2605
|
-
const oldWs = conn;
|
|
2606
|
-
const expiredMsg = {
|
|
2607
|
-
type: "sessionExpired",
|
|
2608
|
-
sessionId: existingSession.id,
|
|
2609
|
-
reason: "kicked"
|
|
2610
|
-
};
|
|
2611
|
-
oldWs.send(JSON.stringify(expiredMsg));
|
|
2612
|
-
oldWs.close(1000, "Session taken by new connection");
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
return true;
|
|
2616
|
-
}
|
|
2617
|
-
case "reject-new": {
|
|
2618
|
-
const expiredMsg = {
|
|
2619
|
-
type: "sessionExpired",
|
|
2620
|
-
sessionId: existingSession.id,
|
|
2621
|
-
reason: "kicked"
|
|
2622
|
-
};
|
|
2623
|
-
ws.send(JSON.stringify(expiredMsg));
|
|
2624
|
-
ws.close(1000, "Session already active");
|
|
2625
|
-
return false;
|
|
2626
|
-
}
|
|
2627
|
-
case "allow-multiple": {
|
|
2628
|
-
return true;
|
|
2629
|
-
}
|
|
2630
|
-
default:
|
|
2631
|
-
return true;
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
async triggerReconnect(clientData, session, savedState) {
|
|
2635
|
-
const handler = this._module?.handlers.onReconnect;
|
|
2636
|
-
if (!handler)
|
|
2637
|
-
return;
|
|
2638
|
-
let restored = false;
|
|
2639
|
-
const restore = (state) => {
|
|
2640
|
-
if (state === null || typeof state !== "object") {
|
|
2641
|
-
log6.warn("restore() called with non-object state, ignoring:", typeof state);
|
|
2642
|
-
return;
|
|
2643
|
-
}
|
|
2644
|
-
restored = true;
|
|
2645
|
-
clientData.moduleInstance.updateState(state);
|
|
2646
|
-
};
|
|
2647
|
-
await handler({ session, restore });
|
|
2648
|
-
}
|
|
2649
|
-
handleMessage(ws, message) {
|
|
2650
|
-
try {
|
|
2651
|
-
const clientData = this.clients.get(ws);
|
|
2652
|
-
if (!clientData)
|
|
2653
|
-
return;
|
|
2654
|
-
const msg = JSON.parse(message.toString());
|
|
2655
|
-
switch (msg.type) {
|
|
2656
|
-
case "hello": {
|
|
2657
|
-
const helloMsg = msg;
|
|
2658
|
-
this.initializeSession(ws, clientData, helloMsg.sessionId, helloMsg.props).catch((err) => log6.error("Error initializing session from hello:", err));
|
|
2659
|
-
break;
|
|
2660
|
-
}
|
|
2661
|
-
case "dispatchAction": {
|
|
2662
|
-
const actionMsg = msg;
|
|
2663
|
-
clientData.engine.dispatchAction(actionMsg.action, actionMsg.payload);
|
|
2664
|
-
if (this._syncActions) {
|
|
2665
|
-
for (const [otherWs, otherClient] of this.clients) {
|
|
2666
|
-
if (otherWs === ws || !otherClient.helloReceived)
|
|
2667
|
-
continue;
|
|
2668
|
-
otherClient.engine.dispatchAction(actionMsg.action, actionMsg.payload);
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
break;
|
|
2672
|
-
}
|
|
2673
|
-
case "updateState": {
|
|
2674
|
-
const stateMsg = msg;
|
|
2675
|
-
clientData.moduleInstance.updateState(stateMsg.state);
|
|
2676
|
-
if (this._syncActions) {
|
|
2677
|
-
for (const [otherWs, otherClient] of this.clients) {
|
|
2678
|
-
if (otherWs === ws || !otherClient.helloReceived)
|
|
2679
|
-
continue;
|
|
2680
|
-
otherClient.moduleInstance.updateState(stateMsg.state);
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2683
|
-
break;
|
|
2684
|
-
}
|
|
2685
|
-
case "subscribeState": {
|
|
2686
|
-
clientData.stateSubscribed = true;
|
|
2687
|
-
log6.info(`Client ${clientData.id} subscribed to state updates`);
|
|
2688
|
-
break;
|
|
2689
|
-
}
|
|
2690
|
-
default:
|
|
2691
|
-
break;
|
|
2692
|
-
}
|
|
2693
|
-
} catch (error) {
|
|
2694
|
-
log6.error("Error handling WebSocket message:", error);
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
async handleClose(ws) {
|
|
2698
|
-
const clientData = this.clients.get(ws);
|
|
2699
|
-
if (!clientData)
|
|
2700
|
-
return;
|
|
2701
|
-
if (clientData.helloTimeout) {
|
|
2702
|
-
clearTimeout(clientData.helloTimeout);
|
|
2703
|
-
}
|
|
2704
|
-
const currentState = clientData.moduleInstance.getState();
|
|
2705
|
-
if (clientData.sessionId && this._module?.handlers.onDisconnect) {
|
|
2706
|
-
const session = this.sessionManager?.getActiveSession(clientData.sessionId);
|
|
2707
|
-
if (session) {
|
|
2708
|
-
await this._module.handlers.onDisconnect({
|
|
2709
|
-
state: currentState,
|
|
2710
|
-
session
|
|
2711
|
-
});
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
if (clientData.sessionId && this.sessionManager) {
|
|
2715
|
-
this.sessionManager.untrackConnection(clientData.sessionId, ws);
|
|
2716
|
-
if (this.sessionManager.getConnectionCount(clientData.sessionId) === 0) {
|
|
2717
|
-
const session = this.sessionManager.getActiveSession(clientData.sessionId);
|
|
2718
|
-
if (session) {
|
|
2719
|
-
this.sessionManager.suspendSession(clientData.sessionId, currentState, async (expiredSession) => {
|
|
2720
|
-
if (this._module?.handlers.onExpire) {
|
|
2721
|
-
await this._module.handlers.onExpire({ session: expiredSession });
|
|
2722
|
-
}
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
await clientData.moduleInstance.destroy();
|
|
2728
|
-
this.clients.delete(ws);
|
|
2729
|
-
const client = {
|
|
2730
|
-
id: clientData.id,
|
|
2731
|
-
socket: ws,
|
|
2732
|
-
connectedAt: clientData.connectedAt
|
|
2733
|
-
};
|
|
2734
|
-
this._onDisconnectionCallbacks.forEach((cb) => cb(client));
|
|
2735
|
-
}
|
|
2736
|
-
async reload() {
|
|
2737
|
-
if (this._sourceDir) {
|
|
2738
|
-
this._ui = "";
|
|
2739
|
-
await this.discoverFromSource();
|
|
2740
|
-
}
|
|
2741
|
-
if (!this._ui) {
|
|
2742
|
-
log6.warn("Reload skipped: no UI template available");
|
|
2743
|
-
return;
|
|
2744
|
-
}
|
|
2745
|
-
for (const [ws, clientData] of this.clients) {
|
|
2746
|
-
if (!clientData.helloReceived)
|
|
2747
|
-
continue;
|
|
2748
|
-
this.setupComponentResolver(clientData.engine);
|
|
2749
|
-
try {
|
|
2750
|
-
clientData.engine.renderSource(this._ui);
|
|
2751
|
-
log6.info(`Hot-reloaded client ${clientData.id}`);
|
|
2752
|
-
} catch (e) {
|
|
2753
|
-
log6.error(`Failed to hot-reload client ${clientData.id}:`, e);
|
|
2754
|
-
}
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
getClientCount() {
|
|
2758
|
-
return this.clients.size;
|
|
2759
|
-
}
|
|
2760
|
-
getSessionStats() {
|
|
2761
|
-
return this.sessionManager?.getStats() ?? {
|
|
2762
|
-
activeSessions: 0,
|
|
2763
|
-
pendingSessions: 0,
|
|
2764
|
-
totalConnections: 0
|
|
2765
|
-
};
|
|
2766
|
-
}
|
|
2767
|
-
broadcast(message) {
|
|
2768
|
-
const json = JSON.stringify(message);
|
|
2769
|
-
this.clients.forEach((_, ws) => {
|
|
2770
|
-
ws.send(json);
|
|
2771
|
-
});
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
async function serve(options) {
|
|
2775
|
-
const server = new RemoteServer().module(options.moduleName ?? "App", options.module);
|
|
2776
|
-
if (options.source) {
|
|
2777
|
-
server.source(options.source);
|
|
2778
|
-
}
|
|
2779
|
-
if (options.ui) {
|
|
2780
|
-
server.ui(options.ui);
|
|
2781
|
-
}
|
|
2782
|
-
if (options.port || options.hostname) {
|
|
2783
|
-
server.config({
|
|
2784
|
-
port: options.port,
|
|
2785
|
-
hostname: options.hostname
|
|
2786
|
-
});
|
|
2787
|
-
}
|
|
2788
|
-
if (options.session) {
|
|
2789
|
-
server.session(options.session);
|
|
2790
|
-
}
|
|
2791
|
-
if (options.onConnection) {
|
|
2792
|
-
server.onConnection(options.onConnection);
|
|
2793
|
-
}
|
|
2794
|
-
if (options.onDisconnection) {
|
|
2795
|
-
server.onDisconnection(options.onDisconnection);
|
|
2796
|
-
}
|
|
2797
|
-
return server.listen(options.port);
|
|
2798
|
-
}
|
|
2799
1056
|
export {
|
|
2800
|
-
serve,
|
|
2801
1057
|
SessionManager,
|
|
2802
|
-
RemoteServer,
|
|
2803
1058
|
RemoteEngine
|
|
2804
1059
|
};
|
|
2805
1060
|
|
|
2806
|
-
//# debugId=
|
|
1061
|
+
//# debugId=F3F2ECE49CFDE50F64756E2164756E21
|