@spearwolf/shadow-objects 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/README.md +49 -13
- package/bundle.js +7 -7
- package/package.json +2 -2
- package/src/elements/ShaeEntElement.d.ts +4 -4
- package/src/elements/ShaeEntElement.d.ts.map +1 -1
- package/src/elements/ShaeEntElement.js +4 -5
- package/src/elements/ShaeEntElement.js.map +2 -2
- package/src/elements/ShaePropElement.d.ts +5 -5
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +2 -2
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +31 -6
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/in-the-dark/Kernel.d.ts +5 -0
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +49 -24
- package/src/in-the-dark/Kernel.js.map +2 -2
- package/src/in-the-dark/Registry.d.ts +1 -1
- package/src/in-the-dark/Registry.d.ts.map +1 -1
- package/src/in-the-dark/Registry.js +24 -20
- package/src/in-the-dark/Registry.js.map +2 -2
- package/src/in-the-dark/ShadowObject.d.ts +1 -1
- package/src/in-the-dark/ShadowObject.d.ts.map +1 -1
- package/src/in-the-dark/ShadowObject.js +2 -2
- package/src/in-the-dark/ShadowObject.js.map +2 -2
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- package/src/in-the-dark/importModule.js +3 -3
- package/src/in-the-dark/importModule.js.map +2 -2
- package/src/types.d.ts +4 -4
- package/src/types.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.js +3 -2
- package/src/utils/ConsoleLogger.js.map +2 -2
- package/src/utils/FrameLoop.d.ts +1 -1
- package/src/utils/FrameLoop.d.ts.map +1 -1
- package/src/utils/FrameLoop.js +0 -2
- package/src/utils/FrameLoop.js.map +2 -2
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +1 -1
- package/src/utils/attr-utils.js.map +2 -2
- package/src/utils/generateUUID.d.ts +1 -1
- package/src/utils/generateUUID.d.ts.map +1 -1
- package/src/utils/props-utils.d.ts +1 -1
- package/src/utils/props-utils.d.ts.map +1 -1
- package/src/utils/waitForMessageOfType.d.ts +5 -1
- package/src/utils/waitForMessageOfType.d.ts.map +1 -1
- package/src/utils/waitForMessageOfType.js +12 -2
- package/src/utils/waitForMessageOfType.js.map +2 -2
- package/src/view/ComponentChanges.d.ts +4 -1
- package/src/view/ComponentChanges.d.ts.map +1 -1
- package/src/view/ComponentChanges.js +20 -6
- package/src/view/ComponentChanges.js.map +2 -2
- package/src/view/ComponentContext.d.ts +43 -1
- package/src/view/ComponentContext.d.ts.map +1 -1
- package/src/view/ComponentContext.js +123 -47
- package/src/view/ComponentContext.js.map +2 -2
- package/src/view/ComponentMemory.js +3 -1
- package/src/view/ComponentMemory.js.map +2 -2
- package/src/view/IShadowObjectEnvProxy.d.ts +6 -0
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +11 -6
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +42 -0
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +130 -18
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +25 -1
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +103 -29
- package/src/view/ShadowEnv.js.map +2 -2
- package/src/view/ViewComponent.d.ts +22 -1
- package/src/view/ViewComponent.d.ts.map +1 -1
- package/src/view/ViewComponent.js +66 -22
- package/src/view/ViewComponent.js.map +2 -2
- package/src/worker/MessageRouter.d.ts.map +1 -1
- package/src/worker/MessageRouter.js +7 -5
- package/src/worker/MessageRouter.js.map +2 -2
package/src/view/ShadowEnv.js
CHANGED
|
@@ -46,12 +46,18 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
46
46
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
47
47
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
48
48
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
49
|
-
var _proxyReady_dec, _viewReady_dec, _comCtx, _shaObjEnvProxy, _syncScheduled, _syncAfterContextCreated, _syncWaitForConfirmation, _afterNextSync, _init, _viewReady, _proxyReady, _isDestroyed,
|
|
49
|
+
var _proxyReady_dec, _viewReady_dec, _comCtx, _shaObjEnvProxy, _syncScheduled, _syncAfterContextCreated, _syncWaitForConfirmation, _afterNextSync, _init, _viewReady, _proxyReady, _isDestroyed, _whenDestroyed, _rejectWhenDestroyed, _ShadowEnv_instances, destroyedSignal_fn, _syncIfScheduled, syncNow_fn, onMessageToView_fn, onProxyFailed_fn;
|
|
50
50
|
import { emit, off, on, onceAsync, Priority, retain, retainClear } from "@spearwolf/eventize";
|
|
51
51
|
import { createEffect, createSignal, destroyObjectSignals, findObjectSignalByName } from "@spearwolf/signalize";
|
|
52
52
|
import { signal } from "@spearwolf/signalize/decorators";
|
|
53
53
|
import { ConsoleLogger } from "../utils/ConsoleLogger.js";
|
|
54
54
|
import { ComponentContext } from "./ComponentContext.js";
|
|
55
|
+
class ShadowEnvDestroyedError extends Error {
|
|
56
|
+
constructor(message = "the shadow environment has been destroyed") {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "ShadowEnvDestroyedError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
55
61
|
_viewReady_dec = [signal()], _proxyReady_dec = [signal()];
|
|
56
62
|
const _ShadowEnv = class _ShadowEnv {
|
|
57
63
|
constructor() {
|
|
@@ -67,8 +73,17 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
67
73
|
__privateAdd(this, _viewReady, __runInitializers(_init, 8, this, false)), __runInitializers(_init, 11, this);
|
|
68
74
|
__privateAdd(this, _proxyReady, __runInitializers(_init, 12, this, false)), __runInitializers(_init, 15, this);
|
|
69
75
|
__privateAdd(this, _isDestroyed, false);
|
|
76
|
+
__privateAdd(this, _whenDestroyed);
|
|
77
|
+
__privateAdd(this, _rejectWhenDestroyed);
|
|
78
|
+
/**
|
|
79
|
+
* Resolves once the environment is ready.
|
|
80
|
+
*
|
|
81
|
+
* @throws {ShadowEnvDestroyedError} if the environment is destroyed before that happens
|
|
82
|
+
*/
|
|
70
83
|
this.ready = async () => {
|
|
71
|
-
|
|
84
|
+
if (__privateGet(this, _isDestroyed)) throw new ShadowEnvDestroyedError();
|
|
85
|
+
if (this.isReady) return this;
|
|
86
|
+
return Promise.race([onceAsync(this, _ShadowEnv.ContextCreated), __privateMethod(this, _ShadowEnv_instances, destroyedSignal_fn).call(this)]);
|
|
72
87
|
};
|
|
73
88
|
__privateAdd(this, _syncIfScheduled, () => {
|
|
74
89
|
if (__privateGet(this, _syncScheduled)) {
|
|
@@ -106,11 +121,11 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
106
121
|
}
|
|
107
122
|
set view(ctx) {
|
|
108
123
|
if (ctx !== __privateGet(this, _comCtx)) {
|
|
109
|
-
if (__privateGet(this, _comCtx)
|
|
124
|
+
if (__privateGet(this, _comCtx)?.ns && globalThis.__shadowEnvs) {
|
|
110
125
|
globalThis.__shadowEnvs.delete(__privateGet(this, _comCtx).ns);
|
|
111
126
|
}
|
|
112
127
|
__privateSet(this, _comCtx, ctx ?? void 0);
|
|
113
|
-
if (__privateGet(this, _comCtx)
|
|
128
|
+
if (__privateGet(this, _comCtx)?.ns) {
|
|
114
129
|
globalThis.__shadowEnvs ??= /* @__PURE__ */ new Map();
|
|
115
130
|
if (globalThis.__shadowEnvs.has(__privateGet(this, _comCtx).ns) && globalThis.__shadowEnvs.get(__privateGet(this, _comCtx).ns) !== this) {
|
|
116
131
|
if (this.logger.isWarn) {
|
|
@@ -135,6 +150,7 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
135
150
|
__privateSet(this, _shaObjEnvProxy, proxy ?? void 0);
|
|
136
151
|
if (__privateGet(this, _shaObjEnvProxy)) {
|
|
137
152
|
__privateGet(this, _shaObjEnvProxy).onMessageToView = __privateMethod(this, _ShadowEnv_instances, onMessageToView_fn).bind(this);
|
|
153
|
+
__privateGet(this, _shaObjEnvProxy).onProxyFailed = __privateMethod(this, _ShadowEnv_instances, onProxyFailed_fn).bind(this);
|
|
138
154
|
}
|
|
139
155
|
if (prevProxy) {
|
|
140
156
|
prevProxy.destroy();
|
|
@@ -152,6 +168,7 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
152
168
|
return Boolean(__privateGet(this, _comCtx) && __privateGet(this, _shaObjEnvProxy) && this.proxyReady && !this.isDestroyed);
|
|
153
169
|
}
|
|
154
170
|
sync() {
|
|
171
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
155
172
|
if (!this.isReady) {
|
|
156
173
|
__privateSet(this, _syncAfterContextCreated, true);
|
|
157
174
|
return;
|
|
@@ -160,31 +177,55 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
160
177
|
__privateSet(this, _syncScheduled, true);
|
|
161
178
|
queueMicrotask(__privateGet(this, _syncIfScheduled));
|
|
162
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Like {@link ShadowEnv.sync}, but resolves with the change trail once the cycle completed.
|
|
182
|
+
*
|
|
183
|
+
* @throws {ShadowEnvDestroyedError} if the environment is destroyed before that happens
|
|
184
|
+
*/
|
|
163
185
|
syncWait() {
|
|
186
|
+
if (__privateGet(this, _isDestroyed)) return Promise.reject(new ShadowEnvDestroyedError());
|
|
164
187
|
__privateSet(this, _syncWaitForConfirmation, true);
|
|
165
188
|
this.sync();
|
|
166
189
|
if (__privateGet(this, _afterNextSync)) return __privateGet(this, _afterNextSync);
|
|
167
|
-
__privateSet(this, _afterNextSync,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
__privateSet(this, _afterNextSync, Promise.race([
|
|
191
|
+
onceAsync(this, _ShadowEnv.AfterSync),
|
|
192
|
+
__privateMethod(this, _ShadowEnv_instances, destroyedSignal_fn).call(this)
|
|
193
|
+
]).then(
|
|
194
|
+
(changeTrail) => {
|
|
195
|
+
__privateSet(this, _afterNextSync, void 0);
|
|
196
|
+
return changeTrail;
|
|
197
|
+
},
|
|
198
|
+
(error) => {
|
|
199
|
+
__privateSet(this, _afterNextSync, void 0);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
));
|
|
171
203
|
return __privateGet(this, _afterNextSync);
|
|
172
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Tear the environment down: the proxy is destroyed, the namespace is released, all signals
|
|
207
|
+
* and listeners are removed, and every caller still waiting on {@link ShadowEnv.ready} or
|
|
208
|
+
* {@link ShadowEnv.syncWait} is rejected with a {@link ShadowEnvDestroyedError} instead of
|
|
209
|
+
* being left pending forever. Calling it more than once is a no-op.
|
|
210
|
+
*/
|
|
173
211
|
destroy() {
|
|
212
|
+
var _a;
|
|
213
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
214
|
+
__privateSet(this, _isDestroyed, true);
|
|
215
|
+
__privateSet(this, _syncScheduled, false);
|
|
216
|
+
__privateSet(this, _syncAfterContextCreated, false);
|
|
217
|
+
__privateSet(this, _syncWaitForConfirmation, false);
|
|
174
218
|
const ns = __privateGet(this, _comCtx)?.ns;
|
|
175
|
-
this.envProxy?.destroy();
|
|
176
219
|
this.envProxy = void 0;
|
|
177
220
|
this.view = void 0;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
globalThis.__shadowEnvs.delete(ns);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
221
|
+
const shadowEnvs = globalThis.__shadowEnvs;
|
|
222
|
+
if (ns && shadowEnvs?.get(ns) === this) {
|
|
223
|
+
shadowEnvs.delete(ns);
|
|
184
224
|
}
|
|
225
|
+
(_a = __privateGet(this, _rejectWhenDestroyed)) == null ? void 0 : _a.call(this, new ShadowEnvDestroyedError());
|
|
226
|
+
__privateSet(this, _afterNextSync, void 0);
|
|
185
227
|
destroyObjectSignals(this);
|
|
186
228
|
off(this);
|
|
187
|
-
__privateSet(this, _isDestroyed, true);
|
|
188
229
|
Object.freeze(this);
|
|
189
230
|
}
|
|
190
231
|
};
|
|
@@ -198,23 +239,45 @@ _afterNextSync = new WeakMap();
|
|
|
198
239
|
_viewReady = new WeakMap();
|
|
199
240
|
_proxyReady = new WeakMap();
|
|
200
241
|
_isDestroyed = new WeakMap();
|
|
201
|
-
|
|
242
|
+
_whenDestroyed = new WeakMap();
|
|
243
|
+
_rejectWhenDestroyed = new WeakMap();
|
|
202
244
|
_ShadowEnv_instances = new WeakSet();
|
|
245
|
+
/**
|
|
246
|
+
* A promise that rejects the moment this environment is destroyed.
|
|
247
|
+
*
|
|
248
|
+
* Every promise the public API hands out races against it. Without that race a caller
|
|
249
|
+
* would wait forever, because {@link ShadowEnv.destroy} tears down the very listeners
|
|
250
|
+
* those promises are built on.
|
|
251
|
+
*/
|
|
252
|
+
destroyedSignal_fn = function() {
|
|
253
|
+
if (__privateGet(this, _whenDestroyed) == null) {
|
|
254
|
+
__privateSet(this, _whenDestroyed, new Promise((_, reject) => {
|
|
255
|
+
__privateSet(this, _rejectWhenDestroyed, reject);
|
|
256
|
+
}));
|
|
257
|
+
__privateGet(this, _whenDestroyed).catch(() => {
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return __privateGet(this, _whenDestroyed);
|
|
261
|
+
};
|
|
262
|
+
_syncIfScheduled = new WeakMap();
|
|
203
263
|
syncNow_fn = async function() {
|
|
204
264
|
__privateSet(this, _syncScheduled, false);
|
|
205
|
-
if (this
|
|
206
|
-
|
|
265
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
266
|
+
if (!this.isReady) {
|
|
267
|
+
__privateSet(this, _syncAfterContextCreated, true);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const data = this.view.buildChangeTrails();
|
|
271
|
+
const waitForConfirmation = __privateGet(this, _syncWaitForConfirmation);
|
|
272
|
+
__privateSet(this, _syncWaitForConfirmation, false);
|
|
273
|
+
try {
|
|
207
274
|
if (data.length > 0) {
|
|
208
|
-
|
|
209
|
-
const waitForConfirmation = __privateGet(this, _syncWaitForConfirmation);
|
|
210
|
-
__privateSet(this, _syncWaitForConfirmation, false);
|
|
211
|
-
await this.envProxy.applyChangeTrail(data, waitForConfirmation);
|
|
212
|
-
} catch (error) {
|
|
213
|
-
this.logger.error("failed to apply change trail", error);
|
|
214
|
-
} finally {
|
|
215
|
-
emit(this, _ShadowEnv.AfterSync, data);
|
|
216
|
-
}
|
|
275
|
+
await this.envProxy.applyChangeTrail(data, waitForConfirmation);
|
|
217
276
|
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
this.logger.error("failed to apply change trail", error);
|
|
279
|
+
} finally {
|
|
280
|
+
emit(this, _ShadowEnv.AfterSync, data);
|
|
218
281
|
}
|
|
219
282
|
};
|
|
220
283
|
onMessageToView_fn = function(event) {
|
|
@@ -223,14 +286,25 @@ onMessageToView_fn = function(event) {
|
|
|
223
286
|
}
|
|
224
287
|
this.view?.dispatchMessage(event.uuid, event.type, event.data, event.traverseChildren);
|
|
225
288
|
};
|
|
289
|
+
onProxyFailed_fn = function(reason) {
|
|
290
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
291
|
+
this.logger.error("the environment proxy failed", reason);
|
|
292
|
+
try {
|
|
293
|
+
emit(this, _ShadowEnv.ProxyFailed, reason, this);
|
|
294
|
+
} finally {
|
|
295
|
+
this.proxyReady = false;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
226
298
|
__decorateElement(_init, 4, "viewReady", _viewReady_dec, _ShadowEnv, _viewReady);
|
|
227
299
|
__decorateElement(_init, 4, "proxyReady", _proxyReady_dec, _ShadowEnv, _proxyReady);
|
|
228
300
|
__decoratorMetadata(_init, _ShadowEnv);
|
|
229
301
|
_ShadowEnv.AfterSync = "afterSync";
|
|
230
302
|
_ShadowEnv.ContextLost = "contextLost";
|
|
231
303
|
_ShadowEnv.ContextCreated = "contextCreated";
|
|
304
|
+
_ShadowEnv.ProxyFailed = "proxyFailed";
|
|
232
305
|
let ShadowEnv = _ShadowEnv;
|
|
233
306
|
export {
|
|
234
|
-
ShadowEnv
|
|
307
|
+
ShadowEnv,
|
|
308
|
+
ShadowEnvDestroyedError
|
|
235
309
|
};
|
|
236
310
|
//# sourceMappingURL=ShadowEnv.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/view/ShadowEnv.ts"],
|
|
4
|
-
"sourcesContent": ["import {emit, off, on, onceAsync, Priority, retain, retainClear} from '@spearwolf/eventize';\nimport {createEffect, createSignal, destroyObjectSignals, findObjectSignalByName} from '@spearwolf/signalize';\nimport {signal} from '@spearwolf/signalize/decorators';\nimport type {MessageToViewEvent} from '../shadow-objects.js';\nimport type {ChangeTrailType, NamespaceType} from '../types.js';\nimport {ConsoleLogger} from '../utils/ConsoleLogger.js';\nimport {ComponentContext} from './ComponentContext.js';\nimport type {IShadowObjectEnvProxy} from './IShadowObjectEnvProxy.js';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __shadowEnvs: Map<NamespaceType, ShadowEnv> | undefined;\n}\n\nexport class ShadowEnv {\n static AfterSync = 'afterSync';\n static ContextLost = 'contextLost';\n static ContextCreated = 'contextCreated';\n\n static get(ns: NamespaceType): ShadowEnv | undefined {\n if (ns == null) return undefined;\n return globalThis.__shadowEnvs?.get(ns);\n }\n\n #comCtx?: ComponentContext;\n #shaObjEnvProxy?: IShadowObjectEnvProxy;\n #syncScheduled = false;\n #syncAfterContextCreated = false;\n #syncWaitForConfirmation = false;\n #afterNextSync?: Promise<ChangeTrailType>;\n\n readonly logger = new ConsoleLogger('ShadowEnv');\n\n readonly ns$ = createSignal<NamespaceType | undefined>();\n\n @signal() accessor viewReady = false;\n @signal() accessor proxyReady = false;\n\n #isDestroyed = false;\n\n get isDestroyed() {\n return this.#isDestroyed;\n }\n\n constructor() {\n const self = this as ShadowEnv;\n retain(self, ShadowEnv.ContextCreated);\n\n on(self, ShadowEnv.ContextLost, Priority.AAA, () => {\n retainClear(self, ShadowEnv.ContextCreated);\n });\n\n createEffect(() => {\n if (this.viewReady && this.proxyReady) {\n this.view!.reCreateChanges();\n emit(self, ShadowEnv.ContextCreated, self);\n if (this.#syncAfterContextCreated) {\n this.#syncAfterContextCreated = false;\n this.#syncNow();\n }\n return () => {\n emit(self, ShadowEnv.ContextLost, self);\n };\n }\n }, [findObjectSignalByName(this, 'viewReady'), findObjectSignalByName(this, 'proxyReady')]);\n }\n\n get view(): ComponentContext | undefined {\n return this.#comCtx;\n }\n\n set view(ctx: ComponentContext | null | undefined) {\n if (ctx !== this.#comCtx) {\n if (this.#comCtx && this.#comCtx.ns && globalThis.__shadowEnvs) {\n globalThis.__shadowEnvs.delete(this.#comCtx.ns);\n }\n\n this.#comCtx = ctx ?? undefined;\n\n if (this.#comCtx && this.#comCtx.ns) {\n globalThis.__shadowEnvs ??= new Map();\n if (globalThis.__shadowEnvs.has(this.#comCtx.ns) && globalThis.__shadowEnvs.get(this.#comCtx.ns) !== this) {\n if (this.logger.isWarn) {\n this.logger.warn(\n 'overwrite a namespace already in use',\n this.#comCtx.ns,\n globalThis.__shadowEnvs.get(this.#comCtx.ns),\n );\n }\n }\n globalThis.__shadowEnvs.set(this.#comCtx.ns, this);\n }\n\n this.viewReady = Boolean(ctx);\n }\n }\n\n get envProxy(): IShadowObjectEnvProxy | undefined {\n return this.#shaObjEnvProxy;\n }\n\n set envProxy(proxy: IShadowObjectEnvProxy | null | undefined) {\n if (proxy !== this.#shaObjEnvProxy) {\n const prevProxy = this.#shaObjEnvProxy;\n this.#shaObjEnvProxy = proxy ?? undefined;\n\n if (this.#shaObjEnvProxy) {\n this.#shaObjEnvProxy.onMessageToView = this.#onMessageToView.bind(this);\n }\n\n if (prevProxy) {\n prevProxy.destroy();\n }\n\n this.proxyReady = false;\n\n proxy\n ?.start()\n .then(() => {\n this.proxyReady = true;\n })\n .catch((error) => {\n this.logger.error('failed to start envProxy', error);\n this.proxyReady = false;\n });\n }\n }\n\n get isReady(): boolean {\n return Boolean(this.#comCtx && this.#shaObjEnvProxy && this.proxyReady && !this.isDestroyed);\n }\n\n readonly ready = async (): Promise<ShadowEnv> => {\n return this.isReady ? this : onceAsync(this as ShadowEnv, ShadowEnv.ContextCreated);\n };\n\n sync(): void {\n if (!this.isReady) {\n this.#syncAfterContextCreated = true;\n return;\n }\n if (this.#syncScheduled) return;\n this.#syncScheduled = true;\n queueMicrotask(this.#syncIfScheduled);\n }\n\n syncWait(): Promise<ChangeTrailType> {\n this.#syncWaitForConfirmation = true;\n this.sync();\n if (this.#afterNextSync) return this.#afterNextSync;\n this.#afterNextSync = onceAsync<ChangeTrailType>(this as ShadowEnv, ShadowEnv.AfterSync).then((changeTrail) => {\n this.#afterNextSync = undefined;\n return changeTrail;\n });\n return this.#afterNextSync;\n }\n\n destroy() {\n const ns = this.#comCtx?.ns;\n\n this.envProxy?.destroy();\n this.envProxy = undefined;\n this.view = undefined;\n\n if (ns) {\n if (globalThis.__shadowEnvs.has(ns)) {\n if (globalThis.__shadowEnvs.get(ns) === this) {\n globalThis.__shadowEnvs.delete(ns);\n }\n }\n }\n\n destroyObjectSignals(this);\n off(this);\n\n this.#isDestroyed = true;\n Object.freeze(this);\n }\n\n #syncIfScheduled = () => {\n if (this.#syncScheduled) {\n this.#syncNow();\n }\n };\n\n async #syncNow() {\n this.#syncScheduled = false;\n if (this.isReady) {\n const data = this.view!.buildChangeTrails();\n if (data.length > 0) {\n try {\n const waitForConfirmation = this.#syncWaitForConfirmation;\n this.#syncWaitForConfirmation = false;\n await this.envProxy!.applyChangeTrail(data, waitForConfirmation);\n } catch (error) {\n this.logger.error('failed to apply change trail', error);\n } finally {\n emit(this as ShadowEnv, ShadowEnv.AfterSync, data);\n }\n }\n }\n }\n\n #onMessageToView(event: Omit<MessageToViewEvent, 'transferables'>) {\n if (this.logger.isDebug) {\n this.logger.debug('onMessageToView', event.type, event.data);\n }\n this.view?.dispatchMessage(event.uuid, event.type, event.data, event.traverseChildren);\n }\n}"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAQ,MAAM,KAAK,IAAI,WAAW,UAAU,QAAQ,mBAAkB;AACtE,SAAQ,cAAc,cAAc,sBAAsB,8BAA6B;AACvF,SAAQ,cAAa;AAGrB,SAAQ,qBAAoB;AAC5B,SAAQ,wBAAuB;
|
|
4
|
+
"sourcesContent": ["import {emit, off, on, onceAsync, Priority, retain, retainClear} from '@spearwolf/eventize';\nimport {createEffect, createSignal, destroyObjectSignals, findObjectSignalByName} from '@spearwolf/signalize';\nimport {signal} from '@spearwolf/signalize/decorators';\nimport type {MessageToViewEvent} from '../shadow-objects.js';\nimport type {ChangeTrailType, NamespaceType} from '../types.js';\nimport {ConsoleLogger} from '../utils/ConsoleLogger.js';\nimport {ComponentContext} from './ComponentContext.js';\nimport type {IShadowObjectEnvProxy} from './IShadowObjectEnvProxy.js';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __shadowEnvs: Map<NamespaceType, ShadowEnv> | undefined;\n}\n\n/**\n * The reason every pending {@link ShadowEnv.ready} and {@link ShadowEnv.syncWait} promise\n * is rejected with when the environment is destroyed.\n */\nexport class ShadowEnvDestroyedError extends Error {\n constructor(message = 'the shadow environment has been destroyed') {\n super(message);\n this.name = 'ShadowEnvDestroyedError';\n }\n}\n\nexport class ShadowEnv {\n static AfterSync = 'afterSync';\n static ContextLost = 'contextLost';\n static ContextCreated = 'contextCreated';\n static ProxyFailed = 'proxyFailed';\n\n static get(ns: NamespaceType): ShadowEnv | undefined {\n if (ns == null) return undefined;\n return globalThis.__shadowEnvs?.get(ns);\n }\n\n #comCtx?: ComponentContext;\n #shaObjEnvProxy?: IShadowObjectEnvProxy;\n #syncScheduled = false;\n #syncAfterContextCreated = false;\n #syncWaitForConfirmation = false;\n #afterNextSync?: Promise<ChangeTrailType>;\n\n readonly logger = new ConsoleLogger('ShadowEnv');\n\n readonly ns$ = createSignal<NamespaceType | undefined>();\n\n @signal() accessor viewReady = false;\n @signal() accessor proxyReady = false;\n\n #isDestroyed = false;\n\n get isDestroyed() {\n return this.#isDestroyed;\n }\n\n constructor() {\n const self = this as ShadowEnv;\n retain(self, ShadowEnv.ContextCreated);\n\n on(self, ShadowEnv.ContextLost, Priority.AAA, () => {\n retainClear(self, ShadowEnv.ContextCreated);\n });\n\n createEffect(() => {\n if (this.viewReady && this.proxyReady) {\n this.view!.reCreateChanges();\n emit(self, ShadowEnv.ContextCreated, self);\n if (this.#syncAfterContextCreated) {\n this.#syncAfterContextCreated = false;\n this.#syncNow();\n }\n return () => {\n emit(self, ShadowEnv.ContextLost, self);\n };\n }\n // the two @signal accessors above create their signals during field initialization,\n // so both lookups resolve by the time the constructor body runs\n }, [findObjectSignalByName(this, 'viewReady')!, findObjectSignalByName(this, 'proxyReady')!]);\n }\n\n get view(): ComponentContext | undefined {\n return this.#comCtx;\n }\n\n set view(ctx: ComponentContext | null | undefined) {\n if (ctx !== this.#comCtx) {\n if (this.#comCtx?.ns && globalThis.__shadowEnvs) {\n globalThis.__shadowEnvs.delete(this.#comCtx.ns);\n }\n\n this.#comCtx = ctx ?? undefined;\n\n if (this.#comCtx?.ns) {\n globalThis.__shadowEnvs ??= new Map();\n if (globalThis.__shadowEnvs.has(this.#comCtx.ns) && globalThis.__shadowEnvs.get(this.#comCtx.ns) !== this) {\n if (this.logger.isWarn) {\n this.logger.warn(\n 'overwrite a namespace already in use',\n this.#comCtx.ns,\n globalThis.__shadowEnvs.get(this.#comCtx.ns),\n );\n }\n }\n globalThis.__shadowEnvs.set(this.#comCtx.ns, this);\n }\n\n this.viewReady = Boolean(ctx);\n }\n }\n\n get envProxy(): IShadowObjectEnvProxy | undefined {\n return this.#shaObjEnvProxy;\n }\n\n set envProxy(proxy: IShadowObjectEnvProxy | null | undefined) {\n if (proxy !== this.#shaObjEnvProxy) {\n const prevProxy = this.#shaObjEnvProxy;\n this.#shaObjEnvProxy = proxy ?? undefined;\n\n if (this.#shaObjEnvProxy) {\n this.#shaObjEnvProxy.onMessageToView = this.#onMessageToView.bind(this);\n this.#shaObjEnvProxy.onProxyFailed = this.#onProxyFailed.bind(this);\n }\n\n if (prevProxy) {\n prevProxy.destroy();\n }\n\n this.proxyReady = false;\n\n proxy\n ?.start()\n .then(() => {\n this.proxyReady = true;\n })\n .catch((error) => {\n this.logger.error('failed to start envProxy', error);\n this.proxyReady = false;\n });\n }\n }\n\n get isReady(): boolean {\n return Boolean(this.#comCtx && this.#shaObjEnvProxy && this.proxyReady && !this.isDestroyed);\n }\n\n #whenDestroyed?: Promise<never>;\n #rejectWhenDestroyed?: (error: Error) => void;\n\n /**\n * A promise that rejects the moment this environment is destroyed.\n *\n * Every promise the public API hands out races against it. Without that race a caller\n * would wait forever, because {@link ShadowEnv.destroy} tears down the very listeners\n * those promises are built on.\n */\n #destroyedSignal(): Promise<never> {\n if (this.#whenDestroyed == null) {\n this.#whenDestroyed = new Promise<never>((_, reject) => {\n this.#rejectWhenDestroyed = reject;\n });\n // the signal is rejected unconditionally, also when nobody happens to be racing against it\n this.#whenDestroyed.catch(() => {});\n }\n return this.#whenDestroyed;\n }\n\n /**\n * Resolves once the environment is ready.\n *\n * @throws {ShadowEnvDestroyedError} if the environment is destroyed before that happens\n */\n readonly ready = async (): Promise<ShadowEnv> => {\n if (this.#isDestroyed) throw new ShadowEnvDestroyedError();\n if (this.isReady) return this;\n return Promise.race([onceAsync<ShadowEnv>(this as ShadowEnv, ShadowEnv.ContextCreated), this.#destroyedSignal()]);\n };\n\n sync(): void {\n if (this.#isDestroyed) return;\n if (!this.isReady) {\n this.#syncAfterContextCreated = true;\n return;\n }\n if (this.#syncScheduled) return;\n this.#syncScheduled = true;\n queueMicrotask(this.#syncIfScheduled);\n }\n\n /**\n * Like {@link ShadowEnv.sync}, but resolves with the change trail once the cycle completed.\n *\n * @throws {ShadowEnvDestroyedError} if the environment is destroyed before that happens\n */\n syncWait(): Promise<ChangeTrailType> {\n if (this.#isDestroyed) return Promise.reject(new ShadowEnvDestroyedError());\n\n this.#syncWaitForConfirmation = true;\n this.sync();\n\n if (this.#afterNextSync) return this.#afterNextSync;\n\n this.#afterNextSync = Promise.race([\n onceAsync<ChangeTrailType>(this as ShadowEnv, ShadowEnv.AfterSync),\n this.#destroyedSignal(),\n ]).then(\n (changeTrail) => {\n this.#afterNextSync = undefined;\n return changeTrail;\n },\n (error) => {\n this.#afterNextSync = undefined;\n throw error;\n },\n );\n\n return this.#afterNextSync;\n }\n\n /**\n * Tear the environment down: the proxy is destroyed, the namespace is released, all signals\n * and listeners are removed, and every caller still waiting on {@link ShadowEnv.ready} or\n * {@link ShadowEnv.syncWait} is rejected with a {@link ShadowEnvDestroyedError} instead of\n * being left pending forever. Calling it more than once is a no-op.\n */\n destroy() {\n if (this.#isDestroyed) return;\n\n // set first: isReady must report false for everything that runs below\n this.#isDestroyed = true;\n\n this.#syncScheduled = false;\n this.#syncAfterContextCreated = false;\n this.#syncWaitForConfirmation = false;\n\n const ns = this.#comCtx?.ns;\n\n // the `envProxy` setter destroys the previous proxy, so it must not be destroyed here as well\n this.envProxy = undefined;\n this.view = undefined;\n\n const shadowEnvs = globalThis.__shadowEnvs;\n if (ns && shadowEnvs?.get(ns) === this) {\n shadowEnvs.delete(ns);\n }\n\n // settle everyone still waiting before the listeners they depend on are removed\n this.#rejectWhenDestroyed?.(new ShadowEnvDestroyedError());\n this.#afterNextSync = undefined;\n\n destroyObjectSignals(this);\n off(this);\n\n Object.freeze(this);\n }\n\n #syncIfScheduled = () => {\n if (this.#syncScheduled) {\n this.#syncNow();\n }\n };\n\n async #syncNow() {\n this.#syncScheduled = false;\n\n if (this.#isDestroyed) return;\n\n if (!this.isReady) {\n // the environment went away between scheduling and running this sync:\n // re-arm instead of dropping it, otherwise a pending syncWait() would never settle\n this.#syncAfterContextCreated = true;\n return;\n }\n\n const data = this.view!.buildChangeTrails();\n\n const waitForConfirmation = this.#syncWaitForConfirmation;\n this.#syncWaitForConfirmation = false;\n\n try {\n if (data.length > 0) {\n await this.envProxy!.applyChangeTrail(data, waitForConfirmation);\n }\n } catch (error) {\n this.logger.error('failed to apply change trail', error);\n } finally {\n // always emitted, even for an empty change trail, so that syncWait() settles on every cycle\n emit(this as ShadowEnv, ShadowEnv.AfterSync, data);\n }\n }\n\n #onMessageToView(event: Omit<MessageToViewEvent, 'transferables'>) {\n if (this.logger.isDebug) {\n this.logger.debug('onMessageToView', event.type, event.data);\n }\n this.view?.dispatchMessage(event.uuid, event.type, event.data, event.traverseChildren);\n }\n\n #onProxyFailed(reason: unknown) {\n // destroy() freezes this instance and destroys its signals; a proxy may report its failure afterwards\n if (this.#isDestroyed) return;\n\n this.logger.error('the environment proxy failed', reason);\n\n try {\n // the reason before the consequence: ContextLost follows from dropping proxyReady\n emit(this as ShadowEnv, ShadowEnv.ProxyFailed, reason, this as ShadowEnv);\n } finally {\n // in the `finally`, because losing the environment is not up for debate:\n // a listener that throws must not leave `isReady` claiming otherwise\n this.proxyReady = false;\n }\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,7 +7,24 @@ export declare class ViewComponent {
|
|
|
7
7
|
get parent(): ViewComponent | undefined;
|
|
8
8
|
set parent(parent: ViewComponent | null | undefined);
|
|
9
9
|
get context(): ComponentContext | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* @throws {ComponentContextDisposedError} if the new context has been disposed. The component
|
|
12
|
+
* keeps its current context in that case: leaving the old one is only worth it if the new
|
|
13
|
+
* one can actually be joined.
|
|
14
|
+
*/
|
|
10
15
|
set context(context: ComponentContext | null | undefined);
|
|
16
|
+
/**
|
|
17
|
+
* A destroyed component is detached from its {@link ComponentContext}: it no longer appears
|
|
18
|
+
* in any change trail and no longer has a corresponding entity.
|
|
19
|
+
*
|
|
20
|
+
* Every mutation that only concerns the component itself (`token`, `order`, properties, events,
|
|
21
|
+
* `removeFromParent`, `destroy`) is silently ignored while destroyed. Operations that would tie
|
|
22
|
+
* a second, live component to it (`addChild`, the `parent` setter) throw instead, because
|
|
23
|
+
* ignoring them would leave the caller with a wrong picture of the entity tree.
|
|
24
|
+
*
|
|
25
|
+
* Assigning a {@link ViewComponent#context} revives the component under the same uuid.
|
|
26
|
+
*/
|
|
27
|
+
get isDestroyed(): boolean;
|
|
11
28
|
/**
|
|
12
29
|
* The order property sets the order to lay out a component in a children array of the parent component.
|
|
13
30
|
*
|
|
@@ -33,7 +50,11 @@ export declare class ViewComponent {
|
|
|
33
50
|
isChildOf(parent: ViewComponent): boolean;
|
|
34
51
|
removeFromParent(): void;
|
|
35
52
|
addChild(child: ViewComponent): void;
|
|
36
|
-
|
|
53
|
+
/**
|
|
54
|
+
* @returns `true` if the value differs from the last one written to the change trail.
|
|
55
|
+
* A destroyed component always returns `false`.
|
|
56
|
+
*/
|
|
57
|
+
setProperty<T = unknown>(name: string, value: T, isEqual?: (a: T, b: T) => boolean): boolean;
|
|
37
58
|
removeProperty(name: string): void;
|
|
38
59
|
dispatchShadowObjectsEvent(type: string, data: unknown, transferables?: Transferable[]): void;
|
|
39
60
|
dispatchEvent(type: string, data: unknown, traverseChildren: boolean): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewComponent.d.ts","sourceRoot":"","sources":["../../../src/view/ViewComponent.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"ViewComponent.d.ts","sourceRoot":"","sources":["../../../src/view/ViewComponent.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,gBAAgB,EAAgC,MAAM,uBAAuB,CAAC;AAsBtF,qBAAa,aAAa;;IAWxB,IAAI,IAAI,WAEP;IAED,IAAI,KAAK,IAIQ,MAAM,GAAG,SAAS,CAFlC;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAKlC;IAED,IAAI,MAAM,IAAI,aAAa,GAAG,SAAS,CAEtC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EAMlD;IAED,IAAI,OAAO,IAAI,gBAAgB,GAAG,SAAS,CAE1C;IAED;;;;OAIG;IACH,IAAI,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAyBvD;IAED;;;;;;;;;;OAUG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACH,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAMzC;IAED;;;;;;OAMG;IACH,IAAI,8BAA8B,IAAI,OAAO,CAE5C;gBAGC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,aAAa,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,8BAA8B,CAAC,EAAE,OAAO,CAAC;KAC1C;IAwBH,SAAS,CAAC,MAAM,EAAE,aAAa;IAI/B,gBAAgB;IAShB,QAAQ,CAAC,KAAK,EAAE,aAAa;IAuB7B;;;OAGG;IACH,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO;IAI5F,cAAc,CAAC,IAAI,EAAE,MAAM;IAI3B,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,YAAY,EAAE;IAItF,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO;IAUpE,OAAO;CAKR"}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { emit, eventize } from "@spearwolf/eventize";
|
|
2
2
|
import { VoidToken } from "../constants.js";
|
|
3
3
|
import { generateUUID } from "../utils/generateUUID.js";
|
|
4
|
-
import { ComponentContext } from "./ComponentContext.js";
|
|
4
|
+
import { ComponentContext, ComponentContextDisposedError } from "./ComponentContext.js";
|
|
5
5
|
class ViewComponentError extends Error {
|
|
6
6
|
constructor(message) {
|
|
7
7
|
super(message);
|
|
8
8
|
this.name = "ViewComponentError";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
function assertUsableAsParent(parent, childContext) {
|
|
12
|
+
if (parent.context == null) {
|
|
13
|
+
throw new ViewComponentError("cannot add a child to a destroyed view component");
|
|
14
|
+
}
|
|
15
|
+
if (parent.context !== childContext) {
|
|
16
|
+
throw new ViewComponentError("cannot add a child from another context");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
11
19
|
class ViewComponent {
|
|
12
20
|
#uuid;
|
|
13
21
|
#token;
|
|
@@ -32,9 +40,6 @@ class ViewComponent {
|
|
|
32
40
|
}
|
|
33
41
|
set parent(parent) {
|
|
34
42
|
if (parent) {
|
|
35
|
-
if (parent.#context !== this.#context) {
|
|
36
|
-
throw new ViewComponentError("cannot set parent from different context");
|
|
37
|
-
}
|
|
38
43
|
parent.addChild(this);
|
|
39
44
|
} else {
|
|
40
45
|
this.removeFromParent();
|
|
@@ -43,17 +48,44 @@ class ViewComponent {
|
|
|
43
48
|
get context() {
|
|
44
49
|
return this.#context;
|
|
45
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* @throws {ComponentContextDisposedError} if the new context has been disposed. The component
|
|
53
|
+
* keeps its current context in that case: leaving the old one is only worth it if the new
|
|
54
|
+
* one can actually be joined.
|
|
55
|
+
*/
|
|
46
56
|
set context(context) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
const next = context ?? void 0;
|
|
58
|
+
if (this.#context === next) return;
|
|
59
|
+
if (next?.isDisposed) {
|
|
60
|
+
throw new ComponentContextDisposedError(
|
|
61
|
+
`the view component ${this.#uuid} cannot join the component context because it has been disposed`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (this.#context) {
|
|
65
|
+
this.destroy();
|
|
66
|
+
}
|
|
67
|
+
this.#context = next;
|
|
68
|
+
try {
|
|
69
|
+
next?.addComponent(this);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.#context = void 0;
|
|
72
|
+
throw error;
|
|
55
73
|
}
|
|
56
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* A destroyed component is detached from its {@link ComponentContext}: it no longer appears
|
|
77
|
+
* in any change trail and no longer has a corresponding entity.
|
|
78
|
+
*
|
|
79
|
+
* Every mutation that only concerns the component itself (`token`, `order`, properties, events,
|
|
80
|
+
* `removeFromParent`, `destroy`) is silently ignored while destroyed. Operations that would tie
|
|
81
|
+
* a second, live component to it (`addChild`, the `parent` setter) throw instead, because
|
|
82
|
+
* ignoring them would leave the caller with a wrong picture of the entity tree.
|
|
83
|
+
*
|
|
84
|
+
* Assigning a {@link ViewComponent#context} revives the component under the same uuid.
|
|
85
|
+
*/
|
|
86
|
+
get isDestroyed() {
|
|
87
|
+
return this.#context == null;
|
|
88
|
+
}
|
|
57
89
|
/**
|
|
58
90
|
* The order property sets the order to lay out a component in a children array of the parent component.
|
|
59
91
|
*
|
|
@@ -66,7 +98,7 @@ class ViewComponent {
|
|
|
66
98
|
const prevOrder = this.#order;
|
|
67
99
|
this.#order = order ?? 0;
|
|
68
100
|
if (prevOrder !== this.#order) {
|
|
69
|
-
this.#context
|
|
101
|
+
this.#context?.changeOrder(this);
|
|
70
102
|
}
|
|
71
103
|
}
|
|
72
104
|
/**
|
|
@@ -85,13 +117,13 @@ class ViewComponent {
|
|
|
85
117
|
options = { parent: options };
|
|
86
118
|
}
|
|
87
119
|
this.#uuid = options?.uuid ?? generateUUID();
|
|
88
|
-
this.#token = token;
|
|
120
|
+
this.#token = token ?? VoidToken;
|
|
89
121
|
this.#order = options?.order ?? 0;
|
|
90
122
|
this.#parent = options?.parent;
|
|
91
123
|
this.#autoDestructionOnParentRemoval = options?.autoDestructionOnParentRemoval ?? false;
|
|
92
124
|
const ctx = options?.context ?? ComponentContext.get();
|
|
93
|
-
if (this.#parent
|
|
94
|
-
|
|
125
|
+
if (this.#parent) {
|
|
126
|
+
assertUsableAsParent(this.#parent, ctx);
|
|
95
127
|
}
|
|
96
128
|
this.context = ctx;
|
|
97
129
|
}
|
|
@@ -107,8 +139,16 @@ class ViewComponent {
|
|
|
107
139
|
}
|
|
108
140
|
}
|
|
109
141
|
addChild(child) {
|
|
110
|
-
if (child.#context
|
|
111
|
-
throw new ViewComponentError("cannot add a
|
|
142
|
+
if (child.#context == null) {
|
|
143
|
+
throw new ViewComponentError("cannot add a destroyed view component as a child");
|
|
144
|
+
}
|
|
145
|
+
assertUsableAsParent(this, child.#context);
|
|
146
|
+
for (let ancestor = this; ancestor != null; ancestor = ancestor.#parent) {
|
|
147
|
+
if (ancestor === child) {
|
|
148
|
+
throw new ViewComponentError(
|
|
149
|
+
"cannot add the component itself or one of its ancestors as a child: this would create a cycle"
|
|
150
|
+
);
|
|
151
|
+
}
|
|
112
152
|
}
|
|
113
153
|
if (!child.isChildOf(this)) {
|
|
114
154
|
child.removeFromParent();
|
|
@@ -116,19 +156,23 @@ class ViewComponent {
|
|
|
116
156
|
this.#context.addToChildren(this, child);
|
|
117
157
|
}
|
|
118
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* @returns `true` if the value differs from the last one written to the change trail.
|
|
161
|
+
* A destroyed component always returns `false`.
|
|
162
|
+
*/
|
|
119
163
|
setProperty(name, value, isEqual) {
|
|
120
|
-
this.#context
|
|
164
|
+
return this.#context?.setProperty(this, name, value, isEqual) ?? false;
|
|
121
165
|
}
|
|
122
166
|
removeProperty(name) {
|
|
123
|
-
this.#context
|
|
167
|
+
this.#context?.removeProperty(this, name);
|
|
124
168
|
}
|
|
125
169
|
dispatchShadowObjectsEvent(type, data, transferables) {
|
|
126
|
-
this.#context
|
|
170
|
+
this.#context?.dispatchShadowObjectsEvent(this, type, data, transferables);
|
|
127
171
|
}
|
|
128
172
|
dispatchEvent(type, data, traverseChildren) {
|
|
129
173
|
emit(this, type, data);
|
|
130
174
|
if (traverseChildren) {
|
|
131
|
-
for (const child of this.#context
|
|
175
|
+
for (const child of this.#context?.getChildren(this) ?? []) {
|
|
132
176
|
child.dispatchEvent(type, data, traverseChildren);
|
|
133
177
|
}
|
|
134
178
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/view/ViewComponent.ts"],
|
|
4
|
-
"sourcesContent": ["import {emit, eventize} from '@spearwolf/eventize';\nimport {VoidToken} from '../constants.js';\nimport {generateUUID} from '../utils/generateUUID.js';\nimport {ComponentContext} from './ComponentContext.js';\n\nclass ViewComponentError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ViewComponentError';\n }\n}\n\nexport class ViewComponent {\n readonly #uuid: string;\n\n #token: string;\n\n #context?: ComponentContext;\n\n #parent?: ViewComponent;\n #order = 0;\n readonly #autoDestructionOnParentRemoval: boolean;\n\n get uuid() {\n return this.#uuid;\n }\n\n get token() {\n return this.#token;\n }\n\n set token(token: string | undefined) {\n token ??= VoidToken;\n if (token === this.#token) return;\n this.#token = token;\n this.#context?.changeToken(this, token);\n }\n\n get parent(): ViewComponent | undefined {\n return this.#parent;\n }\n\n set parent(parent: ViewComponent | null | undefined) {\n if (parent) {\n
|
|
5
|
-
"mappings": "AAAA,SAAQ,MAAM,gBAAe;AAC7B,SAAQ,iBAAgB;AACxB,SAAQ,oBAAmB;AAC3B,SAAQ,
|
|
4
|
+
"sourcesContent": ["import {emit, eventize} from '@spearwolf/eventize';\nimport {VoidToken} from '../constants.js';\nimport {generateUUID} from '../utils/generateUUID.js';\nimport {ComponentContext, ComponentContextDisposedError} from './ComponentContext.js';\n\nclass ViewComponentError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ViewComponentError';\n }\n}\n\n/**\n * Guards every path that attaches a child to a parent: the constructor, {@link ViewComponent#addChild}\n * and the `parent` setter all funnel through here so that they fail with the same message.\n */\nfunction assertUsableAsParent(parent: ViewComponent, childContext: ComponentContext | undefined) {\n if (parent.context == null) {\n throw new ViewComponentError('cannot add a child to a destroyed view component');\n }\n if (parent.context !== childContext) {\n throw new ViewComponentError('cannot add a child from another context');\n }\n}\n\nexport class ViewComponent {\n readonly #uuid: string;\n\n #token: string;\n\n #context?: ComponentContext;\n\n #parent?: ViewComponent;\n #order = 0;\n readonly #autoDestructionOnParentRemoval: boolean;\n\n get uuid() {\n return this.#uuid;\n }\n\n get token() {\n return this.#token;\n }\n\n set token(token: string | undefined) {\n token ??= VoidToken;\n if (token === this.#token) return;\n this.#token = token;\n this.#context?.changeToken(this, token);\n }\n\n get parent(): ViewComponent | undefined {\n return this.#parent;\n }\n\n set parent(parent: ViewComponent | null | undefined) {\n if (parent) {\n parent.addChild(this);\n } else {\n this.removeFromParent();\n }\n }\n\n get context(): ComponentContext | undefined {\n return this.#context;\n }\n\n /**\n * @throws {ComponentContextDisposedError} if the new context has been disposed. The component\n * keeps its current context in that case: leaving the old one is only worth it if the new\n * one can actually be joined.\n */\n set context(context: ComponentContext | null | undefined) {\n const next = context ?? undefined;\n if (this.#context === next) return;\n\n // checked before the teardown below, so a rejected join costs the component nothing\n if (next?.isDisposed) {\n throw new ComponentContextDisposedError(\n `the view component ${this.#uuid} cannot join the component context because it has been disposed`,\n );\n }\n\n if (this.#context) {\n this.destroy();\n }\n\n this.#context = next;\n\n try {\n next?.addComponent(this);\n } catch (error) {\n // a failed join must not leave a destroyed component pointing at a context that never\n // took it in \u2014 every later mutation would silently go nowhere while `isDestroyed` lies\n this.#context = undefined;\n throw error;\n }\n }\n\n /**\n * A destroyed component is detached from its {@link ComponentContext}: it no longer appears\n * in any change trail and no longer has a corresponding entity.\n *\n * Every mutation that only concerns the component itself (`token`, `order`, properties, events,\n * `removeFromParent`, `destroy`) is silently ignored while destroyed. Operations that would tie\n * a second, live component to it (`addChild`, the `parent` setter) throw instead, because\n * ignoring them would leave the caller with a wrong picture of the entity tree.\n *\n * Assigning a {@link ViewComponent#context} revives the component under the same uuid.\n */\n get isDestroyed(): boolean {\n return this.#context == null;\n }\n\n /**\n * The order property sets the order to lay out a component in a children array of the parent component.\n *\n * Items in a children array are sorted by ascending order value and then by their insertion order.\n */\n get order(): number {\n return this.#order;\n }\n\n set order(order: number | null | undefined) {\n const prevOrder = this.#order;\n this.#order = order ?? 0;\n if (prevOrder !== this.#order) {\n this.#context?.changeOrder(this);\n }\n }\n\n /**\n * Whether the corresponding entity should be destroyed when its parent entity is destroyed.\n *\n * Set via the constructor option of the same name; immutable after creation.\n * Children that opt out (the default) are promoted to root entities when their\n * parent is destroyed, so they remain reachable.\n */\n get autoDestructionOnParentRemoval(): boolean {\n return this.#autoDestructionOnParentRemoval;\n }\n\n constructor(\n token: string,\n options?: {\n parent?: ViewComponent;\n order?: number;\n context?: ComponentContext;\n uuid?: string;\n autoDestructionOnParentRemoval?: boolean;\n },\n ) {\n eventize(this);\n\n if (options instanceof ViewComponent) {\n options = {parent: options};\n }\n\n this.#uuid = options?.uuid ?? generateUUID();\n\n this.#token = token ?? VoidToken;\n this.#order = options?.order ?? 0;\n this.#parent = options?.parent;\n this.#autoDestructionOnParentRemoval = options?.autoDestructionOnParentRemoval ?? false;\n\n const ctx = options?.context ?? ComponentContext.get();\n\n if (this.#parent) {\n assertUsableAsParent(this.#parent, ctx);\n }\n\n this.context = ctx;\n }\n\n isChildOf(parent: ViewComponent) {\n return this.#parent === parent;\n }\n\n removeFromParent() {\n if (this.#parent) {\n this.#context?.removeFromParent(this.uuid, this.#parent);\n this.#parent = undefined;\n } else {\n this.#context?.moveToRoot(this.uuid);\n }\n }\n\n addChild(child: ViewComponent) {\n if (child.#context == null) {\n throw new ViewComponentError('cannot add a destroyed view component as a child');\n }\n\n assertUsableAsParent(this, child.#context);\n\n // walking up via #parent is safe: the entity tree is kept acyclic by exactly this guard\n for (let ancestor: ViewComponent | undefined = this; ancestor != null; ancestor = ancestor.#parent) {\n if (ancestor === child) {\n throw new ViewComponentError(\n 'cannot add the component itself or one of its ancestors as a child: this would create a cycle',\n );\n }\n }\n\n if (!child.isChildOf(this)) {\n child.removeFromParent();\n child.#parent = this;\n this.#context!.addToChildren(this, child);\n }\n }\n\n /**\n * @returns `true` if the value differs from the last one written to the change trail.\n * A destroyed component always returns `false`.\n */\n setProperty<T = unknown>(name: string, value: T, isEqual?: (a: T, b: T) => boolean): boolean {\n return this.#context?.setProperty(this, name, value, isEqual) ?? false;\n }\n\n removeProperty(name: string) {\n this.#context?.removeProperty(this, name);\n }\n\n dispatchShadowObjectsEvent(type: string, data: unknown, transferables?: Transferable[]) {\n this.#context?.dispatchShadowObjectsEvent(this, type, data, transferables);\n }\n\n dispatchEvent(type: string, data: unknown, traverseChildren: boolean) {\n emit(this as ViewComponent, type, data);\n\n if (traverseChildren) {\n for (const child of this.#context?.getChildren(this) ?? []) {\n child.dispatchEvent(type, data, traverseChildren);\n }\n }\n }\n\n destroy() {\n this.removeFromParent();\n this.#context?.destroyComponent(this);\n this.#context = undefined;\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageRouter.d.ts","sourceRoot":"","sources":["../../../src/worker/MessageRouter.ts"],"names":[],"mappings":"AAYA,OAAO,EAAC,MAAM,EAAE,KAAK,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AAQzE,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,IAAI,CAAC,WAAW,CAAC;CACvC;AAED,qBAAa,aAAa;;IAGxB,MAAM,EAAE,MAAM,CAAC;IAEf,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,CAAC;gBAEzB,OAAO,CAAC,EAAE,oBAAoB;IAQ1C,KAAK,CAAC,KAAK,EAAE,YAAY;IAmBzB,eAAe,CAAC,KAAK,EAAE,kBAAkB;
|
|
1
|
+
{"version":3,"file":"MessageRouter.d.ts","sourceRoot":"","sources":["../../../src/worker/MessageRouter.ts"],"names":[],"mappings":"AAYA,OAAO,EAAC,MAAM,EAAE,KAAK,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AAQzE,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,IAAI,CAAC,WAAW,CAAC;CACvC;AAED,qBAAa,aAAa;;IAGxB,MAAM,EAAE,MAAM,CAAC;IAEf,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,CAAC;gBAEzB,OAAO,CAAC,EAAE,oBAAoB;IAQ1C,KAAK,CAAC,KAAK,EAAE,YAAY;IAmBzB,eAAe,CAAC,KAAK,EAAE,kBAAkB;CA+C1C"}
|
|
@@ -39,24 +39,26 @@ class MessageRouter {
|
|
|
39
39
|
this.postMessage({ type: MessageToView, data }, { transfer });
|
|
40
40
|
}
|
|
41
41
|
async #configure(data) {
|
|
42
|
+
const url = data.importModule;
|
|
42
43
|
try {
|
|
44
|
+
if (!url) throw new Error('missing "importModule" url');
|
|
43
45
|
const module = await import(
|
|
44
46
|
/* @vite-ignore */
|
|
45
|
-
toUrlString(
|
|
47
|
+
toUrlString(url)
|
|
46
48
|
);
|
|
47
49
|
if (module[ShadowObjectsExport]) {
|
|
48
50
|
await importModule(this.kernel, module[ShadowObjectsExport], this.#importedModules);
|
|
49
|
-
this.postMessage({ type: ImportedModule, url
|
|
51
|
+
this.postMessage({ type: ImportedModule, url });
|
|
50
52
|
} else {
|
|
51
53
|
this.postMessage({
|
|
52
54
|
type: ImportedModule,
|
|
53
|
-
url
|
|
55
|
+
url,
|
|
54
56
|
error: `module has no "${ShadowObjectsExport}" export`
|
|
55
57
|
});
|
|
56
58
|
}
|
|
57
59
|
} catch (error) {
|
|
58
60
|
console.error("[MessageRouter] failed to import module", error);
|
|
59
|
-
this.postMessage({ type: ImportedModule, url
|
|
61
|
+
this.postMessage({ type: ImportedModule, url, error: `${error}` });
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
#onChangeTrail(data) {
|
|
@@ -64,7 +66,7 @@ class MessageRouter {
|
|
|
64
66
|
this.kernel.run(data);
|
|
65
67
|
} catch (error) {
|
|
66
68
|
console.error("[MessageRouter] failed to apply change trail", error);
|
|
67
|
-
this.postMessage({ type: AppliedChangeTrail, serial: data.serial, error: error
|
|
69
|
+
this.postMessage({ type: AppliedChangeTrail, serial: data.serial, error: `${error}` });
|
|
68
70
|
}
|
|
69
71
|
if (data.serial) {
|
|
70
72
|
this.postMessage({ type: AppliedChangeTrail, serial: data.serial });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/worker/MessageRouter.ts"],
|
|
4
|
-
"sourcesContent": ["import {off, on} from '@spearwolf/eventize';\nimport {\n AppliedChangeTrail,\n ChangeTrail,\n Configure,\n Destroy,\n Destroyed,\n ImportedModule,\n MessageToView,\n ShadowObjectsExport,\n} from '../constants.js';\nimport {importModule} from '../in-the-dark/importModule.js';\nimport {Kernel, type MessageToViewEvent} from '../in-the-dark/Kernel.js';\nimport type {AppliedChangeTrailEvent, ImportedModuleEvent, ShadowObjectsModule, SyncEvent} from '../types.js';\nimport {toUrlString} from '../utils/toUrlString.js';\n\ninterface ConfigurePayloadData {\n importModule?: string;\n}\n\nexport interface MessageRouterOptions {\n kernel?: Kernel;\n postMessage?: typeof self.postMessage;\n}\n\nexport class MessageRouter {\n #importedModules: Set<ShadowObjectsModule> = new Set();\n\n kernel: Kernel;\n\n postMessage: typeof self.postMessage;\n\n constructor(options?: MessageRouterOptions) {\n this.kernel = options?.kernel ?? new Kernel();\n\n this.postMessage = options?.postMessage ?? self.postMessage.bind(self);\n\n on(this.kernel, MessageToView, 'onMessageToView', this);\n }\n\n route(event: MessageEvent) {\n switch (event.data.type) {\n case Configure:\n this.#configure(event.data);\n break;\n\n case ChangeTrail:\n this.#onChangeTrail(event.data);\n break;\n\n case Destroy:\n this.#onDestroy(event.data);\n break;\n\n default:\n console.warn('[MessageRouter] unknown message', event.data.type ?? event.data);\n }\n }\n\n onMessageToView(event: MessageToViewEvent) {\n const {transferables: transfer, ...data} = event;\n this.postMessage({type: MessageToView, data}, {transfer});\n }\n\n async #configure(data: ConfigurePayloadData) {\n try {\n const module = await import(/* @vite-ignore */ toUrlString(
|
|
5
|
-
"mappings": "AAAA,SAAQ,KAAK,UAAS;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAQ,oBAAmB;AAC3B,SAAQ,cAAsC;AAE9C,SAAQ,mBAAkB;AAWnB,MAAM,cAAc;AAAA,EACzB,mBAA6C,oBAAI,IAAI;AAAA,EAMrD,YAAY,SAAgC;AAC1C,SAAK,SAAS,SAAS,UAAU,IAAI,OAAO;AAE5C,SAAK,cAAc,SAAS,eAAe,KAAK,YAAY,KAAK,IAAI;AAErE,OAAG,KAAK,QAAQ,eAAe,mBAAmB,IAAI;AAAA,EACxD;AAAA,EAEA,MAAM,OAAqB;AACzB,YAAQ,MAAM,KAAK,MAAM;AAAA,MACvB,KAAK;AACH,aAAK,WAAW,MAAM,IAAI;AAC1B;AAAA,MAEF,KAAK;AACH,aAAK,eAAe,MAAM,IAAI;AAC9B;AAAA,MAEF,KAAK;AACH,aAAK,WAAW,MAAM,IAAI;AAC1B;AAAA,MAEF;AACE,gBAAQ,KAAK,mCAAmC,MAAM,KAAK,QAAQ,MAAM,IAAI;AAAA,IACjF;AAAA,EACF;AAAA,EAEA,gBAAgB,OAA2B;AACzC,UAAM,EAAC,eAAe,UAAU,GAAG,KAAI,IAAI;AAC3C,SAAK,YAAY,EAAC,MAAM,eAAe,KAAI,GAAG,EAAC,SAAQ,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,WAAW,MAA4B;AAC3C,QAAI;AACF,YAAM,SAAS,MAAM;AAAA;AAAA,QAA0B,YAAY,
|
|
4
|
+
"sourcesContent": ["import {off, on} from '@spearwolf/eventize';\nimport {\n AppliedChangeTrail,\n ChangeTrail,\n Configure,\n Destroy,\n Destroyed,\n ImportedModule,\n MessageToView,\n ShadowObjectsExport,\n} from '../constants.js';\nimport {importModule} from '../in-the-dark/importModule.js';\nimport {Kernel, type MessageToViewEvent} from '../in-the-dark/Kernel.js';\nimport type {AppliedChangeTrailEvent, ImportedModuleEvent, ShadowObjectsModule, SyncEvent} from '../types.js';\nimport {toUrlString} from '../utils/toUrlString.js';\n\ninterface ConfigurePayloadData {\n importModule?: string;\n}\n\nexport interface MessageRouterOptions {\n kernel?: Kernel;\n postMessage?: typeof self.postMessage;\n}\n\nexport class MessageRouter {\n #importedModules: Set<ShadowObjectsModule> = new Set();\n\n kernel: Kernel;\n\n postMessage: typeof self.postMessage;\n\n constructor(options?: MessageRouterOptions) {\n this.kernel = options?.kernel ?? new Kernel();\n\n this.postMessage = options?.postMessage ?? self.postMessage.bind(self);\n\n on(this.kernel, MessageToView, 'onMessageToView', this);\n }\n\n route(event: MessageEvent) {\n switch (event.data.type) {\n case Configure:\n this.#configure(event.data);\n break;\n\n case ChangeTrail:\n this.#onChangeTrail(event.data);\n break;\n\n case Destroy:\n this.#onDestroy(event.data);\n break;\n\n default:\n console.warn('[MessageRouter] unknown message', event.data.type ?? event.data);\n }\n }\n\n onMessageToView(event: MessageToViewEvent) {\n const {transferables: transfer, ...data} = event;\n this.postMessage({type: MessageToView, data}, {transfer});\n }\n\n async #configure(data: ConfigurePayloadData) {\n const url = data.importModule;\n try {\n if (!url) throw new Error('missing \"importModule\" url');\n const module = await import(/* @vite-ignore */ toUrlString(url));\n if (module[ShadowObjectsExport]) {\n await importModule(this.kernel, module[ShadowObjectsExport], this.#importedModules);\n this.postMessage({type: ImportedModule, url});\n } else {\n this.postMessage({\n type: ImportedModule,\n url,\n error: `module has no \"${ShadowObjectsExport}\" export`,\n } as ImportedModuleEvent);\n }\n } catch (error) {\n console.error('[MessageRouter] failed to import module', error);\n this.postMessage({type: ImportedModule, url, error: `${error}`} as ImportedModuleEvent);\n }\n }\n\n #onChangeTrail(data: SyncEvent) {\n // console.debug('[MessageRouter] parseChangeTrail', {data, kernel: this.kernel});\n\n try {\n this.kernel.run(data);\n } catch (error) {\n console.error('[MessageRouter] failed to apply change trail', error);\n this.postMessage({type: AppliedChangeTrail, serial: data.serial, error: `${error}`} as AppliedChangeTrailEvent);\n }\n\n if (data.serial) {\n this.postMessage({type: AppliedChangeTrail, serial: data.serial} as AppliedChangeTrailEvent);\n }\n }\n\n #onDestroy(data: any) {\n console.debug('[MessageRouter] on destroy', data);\n off(this.kernel, this);\n this.#importedModules.clear();\n this.postMessage({type: Destroyed});\n }\n}"],
|
|
5
|
+
"mappings": "AAAA,SAAQ,KAAK,UAAS;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAQ,oBAAmB;AAC3B,SAAQ,cAAsC;AAE9C,SAAQ,mBAAkB;AAWnB,MAAM,cAAc;AAAA,EACzB,mBAA6C,oBAAI,IAAI;AAAA,EAMrD,YAAY,SAAgC;AAC1C,SAAK,SAAS,SAAS,UAAU,IAAI,OAAO;AAE5C,SAAK,cAAc,SAAS,eAAe,KAAK,YAAY,KAAK,IAAI;AAErE,OAAG,KAAK,QAAQ,eAAe,mBAAmB,IAAI;AAAA,EACxD;AAAA,EAEA,MAAM,OAAqB;AACzB,YAAQ,MAAM,KAAK,MAAM;AAAA,MACvB,KAAK;AACH,aAAK,WAAW,MAAM,IAAI;AAC1B;AAAA,MAEF,KAAK;AACH,aAAK,eAAe,MAAM,IAAI;AAC9B;AAAA,MAEF,KAAK;AACH,aAAK,WAAW,MAAM,IAAI;AAC1B;AAAA,MAEF;AACE,gBAAQ,KAAK,mCAAmC,MAAM,KAAK,QAAQ,MAAM,IAAI;AAAA,IACjF;AAAA,EACF;AAAA,EAEA,gBAAgB,OAA2B;AACzC,UAAM,EAAC,eAAe,UAAU,GAAG,KAAI,IAAI;AAC3C,SAAK,YAAY,EAAC,MAAM,eAAe,KAAI,GAAG,EAAC,SAAQ,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,WAAW,MAA4B;AAC3C,UAAM,MAAM,KAAK;AACjB,QAAI;AACF,UAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4BAA4B;AACtD,YAAM,SAAS,MAAM;AAAA;AAAA,QAA0B,YAAY,GAAG;AAAA;AAC9D,UAAI,OAAO,mBAAmB,GAAG;AAC/B,cAAM,aAAa,KAAK,QAAQ,OAAO,mBAAmB,GAAG,KAAK,gBAAgB;AAClF,aAAK,YAAY,EAAC,MAAM,gBAAgB,IAAG,CAAC;AAAA,MAC9C,OAAO;AACL,aAAK,YAAY;AAAA,UACf,MAAM;AAAA,UACN;AAAA,UACA,OAAO,kBAAkB,mBAAmB;AAAA,QAC9C,CAAwB;AAAA,MAC1B;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,2CAA2C,KAAK;AAC9D,WAAK,YAAY,EAAC,MAAM,gBAAgB,KAAK,OAAO,GAAG,KAAK,GAAE,CAAwB;AAAA,IACxF;AAAA,EACF;AAAA,EAEA,eAAe,MAAiB;AAG9B,QAAI;AACF,WAAK,OAAO,IAAI,IAAI;AAAA,IACtB,SAAS,OAAO;AACd,cAAQ,MAAM,gDAAgD,KAAK;AACnE,WAAK,YAAY,EAAC,MAAM,oBAAoB,QAAQ,KAAK,QAAQ,OAAO,GAAG,KAAK,GAAE,CAA4B;AAAA,IAChH;AAEA,QAAI,KAAK,QAAQ;AACf,WAAK,YAAY,EAAC,MAAM,oBAAoB,QAAQ,KAAK,OAAM,CAA4B;AAAA,IAC7F;AAAA,EACF;AAAA,EAEA,WAAW,MAAW;AACpB,YAAQ,MAAM,8BAA8B,IAAI;AAChD,QAAI,KAAK,QAAQ,IAAI;AACrB,SAAK,iBAAiB,MAAM;AAC5B,SAAK,YAAY,EAAC,MAAM,UAAS,CAAC;AAAA,EACpC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|