@virentia/effector 0.2.1 → 0.3.1
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/README.md +113 -25
- package/dist/index.cjs +255 -274
- package/dist/index.d.cts +23 -19
- package/dist/index.d.mts +23 -19
- package/dist/index.mjs +253 -275
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import * as virentia from "@virentia/core";
|
|
2
2
|
import { Effect, EventCallable, Scope, Unit, UnitTargetable } from "effector";
|
|
3
3
|
|
|
4
|
-
//#region lib/
|
|
5
|
-
interface EffectorCompatibilityOptions {
|
|
6
|
-
name?: string;
|
|
7
|
-
}
|
|
4
|
+
//#region lib/types.d.ts
|
|
8
5
|
interface EffectorAssociationConfig {
|
|
9
6
|
virentia: virentia.Scope;
|
|
10
7
|
effector: Scope;
|
|
@@ -13,24 +10,31 @@ interface EffectorAssociationLookup {
|
|
|
13
10
|
virentia?: virentia.Scope;
|
|
14
11
|
effector?: Scope;
|
|
15
12
|
}
|
|
16
|
-
type EffectorPayloadMap<From, To> = (payload: From) => To;
|
|
17
|
-
type EffectorCompatibilityUnsubscribe = () => void;
|
|
18
13
|
interface EffectorAssociation {
|
|
19
|
-
|
|
14
|
+
readonly virentia: virentia.Scope;
|
|
15
|
+
readonly effector: Scope;
|
|
20
16
|
}
|
|
21
|
-
interface
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
link<From, To = From>(from: Unit<From> | VirentiaUnit<From>, to: UnitTargetable<To> | VirentiaTarget<To>, map?: EffectorPayloadMap<From, To>): EffectorCompatibilityUnsubscribe;
|
|
25
|
-
asEffector<T>(unit: virentia.Effect<T, any, any>): Effect<T, any, unknown>;
|
|
26
|
-
asEffector<T>(unit: VirentiaUnit<T>): EventCallable<T>;
|
|
27
|
-
asEffector<T>(unit: Unit<T>): Unit<T>;
|
|
28
|
-
asVirentia<T>(unit: VirentiaUnit<T>): VirentiaUnit<T>;
|
|
29
|
-
asVirentia<T>(unit: Effect<T, any, any>): virentia.Effect<T, any, unknown>;
|
|
30
|
-
asVirentia<T>(unit: Unit<T>): virentia.EventCallable<T>;
|
|
17
|
+
interface EffectorAssociations {
|
|
18
|
+
readonly byVirentia: WeakMap<virentia.Scope, EffectorAssociation>;
|
|
19
|
+
readonly byEffector: WeakMap<Scope, EffectorAssociation>;
|
|
31
20
|
}
|
|
32
21
|
type VirentiaUnit<T = unknown> = virentia.Event<T> | virentia.EventCallable<T> | virentia.Effect<T, any, any> | virentia.Store<T> | virentia.StoreWritable<T>;
|
|
33
22
|
type VirentiaTarget<T = unknown> = virentia.EventCallable<T> | virentia.Effect<T, any, any> | virentia.StoreWritable<T>;
|
|
34
|
-
declare function createEffectorCompatibility(_options?: EffectorCompatibilityOptions): EffectorCompatibility;
|
|
35
23
|
//#endregion
|
|
36
|
-
|
|
24
|
+
//#region lib/associations.d.ts
|
|
25
|
+
declare const effectorAssociations: EffectorAssociations;
|
|
26
|
+
declare function associate(config: EffectorAssociationConfig): EffectorAssociation;
|
|
27
|
+
declare function ensureAssociation(config?: EffectorAssociationLookup): EffectorAssociation;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region lib/fool.d.ts
|
|
30
|
+
declare function fool<Params, Done, Fail>(unit: virentia.Effect<Params, Done, Fail>): virentia.Effect<Params, Done, Fail> & Effect<Params, Done, Fail>;
|
|
31
|
+
declare function fool<T>(unit: virentia.EventCallable<T>): virentia.EventCallable<T> & EventCallable<T>;
|
|
32
|
+
declare function fool<T>(unit: virentia.Event<T>): virentia.Event<T> & EventCallable<T>;
|
|
33
|
+
declare function fool<T>(unit: virentia.StoreWritable<T>): virentia.StoreWritable<T> & EventCallable<T>;
|
|
34
|
+
declare function fool<T>(unit: virentia.Store<T>): virentia.Store<T> & EventCallable<T>;
|
|
35
|
+
declare function fool<Params, Done, Fail>(unit: Effect<Params, Done, Fail>): Effect<Params, Done, Fail> & virentia.Effect<Params, Done, Fail>;
|
|
36
|
+
declare function fool<T>(unit: EventCallable<T>): EventCallable<T> & virentia.EventCallable<T>;
|
|
37
|
+
declare function fool<T>(unit: UnitTargetable<T>): UnitTargetable<T> & virentia.EventCallable<T>;
|
|
38
|
+
declare function fool<T>(unit: Unit<T>): Unit<T> & virentia.EventCallable<T>;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { EffectorAssociation, EffectorAssociationConfig, EffectorAssociationLookup, EffectorAssociations, VirentiaTarget, VirentiaUnit, associate, effectorAssociations, ensureAssociation, fool };
|
package/dist/index.mjs
CHANGED
|
@@ -1,240 +1,185 @@
|
|
|
1
1
|
import * as virentia from "@virentia/core";
|
|
2
|
-
import { allSettled, clearNode, createEffect, createEvent, createNode,
|
|
3
|
-
//#region lib/
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!config.virentia) throw new Error("Effector compatibility association requires a Virentia scope");
|
|
28
|
-
if (!config.effector) throw new Error("Effector compatibility association requires an Effector scope");
|
|
29
|
-
assertScopesAvailable(config);
|
|
30
|
-
const existing = findRuntime(config);
|
|
31
|
-
if (existing) {
|
|
32
|
-
existing.assertSamePair(config);
|
|
33
|
-
return existing;
|
|
34
|
-
}
|
|
35
|
-
const runtime = new EffectorRuntimeImpl({
|
|
36
|
-
...config,
|
|
37
|
-
release: () => {
|
|
38
|
-
runtimes.delete(runtime);
|
|
39
|
-
effectorByVirentia.delete(config.virentia);
|
|
40
|
-
virentiaByEffector.delete(config.effector);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
runtimes.add(runtime);
|
|
44
|
-
effectorByVirentia.set(config.virentia, config.effector);
|
|
45
|
-
virentiaByEffector.set(config.effector, config.virentia);
|
|
46
|
-
for (const installer of installers) runtime.addInstaller(installer);
|
|
47
|
-
return runtime;
|
|
48
|
-
}
|
|
49
|
-
function assertScopesAvailable(config) {
|
|
50
|
-
const existingEffector = effectorByVirentia.get(config.virentia);
|
|
51
|
-
if (existingEffector && existingEffector !== config.effector) throw new Error("Virentia scope is already associated with another Effector scope");
|
|
52
|
-
const existingVirentia = virentiaByEffector.get(config.effector);
|
|
53
|
-
if (existingVirentia && existingVirentia !== config.virentia) throw new Error("Effector scope is already associated with another Virentia scope");
|
|
2
|
+
import { allSettled, clearNode, createEffect, createEvent, createNode, is, launch, step } from "effector";
|
|
3
|
+
//#region lib/errors.ts
|
|
4
|
+
function createMissingAssociationError(config) {
|
|
5
|
+
if (config.effector) return /* @__PURE__ */ new Error("Effector association is missing for provided Effector scope");
|
|
6
|
+
if (config.virentia) return /* @__PURE__ */ new Error("Effector association is missing for provided Virentia scope");
|
|
7
|
+
return /* @__PURE__ */ new Error("Effector association is missing. Call associate({ virentia, effector }) before using fooled units.");
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region lib/associations.ts
|
|
11
|
+
const effectorAssociations = {
|
|
12
|
+
byVirentia: /* @__PURE__ */ new WeakMap(),
|
|
13
|
+
byEffector: /* @__PURE__ */ new WeakMap()
|
|
14
|
+
};
|
|
15
|
+
function associate(config) {
|
|
16
|
+
if (!config.virentia) throw new Error("Effector association requires a Virentia scope");
|
|
17
|
+
if (!config.effector) throw new Error("Effector association requires an Effector scope");
|
|
18
|
+
const existingByVirentia = effectorAssociations.byVirentia.get(config.virentia);
|
|
19
|
+
const existingByEffector = effectorAssociations.byEffector.get(config.effector);
|
|
20
|
+
if (existingByVirentia && existingByVirentia.effector !== config.effector) throw new Error("Virentia scope is already associated with another Effector scope");
|
|
21
|
+
if (existingByEffector && existingByEffector.virentia !== config.virentia) throw new Error("Effector scope is already associated with another Virentia scope");
|
|
22
|
+
const existing = existingByVirentia ?? existingByEffector;
|
|
23
|
+
if (existing) {
|
|
24
|
+
effectorAssociations.byVirentia.set(config.virentia, existing);
|
|
25
|
+
effectorAssociations.byEffector.set(config.effector, existing);
|
|
26
|
+
return existing;
|
|
54
27
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
28
|
+
const association = {
|
|
29
|
+
virentia: config.virentia,
|
|
30
|
+
effector: config.effector
|
|
31
|
+
};
|
|
32
|
+
effectorAssociations.byVirentia.set(config.virentia, association);
|
|
33
|
+
effectorAssociations.byEffector.set(config.effector, association);
|
|
34
|
+
return association;
|
|
35
|
+
}
|
|
36
|
+
function ensureAssociation(config = {}) {
|
|
37
|
+
const association = findAssociation(config);
|
|
38
|
+
if (!association) throw createMissingAssociationError(config);
|
|
39
|
+
return association;
|
|
40
|
+
}
|
|
41
|
+
function findAssociation(config = {}) {
|
|
42
|
+
let association;
|
|
43
|
+
if (config.virentia) {
|
|
44
|
+
association = effectorAssociations.byVirentia.get(config.virentia);
|
|
45
|
+
if (association && config.effector && association.effector !== config.effector) return null;
|
|
46
|
+
if (association) return association;
|
|
59
47
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (runtime) return runtime;
|
|
65
|
-
}
|
|
66
|
-
if (config.effector) {
|
|
67
|
-
const virentiaScope = virentiaByEffector.get(config.effector);
|
|
68
|
-
const runtime = virentiaScope ? findRuntimeByPair(virentiaScope, config.effector) : null;
|
|
69
|
-
if (runtime) return runtime;
|
|
70
|
-
}
|
|
71
|
-
return null;
|
|
48
|
+
if (config.effector) {
|
|
49
|
+
association = effectorAssociations.byEffector.get(config.effector);
|
|
50
|
+
if (association && config.virentia && association.virentia !== config.virentia) return null;
|
|
51
|
+
if (association) return association;
|
|
72
52
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region lib/association-state.ts
|
|
57
|
+
const associationState = /* @__PURE__ */ new WeakMap();
|
|
58
|
+
function shouldSkipEffector(association, unit) {
|
|
59
|
+
return (getAssociationState(association).suppressedEffector.get(unit) ?? 0) > 0;
|
|
60
|
+
}
|
|
61
|
+
function shouldSkipVirentia(association, unit) {
|
|
62
|
+
return (getAssociationState(association).suppressedVirentia.get(unit) ?? 0) > 0;
|
|
63
|
+
}
|
|
64
|
+
function suppressEffector(association, unit, fn) {
|
|
65
|
+
const state = getAssociationState(association);
|
|
66
|
+
incrementSuppression(state.suppressedEffector, unit);
|
|
67
|
+
try {
|
|
68
|
+
return fn();
|
|
69
|
+
} finally {
|
|
70
|
+
decrementSuppression(state.suppressedEffector, unit);
|
|
76
71
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
}
|
|
73
|
+
function suppressVirentia(association, unit) {
|
|
74
|
+
const state = getAssociationState(association);
|
|
75
|
+
incrementSuppression(state.suppressedVirentia, unit);
|
|
76
|
+
return () => {
|
|
77
|
+
decrementSuppression(state.suppressedVirentia, unit);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function getAssociationState(association) {
|
|
81
|
+
let state = associationState.get(association);
|
|
82
|
+
if (!state) {
|
|
83
|
+
state = {
|
|
84
|
+
suppressedEffector: /* @__PURE__ */ new Map(),
|
|
85
|
+
suppressedVirentia: /* @__PURE__ */ new Map()
|
|
83
86
|
};
|
|
87
|
+
associationState.set(association, state);
|
|
84
88
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
return state;
|
|
90
|
+
}
|
|
91
|
+
function incrementSuppression(map, unit) {
|
|
92
|
+
map.set(unit, (map.get(unit) ?? 0) + 1);
|
|
93
|
+
}
|
|
94
|
+
function decrementSuppression(map, unit) {
|
|
95
|
+
const next = (map.get(unit) ?? 0) - 1;
|
|
96
|
+
if (next <= 0) map.delete(unit);
|
|
97
|
+
else map.set(unit, next);
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region lib/guards.ts
|
|
101
|
+
function isEffectorUnit(value) {
|
|
102
|
+
return is.unit(value);
|
|
103
|
+
}
|
|
104
|
+
function isVirentiaUnit(value) {
|
|
105
|
+
return Boolean(value && (typeof value === "object" || typeof value === "function") && "node" in value && !isEffectorUnit(value));
|
|
106
|
+
}
|
|
107
|
+
function isVirentiaEffect(value) {
|
|
108
|
+
return Boolean(isVirentiaUnit(value) && "doneData" in value && "pending" in value);
|
|
109
|
+
}
|
|
110
|
+
function isObjectLike(value) {
|
|
111
|
+
return (typeof value === "object" || typeof value === "function") && value !== null;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region lib/runtime.ts
|
|
115
|
+
function installVirentiaToEffectorLink(from, to) {
|
|
116
|
+
installVirentiaToTargetLink(from, to, identity, true);
|
|
117
|
+
}
|
|
118
|
+
function installVirentiaToTargetLink(from, to, map, suppressEffectorWatch) {
|
|
119
|
+
const watcher = virentia.reaction({
|
|
120
|
+
on: from,
|
|
121
|
+
run(payload) {
|
|
122
|
+
const association = resolveAssociationFromVirentiaScope();
|
|
123
|
+
if (shouldSkipVirentia(association, from)) return;
|
|
124
|
+
runBridgeTarget(association, to, map(payload), { suppressWatch: suppressEffectorWatch });
|
|
96
125
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
runtime.emitVirentia(unit, payload, { suppressReaction: true });
|
|
102
|
-
});
|
|
103
|
-
registerInstaller((runtime) => installLink(runtime, {
|
|
104
|
-
from: unit,
|
|
105
|
-
to: adapter
|
|
106
|
-
}));
|
|
107
|
-
return adapter;
|
|
108
|
-
}
|
|
109
|
-
function createVirentiaAdapter(unit) {
|
|
110
|
-
if (isVirentiaUnit(unit)) return unit;
|
|
111
|
-
if (is.effect(unit)) return virentia.effect((payload) => {
|
|
112
|
-
return resolveRuntimeFromVirentiaScope().call(unit, payload);
|
|
113
|
-
});
|
|
114
|
-
const adapter = virentia.event();
|
|
115
|
-
createEffectorScopeNode(unit, (payload, scope) => {
|
|
116
|
-
const runtime = resolveRuntimeFromEffectorScope(scope);
|
|
117
|
-
if (runtime.shouldSkipEffector(unit)) return;
|
|
118
|
-
runtime.emitVirentia(adapter, payload, { suppressReaction: true });
|
|
119
|
-
});
|
|
120
|
-
registerInstaller((runtime) => installLink(runtime, {
|
|
121
|
-
from: adapter,
|
|
122
|
-
to: unit
|
|
123
|
-
}));
|
|
124
|
-
return adapter;
|
|
125
|
-
}
|
|
126
|
-
function resolveRuntimeFromEffectorScope(scope) {
|
|
127
|
-
if (!scope) throw createMissingRuntimeError({});
|
|
128
|
-
const runtime = ensureAssociation({ effector: scope });
|
|
129
|
-
const activeVirentiaScope = virentia.getCurrentScope();
|
|
130
|
-
if (activeVirentiaScope && activeVirentiaScope !== runtime.virentia) throw new Error("Effector scope is associated with another Virentia scope");
|
|
131
|
-
return runtime;
|
|
132
|
-
}
|
|
133
|
-
function resolveRuntimeFromVirentiaScope() {
|
|
134
|
-
const activeVirentiaScope = virentia.getCurrentScope();
|
|
135
|
-
if (!activeVirentiaScope) throw createMissingRuntimeError({});
|
|
136
|
-
return ensureAssociation({ virentia: activeVirentiaScope });
|
|
137
|
-
}
|
|
126
|
+
});
|
|
127
|
+
return () => {
|
|
128
|
+
watcher.stop();
|
|
129
|
+
};
|
|
138
130
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
cleanups = /* @__PURE__ */ new Set();
|
|
144
|
-
cleanupByInstaller = /* @__PURE__ */ new Map();
|
|
145
|
-
releaseAssociation;
|
|
146
|
-
suppressedEffector = /* @__PURE__ */ new Map();
|
|
147
|
-
suppressedVirentia = /* @__PURE__ */ new Map();
|
|
148
|
-
constructor(config) {
|
|
149
|
-
this.virentia = config.virentia;
|
|
150
|
-
this.effector = config.effector;
|
|
151
|
-
this.releaseAssociation = config.release;
|
|
131
|
+
function runBridgeTarget(association, to, payload, options = {}) {
|
|
132
|
+
if (isEffectorUnit(to)) {
|
|
133
|
+
launchEffector(association, to, payload, { suppressWatch: options.suppressWatch });
|
|
134
|
+
return;
|
|
152
135
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
136
|
+
emitVirentia(association, to, payload, { suppressReaction: options.suppressReaction });
|
|
137
|
+
}
|
|
138
|
+
async function callAssociation(association, unit, payload) {
|
|
139
|
+
if (isEffectorUnit(unit)) return allSettled(unit, {
|
|
140
|
+
params: payload,
|
|
141
|
+
scope: association.effector
|
|
142
|
+
});
|
|
143
|
+
if (isVirentiaEffect(unit)) return virentia.scoped(association.virentia, () => unit(payload));
|
|
144
|
+
await virentia.allSettled(unit, {
|
|
145
|
+
payload,
|
|
146
|
+
scope: association.virentia
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function launchEffector(association, unit, payload, options = {}) {
|
|
150
|
+
const launchUnit = () => {
|
|
151
|
+
launch({
|
|
152
|
+
target: unit,
|
|
156
153
|
params: payload,
|
|
157
|
-
scope:
|
|
154
|
+
scope: association.effector
|
|
158
155
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
trackCleanup(unsubscribe) {
|
|
166
|
-
this.cleanups.add(unsubscribe);
|
|
167
|
-
return () => {
|
|
168
|
-
this.cleanups.delete(unsubscribe);
|
|
169
|
-
unsubscribe();
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
dispose() {
|
|
173
|
-
if (this.disposed) return;
|
|
174
|
-
this.disposed = true;
|
|
175
|
-
for (const cleanup of [...this.cleanups]) cleanup();
|
|
176
|
-
this.cleanups.clear();
|
|
177
|
-
this.cleanupByInstaller.clear();
|
|
178
|
-
this.releaseAssociation();
|
|
179
|
-
}
|
|
180
|
-
addInstaller(installer) {
|
|
181
|
-
if (this.disposed || this.cleanupByInstaller.has(installer)) return;
|
|
182
|
-
const cleanup = installer(this);
|
|
183
|
-
this.cleanupByInstaller.set(installer, cleanup);
|
|
184
|
-
this.cleanups.add(cleanup);
|
|
185
|
-
}
|
|
186
|
-
removeInstaller(installer) {
|
|
187
|
-
const cleanup = this.cleanupByInstaller.get(installer);
|
|
188
|
-
if (!cleanup) return;
|
|
189
|
-
this.cleanupByInstaller.delete(installer);
|
|
190
|
-
this.cleanups.delete(cleanup);
|
|
191
|
-
cleanup();
|
|
192
|
-
}
|
|
193
|
-
assertSamePair(config) {
|
|
194
|
-
if (config.virentia !== this.virentia || config.effector !== this.effector) throw new Error("Effector compatibility association is already bound to another scope pair");
|
|
195
|
-
}
|
|
196
|
-
launchEffector(unit, payload, options = {}) {
|
|
197
|
-
this.assertAlive();
|
|
198
|
-
if (options.suppressWatch) this.incrementSuppression(this.suppressedEffector, unit);
|
|
199
|
-
try {
|
|
200
|
-
launch({
|
|
201
|
-
target: unit,
|
|
202
|
-
params: payload,
|
|
203
|
-
scope: this.effector
|
|
204
|
-
});
|
|
205
|
-
} finally {
|
|
206
|
-
if (options.suppressWatch) this.decrementSuppression(this.suppressedEffector, unit);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
emitVirentia(unit, payload, options = {}) {
|
|
210
|
-
this.assertAlive();
|
|
211
|
-
if (options.suppressReaction) this.incrementSuppression(this.suppressedVirentia, unit);
|
|
212
|
-
const settled = virentia.allSettled(unit, {
|
|
213
|
-
payload,
|
|
214
|
-
scope: this.virentia
|
|
215
|
-
});
|
|
216
|
-
if (options.suppressReaction) settled.finally(() => {
|
|
217
|
-
this.decrementSuppression(this.suppressedVirentia, unit);
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
shouldSkipEffector(unit) {
|
|
221
|
-
return (this.suppressedEffector.get(unit) ?? 0) > 0;
|
|
222
|
-
}
|
|
223
|
-
shouldSkipVirentia(unit) {
|
|
224
|
-
return (this.suppressedVirentia.get(unit) ?? 0) > 0;
|
|
225
|
-
}
|
|
226
|
-
assertAlive() {
|
|
227
|
-
if (this.disposed) throw new Error("Effector compatibility association is disposed");
|
|
228
|
-
}
|
|
229
|
-
incrementSuppression(map, unit) {
|
|
230
|
-
map.set(unit, (map.get(unit) ?? 0) + 1);
|
|
231
|
-
}
|
|
232
|
-
decrementSuppression(map, unit) {
|
|
233
|
-
const next = (map.get(unit) ?? 0) - 1;
|
|
234
|
-
if (next <= 0) map.delete(unit);
|
|
235
|
-
else map.set(unit, next);
|
|
156
|
+
};
|
|
157
|
+
if (options.suppressWatch) {
|
|
158
|
+
suppressEffector(association, unit, launchUnit);
|
|
159
|
+
return;
|
|
236
160
|
}
|
|
237
|
-
|
|
161
|
+
launchUnit();
|
|
162
|
+
}
|
|
163
|
+
function emitVirentia(association, unit, payload, options = {}) {
|
|
164
|
+
const release = options.suppressReaction ? suppressVirentia(association, unit) : null;
|
|
165
|
+
const settled = virentia.allSettled(unit, {
|
|
166
|
+
payload,
|
|
167
|
+
scope: association.virentia
|
|
168
|
+
});
|
|
169
|
+
if (release) settled.finally(release);
|
|
170
|
+
}
|
|
171
|
+
function resolveAssociationFromEffectorScope(scope) {
|
|
172
|
+
if (!scope) throw createMissingAssociationError({});
|
|
173
|
+
const association = ensureAssociation({ effector: scope });
|
|
174
|
+
const activeVirentiaScope = virentia.getCurrentScope();
|
|
175
|
+
if (activeVirentiaScope && activeVirentiaScope !== association.virentia) throw new Error("Effector scope is associated with another Virentia scope");
|
|
176
|
+
return association;
|
|
177
|
+
}
|
|
178
|
+
function resolveAssociationFromVirentiaScope() {
|
|
179
|
+
const activeVirentiaScope = virentia.getCurrentScope();
|
|
180
|
+
if (!activeVirentiaScope) throw createMissingAssociationError({});
|
|
181
|
+
return ensureAssociation({ virentia: activeVirentiaScope });
|
|
182
|
+
}
|
|
238
183
|
function createEffectorScopeNode(unit, fn) {
|
|
239
184
|
const node = createNode({
|
|
240
185
|
parent: [unit],
|
|
@@ -250,62 +195,95 @@ function createEffectorScopeNode(unit, fn) {
|
|
|
250
195
|
clearNode(node);
|
|
251
196
|
};
|
|
252
197
|
}
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
if (isEffectorUnit(definition.from)) return toUnsubscribe(createWatch({
|
|
256
|
-
unit: definition.from,
|
|
257
|
-
scope: runtime.effector,
|
|
258
|
-
fn(payload) {
|
|
259
|
-
if (runtime.shouldSkipEffector(definition.from)) return;
|
|
260
|
-
const nextPayload = map(payload);
|
|
261
|
-
if (isEffectorUnit(definition.to)) {
|
|
262
|
-
runtime.launchEffector(definition.to, nextPayload);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
runtime.emitVirentia(definition.to, nextPayload, { suppressReaction: true });
|
|
266
|
-
}
|
|
267
|
-
}));
|
|
268
|
-
if (isVirentiaUnit(definition.from)) {
|
|
269
|
-
const watcher = virentia.reaction({
|
|
270
|
-
on: definition.from,
|
|
271
|
-
scope: runtime.virentia,
|
|
272
|
-
run(payload) {
|
|
273
|
-
if (runtime.shouldSkipVirentia(definition.from)) return;
|
|
274
|
-
const nextPayload = map(payload);
|
|
275
|
-
if (isEffectorUnit(definition.to)) {
|
|
276
|
-
runtime.launchEffector(definition.to, nextPayload, { suppressWatch: true });
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
runtime.emitVirentia(definition.to, nextPayload);
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
return () => {
|
|
283
|
-
watcher.stop();
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
throw new Error("Effector compatibility link expects Effector or Virentia units");
|
|
198
|
+
function identity(value) {
|
|
199
|
+
return value;
|
|
287
200
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region lib/fool.ts
|
|
203
|
+
const fooledUnit = Symbol("virentia.effector.fooledUnit");
|
|
204
|
+
const fooledUnits = /* @__PURE__ */ new WeakMap();
|
|
205
|
+
function fool(unit) {
|
|
206
|
+
if (!isObjectLike(unit)) throw new Error("fool() expects an Effector or Virentia unit");
|
|
207
|
+
if (unit[fooledUnit]) return unit;
|
|
208
|
+
const cached = fooledUnits.get(unit);
|
|
209
|
+
if (cached) return cached;
|
|
210
|
+
const fooled = isEffectorUnit(unit) ? createFooledEffectorUnit(unit) : isVirentiaUnit(unit) ? createFooledVirentiaUnit(unit) : null;
|
|
211
|
+
if (!fooled) throw new Error("fool() expects an Effector or Virentia unit");
|
|
212
|
+
markFooledUnit(fooled);
|
|
213
|
+
fooledUnits.set(unit, fooled);
|
|
214
|
+
return fooled;
|
|
292
215
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
216
|
+
function createFooledVirentiaUnit(unit) {
|
|
217
|
+
const effectorUnit = createEffectorAdapter(unit);
|
|
218
|
+
const base = typeof unit === "function" ? createCallableBridge((...args) => unit(...args)) : {};
|
|
219
|
+
copyUnitProperties(base, effectorUnit);
|
|
220
|
+
copyUnitProperties(base, unit);
|
|
221
|
+
return base;
|
|
295
222
|
}
|
|
296
|
-
function
|
|
297
|
-
|
|
223
|
+
function createFooledEffectorUnit(unit) {
|
|
224
|
+
const virentiaUnit = createVirentiaAdapter(unit);
|
|
225
|
+
const base = typeof unit === "function" || typeof virentiaUnit === "function" ? createCallableBridge((...args) => {
|
|
226
|
+
if (virentia.getCurrentScope() && typeof virentiaUnit === "function") return virentiaUnit(...args);
|
|
227
|
+
if (typeof unit === "function") return unit(...args);
|
|
228
|
+
throw new Error("Effector store cannot be called");
|
|
229
|
+
}) : {};
|
|
230
|
+
copyUnitProperties(base, virentiaUnit);
|
|
231
|
+
copyUnitProperties(base, unit);
|
|
232
|
+
return base;
|
|
298
233
|
}
|
|
299
|
-
function
|
|
300
|
-
return
|
|
234
|
+
function createCallableBridge(call) {
|
|
235
|
+
return (...args) => call(...args);
|
|
301
236
|
}
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
237
|
+
function createEffectorAdapter(unit) {
|
|
238
|
+
if (isEffectorUnit(unit)) return unit;
|
|
239
|
+
if (isVirentiaEffect(unit)) {
|
|
240
|
+
const scopeQueue = [];
|
|
241
|
+
const adapter = createEffect((payload) => {
|
|
242
|
+
return callAssociation(resolveAssociationFromEffectorScope(scopeQueue.shift()), unit, payload);
|
|
243
|
+
});
|
|
244
|
+
createEffectorScopeNode(adapter, (_payload, scope) => {
|
|
245
|
+
scopeQueue.push(scope);
|
|
246
|
+
});
|
|
247
|
+
return adapter;
|
|
248
|
+
}
|
|
249
|
+
const adapter = createEvent();
|
|
250
|
+
createEffectorScopeNode(adapter, (payload, scope) => {
|
|
251
|
+
const association = resolveAssociationFromEffectorScope(scope);
|
|
252
|
+
if (shouldSkipEffector(association, adapter)) return;
|
|
253
|
+
emitVirentia(association, unit, payload, { suppressReaction: true });
|
|
254
|
+
});
|
|
255
|
+
installVirentiaToEffectorLink(unit, adapter);
|
|
256
|
+
return adapter;
|
|
306
257
|
}
|
|
307
|
-
function
|
|
308
|
-
return
|
|
258
|
+
function createVirentiaAdapter(unit) {
|
|
259
|
+
if (isVirentiaUnit(unit)) return unit;
|
|
260
|
+
if (is.effect(unit)) return virentia.effect((payload) => {
|
|
261
|
+
return callAssociation(resolveAssociationFromVirentiaScope(), unit, payload);
|
|
262
|
+
});
|
|
263
|
+
const adapter = virentia.event();
|
|
264
|
+
createEffectorScopeNode(unit, (payload, scope) => {
|
|
265
|
+
const association = resolveAssociationFromEffectorScope(scope);
|
|
266
|
+
if (shouldSkipEffector(association, unit)) return;
|
|
267
|
+
emitVirentia(association, adapter, payload, { suppressReaction: true });
|
|
268
|
+
});
|
|
269
|
+
installVirentiaToEffectorLink(adapter, unit);
|
|
270
|
+
return adapter;
|
|
271
|
+
}
|
|
272
|
+
function copyUnitProperties(target, source) {
|
|
273
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
274
|
+
if (key === "length" || key === "name" || key === "prototype") continue;
|
|
275
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
276
|
+
if (!descriptor) continue;
|
|
277
|
+
const existing = Object.getOwnPropertyDescriptor(target, key);
|
|
278
|
+
if (existing && !existing.configurable) continue;
|
|
279
|
+
Object.defineProperty(target, key, descriptor);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function markFooledUnit(unit) {
|
|
283
|
+
Object.defineProperty(unit, fooledUnit, {
|
|
284
|
+
enumerable: false,
|
|
285
|
+
value: true
|
|
286
|
+
});
|
|
309
287
|
}
|
|
310
288
|
//#endregion
|
|
311
|
-
export {
|
|
289
|
+
export { associate, effectorAssociations, ensureAssociation, fool };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@virentia/effector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Compatibility layer between @virentia/core and the real Effector package",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://movpushmov.dev/virentia/effector/",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"effector": "^23.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@virentia/core": "0.
|
|
38
|
+
"@virentia/core": "0.3.1"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsdown",
|