@spearwolf/shadow-objects 0.32.0 → 0.34.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 +333 -10
- package/README.md +100 -20
- package/bundle.js +5 -54
- package/package.json +7 -3
- package/src/ChangeTrailRefusedError.d.ts +27 -0
- package/src/ChangeTrailRefusedError.d.ts.map +1 -0
- package/src/ChangeTrailRefusedError.js +12 -0
- package/src/ChangeTrailRefusedError.js.map +7 -0
- package/src/EntityUuidInUseError.d.ts +19 -0
- package/src/EntityUuidInUseError.d.ts.map +1 -0
- package/src/EntityUuidInUseError.js +11 -0
- package/src/EntityUuidInUseError.js.map +7 -0
- package/src/WorkerTimeoutError.d.ts +25 -0
- package/src/WorkerTimeoutError.d.ts.map +1 -0
- package/src/WorkerTimeoutError.js +12 -0
- package/src/WorkerTimeoutError.js.map +7 -0
- package/src/bundle.d.ts +1 -1
- package/src/bundle.d.ts.map +1 -1
- package/src/bundle.js +1 -1
- package/src/bundle.js.map +2 -2
- package/src/constants.d.ts +9 -2
- package/src/constants.d.ts.map +1 -1
- package/src/constants.js +1 -1
- package/src/constants.js.map +2 -2
- package/src/create-worker.bundle.d.ts +1 -1
- package/src/create-worker.bundle.d.ts.map +1 -1
- package/src/create-worker.bundle.js.map +1 -1
- package/src/create-worker.d.ts +1 -1
- package/src/create-worker.d.ts.map +1 -1
- package/src/create-worker.js.map +1 -1
- package/src/elements/ShaeElement.d.ts +70 -2
- package/src/elements/ShaeElement.d.ts.map +1 -1
- package/src/elements/ShaeElement.js +129 -25
- package/src/elements/ShaeElement.js.map +3 -3
- package/src/elements/ShaeEntElement.d.ts +43 -5
- package/src/elements/ShaeEntElement.d.ts.map +1 -1
- package/src/elements/ShaeEntElement.js +399 -170
- package/src/elements/ShaeEntElement.js.map +2 -2
- package/src/elements/ShaeLifecycleElement.d.ts +71 -0
- package/src/elements/ShaeLifecycleElement.d.ts.map +1 -0
- package/src/elements/ShaeLifecycleElement.js +83 -0
- package/src/elements/ShaeLifecycleElement.js.map +7 -0
- package/src/elements/ShaePropElement.d.ts +57 -7
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +241 -220
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts +49 -5
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +230 -74
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/elements/constants.d.ts +6 -0
- package/src/elements/constants.d.ts.map +1 -1
- package/src/elements/constants.js +12 -0
- package/src/elements/constants.js.map +2 -2
- package/src/elements/deferredTeardown.d.ts +24 -0
- package/src/elements/deferredTeardown.d.ts.map +1 -0
- package/src/elements/deferredTeardown.js +30 -0
- package/src/elements/deferredTeardown.js.map +7 -0
- package/src/elements/displayContentsRule.d.ts +25 -0
- package/src/elements/displayContentsRule.d.ts.map +1 -0
- package/src/elements/displayContentsRule.js +37 -0
- package/src/elements/displayContentsRule.js.map +7 -0
- package/src/elements/events.d.ts +10 -7
- package/src/elements/events.d.ts.map +1 -1
- package/src/elements/forwardCustomEvents.d.ts +19 -0
- package/src/elements/forwardCustomEvents.d.ts.map +1 -0
- package/src/elements/forwardCustomEvents.js +47 -0
- package/src/elements/forwardCustomEvents.js.map +7 -0
- package/src/elements/hostedSlots.d.ts +36 -0
- package/src/elements/hostedSlots.d.ts.map +1 -0
- package/src/elements/hostedSlots.js +118 -0
- package/src/elements/hostedSlots.js.map +7 -0
- package/src/elements/parentRemoval.d.ts +18 -0
- package/src/elements/parentRemoval.d.ts.map +1 -0
- package/src/elements/parentRemoval.js +60 -0
- package/src/elements/parentRemoval.js.map +7 -0
- package/src/elements/propValueConverters.d.ts +3 -0
- package/src/elements/propValueConverters.d.ts.map +1 -0
- package/src/elements/propValueConverters.js +54 -0
- package/src/elements/propValueConverters.js.map +7 -0
- package/src/elements/requestEntAncestor.d.ts +32 -0
- package/src/elements/requestEntAncestor.d.ts.map +1 -0
- package/src/elements/requestEntAncestor.js +16 -0
- package/src/elements/requestEntAncestor.js.map +7 -0
- package/src/elements.js.map +1 -1
- package/src/in-the-dark/Entity.d.ts +113 -3
- package/src/in-the-dark/Entity.d.ts.map +1 -1
- package/src/in-the-dark/Entity.js +292 -63
- package/src/in-the-dark/Entity.js.map +3 -3
- package/src/in-the-dark/Kernel.d.ts +81 -17
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +449 -392
- package/src/in-the-dark/Kernel.js.map +3 -3
- 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 +99 -26
- 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 +3 -2
- package/src/in-the-dark/ShadowObject.js.map +2 -2
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts +126 -0
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js +596 -0
- package/src/in-the-dark/ShadowObjectCreationScope.js.map +7 -0
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js +2 -2
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- package/src/in-the-dark/events.js.map +1 -1
- package/src/in-the-dark/importModule.d.ts +7 -0
- package/src/in-the-dark/importModule.d.ts.map +1 -1
- package/src/in-the-dark/importModule.js +8 -5
- package/src/in-the-dark/importModule.js.map +2 -2
- package/src/index.d.ts +7 -1
- package/src/index.d.ts.map +1 -1
- package/src/index.js +5 -1
- package/src/index.js.map +2 -2
- package/src/shadow-objects.d.ts +2 -0
- package/src/shadow-objects.d.ts.map +1 -1
- package/src/shadow-objects.js +2 -0
- package/src/shadow-objects.js.map +2 -2
- package/src/shadow-objects.worker.js +0 -1
- package/src/shadow-objects.worker.js.map +2 -2
- package/src/shae-ent.js.map +1 -1
- package/src/shae-prop.js +2 -2
- package/src/shae-prop.js.map +2 -2
- package/src/shae-worker.js.map +1 -1
- package/src/types.d.ts +39 -8
- package/src/types.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.d.ts +45 -10
- package/src/utils/ConsoleLogger.d.ts.map +1 -1
- package/src/utils/ConsoleLogger.js +54 -28
- package/src/utils/ConsoleLogger.js.map +2 -2
- package/src/utils/FrameLoop.d.ts +42 -3
- package/src/utils/FrameLoop.d.ts.map +1 -1
- package/src/utils/FrameLoop.js +71 -12
- package/src/utils/FrameLoop.js.map +2 -2
- package/src/utils/MicrotaskCollector.d.ts +45 -0
- package/src/utils/MicrotaskCollector.d.ts.map +1 -0
- package/src/utils/MicrotaskCollector.js +33 -0
- package/src/utils/MicrotaskCollector.js.map +7 -0
- package/src/utils/MicrotaskGate.d.ts +17 -0
- package/src/utils/MicrotaskGate.d.ts.map +1 -0
- package/src/utils/MicrotaskGate.js +20 -0
- package/src/utils/MicrotaskGate.js.map +7 -0
- package/src/utils/array-utils.js.map +1 -1
- package/src/utils/attr-utils.d.ts +6 -0
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +4 -2
- package/src/utils/attr-utils.js.map +2 -2
- package/src/utils/constants.js.map +1 -1
- package/src/utils/generateUUID.d.ts +12 -1
- package/src/utils/generateUUID.d.ts.map +1 -1
- package/src/utils/generateUUID.js +31 -266
- package/src/utils/generateUUID.js.map +2 -2
- package/src/utils/props-utils.d.ts +3 -2
- package/src/utils/props-utils.d.ts.map +1 -1
- package/src/utils/props-utils.js +13 -24
- package/src/utils/props-utils.js.map +2 -2
- package/src/utils/runGuarded.d.ts +27 -0
- package/src/utils/runGuarded.d.ts.map +1 -0
- package/src/utils/runGuarded.js +11 -0
- package/src/utils/runGuarded.js.map +7 -0
- package/src/utils/toMaybe.d.ts.map +1 -1
- package/src/utils/toMaybe.js.map +1 -1
- package/src/utils/toNamespace.d.ts.map +1 -1
- package/src/utils/toNamespace.js.map +1 -1
- package/src/utils/toUrlString.d.ts +12 -0
- package/src/utils/toUrlString.d.ts.map +1 -1
- package/src/utils/toUrlString.js.map +2 -2
- package/src/utils/waitForMessageOfType.d.ts +16 -1
- package/src/utils/waitForMessageOfType.d.ts.map +1 -1
- package/src/utils/waitForMessageOfType.js +28 -3
- package/src/utils/waitForMessageOfType.js.map +2 -2
- package/src/view/ComponentChanges.d.ts +91 -1
- package/src/view/ComponentChanges.d.ts.map +1 -1
- package/src/view/ComponentChanges.js +253 -69
- package/src/view/ComponentChanges.js.map +2 -2
- package/src/view/ComponentContext.d.ts +239 -7
- package/src/view/ComponentContext.d.ts.map +1 -1
- package/src/view/ComponentContext.js +648 -115
- package/src/view/ComponentContext.js.map +2 -2
- package/src/view/ComponentMemory.d.ts +3 -3
- package/src/view/ComponentMemory.d.ts.map +1 -1
- package/src/view/ComponentMemory.js +3 -1
- package/src/view/ComponentMemory.js.map +2 -2
- package/src/view/IShadowObjectEnvProxy.d.ts +33 -1
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +2 -2
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +18 -11
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +129 -3
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +290 -60
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +73 -2
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +301 -64
- package/src/view/ShadowEnv.js.map +2 -2
- package/src/view/ViewComponent.d.ts +93 -8
- package/src/view/ViewComponent.d.ts.map +1 -1
- package/src/view/ViewComponent.js +148 -28
- package/src/view/ViewComponent.js.map +2 -2
- package/src/view/cloneChangeTrail.js.map +2 -2
- package/src/worker/MessageRouter.d.ts +14 -0
- package/src/worker/MessageRouter.d.ts.map +1 -1
- package/src/worker/MessageRouter.js +80 -18
- package/src/worker/MessageRouter.js.map +2 -2
- package/src/worker/WorkerRuntime.d.ts +22 -1
- package/src/worker/WorkerRuntime.d.ts.map +1 -1
- package/src/worker/WorkerRuntime.js +60 -8
- package/src/worker/WorkerRuntime.js.map +2 -2
package/src/view/ShadowEnv.js
CHANGED
|
@@ -46,12 +46,33 @@ 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
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
var __privateWrapper = (obj, member, setter, getter) => ({
|
|
50
|
+
set _(value) {
|
|
51
|
+
__privateSet(obj, member, value, setter);
|
|
52
|
+
},
|
|
53
|
+
get _() {
|
|
54
|
+
return __privateGet(obj, member, getter);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
var _proxyReady_dec, _viewReady_dec, _comCtx, _shaObjEnvProxy, _syncScheduled, _syncAfterContextCreated, _syncWaitForConfirmation, _nextSyncCycle, _logger, _init, _viewReady, _proxyReady, _isDestroyed, _contextEffect, _ShadowEnv_instances, ensureContextEffect_fn, releaseNamespace_fn, _proxyGeneration, _whenDestroyed, _rejectWhenDestroyed, destroyedSignal_fn, openSyncCycle_fn, _syncIfScheduled, syncNow_fn, commitSyncCycle_fn, announceContext_fn, endSyncCycle_fn, onMessageToView_fn, onProxyFailed_fn;
|
|
58
|
+
import { emitStrict, off, on, onceAsync, Priority, retain, retainClear } from "@spearwolf/eventize";
|
|
59
|
+
import {
|
|
60
|
+
createEffect,
|
|
61
|
+
createSignal,
|
|
62
|
+
destroyObjectSignals,
|
|
63
|
+
findObjectSignalByName,
|
|
64
|
+
hibernate
|
|
65
|
+
} from "@spearwolf/signalize";
|
|
52
66
|
import { signal } from "@spearwolf/signalize/decorators";
|
|
67
|
+
import { ChangeTrailRefusedError } from "../ChangeTrailRefusedError.js";
|
|
53
68
|
import { ConsoleLogger } from "../utils/ConsoleLogger.js";
|
|
54
69
|
import { ComponentContext } from "./ComponentContext.js";
|
|
70
|
+
class ShadowEnvDestroyedError extends Error {
|
|
71
|
+
constructor(message = "the shadow environment has been destroyed") {
|
|
72
|
+
super(message);
|
|
73
|
+
this.name = "ShadowEnvDestroyedError";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
55
76
|
_viewReady_dec = [signal()], _proxyReady_dec = [signal()];
|
|
56
77
|
const _ShadowEnv = class _ShadowEnv {
|
|
57
78
|
constructor() {
|
|
@@ -61,14 +82,33 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
61
82
|
__privateAdd(this, _syncScheduled, false);
|
|
62
83
|
__privateAdd(this, _syncAfterContextCreated, false);
|
|
63
84
|
__privateAdd(this, _syncWaitForConfirmation, false);
|
|
64
|
-
__privateAdd(this,
|
|
65
|
-
this
|
|
85
|
+
__privateAdd(this, _nextSyncCycle);
|
|
86
|
+
__privateAdd(this, _logger, new ConsoleLogger("ShadowEnv"));
|
|
87
|
+
/**
|
|
88
|
+
* The namespace of the {@link ComponentContext} this environment observes, and `undefined`
|
|
89
|
+
* while it observes none. The `view` setter writes it, so it carries the name
|
|
90
|
+
* {@link ShadowEnv.get} finds this environment under, unless another environment has since
|
|
91
|
+
* taken that namespace over; a `destroy()` leaves it on `undefined`.
|
|
92
|
+
*/
|
|
66
93
|
this.ns$ = createSignal();
|
|
67
94
|
__privateAdd(this, _viewReady, __runInitializers(_init, 8, this, false)), __runInitializers(_init, 11, this);
|
|
68
95
|
__privateAdd(this, _proxyReady, __runInitializers(_init, 12, this, false)), __runInitializers(_init, 15, this);
|
|
69
96
|
__privateAdd(this, _isDestroyed, false);
|
|
97
|
+
__privateAdd(this, _contextEffect);
|
|
98
|
+
// Each assignment to `envProxy` opens a generation. A start that finishes outside the generation
|
|
99
|
+
// it belongs to speaks for a proxy this environment has already let go, and is discarded.
|
|
100
|
+
__privateAdd(this, _proxyGeneration, 0);
|
|
101
|
+
__privateAdd(this, _whenDestroyed);
|
|
102
|
+
__privateAdd(this, _rejectWhenDestroyed);
|
|
103
|
+
/**
|
|
104
|
+
* Resolves once the environment is ready.
|
|
105
|
+
*
|
|
106
|
+
* @throws {ShadowEnvDestroyedError} if the environment is destroyed before that happens
|
|
107
|
+
*/
|
|
70
108
|
this.ready = async () => {
|
|
71
|
-
|
|
109
|
+
if (__privateGet(this, _isDestroyed)) throw new ShadowEnvDestroyedError();
|
|
110
|
+
if (this.isReady) return this;
|
|
111
|
+
return Promise.race([onceAsync(this, _ShadowEnv.ContextCreated), __privateMethod(this, _ShadowEnv_instances, destroyedSignal_fn).call(this)]);
|
|
72
112
|
};
|
|
73
113
|
__privateAdd(this, _syncIfScheduled, () => {
|
|
74
114
|
if (__privateGet(this, _syncScheduled)) {
|
|
@@ -77,27 +117,18 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
77
117
|
});
|
|
78
118
|
const self = this;
|
|
79
119
|
retain(self, _ShadowEnv.ContextCreated);
|
|
80
|
-
on(self, _ShadowEnv.ContextLost, Priority.
|
|
120
|
+
on(self, _ShadowEnv.ContextLost, Priority.Critical, () => {
|
|
81
121
|
retainClear(self, _ShadowEnv.ContextCreated);
|
|
82
122
|
});
|
|
83
|
-
createEffect(() => {
|
|
84
|
-
if (this.viewReady && this.proxyReady) {
|
|
85
|
-
this.view.reCreateChanges();
|
|
86
|
-
emit(self, _ShadowEnv.ContextCreated, self);
|
|
87
|
-
if (__privateGet(this, _syncAfterContextCreated)) {
|
|
88
|
-
__privateSet(this, _syncAfterContextCreated, false);
|
|
89
|
-
__privateMethod(this, _ShadowEnv_instances, syncNow_fn).call(this);
|
|
90
|
-
}
|
|
91
|
-
return () => {
|
|
92
|
-
emit(self, _ShadowEnv.ContextLost, self);
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}, [findObjectSignalByName(this, "viewReady"), findObjectSignalByName(this, "proxyReady")]);
|
|
96
123
|
}
|
|
97
124
|
static get(ns) {
|
|
98
125
|
if (ns == null) return void 0;
|
|
99
126
|
return globalThis.__shadowEnvs?.get(ns);
|
|
100
127
|
}
|
|
128
|
+
/** The logger this environment reports through. */
|
|
129
|
+
get logger() {
|
|
130
|
+
return __privateGet(this, _logger);
|
|
131
|
+
}
|
|
101
132
|
get isDestroyed() {
|
|
102
133
|
return __privateGet(this, _isDestroyed);
|
|
103
134
|
}
|
|
@@ -106,23 +137,17 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
106
137
|
}
|
|
107
138
|
set view(ctx) {
|
|
108
139
|
if (ctx !== __privateGet(this, _comCtx)) {
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
}
|
|
140
|
+
if (ctx) __privateMethod(this, _ShadowEnv_instances, ensureContextEffect_fn).call(this);
|
|
141
|
+
__privateMethod(this, _ShadowEnv_instances, releaseNamespace_fn).call(this, __privateGet(this, _comCtx)?.ns);
|
|
112
142
|
__privateSet(this, _comCtx, ctx ?? void 0);
|
|
113
|
-
if (__privateGet(this, _comCtx)
|
|
143
|
+
if (__privateGet(this, _comCtx)?.ns) {
|
|
114
144
|
globalThis.__shadowEnvs ??= /* @__PURE__ */ new Map();
|
|
115
145
|
if (globalThis.__shadowEnvs.has(__privateGet(this, _comCtx).ns) && globalThis.__shadowEnvs.get(__privateGet(this, _comCtx).ns) !== this) {
|
|
116
|
-
|
|
117
|
-
this.logger.warn(
|
|
118
|
-
"overwrite a namespace already in use",
|
|
119
|
-
__privateGet(this, _comCtx).ns,
|
|
120
|
-
globalThis.__shadowEnvs.get(__privateGet(this, _comCtx).ns)
|
|
121
|
-
);
|
|
122
|
-
}
|
|
146
|
+
this.logger.warn("overwrite a namespace already in use", __privateGet(this, _comCtx).ns, globalThis.__shadowEnvs.get(__privateGet(this, _comCtx).ns));
|
|
123
147
|
}
|
|
124
148
|
globalThis.__shadowEnvs.set(__privateGet(this, _comCtx).ns, this);
|
|
125
149
|
}
|
|
150
|
+
this.ns$.set(__privateGet(this, _comCtx)?.ns);
|
|
126
151
|
this.viewReady = Boolean(ctx);
|
|
127
152
|
}
|
|
128
153
|
}
|
|
@@ -131,18 +156,27 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
131
156
|
}
|
|
132
157
|
set envProxy(proxy) {
|
|
133
158
|
if (proxy !== __privateGet(this, _shaObjEnvProxy)) {
|
|
159
|
+
if (proxy) __privateMethod(this, _ShadowEnv_instances, ensureContextEffect_fn).call(this);
|
|
134
160
|
const prevProxy = __privateGet(this, _shaObjEnvProxy);
|
|
135
161
|
__privateSet(this, _shaObjEnvProxy, proxy ?? void 0);
|
|
162
|
+
const generation = ++__privateWrapper(this, _proxyGeneration)._;
|
|
136
163
|
if (__privateGet(this, _shaObjEnvProxy)) {
|
|
137
164
|
__privateGet(this, _shaObjEnvProxy).onMessageToView = __privateMethod(this, _ShadowEnv_instances, onMessageToView_fn).bind(this);
|
|
165
|
+
__privateGet(this, _shaObjEnvProxy).onProxyFailed = __privateMethod(this, _ShadowEnv_instances, onProxyFailed_fn).bind(this);
|
|
138
166
|
}
|
|
139
167
|
if (prevProxy) {
|
|
140
168
|
prevProxy.destroy();
|
|
169
|
+
prevProxy.onProxyFailed = void 0;
|
|
170
|
+
queueMicrotask(() => {
|
|
171
|
+
prevProxy.onMessageToView = void 0;
|
|
172
|
+
});
|
|
141
173
|
}
|
|
142
174
|
this.proxyReady = false;
|
|
143
175
|
proxy?.start().then(() => {
|
|
176
|
+
if (generation !== __privateGet(this, _proxyGeneration)) return;
|
|
144
177
|
this.proxyReady = true;
|
|
145
178
|
}).catch((error) => {
|
|
179
|
+
if (generation !== __privateGet(this, _proxyGeneration)) return;
|
|
146
180
|
this.logger.error("failed to start envProxy", error);
|
|
147
181
|
this.proxyReady = false;
|
|
148
182
|
});
|
|
@@ -151,7 +185,23 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
151
185
|
get isReady() {
|
|
152
186
|
return Boolean(__privateGet(this, _comCtx) && __privateGet(this, _shaObjEnvProxy) && this.proxyReady && !this.isDestroyed);
|
|
153
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Send the change trail without asking the Shadow Environment to confirm it --
|
|
190
|
+
* {@link ShadowEnv.syncWait} is the call that asks.
|
|
191
|
+
*
|
|
192
|
+
* What becomes of a refusal is then the proxy's decision, and the two shipped ones differ.
|
|
193
|
+
* `LocalShadowObjectEnv` runs the Kernel synchronously, inside the call the environment makes to
|
|
194
|
+
* the proxy -- one microtask after `sync()` returned, not inside the call to `sync()` itself --
|
|
195
|
+
* and rejects with what it threw, so a refusal reaches {@link ShadowEnv.SyncFailed} here as well,
|
|
196
|
+
* carrying its number.
|
|
197
|
+
* `RemoteWorkerEnv` sends no serial without a confirmation and gets no answer back, so a
|
|
198
|
+
* refusal stays in the worker: it is written to the console there, `SyncFailed` stays silent, and
|
|
199
|
+
* the whole trail is booked as applied.
|
|
200
|
+
*
|
|
201
|
+
* {@link ShadowEnv.syncWait} is the way both proxies answer on.
|
|
202
|
+
*/
|
|
154
203
|
sync() {
|
|
204
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
155
205
|
if (!this.isReady) {
|
|
156
206
|
__privateSet(this, _syncAfterContextCreated, true);
|
|
157
207
|
return;
|
|
@@ -160,31 +210,63 @@ const _ShadowEnv = class _ShadowEnv {
|
|
|
160
210
|
__privateSet(this, _syncScheduled, true);
|
|
161
211
|
queueMicrotask(__privateGet(this, _syncIfScheduled));
|
|
162
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Like {@link ShadowEnv.sync}, but resolves with the change trail once the cycle completed.
|
|
215
|
+
*
|
|
216
|
+
* A cycle whose change trail the Shadow Environment could not apply rejects instead, with the
|
|
217
|
+
* reason the proxy gave. Where the Kernel itself refused the trail that reason is a
|
|
218
|
+
* {@link ChangeTrailRefusedError}: it names how many entries the Kernel applied, this side folds
|
|
219
|
+
* exactly those into its bookkeeping, and everything behind that line stays pending and goes out
|
|
220
|
+
* again with the next cycle. A trail whose cause of refusal stays put is refused every time, which
|
|
221
|
+
* is what {@link ShadowEnv.SyncFailed} is the place to act on.
|
|
222
|
+
*
|
|
223
|
+
* A reason that says nothing about how far the Kernel got -- a confirmation window that ran out,
|
|
224
|
+
* a proxy whose environment is gone -- counts the whole trail as applied, because a Shadow
|
|
225
|
+
* Environment that fell silent may well hold all of it. Then the trail is gone, and only a
|
|
226
|
+
* re-creation from the Component Memory brings it back. That re-creation belongs to a fresh
|
|
227
|
+
* proxy: an environment that still holds the entities refuses a creation for a uuid it already
|
|
228
|
+
* has. Handing {@link ShadowEnv.envProxy} a new proxy is therefore the way back -- the Shadow
|
|
229
|
+
* Environment calls {@link ComponentContext.reCreateChanges} itself once the new proxy is ready.
|
|
230
|
+
* Making that call is the consumer's decision, the same way recovering from a
|
|
231
|
+
* {@link ShadowEnv.ProxyFailed} is.
|
|
232
|
+
*
|
|
233
|
+
* Which cycle a caller gets is decided when the change trail is built. Everyone who arrives
|
|
234
|
+
* before that point waits on the same promise -- they all ride the same trail. From the build
|
|
235
|
+
* on the trail is fixed, and a call after it belongs to the next cycle, the one that carries
|
|
236
|
+
* the changes made since. That holds inside a listener of {@link ShadowEnv.AfterSync} or
|
|
237
|
+
* {@link ShadowEnv.SyncFailed} as well: the cycle it was told about is over, so the call opens
|
|
238
|
+
* the one behind it.
|
|
239
|
+
*
|
|
240
|
+
* @throws {ShadowEnvDestroyedError} if the environment is destroyed before the cycle completes
|
|
241
|
+
*/
|
|
163
242
|
syncWait() {
|
|
243
|
+
if (__privateGet(this, _isDestroyed)) return Promise.reject(new ShadowEnvDestroyedError());
|
|
164
244
|
__privateSet(this, _syncWaitForConfirmation, true);
|
|
165
245
|
this.sync();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
__privateSet(this, _afterNextSync, void 0);
|
|
169
|
-
return changeTrail;
|
|
170
|
-
}));
|
|
171
|
-
return __privateGet(this, _afterNextSync);
|
|
246
|
+
__privateGet(this, _nextSyncCycle) ?? __privateSet(this, _nextSyncCycle, __privateMethod(this, _ShadowEnv_instances, openSyncCycle_fn).call(this));
|
|
247
|
+
return __privateGet(this, _nextSyncCycle).promise;
|
|
172
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Tear the environment down: the proxy is destroyed, the namespace is released, all signals
|
|
251
|
+
* and listeners are removed, and every caller still waiting on {@link ShadowEnv.ready} or
|
|
252
|
+
* {@link ShadowEnv.syncWait} is rejected with a {@link ShadowEnvDestroyedError} instead of
|
|
253
|
+
* being left pending forever. Calling it more than once is a no-op.
|
|
254
|
+
*/
|
|
173
255
|
destroy() {
|
|
174
|
-
|
|
175
|
-
this
|
|
256
|
+
var _a;
|
|
257
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
258
|
+
__privateSet(this, _isDestroyed, true);
|
|
259
|
+
__privateSet(this, _syncScheduled, false);
|
|
260
|
+
__privateSet(this, _syncAfterContextCreated, false);
|
|
261
|
+
__privateSet(this, _syncWaitForConfirmation, false);
|
|
176
262
|
this.envProxy = void 0;
|
|
177
263
|
this.view = void 0;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
264
|
+
(_a = __privateGet(this, _rejectWhenDestroyed)) == null ? void 0 : _a.call(this, new ShadowEnvDestroyedError());
|
|
265
|
+
__privateSet(this, _nextSyncCycle, void 0);
|
|
266
|
+
__privateGet(this, _contextEffect)?.destroy();
|
|
267
|
+
__privateSet(this, _contextEffect, void 0);
|
|
185
268
|
destroyObjectSignals(this);
|
|
186
269
|
off(this);
|
|
187
|
-
__privateSet(this, _isDestroyed, true);
|
|
188
270
|
Object.freeze(this);
|
|
189
271
|
}
|
|
190
272
|
};
|
|
@@ -194,43 +276,198 @@ _shaObjEnvProxy = new WeakMap();
|
|
|
194
276
|
_syncScheduled = new WeakMap();
|
|
195
277
|
_syncAfterContextCreated = new WeakMap();
|
|
196
278
|
_syncWaitForConfirmation = new WeakMap();
|
|
197
|
-
|
|
279
|
+
_nextSyncCycle = new WeakMap();
|
|
280
|
+
_logger = new WeakMap();
|
|
198
281
|
_viewReady = new WeakMap();
|
|
199
282
|
_proxyReady = new WeakMap();
|
|
200
283
|
_isDestroyed = new WeakMap();
|
|
201
|
-
|
|
284
|
+
_contextEffect = new WeakMap();
|
|
202
285
|
_ShadowEnv_instances = new WeakSet();
|
|
286
|
+
/**
|
|
287
|
+
* Builds the effect that reports {@link ShadowEnv.ContextCreated} and {@link ShadowEnv.ContextLost}
|
|
288
|
+
* once, on the first half of the environment that arrives.
|
|
289
|
+
*/
|
|
290
|
+
ensureContextEffect_fn = function() {
|
|
291
|
+
if (__privateGet(this, _isDestroyed) || __privateGet(this, _contextEffect) != null) return;
|
|
292
|
+
const self = this;
|
|
293
|
+
hibernate(() => {
|
|
294
|
+
__privateSet(this, _contextEffect, createEffect(() => {
|
|
295
|
+
if (this.viewReady && this.proxyReady) {
|
|
296
|
+
this.view.reCreateChanges();
|
|
297
|
+
__privateMethod(this, _ShadowEnv_instances, announceContext_fn).call(this, _ShadowEnv.ContextCreated, self);
|
|
298
|
+
if (__privateGet(this, _syncAfterContextCreated)) {
|
|
299
|
+
__privateSet(this, _syncAfterContextCreated, false);
|
|
300
|
+
__privateMethod(this, _ShadowEnv_instances, syncNow_fn).call(this);
|
|
301
|
+
}
|
|
302
|
+
return () => {
|
|
303
|
+
__privateMethod(this, _ShadowEnv_instances, announceContext_fn).call(this, _ShadowEnv.ContextLost, self);
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}, [findObjectSignalByName(this, "viewReady"), findObjectSignalByName(this, "proxyReady")]));
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* Releases the namespace registration, but only while this environment holds it. A namespace
|
|
311
|
+
* carries one environment at a time, and an assignment that displaces another one leaves that
|
|
312
|
+
* other environment registered under nothing -- taking its entry along on the way out would
|
|
313
|
+
* make `ShadowEnv.get()` answer `undefined` for an environment that is very much alive.
|
|
314
|
+
*/
|
|
315
|
+
releaseNamespace_fn = function(ns) {
|
|
316
|
+
if (!ns) return;
|
|
317
|
+
const shadowEnvs = globalThis.__shadowEnvs;
|
|
318
|
+
if (shadowEnvs?.get(ns) === this) {
|
|
319
|
+
shadowEnvs.delete(ns);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
_proxyGeneration = new WeakMap();
|
|
323
|
+
_whenDestroyed = new WeakMap();
|
|
324
|
+
_rejectWhenDestroyed = new WeakMap();
|
|
325
|
+
/**
|
|
326
|
+
* A promise that rejects the moment this environment is destroyed.
|
|
327
|
+
*
|
|
328
|
+
* Every promise the public API hands out races against it. Without that race a caller
|
|
329
|
+
* would wait forever, because {@link ShadowEnv.destroy} tears down the very listeners
|
|
330
|
+
* those promises are built on.
|
|
331
|
+
*/
|
|
332
|
+
destroyedSignal_fn = function() {
|
|
333
|
+
if (__privateGet(this, _whenDestroyed) == null) {
|
|
334
|
+
__privateSet(this, _whenDestroyed, new Promise((_, reject) => {
|
|
335
|
+
__privateSet(this, _rejectWhenDestroyed, reject);
|
|
336
|
+
}));
|
|
337
|
+
__privateGet(this, _whenDestroyed).catch(() => {
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return __privateGet(this, _whenDestroyed);
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* The cycle settles this promise by hand rather than through `AfterSync` / `SyncFailed`. A
|
|
344
|
+
* subscription made here would stand in line behind the listeners the application registered
|
|
345
|
+
* during setup, and an eventize emit stops at the first listener that throws -- everything
|
|
346
|
+
* behind it, this promise included, would be left waiting forever. Settling by hand also
|
|
347
|
+
* spares the bookkeeping a subscription per call would need: a cycle produces one of the two
|
|
348
|
+
* events, and the subscriber of the other one would stay behind and pile up.
|
|
349
|
+
*/
|
|
350
|
+
openSyncCycle_fn = function() {
|
|
351
|
+
let resolve;
|
|
352
|
+
let reject;
|
|
353
|
+
const outcome = new Promise((res, rej) => {
|
|
354
|
+
resolve = res;
|
|
355
|
+
reject = rej;
|
|
356
|
+
});
|
|
357
|
+
return { promise: Promise.race([outcome, __privateMethod(this, _ShadowEnv_instances, destroyedSignal_fn).call(this)]), resolve, reject };
|
|
358
|
+
};
|
|
359
|
+
_syncIfScheduled = new WeakMap();
|
|
203
360
|
syncNow_fn = async function() {
|
|
204
361
|
__privateSet(this, _syncScheduled, false);
|
|
205
|
-
if (this
|
|
206
|
-
|
|
362
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
363
|
+
if (!this.isReady) {
|
|
364
|
+
__privateSet(this, _syncAfterContextCreated, true);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const data = this.view.buildChangeTrails(false);
|
|
368
|
+
const cycle = __privateGet(this, _nextSyncCycle);
|
|
369
|
+
__privateSet(this, _nextSyncCycle, void 0);
|
|
370
|
+
const waitForConfirmation = __privateGet(this, _syncWaitForConfirmation);
|
|
371
|
+
__privateSet(this, _syncWaitForConfirmation, false);
|
|
372
|
+
try {
|
|
207
373
|
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
|
-
}
|
|
374
|
+
await this.envProxy.applyChangeTrail(data, waitForConfirmation);
|
|
217
375
|
}
|
|
376
|
+
} catch (error) {
|
|
377
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
378
|
+
this.logger.error("failed to apply change trail", error);
|
|
379
|
+
__privateMethod(this, _ShadowEnv_instances, commitSyncCycle_fn).call(this, data, error);
|
|
380
|
+
__privateMethod(this, _ShadowEnv_instances, endSyncCycle_fn).call(this, data, cycle, { reason: error });
|
|
381
|
+
return;
|
|
218
382
|
}
|
|
383
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
384
|
+
__privateMethod(this, _ShadowEnv_instances, commitSyncCycle_fn).call(this, data);
|
|
385
|
+
__privateMethod(this, _ShadowEnv_instances, endSyncCycle_fn).call(this, data, cycle);
|
|
219
386
|
};
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Draws the line between what the Shadow Environment applied and what it still owes, and hands
|
|
389
|
+
* it to the view. Runs ahead of {@link ShadowEnv.#endSyncCycle} so that a `SyncFailed` listener
|
|
390
|
+
* and a waiting {@link ShadowEnv.syncWait} caller find bookkeeping that already holds.
|
|
391
|
+
*
|
|
392
|
+
* An empty change trail is settled as well: the build may have retired components even without
|
|
393
|
+
* writing an entry for any of them.
|
|
394
|
+
*/
|
|
395
|
+
commitSyncCycle_fn = function(changeTrail, reason) {
|
|
396
|
+
const appliedCount = reason instanceof ChangeTrailRefusedError ? reason.appliedCount : changeTrail.length;
|
|
397
|
+
this.view?.commitChangeTrail(appliedCount, changeTrail);
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* Sends one of the two context events and keeps a listener that throws to itself.
|
|
401
|
+
*
|
|
402
|
+
* Both go out from inside the effect that watches `viewReady` and `proxyReady` -- `ContextLost`
|
|
403
|
+
* from its cleanup -- so a throw would leave through whatever signal write set that effect
|
|
404
|
+
* going, and reach code that has nothing to do with either event. `emitStrict()` serves every
|
|
405
|
+
* listener whatever the one before it did, and what they threw is reported here and travels no
|
|
406
|
+
* further.
|
|
407
|
+
*/
|
|
408
|
+
announceContext_fn = function(eventName, self) {
|
|
409
|
+
try {
|
|
410
|
+
emitStrict(self, eventName, self);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
this.logger.error(`a ${eventName} listener threw`, error);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* Ends a synchronization cycle in exactly one of its two outcomes. A cycle the Shadow
|
|
417
|
+
* Environment applied resolves {@link ShadowEnv.syncWait} and emits {@link ShadowEnv.AfterSync};
|
|
418
|
+
* a cycle whose change trail it refused rejects and emits {@link ShadowEnv.SyncFailed}. Only a
|
|
419
|
+
* listener that can tell the two apart can react to either, so no cycle ever sends both.
|
|
420
|
+
*
|
|
421
|
+
* The waiting caller is settled before the event goes out, and the emit is the last thing that
|
|
422
|
+
* happens: a settlement that travelled as a listener of its own would sit behind whatever the
|
|
423
|
+
* application registered during setup, and this method is the wrong place to make a promise
|
|
424
|
+
* depend on the order consumers subscribe in. The dispatch is `emitStrict()`, so every listener
|
|
425
|
+
* hears about the cycle whatever the one before it did, and what they threw arrives here
|
|
426
|
+
* afterwards -- one of them unchanged, several as an `AggregateError` in dispatch order. It ends
|
|
427
|
+
* here as well: `#syncNow()` runs unawaited, and an error escaping it becomes an unhandled
|
|
428
|
+
* rejection rather than a report anybody reads.
|
|
429
|
+
*
|
|
430
|
+
* Which cycle is being ended arrives as an argument: the caller carries it from the moment its
|
|
431
|
+
* change trail was built, so a cycle that is still in flight cannot be settled by the one behind it.
|
|
432
|
+
*/
|
|
433
|
+
endSyncCycle_fn = function(changeTrail, cycle, failure) {
|
|
434
|
+
try {
|
|
435
|
+
if (failure) {
|
|
436
|
+
cycle?.reject(failure.reason);
|
|
437
|
+
emitStrict(this, _ShadowEnv.SyncFailed, failure.reason, changeTrail, this);
|
|
438
|
+
} else {
|
|
439
|
+
cycle?.resolve(changeTrail);
|
|
440
|
+
emitStrict(this, _ShadowEnv.AfterSync, changeTrail);
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
this.logger.error("a sync cycle listener threw", error);
|
|
223
444
|
}
|
|
445
|
+
};
|
|
446
|
+
onMessageToView_fn = function(event) {
|
|
447
|
+
this.logger.debug("onMessageToView", event.type, event.data);
|
|
224
448
|
this.view?.dispatchMessage(event.uuid, event.type, event.data, event.traverseChildren);
|
|
225
449
|
};
|
|
450
|
+
onProxyFailed_fn = function(reason) {
|
|
451
|
+
if (__privateGet(this, _isDestroyed)) return;
|
|
452
|
+
this.logger.error("the environment proxy failed", reason);
|
|
453
|
+
++__privateWrapper(this, _proxyGeneration)._;
|
|
454
|
+
try {
|
|
455
|
+
emitStrict(this, _ShadowEnv.ProxyFailed, reason, this);
|
|
456
|
+
} finally {
|
|
457
|
+
this.proxyReady = false;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
226
460
|
__decorateElement(_init, 4, "viewReady", _viewReady_dec, _ShadowEnv, _viewReady);
|
|
227
461
|
__decorateElement(_init, 4, "proxyReady", _proxyReady_dec, _ShadowEnv, _proxyReady);
|
|
228
462
|
__decoratorMetadata(_init, _ShadowEnv);
|
|
229
463
|
_ShadowEnv.AfterSync = "afterSync";
|
|
464
|
+
_ShadowEnv.SyncFailed = "syncFailed";
|
|
230
465
|
_ShadowEnv.ContextLost = "contextLost";
|
|
231
466
|
_ShadowEnv.ContextCreated = "contextCreated";
|
|
467
|
+
_ShadowEnv.ProxyFailed = "proxyFailed";
|
|
232
468
|
let ShadowEnv = _ShadowEnv;
|
|
233
469
|
export {
|
|
234
|
-
ShadowEnv
|
|
470
|
+
ShadowEnv,
|
|
471
|
+
ShadowEnvDestroyedError
|
|
235
472
|
};
|
|
236
473
|
//# 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": "
|
|
4
|
+
"sourcesContent": ["import {emitStrict, off, on, onceAsync, Priority, retain, retainClear} from '@spearwolf/eventize';\nimport {\n createEffect,\n createSignal,\n destroyObjectSignals,\n type Effect,\n findObjectSignalByName,\n hibernate,\n} from '@spearwolf/signalize';\nimport {signal} from '@spearwolf/signalize/decorators';\nimport {ChangeTrailRefusedError} from '../ChangeTrailRefusedError.js';\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 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\n/**\n * The settlement of one synchronization cycle: the promise `syncWait()` hands out, and the pair\n * that settles it. A cycle takes its own settlement with it the moment its change trail is\n * built, which is what lets two cycles be in flight at once without settling each other's\n * callers.\n */\ntype SyncCycle = {\n promise: Promise<ChangeTrailType>;\n resolve: (changeTrail: ChangeTrailType) => void;\n reject: (reason: unknown) => void;\n};\n\nexport class ShadowEnv {\n static AfterSync = 'afterSync';\n static SyncFailed = 'syncFailed';\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 | undefined;\n #shaObjEnvProxy?: IShadowObjectEnvProxy | undefined;\n #syncScheduled = false;\n #syncAfterContextCreated = false;\n #syncWaitForConfirmation = false;\n #nextSyncCycle?: SyncCycle | undefined;\n\n readonly #logger = new ConsoleLogger('ShadowEnv');\n\n /** The logger this environment reports through. */\n get logger(): ConsoleLogger {\n return this.#logger;\n }\n\n /**\n * The namespace of the {@link ComponentContext} this environment observes, and `undefined`\n * while it observes none. The `view` setter writes it, so it carries the name\n * {@link ShadowEnv.get} finds this environment under, unless another environment has since\n * taken that namespace over; a `destroy()` leaves it on `undefined`.\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.Critical, () => {\n retainClear(self, ShadowEnv.ContextCreated);\n });\n\n // the effect that reports the context comes with the first `view` or `envProxy` this\n // environment is given: an effect stands in a module-wide queue until it is destroyed, and an\n // environment that never receives either half must stay collectable\n }\n\n #contextEffect?: Effect | undefined;\n\n /**\n * Builds the effect that reports {@link ShadowEnv.ContextCreated} and {@link ShadowEnv.ContextLost}\n * once, on the first half of the environment that arrives.\n */\n #ensureContextEffect(): void {\n if (this.#isDestroyed || this.#contextEffect != null) return;\n\n const self = this as ShadowEnv;\n\n // `hibernate()` clears the effect stack for the duration, and that is not optional here. Both\n // setters below are public API and are called from application code that may well sit inside a\n // `createEffect()` -- `<shae-worker>` is one such caller: it assigns `view` from inside an\n // `ns$.onChange()` callback. An effect built while a foreign effect is running becomes that\n // effect's child and dies at its next run, taking the context reports with it. The three custom\n // elements shield their `connectedCallback` the same way.\n //\n // One observable side effect comes with it: a `batch()` the caller has open is pushed through\n // before this returns, so the effects it was holding back run at this point. The writes the\n // setter makes afterwards go back into that batch and wait for it to close, the way they would\n // without any of this.\n hibernate(() => {\n this.#contextEffect = createEffect(() => {\n if (this.viewReady && this.proxyReady) {\n // Both halves being ready is what this reacts to, and either half can be the one that\n // arrived last. A fresh proxy under the standing view is the case the recovery is written\n // for: it holds none of the uuids, and the re-created trail goes through. A view that is\n // taken off and hung back on -- `env.view = undefined; env.view = ctx`, and `get(ns)`\n // hands back the very same context -- is the one that does not: its memory is still full,\n // the proxy that stayed still holds every uuid in it, and the trail is refused at its\n // first creation and stays refused. A context that is genuinely new carries an empty\n // memory and re-creates nothing at all. Whoever swaps the view of a live environment\n // tears the proxy down with it.\n this.view!.reCreateChanges();\n this.#announceContext(ShadowEnv.ContextCreated, self);\n if (this.#syncAfterContextCreated) {\n this.#syncAfterContextCreated = false;\n this.#syncNow();\n }\n return () => {\n this.#announceContext(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 a setter runs\n }, [findObjectSignalByName(this, 'viewReady')!, findObjectSignalByName(this, 'proxyReady')!]);\n });\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 (ctx) this.#ensureContextEffect();\n\n this.#releaseNamespace(this.#comCtx?.ns);\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 this.logger.warn('overwrite a namespace already in use', this.#comCtx.ns, globalThis.__shadowEnvs.get(this.#comCtx.ns));\n }\n globalThis.__shadowEnvs.set(this.#comCtx.ns, this);\n }\n\n // the namespace this environment observes, published where the name promises it. `view` is\n // the only way one reaches this object -- `ComponentContext.ns` is assigned in its\n // constructor and never again -- and the write stands behind the registration above, so\n // whoever reacts to it finds `ShadowEnv.get()` already answering this environment. It needs\n // no truthiness guard of its own: `toNamespace()` turns an empty or whitespace-only string\n // into `GlobalNS`, so a context that exists has a namespace that registers.\n this.ns$.set(this.#comCtx?.ns);\n\n this.viewReady = Boolean(ctx);\n }\n }\n\n /**\n * Releases the namespace registration, but only while this environment holds it. A namespace\n * carries one environment at a time, and an assignment that displaces another one leaves that\n * other environment registered under nothing -- taking its entry along on the way out would\n * make `ShadowEnv.get()` answer `undefined` for an environment that is very much alive.\n */\n #releaseNamespace(ns: NamespaceType | undefined): void {\n // the same truthiness check the `view` setter above uses before it registers a namespace,\n // so release and registration recognize the same namespace as \"none\"\n if (!ns) return;\n const shadowEnvs = globalThis.__shadowEnvs;\n if (shadowEnvs?.get(ns) === this) {\n shadowEnvs.delete(ns);\n }\n }\n\n get envProxy(): IShadowObjectEnvProxy | undefined {\n return this.#shaObjEnvProxy;\n }\n\n // Each assignment to `envProxy` opens a generation. A start that finishes outside the generation\n // it belongs to speaks for a proxy this environment has already let go, and is discarded.\n #proxyGeneration = 0;\n\n set envProxy(proxy: IShadowObjectEnvProxy | null | undefined) {\n if (proxy !== this.#shaObjEnvProxy) {\n if (proxy) this.#ensureContextEffect();\n\n const prevProxy = this.#shaObjEnvProxy;\n this.#shaObjEnvProxy = proxy ?? undefined;\n\n const generation = ++this.#proxyGeneration;\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 // a proxy this environment has let go speaks for an environment that is gone; whatever\n // it makes of its own failure from here on is no longer this environment's business\n prevProxy.onProxyFailed = undefined;\n\n // one microtask later, not synchronously: a local environment hands a message an\n // `onDestroy` sends towards the view to a microtask queued while `destroy()` runs, and\n // that message is still addressed to this environment. The microtask queue is served in\n // the order it was filled, so every message the teardown queued runs ahead of this line\n // -- and the released proxy falls silent for everything after it\n queueMicrotask(() => {\n prevProxy.onMessageToView = undefined;\n });\n }\n\n this.proxyReady = false;\n\n // the catch stays behind the then: a listener of ContextCreated that throws is reported here,\n // and turning this into `then(onFulfilled, onRejected)` would let it escape as an unhandled rejection\n proxy\n ?.start()\n .then(() => {\n if (generation !== this.#proxyGeneration) return;\n this.proxyReady = true;\n })\n .catch((error) => {\n if (generation !== this.#proxyGeneration) return;\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 /**\n * Send the change trail without asking the Shadow Environment to confirm it --\n * {@link ShadowEnv.syncWait} is the call that asks.\n *\n * What becomes of a refusal is then the proxy's decision, and the two shipped ones differ.\n * `LocalShadowObjectEnv` runs the Kernel synchronously, inside the call the environment makes to\n * the proxy -- one microtask after `sync()` returned, not inside the call to `sync()` itself --\n * and rejects with what it threw, so a refusal reaches {@link ShadowEnv.SyncFailed} here as well,\n * carrying its number.\n * `RemoteWorkerEnv` sends no serial without a confirmation and gets no answer back, so a\n * refusal stays in the worker: it is written to the console there, `SyncFailed` stays silent, and\n * the whole trail is booked as applied.\n *\n * {@link ShadowEnv.syncWait} is the way both proxies answer on.\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 * A cycle whose change trail the Shadow Environment could not apply rejects instead, with the\n * reason the proxy gave. Where the Kernel itself refused the trail that reason is a\n * {@link ChangeTrailRefusedError}: it names how many entries the Kernel applied, this side folds\n * exactly those into its bookkeeping, and everything behind that line stays pending and goes out\n * again with the next cycle. A trail whose cause of refusal stays put is refused every time, which\n * is what {@link ShadowEnv.SyncFailed} is the place to act on.\n *\n * A reason that says nothing about how far the Kernel got -- a confirmation window that ran out,\n * a proxy whose environment is gone -- counts the whole trail as applied, because a Shadow\n * Environment that fell silent may well hold all of it. Then the trail is gone, and only a\n * re-creation from the Component Memory brings it back. That re-creation belongs to a fresh\n * proxy: an environment that still holds the entities refuses a creation for a uuid it already\n * has. Handing {@link ShadowEnv.envProxy} a new proxy is therefore the way back -- the Shadow\n * Environment calls {@link ComponentContext.reCreateChanges} itself once the new proxy is ready.\n * Making that call is the consumer's decision, the same way recovering from a\n * {@link ShadowEnv.ProxyFailed} is.\n *\n * Which cycle a caller gets is decided when the change trail is built. Everyone who arrives\n * before that point waits on the same promise -- they all ride the same trail. From the build\n * on the trail is fixed, and a call after it belongs to the next cycle, the one that carries\n * the changes made since. That holds inside a listener of {@link ShadowEnv.AfterSync} or\n * {@link ShadowEnv.SyncFailed} as well: the cycle it was told about is over, so the call opens\n * the one behind it.\n *\n * @throws {ShadowEnvDestroyedError} if the environment is destroyed before the cycle completes\n */\n syncWait(): Promise<ChangeTrailType> {\n if (this.#isDestroyed) return Promise.reject(new ShadowEnvDestroyedError());\n\n this.#syncWaitForConfirmation = true;\n this.sync();\n\n // Every caller that arrives before the change trail is built waits on the same promise: they\n // all ride the same trail. From the build on the trail is fixed, `#syncNow()` has taken this\n // cycle with it, and the next caller opens the one behind it -- the cycle that will carry the\n // change they are about to make.\n this.#nextSyncCycle ??= this.#openSyncCycle();\n\n return this.#nextSyncCycle.promise;\n }\n\n /**\n * The cycle settles this promise by hand rather than through `AfterSync` / `SyncFailed`. A\n * subscription made here would stand in line behind the listeners the application registered\n * during setup, and an eventize emit stops at the first listener that throws -- everything\n * behind it, this promise included, would be left waiting forever. Settling by hand also\n * spares the bookkeeping a subscription per call would need: a cycle produces one of the two\n * events, and the subscriber of the other one would stay behind and pile up.\n */\n #openSyncCycle(): SyncCycle {\n let resolve!: (changeTrail: ChangeTrailType) => void;\n let reject!: (reason: unknown) => void;\n\n const outcome = new Promise<ChangeTrailType>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n // the race is what settles a caller whose environment is destroyed before the cycle ends:\n // `destroy()` tears down the very listeners any other route would depend on\n return {promise: Promise.race([outcome, this.#destroyedSignal()]), resolve, reject};\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 // the `envProxy` setter destroys the previous proxy, so it must not be destroyed here as well\n this.envProxy = undefined;\n // the `view` setter releases the namespace registration on the way out, ownership-checked, and clears `ns$` with it\n this.view = undefined;\n\n // settle everyone still waiting before the listeners they depend on are removed\n this.#rejectWhenDestroyed?.(new ShadowEnvDestroyedError());\n this.#nextSyncCycle = undefined;\n\n // The effect is built here, so it is released here: its lifetime hangs on this class rather\n // than on what a reactivity library makes of an effect whose dependencies are taken away.\n // `ContextLost` goes out exactly once, from whichever of the two gets there first: the effect\n // rerun that `this.envProxy = undefined` triggers above, or -- while an open `batch()` parks\n // that drop of `proxyReady` -- the destroy on this line. A cleanup function belongs to the run\n // that returned it, and is spent by the one that runs it.\n this.#contextEffect?.destroy();\n this.#contextEffect = 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(false);\n\n // The trail is fixed from here on, and with it the set of callers this cycle answers: it\n // leaves holding `#nextSyncCycle`, and a `syncWait()` from now on opens the cycle behind it.\n // The settlement travels in this frame rather than in a field, because two cycles can be in\n // flight at once -- `ComponentContext.buildChangeTrails()` says as much at its own end of this.\n const cycle = this.#nextSyncCycle;\n this.#nextSyncCycle = undefined;\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 // an environment that was torn down while its trail was in flight ends its cycle in silence:\n // destroy() has already rejected whoever waited on it and taken the listeners off, and a proxy\n // that refuses because it is being destroyed is not a failure anybody needs reported. The same\n // guard `#onProxyFailed()` carries at the neighbouring spot.\n if (this.#isDestroyed) return;\n\n // the log entry before anything else: what went wrong is on the record even if the report\n // of it runs into a listener that cannot cope\n this.logger.error('failed to apply change trail', error);\n this.#commitSyncCycle(data, error);\n this.#endSyncCycle(data, cycle, {reason: error});\n return;\n }\n\n if (this.#isDestroyed) return;\n\n // an empty change trail ends here as well -- nothing is sent, so nothing can be refused\n this.#commitSyncCycle(data);\n this.#endSyncCycle(data, cycle);\n }\n\n /**\n * Draws the line between what the Shadow Environment applied and what it still owes, and hands\n * it to the view. Runs ahead of {@link ShadowEnv.#endSyncCycle} so that a `SyncFailed` listener\n * and a waiting {@link ShadowEnv.syncWait} caller find bookkeeping that already holds.\n *\n * An empty change trail is settled as well: the build may have retired components even without\n * writing an entry for any of them.\n */\n #commitSyncCycle(changeTrail: ChangeTrailType, reason?: unknown): void {\n // A reason that does not say how far the Kernel got says nothing about the trail either: a\n // confirmation window that ran out leaves an environment that may well have applied every\n // entry, and a creation re-sent for an entity it already holds is refused -- a trail kept\n // pending on a guess would come back to that refusal cycle after cycle. The line moves only\n // where the Kernel itself named the count.\n const appliedCount = reason instanceof ChangeTrailRefusedError ? reason.appliedCount : changeTrail.length;\n\n // `?.` rather than `!`: the view can be taken off -- `env.view = undefined` -- between the\n // await above and this line\n this.view?.commitChangeTrail(appliedCount, changeTrail);\n }\n\n /**\n * Sends one of the two context events and keeps a listener that throws to itself.\n *\n * Both go out from inside the effect that watches `viewReady` and `proxyReady` -- `ContextLost`\n * from its cleanup -- so a throw would leave through whatever signal write set that effect\n * going, and reach code that has nothing to do with either event. `emitStrict()` serves every\n * listener whatever the one before it did, and what they threw is reported here and travels no\n * further.\n */\n #announceContext(eventName: string, self: ShadowEnv): void {\n try {\n emitStrict(self, eventName, self);\n } catch (error) {\n this.logger.error(`a ${eventName} listener threw`, error);\n }\n }\n\n /**\n * Ends a synchronization cycle in exactly one of its two outcomes. A cycle the Shadow\n * Environment applied resolves {@link ShadowEnv.syncWait} and emits {@link ShadowEnv.AfterSync};\n * a cycle whose change trail it refused rejects and emits {@link ShadowEnv.SyncFailed}. Only a\n * listener that can tell the two apart can react to either, so no cycle ever sends both.\n *\n * The waiting caller is settled before the event goes out, and the emit is the last thing that\n * happens: a settlement that travelled as a listener of its own would sit behind whatever the\n * application registered during setup, and this method is the wrong place to make a promise\n * depend on the order consumers subscribe in. The dispatch is `emitStrict()`, so every listener\n * hears about the cycle whatever the one before it did, and what they threw arrives here\n * afterwards -- one of them unchanged, several as an `AggregateError` in dispatch order. It ends\n * here as well: `#syncNow()` runs unawaited, and an error escaping it becomes an unhandled\n * rejection rather than a report anybody reads.\n *\n * Which cycle is being ended arrives as an argument: the caller carries it from the moment its\n * change trail was built, so a cycle that is still in flight cannot be settled by the one behind it.\n */\n #endSyncCycle(changeTrail: ChangeTrailType, cycle: SyncCycle | undefined, failure?: {reason: unknown}) {\n try {\n if (failure) {\n cycle?.reject(failure.reason);\n emitStrict(this as ShadowEnv, ShadowEnv.SyncFailed, failure.reason, changeTrail, this as ShadowEnv);\n } else {\n cycle?.resolve(changeTrail);\n emitStrict(this as ShadowEnv, ShadowEnv.AfterSync, changeTrail);\n }\n } catch (error) {\n this.logger.error('a sync cycle listener threw', error);\n }\n }\n\n #onMessageToView(event: Omit<MessageToViewEvent, 'transferables'>) {\n this.logger.debug('onMessageToView', event.type, event.data);\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 // the failure ends this proxy's turn the same way a reassignment would: a start of its own\n // that resolves afterwards must not report a lost environment as ready\n ++this.#proxyGeneration;\n\n try {\n // the reason before the consequence: ContextLost follows from dropping proxyReady.\n // `emitStrict()` so that one listener that cannot cope does not keep the rest from hearing\n // that the environment is gone; what it threw travels on to the proxy that reported the\n // failure, which is where this method has always put it.\n emitStrict(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}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAQ,YAAY,KAAK,IAAI,WAAW,UAAU,QAAQ,mBAAkB;AAC5E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AACP,SAAQ,cAAa;AACrB,SAAQ,+BAA8B;AAGtC,SAAQ,qBAAoB;AAC5B,SAAQ,wBAAuB;AAWxB,MAAM,gCAAgC,MAAM;AAAA,EACjD,YAAY,UAAU,6CAA6C;AACjE,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAgDE,kBAAC,OAAO,IACR,mBAAC,OAAO;AAnCH,MAAM,aAAN,MAAM,WAAU;AAAA,EA2CrB,cAAc;AA3CT;AAYL;AACA;AACA,uCAAiB;AACjB,iDAA2B;AAC3B,iDAA2B;AAC3B;AAEA,uBAAS,SAAU,IAAI,cAAc,WAAW;AAahD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,MAAM,aAAwC;AAE7C,uBAAS,YAAY,kBAArB,gBAAqB,SAArB;AACA,uBAAS,aAAa,kBAAtB,iBAAsB,SAAtB;AAEV,qCAAe;AAmBf;AAwGA;AAAA;AAAA,yCAAmB;AAuDnB;AACA;AAyBA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,QAAQ,YAAgC;AAC/C,UAAI,mBAAK,cAAc,OAAM,IAAI,wBAAwB;AACzD,UAAI,KAAK,QAAS,QAAO;AACzB,aAAO,QAAQ,KAAK,CAAC,UAAqB,MAAmB,WAAU,cAAc,GAAG,sBAAK,0CAAL,UAAuB,CAAC;AAAA,IAClH;AAsIA,yCAAmB,MAAM;AACvB,UAAI,mBAAK,iBAAgB;AACvB,8BAAK,kCAAL;AAAA,MACF;AAAA,IACF;AAnVE,UAAM,OAAO;AACb,WAAO,MAAM,WAAU,cAAc;AAErC,OAAG,MAAM,WAAU,aAAa,SAAS,UAAU,MAAM;AACvD,kBAAY,MAAM,WAAU,cAAc;AAAA,IAC5C,CAAC;AAAA,EAKH;AAAA,EA/CA,OAAO,IAAI,IAA0C;AACnD,QAAI,MAAM,KAAM,QAAO;AACvB,WAAO,WAAW,cAAc,IAAI,EAAE;AAAA,EACxC;AAAA;AAAA,EAYA,IAAI,SAAwB;AAC1B,WAAO,mBAAK;AAAA,EACd;AAAA,EAeA,IAAI,cAAc;AAChB,WAAO,mBAAK;AAAA,EACd;AAAA,EAiEA,IAAI,OAAqC;AACvC,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,IAAI,KAAK,KAA0C;AACjD,QAAI,QAAQ,mBAAK,UAAS;AACxB,UAAI,IAAK,uBAAK,8CAAL;AAET,4BAAK,2CAAL,WAAuB,mBAAK,UAAS;AAErC,yBAAK,SAAU,OAAO;AAEtB,UAAI,mBAAK,UAAS,IAAI;AACpB,mBAAW,iBAAiB,oBAAI,IAAI;AACpC,YAAI,WAAW,aAAa,IAAI,mBAAK,SAAQ,EAAE,KAAK,WAAW,aAAa,IAAI,mBAAK,SAAQ,EAAE,MAAM,MAAM;AACzG,eAAK,OAAO,KAAK,wCAAwC,mBAAK,SAAQ,IAAI,WAAW,aAAa,IAAI,mBAAK,SAAQ,EAAE,CAAC;AAAA,QACxH;AACA,mBAAW,aAAa,IAAI,mBAAK,SAAQ,IAAI,IAAI;AAAA,MACnD;AAQA,WAAK,IAAI,IAAI,mBAAK,UAAS,EAAE;AAE7B,WAAK,YAAY,QAAQ,GAAG;AAAA,IAC9B;AAAA,EACF;AAAA,EAkBA,IAAI,WAA8C;AAChD,WAAO,mBAAK;AAAA,EACd;AAAA,EAMA,IAAI,SAAS,OAAiD;AAC5D,QAAI,UAAU,mBAAK,kBAAiB;AAClC,UAAI,MAAO,uBAAK,8CAAL;AAEX,YAAM,YAAY,mBAAK;AACvB,yBAAK,iBAAkB,SAAS;AAEhC,YAAM,aAAoB,EAAL,uBAAK,kBAAL;AAErB,UAAI,mBAAK,kBAAiB;AACxB,2BAAK,iBAAgB,kBAAkB,sBAAK,0CAAiB,KAAK,IAAI;AACtE,2BAAK,iBAAgB,gBAAgB,sBAAK,wCAAe,KAAK,IAAI;AAAA,MACpE;AAEA,UAAI,WAAW;AACb,kBAAU,QAAQ;AAIlB,kBAAU,gBAAgB;AAO1B,uBAAe,MAAM;AACnB,oBAAU,kBAAkB;AAAA,QAC9B,CAAC;AAAA,MACH;AAEA,WAAK,aAAa;AAIlB,aACI,MAAM,EACP,KAAK,MAAM;AACV,YAAI,eAAe,mBAAK,kBAAkB;AAC1C,aAAK,aAAa;AAAA,MACpB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,YAAI,eAAe,mBAAK,kBAAkB;AAC1C,aAAK,OAAO,MAAM,4BAA4B,KAAK;AACnD,aAAK,aAAa;AAAA,MACpB,CAAC;AAAA,IACL;AAAA,EACF;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,QAAQ,mBAAK,YAAW,mBAAK,oBAAmB,KAAK,cAAc,CAAC,KAAK,WAAW;AAAA,EAC7F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiDA,OAAa;AACX,QAAI,mBAAK,cAAc;AACvB,QAAI,CAAC,KAAK,SAAS;AACjB,yBAAK,0BAA2B;AAChC;AAAA,IACF;AACA,QAAI,mBAAK,gBAAgB;AACzB,uBAAK,gBAAiB;AACtB,mBAAe,mBAAK,iBAAgB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BA,WAAqC;AACnC,QAAI,mBAAK,cAAc,QAAO,QAAQ,OAAO,IAAI,wBAAwB,CAAC;AAE1E,uBAAK,0BAA2B;AAChC,SAAK,KAAK;AAMV,uBAAK,mBAAL,mBAAK,gBAAmB,sBAAK,wCAAL;AAExB,WAAO,mBAAK,gBAAe;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,UAAU;AArYZ;AAsYI,QAAI,mBAAK,cAAc;AAGvB,uBAAK,cAAe;AAEpB,uBAAK,gBAAiB;AACtB,uBAAK,0BAA2B;AAChC,uBAAK,0BAA2B;AAGhC,SAAK,WAAW;AAEhB,SAAK,OAAO;AAGZ,6BAAK,0BAAL,8BAA4B,IAAI,wBAAwB;AACxD,uBAAK,gBAAiB;AAQtB,uBAAK,iBAAgB,QAAQ;AAC7B,uBAAK,gBAAiB;AAEtB,yBAAqB,IAAI;AACzB,QAAI,IAAI;AAER,WAAO,OAAO,IAAI;AAAA,EACpB;AA2JF;AAphBO;AAYL;AACA;AACA;AACA;AACA;AACA;AAES;AAeU;AACA;AAEnB;AAmBA;AAxDK;AAAA;AAAA;AAAA;AAAA;AA8DL,yBAAoB,WAAS;AAC3B,MAAI,mBAAK,iBAAgB,mBAAK,mBAAkB,KAAM;AAEtD,QAAM,OAAO;AAab,YAAU,MAAM;AACd,uBAAK,gBAAiB,aAAa,MAAM;AACvC,UAAI,KAAK,aAAa,KAAK,YAAY;AAUrC,aAAK,KAAM,gBAAgB;AAC3B,8BAAK,0CAAL,WAAsB,WAAU,gBAAgB;AAChD,YAAI,mBAAK,2BAA0B;AACjC,6BAAK,0BAA2B;AAChC,gCAAK,kCAAL;AAAA,QACF;AACA,eAAO,MAAM;AACX,gCAAK,0CAAL,WAAsB,WAAU,aAAa;AAAA,QAC/C;AAAA,MACF;AAAA,IAGF,GAAG,CAAC,uBAAuB,MAAM,WAAW,GAAI,uBAAuB,MAAM,YAAY,CAAE,CAAC;AAAA,EAC9F,CAAC;AACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCA,sBAAiB,SAAC,IAAqC;AAGrD,MAAI,CAAC,GAAI;AACT,QAAM,aAAa,WAAW;AAC9B,MAAI,YAAY,IAAI,EAAE,MAAM,MAAM;AAChC,eAAW,OAAO,EAAE;AAAA,EACtB;AACF;AAQA;AAuDA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,qBAAgB,WAAmB;AACjC,MAAI,mBAAK,mBAAkB,MAAM;AAC/B,uBAAK,gBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACtD,yBAAK,sBAAuB;AAAA,IAC9B,CAAC;AAED,uBAAK,gBAAe,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACpC;AACA,SAAO,mBAAK;AACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2FA,mBAAc,WAAc;AAC1B,MAAI;AACJ,MAAI;AAEJ,QAAM,UAAU,IAAI,QAAyB,CAAC,KAAK,QAAQ;AACzD,cAAU;AACV,aAAS;AAAA,EACX,CAAC;AAID,SAAO,EAAC,SAAS,QAAQ,KAAK,CAAC,SAAS,sBAAK,0CAAL,UAAuB,CAAC,GAAG,SAAS,OAAM;AACpF;AA0CA;AAMM,aAAQ,iBAAG;AACf,qBAAK,gBAAiB;AAEtB,MAAI,mBAAK,cAAc;AAEvB,MAAI,CAAC,KAAK,SAAS;AAGjB,uBAAK,0BAA2B;AAChC;AAAA,EACF;AAEA,QAAM,OAAO,KAAK,KAAM,kBAAkB,KAAK;AAM/C,QAAM,QAAQ,mBAAK;AACnB,qBAAK,gBAAiB;AAEtB,QAAM,sBAAsB,mBAAK;AACjC,qBAAK,0BAA2B;AAEhC,MAAI;AACF,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,KAAK,SAAU,iBAAiB,MAAM,mBAAmB;AAAA,IACjE;AAAA,EACF,SAAS,OAAO;AAKd,QAAI,mBAAK,cAAc;AAIvB,SAAK,OAAO,MAAM,gCAAgC,KAAK;AACvD,0BAAK,0CAAL,WAAsB,MAAM;AAC5B,0BAAK,uCAAL,WAAmB,MAAM,OAAO,EAAC,QAAQ,MAAK;AAC9C;AAAA,EACF;AAEA,MAAI,mBAAK,cAAc;AAGvB,wBAAK,0CAAL,WAAsB;AACtB,wBAAK,uCAAL,WAAmB,MAAM;AAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,qBAAgB,SAAC,aAA8B,QAAwB;AAMrE,QAAM,eAAe,kBAAkB,0BAA0B,OAAO,eAAe,YAAY;AAInG,OAAK,MAAM,kBAAkB,cAAc,WAAW;AACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,qBAAgB,SAAC,WAAmB,MAAuB;AACzD,MAAI;AACF,eAAW,MAAM,WAAW,IAAI;AAAA,EAClC,SAAS,OAAO;AACd,SAAK,OAAO,MAAM,KAAK,SAAS,mBAAmB,KAAK;AAAA,EAC1D;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBA,kBAAa,SAAC,aAA8B,OAA8B,SAA6B;AACrG,MAAI;AACF,QAAI,SAAS;AACX,aAAO,OAAO,QAAQ,MAAM;AAC5B,iBAAW,MAAmB,WAAU,YAAY,QAAQ,QAAQ,aAAa,IAAiB;AAAA,IACpG,OAAO;AACL,aAAO,QAAQ,WAAW;AAC1B,iBAAW,MAAmB,WAAU,WAAW,WAAW;AAAA,IAChE;AAAA,EACF,SAAS,OAAO;AACd,SAAK,OAAO,MAAM,+BAA+B,KAAK;AAAA,EACxD;AACF;AAEA,qBAAgB,SAAC,OAAkD;AACjE,OAAK,OAAO,MAAM,mBAAmB,MAAM,MAAM,MAAM,IAAI;AAC3D,OAAK,MAAM,gBAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,gBAAgB;AACvF;AAEA,mBAAc,SAAC,QAAiB;AAE9B,MAAI,mBAAK,cAAc;AAEvB,OAAK,OAAO,MAAM,gCAAgC,MAAM;AAIxD,EAAO,EAAL,uBAAK,kBAAL;AAEF,MAAI;AAKF,eAAW,MAAmB,WAAU,aAAa,QAAQ,IAAiB;AAAA,EAChF,UAAE;AAGA,SAAK,aAAa;AAAA,EACpB;AACF;AAjfU,4BAAS,aAAnB,gBAlCW,YAkCQ;AACT,4BAAS,cAAnB,iBAnCW,YAmCQ;AAnCd,2BAAM;AAAA,WACJ,YAAY;AADR,WAEJ,aAAa;AAFT,WAGJ,cAAc;AAHV,WAIJ,iBAAiB;AAJb,WAKJ,cAAc;AALhB,IAAM,YAAN;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|