bunja 0.0.12 → 1.0.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/.vscode/settings.json +2 -1
- package/README.md +167 -1
- package/bunja.ts +68 -53
- package/deno.json +9 -0
- package/deno.lock +525 -0
- package/dist/bunja-Q0ZusYIM.cjs +189 -0
- package/dist/bunja-fHIhQAuL.js +158 -0
- package/dist/bunja.cjs +9 -0
- package/dist/bunja.d.cts +55 -0
- package/dist/bunja.d.ts +55 -0
- package/dist/bunja.js +3 -0
- package/dist/react.cjs +65 -0
- package/dist/react.d.cts +9 -0
- package/dist/react.d.ts +9 -0
- package/dist/react.js +36 -0
- package/package.json +44 -4
- package/presentations/2024-11-28-en.pdf +0 -0
- package/presentations/2024-11-28.pdf +0 -0
- package/presentations/README.md +9 -0
- package/react.ts +23 -13
- package/test.ts +206 -0
- package/tsconfig.json +7 -1
- package/tsdown.config.ts +10 -0
package/.vscode/settings.json
CHANGED
package/README.md
CHANGED
|
@@ -57,4 +57,170 @@ function MyComponent() {
|
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
### Defining a Bunja that relies on other Bunja
|
|
61
|
+
|
|
62
|
+
If you want to manage a state with a broad lifetime and another state with a narrower lifetime, you can create a (narrower) bunja that depends on a (broader) bunja.
|
|
63
|
+
For example, you can think of a bunja that manages the WebSocket connection and disconnection, and another bunja that subscribes to a specific resource over the connected WebSocket.
|
|
64
|
+
|
|
65
|
+
In an application composed of multiple pages, you might want to subscribe to the Foo resource on page A and the Bar resource on page B, while using the same WebSocket connection regardless of which page you're on.
|
|
66
|
+
In such a case, you can write the following code.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// To simplify the example, code for buffering and reconnection has been omitted.
|
|
70
|
+
const websocketBunja = bunja([], () => {
|
|
71
|
+
let socket;
|
|
72
|
+
const send = (message) => socket.send(JSON.stringify(message));
|
|
73
|
+
|
|
74
|
+
const emitter = new EventEmitter();
|
|
75
|
+
const on = (handler) => {
|
|
76
|
+
emitter.on("message", handler);
|
|
77
|
+
return () => emitter.off("message", handler);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
send,
|
|
82
|
+
on,
|
|
83
|
+
[bunja.effect]() {
|
|
84
|
+
socket = new WebSocket("...");
|
|
85
|
+
socket.onmessage = (e) => emitter.emit("message", JSON.parse(e.data));
|
|
86
|
+
return () => socket.close();
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const resourceFooBunja = bunja([websocketBunja], ({ send, on }) => {
|
|
92
|
+
const resourceFooAtom = atom();
|
|
93
|
+
return {
|
|
94
|
+
resourceFooAtom,
|
|
95
|
+
[bunja.effect]() {
|
|
96
|
+
const off = on((message) => {
|
|
97
|
+
if (message.type === "foo") store.set(resourceAtom, message.value);
|
|
98
|
+
});
|
|
99
|
+
send("subscribe-foo");
|
|
100
|
+
return () => {
|
|
101
|
+
send("unsubscribe-foo");
|
|
102
|
+
off();
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const resourceBarBunja = bunja([websocketBunja], ({ send, on }) => {
|
|
109
|
+
const resourceBarAtom = atom();
|
|
110
|
+
// ...
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
function PageA() {
|
|
114
|
+
const { resourceFooAtom } = useBunja(resourceFooBunja);
|
|
115
|
+
const resourceFoo = useAtomValue(resourceFooAtom);
|
|
116
|
+
// ...
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function PageB() {
|
|
120
|
+
const { resourceBarAtom } = useBunja(resourceBarBunja);
|
|
121
|
+
const resourceBar = useAtomValue(resourceBarAtom);
|
|
122
|
+
// ...
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Notice that `websocketBunja` is not directly `useBunja`-ed.
|
|
127
|
+
When you `useBunja` either `resourceFooBunja` or `resourceBarBunja`, since they depend on `websocketBunja`,
|
|
128
|
+
it has the same effect as if `websocketBunja` were also `useBunja`-ed.
|
|
129
|
+
|
|
130
|
+
> [!NOTE]
|
|
131
|
+
> When a bunja starts, the initialization effect of the bunja with a broader lifetime is called first.\
|
|
132
|
+
> Similarly, when a bunja ends, the cleanup effect of the bunja with the broader lifetime is called first.\
|
|
133
|
+
> This behavior is aligned with how React's `useEffect` cleanup function is invoked, where the parent’s cleanup is executed before the child’s in the render tree.
|
|
134
|
+
>
|
|
135
|
+
> See: <https://github.com/facebook/react/issues/16728>
|
|
136
|
+
|
|
137
|
+
### Dependency injection using Scope
|
|
138
|
+
|
|
139
|
+
You can use a bunja for local state management.\
|
|
140
|
+
When you specify a scope as a dependency of the bunja, separate bunja instances are created based on the values injected into the scope.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { bunja, createScope } from "bunja";
|
|
144
|
+
|
|
145
|
+
const UrlScope = createScope();
|
|
146
|
+
|
|
147
|
+
const fetchBunja = bunja([UrlScope], (url) => {
|
|
148
|
+
const queryAtom = atomWithQuery((get) => ({
|
|
149
|
+
queryKey: [url],
|
|
150
|
+
queryFn: async () => (await fetch(url)).json(),
|
|
151
|
+
}));
|
|
152
|
+
return { queryAtom };
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Injecting dependencies via React context
|
|
157
|
+
|
|
158
|
+
If you bind a scope to a React context, bunjas that depend on the scope can retrieve values from the corresponding React context.
|
|
159
|
+
|
|
160
|
+
In the example below, there are two React instances (`<ChildComponent />`) that reference the same `fetchBunja`, but since each looks at a different context value, two separate bunja instances are also created.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { createContext } from "react";
|
|
164
|
+
import { bunja, createScope } from "bunja";
|
|
165
|
+
import { bindScope } from "bunja/react";
|
|
166
|
+
|
|
167
|
+
const UrlContext = createContext("https://example.com/");
|
|
168
|
+
const UrlScope = createScope();
|
|
169
|
+
bindScope(UrlScope, UrlContext);
|
|
170
|
+
|
|
171
|
+
const fetchBunja = bunja([UrlScope], (url) => {
|
|
172
|
+
const queryAtom = atomWithQuery((get) => ({
|
|
173
|
+
queryKey: [url],
|
|
174
|
+
queryFn: async () => (await fetch(url)).json(),
|
|
175
|
+
}));
|
|
176
|
+
return { queryAtom };
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
function ParentComponent() {
|
|
180
|
+
return (
|
|
181
|
+
<>
|
|
182
|
+
<UrlContext.Provider value="https://example.com/foo">
|
|
183
|
+
<ChildComponent />
|
|
184
|
+
</UrlContext.Provider>
|
|
185
|
+
<UrlContext.Provider value="https://example.com/bar">
|
|
186
|
+
<ChildComponent />
|
|
187
|
+
</UrlContext.Provider>
|
|
188
|
+
</>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function ChildComponent() {
|
|
193
|
+
const { queryAtom } = useBunja(fetchBunja);
|
|
194
|
+
const { data, isPending, isError } = useAtomValue(queryAtom);
|
|
195
|
+
// Your component logic here
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
You can use the `createScopeFromContext` function to handle both the creation of the scope and the binding to the context in one step.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { createContext } from "react";
|
|
203
|
+
import { createScopeFromContext } from "bunja/react";
|
|
204
|
+
|
|
205
|
+
const UrlContext = createContext("https://example.com/");
|
|
206
|
+
const UrlScope = createScopeFromContext(UrlContext);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Injecting dependencies directly into the scope
|
|
210
|
+
|
|
211
|
+
You might want to use a bunja directly within a React component where the values to be injected into the scope are created.
|
|
212
|
+
|
|
213
|
+
In such cases, you can use the inject function to inject values into the scope without wrapping the context separately.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { inject } from "bunja/react";
|
|
217
|
+
|
|
218
|
+
function MyComponent() {
|
|
219
|
+
const { queryAtom } = useBunja(
|
|
220
|
+
fetchBunja,
|
|
221
|
+
inject([[UrlScope, "https://example.com/"]])
|
|
222
|
+
);
|
|
223
|
+
const { data, isPending, isError } = useAtomValue(queryAtom);
|
|
224
|
+
// Your component logic here
|
|
225
|
+
}
|
|
226
|
+
```
|
package/bunja.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export type Dep<T> = Bunja<T> | Scope<T>;
|
|
2
2
|
|
|
3
|
+
const bunjaEffectSymbol: unique symbol = Symbol("Bunja.effect");
|
|
4
|
+
type BunjaEffectSymbol = typeof bunjaEffectSymbol;
|
|
5
|
+
|
|
3
6
|
export class Bunja<T> {
|
|
4
7
|
public static readonly bunjas: Bunja<any>[] = [];
|
|
5
8
|
public readonly id: number;
|
|
@@ -9,27 +12,29 @@ export class Bunja<T> {
|
|
|
9
12
|
public parents: Bunja<any>[], // one depth parents
|
|
10
13
|
public relatedBunjas: Bunja<any>[], // toposorted parents without self
|
|
11
14
|
public relatedScopes: Scope<any>[], // deduped
|
|
12
|
-
public init: (...args: any[]) => T & BunjaValue
|
|
15
|
+
public init: (...args: any[]) => T & BunjaValue,
|
|
13
16
|
) {
|
|
14
17
|
this.id = Bunja.bunjas.length;
|
|
15
18
|
Bunja.bunjas.push(this);
|
|
16
19
|
}
|
|
17
|
-
static readonly effect =
|
|
18
|
-
toString() {
|
|
20
|
+
static readonly effect: BunjaEffectSymbol = bunjaEffectSymbol;
|
|
21
|
+
toString(): string {
|
|
19
22
|
const { id, debugLabel } = this;
|
|
20
23
|
return `[Bunja:${id}${debugLabel && ` - ${debugLabel}`}]`;
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
export type HashFn<T = any, U = any> = (value: T) => U;
|
|
28
|
+
|
|
24
29
|
export class Scope<T> {
|
|
25
30
|
public static readonly scopes: Scope<any>[] = [];
|
|
26
31
|
public readonly id: number;
|
|
27
32
|
public debugLabel: string = "";
|
|
28
|
-
constructor() {
|
|
33
|
+
constructor(public readonly hash: HashFn = id) {
|
|
29
34
|
this.id = Scope.scopes.length;
|
|
30
35
|
Scope.scopes.push(this);
|
|
31
36
|
}
|
|
32
|
-
toString() {
|
|
37
|
+
toString(): string {
|
|
33
38
|
const { id, debugLabel } = this;
|
|
34
39
|
return `[Scope:${id}${debugLabel && ` - ${debugLabel}`}]`;
|
|
35
40
|
}
|
|
@@ -40,12 +45,19 @@ export type ReadScope = <T>(scope: Scope<T>) => T;
|
|
|
40
45
|
export class BunjaStore {
|
|
41
46
|
#bunjas: Record<string, BunjaInstance> = {};
|
|
42
47
|
#scopes: Map<Scope<any>, Map<any, ScopeInstance>> = new Map();
|
|
43
|
-
get<T>(
|
|
48
|
+
get<T>(
|
|
49
|
+
bunja: Bunja<T>,
|
|
50
|
+
readScope: ReadScope,
|
|
51
|
+
): {
|
|
52
|
+
value: T;
|
|
53
|
+
mount: () => () => void;
|
|
54
|
+
deps: any[];
|
|
55
|
+
} {
|
|
44
56
|
const scopeInstanceMap = new Map(
|
|
45
57
|
bunja.relatedScopes.map((scope) => [
|
|
46
58
|
scope,
|
|
47
59
|
this.#getScopeInstance(scope, readScope(scope)),
|
|
48
|
-
])
|
|
60
|
+
]),
|
|
49
61
|
);
|
|
50
62
|
const bunjaInstance = this.#getBunjaInstance(bunja, scopeInstanceMap);
|
|
51
63
|
const { relatedBunjaInstanceMap } = bunjaInstance; // toposorted
|
|
@@ -55,7 +67,7 @@ export class BunjaStore {
|
|
|
55
67
|
relatedBunjaInstanceMap.forEach((related) => related.add());
|
|
56
68
|
bunjaInstance.add();
|
|
57
69
|
scopeInstanceMap.forEach((scope) => scope.add());
|
|
58
|
-
return function unmount() {
|
|
70
|
+
return function unmount(): void {
|
|
59
71
|
// concern: reverse order?
|
|
60
72
|
relatedBunjaInstanceMap.forEach((related) => related.sub());
|
|
61
73
|
bunjaInstance.sub();
|
|
@@ -67,10 +79,10 @@ export class BunjaStore {
|
|
|
67
79
|
}
|
|
68
80
|
#getBunjaInstance(
|
|
69
81
|
bunja: Bunja<any>,
|
|
70
|
-
scopeInstanceMap: Map<Scope<any>, ScopeInstance
|
|
82
|
+
scopeInstanceMap: Map<Scope<any>, ScopeInstance>,
|
|
71
83
|
): BunjaInstance {
|
|
72
84
|
const localScopeInstanceMap = new Map(
|
|
73
|
-
bunja.relatedScopes.map((scope) => [scope, scopeInstanceMap.get(scope)!])
|
|
85
|
+
bunja.relatedScopes.map((scope) => [scope, scopeInstanceMap.get(scope)!]),
|
|
74
86
|
);
|
|
75
87
|
const scopeInstanceIds = Array.from(localScopeInstanceMap.values())
|
|
76
88
|
.map(({ instanceId }) => instanceId)
|
|
@@ -81,7 +93,7 @@ export class BunjaStore {
|
|
|
81
93
|
bunja.relatedBunjas.map((relatedBunja) => [
|
|
82
94
|
relatedBunja,
|
|
83
95
|
this.#getBunjaInstance(relatedBunja, scopeInstanceMap),
|
|
84
|
-
])
|
|
96
|
+
]),
|
|
85
97
|
);
|
|
86
98
|
const args = bunja.deps.map((dep) => {
|
|
87
99
|
if (dep instanceof Bunja) return relatedBunjaInstanceMap.get(dep)!.value;
|
|
@@ -92,76 +104,78 @@ export class BunjaStore {
|
|
|
92
104
|
() => delete this.#bunjas[bunjaInstanceId],
|
|
93
105
|
bunjaInstanceId,
|
|
94
106
|
relatedBunjaInstanceMap,
|
|
95
|
-
bunja.init.apply(bunja, args)
|
|
107
|
+
bunja.init.apply(bunja, args),
|
|
96
108
|
);
|
|
97
109
|
this.#bunjas[bunjaInstanceId] = bunjaInstance;
|
|
98
110
|
return bunjaInstance;
|
|
99
111
|
}
|
|
100
112
|
#getScopeInstance(scope: Scope<any>, value: any): ScopeInstance {
|
|
101
|
-
const
|
|
102
|
-
|
|
113
|
+
const key = scope.hash(value);
|
|
114
|
+
const scopeInstanceMap = this.#scopes.get(scope) ??
|
|
115
|
+
this.#scopes.set(scope, new Map()).get(scope)!;
|
|
103
116
|
const init = () =>
|
|
104
117
|
new ScopeInstance(
|
|
105
|
-
() => scopeInstanceMap.delete(
|
|
118
|
+
() => scopeInstanceMap.delete(key),
|
|
106
119
|
ScopeInstance.counter++,
|
|
107
120
|
scope,
|
|
108
|
-
value
|
|
121
|
+
value,
|
|
109
122
|
);
|
|
110
123
|
return (
|
|
111
|
-
scopeInstanceMap.get(
|
|
112
|
-
|
|
124
|
+
scopeInstanceMap.get(key) ??
|
|
125
|
+
scopeInstanceMap.set(key, init()).get(key)!
|
|
113
126
|
);
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
export const createBunjaStore = () => new BunjaStore();
|
|
130
|
+
export const createBunjaStore = (): BunjaStore => new BunjaStore();
|
|
118
131
|
|
|
119
132
|
export type BunjaEffectFn = () => () => void;
|
|
120
133
|
export interface BunjaValue {
|
|
121
134
|
[Bunja.effect]?: BunjaEffectFn;
|
|
122
135
|
}
|
|
123
136
|
|
|
124
|
-
|
|
125
|
-
export function bunja<T, U>(
|
|
126
|
-
deps: [Dep<U>],
|
|
127
|
-
init: (u: U) => T & BunjaValue
|
|
128
|
-
): Bunja<T>;
|
|
129
|
-
export function bunja<T, U, V>(
|
|
130
|
-
deps: [Dep<U>, Dep<V>],
|
|
131
|
-
init: (u: U, v: V) => T & BunjaValue
|
|
132
|
-
): Bunja<T>;
|
|
133
|
-
export function bunja<T, U, V, W>(
|
|
134
|
-
deps: [Dep<U>, Dep<V>, Dep<W>],
|
|
135
|
-
init: (u: U, v: V, w: W) => T & BunjaValue
|
|
136
|
-
): Bunja<T>;
|
|
137
|
-
export function bunja<T, U, V, W, X>(
|
|
138
|
-
deps: [Dep<U>, Dep<V>, Dep<W>, Dep<X>],
|
|
139
|
-
init: (u: U, v: V, w: W, x: X) => T & BunjaValue
|
|
140
|
-
): Bunja<T>;
|
|
141
|
-
export function bunja<T, U, V, W, X, Y>(
|
|
142
|
-
deps: [Dep<U>, Dep<V>, Dep<W>, Dep<X>, Dep<Y>],
|
|
143
|
-
init: (u: U, v: V, w: W, x: X, y: Y) => T & BunjaValue
|
|
144
|
-
): Bunja<T>;
|
|
145
|
-
export function bunja<T, U, V, W, X, Y, Z>(
|
|
146
|
-
deps: [Dep<U>, Dep<V>, Dep<W>, Dep<X>, Dep<Y>, Dep<Z>],
|
|
147
|
-
init: (u: U, v: V, w: W, x: X, y: Y, z: Z) => T & BunjaValue
|
|
148
|
-
): Bunja<T>;
|
|
149
|
-
export function bunja<T, const U extends any[]>(
|
|
137
|
+
function bunjaImpl<T, const U extends any[]>(
|
|
150
138
|
deps: { [K in keyof U]: Dep<U[K]> },
|
|
151
|
-
init: (...args: U) => T & BunjaValue
|
|
139
|
+
init: (...args: U) => T & BunjaValue,
|
|
152
140
|
): Bunja<T> {
|
|
153
141
|
const parents = deps.filter((dep) => dep instanceof Bunja) as Bunja<any>[];
|
|
154
142
|
const scopes = deps.filter((dep) => dep instanceof Scope) as Scope<any>[];
|
|
155
143
|
const relatedBunjas = toposort(parents);
|
|
156
144
|
const relatedScopes = Array.from(
|
|
157
|
-
new Set([...scopes, ...parents.flatMap((parent) => parent.relatedScopes)])
|
|
145
|
+
new Set([...scopes, ...parents.flatMap((parent) => parent.relatedScopes)]),
|
|
158
146
|
);
|
|
159
147
|
return new Bunja(deps, parents, relatedBunjas, relatedScopes, init as any);
|
|
160
148
|
}
|
|
161
|
-
|
|
149
|
+
bunjaImpl.effect = Bunja.effect;
|
|
162
150
|
|
|
163
|
-
export
|
|
164
|
-
|
|
151
|
+
export const bunja: {
|
|
152
|
+
<T>(deps: [], init: () => T & BunjaValue): Bunja<T>;
|
|
153
|
+
<T, U>(deps: [Dep<U>], init: (u: U) => T & BunjaValue): Bunja<T>;
|
|
154
|
+
<T, U, V>(
|
|
155
|
+
deps: [Dep<U>, Dep<V>],
|
|
156
|
+
init: (u: U, v: V) => T & BunjaValue,
|
|
157
|
+
): Bunja<T>;
|
|
158
|
+
<T, U, V, W>(
|
|
159
|
+
deps: [Dep<U>, Dep<V>, Dep<W>],
|
|
160
|
+
init: (u: U, v: V, w: W) => T & BunjaValue,
|
|
161
|
+
): Bunja<T>;
|
|
162
|
+
<T, U, V, W, X>(
|
|
163
|
+
deps: [Dep<U>, Dep<V>, Dep<W>, Dep<X>],
|
|
164
|
+
init: (u: U, v: V, w: W, x: X) => T & BunjaValue,
|
|
165
|
+
): Bunja<T>;
|
|
166
|
+
<T, U, V, W, X, Y>(
|
|
167
|
+
deps: [Dep<U>, Dep<V>, Dep<W>, Dep<X>, Dep<Y>],
|
|
168
|
+
init: (u: U, v: V, w: W, x: X, y: Y) => T & BunjaValue,
|
|
169
|
+
): Bunja<T>;
|
|
170
|
+
<T, U, V, W, X, Y, Z>(
|
|
171
|
+
deps: [Dep<U>, Dep<V>, Dep<W>, Dep<X>, Dep<Y>, Dep<Z>],
|
|
172
|
+
init: (u: U, v: V, w: W, x: X, y: Y, z: Z) => T & BunjaValue,
|
|
173
|
+
): Bunja<T>;
|
|
174
|
+
readonly effect: BunjaEffectSymbol;
|
|
175
|
+
} = bunjaImpl;
|
|
176
|
+
|
|
177
|
+
export function createScope<T>(hash?: HashFn): Scope<T> {
|
|
178
|
+
return new Scope(hash);
|
|
165
179
|
}
|
|
166
180
|
|
|
167
181
|
abstract class RefCounter {
|
|
@@ -183,6 +197,7 @@ abstract class RefCounter {
|
|
|
183
197
|
abstract dispose(): void;
|
|
184
198
|
}
|
|
185
199
|
|
|
200
|
+
const id = <T>(x: T): T => x;
|
|
186
201
|
const noop = () => {};
|
|
187
202
|
class BunjaInstance extends RefCounter {
|
|
188
203
|
#cleanup: (() => void) | undefined;
|
|
@@ -191,7 +206,7 @@ class BunjaInstance extends RefCounter {
|
|
|
191
206
|
dispose: () => void,
|
|
192
207
|
public instanceId: string,
|
|
193
208
|
public relatedBunjaInstanceMap: Map<Bunja<any>, BunjaInstance>,
|
|
194
|
-
public value: BunjaValue
|
|
209
|
+
public value: BunjaValue,
|
|
195
210
|
) {
|
|
196
211
|
super();
|
|
197
212
|
this.#dispose = () => {
|
|
@@ -199,7 +214,7 @@ class BunjaInstance extends RefCounter {
|
|
|
199
214
|
dispose();
|
|
200
215
|
};
|
|
201
216
|
}
|
|
202
|
-
add() {
|
|
217
|
+
override add() {
|
|
203
218
|
this.#cleanup ??= this.value[Bunja.effect]?.() ?? noop;
|
|
204
219
|
super.add();
|
|
205
220
|
}
|
|
@@ -214,7 +229,7 @@ class ScopeInstance extends RefCounter {
|
|
|
214
229
|
public dispose: () => void,
|
|
215
230
|
public instanceId: number,
|
|
216
231
|
public scope: Scope<any>,
|
|
217
|
-
public value: any
|
|
232
|
+
public value: any,
|
|
218
233
|
) {
|
|
219
234
|
super();
|
|
220
235
|
}
|