@unrulysystems/native-motion-core 0.1.0-alpha.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 +9 -0
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/clock.cjs +71 -0
- package/dist/clock.d.cts +23 -0
- package/dist/clock.d.ts +23 -0
- package/dist/clock.js +66 -0
- package/dist/collect-reads.cjs +68 -0
- package/dist/collect-reads.d.cts +23 -0
- package/dist/collect-reads.d.ts +23 -0
- package/dist/collect-reads.js +62 -0
- package/dist/component/boundedArray.cjs +74 -0
- package/dist/component/boundedArray.d.cts +25 -0
- package/dist/component/boundedArray.d.ts +25 -0
- package/dist/component/boundedArray.js +68 -0
- package/dist/component/index.cjs +57 -0
- package/dist/component/index.d.cts +12 -0
- package/dist/component/index.d.ts +12 -0
- package/dist/component/index.js +22 -0
- package/dist/component/orchestration.cjs +129 -0
- package/dist/component/orchestration.d.cts +66 -0
- package/dist/component/orchestration.d.ts +66 -0
- package/dist/component/orchestration.js +124 -0
- package/dist/component/resolve.cjs +104 -0
- package/dist/component/resolve.d.cts +11 -0
- package/dist/component/resolve.d.ts +11 -0
- package/dist/component/resolve.js +97 -0
- package/dist/component/transition.cjs +352 -0
- package/dist/component/transition.d.cts +33 -0
- package/dist/component/transition.d.ts +33 -0
- package/dist/component/transition.js +339 -0
- package/dist/component/types.cjs +114 -0
- package/dist/component/types.d.cts +60 -0
- package/dist/component/types.d.ts +60 -0
- package/dist/component/types.js +111 -0
- package/dist/component/validate.cjs +1015 -0
- package/dist/component/validate.d.cts +37 -0
- package/dist/component/validate.d.ts +37 -0
- package/dist/component/validate.js +1002 -0
- package/dist/component/variants.cjs +333 -0
- package/dist/component/variants.d.cts +106 -0
- package/dist/component/variants.d.ts +106 -0
- package/dist/component/variants.js +321 -0
- package/dist/config/constants.cjs +41 -0
- package/dist/config/constants.d.cts +29 -0
- package/dist/config/constants.d.ts +29 -0
- package/dist/config/constants.js +38 -0
- package/dist/delay.cjs +72 -0
- package/dist/delay.d.cts +26 -0
- package/dist/delay.d.ts +26 -0
- package/dist/delay.js +70 -0
- package/dist/derived.cjs +143 -0
- package/dist/derived.d.cts +41 -0
- package/dist/derived.d.ts +41 -0
- package/dist/derived.js +139 -0
- package/dist/driver/index.cjs +22 -0
- package/dist/driver/index.d.cts +7 -0
- package/dist/driver/index.d.ts +7 -0
- package/dist/driver/index.js +14 -0
- package/dist/driver/keyframeTiming.cjs +130 -0
- package/dist/driver/keyframeTiming.d.cts +18 -0
- package/dist/driver/keyframeTiming.d.ts +18 -0
- package/dist/driver/keyframeTiming.js +124 -0
- package/dist/driver/keyframeTimingConfig.cjs +24 -0
- package/dist/driver/keyframeTimingConfig.d.cts +5 -0
- package/dist/driver/keyframeTimingConfig.d.ts +5 -0
- package/dist/driver/keyframeTimingConfig.js +22 -0
- package/dist/driver/prepare.cjs +459 -0
- package/dist/driver/prepare.d.cts +4 -0
- package/dist/driver/prepare.d.ts +4 -0
- package/dist/driver/prepare.js +454 -0
- package/dist/driver/reference.cjs +762 -0
- package/dist/driver/reference.d.cts +53 -0
- package/dist/driver/reference.d.ts +53 -0
- package/dist/driver/reference.js +757 -0
- package/dist/driver/step.cjs +55 -0
- package/dist/driver/step.d.cts +9 -0
- package/dist/driver/step.d.ts +9 -0
- package/dist/driver/step.js +52 -0
- package/dist/driver/tiers.cjs +40 -0
- package/dist/driver/tiers.d.cts +2 -0
- package/dist/driver/tiers.d.ts +2 -0
- package/dist/driver/tiers.js +37 -0
- package/dist/driver/types.cjs +9 -0
- package/dist/driver/types.d.cts +68 -0
- package/dist/driver/types.d.ts +68 -0
- package/dist/driver/types.js +8 -0
- package/dist/external-animation-ledger.cjs +1250 -0
- package/dist/external-animation-ledger.d.cts +416 -0
- package/dist/external-animation-ledger.d.ts +416 -0
- package/dist/external-animation-ledger.js +1243 -0
- package/dist/gesture/directionLock.cjs +25 -0
- package/dist/gesture/directionLock.d.cts +8 -0
- package/dist/gesture/directionLock.d.ts +8 -0
- package/dist/gesture/directionLock.js +21 -0
- package/dist/gesture/dragConfig.cjs +634 -0
- package/dist/gesture/dragConfig.d.cts +298 -0
- package/dist/gesture/dragConfig.d.ts +298 -0
- package/dist/gesture/dragConfig.js +624 -0
- package/dist/gesture/elastic.cjs +44 -0
- package/dist/gesture/elastic.d.cts +4 -0
- package/dist/gesture/elastic.d.ts +4 -0
- package/dist/gesture/elastic.js +39 -0
- package/dist/gesture/handoffSession.cjs +161 -0
- package/dist/gesture/handoffSession.d.cts +35 -0
- package/dist/gesture/handoffSession.d.ts +35 -0
- package/dist/gesture/handoffSession.js +158 -0
- package/dist/gesture/index.cjs +37 -0
- package/dist/gesture/index.d.cts +12 -0
- package/dist/gesture/index.d.ts +12 -0
- package/dist/gesture/index.js +11 -0
- package/dist/gesture/projection.cjs +95 -0
- package/dist/gesture/projection.d.cts +7 -0
- package/dist/gesture/projection.d.ts +7 -0
- package/dist/gesture/projection.js +87 -0
- package/dist/gesture/session.cjs +162 -0
- package/dist/gesture/session.d.cts +29 -0
- package/dist/gesture/session.d.ts +29 -0
- package/dist/gesture/session.js +158 -0
- package/dist/gesture/types.cjs +5 -0
- package/dist/gesture/types.d.cts +13 -0
- package/dist/gesture/types.d.ts +13 -0
- package/dist/gesture/types.js +4 -0
- package/dist/gesture/viewportConstraints.cjs +38 -0
- package/dist/gesture/viewportConstraints.d.cts +6 -0
- package/dist/gesture/viewportConstraints.d.ts +6 -0
- package/dist/gesture/viewportConstraints.js +34 -0
- package/dist/graph.cjs +226 -0
- package/dist/graph.d.cts +2 -0
- package/dist/graph.d.ts +2 -0
- package/dist/graph.js +223 -0
- package/dist/index.cjs +140 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +70 -0
- package/dist/inertia.cjs +214 -0
- package/dist/inertia.d.cts +53 -0
- package/dist/inertia.d.ts +53 -0
- package/dist/inertia.js +212 -0
- package/dist/instant.cjs +66 -0
- package/dist/instant.d.cts +16 -0
- package/dist/instant.d.ts +16 -0
- package/dist/instant.js +63 -0
- package/dist/internal-driver.cjs +81 -0
- package/dist/internal-driver.d.cts +19 -0
- package/dist/internal-driver.d.ts +19 -0
- package/dist/internal-driver.js +35 -0
- package/dist/keyframes.cjs +191 -0
- package/dist/keyframes.d.cts +13 -0
- package/dist/keyframes.d.ts +13 -0
- package/dist/keyframes.js +188 -0
- package/dist/layout/commitDetector.cjs +67 -0
- package/dist/layout/commitDetector.d.cts +21 -0
- package/dist/layout/commitDetector.d.ts +21 -0
- package/dist/layout/commitDetector.js +64 -0
- package/dist/layout/compose.cjs +67 -0
- package/dist/layout/compose.d.cts +30 -0
- package/dist/layout/compose.d.ts +30 -0
- package/dist/layout/compose.js +65 -0
- package/dist/layout/constants.cjs +13 -0
- package/dist/layout/constants.d.cts +6 -0
- package/dist/layout/constants.d.ts +6 -0
- package/dist/layout/constants.js +10 -0
- package/dist/layout/identity.cjs +700 -0
- package/dist/layout/identity.d.cts +67 -0
- package/dist/layout/identity.d.ts +67 -0
- package/dist/layout/identity.js +698 -0
- package/dist/layout/index.cjs +36 -0
- package/dist/layout/index.d.cts +18 -0
- package/dist/layout/index.d.ts +18 -0
- package/dist/layout/index.js +13 -0
- package/dist/layout/measure.cjs +81 -0
- package/dist/layout/measure.d.cts +43 -0
- package/dist/layout/measure.d.ts +43 -0
- package/dist/layout/measure.js +78 -0
- package/dist/layout/projection.cjs +99 -0
- package/dist/layout/projection.d.cts +23 -0
- package/dist/layout/projection.d.ts +23 -0
- package/dist/layout/projection.js +97 -0
- package/dist/layout/scroll.cjs +27 -0
- package/dist/layout/scroll.d.cts +13 -0
- package/dist/layout/scroll.d.ts +13 -0
- package/dist/layout/scroll.js +24 -0
- package/dist/layout/session.cjs +207 -0
- package/dist/layout/session.d.cts +73 -0
- package/dist/layout/session.d.ts +73 -0
- package/dist/layout/session.js +205 -0
- package/dist/layout/tree.cjs +826 -0
- package/dist/layout/tree.d.cts +70 -0
- package/dist/layout/tree.d.ts +70 -0
- package/dist/layout/tree.js +823 -0
- package/dist/layout/types.cjs +36 -0
- package/dist/layout/types.d.cts +31 -0
- package/dist/layout/types.d.ts +31 -0
- package/dist/layout/types.js +35 -0
- package/dist/motion-arc.cjs +184 -0
- package/dist/motion-arc.d.cts +78 -0
- package/dist/motion-arc.d.ts +78 -0
- package/dist/motion-arc.js +183 -0
- package/dist/motion-mix.cjs +205 -0
- package/dist/motion-mix.d.cts +3 -0
- package/dist/motion-mix.d.ts +3 -0
- package/dist/motion-mix.js +202 -0
- package/dist/motion-value-driver-port.cjs +636 -0
- package/dist/motion-value-driver-port.d.cts +406 -0
- package/dist/motion-value-driver-port.d.ts +406 -0
- package/dist/motion-value-driver-port.js +627 -0
- package/dist/motion-value.cjs +189 -0
- package/dist/motion-value.d.cts +51 -0
- package/dist/motion-value.d.ts +51 -0
- package/dist/motion-value.js +185 -0
- package/dist/presence/controller.cjs +657 -0
- package/dist/presence/controller.d.cts +6 -0
- package/dist/presence/controller.d.ts +6 -0
- package/dist/presence/controller.js +652 -0
- package/dist/presence/index.cjs +19 -0
- package/dist/presence/index.d.cts +4 -0
- package/dist/presence/index.d.ts +4 -0
- package/dist/presence/index.js +12 -0
- package/dist/presence/machine.cjs +50 -0
- package/dist/presence/machine.d.cts +10 -0
- package/dist/presence/machine.d.ts +10 -0
- package/dist/presence/machine.js +46 -0
- package/dist/presence/types.cjs +6 -0
- package/dist/presence/types.d.cts +32 -0
- package/dist/presence/types.d.ts +32 -0
- package/dist/presence/types.js +5 -0
- package/dist/repeat.cjs +311 -0
- package/dist/repeat.d.cts +174 -0
- package/dist/repeat.d.ts +174 -0
- package/dist/repeat.js +300 -0
- package/dist/spring.cjs +128 -0
- package/dist/spring.d.cts +18 -0
- package/dist/spring.d.ts +18 -0
- package/dist/spring.js +125 -0
- package/dist/subscriptions.cjs +74 -0
- package/dist/subscriptions.d.cts +19 -0
- package/dist/subscriptions.d.ts +19 -0
- package/dist/subscriptions.js +69 -0
- package/dist/subset/index.cjs +23 -0
- package/dist/subset/index.d.cts +6 -0
- package/dist/subset/index.d.ts +6 -0
- package/dist/subset/index.js +13 -0
- package/dist/subset/normalize.cjs +90 -0
- package/dist/subset/normalize.d.cts +10 -0
- package/dist/subset/normalize.d.ts +10 -0
- package/dist/subset/normalize.js +85 -0
- package/dist/subset/registry.cjs +259 -0
- package/dist/subset/registry.d.cts +25 -0
- package/dist/subset/registry.d.ts +25 -0
- package/dist/subset/registry.js +256 -0
- package/dist/subset/resolve.cjs +98 -0
- package/dist/subset/resolve.d.cts +24 -0
- package/dist/subset/resolve.d.ts +24 -0
- package/dist/subset/resolve.js +90 -0
- package/dist/timing.cjs +206 -0
- package/dist/timing.d.cts +17 -0
- package/dist/timing.d.ts +17 -0
- package/dist/timing.js +201 -0
- package/dist/transformTemplate.cjs +219 -0
- package/dist/transformTemplate.d.cts +29 -0
- package/dist/transformTemplate.d.ts +29 -0
- package/dist/transformTemplate.js +215 -0
- package/dist/transition.cjs +231 -0
- package/dist/transition.d.cts +89 -0
- package/dist/transition.d.ts +89 -0
- package/dist/transition.js +223 -0
- package/dist/types.cjs +4 -0
- package/dist/types.d.cts +175 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.js +3 -0
- package/dist/value-types/color.cjs +220 -0
- package/dist/value-types/color.d.cts +19 -0
- package/dist/value-types/color.d.ts +19 -0
- package/dist/value-types/color.js +218 -0
- package/dist/value-types/complex.cjs +160 -0
- package/dist/value-types/complex.d.cts +19 -0
- package/dist/value-types/complex.d.ts +19 -0
- package/dist/value-types/complex.js +155 -0
- package/dist/value-types/constants.cjs +15 -0
- package/dist/value-types/constants.d.cts +6 -0
- package/dist/value-types/constants.d.ts +6 -0
- package/dist/value-types/constants.js +12 -0
- package/dist/value-types/discrete.cjs +62 -0
- package/dist/value-types/discrete.d.cts +4 -0
- package/dist/value-types/discrete.d.ts +4 -0
- package/dist/value-types/discrete.js +56 -0
- package/dist/value-types/index.cjs +59 -0
- package/dist/value-types/index.d.cts +14 -0
- package/dist/value-types/index.d.ts +14 -0
- package/dist/value-types/index.js +24 -0
- package/dist/value-types/measure-resolve.cjs +325 -0
- package/dist/value-types/measure-resolve.d.cts +91 -0
- package/dist/value-types/measure-resolve.d.ts +91 -0
- package/dist/value-types/measure-resolve.js +313 -0
- package/dist/value-types/mix.cjs +90 -0
- package/dist/value-types/mix.d.cts +27 -0
- package/dist/value-types/mix.d.ts +27 -0
- package/dist/value-types/mix.js +85 -0
- package/dist/value-types/named-colors.cjs +61 -0
- package/dist/value-types/named-colors.d.cts +2 -0
- package/dist/value-types/named-colors.d.ts +2 -0
- package/dist/value-types/named-colors.js +58 -0
- package/dist/value-types/numeric.cjs +86 -0
- package/dist/value-types/numeric.d.cts +16 -0
- package/dist/value-types/numeric.d.ts +16 -0
- package/dist/value-types/numeric.js +79 -0
- package/dist/worklet-layout/config/constants.js +39 -0
- package/dist/worklet-layout/layout/constants.js +11 -0
- package/dist/worklet-layout/layout/identity.js +699 -0
- package/dist/worklet-layout/layout/projection.js +98 -0
- package/dist/worklet-layout/layout/session.js +206 -0
- package/dist/worklet-layout/layout/tree.js +824 -0
- package/dist/worklet-layout/layout/types.js +36 -0
- package/dist/worklet-layout/spring.js +125 -0
- package/dist/worklet-layout/timing.js +201 -0
- package/dist/worklet-layout/transition.js +223 -0
- package/dist/worklet-layout.cjs +47 -0
- package/dist/worklet-layout.d.cts +15 -0
- package/dist/worklet-layout.d.ts +15 -0
- package/dist/worklet-layout.js +28 -0
- package/dist/wrap.cjs +7 -0
- package/dist/wrap.d.cts +1 -0
- package/dist/wrap.d.ts +1 -0
- package/dist/wrap.js +4 -0
- package/package.json +45 -0
|
@@ -0,0 +1,1250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Private, host-free external-animation and target-batch ledger seam (REQ-VALUE-015). The state
|
|
3
|
+
// shapes are serializable and deterministic so the eventual driver implementation and CORE tests
|
|
4
|
+
// share one lifecycle vocabulary. `advance` applies one event immutably and returns the ordered
|
|
5
|
+
// effects the host must perform; malformed, conflicting, or out-of-sequence input fails closed
|
|
6
|
+
// with `ExternalAnimationLedgerInvariantError` and leaves the retained state untouched.
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.ExternalAnimationLedgerInvariantError = exports.EXTERNAL_ANIMATION_LEDGER_INVARIANT = exports.ExternalAnimationLedgerUnimplementedError = exports.EXTERNAL_ANIMATION_LEDGER_UNIMPLEMENTED = void 0;
|
|
9
|
+
exports.initialExternalAnimationLedgerState = initialExternalAnimationLedgerState;
|
|
10
|
+
exports.createExternalAnimationLedger = createExternalAnimationLedger;
|
|
11
|
+
exports.EXTERNAL_ANIMATION_LEDGER_UNIMPLEMENTED = 'NATIVE_MOTION_CORE_EXTERNAL_ANIMATION_LEDGER_UNIMPLEMENTED';
|
|
12
|
+
class ExternalAnimationLedgerUnimplementedError extends Error {
|
|
13
|
+
code = exports.EXTERNAL_ANIMATION_LEDGER_UNIMPLEMENTED;
|
|
14
|
+
operation;
|
|
15
|
+
constructor(operation) {
|
|
16
|
+
super(`native-motion-core: external-animation ledger operation '${operation}' is unimplemented ` +
|
|
17
|
+
'(REQ-VALUE-015)');
|
|
18
|
+
this.name = 'ExternalAnimationLedgerUnimplementedError';
|
|
19
|
+
this.operation = operation;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.ExternalAnimationLedgerUnimplementedError = ExternalAnimationLedgerUnimplementedError;
|
|
23
|
+
exports.EXTERNAL_ANIMATION_LEDGER_INVARIANT = 'NATIVE_MOTION_CORE_EXTERNAL_ANIMATION_LEDGER_INVARIANT';
|
|
24
|
+
/** Typed fail-closed refusal for malformed, conflicting, or out-of-sequence ledger input. */
|
|
25
|
+
class ExternalAnimationLedgerInvariantError extends Error {
|
|
26
|
+
code = exports.EXTERNAL_ANIMATION_LEDGER_INVARIANT;
|
|
27
|
+
invariant;
|
|
28
|
+
constructor(invariant) {
|
|
29
|
+
super(`native-motion-core: external-animation ledger invariant '${invariant}' was violated ` +
|
|
30
|
+
'(REQ-VALUE-015)');
|
|
31
|
+
this.name = 'ExternalAnimationLedgerInvariantError';
|
|
32
|
+
this.invariant = invariant;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.ExternalAnimationLedgerInvariantError = ExternalAnimationLedgerInvariantError;
|
|
36
|
+
function initialExternalAnimationLedgerState() {
|
|
37
|
+
return {
|
|
38
|
+
identityAuthorities: [],
|
|
39
|
+
targetBatchAuthority: {
|
|
40
|
+
targetBatchActivities: [],
|
|
41
|
+
nextAggregateReactionSequence: 1,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function executionCountsAsActive(_execution) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function completionIsArmed(execution) {
|
|
49
|
+
switch (execution.kind) {
|
|
50
|
+
case 'completion-armed-running':
|
|
51
|
+
case 'completion-armed-physical-resolved':
|
|
52
|
+
case 'completion-dispatching':
|
|
53
|
+
case 'awaiting-completion-authoritative-acknowledgement':
|
|
54
|
+
return true;
|
|
55
|
+
case 'running-unarmed':
|
|
56
|
+
case 'awaiting-cancellation-authoritative-acknowledgement':
|
|
57
|
+
return false;
|
|
58
|
+
default: {
|
|
59
|
+
const exhaustive = execution;
|
|
60
|
+
return exhaustive;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function targetBatchActivityIsOpen(activity) {
|
|
65
|
+
return activity.kind !== 'released';
|
|
66
|
+
}
|
|
67
|
+
function observeExternalAnimationIdentityAuthority(state) {
|
|
68
|
+
let externalActiveAnimationCount = 0;
|
|
69
|
+
let completionArmedExecutionCount = 0;
|
|
70
|
+
let awaitingAuthoritativeAcknowledgementCount = 0;
|
|
71
|
+
for (const execution of state.executions) {
|
|
72
|
+
if (executionCountsAsActive(execution))
|
|
73
|
+
externalActiveAnimationCount += 1;
|
|
74
|
+
if (completionIsArmed(execution))
|
|
75
|
+
completionArmedExecutionCount += 1;
|
|
76
|
+
if (execution.kind === 'awaiting-completion-authoritative-acknowledgement' ||
|
|
77
|
+
execution.kind === 'awaiting-cancellation-authoritative-acknowledgement') {
|
|
78
|
+
awaitingAuthoritativeAcknowledgementCount += 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
identityId: state.identityId,
|
|
83
|
+
publicControlSlotExecutionId: state.publicControlSlot.kind === 'empty' ? null : state.publicControlSlot.executionId,
|
|
84
|
+
retainedPhysicalExecutionCount: state.executions.length,
|
|
85
|
+
externalActiveAnimationCount,
|
|
86
|
+
completionArmedExecutionCount,
|
|
87
|
+
awaitingAuthoritativeAcknowledgementCount,
|
|
88
|
+
pendingFrameActivityCount: state.pendingFrameActivities.length,
|
|
89
|
+
identityTargetBatchDependencyEventOutboxCount: state.identityTargetBatchDependencyEventOutbox.length,
|
|
90
|
+
identityTargetBatchDependencyEventAcknowledgementWatermark: state.identityTargetBatchDependencyEventAcknowledgementWatermark,
|
|
91
|
+
authoritativeAcknowledgementWatermark: state.authoritativeAcknowledgementWatermark,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function observeExternalAnimationLedger(graph, state) {
|
|
95
|
+
const identityObservations = [];
|
|
96
|
+
let retainedPhysicalExecutionCount = 0;
|
|
97
|
+
let externalActiveAnimationCount = 0;
|
|
98
|
+
let completionArmedExecutionCount = 0;
|
|
99
|
+
let awaitingAuthoritativeAcknowledgementCount = 0;
|
|
100
|
+
let pendingFrameActivityCount = 0;
|
|
101
|
+
let identityTargetBatchDependencyEventOutboxCount = 0;
|
|
102
|
+
for (const identityAuthority of state.identityAuthorities) {
|
|
103
|
+
const observation = observeExternalAnimationIdentityAuthority(identityAuthority);
|
|
104
|
+
identityObservations.push(observation);
|
|
105
|
+
retainedPhysicalExecutionCount += observation.retainedPhysicalExecutionCount;
|
|
106
|
+
externalActiveAnimationCount += observation.externalActiveAnimationCount;
|
|
107
|
+
completionArmedExecutionCount += observation.completionArmedExecutionCount;
|
|
108
|
+
awaitingAuthoritativeAcknowledgementCount +=
|
|
109
|
+
observation.awaitingAuthoritativeAcknowledgementCount;
|
|
110
|
+
pendingFrameActivityCount += observation.pendingFrameActivityCount;
|
|
111
|
+
identityTargetBatchDependencyEventOutboxCount +=
|
|
112
|
+
observation.identityTargetBatchDependencyEventOutboxCount;
|
|
113
|
+
}
|
|
114
|
+
let openTargetBatchActivityCount = 0;
|
|
115
|
+
for (const activity of state.targetBatchAuthority.targetBatchActivities) {
|
|
116
|
+
if (targetBatchActivityIsOpen(activity))
|
|
117
|
+
openTargetBatchActivityCount += 1;
|
|
118
|
+
}
|
|
119
|
+
const combinedActiveAnimationCount = graph.activeAnimationCount() + externalActiveAnimationCount;
|
|
120
|
+
const isSettled = graph.isSettled() &&
|
|
121
|
+
externalActiveAnimationCount === 0 &&
|
|
122
|
+
identityTargetBatchDependencyEventOutboxCount === 0 &&
|
|
123
|
+
openTargetBatchActivityCount === 0 &&
|
|
124
|
+
pendingFrameActivityCount === 0;
|
|
125
|
+
return {
|
|
126
|
+
identityObservations,
|
|
127
|
+
retainedPhysicalExecutionCount,
|
|
128
|
+
externalActiveAnimationCount,
|
|
129
|
+
completionArmedExecutionCount,
|
|
130
|
+
awaitingAuthoritativeAcknowledgementCount,
|
|
131
|
+
openTargetBatchActivityCount,
|
|
132
|
+
pendingFrameActivityCount,
|
|
133
|
+
identityTargetBatchDependencyEventOutboxCount,
|
|
134
|
+
combinedActiveAnimationCount,
|
|
135
|
+
isSettled,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function observeIdentityOrThrow(state, identityId) {
|
|
139
|
+
for (const identityAuthority of state.identityAuthorities) {
|
|
140
|
+
if (identityAuthority.identityId === identityId) {
|
|
141
|
+
return observeExternalAnimationIdentityAuthority(identityAuthority);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
throw new ExternalAnimationLedgerInvariantError('identity-not-retained');
|
|
145
|
+
}
|
|
146
|
+
function stepWithEffects(state, effects = []) {
|
|
147
|
+
return { state, effects };
|
|
148
|
+
}
|
|
149
|
+
function requireIdentityAuthority(state, identityId) {
|
|
150
|
+
for (let index = 0; index < state.identityAuthorities.length; index += 1) {
|
|
151
|
+
const authority = state.identityAuthorities[index];
|
|
152
|
+
if (authority !== undefined && authority.identityId === identityId) {
|
|
153
|
+
return { index, authority };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
throw new ExternalAnimationLedgerInvariantError('identity-not-retained');
|
|
157
|
+
}
|
|
158
|
+
function replaceIdentityAuthority(state, index, authority) {
|
|
159
|
+
const identityAuthorities = [...state.identityAuthorities];
|
|
160
|
+
identityAuthorities[index] = authority;
|
|
161
|
+
return { ...state, identityAuthorities };
|
|
162
|
+
}
|
|
163
|
+
function requireExecution(authority, executionId) {
|
|
164
|
+
for (const execution of authority.executions) {
|
|
165
|
+
if (execution.executionId === executionId)
|
|
166
|
+
return execution;
|
|
167
|
+
}
|
|
168
|
+
throw new ExternalAnimationLedgerInvariantError('execution-not-retained');
|
|
169
|
+
}
|
|
170
|
+
function replaceExecution(authority, replacement) {
|
|
171
|
+
return {
|
|
172
|
+
...authority,
|
|
173
|
+
executions: authority.executions.map((execution) => execution.executionId === replacement.executionId ? replacement : execution),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function requireTargetBatchActivity(state, batchId) {
|
|
177
|
+
const activities = state.targetBatchAuthority.targetBatchActivities;
|
|
178
|
+
for (let index = 0; index < activities.length; index += 1) {
|
|
179
|
+
const activity = activities[index];
|
|
180
|
+
if (activity !== undefined && activity.batchId === batchId)
|
|
181
|
+
return { index, activity };
|
|
182
|
+
}
|
|
183
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-not-retained');
|
|
184
|
+
}
|
|
185
|
+
function requireTargetBatchDependency(activity, dependencyId) {
|
|
186
|
+
for (let index = 0; index < activity.dependencies.length; index += 1) {
|
|
187
|
+
const dependency = activity.dependencies[index];
|
|
188
|
+
if (dependency !== undefined && dependency.dependencyId === dependencyId) {
|
|
189
|
+
return { index, dependency };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
193
|
+
}
|
|
194
|
+
function replaceTargetBatchActivity(state, index, activity) {
|
|
195
|
+
const targetBatchActivities = [...state.targetBatchAuthority.targetBatchActivities];
|
|
196
|
+
targetBatchActivities[index] = activity;
|
|
197
|
+
return {
|
|
198
|
+
...state,
|
|
199
|
+
targetBatchAuthority: { ...state.targetBatchAuthority, targetBatchActivities },
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function replaceTargetBatchDependency(dependencies, index, replacement) {
|
|
203
|
+
const next = [...dependencies];
|
|
204
|
+
next[index] = replacement;
|
|
205
|
+
return next;
|
|
206
|
+
}
|
|
207
|
+
function dependencySourceEquals(left, right) {
|
|
208
|
+
if (left.kind !== right.kind)
|
|
209
|
+
return false;
|
|
210
|
+
if (left.kind === 'public-identity' && right.kind === 'public-identity') {
|
|
211
|
+
return left.identityId === right.identityId && left.executionId === right.executionId;
|
|
212
|
+
}
|
|
213
|
+
if (left.kind === 'private-control' && right.kind === 'private-control') {
|
|
214
|
+
return left.controlId === right.controlId;
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
function targetBatchDependencyEventEquals(left, right) {
|
|
219
|
+
return (left.kind === right.kind &&
|
|
220
|
+
left.batchId === right.batchId &&
|
|
221
|
+
left.dependencyId === right.dependencyId &&
|
|
222
|
+
left.valueCompletionSequence === right.valueCompletionSequence &&
|
|
223
|
+
left.eventSequence === right.eventSequence &&
|
|
224
|
+
dependencySourceEquals(left.source, right.source));
|
|
225
|
+
}
|
|
226
|
+
function requireDependencyProvenance(dependency, event) {
|
|
227
|
+
if (!dependencySourceEquals(dependency.source, event.source)) {
|
|
228
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-source-mismatch');
|
|
229
|
+
}
|
|
230
|
+
if (dependency.valueCompletionSequence !== event.valueCompletionSequence) {
|
|
231
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-value-completion-sequence-mismatch');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** Exact replay of an event the dependency already recorded; anything else at a stale sequence conflicts. */
|
|
235
|
+
function dependencyEventIsRecordedReplay(dependency, event) {
|
|
236
|
+
switch (event.kind) {
|
|
237
|
+
case 'raw-resolved':
|
|
238
|
+
return ((dependency.kind === 'waiting-value-acknowledgement' ||
|
|
239
|
+
dependency.kind === 'acknowledged') &&
|
|
240
|
+
dependency.rawResolvedEventSequence === event.eventSequence);
|
|
241
|
+
case 'value-acknowledged':
|
|
242
|
+
return (dependency.kind === 'acknowledged' &&
|
|
243
|
+
dependency.valueAcknowledgedEventSequence === event.eventSequence);
|
|
244
|
+
case 'permanently-unresolved':
|
|
245
|
+
return (dependency.kind === 'permanently-unresolved' &&
|
|
246
|
+
dependency.eventSequence === event.eventSequence);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** One exact carrier acquisition against its identity-local sequence namespace. */
|
|
250
|
+
function acquirePendingFrameActivity(state, identityId, carrierSequence) {
|
|
251
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, identityId);
|
|
252
|
+
for (const activity of authority.pendingFrameActivities) {
|
|
253
|
+
if (activity.carrierSequence === carrierSequence) {
|
|
254
|
+
throw new ExternalAnimationLedgerInvariantError('pending-frame-activity-already-retained');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const activity of authority.releasedPendingFrameActivities) {
|
|
258
|
+
if (activity.carrierSequence === carrierSequence) {
|
|
259
|
+
throw new ExternalAnimationLedgerInvariantError('pending-frame-activity-sequence-out-of-order');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const nextAuthority = {
|
|
263
|
+
...authority,
|
|
264
|
+
pendingFrameActivities: [...authority.pendingFrameActivities, { carrierSequence }],
|
|
265
|
+
};
|
|
266
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), [
|
|
267
|
+
{ kind: 'acquire-pending-frame-activity', identityId, carrierSequence },
|
|
268
|
+
]);
|
|
269
|
+
}
|
|
270
|
+
function advanceRegisterExternalAnimationIdentity(state, event) {
|
|
271
|
+
for (const authority of state.identityAuthorities) {
|
|
272
|
+
if (authority.identityId === event.identityId) {
|
|
273
|
+
throw new ExternalAnimationLedgerInvariantError('identity-id-already-retained');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const authority = {
|
|
277
|
+
identityId: event.identityId,
|
|
278
|
+
publicControlSlot: { kind: 'empty' },
|
|
279
|
+
executions: [],
|
|
280
|
+
identityTargetBatchDependencyEventOutbox: [],
|
|
281
|
+
identityTargetBatchDependencyEventAcknowledgementWatermark: 0,
|
|
282
|
+
nextIdentityTargetBatchDependencyEventSequence: 1,
|
|
283
|
+
pendingFrameActivities: [],
|
|
284
|
+
releasedPendingFrameActivities: [],
|
|
285
|
+
authoritativeAcknowledgementWatermark: 0,
|
|
286
|
+
nextExecutionCreationSequence: 1,
|
|
287
|
+
};
|
|
288
|
+
return stepWithEffects({
|
|
289
|
+
...state,
|
|
290
|
+
identityAuthorities: [...state.identityAuthorities, authority],
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function identityAuthorityIsQuiescent(state, authority) {
|
|
294
|
+
if (authority.publicControlSlot.kind !== 'empty')
|
|
295
|
+
return false;
|
|
296
|
+
if (authority.executions.length > 0)
|
|
297
|
+
return false;
|
|
298
|
+
if (authority.identityTargetBatchDependencyEventOutbox.length > 0)
|
|
299
|
+
return false;
|
|
300
|
+
if (authority.pendingFrameActivities.length > 0)
|
|
301
|
+
return false;
|
|
302
|
+
for (const activity of state.targetBatchAuthority.targetBatchActivities) {
|
|
303
|
+
if (activity.kind === 'released')
|
|
304
|
+
continue;
|
|
305
|
+
for (const dependency of activity.dependencies) {
|
|
306
|
+
if (dependency.source.kind === 'public-identity' &&
|
|
307
|
+
dependency.source.identityId === authority.identityId) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if ('authoredCarriers' in activity) {
|
|
312
|
+
for (const carrier of activity.authoredCarriers) {
|
|
313
|
+
if (carrier.identityId === authority.identityId)
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
function advanceRetireExternalAnimationIdentity(state, event) {
|
|
321
|
+
const { authority } = requireIdentityAuthority(state, event.identityId);
|
|
322
|
+
if (!identityAuthorityIsQuiescent(state, authority)) {
|
|
323
|
+
throw new ExternalAnimationLedgerInvariantError('identity-authority-not-quiescent');
|
|
324
|
+
}
|
|
325
|
+
return stepWithEffects({
|
|
326
|
+
...state,
|
|
327
|
+
identityAuthorities: state.identityAuthorities.filter((candidate) => candidate.identityId !== event.identityId),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function advanceInstallOrdinaryExecution(state, event) {
|
|
331
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
332
|
+
for (const execution of authority.executions) {
|
|
333
|
+
if (execution.executionId === event.executionId) {
|
|
334
|
+
throw new ExternalAnimationLedgerInvariantError('execution-id-already-retained');
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (event.creationSequence !== authority.nextExecutionCreationSequence) {
|
|
338
|
+
throw new ExternalAnimationLedgerInvariantError('execution-creation-sequence-mismatch');
|
|
339
|
+
}
|
|
340
|
+
const execution = {
|
|
341
|
+
kind: 'running-unarmed',
|
|
342
|
+
executionId: event.executionId,
|
|
343
|
+
controlId: event.controlId,
|
|
344
|
+
creationSequence: event.creationSequence,
|
|
345
|
+
directOperationEpoch: event.directOperationEpoch,
|
|
346
|
+
capturedTargetBatchDependencies: [],
|
|
347
|
+
};
|
|
348
|
+
const nextAuthority = {
|
|
349
|
+
...authority,
|
|
350
|
+
publicControlSlot: {
|
|
351
|
+
kind: 'installed',
|
|
352
|
+
controlId: event.controlId,
|
|
353
|
+
executionId: event.executionId,
|
|
354
|
+
},
|
|
355
|
+
executions: [...authority.executions, execution],
|
|
356
|
+
nextExecutionCreationSequence: event.creationSequence + 1,
|
|
357
|
+
};
|
|
358
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority));
|
|
359
|
+
}
|
|
360
|
+
function advanceReplacePublicControlSlot(state, event) {
|
|
361
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
362
|
+
const execution = requireExecution(authority, event.executionId);
|
|
363
|
+
if (execution.controlId !== event.controlId) {
|
|
364
|
+
throw new ExternalAnimationLedgerInvariantError('public-control-slot-reference-mismatch');
|
|
365
|
+
}
|
|
366
|
+
const nextAuthority = {
|
|
367
|
+
...authority,
|
|
368
|
+
publicControlSlot: {
|
|
369
|
+
kind: 'installed',
|
|
370
|
+
controlId: event.controlId,
|
|
371
|
+
executionId: event.executionId,
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority));
|
|
375
|
+
}
|
|
376
|
+
function advanceCaptureExecutionTargetBatchDependency(state, event) {
|
|
377
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
378
|
+
const execution = requireExecution(authority, event.executionId);
|
|
379
|
+
for (const dependency of execution.capturedTargetBatchDependencies) {
|
|
380
|
+
if (dependency.batchId === event.batchId && dependency.dependencyId === event.dependencyId) {
|
|
381
|
+
throw new ExternalAnimationLedgerInvariantError('execution-target-batch-dependency-already-captured');
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const nextExecution = {
|
|
385
|
+
...execution,
|
|
386
|
+
capturedTargetBatchDependencies: [
|
|
387
|
+
...execution.capturedTargetBatchDependencies,
|
|
388
|
+
{
|
|
389
|
+
batchId: event.batchId,
|
|
390
|
+
dependencyId: event.dependencyId,
|
|
391
|
+
valueCompletionSequence: event.valueCompletionSequence,
|
|
392
|
+
},
|
|
393
|
+
],
|
|
394
|
+
};
|
|
395
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, replaceExecution(authority, nextExecution)));
|
|
396
|
+
}
|
|
397
|
+
function advanceArmCompletionBeforeFinalChange(state, event) {
|
|
398
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
399
|
+
const execution = requireExecution(authority, event.executionId);
|
|
400
|
+
if (execution.kind !== 'running-unarmed') {
|
|
401
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
402
|
+
}
|
|
403
|
+
const nextExecution = {
|
|
404
|
+
kind: 'completion-armed-running',
|
|
405
|
+
executionId: execution.executionId,
|
|
406
|
+
controlId: execution.controlId,
|
|
407
|
+
creationSequence: execution.creationSequence,
|
|
408
|
+
directOperationEpoch: execution.directOperationEpoch,
|
|
409
|
+
capturedTargetBatchDependencies: execution.capturedTargetBatchDependencies,
|
|
410
|
+
completionSequence: event.completionSequence,
|
|
411
|
+
lifecycleEventSequence: event.lifecycleEventSequence,
|
|
412
|
+
cancellationCountAfterArm: 0,
|
|
413
|
+
};
|
|
414
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, replaceExecution(authority, nextExecution)));
|
|
415
|
+
}
|
|
416
|
+
function advanceCancelInstalledControl(state, event) {
|
|
417
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
418
|
+
const slot = authority.publicControlSlot;
|
|
419
|
+
if (slot.kind !== 'installed') {
|
|
420
|
+
throw new ExternalAnimationLedgerInvariantError('public-control-slot-reference-mismatch');
|
|
421
|
+
}
|
|
422
|
+
const execution = authority.executions.find((candidate) => candidate.executionId === slot.executionId);
|
|
423
|
+
if (execution === undefined || execution.controlId !== slot.controlId) {
|
|
424
|
+
throw new ExternalAnimationLedgerInvariantError('public-control-slot-reference-mismatch');
|
|
425
|
+
}
|
|
426
|
+
const effects = [];
|
|
427
|
+
if (execution.kind === 'completion-armed-physical-resolved' ||
|
|
428
|
+
execution.kind === 'completion-dispatching') {
|
|
429
|
+
// A stop racing an already-armed completion cannot unresolve the physical control: the cancel
|
|
430
|
+
// is recorded against the armed execution and the token-blind continuation still completes.
|
|
431
|
+
effects.push({
|
|
432
|
+
kind: 'emit-animation-cancel-listeners',
|
|
433
|
+
identityId: event.identityId,
|
|
434
|
+
executionId: execution.executionId,
|
|
435
|
+
});
|
|
436
|
+
effects.push({
|
|
437
|
+
kind: 'drain-reentrant-listener-commands',
|
|
438
|
+
identityId: event.identityId,
|
|
439
|
+
executionId: execution.executionId,
|
|
440
|
+
});
|
|
441
|
+
effects.push({
|
|
442
|
+
kind: 'clear-public-control-slot-token-blind',
|
|
443
|
+
identityId: event.identityId,
|
|
444
|
+
causedByExecutionId: execution.executionId,
|
|
445
|
+
});
|
|
446
|
+
const nextExecution = {
|
|
447
|
+
...execution,
|
|
448
|
+
cancellationCountAfterArm: execution.cancellationCountAfterArm + 1,
|
|
449
|
+
};
|
|
450
|
+
const nextAuthority = {
|
|
451
|
+
...replaceExecution(authority, nextExecution),
|
|
452
|
+
publicControlSlot: { kind: 'empty' },
|
|
453
|
+
};
|
|
454
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), effects);
|
|
455
|
+
}
|
|
456
|
+
if (execution.kind !== 'running-unarmed' && execution.kind !== 'completion-armed-running') {
|
|
457
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
458
|
+
}
|
|
459
|
+
effects.push({
|
|
460
|
+
kind: 'stop-physical-execution',
|
|
461
|
+
identityId: event.identityId,
|
|
462
|
+
executionId: execution.executionId,
|
|
463
|
+
});
|
|
464
|
+
effects.push({
|
|
465
|
+
kind: 'emit-animation-cancel-listeners',
|
|
466
|
+
identityId: event.identityId,
|
|
467
|
+
executionId: execution.executionId,
|
|
468
|
+
});
|
|
469
|
+
effects.push({
|
|
470
|
+
kind: 'drain-reentrant-listener-commands',
|
|
471
|
+
identityId: event.identityId,
|
|
472
|
+
executionId: execution.executionId,
|
|
473
|
+
});
|
|
474
|
+
effects.push({
|
|
475
|
+
kind: 'clear-public-control-slot-token-blind',
|
|
476
|
+
identityId: event.identityId,
|
|
477
|
+
causedByExecutionId: execution.executionId,
|
|
478
|
+
});
|
|
479
|
+
// A cancelled control never settles, so every captured dependency is permanently unresolved.
|
|
480
|
+
const outbox = [...authority.identityTargetBatchDependencyEventOutbox];
|
|
481
|
+
let nextEventSequence = authority.nextIdentityTargetBatchDependencyEventSequence;
|
|
482
|
+
for (const dependency of execution.capturedTargetBatchDependencies) {
|
|
483
|
+
const dependencyEvent = {
|
|
484
|
+
kind: 'permanently-unresolved',
|
|
485
|
+
batchId: dependency.batchId,
|
|
486
|
+
dependencyId: dependency.dependencyId,
|
|
487
|
+
source: {
|
|
488
|
+
kind: 'public-identity',
|
|
489
|
+
identityId: event.identityId,
|
|
490
|
+
executionId: execution.executionId,
|
|
491
|
+
},
|
|
492
|
+
valueCompletionSequence: dependency.valueCompletionSequence,
|
|
493
|
+
eventSequence: nextEventSequence,
|
|
494
|
+
};
|
|
495
|
+
nextEventSequence += 1;
|
|
496
|
+
outbox.push({ event: dependencyEvent });
|
|
497
|
+
effects.push({
|
|
498
|
+
kind: 'emit-target-batch-dependency-event',
|
|
499
|
+
identityId: event.identityId,
|
|
500
|
+
event: dependencyEvent,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
const nextExecution = {
|
|
504
|
+
kind: 'awaiting-cancellation-authoritative-acknowledgement',
|
|
505
|
+
executionId: execution.executionId,
|
|
506
|
+
controlId: execution.controlId,
|
|
507
|
+
creationSequence: execution.creationSequence,
|
|
508
|
+
directOperationEpoch: execution.directOperationEpoch,
|
|
509
|
+
capturedTargetBatchDependencies: execution.capturedTargetBatchDependencies,
|
|
510
|
+
cancellationSequence: event.cancellationSequence,
|
|
511
|
+
rawControlPromise: 'permanently-pending',
|
|
512
|
+
publicControlSlotCleared: true,
|
|
513
|
+
authoritativeCancellationAcknowledged: false,
|
|
514
|
+
};
|
|
515
|
+
const nextAuthority = {
|
|
516
|
+
...replaceExecution(authority, nextExecution),
|
|
517
|
+
publicControlSlot: { kind: 'empty' },
|
|
518
|
+
identityTargetBatchDependencyEventOutbox: outbox,
|
|
519
|
+
nextIdentityTargetBatchDependencyEventSequence: nextEventSequence,
|
|
520
|
+
};
|
|
521
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), effects);
|
|
522
|
+
}
|
|
523
|
+
/** The cancellation entry is retained until the authoritative ack and every caused outbox event. */
|
|
524
|
+
function cancellationGateIsOpen(authority) {
|
|
525
|
+
return authority.identityTargetBatchDependencyEventOutbox.length === 0;
|
|
526
|
+
}
|
|
527
|
+
function advanceAcknowledgeCancellation(state, event) {
|
|
528
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
529
|
+
const execution = requireExecution(authority, event.executionId);
|
|
530
|
+
if (execution.kind !== 'awaiting-cancellation-authoritative-acknowledgement') {
|
|
531
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
532
|
+
}
|
|
533
|
+
if (execution.cancellationSequence !== event.cancellationSequence) {
|
|
534
|
+
throw new ExternalAnimationLedgerInvariantError('cancellation-sequence-mismatch');
|
|
535
|
+
}
|
|
536
|
+
if (event.authoritativeAcknowledgementSequence <= authority.authoritativeAcknowledgementWatermark) {
|
|
537
|
+
throw new ExternalAnimationLedgerInvariantError('authoritative-acknowledgement-out-of-order');
|
|
538
|
+
}
|
|
539
|
+
const effects = [
|
|
540
|
+
{
|
|
541
|
+
kind: 'acknowledge-authoritative-cancellation',
|
|
542
|
+
identityId: event.identityId,
|
|
543
|
+
executionId: execution.executionId,
|
|
544
|
+
acknowledgementSequence: event.authoritativeAcknowledgementSequence,
|
|
545
|
+
},
|
|
546
|
+
];
|
|
547
|
+
let nextAuthority = {
|
|
548
|
+
...authority,
|
|
549
|
+
authoritativeAcknowledgementWatermark: event.authoritativeAcknowledgementSequence,
|
|
550
|
+
};
|
|
551
|
+
if (cancellationGateIsOpen(nextAuthority)) {
|
|
552
|
+
nextAuthority = {
|
|
553
|
+
...nextAuthority,
|
|
554
|
+
executions: nextAuthority.executions.filter((candidate) => candidate.executionId !== execution.executionId),
|
|
555
|
+
};
|
|
556
|
+
effects.push({
|
|
557
|
+
kind: 'release-external-animation-entry',
|
|
558
|
+
identityId: event.identityId,
|
|
559
|
+
executionId: execution.executionId,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
nextAuthority = replaceExecution(nextAuthority, {
|
|
564
|
+
...execution,
|
|
565
|
+
authoritativeCancellationAcknowledged: true,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), effects);
|
|
569
|
+
}
|
|
570
|
+
function advanceResolveArmedPhysicalExecution(state, event) {
|
|
571
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
572
|
+
const execution = requireExecution(authority, event.executionId);
|
|
573
|
+
if (execution.kind !== 'completion-armed-running') {
|
|
574
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
575
|
+
}
|
|
576
|
+
if (execution.completionSequence !== event.completionSequence) {
|
|
577
|
+
throw new ExternalAnimationLedgerInvariantError('completion-sequence-mismatch');
|
|
578
|
+
}
|
|
579
|
+
const nextExecution = {
|
|
580
|
+
...execution,
|
|
581
|
+
kind: 'completion-armed-physical-resolved',
|
|
582
|
+
};
|
|
583
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, replaceExecution(authority, nextExecution)));
|
|
584
|
+
}
|
|
585
|
+
function advanceDrainArmedCompletion(state, event) {
|
|
586
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
587
|
+
const execution = requireExecution(authority, event.executionId);
|
|
588
|
+
if (execution.kind !== 'completion-armed-running' &&
|
|
589
|
+
execution.kind !== 'completion-armed-physical-resolved') {
|
|
590
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
591
|
+
}
|
|
592
|
+
if (execution.completionSequence !== event.completionSequence) {
|
|
593
|
+
throw new ExternalAnimationLedgerInvariantError('completion-sequence-mismatch');
|
|
594
|
+
}
|
|
595
|
+
const nextExecution = {
|
|
596
|
+
...execution,
|
|
597
|
+
kind: 'completion-dispatching',
|
|
598
|
+
};
|
|
599
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, replaceExecution(authority, nextExecution)), [
|
|
600
|
+
{
|
|
601
|
+
kind: 'emit-animation-complete-listeners',
|
|
602
|
+
identityId: event.identityId,
|
|
603
|
+
executionId: execution.executionId,
|
|
604
|
+
completionSequence: execution.completionSequence,
|
|
605
|
+
},
|
|
606
|
+
]);
|
|
607
|
+
}
|
|
608
|
+
function advanceFinishCompletionDispatch(state, event) {
|
|
609
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
610
|
+
const execution = requireExecution(authority, event.executionId);
|
|
611
|
+
if (execution.kind !== 'completion-dispatching') {
|
|
612
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
613
|
+
}
|
|
614
|
+
if (execution.completionSequence !== event.completionSequence) {
|
|
615
|
+
throw new ExternalAnimationLedgerInvariantError('completion-sequence-mismatch');
|
|
616
|
+
}
|
|
617
|
+
const nextExecution = {
|
|
618
|
+
kind: 'awaiting-completion-authoritative-acknowledgement',
|
|
619
|
+
executionId: execution.executionId,
|
|
620
|
+
controlId: execution.controlId,
|
|
621
|
+
creationSequence: execution.creationSequence,
|
|
622
|
+
directOperationEpoch: execution.directOperationEpoch,
|
|
623
|
+
capturedTargetBatchDependencies: execution.capturedTargetBatchDependencies,
|
|
624
|
+
completionSequence: execution.completionSequence,
|
|
625
|
+
lifecycleEventSequence: execution.lifecycleEventSequence,
|
|
626
|
+
publicStartPromiseResolved: true,
|
|
627
|
+
};
|
|
628
|
+
const nextAuthority = {
|
|
629
|
+
...replaceExecution(authority, nextExecution),
|
|
630
|
+
publicControlSlot: { kind: 'empty' },
|
|
631
|
+
};
|
|
632
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), [
|
|
633
|
+
{
|
|
634
|
+
kind: 'drain-reentrant-listener-commands',
|
|
635
|
+
identityId: event.identityId,
|
|
636
|
+
executionId: execution.executionId,
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
kind: 'clear-public-control-slot-token-blind',
|
|
640
|
+
identityId: event.identityId,
|
|
641
|
+
causedByExecutionId: execution.executionId,
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
kind: 'resolve-public-start-promise',
|
|
645
|
+
identityId: event.identityId,
|
|
646
|
+
executionId: execution.executionId,
|
|
647
|
+
completionSequence: execution.completionSequence,
|
|
648
|
+
},
|
|
649
|
+
]);
|
|
650
|
+
}
|
|
651
|
+
function advanceAcknowledgeCompletion(state, event) {
|
|
652
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
653
|
+
const execution = requireExecution(authority, event.executionId);
|
|
654
|
+
if (execution.kind !== 'awaiting-completion-authoritative-acknowledgement') {
|
|
655
|
+
throw new ExternalAnimationLedgerInvariantError('execution-state-mismatch');
|
|
656
|
+
}
|
|
657
|
+
if (execution.completionSequence !== event.completionSequence) {
|
|
658
|
+
throw new ExternalAnimationLedgerInvariantError('completion-sequence-mismatch');
|
|
659
|
+
}
|
|
660
|
+
if (event.authoritativeAcknowledgementSequence <= authority.authoritativeAcknowledgementWatermark) {
|
|
661
|
+
throw new ExternalAnimationLedgerInvariantError('authoritative-acknowledgement-out-of-order');
|
|
662
|
+
}
|
|
663
|
+
const captured = execution.capturedTargetBatchDependencies;
|
|
664
|
+
const acknowledgements = event.targetBatchDependencyAcknowledgements;
|
|
665
|
+
if (acknowledgements.length !== captured.length) {
|
|
666
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-reference-mismatch');
|
|
667
|
+
}
|
|
668
|
+
const effects = [];
|
|
669
|
+
const outbox = [...authority.identityTargetBatchDependencyEventOutbox];
|
|
670
|
+
let nextEventSequence = authority.nextIdentityTargetBatchDependencyEventSequence;
|
|
671
|
+
for (let index = 0; index < captured.length; index += 1) {
|
|
672
|
+
const reference = captured[index];
|
|
673
|
+
const acknowledgement = acknowledgements[index];
|
|
674
|
+
if (reference === undefined || acknowledgement === undefined) {
|
|
675
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-reference-mismatch');
|
|
676
|
+
}
|
|
677
|
+
if (acknowledgement.batchId !== reference.batchId ||
|
|
678
|
+
acknowledgement.dependencyId !== reference.dependencyId ||
|
|
679
|
+
acknowledgement.valueCompletionSequence !== reference.valueCompletionSequence) {
|
|
680
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-reference-mismatch');
|
|
681
|
+
}
|
|
682
|
+
if (acknowledgement.valueAcknowledgedEventSequence !== nextEventSequence) {
|
|
683
|
+
throw new ExternalAnimationLedgerInvariantError('identity-target-batch-dependency-event-acknowledgement-out-of-order');
|
|
684
|
+
}
|
|
685
|
+
const dependencyEvent = {
|
|
686
|
+
kind: 'value-acknowledged',
|
|
687
|
+
batchId: reference.batchId,
|
|
688
|
+
dependencyId: reference.dependencyId,
|
|
689
|
+
source: {
|
|
690
|
+
kind: 'public-identity',
|
|
691
|
+
identityId: event.identityId,
|
|
692
|
+
executionId: execution.executionId,
|
|
693
|
+
},
|
|
694
|
+
valueCompletionSequence: reference.valueCompletionSequence,
|
|
695
|
+
eventSequence: nextEventSequence,
|
|
696
|
+
};
|
|
697
|
+
nextEventSequence += 1;
|
|
698
|
+
outbox.push({ event: dependencyEvent });
|
|
699
|
+
effects.push({
|
|
700
|
+
kind: 'emit-target-batch-dependency-event',
|
|
701
|
+
identityId: event.identityId,
|
|
702
|
+
event: dependencyEvent,
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
effects.push({
|
|
706
|
+
kind: 'acknowledge-authoritative-completion',
|
|
707
|
+
identityId: event.identityId,
|
|
708
|
+
executionId: execution.executionId,
|
|
709
|
+
acknowledgementSequence: event.authoritativeAcknowledgementSequence,
|
|
710
|
+
});
|
|
711
|
+
effects.push({
|
|
712
|
+
kind: 'release-external-animation-entry',
|
|
713
|
+
identityId: event.identityId,
|
|
714
|
+
executionId: execution.executionId,
|
|
715
|
+
});
|
|
716
|
+
const nextAuthority = {
|
|
717
|
+
...authority,
|
|
718
|
+
executions: authority.executions.filter((candidate) => candidate.executionId !== execution.executionId),
|
|
719
|
+
identityTargetBatchDependencyEventOutbox: outbox,
|
|
720
|
+
nextIdentityTargetBatchDependencyEventSequence: nextEventSequence,
|
|
721
|
+
authoritativeAcknowledgementWatermark: event.authoritativeAcknowledgementSequence,
|
|
722
|
+
};
|
|
723
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), effects);
|
|
724
|
+
}
|
|
725
|
+
function advanceOpenTargetBatchActivity(state, event) {
|
|
726
|
+
for (const activity of state.targetBatchAuthority.targetBatchActivities) {
|
|
727
|
+
if (activity.batchId === event.batchId) {
|
|
728
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-id-already-retained');
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
const activity = {
|
|
732
|
+
kind: 'capturing',
|
|
733
|
+
batchId: event.batchId,
|
|
734
|
+
dependencies: [],
|
|
735
|
+
aggregateReactionSequence: null,
|
|
736
|
+
};
|
|
737
|
+
return stepWithEffects({
|
|
738
|
+
...state,
|
|
739
|
+
targetBatchAuthority: {
|
|
740
|
+
...state.targetBatchAuthority,
|
|
741
|
+
targetBatchActivities: [...state.targetBatchAuthority.targetBatchActivities, activity],
|
|
742
|
+
},
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
function advanceAppendTargetBatchDependency(state, event) {
|
|
746
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.batchId);
|
|
747
|
+
if (activity.kind !== 'capturing') {
|
|
748
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
749
|
+
}
|
|
750
|
+
for (const dependency of activity.dependencies) {
|
|
751
|
+
if (dependency.dependencyId === event.dependencyId) {
|
|
752
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-id-already-retained');
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const dependency = {
|
|
756
|
+
kind: 'waiting-raw',
|
|
757
|
+
dependencyId: event.dependencyId,
|
|
758
|
+
source: event.source,
|
|
759
|
+
valueCompletionSequence: event.valueCompletionSequence,
|
|
760
|
+
eventAckWatermark: 0,
|
|
761
|
+
};
|
|
762
|
+
const nextActivity = {
|
|
763
|
+
...activity,
|
|
764
|
+
dependencies: [...activity.dependencies, dependency],
|
|
765
|
+
};
|
|
766
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
767
|
+
}
|
|
768
|
+
function advanceSealTargetBatchActivity(state, event) {
|
|
769
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.batchId);
|
|
770
|
+
if (activity.kind !== 'capturing') {
|
|
771
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-reaction-state-mismatch');
|
|
772
|
+
}
|
|
773
|
+
if (event.aggregateReactionSequence !== state.targetBatchAuthority.nextAggregateReactionSequence) {
|
|
774
|
+
throw new ExternalAnimationLedgerInvariantError('aggregate-reaction-sequence-mismatch');
|
|
775
|
+
}
|
|
776
|
+
const targetBatchAuthority = {
|
|
777
|
+
...state.targetBatchAuthority,
|
|
778
|
+
nextAggregateReactionSequence: event.aggregateReactionSequence + 1,
|
|
779
|
+
};
|
|
780
|
+
let nextState = { ...state, targetBatchAuthority };
|
|
781
|
+
const effects = [];
|
|
782
|
+
if (activity.dependencies.length === 0) {
|
|
783
|
+
// A zero-dependency seal is already resolved: every authored carrier is retained at seal,
|
|
784
|
+
// before the aggregate reaction registers.
|
|
785
|
+
for (const carrier of event.authoredCarriers) {
|
|
786
|
+
const acquisition = acquirePendingFrameActivity(nextState, carrier.identityId, carrier.carrierSequence);
|
|
787
|
+
nextState = acquisition.state;
|
|
788
|
+
effects.push(...acquisition.effects);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const sealedActivity = activity.dependencies.length === 0
|
|
792
|
+
? {
|
|
793
|
+
kind: 'sealed-empty',
|
|
794
|
+
batchId: activity.batchId,
|
|
795
|
+
aggregateReactionSequence: event.aggregateReactionSequence,
|
|
796
|
+
dependencies: [],
|
|
797
|
+
authoredCarriers: event.authoredCarriers,
|
|
798
|
+
nextCarrierRetentionIndex: event.authoredCarriers.length,
|
|
799
|
+
}
|
|
800
|
+
: {
|
|
801
|
+
kind: 'waiting-dependencies',
|
|
802
|
+
batchId: activity.batchId,
|
|
803
|
+
aggregateReactionSequence: event.aggregateReactionSequence,
|
|
804
|
+
dependencies: activity.dependencies,
|
|
805
|
+
authoredCarriers: event.authoredCarriers,
|
|
806
|
+
};
|
|
807
|
+
nextState = replaceTargetBatchActivity(nextState, activityIndex, sealedActivity);
|
|
808
|
+
effects.push({
|
|
809
|
+
kind: 'register-target-batch-aggregate-reaction',
|
|
810
|
+
batchId: event.batchId,
|
|
811
|
+
aggregateReactionSequence: event.aggregateReactionSequence,
|
|
812
|
+
});
|
|
813
|
+
return stepWithEffects(nextState, effects);
|
|
814
|
+
}
|
|
815
|
+
function advanceReportTargetBatchDependencyEvent(state, event) {
|
|
816
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.event.batchId);
|
|
817
|
+
if (activity.kind !== 'waiting-dependencies' && activity.kind !== 'permanently-unrunnable') {
|
|
818
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
819
|
+
}
|
|
820
|
+
const { index: dependencyIndex, dependency } = requireTargetBatchDependency(activity, event.event.dependencyId);
|
|
821
|
+
requireDependencyProvenance(dependency, event.event);
|
|
822
|
+
const watermark = dependency.kind === 'permanently-unresolved'
|
|
823
|
+
? dependency.eventSequence
|
|
824
|
+
: dependency.eventAckWatermark;
|
|
825
|
+
if (event.event.eventSequence <= watermark) {
|
|
826
|
+
if (dependencyEventIsRecordedReplay(dependency, event.event))
|
|
827
|
+
return stepWithEffects(state);
|
|
828
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-event-sequence-replay-conflict');
|
|
829
|
+
}
|
|
830
|
+
switch (event.event.kind) {
|
|
831
|
+
case 'raw-resolved': {
|
|
832
|
+
if (dependency.kind !== 'waiting-raw') {
|
|
833
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
834
|
+
}
|
|
835
|
+
const nextDependency = {
|
|
836
|
+
kind: 'waiting-value-acknowledgement',
|
|
837
|
+
dependencyId: dependency.dependencyId,
|
|
838
|
+
source: dependency.source,
|
|
839
|
+
valueCompletionSequence: dependency.valueCompletionSequence,
|
|
840
|
+
rawResolvedEventSequence: event.event.eventSequence,
|
|
841
|
+
eventAckWatermark: dependency.eventAckWatermark,
|
|
842
|
+
};
|
|
843
|
+
const nextActivity = {
|
|
844
|
+
...activity,
|
|
845
|
+
dependencies: replaceTargetBatchDependency(activity.dependencies, dependencyIndex, nextDependency),
|
|
846
|
+
};
|
|
847
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
848
|
+
}
|
|
849
|
+
case 'value-acknowledged': {
|
|
850
|
+
if (dependency.kind !== 'waiting-value-acknowledgement') {
|
|
851
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
852
|
+
}
|
|
853
|
+
const nextDependency = {
|
|
854
|
+
kind: 'acknowledged',
|
|
855
|
+
dependencyId: dependency.dependencyId,
|
|
856
|
+
source: dependency.source,
|
|
857
|
+
valueCompletionSequence: dependency.valueCompletionSequence,
|
|
858
|
+
rawResolvedEventSequence: dependency.rawResolvedEventSequence,
|
|
859
|
+
valueAcknowledgedEventSequence: event.event.eventSequence,
|
|
860
|
+
eventAckWatermark: dependency.eventAckWatermark,
|
|
861
|
+
};
|
|
862
|
+
const dependencies = replaceTargetBatchDependency(activity.dependencies, dependencyIndex, nextDependency);
|
|
863
|
+
const everyDependencyAcknowledged = dependencies.every((candidate) => candidate.kind === 'acknowledged');
|
|
864
|
+
const nextActivity = activity.kind === 'waiting-dependencies' && everyDependencyAcknowledged
|
|
865
|
+
? {
|
|
866
|
+
kind: 'ready-awaiting-carrier-retention',
|
|
867
|
+
batchId: activity.batchId,
|
|
868
|
+
aggregateReactionSequence: activity.aggregateReactionSequence,
|
|
869
|
+
dependencies,
|
|
870
|
+
authoredCarriers: activity.authoredCarriers,
|
|
871
|
+
nextCarrierRetentionIndex: 0,
|
|
872
|
+
}
|
|
873
|
+
: { ...activity, dependencies };
|
|
874
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
875
|
+
}
|
|
876
|
+
case 'permanently-unresolved': {
|
|
877
|
+
if (dependency.kind !== 'waiting-raw' &&
|
|
878
|
+
dependency.kind !== 'waiting-value-acknowledgement') {
|
|
879
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
880
|
+
}
|
|
881
|
+
const nextDependency = {
|
|
882
|
+
kind: 'permanently-unresolved',
|
|
883
|
+
dependencyId: dependency.dependencyId,
|
|
884
|
+
source: dependency.source,
|
|
885
|
+
valueCompletionSequence: dependency.valueCompletionSequence,
|
|
886
|
+
eventSequence: event.event.eventSequence,
|
|
887
|
+
eventAcknowledged: false,
|
|
888
|
+
};
|
|
889
|
+
const dependencies = replaceTargetBatchDependency(activity.dependencies, dependencyIndex, nextDependency);
|
|
890
|
+
const everyOtherDependencyTerminal = dependencies.every((candidate, candidateIndex) => candidateIndex === dependencyIndex ||
|
|
891
|
+
candidate.kind === 'acknowledged' ||
|
|
892
|
+
candidate.kind === 'permanently-unresolved');
|
|
893
|
+
const nextActivity = {
|
|
894
|
+
kind: 'permanently-unrunnable',
|
|
895
|
+
batchId: activity.batchId,
|
|
896
|
+
aggregateReactionSequence: activity.aggregateReactionSequence,
|
|
897
|
+
dependencies,
|
|
898
|
+
authoredCarriers: activity.authoredCarriers,
|
|
899
|
+
everyOtherDependencyTerminal,
|
|
900
|
+
unacknowledgedEventCount: (activity.kind === 'permanently-unrunnable' ? activity.unacknowledgedEventCount : 0) + 1,
|
|
901
|
+
};
|
|
902
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
function advanceAcknowledgeTargetBatchDependencyEvent(state, event) {
|
|
907
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.event.batchId);
|
|
908
|
+
if (activity.kind === 'capturing' ||
|
|
909
|
+
activity.kind === 'released' ||
|
|
910
|
+
activity.kind === 'sealed-empty') {
|
|
911
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
912
|
+
}
|
|
913
|
+
const { index: dependencyIndex, dependency } = requireTargetBatchDependency(activity, event.event.dependencyId);
|
|
914
|
+
requireDependencyProvenance(dependency, event.event);
|
|
915
|
+
let recordedEventSequence;
|
|
916
|
+
if (event.event.kind === 'raw-resolved' &&
|
|
917
|
+
(dependency.kind === 'waiting-value-acknowledgement' || dependency.kind === 'acknowledged')) {
|
|
918
|
+
recordedEventSequence = dependency.rawResolvedEventSequence;
|
|
919
|
+
}
|
|
920
|
+
else if (event.event.kind === 'value-acknowledged' && dependency.kind === 'acknowledged') {
|
|
921
|
+
recordedEventSequence = dependency.valueAcknowledgedEventSequence;
|
|
922
|
+
}
|
|
923
|
+
else if (event.event.kind === 'permanently-unresolved' &&
|
|
924
|
+
dependency.kind === 'permanently-unresolved') {
|
|
925
|
+
recordedEventSequence = dependency.eventSequence;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
929
|
+
}
|
|
930
|
+
if (event.event.eventSequence !== recordedEventSequence) {
|
|
931
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-event-sequence-replay-conflict');
|
|
932
|
+
}
|
|
933
|
+
if (dependency.kind === 'permanently-unresolved') {
|
|
934
|
+
if (dependency.eventAcknowledged)
|
|
935
|
+
return stepWithEffects(state);
|
|
936
|
+
if (activity.kind !== 'permanently-unrunnable') {
|
|
937
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-dependency-state-mismatch');
|
|
938
|
+
}
|
|
939
|
+
const unacknowledgedEventCount = activity.unacknowledgedEventCount - 1;
|
|
940
|
+
const dependencies = replaceTargetBatchDependency(activity.dependencies, dependencyIndex, {
|
|
941
|
+
...dependency,
|
|
942
|
+
eventAcknowledged: true,
|
|
943
|
+
});
|
|
944
|
+
if (unacknowledgedEventCount === 0 && activity.everyOtherDependencyTerminal) {
|
|
945
|
+
const released = {
|
|
946
|
+
kind: 'released',
|
|
947
|
+
batchId: activity.batchId,
|
|
948
|
+
terminal: 'permanently-unrunnable',
|
|
949
|
+
};
|
|
950
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, released), [
|
|
951
|
+
{
|
|
952
|
+
kind: 'release-target-batch-activity',
|
|
953
|
+
batchId: activity.batchId,
|
|
954
|
+
terminal: 'permanently-unrunnable',
|
|
955
|
+
},
|
|
956
|
+
]);
|
|
957
|
+
}
|
|
958
|
+
const nextActivity = {
|
|
959
|
+
...activity,
|
|
960
|
+
dependencies,
|
|
961
|
+
unacknowledgedEventCount,
|
|
962
|
+
};
|
|
963
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
964
|
+
}
|
|
965
|
+
if (event.event.eventSequence <= dependency.eventAckWatermark)
|
|
966
|
+
return stepWithEffects(state);
|
|
967
|
+
const nextDependency = {
|
|
968
|
+
...dependency,
|
|
969
|
+
eventAckWatermark: event.event.eventSequence,
|
|
970
|
+
};
|
|
971
|
+
const nextActivity = {
|
|
972
|
+
...activity,
|
|
973
|
+
dependencies: replaceTargetBatchDependency(activity.dependencies, dependencyIndex, nextDependency),
|
|
974
|
+
};
|
|
975
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
976
|
+
}
|
|
977
|
+
function advanceAcknowledgeIdentityTargetBatchDependencyEvent(state, event) {
|
|
978
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
979
|
+
const head = authority.identityTargetBatchDependencyEventOutbox[0];
|
|
980
|
+
if (head === undefined) {
|
|
981
|
+
throw new ExternalAnimationLedgerInvariantError('identity-target-batch-dependency-event-not-retained');
|
|
982
|
+
}
|
|
983
|
+
if (!targetBatchDependencyEventEquals(head.event, event.event)) {
|
|
984
|
+
if (event.event.eventSequence <=
|
|
985
|
+
authority.identityTargetBatchDependencyEventAcknowledgementWatermark) {
|
|
986
|
+
throw new ExternalAnimationLedgerInvariantError('identity-target-batch-dependency-event-acknowledgement-out-of-order');
|
|
987
|
+
}
|
|
988
|
+
throw new ExternalAnimationLedgerInvariantError('identity-target-batch-dependency-event-not-retained');
|
|
989
|
+
}
|
|
990
|
+
const effects = [];
|
|
991
|
+
let nextAuthority = {
|
|
992
|
+
...authority,
|
|
993
|
+
identityTargetBatchDependencyEventOutbox: authority.identityTargetBatchDependencyEventOutbox.slice(1),
|
|
994
|
+
identityTargetBatchDependencyEventAcknowledgementWatermark: event.event.eventSequence,
|
|
995
|
+
};
|
|
996
|
+
if (cancellationGateIsOpen(nextAuthority)) {
|
|
997
|
+
const retainedExecutions = [];
|
|
998
|
+
for (const execution of nextAuthority.executions) {
|
|
999
|
+
if (execution.kind === 'awaiting-cancellation-authoritative-acknowledgement' &&
|
|
1000
|
+
execution.authoritativeCancellationAcknowledged) {
|
|
1001
|
+
effects.push({
|
|
1002
|
+
kind: 'release-external-animation-entry',
|
|
1003
|
+
identityId: event.identityId,
|
|
1004
|
+
executionId: execution.executionId,
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
retainedExecutions.push(execution);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (retainedExecutions.length !== nextAuthority.executions.length) {
|
|
1012
|
+
nextAuthority = { ...nextAuthority, executions: retainedExecutions };
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), effects);
|
|
1016
|
+
}
|
|
1017
|
+
function advanceTransferNextAuthoredCarrierRetention(state, event) {
|
|
1018
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.batchId);
|
|
1019
|
+
if (activity.kind !== 'ready-awaiting-carrier-retention') {
|
|
1020
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-reaction-state-mismatch');
|
|
1021
|
+
}
|
|
1022
|
+
const carrier = activity.authoredCarriers[activity.nextCarrierRetentionIndex];
|
|
1023
|
+
if (carrier === undefined ||
|
|
1024
|
+
event.carrierIndex !== activity.nextCarrierRetentionIndex ||
|
|
1025
|
+
carrier.identityId !== event.identityId ||
|
|
1026
|
+
carrier.carrierSequence !== event.carrierSequence) {
|
|
1027
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-carrier-order-mismatch');
|
|
1028
|
+
}
|
|
1029
|
+
const acquisition = acquirePendingFrameActivity(state, event.identityId, event.carrierSequence);
|
|
1030
|
+
const nextCarrierRetentionIndex = activity.nextCarrierRetentionIndex + 1;
|
|
1031
|
+
const nextActivity = nextCarrierRetentionIndex === activity.authoredCarriers.length
|
|
1032
|
+
? {
|
|
1033
|
+
kind: 'ready-awaiting-delivery',
|
|
1034
|
+
batchId: activity.batchId,
|
|
1035
|
+
aggregateReactionSequence: activity.aggregateReactionSequence,
|
|
1036
|
+
dependencies: activity.dependencies,
|
|
1037
|
+
authoredCarriers: activity.authoredCarriers,
|
|
1038
|
+
}
|
|
1039
|
+
: { ...activity, nextCarrierRetentionIndex };
|
|
1040
|
+
return stepWithEffects(replaceTargetBatchActivity(acquisition.state, activityIndex, nextActivity), acquisition.effects);
|
|
1041
|
+
}
|
|
1042
|
+
/** Delivery once the aggregate reaction runs: the live owner observes element completion. */
|
|
1043
|
+
function deliverTargetBatchAggregate(state, activityIndex, batchId, ownerGenerationLive) {
|
|
1044
|
+
const released = {
|
|
1045
|
+
kind: 'released',
|
|
1046
|
+
batchId,
|
|
1047
|
+
terminal: 'delivered',
|
|
1048
|
+
};
|
|
1049
|
+
const effects = [];
|
|
1050
|
+
if (ownerGenerationLive) {
|
|
1051
|
+
effects.push({ kind: 'emit-target-owner-element-completion', batchId });
|
|
1052
|
+
}
|
|
1053
|
+
effects.push({ kind: 'release-target-batch-activity', batchId, terminal: 'delivered' });
|
|
1054
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, released), effects);
|
|
1055
|
+
}
|
|
1056
|
+
function advanceReactToTargetBatchAggregate(state, event) {
|
|
1057
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.batchId);
|
|
1058
|
+
if (activity.kind === 'ready-awaiting-carrier-retention') {
|
|
1059
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-carrier-retention-incomplete');
|
|
1060
|
+
}
|
|
1061
|
+
if (activity.kind !== 'ready-awaiting-delivery' && activity.kind !== 'sealed-empty') {
|
|
1062
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-reaction-state-mismatch');
|
|
1063
|
+
}
|
|
1064
|
+
if (activity.aggregateReactionSequence !== event.aggregateReactionSequence) {
|
|
1065
|
+
throw new ExternalAnimationLedgerInvariantError('aggregate-reaction-sequence-mismatch');
|
|
1066
|
+
}
|
|
1067
|
+
if (event.orchestrationPending) {
|
|
1068
|
+
const nextActivity = {
|
|
1069
|
+
kind: 'waiting-orchestration',
|
|
1070
|
+
batchId: activity.batchId,
|
|
1071
|
+
aggregateReactionSequence: activity.aggregateReactionSequence,
|
|
1072
|
+
dependencies: activity.dependencies,
|
|
1073
|
+
authoredCarriers: activity.authoredCarriers,
|
|
1074
|
+
};
|
|
1075
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, nextActivity));
|
|
1076
|
+
}
|
|
1077
|
+
return deliverTargetBatchAggregate(state, activityIndex, event.batchId, event.ownerGenerationLive);
|
|
1078
|
+
}
|
|
1079
|
+
function advanceCompleteTargetBatchOrchestration(state, event) {
|
|
1080
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.batchId);
|
|
1081
|
+
if (activity.kind !== 'waiting-orchestration') {
|
|
1082
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-reaction-state-mismatch');
|
|
1083
|
+
}
|
|
1084
|
+
if (activity.aggregateReactionSequence !== event.aggregateReactionSequence) {
|
|
1085
|
+
throw new ExternalAnimationLedgerInvariantError('aggregate-reaction-sequence-mismatch');
|
|
1086
|
+
}
|
|
1087
|
+
return deliverTargetBatchAggregate(state, activityIndex, event.batchId, event.ownerGenerationLive);
|
|
1088
|
+
}
|
|
1089
|
+
function advanceReleaseTargetBatchActivity(state, event) {
|
|
1090
|
+
const { index: activityIndex, activity } = requireTargetBatchActivity(state, event.batchId);
|
|
1091
|
+
if (activity.kind !== 'ready-awaiting-delivery') {
|
|
1092
|
+
throw new ExternalAnimationLedgerInvariantError('target-batch-release-before-terminal');
|
|
1093
|
+
}
|
|
1094
|
+
const released = {
|
|
1095
|
+
kind: 'released',
|
|
1096
|
+
batchId: activity.batchId,
|
|
1097
|
+
terminal: event.terminal,
|
|
1098
|
+
};
|
|
1099
|
+
return stepWithEffects(replaceTargetBatchActivity(state, activityIndex, released), [
|
|
1100
|
+
{ kind: 'release-target-batch-activity', batchId: activity.batchId, terminal: event.terminal },
|
|
1101
|
+
]);
|
|
1102
|
+
}
|
|
1103
|
+
function advanceAcquirePendingFrameActivity(state, event) {
|
|
1104
|
+
return acquirePendingFrameActivity(state, event.identityId, event.carrierSequence);
|
|
1105
|
+
}
|
|
1106
|
+
function advanceReleasePendingFrameActivity(state, event) {
|
|
1107
|
+
const { index: authorityIndex, authority } = requireIdentityAuthority(state, event.identityId);
|
|
1108
|
+
const head = authority.pendingFrameActivities[0];
|
|
1109
|
+
if (head === undefined) {
|
|
1110
|
+
throw new ExternalAnimationLedgerInvariantError('pending-frame-activity-not-retained');
|
|
1111
|
+
}
|
|
1112
|
+
if (head.carrierSequence !== event.carrierSequence) {
|
|
1113
|
+
for (const activity of authority.pendingFrameActivities) {
|
|
1114
|
+
if (activity.carrierSequence === event.carrierSequence) {
|
|
1115
|
+
throw new ExternalAnimationLedgerInvariantError('pending-frame-activity-release-order-mismatch');
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
throw new ExternalAnimationLedgerInvariantError('pending-frame-activity-not-retained');
|
|
1119
|
+
}
|
|
1120
|
+
const nextAuthority = {
|
|
1121
|
+
...authority,
|
|
1122
|
+
pendingFrameActivities: authority.pendingFrameActivities.slice(1),
|
|
1123
|
+
releasedPendingFrameActivities: [
|
|
1124
|
+
...authority.releasedPendingFrameActivities,
|
|
1125
|
+
{ carrierSequence: event.carrierSequence },
|
|
1126
|
+
],
|
|
1127
|
+
};
|
|
1128
|
+
return stepWithEffects(replaceIdentityAuthority(state, authorityIndex, nextAuthority), [
|
|
1129
|
+
{
|
|
1130
|
+
kind: 'release-pending-frame-activity',
|
|
1131
|
+
identityId: event.identityId,
|
|
1132
|
+
carrierSequence: event.carrierSequence,
|
|
1133
|
+
},
|
|
1134
|
+
]);
|
|
1135
|
+
}
|
|
1136
|
+
function advanceExternalAnimationLedger(state, event) {
|
|
1137
|
+
switch (event.kind) {
|
|
1138
|
+
case 'register-external-animation-identity':
|
|
1139
|
+
return advanceRegisterExternalAnimationIdentity(state, event);
|
|
1140
|
+
case 'retire-external-animation-identity':
|
|
1141
|
+
return advanceRetireExternalAnimationIdentity(state, event);
|
|
1142
|
+
case 'install-ordinary-execution':
|
|
1143
|
+
return advanceInstallOrdinaryExecution(state, event);
|
|
1144
|
+
case 'replace-public-control-slot':
|
|
1145
|
+
return advanceReplacePublicControlSlot(state, event);
|
|
1146
|
+
case 'capture-execution-target-batch-dependency':
|
|
1147
|
+
return advanceCaptureExecutionTargetBatchDependency(state, event);
|
|
1148
|
+
case 'arm-completion-before-final-change':
|
|
1149
|
+
return advanceArmCompletionBeforeFinalChange(state, event);
|
|
1150
|
+
case 'cancel-installed-control':
|
|
1151
|
+
return advanceCancelInstalledControl(state, event);
|
|
1152
|
+
case 'acknowledge-cancellation':
|
|
1153
|
+
return advanceAcknowledgeCancellation(state, event);
|
|
1154
|
+
case 'resolve-armed-physical-execution':
|
|
1155
|
+
return advanceResolveArmedPhysicalExecution(state, event);
|
|
1156
|
+
case 'drain-armed-completion':
|
|
1157
|
+
return advanceDrainArmedCompletion(state, event);
|
|
1158
|
+
case 'finish-completion-dispatch':
|
|
1159
|
+
return advanceFinishCompletionDispatch(state, event);
|
|
1160
|
+
case 'acknowledge-completion':
|
|
1161
|
+
return advanceAcknowledgeCompletion(state, event);
|
|
1162
|
+
case 'open-target-batch-activity':
|
|
1163
|
+
return advanceOpenTargetBatchActivity(state, event);
|
|
1164
|
+
case 'append-target-batch-dependency':
|
|
1165
|
+
return advanceAppendTargetBatchDependency(state, event);
|
|
1166
|
+
case 'seal-target-batch-activity':
|
|
1167
|
+
return advanceSealTargetBatchActivity(state, event);
|
|
1168
|
+
case 'report-target-batch-dependency-event':
|
|
1169
|
+
return advanceReportTargetBatchDependencyEvent(state, event);
|
|
1170
|
+
case 'acknowledge-target-batch-dependency-event':
|
|
1171
|
+
return advanceAcknowledgeTargetBatchDependencyEvent(state, event);
|
|
1172
|
+
case 'acknowledge-identity-target-batch-dependency-event':
|
|
1173
|
+
return advanceAcknowledgeIdentityTargetBatchDependencyEvent(state, event);
|
|
1174
|
+
case 'transfer-next-authored-carrier-retention':
|
|
1175
|
+
return advanceTransferNextAuthoredCarrierRetention(state, event);
|
|
1176
|
+
case 'react-to-target-batch-aggregate':
|
|
1177
|
+
return advanceReactToTargetBatchAggregate(state, event);
|
|
1178
|
+
case 'complete-target-batch-orchestration':
|
|
1179
|
+
return advanceCompleteTargetBatchOrchestration(state, event);
|
|
1180
|
+
case 'release-target-batch-activity':
|
|
1181
|
+
return advanceReleaseTargetBatchActivity(state, event);
|
|
1182
|
+
case 'acquire-pending-frame-activity':
|
|
1183
|
+
return advanceAcquirePendingFrameActivity(state, event);
|
|
1184
|
+
case 'release-pending-frame-activity':
|
|
1185
|
+
return advanceReleasePendingFrameActivity(state, event);
|
|
1186
|
+
default: {
|
|
1187
|
+
// An untyped/JS caller can reach this with a kind outside the union: fail closed with the
|
|
1188
|
+
// typed invariant (never return the event as a step — that would corrupt retained state).
|
|
1189
|
+
throw new ExternalAnimationLedgerInvariantError('ledger-event-kind-unrecognized');
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
class ExternalAnimationLedgerImpl {
|
|
1194
|
+
#graph;
|
|
1195
|
+
#state;
|
|
1196
|
+
/** Graph settle handles mirroring retained pending-frame carriers (REQ-SETTLE-001). */
|
|
1197
|
+
#pendingFrameActivityHandles = new Map();
|
|
1198
|
+
constructor(graph, state) {
|
|
1199
|
+
this.#graph = graph;
|
|
1200
|
+
this.#state = state;
|
|
1201
|
+
this.#reconcilePendingFrameActivities();
|
|
1202
|
+
}
|
|
1203
|
+
observe() {
|
|
1204
|
+
return observeExternalAnimationLedger(this.#graph, this.#state);
|
|
1205
|
+
}
|
|
1206
|
+
observeState() {
|
|
1207
|
+
return this.#state;
|
|
1208
|
+
}
|
|
1209
|
+
observeIdentity(identityId) {
|
|
1210
|
+
return observeIdentityOrThrow(this.#state, identityId);
|
|
1211
|
+
}
|
|
1212
|
+
advance(event) {
|
|
1213
|
+
const step = advanceExternalAnimationLedger(this.#state, event);
|
|
1214
|
+
this.#state = step.state;
|
|
1215
|
+
this.#reconcilePendingFrameActivities();
|
|
1216
|
+
return {
|
|
1217
|
+
state: step.state,
|
|
1218
|
+
observation: observeExternalAnimationLedger(this.#graph, step.state),
|
|
1219
|
+
orderedEffects: step.effects,
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
/** Every retained carrier holds one open graph activity; released carriers release theirs. */
|
|
1223
|
+
#reconcilePendingFrameActivities() {
|
|
1224
|
+
for (const authority of this.#state.identityAuthorities) {
|
|
1225
|
+
let handles = this.#pendingFrameActivityHandles.get(authority.identityId);
|
|
1226
|
+
for (const activity of authority.pendingFrameActivities) {
|
|
1227
|
+
if (handles?.has(activity.carrierSequence))
|
|
1228
|
+
continue;
|
|
1229
|
+
if (handles === undefined) {
|
|
1230
|
+
handles = new Map();
|
|
1231
|
+
this.#pendingFrameActivityHandles.set(authority.identityId, handles);
|
|
1232
|
+
}
|
|
1233
|
+
handles.set(activity.carrierSequence, this.#graph.registerActivity());
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
for (const [identityId, handles] of this.#pendingFrameActivityHandles) {
|
|
1237
|
+
const authority = this.#state.identityAuthorities.find((candidate) => candidate.identityId === identityId);
|
|
1238
|
+
for (const [carrierSequence, handle] of handles) {
|
|
1239
|
+
const retained = authority?.pendingFrameActivities.some((activity) => activity.carrierSequence === carrierSequence);
|
|
1240
|
+
if (retained !== true) {
|
|
1241
|
+
handle.release();
|
|
1242
|
+
handles.delete(carrierSequence);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
function createExternalAnimationLedger(options) {
|
|
1249
|
+
return new ExternalAnimationLedgerImpl(options.graph, options.initialState ?? initialExternalAnimationLedgerState());
|
|
1250
|
+
}
|