@oscarpalmer/mora 0.9.0 → 0.10.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,6 +5,7 @@ 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 subscription = require("./subscription.cjs");
8
9
  exports.activeComputed = void 0;
9
10
  class Computed {
10
11
  constructor(callback) {
@@ -17,6 +18,7 @@ class Computed {
17
18
  dirty: true,
18
19
  effect: void 0,
19
20
  effects: /* @__PURE__ */ new Set(),
21
+ subscriptions: /* @__PURE__ */ new Map(),
20
22
  value: void 0
21
23
  };
22
24
  this.state.effect = effect.effect(() => {
@@ -31,7 +33,10 @@ class Computed {
31
33
  computed2.state.dirty = true;
32
34
  }
33
35
  for (const effect2 of this.state.effects) {
34
- effect.dirtyEffects.add(effect2);
36
+ batch.batchedHandlers.add(effect2);
37
+ }
38
+ for (const [, subscription2] of this.state.subscriptions) {
39
+ subscription2.callback(value);
35
40
  }
36
41
  }
37
42
  this.state.dirty = false;
@@ -59,6 +64,18 @@ class Computed {
59
64
  peek() {
60
65
  return this.state.value;
61
66
  }
67
+ /**
68
+ * Subscribe to changes
69
+ */
70
+ subscribe(callback) {
71
+ return subscription.subscribe(this.state, callback);
72
+ }
73
+ /**
74
+ * Unsubscribe from changes
75
+ */
76
+ unsubscribe(callback) {
77
+ subscription.unsubscribe(this.state, callback);
78
+ }
62
79
  }
63
80
  function computed(callback) {
64
81
  return new Computed(callback);
package/dist/computed.js CHANGED
@@ -1,8 +1,9 @@
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 { subscribe, unsubscribe } from "./subscription.js";
6
7
  let activeComputed;
7
8
  class Computed {
8
9
  constructor(callback) {
@@ -15,6 +16,7 @@ class Computed {
15
16
  dirty: true,
16
17
  effect: void 0,
17
18
  effects: /* @__PURE__ */ new Set(),
19
+ subscriptions: /* @__PURE__ */ new Map(),
18
20
  value: void 0
19
21
  };
20
22
  this.state.effect = effect(() => {
@@ -29,7 +31,10 @@ class Computed {
29
31
  computed2.state.dirty = true;
30
32
  }
31
33
  for (const effect2 of this.state.effects) {
32
- dirtyEffects.add(effect2);
34
+ batchedHandlers.add(effect2);
35
+ }
36
+ for (const [, subscription] of this.state.subscriptions) {
37
+ subscription.callback(value);
33
38
  }
34
39
  }
35
40
  this.state.dirty = false;
@@ -57,6 +62,18 @@ class Computed {
57
62
  peek() {
58
63
  return this.state.value;
59
64
  }
65
+ /**
66
+ * Subscribe to changes
67
+ */
68
+ subscribe(callback) {
69
+ return subscribe(this.state, callback);
70
+ }
71
+ /**
72
+ * Unsubscribe from changes
73
+ */
74
+ unsubscribe(callback) {
75
+ unsubscribe(this.state, callback);
76
+ }
60
77
  }
61
78
  function computed(callback) {
62
79
  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/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,8 +81,37 @@ function stopBatch() {
52
81
  }
53
82
  flushEffects();
54
83
  }
84
+ const batchedHandlers = new Set();
55
85
  let batchDepth = 0;
56
86
 
87
+ class Subscription {
88
+ state;
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
+
57
115
  let activeComputed;
58
116
  class Computed {
59
117
  state;
@@ -66,6 +124,7 @@ class Computed {
66
124
  dirty: true,
67
125
  effect: undefined,
68
126
  effects: new Set(),
127
+ subscriptions: new Map(),
69
128
  value: undefined,
70
129
  };
71
130
  this.state.effect = effect(() => {
@@ -80,7 +139,10 @@ class Computed {
80
139
  computed.state.dirty = true;
81
140
  }
82
141
  for (const effect of this.state.effects) {
83
- dirtyEffects.add(effect);
142
+ batchedHandlers.add(effect);
143
+ }
144
+ for (const [, subscription] of this.state.subscriptions) {
145
+ subscription.callback(value);
84
146
  }
85
147
  }
86
148
  this.state.dirty = false;
@@ -108,6 +170,18 @@ class Computed {
108
170
  peek() {
109
171
  return this.state.value;
110
172
  }
173
+ /**
174
+ * Subscribe to changes
175
+ */
176
+ subscribe(callback) {
177
+ return subscribe(this.state, callback);
178
+ }
179
+ /**
180
+ * Unsubscribe from changes
181
+ */
182
+ unsubscribe(callback) {
183
+ unsubscribe(this.state, callback);
184
+ }
111
185
  }
112
186
  /**
113
187
  * Create a computed value
@@ -116,31 +190,6 @@ function computed(callback) {
116
190
  return new Computed(callback);
117
191
  }
118
192
 
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
193
  class Signal {
145
194
  state;
146
195
  constructor(value) {
@@ -151,6 +200,7 @@ class Signal {
151
200
  value,
152
201
  computeds: new Set(),
153
202
  effects: new Set(),
203
+ subscriptions: new Map(),
154
204
  };
155
205
  }
156
206
  /**
@@ -183,12 +233,27 @@ class Signal {
183
233
  computed.state.dirty = true;
184
234
  }
185
235
  for (const effect of this.state.effects) {
186
- dirtyEffects.add(effect);
236
+ batchedHandlers.add(effect);
237
+ }
238
+ for (const [, subscription] of this.state.subscriptions) {
239
+ batchedHandlers.add(subscription);
187
240
  }
188
241
  if (batchDepth === 0) {
189
242
  flushEffects();
190
243
  }
191
244
  }
245
+ /**
246
+ * Subscribe to changes
247
+ */
248
+ subscribe(callback) {
249
+ return subscribe(this.state, callback);
250
+ }
251
+ /**
252
+ * Unsubscribe from changes
253
+ */
254
+ unsubscribe(callback) {
255
+ unsubscribe(this.state, callback);
256
+ }
192
257
  /**
193
258
  * Update the value
194
259
  */
package/dist/signal.cjs CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
6
  const batch = require("./batch.cjs");
7
7
  const computed = require("./computed.cjs");
8
8
  const effect = require("./effect.cjs");
9
+ const subscription = require("./subscription.cjs");
9
10
  class Signal {
10
11
  constructor(value) {
11
12
  __publicField(this, "state");
@@ -15,7 +16,8 @@ class Signal {
15
16
  this.state = {
16
17
  value,
17
18
  computeds: /* @__PURE__ */ new Set(),
18
- effects: /* @__PURE__ */ new Set()
19
+ effects: /* @__PURE__ */ new Set(),
20
+ subscriptions: /* @__PURE__ */ new Map()
19
21
  };
20
22
  }
21
23
  /**
@@ -47,13 +49,28 @@ class Signal {
47
49
  for (const computed2 of this.state.computeds) {
48
50
  computed2.state.dirty = true;
49
51
  }
50
- for (const effect$1 of this.state.effects) {
51
- effect.dirtyEffects.add(effect$1);
52
+ for (const effect2 of this.state.effects) {
53
+ batch.batchedHandlers.add(effect2);
54
+ }
55
+ for (const [, subscription2] of this.state.subscriptions) {
56
+ batch.batchedHandlers.add(subscription2);
52
57
  }
53
58
  if (batch.batchDepth === 0) {
54
59
  batch.flushEffects();
55
60
  }
56
61
  }
62
+ /**
63
+ * Subscribe to changes
64
+ */
65
+ subscribe(callback) {
66
+ return subscription.subscribe(this.state, callback);
67
+ }
68
+ /**
69
+ * Unsubscribe from changes
70
+ */
71
+ unsubscribe(callback) {
72
+ subscription.unsubscribe(this.state, callback);
73
+ }
57
74
  /**
58
75
  * Update the value
59
76
  */
package/dist/signal.js CHANGED
@@ -1,9 +1,10 @@
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 { flushEffects, batchDepth } from "./batch.js";
4
+ import { batchedHandlers, flushEffects, batchDepth } from "./batch.js";
5
5
  import { activeComputed } from "./computed.js";
6
- import { activeEffect, dirtyEffects } from "./effect.js";
6
+ import { activeEffect } from "./effect.js";
7
+ import { subscribe, unsubscribe } from "./subscription.js";
7
8
  class Signal {
8
9
  constructor(value) {
9
10
  __publicField(this, "state");
@@ -13,7 +14,8 @@ class Signal {
13
14
  this.state = {
14
15
  value,
15
16
  computeds: /* @__PURE__ */ new Set(),
16
- effects: /* @__PURE__ */ new Set()
17
+ effects: /* @__PURE__ */ new Set(),
18
+ subscriptions: /* @__PURE__ */ new Map()
17
19
  };
18
20
  }
19
21
  /**
@@ -46,12 +48,27 @@ class Signal {
46
48
  computed.state.dirty = true;
47
49
  }
48
50
  for (const effect of this.state.effects) {
49
- dirtyEffects.add(effect);
51
+ batchedHandlers.add(effect);
52
+ }
53
+ for (const [, subscription] of this.state.subscriptions) {
54
+ batchedHandlers.add(subscription);
50
55
  }
51
56
  if (batchDepth === 0) {
52
57
  flushEffects();
53
58
  }
54
59
  }
60
+ /**
61
+ * Subscribe to changes
62
+ */
63
+ subscribe(callback) {
64
+ return subscribe(this.state, callback);
65
+ }
66
+ /**
67
+ * Unsubscribe from changes
68
+ */
69
+ unsubscribe(callback) {
70
+ unsubscribe(this.state, callback);
71
+ }
55
72
  /**
56
73
  * Update the value
57
74
  */
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class Subscription {
4
+ constructor(state, callback) {
5
+ this.state = state;
6
+ this.callback = callback;
7
+ callback(state.value);
8
+ }
9
+ }
10
+ function noop() {
11
+ }
12
+ function subscribe(state, callback) {
13
+ if (typeof callback !== "function" || state.subscriptions.has(callback)) {
14
+ return noop;
15
+ }
16
+ state.subscriptions.set(callback, new Subscription(state, callback));
17
+ return () => {
18
+ unsubscribe(state, callback);
19
+ };
20
+ }
21
+ function unsubscribe(state, callback) {
22
+ const subscription = state.subscriptions.get(callback);
23
+ state.subscriptions.delete(callback);
24
+ if (subscription != null) {
25
+ subscription.callback = noop;
26
+ subscription.state = void 0;
27
+ }
28
+ }
29
+ exports.Subscription = Subscription;
30
+ exports.noop = noop;
31
+ exports.subscribe = subscribe;
32
+ exports.unsubscribe = unsubscribe;
@@ -0,0 +1,32 @@
1
+ class Subscription {
2
+ constructor(state, callback) {
3
+ this.state = state;
4
+ this.callback = callback;
5
+ callback(state.value);
6
+ }
7
+ }
8
+ function noop() {
9
+ }
10
+ function subscribe(state, callback) {
11
+ if (typeof callback !== "function" || state.subscriptions.has(callback)) {
12
+ return noop;
13
+ }
14
+ state.subscriptions.set(callback, new Subscription(state, callback));
15
+ return () => {
16
+ unsubscribe(state, callback);
17
+ };
18
+ }
19
+ function unsubscribe(state, callback) {
20
+ const subscription = state.subscriptions.get(callback);
21
+ state.subscriptions.delete(callback);
22
+ if (subscription != null) {
23
+ subscription.callback = noop;
24
+ subscription.state = void 0;
25
+ }
26
+ }
27
+ export {
28
+ Subscription,
29
+ noop,
30
+ subscribe,
31
+ unsubscribe
32
+ };
package/package.json CHANGED
@@ -54,5 +54,5 @@
54
54
  },
55
55
  "type": "module",
56
56
  "types": "./types/index.d.ts",
57
- "version": "0.9.0"
57
+ "version": "0.10.0"
58
58
  }
package/src/batch.ts CHANGED
@@ -1,13 +1,19 @@
1
- import {dirtyEffects, runEffect} from './effect';
1
+ import {type Effect, runEffect} from './effect';
2
+ import {isEffect} from './is';
3
+ import type {Subscription} from './subscription';
2
4
 
3
5
  export function flushEffects(): void {
4
- while (batchDepth === 0 && dirtyEffects.size > 0) {
5
- const effects = [...dirtyEffects];
6
+ while (batchDepth === 0 && batchedHandlers.size > 0) {
7
+ const handlers = [...batchedHandlers];
6
8
 
7
- dirtyEffects.clear();
9
+ batchedHandlers.clear();
8
10
 
9
- for (const effect of effects) {
10
- runEffect(effect);
11
+ for (const handler of handlers) {
12
+ if (isEffect(handler)) {
13
+ runEffect(handler);
14
+ } else {
15
+ handler.callback(handler.state.value);
16
+ }
11
17
  }
12
18
  }
13
19
  }
@@ -30,4 +36,6 @@ export function stopBatch(): void {
30
36
  flushEffects();
31
37
  }
32
38
 
39
+ export const batchedHandlers = new Set<Effect | Subscription>();
40
+
33
41
  export let batchDepth = 0;