@playfast/reform-proof 1.1.1 → 1.2.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/src/facade.ts DELETED
@@ -1,258 +0,0 @@
1
- import { Array as Arr, Effect, ManagedRuntime, Option, Schema as S } from 'effect'
2
- import {
3
- Composition,
4
- type CompositionClass,
5
- type CompositionService,
6
- isStructure,
7
- type RenderEnv,
8
- type SlotChild,
9
- type SlotClass,
10
- type Trigger,
11
- type UiCapture,
12
- type UiContract,
13
- } from '@playfast/reform'
14
- import { AssertionFailed, UnknownAction, UnknownSlot } from './errors'
15
- import { matchProps } from './assertions'
16
- import type { Action, Facade, SlotFacade } from './index'
17
- import {
18
- keyed,
19
- SETTLE_MAX_RENDERS,
20
- SETTLE_STEP,
21
- settleDrain,
22
- type RuntimeServices,
23
- type Sink,
24
- uiNameOf,
25
- } from './sink'
26
- import {
27
- collectBindings,
28
- driveStructure,
29
- type Mounted,
30
- type NodeRef,
31
- type SettleProgress,
32
- slotsOf,
33
- } from './treeBindings'
34
-
35
- type AnyFacade = Facade<UiContract>
36
- type AnySlotFacade = SlotFacade<UiContract>
37
- const encodeUnknown = S.encodeSync(S.parseJson(S.Unknown))
38
-
39
- const renderMounted = Effect.fn('renderMounted')(function* (
40
- mounted: Mounted,
41
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
42
- sink: Sink,
43
- ): Effect.fn.Return<ReadonlyArray<Mounted>, never, CompositionService> {
44
- const service = yield* mounted.comp.tag
45
- const env: RenderEnv = { props: mounted.props, tracker: { add: () => {} } }
46
- const endRenderSpan = sink.instrumentation.uiRendered(uiNameOf(mounted.comp))
47
- const frame = yield* Composition.render(service, env)
48
- endRenderSpan()
49
- if (!isStructure(frame)) {
50
- return yield* Effect.dieMessage(
51
- `reform-proof: composition ${uiNameOf(mounted.comp)} did not return a Structure`,
52
- )
53
- }
54
- return driveStructure(mounted.comp, frame, mounted.key, sink, bindings)
55
- })
56
-
57
- const renderLevel: (
58
- frontier: ReadonlyArray<Mounted>,
59
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
60
- sink: Sink,
61
- ) => Effect.Effect<void, never, CompositionService> = Effect.fn('renderLevel')(function* (
62
- frontier: ReadonlyArray<Mounted>,
63
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
64
- sink: Sink,
65
- ): Effect.fn.Return<void, never, CompositionService> {
66
- if (frontier.length === 0) {
67
- return
68
- }
69
- const levels = yield* Effect.forEach(frontier, (mounted) =>
70
- renderMounted(mounted, bindings, sink),
71
- )
72
- yield* renderLevel(levels.flat(), bindings, sink)
73
- })
74
-
75
- const renderTree = Effect.fn('renderTree')(function* (
76
- root: CompositionClass<unknown>,
77
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
78
- sink: Sink,
79
- ): Effect.fn.Return<void, never, CompositionService> {
80
- sink.reset()
81
- yield* renderLevel([{ comp: root, props: {}, key: Option.none() }], bindings, sink)
82
- })
83
-
84
- export const capturesFor = (sink: Sink, name: string): ReadonlyArray<UiCapture> =>
85
- sink.captures.filter((capture) => capture.name === name)
86
-
87
- export const fingerprint = (sink: Sink): string =>
88
- encodeUnknown(sink.captures.map((capture) => [capture.name, capture.props]))
89
-
90
- const settleTree = Effect.fn('settleTree')(function* (
91
- root: CompositionClass<unknown>,
92
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
93
- sink: Sink,
94
- ): Effect.fn.Return<void, never, CompositionService> {
95
- yield* settleDrain
96
- yield* renderTree(root, bindings, sink)
97
- yield* Effect.iterate(
98
- {
99
- previous: fingerprint(sink),
100
- remaining: SETTLE_MAX_RENDERS,
101
- stable: false,
102
- },
103
- {
104
- while: (progress: SettleProgress) => !progress.stable && progress.remaining > 0,
105
- body: (progress) =>
106
- Effect.gen(function* () {
107
- yield* settleDrain
108
- yield* Effect.sleep(SETTLE_STEP)
109
- yield* renderTree(root, bindings, sink)
110
- const current = fingerprint(sink)
111
- return {
112
- previous: current,
113
- remaining: progress.remaining - 1,
114
- stable: current === progress.previous,
115
- }
116
- }),
117
- },
118
- )
119
- })
120
-
121
- export const makeFacadeEffect: (
122
- root: CompositionClass<unknown>,
123
- sink: Sink,
124
- dispatched: Set<string>,
125
- ) => Effect.Effect<
126
- {
127
- readonly facade: AnyFacade
128
- readonly settle: Effect.Effect<void, never, CompositionService>
129
- },
130
- never,
131
- SlotChild
132
- > = Effect.fn('makeFacadeEffect')(function* (
133
- root: CompositionClass<unknown>,
134
- sink: Sink,
135
- dispatched: Set<string>,
136
- ): Effect.fn.Return<
137
- {
138
- readonly facade: AnyFacade
139
- readonly settle: Effect.Effect<void, never, CompositionService>
140
- },
141
- never,
142
- SlotChild
143
- > {
144
- const bindings = yield* collectBindings(root)
145
- const settle = settleTree(root, bindings, sink)
146
- const rerender = renderTree(root, bindings, sink)
147
-
148
- const triggerOf = (ref: NodeRef, event: string): Effect.Effect<Trigger<unknown>> =>
149
- Option.match(Option.fromNullable(capturesFor(sink, ref.name)[ref.index]?.events[event]), {
150
- onNone: () =>
151
- Effect.dieMessage(
152
- new UnknownAction({
153
- composition: ref.name,
154
- action: event,
155
- rendered: capturesFor(sink, ref.name).length,
156
- }).message,
157
- ),
158
- onSome: Effect.succeed,
159
- })
160
-
161
- const propsFor = (ref: NodeRef): Effect.Effect<unknown, never, CompositionService> =>
162
- Effect.map(rerender, () => capturesFor(sink, ref.name)[ref.index]?.props)
163
-
164
- const nodeFacade = (ref: NodeRef, comp: CompositionClass<unknown>): AnyFacade => ({
165
- props: propsFor(ref),
166
- expectProps: (partial) =>
167
- propsFor(ref).pipe(
168
- Effect.flatMap((props) => {
169
- const mismatch = matchProps({ actual: props, expected: partial })
170
- return mismatch === undefined
171
- ? Effect.void
172
- : Effect.dieMessage(new AssertionFailed({ detail: mismatch }).message)
173
- }),
174
- ),
175
- actions: keyed(
176
- (event): Action =>
177
- (payload) =>
178
- rerender.pipe(
179
- Effect.flatMap(() => triggerOf(ref, event)),
180
- Effect.flatMap((trigger) =>
181
- Effect.sync(() => {
182
- dispatched.add(event)
183
- trigger(payload)
184
- }),
185
- ),
186
- Effect.flatMap(() => settle),
187
- Effect.flatMap(() => propsFor(ref)),
188
- ),
189
- ),
190
- slots: keyed((slotName) => slotFacade(comp, slotName)),
191
- frame: settle,
192
- })
193
-
194
- const slotFacade = (parent: CompositionClass<unknown>, slotName: string): AnySlotFacade => {
195
- const slotClass = slotsOf(parent)[slotName]
196
- const child = slotClass && bindings.get(slotClass)
197
- if (child === undefined) {
198
- return Effect.runSync(
199
- Effect.dieMessage(new UnknownSlot({ parent: uiNameOf(parent), slot: slotName }).message),
200
- )
201
- }
202
- const childName = uiNameOf(child)
203
- const atIndex = (index: number): Effect.Effect<AnyFacade, never, CompositionService> =>
204
- Effect.as(rerender, nodeFacade({ name: childName, index }, child))
205
- const first = nodeFacade({ name: childName, index: 0 }, child)
206
- const indexOfKey = (key: string): Option.Option<number> =>
207
- Arr.findFirstIndex(capturesFor(sink, childName), (capture) => capture.key === key)
208
- return {
209
- first: atIndex(0),
210
- at: atIndex,
211
- all: Effect.map(rerender, () =>
212
- capturesFor(sink, childName).map((_capture, index) =>
213
- nodeFacade({ name: childName, index }, child),
214
- ),
215
- ),
216
- byKey: (key) =>
217
- Effect.flatMap(rerender, () =>
218
- Option.match(indexOfKey(key), {
219
- onNone: () =>
220
- Effect.dieMessage(
221
- new UnknownSlot({
222
- parent: uiNameOf(parent),
223
- slot: `${slotName}[key=${key}]`,
224
- }).message,
225
- ),
226
- onSome: (index) => Effect.succeed(nodeFacade({ name: childName, index }, child)),
227
- }),
228
- ),
229
- where: (predicate) =>
230
- Effect.map(rerender, () =>
231
- capturesFor(sink, childName).flatMap((capture, index) =>
232
- predicate(capture.props) ? [nodeFacade({ name: childName, index }, child)] : [],
233
- ),
234
- ),
235
- count: Effect.map(rerender, () => capturesFor(sink, childName).length),
236
- props: first.props,
237
- expectProps: first.expectProps,
238
- actions: first.actions,
239
- slots: first.slots,
240
- frame: first.frame,
241
- }
242
- }
243
-
244
- return {
245
- facade: nodeFacade({ name: uiNameOf(root), index: 0 }, root),
246
- settle,
247
- }
248
- })
249
-
250
- export const makeFacade = (
251
- runtime: ManagedRuntime.ManagedRuntime<RuntimeServices, never>,
252
- root: CompositionClass<unknown>,
253
- sink: Sink,
254
- dispatched: Set<string>,
255
- ): {
256
- readonly facade: AnyFacade
257
- readonly settle: Effect.Effect<void, never, CompositionService>
258
- } => runtime.runSync(makeFacadeEffect(root, sink, dispatched))
package/src/product.ts DELETED
@@ -1,77 +0,0 @@
1
- import { Option, type Schema } from 'effect'
2
- import type { CompositionClass } from '@playfast/reform'
3
- import type { ContractOf, EventsOf } from './index'
4
-
5
- type AnyValue = Schema.Schema.Type<Schema.Schema.Any>
6
- type AnyComposition = CompositionClass<AnyValue, AnyValue>
7
-
8
- export interface RequirementManifest<Comp extends AnyComposition, Statement extends string> {
9
- readonly kind: 'ProductRequirement'
10
- readonly name: Statement
11
- readonly statement: Statement
12
- readonly composition: Comp
13
- readonly events: Option.Option<ReadonlyArray<keyof EventsOf<ContractOf<Comp>>>>
14
- }
15
-
16
- export interface RequirementClass<Comp extends AnyComposition, Statement extends string> {
17
- new (): {}
18
- readonly manifest: RequirementManifest<Comp, Statement>
19
- }
20
-
21
- export interface ProductManifest<Comp extends AnyComposition> {
22
- readonly kind: 'Product'
23
- readonly name: string
24
- readonly composition: Comp
25
- readonly requirements: ReadonlyArray<RequirementClass<Comp, string>>
26
- }
27
-
28
- export interface ProductClass<Comp extends AnyComposition = AnyComposition> {
29
- new (): {}
30
- readonly manifest: ProductManifest<Comp>
31
- }
32
-
33
- interface MakeRequirementOptionsExternalApi<Comp extends AnyComposition> {
34
- readonly events?: ReadonlyArray<keyof EventsOf<ContractOf<Comp>>>
35
- }
36
-
37
- const makeRequirement = <Comp extends AnyComposition, const Statement extends string>(
38
- composition: Comp,
39
- statement: Statement,
40
- options?: MakeRequirementOptionsExternalApi<Comp>,
41
- ): RequirementClass<Comp, Statement> => {
42
- const manifest: RequirementManifest<Comp, Statement> = {
43
- kind: 'ProductRequirement',
44
- name: statement,
45
- statement,
46
- composition,
47
- events: Option.fromNullable(options?.events),
48
- }
49
- return class {
50
- static readonly manifest = manifest
51
- }
52
- }
53
-
54
- export const ProductRequirement: { readonly make: typeof makeRequirement } = {
55
- make: makeRequirement,
56
- }
57
-
58
- interface MakeProductConfig<Comp extends AnyComposition> {
59
- readonly requirements: ReadonlyArray<RequirementClass<Comp, string>>
60
- }
61
-
62
- const makeProduct = <Comp extends AnyComposition>(
63
- composition: Comp,
64
- config: MakeProductConfig<Comp>,
65
- ): ProductClass<Comp> => {
66
- const manifest: ProductManifest<Comp> = {
67
- kind: 'Product',
68
- name: composition.manifest.name,
69
- composition,
70
- requirements: config.requirements,
71
- }
72
- return class {
73
- static readonly manifest = manifest
74
- }
75
- }
76
-
77
- export const Product: { readonly make: typeof makeProduct } = { make: makeProduct }
@@ -1,269 +0,0 @@
1
- import { Array as Arr, Effect, Option, Record as Rec } from 'effect'
2
- import {
3
- Composition,
4
- type CompositionClass,
5
- isStructure,
6
- type RenderEnv,
7
- type RuntimeHandle,
8
- type SlotClass,
9
- type Trigger,
10
- } from '@playfast/reform'
11
- import { AssertionFailed, UnknownAction, UnknownSlot } from './errors'
12
- import { matchProps } from './assertions'
13
- import { capturesFor, fingerprint } from './facade'
14
- import {
15
- keyed,
16
- SETTLE_MAX_RENDERS,
17
- SETTLE_STEP,
18
- settleDrain,
19
- type Sink,
20
- uiNameOf,
21
- } from './sink'
22
- import {
23
- childComposition,
24
- driveStructure,
25
- type Mounted,
26
- type NodeRef,
27
- type SettleProgress,
28
- slotsOf,
29
- } from './treeBindings'
30
-
31
- export interface MountedFacade {
32
- readonly props: Effect.Effect<unknown, never, never>
33
- readonly expectProps: (partial: unknown) => Effect.Effect<void, never, never>
34
- readonly actions: Record<string, (payload: unknown) => Effect.Effect<unknown, never, never>>
35
- readonly slots: Record<string, MountedSlotFacade>
36
- readonly frame: Effect.Effect<void, never, never>
37
- }
38
-
39
- export interface MountedSlotFacade extends MountedFacade {
40
- readonly first: Effect.Effect<MountedFacade, never, never>
41
- readonly at: (index: number) => Effect.Effect<MountedFacade, never, never>
42
- readonly all: Effect.Effect<ReadonlyArray<MountedFacade>, never, never>
43
- readonly byKey: (key: string) => Effect.Effect<MountedFacade, never, never>
44
- readonly where: (
45
- predicate: (props: unknown) => boolean,
46
- ) => Effect.Effect<ReadonlyArray<MountedFacade>, never, never>
47
- readonly count: Effect.Effect<number, never, never>
48
- }
49
-
50
- const collectRuntimeBindings = (
51
- root: CompositionClass<unknown>,
52
- runtime: RuntimeHandle,
53
- ): Map<SlotClass, CompositionClass<unknown>> => {
54
- const bindings = new Map<SlotClass, CompositionClass<unknown>>()
55
- const walk = (comp: CompositionClass<unknown>): void => {
56
- Rec.values(slotsOf(comp)).forEach((slotClass) => {
57
- if (bindings.has(slotClass)) {
58
- return
59
- }
60
- const child = childComposition(runtime.read(slotClass.tag))
61
- bindings.set(slotClass, child)
62
- walk(child)
63
- })
64
- }
65
- walk(root)
66
- return bindings
67
- }
68
-
69
- const renderMountedRuntime = (
70
- runtime: RuntimeHandle,
71
- mounted: Mounted,
72
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
73
- sink: Sink,
74
- ): ReadonlyArray<Mounted> => {
75
- const service = runtime.read(mounted.comp.tag)
76
- const env: RenderEnv = { props: mounted.props, tracker: { add: () => {} } }
77
- const endRenderSpan = sink.instrumentation.uiRendered(uiNameOf(mounted.comp))
78
- const frame = Effect.runSync(Composition.render(service, env))
79
- endRenderSpan()
80
- if (!isStructure(frame)) {
81
- return Effect.runSync(
82
- Effect.dieMessage(
83
- `reform-proof: composition ${uiNameOf(mounted.comp)} did not return a Structure`,
84
- ),
85
- )
86
- }
87
- return driveStructure(mounted.comp, frame, mounted.key, sink, bindings)
88
- }
89
-
90
- const renderLevelRuntime = (
91
- runtime: RuntimeHandle,
92
- frontier: ReadonlyArray<Mounted>,
93
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
94
- sink: Sink,
95
- ): void => {
96
- if (frontier.length === 0) {
97
- return
98
- }
99
- const levels = frontier.map((mounted) => renderMountedRuntime(runtime, mounted, bindings, sink))
100
- renderLevelRuntime(runtime, levels.flat(), bindings, sink)
101
- }
102
-
103
- const renderTreeRuntime = (
104
- runtime: RuntimeHandle,
105
- root: CompositionClass<unknown>,
106
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
107
- sink: Sink,
108
- ): Effect.Effect<void, never, never> =>
109
- Effect.sync(() => {
110
- sink.reset()
111
- renderLevelRuntime(runtime, [{ comp: root, props: {}, key: Option.none() }], bindings, sink)
112
- })
113
-
114
- const settleTreeRuntime = Effect.fn('settleTreeRuntime')(function* (
115
- runtime: RuntimeHandle,
116
- root: CompositionClass<unknown>,
117
- bindings: ReadonlyMap<SlotClass, CompositionClass<unknown>>,
118
- sink: Sink,
119
- ): Effect.fn.Return<void, never, never> {
120
- yield* settleDrain
121
- yield* renderTreeRuntime(runtime, root, bindings, sink)
122
- yield* Effect.iterate(
123
- {
124
- previous: fingerprint(sink),
125
- remaining: SETTLE_MAX_RENDERS,
126
- stable: false,
127
- },
128
- {
129
- while: (progress: SettleProgress) => !progress.stable && progress.remaining > 0,
130
- body: (progress) =>
131
- Effect.gen(function* () {
132
- yield* settleDrain
133
- yield* Effect.sleep(SETTLE_STEP)
134
- yield* renderTreeRuntime(runtime, root, bindings, sink)
135
- const current = fingerprint(sink)
136
- return {
137
- previous: current,
138
- remaining: progress.remaining - 1,
139
- stable: current === progress.previous,
140
- }
141
- }),
142
- },
143
- )
144
- })
145
-
146
- const makeMountedFacade = (
147
- runtime: RuntimeHandle,
148
- root: CompositionClass<unknown>,
149
- sink: Sink,
150
- dispatched: Set<string>,
151
- ): {
152
- readonly facade: MountedFacade
153
- readonly settle: Effect.Effect<void, never, never>
154
- } => {
155
- const bindings = collectRuntimeBindings(root, runtime)
156
- const settle = settleTreeRuntime(runtime, root, bindings, sink)
157
- const rerender = renderTreeRuntime(runtime, root, bindings, sink)
158
-
159
- const triggerOf = (ref: NodeRef, event: string): Effect.Effect<Trigger<unknown>, never, never> =>
160
- Option.match(Option.fromNullable(capturesFor(sink, ref.name)[ref.index]?.events[event]), {
161
- onNone: () =>
162
- Effect.dieMessage(
163
- new UnknownAction({
164
- composition: ref.name,
165
- action: event,
166
- rendered: capturesFor(sink, ref.name).length,
167
- }).message,
168
- ),
169
- onSome: Effect.succeed,
170
- })
171
-
172
- const propsFor = (ref: NodeRef): Effect.Effect<unknown, never, never> =>
173
- Effect.map(rerender, () => capturesFor(sink, ref.name)[ref.index]?.props)
174
-
175
- const nodeFacade = (ref: NodeRef, comp: CompositionClass<unknown>): MountedFacade => ({
176
- props: propsFor(ref),
177
- expectProps: (partial) =>
178
- propsFor(ref).pipe(
179
- Effect.flatMap((props) => {
180
- const mismatch = matchProps({ actual: props, expected: partial })
181
- return mismatch === undefined
182
- ? Effect.void
183
- : Effect.dieMessage(new AssertionFailed({ detail: mismatch }).message)
184
- }),
185
- ),
186
- actions: keyed(
187
- (event) => (payload: unknown) =>
188
- rerender.pipe(
189
- Effect.flatMap(() => triggerOf(ref, event)),
190
- Effect.flatMap((trigger) =>
191
- Effect.sync(() => {
192
- dispatched.add(event)
193
- trigger(payload)
194
- }),
195
- ),
196
- Effect.flatMap(() => settle),
197
- Effect.flatMap(() => propsFor(ref)),
198
- ),
199
- ),
200
- slots: keyed((slotName) => slotFacade(comp, slotName)),
201
- frame: settle,
202
- })
203
-
204
- const slotFacade = (parent: CompositionClass<unknown>, slotName: string): MountedSlotFacade => {
205
- const slotClass = slotsOf(parent)[slotName]
206
- const child = slotClass && bindings.get(slotClass)
207
- if (child === undefined) {
208
- return Effect.runSync(
209
- Effect.dieMessage(new UnknownSlot({ parent: uiNameOf(parent), slot: slotName }).message),
210
- )
211
- }
212
- const childName = uiNameOf(child)
213
- const atIndex = (index: number): Effect.Effect<MountedFacade, never, never> =>
214
- Effect.as(rerender, nodeFacade({ name: childName, index }, child))
215
- const first = nodeFacade({ name: childName, index: 0 }, child)
216
- const indexOfKey = (key: string): Option.Option<number> =>
217
- Arr.findFirstIndex(capturesFor(sink, childName), (capture) => capture.key === key)
218
- return {
219
- first: atIndex(0),
220
- at: atIndex,
221
- all: Effect.map(rerender, () =>
222
- capturesFor(sink, childName).map((_capture, index) =>
223
- nodeFacade({ name: childName, index }, child),
224
- ),
225
- ),
226
- byKey: (key) =>
227
- Effect.flatMap(rerender, () =>
228
- Option.match(indexOfKey(key), {
229
- onNone: () =>
230
- Effect.dieMessage(
231
- new UnknownSlot({
232
- parent: uiNameOf(parent),
233
- slot: `${slotName}[key=${key}]`,
234
- }).message,
235
- ),
236
- onSome: (index) => Effect.succeed(nodeFacade({ name: childName, index }, child)),
237
- }),
238
- ),
239
- where: (predicate) =>
240
- Effect.map(rerender, () =>
241
- capturesFor(sink, childName).flatMap((capture, index) =>
242
- predicate(capture.props) ? [nodeFacade({ name: childName, index }, child)] : [],
243
- ),
244
- ),
245
- count: Effect.map(rerender, () => capturesFor(sink, childName).length),
246
- props: first.props,
247
- expectProps: first.expectProps,
248
- actions: first.actions,
249
- slots: first.slots,
250
- frame: first.frame,
251
- }
252
- }
253
-
254
- return {
255
- facade: nodeFacade({ name: uiNameOf(root), index: 0 }, root),
256
- settle,
257
- }
258
- }
259
-
260
- export const makeRuntimeHandleFacade = (
261
- runtime: RuntimeHandle,
262
- root: CompositionClass<unknown>,
263
- sink: Sink,
264
- dispatched: Set<string>,
265
- ): {
266
- readonly facade: MountedFacade
267
- readonly settle: Effect.Effect<void, never, never>
268
- } => makeMountedFacade(runtime, root, sink, dispatched)
269
-
@@ -1,21 +0,0 @@
1
- import { Array as Arr, Order } from 'effect'
2
- import type { TreeFeatureMount } from './runtimeTreeTypes'
3
-
4
- interface DisposeTreeMountsOptions {
5
- readonly status: { disposed: boolean }
6
- readonly mounts: Map<string, TreeFeatureMount>
7
- }
8
-
9
- export const disposeTreeMounts = ({ status, mounts }: DisposeTreeMountsOptions): void => {
10
- if (status.disposed) {
11
- return
12
- }
13
- status.disposed = true
14
- Arr.sortWith(Array.from(mounts.values()), (record) => -record.depth, Order.number).forEach(
15
- (record) => {
16
- record.attempt += 1
17
- record.dispose()
18
- },
19
- )
20
- mounts.clear()
21
- }