@raphy-js/raph 1.0.1-dev.20 → 1.0.1-dev.25
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/dist/{core-CyilRD7s.js → core-CClhT8WR.js} +389 -400
- package/dist/core-cPQWYD4K.cjs +1 -0
- package/dist/debug/api/Raph.cjs +1 -1
- package/dist/debug/api/Raph.js +46 -47
- package/dist/debug/api/RaphFacadeSession.cjs +1 -1
- package/dist/debug/api/RaphFacadeSession.js +85 -89
- package/dist/debug/api/RaphReactivityInstance.cjs +1 -1
- package/dist/debug/api/RaphReactivityInstance.js +27 -30
- package/dist/debug/path/DataPath.cjs +1 -1
- package/dist/debug/path/DataPath.js +198 -202
- package/dist/debug/path/PathRouter.cjs +1 -1
- package/dist/debug/path/PathRouter.js +44 -50
- package/dist/debug/path/RaphKernel.cjs +1 -1
- package/dist/debug/path/RaphKernel.js +257 -228
- package/dist/debug/path/data-path.utils.cjs +1 -1
- package/dist/debug/path/data-path.utils.js +41 -41
- package/dist/debug/path/derived/DerivedRegistry.cjs +1 -1
- package/dist/debug/path/derived/DerivedRegistry.js +52 -66
- package/dist/debug/path/derived/RaphDerivedHandle.cjs +1 -1
- package/dist/debug/path/derived/RaphDerivedHandle.js +19 -22
- package/dist/debug/path/derived/derived-collection.utils.cjs +1 -1
- package/dist/debug/path/derived/derived-collection.utils.js +70 -70
- package/dist/debug/path/meta/MetaController.cjs +1 -1
- package/dist/debug/path/meta/MetaController.js +57 -65
- package/dist/debug/path/meta/RaphMeta.cjs +1 -1
- package/dist/debug/path/meta/RaphMeta.js +26 -32
- package/dist/debug/path/meta/RaphMetaStore.cjs +1 -1
- package/dist/debug/path/meta/RaphMetaStore.js +24 -30
- package/dist/debug/path/storage/AdapterPathStore.cjs +1 -1
- package/dist/debug/path/storage/AdapterPathStore.js +49 -54
- package/dist/debug/path/storage/DefaultDataAdapter.cjs +1 -1
- package/dist/debug/path/storage/DefaultDataAdapter.js +46 -56
- package/dist/debug/path/storage/PathEventHub.cjs +1 -1
- package/dist/debug/path/storage/PathEventHub.js +74 -61
- package/dist/debug/path/storage/PathStore.cjs +1 -1
- package/dist/debug/path/storage/PathStore.js +89 -99
- package/dist/debug/path/storage/createDataDraft.cjs +1 -1
- package/dist/debug/path/storage/createDataDraft.js +24 -28
- package/dist/debug/reactivity/disposal.cjs +1 -0
- package/dist/debug/reactivity/disposal.js +32 -0
- package/dist/debug/reactivity/engine.cjs +1 -1
- package/dist/debug/reactivity/engine.js +787 -392
- package/dist/debug/reactivity/reactive-object.cjs +1 -1
- package/dist/debug/reactivity/reactive-object.js +81 -96
- package/dist/debug/reactivity/scope.cjs +1 -1
- package/dist/debug/reactivity/scope.js +221 -117
- package/dist/debug/reactivity/watch.cjs +1 -1
- package/dist/debug/reactivity/watch.js +1 -1
- package/dist/debug/runtime/RaphRuntime.cjs +1 -1
- package/dist/debug/runtime/RaphRuntime.js +764 -411
- package/dist/debug/runtime/diagnostics/RaphDebug.cjs +1 -1
- package/dist/debug/runtime/diagnostics/RaphDebug.js +90 -100
- package/dist/debug/runtime/ownership/RaphEffectScope.cjs +1 -1
- package/dist/debug/runtime/ownership/RaphEffectScope.js +215 -82
- package/dist/debug/runtime/ownership/RaphNode.cjs +1 -1
- package/dist/debug/runtime/ownership/RaphNode.js +595 -136
- package/dist/debug/runtime/ownership/RaphScope.cjs +1 -1
- package/dist/debug/runtime/ownership/RaphScope.js +203 -68
- package/dist/debug/runtime/phases/RaphFrameLoop.cjs +1 -1
- package/dist/debug/runtime/phases/RaphFrameLoop.js +13 -17
- package/dist/debug/runtime/phases/RaphPhasePipeline.cjs +1 -1
- package/dist/debug/runtime/phases/RaphPhasePipeline.js +618 -357
- package/dist/debug/runtime/phases/RaphPhaseTaskRecord.cjs +1 -0
- package/dist/debug/runtime/phases/RaphPhaseTaskRecord.js +176 -0
- package/dist/debug/runtime/phases/RaphPhaseWatch.cjs +1 -0
- package/dist/debug/runtime/phases/RaphPhaseWatch.js +20 -0
- package/dist/debug/runtime/reactivity/PathHandle.cjs +1 -1
- package/dist/debug/runtime/reactivity/PathHandle.js +50 -60
- package/dist/debug/runtime/reactivity/RaphMetaSource.cjs +1 -1
- package/dist/debug/runtime/reactivity/RaphMetaSource.js +28 -32
- package/dist/debug/runtime/reactivity/RaphSelector.cjs +1 -0
- package/dist/debug/runtime/reactivity/RaphSelector.js +503 -0
- package/dist/debug/runtime/reactivity/RaphWorkGraph.cjs +1 -1
- package/dist/debug/runtime/reactivity/RaphWorkGraph.js +541 -379
- package/dist/debug/runtime/reactivity/RuntimeReactivity.cjs +1 -1
- package/dist/debug/runtime/reactivity/RuntimeReactivity.js +484 -90
- package/dist/debug/runtime/reactivity/RuntimeWatch.cjs +1 -1
- package/dist/debug/runtime/reactivity/RuntimeWatch.js +33 -44
- package/dist/debug/runtime/reactivity/disposal-sweep.utils.cjs +1 -0
- package/dist/debug/runtime/reactivity/disposal-sweep.utils.js +42 -0
- package/dist/entrypoints/runtime.d.ts +2 -1
- package/dist/machine-testing.cjs +1 -1
- package/dist/machine-testing.js +1 -1
- package/dist/machine.cjs +1 -1
- package/dist/machine.js +216 -231
- package/dist/path/RaphKernel.d.ts +24 -0
- package/dist/path/storage/PathEventHub.d.ts +14 -0
- package/dist/path/storage/PathStore.d.ts +4 -4
- package/dist/reactivity/disposal.d.ts +14 -0
- package/dist/reactivity/engine.d.ts +177 -4
- package/dist/reactivity/scope.d.ts +108 -30
- package/dist/runtime/RaphRuntime.d.ts +236 -17
- package/dist/runtime/ownership/RaphEffectScope.d.ts +70 -1
- package/dist/runtime/ownership/RaphNode.d.ts +276 -6
- package/dist/runtime/ownership/RaphScope.d.ts +144 -14
- package/dist/runtime/phases/RaphPhasePipeline.d.ts +202 -28
- package/dist/runtime/phases/RaphPhaseTaskRecord.d.ts +195 -0
- package/dist/runtime/phases/RaphPhaseWatch.d.ts +22 -0
- package/dist/runtime/phases/phase.types.d.ts +83 -4
- package/dist/runtime/reactivity/RaphSelector.d.ts +338 -0
- package/dist/runtime/reactivity/RaphWorkGraph.d.ts +163 -25
- package/dist/runtime/reactivity/RuntimeReactivity.d.ts +258 -10
- package/dist/runtime/reactivity/RuntimeWatch.d.ts +8 -8
- package/dist/runtime/reactivity/disposal-sweep.utils.d.ts +19 -0
- package/dist/runtime/reactivity/runtime-reactivity.types.d.ts +48 -3
- package/package.json +9 -1
- package/dist/core-ugchZNqC.cjs +0 -1
|
@@ -1,25 +1,37 @@
|
|
|
1
|
-
import { Disposer, EffectTask } from '../../reactivity/types';
|
|
2
1
|
import { RaphScope } from '../ownership/RaphScope';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import { RuntimeReactivity } from '../reactivity/RuntimeReactivity';
|
|
3
|
+
import { RaphFrameContext, RaphPhase, RaphPhaseEffect, RaphSchedulerMode, RaphWorkCause } from './phase.types';
|
|
4
|
+
import { RaphPhaseTaskRecord } from './RaphPhaseTaskRecord';
|
|
5
|
+
/**
|
|
6
|
+
* Постоянное состояние одной phase: живая очередь, запасной массив для обмена
|
|
7
|
+
* при разборе и число bindings. Состояние переживает переопределение phases, пока
|
|
8
|
+
* имя phase сохраняется, поэтому запись binding держит прямую ссылку на него и
|
|
9
|
+
* ставится в очередь без поиска по имени. Через него же запись находит свой
|
|
10
|
+
* pipeline: отдельной ссылки на pipeline у записи нет.
|
|
11
|
+
*
|
|
12
|
+
* Инвариант: каждая запись живой очереди активна и ожидает доставки, а
|
|
13
|
+
* `spare` пуст вне разбора своей phase.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RaphPhaseState {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly pipeline: RaphPhasePipeline;
|
|
18
|
+
queue: RaphPhaseTaskRecord[];
|
|
19
|
+
spare: RaphPhaseTaskRecord[];
|
|
20
|
+
bindings: number;
|
|
21
|
+
constructor(name: string, pipeline: RaphPhasePipeline);
|
|
11
22
|
}
|
|
12
23
|
/** Единая Runtime-owned очередь persistent phase bindings. */
|
|
13
24
|
export declare class RaphPhasePipeline {
|
|
14
25
|
private readonly _scheduler;
|
|
15
|
-
private readonly
|
|
26
|
+
private readonly _reactivity;
|
|
16
27
|
private readonly _resolveCause;
|
|
17
28
|
private readonly _deferSync;
|
|
18
29
|
private readonly _tracePhase?;
|
|
19
30
|
private _phases;
|
|
20
31
|
private _phaseByName;
|
|
21
|
-
private
|
|
22
|
-
private
|
|
32
|
+
private _resolvedInput;
|
|
33
|
+
private _resolvedState;
|
|
34
|
+
private _queuedCount;
|
|
23
35
|
private readonly _deferred;
|
|
24
36
|
private readonly _idleWaiters;
|
|
25
37
|
private _taskSequence;
|
|
@@ -28,51 +40,213 @@ export declare class RaphPhasePipeline {
|
|
|
28
40
|
private _frameHandle;
|
|
29
41
|
private _frameHandleKind;
|
|
30
42
|
private _configurationLocked;
|
|
31
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Flush исполняется сейчас: новая работа попадёт в его следующий проход.
|
|
45
|
+
*
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
_running: boolean;
|
|
49
|
+
private _idleSlotFlags;
|
|
32
50
|
private _disposed;
|
|
33
51
|
private _firstFrameAt;
|
|
34
52
|
private _lastFrameAt;
|
|
35
53
|
private _frameNumber;
|
|
36
54
|
private _frame;
|
|
37
|
-
constructor(_scheduler: RaphSchedulerMode,
|
|
55
|
+
constructor(_scheduler: RaphSchedulerMode, _reactivity: RuntimeReactivity, _resolveCause: (token: unknown) => RaphWorkCause | undefined, _deferSync?: () => boolean, _tracePhase?: ((phase: string, status: "end" | "error" | "start", error?: unknown) => void) | undefined);
|
|
38
56
|
get frame(): RaphFrameContext;
|
|
39
57
|
get phases(): readonly RaphPhase[];
|
|
40
58
|
definePhases(phases: readonly RaphPhase[]): void;
|
|
41
59
|
addPhase(phase: RaphPhase): void;
|
|
42
60
|
clearPhases(): void;
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Находит состояние объявленной phase по имени, которое передал вызывающий.
|
|
63
|
+
* Подряд идущие bindings одной phase попадают в кэш из одной записи; точное
|
|
64
|
+
* имя находится без `trim()`, и только имя с пробелами по краям нормализуется.
|
|
65
|
+
*/
|
|
66
|
+
resolvePhase(input: string): RaphPhaseState;
|
|
67
|
+
/**
|
|
68
|
+
* Создаёт постоянную запись одного binding. Вызывающий уже разрешил phase
|
|
69
|
+
* и проверил priority, поэтому горячий путь регистрации проверяет их один раз.
|
|
70
|
+
* Запись ничего не выполняет, пока вызывающий не подключит engine effect
|
|
71
|
+
* (`node`) или watch lifecycle (`watch`).
|
|
72
|
+
*/
|
|
73
|
+
bindTask(phase: RaphPhaseState, consumerOwner: RaphScope, effect: RaphPhaseEffect, priority: number): RaphPhaseTaskRecord;
|
|
74
|
+
/**
|
|
75
|
+
* Создаёт постоянную запись группы из `count` slots: одна запись binding и
|
|
76
|
+
* её состояние с местом под запись графа каждого slot. Вызывающий уже
|
|
77
|
+
* разрешил phase и проверил priority и `count`; записи графа он подключает
|
|
78
|
+
* сам через _allocateTaskEffect() и кладёт в состояние по порядку slots.
|
|
79
|
+
* Slot ничего не исполняет, пока его запись не запланирована.
|
|
80
|
+
*
|
|
81
|
+
* `runSlot` занимает место callback записи: его показывает `task.effect`
|
|
82
|
+
* группы, а вызывает только общее тело записей engine — с состоянием
|
|
83
|
+
* группы, где лежат её context и номер slot. Поэтому запись группы несёт
|
|
84
|
+
* `TASK_TASKLESS`: вместо task тело получает поле `group`.
|
|
85
|
+
*/
|
|
86
|
+
bindGroup(phase: RaphPhaseState, consumerOwner: RaphScope, runSlot: RaphPhaseEffect, context: unknown, count: number, priority: number): RaphPhaseTaskRecord;
|
|
45
87
|
request(): void;
|
|
46
88
|
afterFlush(): Promise<void>;
|
|
47
89
|
flush(): void;
|
|
48
90
|
dispose(): void;
|
|
49
91
|
_debugPending(): boolean;
|
|
50
92
|
private _describeQueued;
|
|
51
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Открывает доставку одной записью `state`. Накопленные причины остаются на
|
|
95
|
+
* своих местах и становятся причинами доставки под TASK_CAUSES_DELIVERED:
|
|
96
|
+
* ничего не копируется, а массив для `task.causes` строится, только если его
|
|
97
|
+
* прочитают. Frame доставки получает только запись с facade: без facade его
|
|
98
|
+
* нечем прочитать, а запись ссылки на новый frame в каждую доставленную
|
|
99
|
+
* запись стоит write barrier.
|
|
100
|
+
*
|
|
101
|
+
* Запись попадает сюда только из живой очереди, а в ней нет записей с
|
|
102
|
+
* открытой доставкой, поэтому причин прошлой доставки здесь не бывает.
|
|
103
|
+
*/
|
|
52
104
|
private _beginDelivery;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Исполняет открытую доставку binding. `failures` передаёт default runner:
|
|
107
|
+
* группа складывает туда сбой каждого slot отдельно. Без него сбои slots
|
|
108
|
+
* бросаются вызывающему — `task.run()` внутри `process` — после прохода: один
|
|
109
|
+
* как есть, несколько одним AggregateError.
|
|
110
|
+
*
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
_runDelivery(record: RaphPhaseTaskRecord, failures?: unknown[]): void;
|
|
114
|
+
/** @internal */
|
|
115
|
+
_deferDelivery(record: RaphPhaseTaskRecord): void;
|
|
116
|
+
/** @internal */
|
|
117
|
+
_cancelDelivery(record: RaphPhaseTaskRecord): void;
|
|
56
118
|
private _finishDelivery;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Делает исполняемыми slots группы, которые остались запланированными после
|
|
121
|
+
* доставки: slot, изменивший собственную зависимость, и slot, чью зависимость
|
|
122
|
+
* изменили после его запуска. Slot, который не запланирован, не трогается:
|
|
123
|
+
* он уже прочитал актуальные значения и в следующей доставке не исполняется.
|
|
124
|
+
* Запись, сделанную до запуска slot в той же доставке, slot читает в ней же:
|
|
125
|
+
* прямую запись engine переводит в обязательный запуск, отложенную находит
|
|
126
|
+
* проверка зависимостей. Отдельный binding после такой записи исполняется
|
|
127
|
+
* ещё раз следующим проходом; slot повторно не исполняется.
|
|
128
|
+
*
|
|
129
|
+
* Запись без зависимостей пропускается. Запланированной она бывает в двух
|
|
130
|
+
* случаях: slot запущен во время доставки и ждёт первого запуска — тогда она
|
|
131
|
+
* исполняема с момента создания; либо повторно запущен slot, который уже
|
|
132
|
+
* исполнился и ничего не прочитал, — тогда исполнять его нечем.
|
|
133
|
+
*/
|
|
134
|
+
private _rearmGroup;
|
|
135
|
+
/** @internal */
|
|
136
|
+
_notifyTask(record: RaphPhaseTaskRecord, token: unknown): void;
|
|
137
|
+
/** @internal */
|
|
138
|
+
_appendCause(record: RaphPhaseTaskRecord, cause: RaphWorkCause, key: object): void;
|
|
139
|
+
/**
|
|
140
|
+
* Переносит причины открытой доставки с места ожидающих в detail, без
|
|
141
|
+
* копирования, и освобождает это место. После вызова запись хранит оба
|
|
142
|
+
* набора раздельно: ожидающие причины — в своих полях, причины доставки — в
|
|
143
|
+
* detail. Массив, уже построенный для `task.causes`, остаётся тем же.
|
|
144
|
+
*/
|
|
145
|
+
private _splitDeliveredCauses;
|
|
146
|
+
/** @internal */
|
|
147
|
+
_queue(record: RaphPhaseTaskRecord, schedule?: boolean): void;
|
|
148
|
+
/** @internal */
|
|
149
|
+
_disposeTask(record: RaphPhaseTaskRecord): void;
|
|
150
|
+
/**
|
|
151
|
+
* Полностью освобождает один binding; каждый шаг идемпотентен, поэтому
|
|
152
|
+
* владелец, engine и пользователь могут прийти сюда в любом порядке и
|
|
153
|
+
* повторно войти в метод.
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
156
|
+
*/
|
|
157
|
+
_disposeBinding(record: RaphPhaseTaskRecord): void;
|
|
158
|
+
/**
|
|
159
|
+
* Убирает из состояния группы slots, записи графа которых уже остановлены, и
|
|
160
|
+
* сообщает, остались ли при этом живые slots.
|
|
161
|
+
*
|
|
162
|
+
* Запись одного slot останавливается отдельно от группы в трёх случаях: упал
|
|
163
|
+
* cleanup перед повторным запуском slot; перезапускается или останавливается
|
|
164
|
+
* effect, внутри тела которого группа зарегистрирована; провалился запуск
|
|
165
|
+
* slot через `start()`. Engine сообщает о каждой такой остановке через task
|
|
166
|
+
* hook, общий для всех slots, и запись в этот момент уже остановлена. Группа
|
|
167
|
+
* тогда теряет только этот slot, как owner потерял бы один из отдельных
|
|
168
|
+
* bindings; запись binding освобождается, когда остановлен последний slot.
|
|
169
|
+
*
|
|
170
|
+
* Вызов владельца или effect scope остановленных записей не находит: место
|
|
171
|
+
* остановленного slot уже занято `null`.
|
|
172
|
+
*
|
|
173
|
+
* @returns true, если вызов был сообщением engine и живые slots остались.
|
|
174
|
+
*/
|
|
175
|
+
private _forgetStoppedSlots;
|
|
176
|
+
/**
|
|
177
|
+
* Останавливает записи графа всех slots группы в порядке, обратном порядку
|
|
178
|
+
* slots: так владелец освобождал бы отдельные bindings. Сбой cleanup одного
|
|
179
|
+
* slot не мешает остановить остальные.
|
|
180
|
+
*
|
|
181
|
+
* Группа — один ресурс, поэтому порядок и вид ошибки не зависят от того, кто
|
|
182
|
+
* её освобождает. Effect scope отдельные bindings освободил бы в порядке
|
|
183
|
+
* регистрации и сложил бы их сбои в собственный список; группу он
|
|
184
|
+
* освобождает в обратном порядке slots и получает несколько сбоев одним
|
|
185
|
+
* AggregateError. Владелец получает тот же AggregateError как один сбой
|
|
186
|
+
* ресурса вместо отдельных сбоев bindings.
|
|
187
|
+
*
|
|
188
|
+
* Остановка первой же записи повторно входит в _disposeBinding() через task
|
|
189
|
+
* hook engine: вложенный вызов уже не находит состояния группы и освобождает
|
|
190
|
+
* состояние pipeline до cleanup slots. Группа без живых slots освобождает
|
|
191
|
+
* его здесь.
|
|
192
|
+
*/
|
|
193
|
+
private _disposeGroup;
|
|
194
|
+
/**
|
|
195
|
+
* Освобождает подписку и состояние pipeline остановленного watch.
|
|
196
|
+
*/
|
|
197
|
+
private _releaseWatch;
|
|
198
|
+
/** @internal */
|
|
199
|
+
_pauseTask(record: RaphPhaseTaskRecord): void;
|
|
200
|
+
/**
|
|
201
|
+
* Убирает ожидающую запись из живой очереди её phase. Запись, которую flush
|
|
202
|
+
* уже забрал на разбор, в живой очереди не находится и счётчик не меняет.
|
|
203
|
+
*/
|
|
204
|
+
private _unqueue;
|
|
205
|
+
/** @internal */
|
|
206
|
+
_resumeTask(record: RaphPhaseTaskRecord): void;
|
|
207
|
+
/**
|
|
208
|
+
* Возвращает причины открытой доставки в ожидающие, впереди причин, которые
|
|
209
|
+
* пришли после её открытия; причины одного source сливаются. Причины доставки
|
|
210
|
+
* при этом остаются причинами доставки до её конца, поэтому сначала они
|
|
211
|
+
* отделяются от ожидающих.
|
|
212
|
+
*/
|
|
62
213
|
private _restoreDeliveryCausesBeforePending;
|
|
63
|
-
private _takePhase;
|
|
64
214
|
private _schedule;
|
|
65
215
|
private _scheduleDeferredTurn;
|
|
66
216
|
private _cancelScheduled;
|
|
67
217
|
private _activateDeferred;
|
|
68
218
|
private _nextFrame;
|
|
69
219
|
private _assertRetainedPhases;
|
|
70
|
-
private _assertDelivery;
|
|
71
|
-
private _hasQueued;
|
|
72
220
|
private _hasPending;
|
|
73
221
|
private _cancelAllQueued;
|
|
74
222
|
private _cancelAllDeferred;
|
|
75
223
|
private _acknowledgeDiscard;
|
|
224
|
+
/**
|
|
225
|
+
* Гасит ожидающую доставку slots группы.
|
|
226
|
+
*
|
|
227
|
+
* Slot, чей первый запуск ещё впереди, погасить нельзя: он не собрал
|
|
228
|
+
* зависимости, и после отмены его ничто не разбудит. Такой slot узнаётся по
|
|
229
|
+
* запланированной записи без единой зависимости: запись, которая уже
|
|
230
|
+
* исполнялась и ничего не прочитала, больше не планируется. Обычная отмена
|
|
231
|
+
* тогда не трогает ни один slot и возвращает false; fatal discard
|
|
232
|
+
* останавливает такие slots окончательно и гасит остальные.
|
|
233
|
+
*
|
|
234
|
+
* Обычная отмена отказывается только от того, что доставка знала при
|
|
235
|
+
* открытии. Причина, пришедшая группе после открытия доставки, означает
|
|
236
|
+
* запись, которую запланированный slot ещё не прочитал. Гашение снимает с
|
|
237
|
+
* записи slot и отметку планирования, и требование запуска, которое engine
|
|
238
|
+
* поставил при прямой записи, поэтому slots, ждавшие запуска, после гашения
|
|
239
|
+
* снова требуют его, как отдельный binding, отменённый при новой причине.
|
|
240
|
+
* Группа тогда доставляется ещё раз с новыми причинами. Fatal discard гасит
|
|
241
|
+
* всё.
|
|
242
|
+
*
|
|
243
|
+
* @returns false, если доставку нельзя отменить либо живых slots не осталось.
|
|
244
|
+
*/
|
|
245
|
+
private _cancelGroup;
|
|
246
|
+
/**
|
|
247
|
+
* Сбрасывает ожидающую работу записи, снятой с очереди либо из отложенных.
|
|
248
|
+
* Такая запись не находится в доставке, поэтому её причины ожидающие.
|
|
249
|
+
*/
|
|
76
250
|
private _clearPendingDelivery;
|
|
77
251
|
private _settleIdleWaiters;
|
|
78
252
|
private _assertAlive;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { EffectTask, OwnedResource, ReactiveNode, ScopeChildren } from '../../reactivity/types';
|
|
2
|
+
import { RaphScope } from '../ownership/RaphScope';
|
|
3
|
+
import { RaphEffectGroup, RaphEffectInitialMode, RaphFrameContext, RaphPhaseEffect, RaphPhaseTask, RaphWorkCause } from './phase.types';
|
|
4
|
+
import { RaphPhaseState } from './RaphPhasePipeline';
|
|
5
|
+
import { RaphPhaseWatch } from './RaphPhaseWatch';
|
|
6
|
+
export declare const EMPTY_CAUSES: readonly RaphWorkCause[];
|
|
7
|
+
export declare const EMPTY_FRAME: RaphFrameContext;
|
|
8
|
+
export declare const TASK_ACTIVE = 1;
|
|
9
|
+
export declare const TASK_PAUSED: number;
|
|
10
|
+
export declare const TASK_PENDING: number;
|
|
11
|
+
export declare const TASK_DEFERRED: number;
|
|
12
|
+
export declare const TASK_IN_DELIVERY: number;
|
|
13
|
+
export declare const TASK_DISCARD_ACKNOWLEDGED: number;
|
|
14
|
+
export declare const TASK_REQUESTED: number;
|
|
15
|
+
export declare const TASK_EXECUTED: number;
|
|
16
|
+
export declare const TASK_CAUSES_DELIVERED: number;
|
|
17
|
+
export declare const TASK_RESOLUTION: number;
|
|
18
|
+
export declare const TASK_UNRESOLVED = 0;
|
|
19
|
+
export declare const TASK_RUN: number;
|
|
20
|
+
export declare const TASK_DEFER: number;
|
|
21
|
+
export declare const TASK_CANCEL: number;
|
|
22
|
+
export declare const TASK_TASKLESS: number;
|
|
23
|
+
/**
|
|
24
|
+
* Состояние группы effects одного binding: плоский массив
|
|
25
|
+
* `[context, номер исполняемого slot, запись графа slot 0, slot 1, ...]`.
|
|
26
|
+
*
|
|
27
|
+
* Группа делит одну запись binding между несколькими отслеживаемыми
|
|
28
|
+
* вычислениями. У каждого slot своя запись графа engine, то есть свой набор
|
|
29
|
+
* зависимостей и свой признак invalidation; общими остаются запись binding и
|
|
30
|
+
* её место в очереди phase и в списке ресурсов владельца. Тело записи engine
|
|
31
|
+
* у всех slots одно, поэтому номер slot доставка записывает сюда перед
|
|
32
|
+
* запуском каждого slot, а тело slot получает само состояние как свой
|
|
33
|
+
* единственный аргумент — запуск `RaphEffectGroupRun`: первые два элемента и
|
|
34
|
+
* есть его публичная часть.
|
|
35
|
+
*
|
|
36
|
+
* На месте записи slot, остановленного отдельно от группы, стоит `null`.
|
|
37
|
+
*
|
|
38
|
+
* Состояние — массив, а не class: на группу приходится одна аллокация точного
|
|
39
|
+
* размера, и у неё нет hidden class, который терялся бы, когда освобождена
|
|
40
|
+
* последняя группа.
|
|
41
|
+
*/
|
|
42
|
+
export type RaphEffectGroupState = unknown[];
|
|
43
|
+
export declare const GROUP_CONTEXT = 0;
|
|
44
|
+
export declare const GROUP_CURSOR = 1;
|
|
45
|
+
export declare const GROUP_NODES = 2;
|
|
46
|
+
/**
|
|
47
|
+
* Frozen public facade of one phase binding. All state stays on the record, so
|
|
48
|
+
* the facade has a single own field and shares its capabilities through the
|
|
49
|
+
* prototype: `run`, `defer` and `cancel` need the task as receiver.
|
|
50
|
+
*/
|
|
51
|
+
export declare class RaphPhaseTaskHandle implements RaphPhaseTask {
|
|
52
|
+
/** @internal */
|
|
53
|
+
readonly _record: RaphPhaseTaskRecord;
|
|
54
|
+
constructor(record: RaphPhaseTaskRecord);
|
|
55
|
+
get phase(): string;
|
|
56
|
+
get consumerOwner(): RaphScope;
|
|
57
|
+
get effect(): RaphPhaseEffect;
|
|
58
|
+
get causes(): readonly RaphWorkCause[];
|
|
59
|
+
get frame(): RaphFrameContext;
|
|
60
|
+
get priority(): number;
|
|
61
|
+
get sequence(): number;
|
|
62
|
+
run(): void;
|
|
63
|
+
defer(): void;
|
|
64
|
+
cancel(): void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Редкое состояние одного phase binding, вынесенное из записи. Объект создаётся
|
|
68
|
+
* при первом входе binding в одно из таких состояний и живёт до освобождения
|
|
69
|
+
* записи:
|
|
70
|
+
*
|
|
71
|
+
* - до доставки накопились причины от второго source (`extraCauses`);
|
|
72
|
+
* - причины открытой доставки пришлось отделить от ожидающих: во время
|
|
73
|
+
* доставки пришла новая причина либо доставка отложена или остановлена
|
|
74
|
+
* pause (`deliveryFirstCause`, `deliveryFirstCauseKey`,
|
|
75
|
+
* `deliveryExtraCauses`);
|
|
76
|
+
* - причины доставки прочитаны через `task.causes` (`deliveryCauses`);
|
|
77
|
+
* - доставка отложена через `defer()` (`deferCount`).
|
|
78
|
+
*
|
|
79
|
+
* Binding, который исполняется и получает не больше одной причины на
|
|
80
|
+
* доставку, не читая `task.causes`, этого объекта не создаёт: ни effect с task
|
|
81
|
+
* или без неё, ни watch, ни группа.
|
|
82
|
+
*
|
|
83
|
+
* Поля объявлены без initializers и присваиваются в constructor в одном
|
|
84
|
+
* фиксированном порядке: у каждого экземпляра один hidden class.
|
|
85
|
+
*
|
|
86
|
+
* Engine удерживает форму экземпляров только через живые экземпляры. Когда в
|
|
87
|
+
* isolate не остаётся ни одного binding с редким состоянием, полная сборка
|
|
88
|
+
* мусора освобождает её вместе с кодом, оптимизированным под неё. Поэтому
|
|
89
|
+
* class владеет одним постоянным экземпляром `_shapeAnchor`.
|
|
90
|
+
*/
|
|
91
|
+
export declare class RaphPhaseTaskDetail {
|
|
92
|
+
static _shapeAnchor: RaphPhaseTaskDetail;
|
|
93
|
+
extraCauses: Map<object, RaphWorkCause> | undefined;
|
|
94
|
+
deliveryFirstCause: RaphWorkCause | undefined;
|
|
95
|
+
deliveryFirstCauseKey: object | undefined;
|
|
96
|
+
deliveryExtraCauses: Map<object, RaphWorkCause> | undefined;
|
|
97
|
+
deliveryCauses: readonly RaphWorkCause[] | undefined;
|
|
98
|
+
deferCount: number;
|
|
99
|
+
constructor();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Постоянное состояние одного phase binding. Один и тот же объект является
|
|
103
|
+
* элементом очереди pipeline, внешней задачей engine effect, элементом списка
|
|
104
|
+
* ресурсов своего consumer owner и pausable-регистрацией, передаваемой effect
|
|
105
|
+
* scopes, поэтому binding стоит одной аллокации.
|
|
106
|
+
*
|
|
107
|
+
* Запись несёт только то, что нужно binding между регистрацией и освобождением
|
|
108
|
+
* на обычном пути: признаки одним числом, место в очереди, владельца, callback,
|
|
109
|
+
* запись графа, lifecycle watch либо состояние группы, связи списка ресурсов,
|
|
110
|
+
* первую причину и лениво создаваемый frozen facade с frame, который он
|
|
111
|
+
* показывает. Остальное — причины со второй, причины доставки, отделённые от
|
|
112
|
+
* ожидающих, показанный массив причин и счётчик defer — лежит в `detail`,
|
|
113
|
+
* который создаётся при первом входе в такое состояние. Pipeline запись
|
|
114
|
+
* находит через её phase: `phaseState.pipeline`.
|
|
115
|
+
*
|
|
116
|
+
* Поля объявлены без initializers и присваиваются в constructor в одном
|
|
117
|
+
* фиксированном порядке: у каждой записи один hidden class, и ни один build
|
|
118
|
+
* target не может превратить поля в вызовы helper.
|
|
119
|
+
*/
|
|
120
|
+
export declare class RaphPhaseTaskRecord implements EffectTask, OwnedResource, RaphEffectGroup {
|
|
121
|
+
state: number;
|
|
122
|
+
sequence: number;
|
|
123
|
+
readonly phaseState: RaphPhaseState;
|
|
124
|
+
readonly priority: number;
|
|
125
|
+
readonly consumerOwner: RaphScope;
|
|
126
|
+
readonly effect: RaphPhaseEffect;
|
|
127
|
+
node: ReactiveNode | undefined;
|
|
128
|
+
watch: RaphPhaseWatch | undefined;
|
|
129
|
+
group: RaphEffectGroupState | undefined;
|
|
130
|
+
scopeParent: ScopeChildren<OwnedResource> | undefined;
|
|
131
|
+
scopePrevious: OwnedResource | undefined;
|
|
132
|
+
scopeNext: OwnedResource | undefined;
|
|
133
|
+
firstCause: RaphWorkCause | undefined;
|
|
134
|
+
firstCauseKey: object | undefined;
|
|
135
|
+
frame: RaphFrameContext;
|
|
136
|
+
facade: RaphPhaseTaskHandle | undefined;
|
|
137
|
+
detail: RaphPhaseTaskDetail | undefined;
|
|
138
|
+
constructor(phaseState: RaphPhaseState, priority: number, consumerOwner: RaphScope, effect: RaphPhaseEffect);
|
|
139
|
+
get phase(): string;
|
|
140
|
+
get dependencyOwner(): RaphScope;
|
|
141
|
+
get allowScheduleDuringRun(): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Становится false, когда binding остановлен: так запись видят effect scopes,
|
|
144
|
+
* которым она передана как pausable-регистрация.
|
|
145
|
+
*/
|
|
146
|
+
get active(): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Причины текущей доставки в том виде, в каком их получает callback или
|
|
149
|
+
* `process`. Массив строится при первом чтении в доставке и до её конца
|
|
150
|
+
* остаётся тем же объектом; доставка, причины которой никто не прочитал, не
|
|
151
|
+
* создаёт ни массива, ни заморозки. Вне доставки и для доставки без причин
|
|
152
|
+
* это общий пустой массив.
|
|
153
|
+
*
|
|
154
|
+
* Это единственное место, где причины выходят наружу, поэтому здесь же они
|
|
155
|
+
* становятся неизменяемыми: WorkGraph создаёт причину без заморозки, а
|
|
156
|
+
* замораживает её первое чтение.
|
|
157
|
+
*/
|
|
158
|
+
get causes(): readonly RaphWorkCause[];
|
|
159
|
+
/**
|
|
160
|
+
* One facade per record: created on first exposure, then reused.
|
|
161
|
+
*
|
|
162
|
+
* Запись начинает вести frame с появлением facade. Facade создаётся внутри
|
|
163
|
+
* открытой доставки своей записи — его запрашивают только тело binding и
|
|
164
|
+
* `process` — и получает её frame; frame каждой следующей доставки
|
|
165
|
+
* записывает её открытие.
|
|
166
|
+
*/
|
|
167
|
+
get task(): RaphPhaseTask;
|
|
168
|
+
/** Возвращает редкое состояние записи и создаёт его при первом обращении. */
|
|
169
|
+
detailed(): RaphPhaseTaskDetail;
|
|
170
|
+
notify(token: unknown): void;
|
|
171
|
+
schedule(): void;
|
|
172
|
+
enqueue(cause: RaphWorkCause, key?: object, schedule?: boolean): void;
|
|
173
|
+
/**
|
|
174
|
+
* Запускает slot группы effects; у записи одиночного effect и watch ничего
|
|
175
|
+
* не делает. Запись группы сама является handle, который возвращает
|
|
176
|
+
* `effectGroup`.
|
|
177
|
+
*/
|
|
178
|
+
start(slot: number, initial?: RaphEffectInitialMode): void;
|
|
179
|
+
/**
|
|
180
|
+
* Внутренний initial invocation renderer; публичный start не меняет signature.
|
|
181
|
+
* Continuation не сохраняется в записи и не вызывается при последующих runs.
|
|
182
|
+
* @internal
|
|
183
|
+
*/
|
|
184
|
+
_start(slot: number, initial: RaphEffectInitialMode, onExecutionFailure: (error: unknown) => never): void;
|
|
185
|
+
pause(): void;
|
|
186
|
+
resume(): void;
|
|
187
|
+
/**
|
|
188
|
+
* Полностью освобождает binding; безопасен для повторного и вложенного вызова:
|
|
189
|
+
* снимает запись со списка владельца, останавливает engine effect или watch
|
|
190
|
+
* lifecycle и сбрасывает состояние pipeline. Engine вызывает его как task hook
|
|
191
|
+
* остановленного effect, владелец — как элемент списка ресурсов, а
|
|
192
|
+
* пользователь — через disposer.
|
|
193
|
+
*/
|
|
194
|
+
dispose(): void;
|
|
195
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Disposer } from '../../reactivity/types';
|
|
2
|
+
import { RaphPhaseTaskRecord } from './RaphPhaseTaskRecord';
|
|
3
|
+
import { RuntimeWatch } from '../reactivity/RuntimeWatch';
|
|
4
|
+
/**
|
|
5
|
+
* Callback lifecycle одного explicit watch binding вместе с подпиской, которая
|
|
6
|
+
* его питает. Подписка освобождается при освобождении binding, до cleanup
|
|
7
|
+
* последней доставки.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle достаёт callback только через запись своего binding и сбрасывает
|
|
10
|
+
* эту ссылку при освобождении. Публичный watch disposer удерживает lifecycle,
|
|
11
|
+
* а не запись, поэтому disposer, переживший свой binding, не удерживает ни
|
|
12
|
+
* callback, ни захваченное им.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RaphPhaseWatch extends RuntimeWatch {
|
|
15
|
+
source: Disposer | undefined;
|
|
16
|
+
record: RaphPhaseTaskRecord | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Создаёт lifecycle без подписки и без binding; оба поля задаёт регистрация.
|
|
19
|
+
*/
|
|
20
|
+
constructor(runUntracked: <T>(operation: () => T) => T, callback: () => void | Disposer);
|
|
21
|
+
}
|
|
22
|
+
export declare function createPhaseWatch(runUntracked: <T>(operation: () => T) => T, record: RaphPhaseTaskRecord): RaphPhaseWatch;
|
|
@@ -45,7 +45,86 @@ export interface RaphWorkCause {
|
|
|
45
45
|
readonly metadata?: unknown;
|
|
46
46
|
}
|
|
47
47
|
export type RaphPhaseEffect = (task: RaphPhaseTask) => void | Disposer;
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Тело binding, зарегистрированного через `bindEffect`: task ему не передаётся,
|
|
50
|
+
* поэтому тело объявляется без параметров. Возвращённый cleanup выполняется
|
|
51
|
+
* перед следующим запуском и при освобождении binding.
|
|
52
|
+
*/
|
|
53
|
+
export type RaphPhaseBody = () => void | Disposer;
|
|
54
|
+
/**
|
|
55
|
+
* Запуск одного slot группы: кортеж, который получает тело slot. Первый элемент
|
|
56
|
+
* — context группы, второй — номер исполняемого slot, считая с нуля.
|
|
57
|
+
*
|
|
58
|
+
* Кортеж один на группу и принадлежит ей: номер slot в нём меняется от запуска
|
|
59
|
+
* к запуску, а остальные элементы — внутреннее состояние группы. Тело читает
|
|
60
|
+
* первые два элемента по индексу во время вызова и не сохраняет кортеж.
|
|
61
|
+
*
|
|
62
|
+
* Кортеж — живое состояние группы, а не копия: от записи его защищает только
|
|
63
|
+
* тип. Тело, которое пишет в кортеж или читает элементы после второго,
|
|
64
|
+
* нарушает contract: запись в первый элемент подменяет context следующим
|
|
65
|
+
* slots, запись в остальные ломает доставку группы.
|
|
66
|
+
*
|
|
67
|
+
* @typeParam Context — значение, которое группа передаёт телу каждого slot.
|
|
68
|
+
*/
|
|
69
|
+
export type RaphEffectGroupRun<Context = unknown> = readonly [
|
|
70
|
+
context: Context,
|
|
71
|
+
slot: number,
|
|
72
|
+
...state: readonly unknown[]
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Тело одного slot группы, зарегистрированной через `effectGroup`. Получает
|
|
76
|
+
* один аргумент — запуск с context группы и номером slot; task ему не
|
|
77
|
+
* передаётся. Чтения внутри вызова отслеживаются отдельно для каждого slot, а
|
|
78
|
+
* возвращённый cleanup принадлежит этому slot: он выполняется перед его
|
|
79
|
+
* следующим запуском и при освобождении группы.
|
|
80
|
+
*
|
|
81
|
+
* Аргумент один, как у тела `effect()`: общее для всех effects тело записи
|
|
82
|
+
* engine вызывает тело slot напрямую, без промежуточной функции на каждый
|
|
83
|
+
* запуск slot.
|
|
84
|
+
*
|
|
85
|
+
* @typeParam Context — значение, которое группа передаёт телу каждого slot.
|
|
86
|
+
*/
|
|
87
|
+
export type RaphEffectGroupSlot<Context = unknown> = (run: RaphEffectGroupRun<Context>) => void | Disposer;
|
|
88
|
+
/** Настройки группы effects: порядок её task среди bindings той же phase. */
|
|
89
|
+
export interface RaphEffectGroupOptions {
|
|
90
|
+
readonly priority?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Группа effects одного binding, которую вернул `effectGroup`. Slots группы
|
|
94
|
+
* ничего не исполняют, пока их не запустит `start`; метод вызывается через
|
|
95
|
+
* группу или с явным receiver.
|
|
96
|
+
*
|
|
97
|
+
* Возвращаемое значение — сама запись binding, а не отдельный объект: handle
|
|
98
|
+
* не стоит группе аллокации. Contract — только `start`; остальные поля и
|
|
99
|
+
* методы записи внутренние, и обращение к ним нарушает contract.
|
|
100
|
+
*/
|
|
101
|
+
export interface RaphEffectGroup {
|
|
102
|
+
/**
|
|
103
|
+
* Запускает slot: с этого вызова slot ведёт себя как binding, который
|
|
104
|
+
* `bindEffect()` зарегистрировал бы в этом месте. `initial` задаёт первую
|
|
105
|
+
* доставку так же, как у `effect()`: `'scheduled'` (по умолчанию) ставит её
|
|
106
|
+
* в очередь, `'sync'` исполняет её до возврата из вызова, если Runtime не
|
|
107
|
+
* занят доставкой и группа не на pause.
|
|
108
|
+
*
|
|
109
|
+
* Если первая доставка выполняется внутри вызова и flush бросает ошибку,
|
|
110
|
+
* slot останавливается окончательно, а ошибка бросается вызывающему, как
|
|
111
|
+
* при сбое регистрации отдельного binding. Остальные slots группы остаются.
|
|
112
|
+
* Запуск остановленного slot и повторный запуск уже запущенного slot не
|
|
113
|
+
* исполняют его тело.
|
|
114
|
+
*
|
|
115
|
+
* Запуск на освобождённом владельце бросает `RaphDisposedError`, как
|
|
116
|
+
* `bindEffect()`: и когда владелец освобождён до вызова, и когда его
|
|
117
|
+
* освободила первая доставка внутри этого вызова, при любом scheduler.
|
|
118
|
+
* Запуск slot группы, которую освободили при живом владельце — через её
|
|
119
|
+
* effect scope либо остановкой последнего slot, — ничего не делает.
|
|
120
|
+
*/
|
|
121
|
+
start: (this: RaphEffectGroup, slot: number, initial?: RaphEffectInitialMode) => void;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Persistent task identity, передаваемая phase process и effect callback.
|
|
125
|
+
* run/defer/cancel вызываются через task или с явным receiver: извлечённый
|
|
126
|
+
* метод не работает без this.
|
|
127
|
+
*/
|
|
49
128
|
export interface RaphPhaseTask {
|
|
50
129
|
readonly phase: string;
|
|
51
130
|
readonly consumerOwner: RaphScope;
|
|
@@ -54,9 +133,9 @@ export interface RaphPhaseTask {
|
|
|
54
133
|
readonly frame: RaphFrameContext;
|
|
55
134
|
readonly priority: number;
|
|
56
135
|
readonly sequence: number;
|
|
57
|
-
run: () => void;
|
|
58
|
-
defer: () => void;
|
|
59
|
-
cancel: () => void;
|
|
136
|
+
run: (this: RaphPhaseTask) => void;
|
|
137
|
+
defer: (this: RaphPhaseTask) => void;
|
|
138
|
+
cancel: (this: RaphPhaseTask) => void;
|
|
60
139
|
}
|
|
61
140
|
export interface RaphPhaseBatch {
|
|
62
141
|
readonly phase: string;
|