@raphy-js/raph 1.0.1-dev.20 → 1.0.1-dev.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/{core-CyilRD7s.js → core-CClhT8WR.js} +389 -400
  2. package/dist/core-cPQWYD4K.cjs +1 -0
  3. package/dist/debug/api/Raph.cjs +1 -1
  4. package/dist/debug/api/Raph.js +46 -47
  5. package/dist/debug/api/RaphFacadeSession.cjs +1 -1
  6. package/dist/debug/api/RaphFacadeSession.js +85 -89
  7. package/dist/debug/api/RaphReactivityInstance.cjs +1 -1
  8. package/dist/debug/api/RaphReactivityInstance.js +27 -30
  9. package/dist/debug/path/DataPath.cjs +1 -1
  10. package/dist/debug/path/DataPath.js +198 -202
  11. package/dist/debug/path/PathRouter.cjs +1 -1
  12. package/dist/debug/path/PathRouter.js +44 -50
  13. package/dist/debug/path/RaphKernel.cjs +1 -1
  14. package/dist/debug/path/RaphKernel.js +257 -228
  15. package/dist/debug/path/data-path.utils.cjs +1 -1
  16. package/dist/debug/path/data-path.utils.js +41 -41
  17. package/dist/debug/path/derived/DerivedRegistry.cjs +1 -1
  18. package/dist/debug/path/derived/DerivedRegistry.js +52 -66
  19. package/dist/debug/path/derived/RaphDerivedHandle.cjs +1 -1
  20. package/dist/debug/path/derived/RaphDerivedHandle.js +19 -22
  21. package/dist/debug/path/derived/derived-collection.utils.cjs +1 -1
  22. package/dist/debug/path/derived/derived-collection.utils.js +70 -70
  23. package/dist/debug/path/meta/MetaController.cjs +1 -1
  24. package/dist/debug/path/meta/MetaController.js +57 -65
  25. package/dist/debug/path/meta/RaphMeta.cjs +1 -1
  26. package/dist/debug/path/meta/RaphMeta.js +26 -32
  27. package/dist/debug/path/meta/RaphMetaStore.cjs +1 -1
  28. package/dist/debug/path/meta/RaphMetaStore.js +24 -30
  29. package/dist/debug/path/storage/AdapterPathStore.cjs +1 -1
  30. package/dist/debug/path/storage/AdapterPathStore.js +49 -54
  31. package/dist/debug/path/storage/DefaultDataAdapter.cjs +1 -1
  32. package/dist/debug/path/storage/DefaultDataAdapter.js +46 -56
  33. package/dist/debug/path/storage/PathEventHub.cjs +1 -1
  34. package/dist/debug/path/storage/PathEventHub.js +74 -61
  35. package/dist/debug/path/storage/PathStore.cjs +1 -1
  36. package/dist/debug/path/storage/PathStore.js +89 -99
  37. package/dist/debug/path/storage/createDataDraft.cjs +1 -1
  38. package/dist/debug/path/storage/createDataDraft.js +24 -28
  39. package/dist/debug/reactivity/disposal.cjs +1 -0
  40. package/dist/debug/reactivity/disposal.js +32 -0
  41. package/dist/debug/reactivity/engine.cjs +1 -1
  42. package/dist/debug/reactivity/engine.js +787 -392
  43. package/dist/debug/reactivity/reactive-object.cjs +1 -1
  44. package/dist/debug/reactivity/reactive-object.js +81 -96
  45. package/dist/debug/reactivity/scope.cjs +1 -1
  46. package/dist/debug/reactivity/scope.js +221 -117
  47. package/dist/debug/reactivity/watch.cjs +1 -1
  48. package/dist/debug/reactivity/watch.js +1 -1
  49. package/dist/debug/runtime/RaphRuntime.cjs +1 -1
  50. package/dist/debug/runtime/RaphRuntime.js +764 -411
  51. package/dist/debug/runtime/diagnostics/RaphDebug.cjs +1 -1
  52. package/dist/debug/runtime/diagnostics/RaphDebug.js +90 -100
  53. package/dist/debug/runtime/ownership/RaphEffectScope.cjs +1 -1
  54. package/dist/debug/runtime/ownership/RaphEffectScope.js +215 -82
  55. package/dist/debug/runtime/ownership/RaphNode.cjs +1 -1
  56. package/dist/debug/runtime/ownership/RaphNode.js +595 -136
  57. package/dist/debug/runtime/ownership/RaphScope.cjs +1 -1
  58. package/dist/debug/runtime/ownership/RaphScope.js +203 -68
  59. package/dist/debug/runtime/phases/RaphFrameLoop.cjs +1 -1
  60. package/dist/debug/runtime/phases/RaphFrameLoop.js +13 -17
  61. package/dist/debug/runtime/phases/RaphPhasePipeline.cjs +1 -1
  62. package/dist/debug/runtime/phases/RaphPhasePipeline.js +618 -357
  63. package/dist/debug/runtime/phases/RaphPhaseTaskRecord.cjs +1 -0
  64. package/dist/debug/runtime/phases/RaphPhaseTaskRecord.js +176 -0
  65. package/dist/debug/runtime/phases/RaphPhaseWatch.cjs +1 -0
  66. package/dist/debug/runtime/phases/RaphPhaseWatch.js +20 -0
  67. package/dist/debug/runtime/reactivity/PathHandle.cjs +1 -1
  68. package/dist/debug/runtime/reactivity/PathHandle.js +50 -60
  69. package/dist/debug/runtime/reactivity/RaphMetaSource.cjs +1 -1
  70. package/dist/debug/runtime/reactivity/RaphMetaSource.js +28 -32
  71. package/dist/debug/runtime/reactivity/RaphSelector.cjs +1 -0
  72. package/dist/debug/runtime/reactivity/RaphSelector.js +503 -0
  73. package/dist/debug/runtime/reactivity/RaphWorkGraph.cjs +1 -1
  74. package/dist/debug/runtime/reactivity/RaphWorkGraph.js +541 -379
  75. package/dist/debug/runtime/reactivity/RuntimeReactivity.cjs +1 -1
  76. package/dist/debug/runtime/reactivity/RuntimeReactivity.js +484 -90
  77. package/dist/debug/runtime/reactivity/RuntimeWatch.cjs +1 -1
  78. package/dist/debug/runtime/reactivity/RuntimeWatch.js +33 -44
  79. package/dist/debug/runtime/reactivity/disposal-sweep.utils.cjs +1 -0
  80. package/dist/debug/runtime/reactivity/disposal-sweep.utils.js +42 -0
  81. package/dist/entrypoints/runtime.d.ts +2 -1
  82. package/dist/machine-testing.cjs +1 -1
  83. package/dist/machine-testing.js +1 -1
  84. package/dist/machine.cjs +1 -1
  85. package/dist/machine.js +216 -231
  86. package/dist/path/RaphKernel.d.ts +24 -0
  87. package/dist/path/storage/PathEventHub.d.ts +14 -0
  88. package/dist/path/storage/PathStore.d.ts +4 -4
  89. package/dist/reactivity/disposal.d.ts +14 -0
  90. package/dist/reactivity/engine.d.ts +177 -4
  91. package/dist/reactivity/scope.d.ts +108 -30
  92. package/dist/runtime/RaphRuntime.d.ts +236 -17
  93. package/dist/runtime/ownership/RaphEffectScope.d.ts +70 -1
  94. package/dist/runtime/ownership/RaphNode.d.ts +276 -6
  95. package/dist/runtime/ownership/RaphScope.d.ts +144 -14
  96. package/dist/runtime/phases/RaphPhasePipeline.d.ts +202 -28
  97. package/dist/runtime/phases/RaphPhaseTaskRecord.d.ts +195 -0
  98. package/dist/runtime/phases/RaphPhaseWatch.d.ts +22 -0
  99. package/dist/runtime/phases/phase.types.d.ts +83 -4
  100. package/dist/runtime/reactivity/RaphSelector.d.ts +338 -0
  101. package/dist/runtime/reactivity/RaphWorkGraph.d.ts +163 -25
  102. package/dist/runtime/reactivity/RuntimeReactivity.d.ts +258 -10
  103. package/dist/runtime/reactivity/RuntimeWatch.d.ts +8 -8
  104. package/dist/runtime/reactivity/disposal-sweep.utils.d.ts +19 -0
  105. package/dist/runtime/reactivity/runtime-reactivity.types.d.ts +48 -3
  106. package/package.json +10 -2
  107. package/dist/core-ugchZNqC.cjs +0 -1
@@ -0,0 +1,338 @@
1
+ import { OwnedSourceEntry } from '../../reactivity/scope';
2
+ import { RaphScope } from '../ownership/RaphScope';
3
+ import { RuntimeDisposalSweeper, RuntimeSignalWriteTap } from './runtime-reactivity.types';
4
+ import { RuntimeReactivity, RuntimeSignal } from './RuntimeReactivity';
5
+ /**
6
+ * Настройки selector.
7
+ *
8
+ * @typeParam Key — тип ключа и значения source.
9
+ */
10
+ export interface RaphSelectorOptions<Key> {
11
+ /**
12
+ * Сравнение ключа со значением source вместо SameValueZero. Функция должна
13
+ * быть чистой: один и тот же ответ для одной пары аргументов и никаких
14
+ * чтений реактивных источников. С ней запись в source сравнивает каждый
15
+ * наблюдаемый ключ с прежним и с новым значением и будит только те ключи,
16
+ * чей ответ изменился.
17
+ */
18
+ readonly equals?: (key: Key, value: Key) => boolean;
19
+ }
20
+ /**
21
+ * Чтение selector: сообщает, совпадает ли ключ с текущим значением source, и
22
+ * подписывает активного потребителя только на этот ключ.
23
+ *
24
+ * @typeParam Key — тип ключа и значения source.
25
+ */
26
+ export type RaphSelector<Key> = (key: Key) => boolean;
27
+ /**
28
+ * Компактное описание selector, которое возвращает его toJSON().
29
+ *
30
+ * @internal
31
+ */
32
+ export interface RuntimeSelectorDescription {
33
+ readonly owner: string;
34
+ readonly triggers: number;
35
+ readonly disposed: boolean;
36
+ }
37
+ /**
38
+ * Selector одним объектом: запись в списке источников владельца, наблюдатель
39
+ * записей source, индекс triggers по ключам и участник уборки Runtime.
40
+ *
41
+ * Trigger ключа — запись-счётчик графа: потребитель, прочитавший ключ,
42
+ * зависит только от неё, а не от source. Запись в source
43
+ * сдвигает счётчики triggers прежнего и нового ключа, и будятся только их
44
+ * потребители. Ответ чтения всегда вычисляется заново по текущему значению
45
+ * source, поэтому лишнее пробуждение безвредно, а пропущенных не бывает:
46
+ * каждая запись, меняющая ответ ключа, сдвигает его счётчик. Вне группировки
47
+ * сдвиг сразу делает потребителя обязательным к пересчёту, внутри неё остаётся
48
+ * у trigger, пока его не прочитает потребитель. Сдвиги не отменяют друг друга:
49
+ * потребитель ключа, через который batch прошёл туда и обратно, выполняется
50
+ * один лишний раз. Исключение то же, что у обычного signal: потребитель,
51
+ * который сам выполнялся в момент записи вне группировки и которого engine не
52
+ * планирует заново, и читатели такого потребителя (см. `_bump`).
53
+ *
54
+ * Trigger нужен, пока у него есть потребители. Как selector узнаёт, что их не
55
+ * осталось, зависит от ключа.
56
+ *
57
+ * - Ключ-объект или функция. Его trigger снимается вместе с последней связью
58
+ * графа: selector не удерживает объект дольше его потребителей. Цена —
59
+ * cleanup на каждую связь.
60
+ * - Остальные ключи. Связи с их triggers ничем не отличаются от связей с
61
+ * signal, и потребитель не платит за selector ни при создании связи, ни при
62
+ * её снятии. Trigger без потребителей остаётся в индексе до уборки. Уборка
63
+ * идёт при записи в source, которая затрагивает такой ключ; когда индекс
64
+ * вырос вдвое после прошлой уборки; когда Runtime после прошлой уборки
65
+ * освободил не меньше владельцев, чем ключей в индексе; при освобождении
66
+ * владельца selector. Уборка после освобождений, которая ничего не убрала,
67
+ * удваивает срок следующей: освобождения посторонних владельцев стоят
68
+ * selector логарифма от их числа, а не уборки на каждые несколько из них.
69
+ * Удержанный до уборки trigger — одна запись графа и значение ключа;
70
+ * потребителей он не удерживает.
71
+ *
72
+ * Индекс triggers состоит из двух частей. Целые ключи, идущие подряд по
73
+ * возрастанию — ключи строк списка, — лежат в ряду: место ряда принадлежит
74
+ * ключу «начало ряда плюс номер места», и ключ стоит selector одного места
75
+ * массива, без записи `Map` и без хеширования. Все остальные ключи лежат в
76
+ * общем индексе `Map`, который создаётся с первым таким ключом. У ключа ровно
77
+ * один trigger: ключ, попавший в общий индекс, остаётся в нём, и ряд через
78
+ * него не растёт. Место ряда принадлежит ключу, только если сумма начала ряда
79
+ * и номера места равна самому ключу: нецелый ключ места не получает никогда.
80
+ *
81
+ * Запись со своим сравнением обходит сначала ряд по возрастанию ключей, затем
82
+ * общий индекс в порядке первого чтения ключей. В этом порядке она будит
83
+ * потребителей; порядок может измениться и контрактом не является.
84
+ *
85
+ * Поля объявлены через `declare` и присваиваются в constructor в одном
86
+ * порядке: у всех selectors один hidden class.
87
+ *
88
+ * @internal
89
+ */
90
+ export declare class RuntimeSelector<Key> implements OwnedSourceEntry, RuntimeSignalWriteTap, RuntimeDisposalSweeper {
91
+ /**
92
+ * Публичное чтение selector; одна функция на весь его lifetime.
93
+ */
94
+ readonly select: RaphSelector<Key>;
95
+ /**
96
+ * Источник, зарегистрированный у владельца перед этим.
97
+ * @internal
98
+ */
99
+ _previousSource: OwnedSourceEntry | undefined;
100
+ /**
101
+ * Ключ-объект текущего отслеживаемого чтения: по нему hook onDependency
102
+ * узнаёт, для какого ключа engine создаёт связь. Вне чтения поле пусто.
103
+ * @internal
104
+ */
105
+ _trackedKey: Key | undefined;
106
+ private readonly _owner;
107
+ private readonly _reactivity;
108
+ private readonly _source;
109
+ private _equals;
110
+ private readonly _dense;
111
+ private _denseBase;
112
+ private _denseCount;
113
+ private _triggers;
114
+ private _scanning;
115
+ private readonly _objectHooks;
116
+ private _growthLimit;
117
+ private _sweptAt;
118
+ private _sweepStride;
119
+ private _disposed;
120
+ /**
121
+ * Место selector среди наблюдателей записи source; ведёт source.
122
+ * @internal
123
+ */
124
+ _tapSlot: number;
125
+ /**
126
+ * Место selector в очереди уборки Runtime; -1, пока selector в ней не стоит.
127
+ * Ведёт Runtime.
128
+ * @internal
129
+ */
130
+ _sweepSlot: number;
131
+ /**
132
+ * Срок selector в очереди уборки Runtime; ведёт Runtime.
133
+ * @internal
134
+ */
135
+ _sweepDue: number;
136
+ /**
137
+ * Создаёт selector и подключает его к записям source. Регистрацию у
138
+ * владельца и проверку аргументов выполняет вызывающий код.
139
+ */
140
+ constructor(owner: RaphScope, reactivity: RuntimeReactivity, source: RuntimeSignal<Key>, equals: ((key: Key, value: Key) => boolean) | undefined);
141
+ /**
142
+ * Тег Object.prototype.toString: инструменты, которые обходят только plain
143
+ * objects, принимают selector за неделимое значение и не идут через него в
144
+ * граф Runtime.
145
+ */
146
+ get [Symbol.toStringTag](): string;
147
+ /**
148
+ * Возвращает компактное описание вместо собственных полей: они ведут к
149
+ * владельцу, Runtime и графу зависимостей.
150
+ */
151
+ toJSON(): RuntimeSelectorDescription;
152
+ /**
153
+ * Владелец selector как наблюдателя записей source.
154
+ * @internal
155
+ */
156
+ get _tapOwner(): RaphScope;
157
+ /**
158
+ * Число ключей в индексе triggers: с потребителями и ещё не убранных.
159
+ * @internal
160
+ */
161
+ _debugTriggerCount(): number;
162
+ /**
163
+ * Отвечает, совпадает ли ключ с текущим значением source, и подписывает
164
+ * активного потребителя на trigger этого ключа. Чтение проверяется как
165
+ * чтение signal владельца selector: тот же lifetime, тот же запрет чужого
166
+ * Runtime и то же правило implicit-зависимостей.
167
+ *
168
+ * Подписка идёт раньше сравнения: потребитель, у которого чтение source или
169
+ * сравнение бросило исключение, всё равно будет разбужен следующей записью.
170
+ * @internal
171
+ */
172
+ _test(key: Key): boolean;
173
+ /**
174
+ * Сообщает, что запись в source может разбудить потребителей selector.
175
+ * @internal
176
+ */
177
+ _observesWrites(): boolean;
178
+ /**
179
+ * Будит потребителей ключей, чей ответ изменила запись в source. Вызывается
180
+ * signal-ом после того, как новое значение стало видимым, внутри его границы
181
+ * записи. Сбой одного пробуждения не отменяет остальные.
182
+ * @internal
183
+ */
184
+ _afterSourceWrite(previous: unknown, value: unknown): void;
185
+ /**
186
+ * Убирает triggers без потребителей, когда Runtime после прошлой уборки
187
+ * освободил не меньше владельцев, чем ключей в индексе: при массовом
188
+ * освобождении потребителей индекс пустеет одной уборкой на последнем из
189
+ * них. Вызов до срока стоит одного сравнения.
190
+ *
191
+ * Уборка, которая ничего не убрала, удваивает срок следующей: владельцы,
192
+ * освобождённые за это время, не держали потребителей selector, и чем дольше
193
+ * так продолжается, тем реже selector проверяет индекс. Первая же уборка,
194
+ * которая что-то убрала, возвращает прежний срок. Опустевший selector
195
+ * выходит из уборки Runtime и вернётся в неё с первым новым trigger.
196
+ * @internal
197
+ */
198
+ _afterOwnerDisposals(disposals: number): number;
199
+ /**
200
+ * Обрабатывает снятие одной связи графа с trigger ключа-объекта. Engine
201
+ * вызывает cleanup связи до того, как вынет её из списка потребителей:
202
+ * связь, оставшаяся в списке единственной, и есть снимаемая, и вместе с ней
203
+ * ключ теряет последнего потребителя. Запись графа при этом не
204
+ * освобождается: её список потребителей в этот момент обходит engine, а без
205
+ * потребителей и без ссылки из индекса она недостижима.
206
+ * @internal
207
+ */
208
+ _edgeDetached(key: Key): void;
209
+ /**
210
+ * Ищет среди потребителей всех triggers первого, чьего владельца принимает
211
+ * accept, и возвращает этого владельца: implicit-рёбра selector лежат только
212
+ * в графе, как и рёбра Runtime signal.
213
+ * @internal
214
+ */
215
+ _findConsumerOwner<Context>(accept: (consumerOwner: object, context: Context) => boolean, context: Context): object | undefined;
216
+ /**
217
+ * Освобождает selector как источник владельца: отключает его от записей
218
+ * source и от уборки Runtime и отсоединяет оставшихся потребителей от всех
219
+ * triggers. Сбой одного шага не отменяет остальные. Повторный вызов
220
+ * безопасен.
221
+ * @internal
222
+ */
223
+ _finalizeSource(): boolean;
224
+ /**
225
+ * Убирает из индекса triggers без потребителей и запоминает, сколько
226
+ * владельцев Runtime освободил к этому моменту. Индекс, в котором
227
+ * потребителей не осталось ни у кого, очищается целиком.
228
+ * @internal
229
+ */
230
+ _sweep(disposals?: number): void;
231
+ /**
232
+ * Число ключей в обеих частях индекса: с потребителями и ещё не убранных.
233
+ */
234
+ private _size;
235
+ /**
236
+ * Подписывает активного потребителя на trigger ключа; без потребителя ничего
237
+ * не создаёт. Trigger появляется первым отслеживаемым чтением ключа.
238
+ */
239
+ private _track;
240
+ /**
241
+ * Создаёт trigger целого ключа на месте `offset` ряда: в конце ряда либо на
242
+ * месте, чей прежний trigger убран. Без активного потребителя ничего не
243
+ * создаёт, и пустой ряд остаётся пустым.
244
+ */
245
+ private _trackDense;
246
+ /**
247
+ * Подписывает активного потребителя на trigger ключа-объекта: связь с ним
248
+ * получает cleanup, который помнит ключ.
249
+ */
250
+ private _trackObject;
251
+ /**
252
+ * Убирает triggers без потребителей, когда индекс вырос вдвое после прошлой
253
+ * уборки, и назначает следующий порог роста: цена уборок остаётся
254
+ * постоянной на один новый trigger. С первым trigger подключает selector к
255
+ * уборке Runtime после освобождения владельцев.
256
+ */
257
+ private _grow;
258
+ /**
259
+ * Будит потребителей прежнего и нового значения source при сравнении
260
+ * SameValueZero: ответ изменился только у этих двух ключей. Trigger нового
261
+ * значения ищется после пробуждения прежнего, которое могло изменить индекс.
262
+ */
263
+ private _wakePair;
264
+ /**
265
+ * Сдвигает счётчик trigger: его потребители получают invalidation. Trigger
266
+ * без потребителей убирается из индекса здесь.
267
+ *
268
+ * Значение trigger только растёт, поэтому сдвиг нельзя отменить следующим:
269
+ * ключ, через который batch прошёл туда и обратно, остаётся изменённым, пока
270
+ * его не прочитает потребитель.
271
+ *
272
+ * Сдвиг — обычная запись signal. Вне группировки она прямая, и потребителя
273
+ * engine делает обязательным к пересчёту в любом его состоянии: без пометок,
274
+ * помеченного «проверить» записью, которую её batch затем отменил
275
+ * (`direct-write-after-check.spec.ts`), и стоящего на стеке собственной
276
+ * проверки зависимостей, getter которой записал source
277
+ * (`direct-write-during-check.spec.ts`, блок `selector trigger`). Внутри
278
+ * группировки сдвиг откладывается у trigger до первого чтения вместе с
279
+ * записью самого source.
280
+ *
281
+ * Потребителя, который выполняется в момент прямого сдвига, engine помечает
282
+ * как при записи любого signal, и непрочитанного сдвига у trigger для него
283
+ * не остаётся. Runtime планирует заново только phase effect, который читает
284
+ * selector сам. Effect без задачи и computed, вернувший прежнее значение,
285
+ * остаются со старым ответом до следующей записи, которая меняет ответ
286
+ * ключа: пометку записи, пришедшей во время запуска, engine с них снимает.
287
+ * Старый ответ такого computed получает и каждый его читатель — phase effect
288
+ * любой формы регистрации, watch и прямое чтение, — в том числе после
289
+ * собственного повторного запуска. Отложенный сдвиг подтверждала им
290
+ * следующая проверка зависимостей или следующий читатель ключа, прямой —
291
+ * нет; обычный signal на месте selector ведёт себя так же, как прямой сдвиг
292
+ * (тот же файл, блок `selector source written while a consumer of the key
293
+ * runs`; причина в engine — блок `known limitations`).
294
+ */
295
+ private _bump;
296
+ /**
297
+ * Сравнивает один наблюдаемый ключ с прежним и с новым значением source и
298
+ * будит его потребителей, если ответ изменился. Ответ, который не удалось
299
+ * вычислить, считается изменившимся: потребитель встретит то же исключение
300
+ * в собственном чтении. Сбои копятся в `errors`; возвращается их список.
301
+ */
302
+ private _wakeChanged;
303
+ /**
304
+ * Возвращает trigger ключа из той части индекса, где лежит ключ. Место ряда
305
+ * с убранным trigger отвечает undefined, как отсутствующий ключ. Место ряда
306
+ * проверяется так же, как при чтении: по нему должен получаться сам ключ.
307
+ */
308
+ private _find;
309
+ /**
310
+ * Убирает trigger ключа из той части индекса, где лежит ключ.
311
+ *
312
+ * Место ряда остаётся пустым, и ряд не сдвигается, даже когда ключ стоял на
313
+ * его краю: запись, убравшая ключ, стоит одинаково при любой длине ряда.
314
+ * Порядок в ряду наводит уборка, которая и так обходит его целиком. Запись
315
+ * делает это сама в двух случаях: ряд опустел, либо мест в нём стало больше,
316
+ * чем вдвое против допустимого для обрезанного ряда. Во втором случае проход
317
+ * оставляет от ряда меньше половины мест либо переносит его в общий индекс,
318
+ * поэтому его цена ложится на создание убранных мест, а не на записи.
319
+ */
320
+ private _forget;
321
+ /**
322
+ * Приводит ряд в порядок после того, как из него убраны triggers: опустевший
323
+ * ряд становится пустым, пустые места с краёв срезаются, а ряд, в котором
324
+ * после этого мест больше удвоенного числа занятых плюс запас, целиком
325
+ * переносится в общий индекс. Так ряд занимает память, пропорциональную
326
+ * числу своих ключей, какой бы старый ключ ни остался наблюдаемым.
327
+ *
328
+ * Проход стоит длины ряда, поэтому его вызывают уборка и запись со своим
329
+ * сравнением, которые обходят ряд сами, а запись, убравшая один ключ, —
330
+ * только по правилу из `_forget()`. Пока запись обходит индекс, ряд не
331
+ * сдвигается и не переносится: порядок наведёт сам обход.
332
+ *
333
+ * Срез начала сдвигает начало ряда на число срезанных мест. Ключ первого
334
+ * оставшегося места — целое число, равное этой сумме, поэтому сложение
335
+ * точное.
336
+ */
337
+ private _settleDense;
338
+ }
@@ -4,7 +4,7 @@ import { RaphScope } from '../ownership/RaphScope';
4
4
  import { RaphAggregateSource, RaphStructureCause, RaphWatchInclude, RaphWatchSource, RaphWorkCause, RaphWorkCauseKind } from '../phases/phase.types';
5
5
  import { RaphRuntime } from '../RaphRuntime';
6
6
  type WorkSink = (cause: RaphWorkCause, key: object, schedule: boolean) => void;
7
- type WorkEdgeSource = RaphProducerRecord | RaphNode | RaphRuntime;
7
+ type WorkEdgeSource = RaphProducer | RaphNode | RaphRuntime;
8
8
  interface WorkEdge {
9
9
  readonly sink: WorkSink;
10
10
  readonly kind: 'producer' | 'runtime' | 'self' | 'subtree';
@@ -17,7 +17,7 @@ interface WorkEdgeList {
17
17
  first?: WorkEdge;
18
18
  last?: WorkEdge;
19
19
  }
20
- type CleanupSource = RaphProducerRecord | RaphNode | RaphAggregate;
20
+ type CleanupSource = RaphProducer | RaphNode | RaphAggregate;
21
21
  interface CleanupEdge {
22
22
  cleanup?: () => void;
23
23
  source?: CleanupSource;
@@ -37,25 +37,70 @@ interface AggregateSubscription {
37
37
  interface ProducerOptions {
38
38
  readonly includeRuntime?: boolean;
39
39
  }
40
- declare const PRODUCER_RECORD: unique symbol;
41
40
  /** @internal */
42
- export interface RaphProducerRecord {
43
- readonly [PRODUCER_RECORD]: true;
44
- readonly runtime: RaphRuntime;
41
+ export type RaphProducerKind = Extract<RaphWorkCauseKind, 'frame' | 'path' | 'producer'>;
42
+ /**
43
+ * Компактное описание producer, которое возвращает его toJSON(): только
44
+ * примитивы, без ссылок на владельца, Runtime и граф зависимостей.
45
+ *
46
+ * @internal
47
+ */
48
+ export interface RaphProducerDescription {
49
+ readonly kind: RaphProducerKind;
50
+ readonly owner: string;
51
+ readonly revision: number;
52
+ readonly disposed: boolean;
53
+ }
54
+ /**
55
+ * Runtime-сторона одного granular source: адрес explicit watch, token причины
56
+ * для implicit consumers и носитель последней причины. Runtime signal
57
+ * наследует этот класс и сам является своим producer; остальные handles
58
+ * получают отдельную запись через createProducer() и реестр.
59
+ *
60
+ * Поля объявлены через `declare` и присваиваются в constructor в одном
61
+ * порядке: у всех producers один hidden class, и ни один build target не
62
+ * превращает объявления в вызовы field-helper.
63
+ *
64
+ * @internal
65
+ */
66
+ export declare class RaphProducer {
45
67
  readonly owner: RaphScope;
46
- readonly kind: Extract<RaphWorkCauseKind, 'frame' | 'path' | 'producer'>;
68
+ readonly kind: RaphProducerKind;
47
69
  readonly includeRuntime: boolean;
48
- subscribers?: WorkEdgeList;
49
- memberships?: CleanupEdgeList;
50
- implicitConsumers?: Map<RaphNode, number>;
51
- alias?: object;
52
- source?: RaphWatchSource;
53
- activate?: () => void | Disposer;
54
- runtimeActivation?: Disposer;
55
- lastCause?: RaphWorkCause;
56
- transientNext?: RaphProducerRecord;
70
+ subscribers: WorkEdgeList | undefined;
71
+ memberships: CleanupEdgeList | undefined;
72
+ implicitConsumers: Map<RaphNode, number> | undefined;
73
+ source: RaphWatchSource | undefined;
74
+ activate: (() => void | Disposer) | undefined;
75
+ runtimeActivation: Disposer | undefined;
76
+ lastCause: RaphWorkCause | undefined;
77
+ transientNext: RaphProducer | undefined;
57
78
  revision: number;
58
79
  disposed: boolean;
80
+ /**
81
+ * Prototype-бренд producer: `true` у RaphProducer и его наследников. Заменяет
82
+ * `instanceof` там, где причина ищется по token на каждое уведомление
83
+ * implicit consumer. Значение лежит на prototype и не занимает поле экземпляра.
84
+ * @internal
85
+ */
86
+ readonly _isProducer: boolean;
87
+ constructor(owner: RaphScope, kind: RaphProducerKind, includeRuntime: boolean);
88
+ get runtime(): RaphRuntime;
89
+ /**
90
+ * Тег Object.prototype.toString: `[object RaphProducer]`. По нему инструменты,
91
+ * которые копируют и обходят только plain objects, принимают producer за
92
+ * неделимое значение: тестовый runner не клонирует его вместе со всем графом
93
+ * Runtime перед печатью diff. Accessor лежит на prototype.
94
+ */
95
+ get [Symbol.toStringTag](): string;
96
+ /**
97
+ * Возвращает компактное описание вместо собственных полей. Поля producer
98
+ * ведут к владельцу, Runtime и графу зависимостей: без этого метода
99
+ * JSON.stringify упирался бы в циклы, а печать значения в логах и в diff
100
+ * неуспешной проверки тестового runner-а обходила бы весь граф Runtime.
101
+ * Метод лежит на prototype и не добавляет producer ни поля, ни closure.
102
+ */
103
+ toJSON(): RaphProducerDescription;
59
104
  }
60
105
  /** Immutable explicit group of granular sources owned by one Runtime scope. */
61
106
  export declare class RaphAggregate {
@@ -102,6 +147,12 @@ export declare class RaphAggregate {
102
147
  /** Runtime-only intrusive indexes for granular sources. */
103
148
  export declare class RaphWorkGraph {
104
149
  private readonly _runtime;
150
+ /**
151
+ * Удерживает форму причины записи значения, пока в isolate нет ни одной
152
+ * живой причины.
153
+ * @internal
154
+ */
155
+ static _valueCauseShapeAnchor: RaphWorkCause;
105
156
  private _selfSubscribers;
106
157
  private _subtreeSubscribers;
107
158
  private _runtimeSubscribers;
@@ -116,15 +167,53 @@ export declare class RaphWorkGraph {
116
167
  private _detachedEdges;
117
168
  private _disposed;
118
169
  constructor(_runtime: RaphRuntime);
119
- createProducer(owner: RaphScope, kind?: RaphProducerRecord['kind'], options?: ProducerOptions): RaphProducerRecord;
120
- attachProducer<T extends object>(record: RaphProducerRecord, source: T): T;
121
- setProducerActivation(record: RaphProducerRecord, activate: () => void | Disposer, activateForRuntime?: boolean): void;
122
- producerHooks(record: RaphProducerRecord): OwnedSourceHooks;
170
+ createProducer(owner: RaphScope, kind?: RaphProducerKind, options?: ProducerOptions): RaphProducer;
171
+ attachProducer<T extends object>(record: RaphProducer, source: T): T;
172
+ setProducerActivation(record: RaphProducer, activate: () => void | Disposer, activateForRuntime?: boolean): void;
173
+ producerHooks(record: RaphProducer): OwnedSourceHooks;
174
+ /**
175
+ * Возвращает последнюю причину producer, который engine назвал своим token.
176
+ * Token чужого Runtime и значение, не являющееся producer, причины не имеют.
177
+ *
178
+ * Метод вызывается на каждое уведомление implicit consumer, поэтому producer
179
+ * распознаётся prototype-брендом, а Runtime читается у владельца напрямую:
180
+ * без `instanceof` и без accessor.
181
+ */
123
182
  causeForToken(token: unknown): RaphWorkCause | undefined;
124
183
  /** Clears mutation-local payloads after engine propagation consumed them. */
125
184
  clearTransientCauses(): void;
126
- notifyProducer(record: RaphProducerRecord, previous?: unknown, value?: unknown, events?: RaphWorkCause['events'], schedule?: boolean, hasReactiveSubscribers?: boolean, includeValues?: boolean): void;
127
- notifyProducerEvents(record: RaphProducerRecord, events: NonNullable<RaphWorkCause['events']>, schedule?: boolean, hasReactiveSubscribers?: boolean): void;
185
+ notifyProducer(record: RaphProducer, previous?: unknown, value?: unknown, events?: RaphWorkCause['events'], schedule?: boolean, hasReactiveSubscribers?: boolean, includeValues?: boolean): void;
186
+ /**
187
+ * Публикует запись значения producer: форма `notifyProducer()` для причины с
188
+ * `previous` и `value`, без events и с планированием. Так свою запись
189
+ * публикует Runtime signal.
190
+ *
191
+ * Причина создаётся при тех же условиях и с теми же полями. Отличие в цене:
192
+ * у producer с implicit consumers, на который не подписан ни один watch, ни
193
+ * его node, ни поддерево, ни Runtime, причину получают только consumers через
194
+ * `causeForToken()`. Тогда обход подписчиков не начинается: запись не строит
195
+ * массив предков и не выполняет ни одного вызова на каждого из них.
196
+ *
197
+ * @internal
198
+ */
199
+ _notifyValueWrite(record: RaphProducer, previous: unknown, value: unknown, hasReactiveSubscribers: boolean): void;
200
+ notifyProducerEvents(record: RaphProducer, events: NonNullable<RaphWorkCause['events']>, schedule?: boolean, hasReactiveSubscribers?: boolean): void;
201
+ /**
202
+ * Дешёвая предварительная проверка structural publication: `false` означает,
203
+ * что `notifyNode` для этой node вернётся до создания cause и без побочных
204
+ * эффектов. `true` консервативен и ведёт на полный путь.
205
+ *
206
+ * @internal
207
+ */
208
+ _hasStructuralDemand(node: RaphNode): boolean;
209
+ /**
210
+ * Сообщает, есть ли хотя бы один runtime-, subtree- или self-подписчик.
211
+ * `true` означает, что `_hasStructuralDemand` может вернуть `true` для
212
+ * какой-то node этого Runtime.
213
+ *
214
+ * @internal
215
+ */
216
+ _hasNodeSubscribers(): boolean;
128
217
  notifyNode(node: RaphNode, kind: Extract<RaphWorkCauseKind, 'structure' | 'touch'>, options?: {
129
218
  readonly metadata?: unknown;
130
219
  readonly structure?: RaphStructureCause;
@@ -134,18 +223,67 @@ export declare class RaphWorkGraph {
134
223
  createAggregate(owner: RaphScope, sources: readonly RaphAggregateSource[]): RaphAggregate;
135
224
  subscribe(input: RaphWatchSource, include: RaphWatchInclude | undefined, sink: WorkSink): () => void;
136
225
  assertSubscriptionAllowed(consumer: RaphScope, input: RaphWatchSource): void;
137
- /** Rejects a move that would make an existing implicit reactive edge illegal. */
226
+ /**
227
+ * Rejects a move that would make an existing implicit reactive edge illegal.
228
+ *
229
+ * Рёбра от producers с отдельной записью (computed, machine) берутся из
230
+ * учёта `implicitConsumers`. Рёбра от Runtime signals нигде не учитываются:
231
+ * они читаются из графа в момент проверки.
232
+ */
138
233
  assertReparentAllowed(node: RaphNode, newParent: RaphNode): void;
234
+ /**
235
+ * Проверяет живые implicit-рёбра от Runtime signals. Implicit-чтение
236
+ * разрешено только у собственного владельца, его предков и root scope,
237
+ * поэтому внешний для переносимого поддерева источник всегда принадлежит
238
+ * прежнему предку `node`. Предок, который остаётся предком нового parent,
239
+ * сохраняет все свои рёбра законными. Остаются предки от прежнего parent до
240
+ * ближайшего общего с новым parent, не включая его: перенос запрещён, если
241
+ * signal такого предка читает потребитель из переносимого поддерева.
242
+ *
243
+ * Посылка о прежнем предке держится на проверке самого чтения: Runtime
244
+ * signal отклоняет implicit-чтение чужого владельца и тогда, когда общий
245
+ * контекст владельца снят untracked-работой другого Runtime, а потребитель
246
+ * этого Runtime остался активным (assertReadable в RuntimeReactivity).
247
+ *
248
+ * Цена переноса. Перенос под прежнего parent и перенос корня дерева
249
+ * проверки не требуют. Иначе для каждого покидаемого предка обходится весь
250
+ * список его источников (не-signals пропускаются за один шаг), а у каждого
251
+ * signal с записью графа — список его потребителей до первого потребителя
252
+ * из переносимого поддерева. Итого O(покидаемые предки × их источники +
253
+ * потребители их signals), без аллокаций. Цена не зависит от того, сколько
254
+ * рёбер между nodes есть в остальном Runtime, но растёт с числом источников
255
+ * и потребителей у покидаемых предков, даже когда их signals читает только
256
+ * сам владелец: перенос из-под parent с тысячей signals обходит всю тысячу.
257
+ * Прежний учёт рёбер стоил наоборот: ничего для таких signals и обход всех
258
+ * producers Runtime с рёбрами между nodes вместе с их потребителями, где бы
259
+ * в дереве они ни находились.
260
+ */
261
+ private _assertSignalEdgesAllowReparent;
139
262
  releaseNode(node: RaphNode): void;
140
263
  dispose(): void;
141
- private _disposeProducer;
264
+ /**
265
+ * Освобождает producer: публикует финальную structural cause при спросе и
266
+ * снимает его подписчиков, memberships и регистрацию. Вызывается hook-ом
267
+ * onDispose owned-источника и самим Runtime signal.
268
+ *
269
+ * @internal
270
+ */
271
+ _disposeProducer(record: RaphProducer, hasReactiveSubscribers?: boolean): void;
142
272
  private _dispatchProducer;
143
273
  private _dispatchProducerLazily;
144
274
  private _createProducerCause;
145
275
  private _retainRuntimeSubscriber;
146
276
  private _releaseRuntimeSubscriber;
147
277
  private _releaseRuntimeActivations;
148
- private _linkImplicitDependency;
278
+ /**
279
+ * Учитывает одно фактическое implicit-ребро графа от producer к consumer
280
+ * owner и возвращает его точное снятие. Вызывается hook-ом onDependency
281
+ * producers с отдельной записью; Runtime signal этого hook не имеет, и его
282
+ * рёбра проверка reparent читает прямо из графа.
283
+ *
284
+ * @internal
285
+ */
286
+ _linkImplicitDependency(record: RaphProducer, consumerOwner: object): (() => void) | undefined;
149
287
  private _subscribeOne;
150
288
  private _subscribeNode;
151
289
  private _createNodeEdgeRelease;