@oscarpalmer/mora 0.9.0 → 0.11.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/batch.cjs CHANGED
@@ -1,12 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const effect = require("./effect.cjs");
4
+ const is = require("./is.cjs");
4
5
  function flushEffects() {
5
- while (exports.batchDepth === 0 && effect.dirtyEffects.size > 0) {
6
- const effects = [...effect.dirtyEffects];
7
- effect.dirtyEffects.clear();
8
- for (const effect$1 of effects) {
9
- effect.runEffect(effect$1);
6
+ while (exports.batchDepth === 0 && batchedHandlers.size > 0) {
7
+ const handlers = [...batchedHandlers];
8
+ batchedHandlers.clear();
9
+ for (const handler of handlers) {
10
+ if (is.isEffect(handler)) {
11
+ effect.runEffect(handler);
12
+ } else {
13
+ handler.callback(handler.state.value);
14
+ }
10
15
  }
11
16
  }
12
17
  }
@@ -19,7 +24,9 @@ function stopBatch() {
19
24
  }
20
25
  flushEffects();
21
26
  }
27
+ const batchedHandlers = /* @__PURE__ */ new Set();
22
28
  exports.batchDepth = 0;
29
+ exports.batchedHandlers = batchedHandlers;
23
30
  exports.flushEffects = flushEffects;
24
31
  exports.startBatch = startBatch;
25
32
  exports.stopBatch = stopBatch;
package/dist/batch.js CHANGED
@@ -1,10 +1,15 @@
1
- import { dirtyEffects, runEffect } from "./effect.js";
1
+ import { runEffect } from "./effect.js";
2
+ import { isEffect } from "./is.js";
2
3
  function flushEffects() {
3
- while (batchDepth === 0 && dirtyEffects.size > 0) {
4
- const effects = [...dirtyEffects];
5
- dirtyEffects.clear();
6
- for (const effect of effects) {
7
- runEffect(effect);
4
+ while (batchDepth === 0 && batchedHandlers.size > 0) {
5
+ const handlers = [...batchedHandlers];
6
+ batchedHandlers.clear();
7
+ for (const handler of handlers) {
8
+ if (isEffect(handler)) {
9
+ runEffect(handler);
10
+ } else {
11
+ handler.callback(handler.state.value);
12
+ }
8
13
  }
9
14
  }
10
15
  }
@@ -17,9 +22,11 @@ function stopBatch() {
17
22
  }
18
23
  flushEffects();
19
24
  }
25
+ const batchedHandlers = /* @__PURE__ */ new Set();
20
26
  let batchDepth = 0;
21
27
  export {
22
28
  batchDepth,
29
+ batchedHandlers,
23
30
  flushEffects,
24
31
  startBatch,
25
32
  stopBatch
package/dist/computed.cjs CHANGED
@@ -5,22 +5,17 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
6
  const batch = require("./batch.cjs");
7
7
  const effect = require("./effect.cjs");
8
+ const reactive = require("./reactive.cjs");
8
9
  exports.activeComputed = void 0;
9
- class Computed {
10
+ class Computed extends reactive.Reactive {
10
11
  constructor(callback) {
11
- __publicField(this, "state");
12
- Object.defineProperty(this, "$mora", {
13
- value: "computed"
14
- });
15
- this.state = {
16
- computeds: /* @__PURE__ */ new Set(),
12
+ super("computed", void 0);
13
+ __publicField(this, "effect", {
17
14
  dirty: true,
18
- effect: void 0,
19
- effects: /* @__PURE__ */ new Set(),
20
- value: void 0
21
- };
22
- this.state.effect = effect.effect(() => {
23
- if (this.state.dirty) {
15
+ instance: void 0
16
+ });
17
+ this.effect.instance = effect.effect(() => {
18
+ if (this.effect.dirty) {
24
19
  const previousComputed = exports.activeComputed;
25
20
  exports.activeComputed = this;
26
21
  const value = callback();
@@ -28,13 +23,16 @@ class Computed {
28
23
  if (!Object.is(this.state.value, value)) {
29
24
  this.state.value = value;
30
25
  for (const computed2 of this.state.computeds) {
31
- computed2.state.dirty = true;
26
+ computed2.effect.dirty = true;
32
27
  }
33
28
  for (const effect2 of this.state.effects) {
34
- effect.dirtyEffects.add(effect2);
29
+ batch.batchedHandlers.add(effect2);
30
+ }
31
+ for (const [, subscription] of this.state.subscriptions) {
32
+ subscription.callback(value);
35
33
  }
36
34
  }
37
- this.state.dirty = false;
35
+ this.effect.dirty = false;
38
36
  }
39
37
  });
40
38
  }
@@ -45,20 +43,14 @@ class Computed {
45
43
  if (exports.activeComputed != null && exports.activeComputed !== this) {
46
44
  this.state.computeds.add(exports.activeComputed);
47
45
  }
48
- if (effect.activeEffect != null && effect.activeEffect !== this.state.effect) {
46
+ if (effect.activeEffect != null && effect.activeEffect !== this.effect.instance) {
49
47
  this.state.effects.add(effect.activeEffect);
50
48
  }
51
- if (this.state.dirty && batch.batchDepth === 0) {
52
- effect.runEffect(this.state.effect);
49
+ if (this.effect.dirty && batch.batchDepth === 0) {
50
+ effect.runEffect(this.effect.instance);
53
51
  }
54
52
  return this.state.value;
55
53
  }
56
- /**
57
- * Get the value _(without reactivity)_
58
- */
59
- peek() {
60
- return this.state.value;
61
- }
62
54
  }
63
55
  function computed(callback) {
64
56
  return new Computed(callback);
package/dist/computed.js CHANGED
@@ -1,24 +1,19 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { batchDepth } from "./batch.js";
5
- import { effect, dirtyEffects, activeEffect, runEffect } from "./effect.js";
4
+ import { batchedHandlers, batchDepth } from "./batch.js";
5
+ import { effect, activeEffect, runEffect } from "./effect.js";
6
+ import { Reactive } from "./reactive.js";
6
7
  let activeComputed;
7
- class Computed {
8
+ class Computed extends Reactive {
8
9
  constructor(callback) {
9
- __publicField(this, "state");
10
- Object.defineProperty(this, "$mora", {
11
- value: "computed"
12
- });
13
- this.state = {
14
- computeds: /* @__PURE__ */ new Set(),
10
+ super("computed", void 0);
11
+ __publicField(this, "effect", {
15
12
  dirty: true,
16
- effect: void 0,
17
- effects: /* @__PURE__ */ new Set(),
18
- value: void 0
19
- };
20
- this.state.effect = effect(() => {
21
- if (this.state.dirty) {
13
+ instance: void 0
14
+ });
15
+ this.effect.instance = effect(() => {
16
+ if (this.effect.dirty) {
22
17
  const previousComputed = activeComputed;
23
18
  activeComputed = this;
24
19
  const value = callback();
@@ -26,13 +21,16 @@ class Computed {
26
21
  if (!Object.is(this.state.value, value)) {
27
22
  this.state.value = value;
28
23
  for (const computed2 of this.state.computeds) {
29
- computed2.state.dirty = true;
24
+ computed2.effect.dirty = true;
30
25
  }
31
26
  for (const effect2 of this.state.effects) {
32
- dirtyEffects.add(effect2);
27
+ batchedHandlers.add(effect2);
28
+ }
29
+ for (const [, subscription] of this.state.subscriptions) {
30
+ subscription.callback(value);
33
31
  }
34
32
  }
35
- this.state.dirty = false;
33
+ this.effect.dirty = false;
36
34
  }
37
35
  });
38
36
  }
@@ -43,20 +41,14 @@ class Computed {
43
41
  if (activeComputed != null && activeComputed !== this) {
44
42
  this.state.computeds.add(activeComputed);
45
43
  }
46
- if (activeEffect != null && activeEffect !== this.state.effect) {
44
+ if (activeEffect != null && activeEffect !== this.effect.instance) {
47
45
  this.state.effects.add(activeEffect);
48
46
  }
49
- if (this.state.dirty && batchDepth === 0) {
50
- runEffect(this.state.effect);
47
+ if (this.effect.dirty && batchDepth === 0) {
48
+ runEffect(this.effect.instance);
51
49
  }
52
50
  return this.state.value;
53
51
  }
54
- /**
55
- * Get the value _(without reactivity)_
56
- */
57
- peek() {
58
- return this.state.value;
59
- }
60
52
  }
61
53
  function computed(callback) {
62
54
  return new Computed(callback);
package/dist/effect.cjs CHANGED
@@ -24,8 +24,6 @@ function effect(callback) {
24
24
  return new Effect(callback);
25
25
  }
26
26
  exports.activeEffect = void 0;
27
- const dirtyEffects = /* @__PURE__ */ new Set();
28
27
  exports.Effect = Effect;
29
- exports.dirtyEffects = dirtyEffects;
30
28
  exports.effect = effect;
31
29
  exports.runEffect = runEffect;
package/dist/effect.js CHANGED
@@ -22,11 +22,9 @@ function effect(callback) {
22
22
  return new Effect(callback);
23
23
  }
24
24
  let activeEffect;
25
- const dirtyEffects = /* @__PURE__ */ new Set();
26
25
  export {
27
26
  Effect,
28
27
  activeEffect,
29
- dirtyEffects,
30
28
  effect,
31
29
  runEffect
32
30
  };
package/dist/is.cjs CHANGED
@@ -9,9 +9,13 @@ function isEffect(value) {
9
9
  function isMora(value, name) {
10
10
  return typeof value === "object" && value != null && "$mora" in value && value.$mora === name;
11
11
  }
12
+ function isReactive(value) {
13
+ return isComputed(value) || isSignal(value);
14
+ }
12
15
  function isSignal(value) {
13
16
  return isMora(value, "signal");
14
17
  }
15
18
  exports.isComputed = isComputed;
16
19
  exports.isEffect = isEffect;
20
+ exports.isReactive = isReactive;
17
21
  exports.isSignal = isSignal;
package/dist/is.js CHANGED
@@ -7,11 +7,15 @@ function isEffect(value) {
7
7
  function isMora(value, name) {
8
8
  return typeof value === "object" && value != null && "$mora" in value && value.$mora === name;
9
9
  }
10
+ function isReactive(value) {
11
+ return isComputed(value) || isSignal(value);
12
+ }
10
13
  function isSignal(value) {
11
14
  return isMora(value, "signal");
12
15
  }
13
16
  export {
14
17
  isComputed,
15
18
  isEffect,
19
+ isReactive,
16
20
  isSignal
17
21
  };
package/dist/mora.full.js CHANGED
@@ -26,14 +26,43 @@ function effect(callback) {
26
26
  return new Effect(callback);
27
27
  }
28
28
  let activeEffect;
29
- const dirtyEffects = new Set();
29
+
30
+ /**
31
+ * Is the value a computed signal?
32
+ */
33
+ function isComputed(value) {
34
+ return isMora(value, 'computed');
35
+ }
36
+ /**
37
+ * Is the value an effect?
38
+ */
39
+ function isEffect(value) {
40
+ return isMora(value, 'effect');
41
+ }
42
+ function isMora(value, name) {
43
+ return (typeof value === 'object' &&
44
+ value != null &&
45
+ '$mora' in value &&
46
+ value.$mora === name);
47
+ }
48
+ /**
49
+ * Is the value a signal?
50
+ */
51
+ function isSignal(value) {
52
+ return isMora(value, 'signal');
53
+ }
30
54
 
31
55
  function flushEffects() {
32
- while (batchDepth === 0 && dirtyEffects.size > 0) {
33
- const effects = [...dirtyEffects];
34
- dirtyEffects.clear();
35
- for (const effect of effects) {
36
- runEffect(effect);
56
+ while (batchDepth === 0 && batchedHandlers.size > 0) {
57
+ const handlers = [...batchedHandlers];
58
+ batchedHandlers.clear();
59
+ for (const handler of handlers) {
60
+ if (isEffect(handler)) {
61
+ runEffect(handler);
62
+ }
63
+ else {
64
+ handler.callback(handler.state.value);
65
+ }
37
66
  }
38
67
  }
39
68
  }
@@ -52,24 +81,80 @@ function stopBatch() {
52
81
  }
53
82
  flushEffects();
54
83
  }
84
+ const batchedHandlers = new Set();
55
85
  let batchDepth = 0;
56
86
 
57
- let activeComputed;
58
- class Computed {
87
+ class Subscription {
59
88
  state;
60
- constructor(callback) {
89
+ callback;
90
+ constructor(state, callback) {
91
+ this.state = state;
92
+ this.callback = callback;
93
+ callback(state.value);
94
+ }
95
+ }
96
+ function noop() { }
97
+ function subscribe(state, callback) {
98
+ if (typeof callback !== 'function' || state.subscriptions.has(callback)) {
99
+ return noop;
100
+ }
101
+ state.subscriptions.set(callback, new Subscription(state, callback));
102
+ return () => {
103
+ unsubscribe(state, callback);
104
+ };
105
+ }
106
+ function unsubscribe(state, callback) {
107
+ const subscription = state.subscriptions.get(callback);
108
+ state.subscriptions.delete(callback);
109
+ if (subscription != null) {
110
+ subscription.callback = noop;
111
+ subscription.state = undefined;
112
+ }
113
+ }
114
+
115
+ class Reactive {
116
+ state = {
117
+ computeds: new Set(),
118
+ effects: new Set(),
119
+ subscriptions: new Map(),
120
+ value: undefined,
121
+ };
122
+ constructor(name, value) {
123
+ this.state.value = value;
61
124
  Object.defineProperty(this, '$mora', {
62
- value: 'computed',
125
+ value: name,
63
126
  });
64
- this.state = {
65
- computeds: new Set(),
66
- dirty: true,
67
- effect: undefined,
68
- effects: new Set(),
69
- value: undefined,
70
- };
71
- this.state.effect = effect(() => {
72
- if (this.state.dirty) {
127
+ }
128
+ /**
129
+ * Get the value _(without reactivity)_
130
+ */
131
+ peek() {
132
+ return this.state.value;
133
+ }
134
+ /**
135
+ * Subscribe to changes
136
+ */
137
+ subscribe(callback) {
138
+ return subscribe(this.state, callback);
139
+ }
140
+ /**
141
+ * Unsubscribe from changes
142
+ */
143
+ unsubscribe(callback) {
144
+ unsubscribe(this.state, callback);
145
+ }
146
+ }
147
+
148
+ let activeComputed;
149
+ class Computed extends Reactive {
150
+ effect = {
151
+ dirty: true,
152
+ instance: undefined,
153
+ };
154
+ constructor(callback) {
155
+ super('computed', undefined);
156
+ this.effect.instance = effect(() => {
157
+ if (this.effect.dirty) {
73
158
  const previousComputed = activeComputed;
74
159
  activeComputed = this;
75
160
  const value = callback();
@@ -77,13 +162,16 @@ class Computed {
77
162
  if (!Object.is(this.state.value, value)) {
78
163
  this.state.value = value;
79
164
  for (const computed of this.state.computeds) {
80
- computed.state.dirty = true;
165
+ computed.effect.dirty = true;
81
166
  }
82
167
  for (const effect of this.state.effects) {
83
- dirtyEffects.add(effect);
168
+ batchedHandlers.add(effect);
169
+ }
170
+ for (const [, subscription] of this.state.subscriptions) {
171
+ subscription.callback(value);
84
172
  }
85
173
  }
86
- this.state.dirty = false;
174
+ this.effect.dirty = false;
87
175
  }
88
176
  });
89
177
  }
@@ -94,20 +182,14 @@ class Computed {
94
182
  if (activeComputed != null && activeComputed !== this) {
95
183
  this.state.computeds.add(activeComputed);
96
184
  }
97
- if (activeEffect != null && activeEffect !== this.state.effect) {
185
+ if (activeEffect != null && activeEffect !== this.effect.instance) {
98
186
  this.state.effects.add(activeEffect);
99
187
  }
100
- if (this.state.dirty && batchDepth === 0) {
101
- runEffect(this.state.effect);
188
+ if (this.effect.dirty && batchDepth === 0) {
189
+ runEffect(this.effect.instance);
102
190
  }
103
191
  return this.state.value;
104
192
  }
105
- /**
106
- * Get the value _(without reactivity)_
107
- */
108
- peek() {
109
- return this.state.value;
110
- }
111
193
  }
112
194
  /**
113
195
  * Create a computed value
@@ -116,42 +198,9 @@ function computed(callback) {
116
198
  return new Computed(callback);
117
199
  }
118
200
 
119
- /**
120
- * Is the value a computed signal?
121
- */
122
- function isComputed(value) {
123
- return isMora(value, 'computed');
124
- }
125
- /**
126
- * Is the value an effect?
127
- */
128
- function isEffect(value) {
129
- return isMora(value, 'effect');
130
- }
131
- function isMora(value, name) {
132
- return (typeof value === 'object' &&
133
- value != null &&
134
- '$mora' in value &&
135
- value.$mora === name);
136
- }
137
- /**
138
- * Is the value a signal?
139
- */
140
- function isSignal(value) {
141
- return isMora(value, 'signal');
142
- }
143
-
144
- class Signal {
145
- state;
201
+ class Signal extends Reactive {
146
202
  constructor(value) {
147
- Object.defineProperty(this, '$mora', {
148
- value: 'signal',
149
- });
150
- this.state = {
151
- value,
152
- computeds: new Set(),
153
- effects: new Set(),
154
- };
203
+ super('signal', value);
155
204
  }
156
205
  /**
157
206
  * Get the value
@@ -165,12 +214,6 @@ class Signal {
165
214
  }
166
215
  return this.state.value;
167
216
  }
168
- /**
169
- * Get the value _(without reactivity)_
170
- */
171
- peek() {
172
- return this.state.value;
173
- }
174
217
  /**
175
218
  * Set the value
176
219
  */
@@ -180,10 +223,13 @@ class Signal {
180
223
  }
181
224
  this.state.value = value;
182
225
  for (const computed of this.state.computeds) {
183
- computed.state.dirty = true;
226
+ computed.effect.dirty = true;
184
227
  }
185
228
  for (const effect of this.state.effects) {
186
- dirtyEffects.add(effect);
229
+ batchedHandlers.add(effect);
230
+ }
231
+ for (const [, subscription] of this.state.subscriptions) {
232
+ batchedHandlers.add(subscription);
187
233
  }
188
234
  if (batchDepth === 0) {
189
235
  flushEffects();
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ const subscription = require("./subscription.cjs");
7
+ class Reactive {
8
+ constructor(name, value) {
9
+ __publicField(this, "state", {
10
+ computeds: /* @__PURE__ */ new Set(),
11
+ effects: /* @__PURE__ */ new Set(),
12
+ subscriptions: /* @__PURE__ */ new Map(),
13
+ value: void 0
14
+ });
15
+ this.state.value = value;
16
+ Object.defineProperty(this, "$mora", {
17
+ value: name
18
+ });
19
+ }
20
+ /**
21
+ * Get the value _(without reactivity)_
22
+ */
23
+ peek() {
24
+ return this.state.value;
25
+ }
26
+ /**
27
+ * Subscribe to changes
28
+ */
29
+ subscribe(callback) {
30
+ return subscription.subscribe(this.state, callback);
31
+ }
32
+ /**
33
+ * Unsubscribe from changes
34
+ */
35
+ unsubscribe(callback) {
36
+ subscription.unsubscribe(this.state, callback);
37
+ }
38
+ }
39
+ exports.Reactive = Reactive;
@@ -0,0 +1,39 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { subscribe, unsubscribe } from "./subscription.js";
5
+ class Reactive {
6
+ constructor(name, value) {
7
+ __publicField(this, "state", {
8
+ computeds: /* @__PURE__ */ new Set(),
9
+ effects: /* @__PURE__ */ new Set(),
10
+ subscriptions: /* @__PURE__ */ new Map(),
11
+ value: void 0
12
+ });
13
+ this.state.value = value;
14
+ Object.defineProperty(this, "$mora", {
15
+ value: name
16
+ });
17
+ }
18
+ /**
19
+ * Get the value _(without reactivity)_
20
+ */
21
+ peek() {
22
+ return this.state.value;
23
+ }
24
+ /**
25
+ * Subscribe to changes
26
+ */
27
+ subscribe(callback) {
28
+ return subscribe(this.state, callback);
29
+ }
30
+ /**
31
+ * Unsubscribe from changes
32
+ */
33
+ unsubscribe(callback) {
34
+ unsubscribe(this.state, callback);
35
+ }
36
+ }
37
+ export {
38
+ Reactive
39
+ };
package/dist/signal.cjs CHANGED
@@ -1,22 +1,12 @@
1
1
  "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
3
  const batch = require("./batch.cjs");
7
4
  const computed = require("./computed.cjs");
8
5
  const effect = require("./effect.cjs");
9
- class Signal {
6
+ const reactive = require("./reactive.cjs");
7
+ class Signal extends reactive.Reactive {
10
8
  constructor(value) {
11
- __publicField(this, "state");
12
- Object.defineProperty(this, "$mora", {
13
- value: "signal"
14
- });
15
- this.state = {
16
- value,
17
- computeds: /* @__PURE__ */ new Set(),
18
- effects: /* @__PURE__ */ new Set()
19
- };
9
+ super("signal", value);
20
10
  }
21
11
  /**
22
12
  * Get the value
@@ -30,12 +20,6 @@ class Signal {
30
20
  }
31
21
  return this.state.value;
32
22
  }
33
- /**
34
- * Get the value _(without reactivity)_
35
- */
36
- peek() {
37
- return this.state.value;
38
- }
39
23
  /**
40
24
  * Set the value
41
25
  */
@@ -45,10 +29,13 @@ class Signal {
45
29
  }
46
30
  this.state.value = value;
47
31
  for (const computed2 of this.state.computeds) {
48
- computed2.state.dirty = true;
32
+ computed2.effect.dirty = true;
33
+ }
34
+ for (const effect2 of this.state.effects) {
35
+ batch.batchedHandlers.add(effect2);
49
36
  }
50
- for (const effect$1 of this.state.effects) {
51
- effect.dirtyEffects.add(effect$1);
37
+ for (const [, subscription] of this.state.subscriptions) {
38
+ batch.batchedHandlers.add(subscription);
52
39
  }
53
40
  if (batch.batchDepth === 0) {
54
41
  batch.flushEffects();