@spearwolf/shadow-objects 0.31.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/README.md +49 -13
  3. package/bundle.js +9 -9
  4. package/package.json +3 -3
  5. package/src/elements/ShaeEntElement.d.ts +4 -4
  6. package/src/elements/ShaeEntElement.d.ts.map +1 -1
  7. package/src/elements/ShaeEntElement.js +4 -5
  8. package/src/elements/ShaeEntElement.js.map +2 -2
  9. package/src/elements/ShaePropElement.d.ts +5 -5
  10. package/src/elements/ShaePropElement.d.ts.map +1 -1
  11. package/src/elements/ShaePropElement.js +2 -2
  12. package/src/elements/ShaePropElement.js.map +2 -2
  13. package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
  14. package/src/elements/ShaeWorkerElement.js +31 -6
  15. package/src/elements/ShaeWorkerElement.js.map +2 -2
  16. package/src/in-the-dark/Kernel.d.ts +5 -0
  17. package/src/in-the-dark/Kernel.d.ts.map +1 -1
  18. package/src/in-the-dark/Kernel.js +49 -24
  19. package/src/in-the-dark/Kernel.js.map +2 -2
  20. package/src/in-the-dark/Registry.d.ts +1 -1
  21. package/src/in-the-dark/Registry.d.ts.map +1 -1
  22. package/src/in-the-dark/Registry.js +24 -20
  23. package/src/in-the-dark/Registry.js.map +2 -2
  24. package/src/in-the-dark/ShadowObject.d.ts +1 -1
  25. package/src/in-the-dark/ShadowObject.d.ts.map +1 -1
  26. package/src/in-the-dark/ShadowObject.js +2 -2
  27. package/src/in-the-dark/ShadowObject.js.map +2 -2
  28. package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
  29. package/src/in-the-dark/SignalsPath.js.map +2 -2
  30. package/src/in-the-dark/importModule.js +3 -3
  31. package/src/in-the-dark/importModule.js.map +2 -2
  32. package/src/types.d.ts +4 -4
  33. package/src/types.d.ts.map +1 -1
  34. package/src/utils/ConsoleLogger.d.ts.map +1 -1
  35. package/src/utils/ConsoleLogger.js +3 -2
  36. package/src/utils/ConsoleLogger.js.map +2 -2
  37. package/src/utils/FrameLoop.d.ts +1 -1
  38. package/src/utils/FrameLoop.d.ts.map +1 -1
  39. package/src/utils/FrameLoop.js +0 -2
  40. package/src/utils/FrameLoop.js.map +2 -2
  41. package/src/utils/attr-utils.d.ts.map +1 -1
  42. package/src/utils/attr-utils.js +1 -1
  43. package/src/utils/attr-utils.js.map +2 -2
  44. package/src/utils/generateUUID.d.ts +1 -1
  45. package/src/utils/generateUUID.d.ts.map +1 -1
  46. package/src/utils/props-utils.d.ts +1 -1
  47. package/src/utils/props-utils.d.ts.map +1 -1
  48. package/src/utils/waitForMessageOfType.d.ts +5 -1
  49. package/src/utils/waitForMessageOfType.d.ts.map +1 -1
  50. package/src/utils/waitForMessageOfType.js +12 -2
  51. package/src/utils/waitForMessageOfType.js.map +2 -2
  52. package/src/view/ComponentChanges.d.ts +4 -1
  53. package/src/view/ComponentChanges.d.ts.map +1 -1
  54. package/src/view/ComponentChanges.js +20 -6
  55. package/src/view/ComponentChanges.js.map +2 -2
  56. package/src/view/ComponentContext.d.ts +43 -1
  57. package/src/view/ComponentContext.d.ts.map +1 -1
  58. package/src/view/ComponentContext.js +123 -47
  59. package/src/view/ComponentContext.js.map +2 -2
  60. package/src/view/ComponentMemory.js +3 -1
  61. package/src/view/ComponentMemory.js.map +2 -2
  62. package/src/view/IShadowObjectEnvProxy.d.ts +6 -0
  63. package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
  64. package/src/view/LocalShadowObjectEnv.d.ts +1 -1
  65. package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
  66. package/src/view/LocalShadowObjectEnv.js +11 -6
  67. package/src/view/LocalShadowObjectEnv.js.map +2 -2
  68. package/src/view/RemoteWorkerEnv.d.ts +42 -0
  69. package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
  70. package/src/view/RemoteWorkerEnv.js +130 -18
  71. package/src/view/RemoteWorkerEnv.js.map +2 -2
  72. package/src/view/ShadowEnv.d.ts +25 -1
  73. package/src/view/ShadowEnv.d.ts.map +1 -1
  74. package/src/view/ShadowEnv.js +103 -29
  75. package/src/view/ShadowEnv.js.map +2 -2
  76. package/src/view/ViewComponent.d.ts +22 -1
  77. package/src/view/ViewComponent.d.ts.map +1 -1
  78. package/src/view/ViewComponent.js +66 -22
  79. package/src/view/ViewComponent.js.map +2 -2
  80. package/src/worker/MessageRouter.d.ts.map +1 -1
  81. package/src/worker/MessageRouter.js +7 -5
  82. package/src/worker/MessageRouter.js.map +2 -2
@@ -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, _syncIfScheduled, _ShadowEnv_instances, syncNow_fn, onMessageToView_fn;
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
- return this.isReady ? this : onceAsync(this, _ShadowEnv.ContextCreated);
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) && __privateGet(this, _comCtx).ns && globalThis.__shadowEnvs) {
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) && __privateGet(this, _comCtx).ns) {
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, onceAsync(this, _ShadowEnv.AfterSync).then((changeTrail) => {
168
- __privateSet(this, _afterNextSync, void 0);
169
- return changeTrail;
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
- if (ns) {
179
- if (globalThis.__shadowEnvs.has(ns)) {
180
- if (globalThis.__shadowEnvs.get(ns) === this) {
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
- _syncIfScheduled = new WeakMap();
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.isReady) {
206
- const data = this.view.buildChangeTrails();
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
- try {
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": "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",
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
- setProperty<T = unknown>(name: string, value: T, isEqual?: (a: T, b: T) => boolean): void;
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,EAAC,MAAM,uBAAuB,CAAC;AASvD,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,EASlD;IAED,IAAI,OAAO,IAAI,gBAAgB,GAAG,SAAS,CAE1C;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAUvD;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;IAW7B,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;IAIlF,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
+ {"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
- if (this.#context != context) {
48
- if (this.#context) {
49
- this.destroy();
50
- }
51
- this.#context = context;
52
- if (context) {
53
- context.addComponent(this);
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.changeOrder(this);
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 && this.#parent.#context !== ctx) {
94
- throw new ViewComponentError("cannot set parent from different context");
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 !== this.#context) {
111
- throw new ViewComponentError("cannot add a child from another context");
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.setProperty(this, name, value, isEqual);
164
+ return this.#context?.setProperty(this, name, value, isEqual) ?? false;
121
165
  }
122
166
  removeProperty(name) {
123
- this.#context.removeProperty(this, name);
167
+ this.#context?.removeProperty(this, name);
124
168
  }
125
169
  dispatchShadowObjectsEvent(type, data, transferables) {
126
- this.#context.dispatchShadowObjectsEvent(this, type, data, transferables);
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.getChildren(this)) {
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 if (parent.#context !== this.#context) {\n throw new ViewComponentError('cannot set parent from different context');\n }\n parent.addChild(this);\n } else {\n this.removeFromParent();\n }\n }\n\n get context(): ComponentContext | undefined {\n return this.#context;\n }\n\n set context(context: ComponentContext | null | undefined) {\n if (this.#context != context) {\n if (this.#context) {\n this.destroy();\n }\n this.#context = context;\n if (context) {\n context.addComponent(this);\n }\n }\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;\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 && this.#parent.#context !== ctx) {\n throw new ViewComponentError('cannot set parent from different context');\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 !== this.#context) {\n throw new ViewComponentError('cannot add a child from another context');\n }\n if (!child.isChildOf(this)) {\n child.removeFromParent();\n child.#parent = this;\n this.#context.addToChildren(this, child);\n }\n }\n\n setProperty<T = unknown>(name: string, value: T, isEqual?: (a: T, b: T) => boolean) {\n this.#context.setProperty(this, name, value, isEqual);\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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",
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;CA6C1C"}
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(data.importModule)
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: data.importModule });
51
+ this.postMessage({ type: ImportedModule, url });
50
52
  } else {
51
53
  this.postMessage({
52
54
  type: ImportedModule,
53
- url: data.importModule,
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: data.importModule, error: `${error}` });
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.toString() });
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(data.importModule));\n if (module[ShadowObjectsExport]) {\n await importModule(this.kernel, module[ShadowObjectsExport], this.#importedModules);\n this.postMessage({type: ImportedModule, url: data.importModule});\n } else {\n this.postMessage({\n type: ImportedModule,\n url: data.importModule,\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: data.importModule, 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.toString()} 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,QAAI;AACF,YAAM,SAAS,MAAM;AAAA;AAAA,QAA0B,YAAY,KAAK,YAAY;AAAA;AAC5E,UAAI,OAAO,mBAAmB,GAAG;AAC/B,cAAM,aAAa,KAAK,QAAQ,OAAO,mBAAmB,GAAG,KAAK,gBAAgB;AAClF,aAAK,YAAY,EAAC,MAAM,gBAAgB,KAAK,KAAK,aAAY,CAAC;AAAA,MACjE,OAAO;AACL,aAAK,YAAY;AAAA,UACf,MAAM;AAAA,UACN,KAAK,KAAK;AAAA,UACV,OAAO,kBAAkB,mBAAmB;AAAA,QAC9C,CAAwB;AAAA,MAC1B;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,2CAA2C,KAAK;AAC9D,WAAK,YAAY,EAAC,MAAM,gBAAgB,KAAK,KAAK,cAAc,OAAO,GAAG,KAAK,GAAE,CAAwB;AAAA,IAC3G;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,MAAM,SAAS,EAAC,CAA4B;AAAA,IACtH;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;",
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
  }