@virentia/effector 0.2.1 → 0.3.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/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/index.d.ts
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
- dispose(): void;
14
+ readonly virentia: virentia.Scope;
15
+ readonly effector: Scope;
20
16
  }
21
- interface EffectorCompatibility {
22
- associate(config: EffectorAssociationConfig): EffectorAssociation;
23
- ensureAssociation(config?: EffectorAssociationLookup): EffectorAssociation;
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
- export { EffectorAssociation, EffectorAssociationConfig, EffectorAssociationLookup, EffectorCompatibility, EffectorCompatibilityOptions, EffectorCompatibilityUnsubscribe, EffectorPayloadMap, VirentiaTarget, VirentiaUnit, createEffectorCompatibility };
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, createWatch, is, launch, step } from "effector";
3
- //#region lib/index.ts
4
- function createEffectorCompatibility(_options = {}) {
5
- const runtimes = /* @__PURE__ */ new Set();
6
- const effectorByVirentia = /* @__PURE__ */ new WeakMap();
7
- const virentiaByEffector = /* @__PURE__ */ new WeakMap();
8
- const installers = /* @__PURE__ */ new Set();
9
- return {
10
- associate,
11
- ensureAssociation(config = {}) {
12
- return ensureAssociation(config);
13
- },
14
- link(from, to, map) {
15
- const definition = {
16
- from,
17
- to,
18
- map
19
- };
20
- const installer = (runtime) => installLink(runtime, definition);
21
- return registerInstaller(installer);
22
- },
23
- asEffector: ((unit) => createEffectorAdapter(unit)),
24
- asVirentia: ((unit) => createVirentiaAdapter(unit))
25
- };
26
- function associate(config) {
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
- function ensureAssociation(config = {}) {
56
- const runtime = findRuntime(config);
57
- if (!runtime) throw createMissingRuntimeError(config);
58
- return runtime;
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
- function findRuntime(config = {}) {
61
- if (config.virentia) {
62
- const effectorScope = effectorByVirentia.get(config.virentia);
63
- const runtime = effectorScope ? findRuntimeByPair(config.virentia, effectorScope) : null;
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
- function findRuntimeByPair(virentiaScope, effectorScope) {
74
- for (const runtime of runtimes) if (runtime.virentia === virentiaScope && runtime.effector === effectorScope) return runtime;
75
- return null;
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
- function registerInstaller(installer) {
78
- installers.add(installer);
79
- for (const runtime of runtimes) runtime.addInstaller(installer);
80
- return () => {
81
- installers.delete(installer);
82
- for (const runtime of runtimes) runtime.removeInstaller(installer);
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
- function createEffectorAdapter(unit) {
86
- if (isEffectorUnit(unit)) return unit;
87
- if (isVirentiaEffect(unit)) {
88
- const scopeQueue = [];
89
- const adapter = createEffect((payload) => {
90
- return resolveRuntimeFromEffectorScope(scopeQueue.shift()).call(unit, payload);
91
- });
92
- createEffectorScopeNode(adapter, (_payload, scope) => {
93
- scopeQueue.push(scope);
94
- });
95
- return adapter;
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
- const adapter = createEvent();
98
- createEffectorScopeNode(adapter, (payload, scope) => {
99
- const runtime = resolveRuntimeFromEffectorScope(scope);
100
- if (runtime.shouldSkipEffector(adapter)) return;
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
- var EffectorRuntimeImpl = class {
140
- virentia;
141
- effector;
142
- disposed = false;
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
- async call(unit, payload) {
154
- this.assertAlive();
155
- if (isEffectorUnit(unit)) return allSettled(unit, {
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: this.effector
154
+ scope: association.effector
158
155
  });
159
- if (isVirentiaEffect(unit)) return virentia.scoped(this.virentia, () => unit(payload));
160
- await virentia.allSettled(unit, {
161
- payload,
162
- scope: this.virentia
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 installLink(runtime, definition) {
254
- const map = definition.map ?? identity;
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
- function createMissingRuntimeError(config) {
289
- if (config.effector) return /* @__PURE__ */ new Error("Effector compatibility association is missing for provided Effector scope");
290
- if (config.virentia) return /* @__PURE__ */ new Error("Effector compatibility association is missing for provided Virentia scope");
291
- return /* @__PURE__ */ new Error("Effector compatibility association is missing. Call associate({ virentia, effector }) before using adapters.");
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 isEffectorUnit(value) {
294
- return is.unit(value);
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 isVirentiaUnit(value) {
297
- return Boolean(value && (typeof value === "object" || typeof value === "function") && "node" in value && !isEffectorUnit(value));
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 isVirentiaEffect(value) {
300
- return Boolean(isVirentiaUnit(value) && "doneData" in value && "$pending" in value);
234
+ function createCallableBridge(call) {
235
+ return (...args) => call(...args);
301
236
  }
302
- function toUnsubscribe(subscription) {
303
- return () => {
304
- subscription.unsubscribe();
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 identity(value) {
308
- return value;
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 { createEffectorCompatibility };
289
+ export { associate, effectorAssociations, ensureAssociation, fool };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@virentia/effector",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
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.2.1"
38
+ "@virentia/core": "0.3.0"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsdown",