@oscarpalmer/mora 0.4.0 → 0.6.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 ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const effect = require("./effect.cjs");
4
+ 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);
10
+ }
11
+ }
12
+ }
13
+ function startBatch() {
14
+ exports.batchDepth += 1;
15
+ }
16
+ function stopBatch() {
17
+ if (exports.batchDepth > 0) {
18
+ exports.batchDepth -= 1;
19
+ }
20
+ flushEffects();
21
+ }
22
+ exports.batchDepth = 0;
23
+ exports.flushEffects = flushEffects;
24
+ exports.startBatch = startBatch;
25
+ exports.stopBatch = stopBatch;
package/dist/batch.js ADDED
@@ -0,0 +1,26 @@
1
+ import { dirtyEffects, runEffect } from "./effect.js";
2
+ 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);
8
+ }
9
+ }
10
+ }
11
+ function startBatch() {
12
+ batchDepth += 1;
13
+ }
14
+ function stopBatch() {
15
+ if (batchDepth > 0) {
16
+ batchDepth -= 1;
17
+ }
18
+ flushEffects();
19
+ }
20
+ let batchDepth = 0;
21
+ export {
22
+ batchDepth,
23
+ flushEffects,
24
+ startBatch,
25
+ stopBatch
26
+ };
package/dist/computed.cjs CHANGED
@@ -3,15 +3,15 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ const batch = require("./batch.cjs");
6
7
  const effect = require("./effect.cjs");
7
- const signal = require("./signal.cjs");
8
8
  exports.activeComputed = void 0;
9
9
  class Computed {
10
10
  constructor(callback) {
11
11
  __publicField(this, "state");
12
12
  this.state = {
13
- dirty: true,
14
13
  computeds: /* @__PURE__ */ new Set(),
14
+ dirty: true,
15
15
  effect: void 0,
16
16
  effects: /* @__PURE__ */ new Set(),
17
17
  value: void 0
@@ -26,16 +26,19 @@ class Computed {
26
26
  this.state.value = value;
27
27
  for (const comp of this.state.computeds) {
28
28
  comp.state.dirty = true;
29
- signal.dirtyEffects.add(comp.state.effect);
29
+ effect.dirtyEffects.add(comp.state.effect);
30
30
  }
31
31
  for (const effect2 of this.state.effects) {
32
- signal.dirtyEffects.add(effect2);
32
+ effect.dirtyEffects.add(effect2);
33
33
  }
34
34
  }
35
35
  this.state.dirty = false;
36
36
  }
37
37
  });
38
38
  }
39
+ /**
40
+ * Get the value
41
+ */
39
42
  get() {
40
43
  if (exports.activeComputed != null && exports.activeComputed !== this) {
41
44
  this.state.computeds.add(exports.activeComputed);
@@ -44,7 +47,11 @@ class Computed {
44
47
  this.state.effects.add(effect.activeEffect);
45
48
  }
46
49
  if (this.state.dirty) {
47
- effect.runEffect(this.state.effect);
50
+ if (batch.batchDepth === 0) {
51
+ effect.runEffect(this.state.effect);
52
+ } else {
53
+ effect.dirtyEffects.add(this.state.effect);
54
+ }
48
55
  }
49
56
  return this.state.value;
50
57
  }
package/dist/computed.js CHANGED
@@ -1,15 +1,15 @@
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 { effect, activeEffect, runEffect } from "./effect.js";
5
- import { dirtyEffects } from "./signal.js";
4
+ import { batchDepth } from "./batch.js";
5
+ import { effect, dirtyEffects, activeEffect, runEffect } from "./effect.js";
6
6
  let activeComputed;
7
7
  class Computed {
8
8
  constructor(callback) {
9
9
  __publicField(this, "state");
10
10
  this.state = {
11
- dirty: true,
12
11
  computeds: /* @__PURE__ */ new Set(),
12
+ dirty: true,
13
13
  effect: void 0,
14
14
  effects: /* @__PURE__ */ new Set(),
15
15
  value: void 0
@@ -34,6 +34,9 @@ class Computed {
34
34
  }
35
35
  });
36
36
  }
37
+ /**
38
+ * Get the value
39
+ */
37
40
  get() {
38
41
  if (activeComputed != null && activeComputed !== this) {
39
42
  this.state.computeds.add(activeComputed);
@@ -42,7 +45,11 @@ class Computed {
42
45
  this.state.effects.add(activeEffect);
43
46
  }
44
47
  if (this.state.dirty) {
45
- runEffect(this.state.effect);
48
+ if (batchDepth === 0) {
49
+ runEffect(this.state.effect);
50
+ } else {
51
+ dirtyEffects.add(this.state.effect);
52
+ }
46
53
  }
47
54
  return this.state.value;
48
55
  }
package/dist/effect.cjs CHANGED
@@ -3,7 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
- exports.activeEffect = void 0;
7
6
  class Effect {
8
7
  constructor(callback) {
9
8
  __publicField(this, "state");
@@ -25,6 +24,9 @@ function runEffect(effect2) {
25
24
  function effect(callback) {
26
25
  return new Effect(callback);
27
26
  }
27
+ exports.activeEffect = void 0;
28
+ const dirtyEffects = /* @__PURE__ */ new Set();
28
29
  exports.Effect = Effect;
30
+ exports.dirtyEffects = dirtyEffects;
29
31
  exports.effect = effect;
30
32
  exports.runEffect = runEffect;
package/dist/effect.js CHANGED
@@ -1,7 +1,6 @@
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
- let activeEffect;
5
4
  class Effect {
6
5
  constructor(callback) {
7
6
  __publicField(this, "state");
@@ -23,9 +22,12 @@ function runEffect(effect2) {
23
22
  function effect(callback) {
24
23
  return new Effect(callback);
25
24
  }
25
+ let activeEffect;
26
+ const dirtyEffects = /* @__PURE__ */ new Set();
26
27
  export {
27
28
  Effect,
28
29
  activeEffect,
30
+ dirtyEffects,
29
31
  effect,
30
32
  runEffect
31
33
  };
package/dist/index.cjs CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const batch = require("./batch.cjs");
3
4
  const computed = require("./computed.cjs");
4
5
  const effect = require("./effect.cjs");
5
6
  const signal = require("./signal.cjs");
7
+ exports.startBatch = batch.startBatch;
8
+ exports.stopBatch = batch.stopBatch;
6
9
  exports.computed = computed.computed;
7
10
  exports.effect = effect.effect;
8
11
  exports.signal = signal.signal;
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
+ import { startBatch, stopBatch } from "./batch.js";
1
2
  import { computed } from "./computed.js";
2
3
  import { effect } from "./effect.js";
3
4
  import { signal } from "./signal.js";
4
5
  export {
5
6
  computed,
6
7
  effect,
7
- signal
8
+ signal,
9
+ startBatch,
10
+ stopBatch
8
11
  };
@@ -0,0 +1,165 @@
1
+ class Effect {
2
+ state;
3
+ constructor(callback) {
4
+ this.state = {
5
+ callback,
6
+ };
7
+ runEffect(this);
8
+ }
9
+ }
10
+ function runEffect(effect) {
11
+ const previousEffect = activeEffect;
12
+ activeEffect = effect;
13
+ try {
14
+ effect.state.callback();
15
+ }
16
+ finally {
17
+ activeEffect = previousEffect;
18
+ }
19
+ }
20
+ /**
21
+ * Create an effect
22
+ */
23
+ function effect(callback) {
24
+ return new Effect(callback);
25
+ }
26
+ let activeEffect;
27
+ const dirtyEffects = new Set();
28
+
29
+ function flushEffects() {
30
+ while (batchDepth === 0 && dirtyEffects.size > 0) {
31
+ const effects = [...dirtyEffects];
32
+ dirtyEffects.clear();
33
+ for (const effect of effects) {
34
+ runEffect(effect);
35
+ }
36
+ }
37
+ }
38
+ /**
39
+ * Start batching effects _(use `stopBatch` to flush and run batched effects)_
40
+ */
41
+ function startBatch() {
42
+ batchDepth += 1;
43
+ }
44
+ /**
45
+ * Stop batching effects and flush _(run)_ them
46
+ */
47
+ function stopBatch() {
48
+ if (batchDepth > 0) {
49
+ batchDepth -= 1;
50
+ }
51
+ flushEffects();
52
+ }
53
+ let batchDepth = 0;
54
+
55
+ let activeComputed;
56
+ class Computed {
57
+ state;
58
+ constructor(callback) {
59
+ this.state = {
60
+ computeds: new Set(),
61
+ dirty: true,
62
+ effect: undefined,
63
+ effects: new Set(),
64
+ value: undefined,
65
+ };
66
+ this.state.effect = effect(() => {
67
+ if (this.state.dirty) {
68
+ const previousComputed = activeComputed;
69
+ activeComputed = this;
70
+ const value = callback();
71
+ activeComputed = previousComputed;
72
+ if (!Object.is(this.state.value, value)) {
73
+ this.state.value = value;
74
+ for (const comp of this.state.computeds) {
75
+ comp.state.dirty = true;
76
+ dirtyEffects.add(comp.state.effect);
77
+ }
78
+ for (const effect of this.state.effects) {
79
+ dirtyEffects.add(effect);
80
+ }
81
+ }
82
+ this.state.dirty = false;
83
+ }
84
+ });
85
+ }
86
+ /**
87
+ * Get the value
88
+ */
89
+ get() {
90
+ if (activeComputed != null && activeComputed !== this) {
91
+ this.state.computeds.add(activeComputed);
92
+ }
93
+ if (activeEffect != null && activeEffect !== this.state.effect) {
94
+ this.state.effects.add(activeEffect);
95
+ }
96
+ if (this.state.dirty) {
97
+ if (batchDepth === 0) {
98
+ runEffect(this.state.effect);
99
+ }
100
+ else {
101
+ dirtyEffects.add(this.state.effect);
102
+ }
103
+ }
104
+ return this.state.value;
105
+ }
106
+ }
107
+ /**
108
+ * Create a computed value
109
+ */
110
+ function computed(callback) {
111
+ return new Computed(callback);
112
+ }
113
+
114
+ class Signal {
115
+ state;
116
+ constructor(value) {
117
+ this.state = {
118
+ value,
119
+ computeds: new Set(),
120
+ effects: new Set(),
121
+ };
122
+ }
123
+ /**
124
+ * Get the value
125
+ */
126
+ get() {
127
+ if (activeComputed != null) {
128
+ this.state.computeds.add(activeComputed);
129
+ }
130
+ if (activeEffect != null) {
131
+ this.state.effects.add(activeEffect);
132
+ }
133
+ return this.state.value;
134
+ }
135
+ /**
136
+ * Set the value
137
+ */
138
+ set(value) {
139
+ if (Object.is(this.state.value, value)) {
140
+ return;
141
+ }
142
+ this.state.value = value;
143
+ for (const comp of this.state.computeds) {
144
+ comp.state.dirty = true;
145
+ dirtyEffects.add(comp.state.effect);
146
+ }
147
+ for (const effect of this.state.effects) {
148
+ dirtyEffects.add(effect);
149
+ }
150
+ if (batchDepth === 0) {
151
+ flushEffects();
152
+ }
153
+ }
154
+ /**
155
+ * Update the value
156
+ */
157
+ update(callback) {
158
+ this.set(callback(this.state.value));
159
+ }
160
+ }
161
+ function signal(value) {
162
+ return new Signal(value);
163
+ }
164
+
165
+ export { computed, effect, signal, startBatch, stopBatch };
package/dist/signal.cjs CHANGED
@@ -3,9 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ const batch = require("./batch.cjs");
6
7
  const computed = require("./computed.cjs");
7
8
  const effect = require("./effect.cjs");
8
- const dirtyEffects = /* @__PURE__ */ new Set();
9
9
  class Signal {
10
10
  constructor(value) {
11
11
  __publicField(this, "state");
@@ -15,6 +15,9 @@ class Signal {
15
15
  effects: /* @__PURE__ */ new Set()
16
16
  };
17
17
  }
18
+ /**
19
+ * Get the value
20
+ */
18
21
  get() {
19
22
  if (computed.activeComputed != null) {
20
23
  this.state.computeds.add(computed.activeComputed);
@@ -24,26 +27,28 @@ class Signal {
24
27
  }
25
28
  return this.state.value;
26
29
  }
30
+ /**
31
+ * Set the value
32
+ */
27
33
  set(value) {
28
34
  if (Object.is(this.state.value, value)) {
29
35
  return;
30
36
  }
31
37
  this.state.value = value;
32
- for (const computed2 of this.state.computeds) {
33
- computed2.state.dirty = true;
34
- dirtyEffects.add(computed2.state.effect);
38
+ for (const comp of this.state.computeds) {
39
+ comp.state.dirty = true;
40
+ effect.dirtyEffects.add(comp.state.effect);
35
41
  }
36
- for (const effect2 of this.state.effects) {
37
- dirtyEffects.add(effect2);
42
+ for (const effect$1 of this.state.effects) {
43
+ effect.dirtyEffects.add(effect$1);
38
44
  }
39
- while (dirtyEffects.size > 0) {
40
- const effects = [...dirtyEffects];
41
- dirtyEffects.clear();
42
- for (const effect$1 of effects) {
43
- effect.runEffect(effect$1);
44
- }
45
+ if (batch.batchDepth === 0) {
46
+ batch.flushEffects();
45
47
  }
46
48
  }
49
+ /**
50
+ * Update the value
51
+ */
47
52
  update(callback) {
48
53
  this.set(callback(this.state.value));
49
54
  }
@@ -52,5 +57,4 @@ function signal(value) {
52
57
  return new Signal(value);
53
58
  }
54
59
  exports.Signal = Signal;
55
- exports.dirtyEffects = dirtyEffects;
56
60
  exports.signal = signal;
package/dist/signal.js CHANGED
@@ -1,9 +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 { flushEffects, batchDepth } from "./batch.js";
4
5
  import { activeComputed } from "./computed.js";
5
- import { activeEffect, runEffect } from "./effect.js";
6
- const dirtyEffects = /* @__PURE__ */ new Set();
6
+ import { activeEffect, dirtyEffects } from "./effect.js";
7
7
  class Signal {
8
8
  constructor(value) {
9
9
  __publicField(this, "state");
@@ -13,6 +13,9 @@ class Signal {
13
13
  effects: /* @__PURE__ */ new Set()
14
14
  };
15
15
  }
16
+ /**
17
+ * Get the value
18
+ */
16
19
  get() {
17
20
  if (activeComputed != null) {
18
21
  this.state.computeds.add(activeComputed);
@@ -22,26 +25,28 @@ class Signal {
22
25
  }
23
26
  return this.state.value;
24
27
  }
28
+ /**
29
+ * Set the value
30
+ */
25
31
  set(value) {
26
32
  if (Object.is(this.state.value, value)) {
27
33
  return;
28
34
  }
29
35
  this.state.value = value;
30
- for (const computed of this.state.computeds) {
31
- computed.state.dirty = true;
32
- dirtyEffects.add(computed.state.effect);
36
+ for (const comp of this.state.computeds) {
37
+ comp.state.dirty = true;
38
+ dirtyEffects.add(comp.state.effect);
33
39
  }
34
40
  for (const effect of this.state.effects) {
35
41
  dirtyEffects.add(effect);
36
42
  }
37
- while (dirtyEffects.size > 0) {
38
- const effects = [...dirtyEffects];
39
- dirtyEffects.clear();
40
- for (const effect of effects) {
41
- runEffect(effect);
42
- }
43
+ if (batchDepth === 0) {
44
+ flushEffects();
43
45
  }
44
46
  }
47
+ /**
48
+ * Update the value
49
+ */
45
50
  update(callback) {
46
51
  this.set(callback(this.state.value));
47
52
  }
@@ -51,6 +56,5 @@ function signal(value) {
51
56
  }
52
57
  export {
53
58
  Signal,
54
- dirtyEffects,
55
59
  signal
56
60
  };
package/package.json CHANGED
@@ -6,18 +6,18 @@
6
6
  "description": "Signals and stuff…",
7
7
  "devDependencies": {
8
8
  "@biomejs/biome": "^1.9",
9
- "@oscarpalmer/atoms": "^0.82",
9
+ "@oscarpalmer/atoms": "^0.100",
10
10
  "@rollup/plugin-node-resolve": "^16",
11
11
  "@rollup/plugin-typescript": "^12.1",
12
- "@types/node": "^22.13",
13
- "@vitest/coverage-istanbul": "^3",
12
+ "@types/node": "^22.15",
13
+ "@vitest/coverage-istanbul": "^3.1",
14
14
  "dts-bundle-generator": "^9.5",
15
15
  "glob": "^11",
16
- "happy-dom": "^17",
16
+ "jsdom": "^26.1",
17
17
  "tslib": "^2.8",
18
- "typescript": "^5.7",
19
- "vite": "^6",
20
- "vitest": "^3"
18
+ "typescript": "^5.8",
19
+ "vite": "^6.3",
20
+ "vitest": "^3.1"
21
21
  },
22
22
  "exports": {
23
23
  ".": {
@@ -31,16 +31,8 @@
31
31
  }
32
32
  }
33
33
  },
34
- "files": [
35
- "dist",
36
- "src",
37
- "types"
38
- ],
39
- "keywords": [
40
- "signal",
41
- "signals",
42
- "reactive"
43
- ],
34
+ "files": ["dist", "src", "types"],
35
+ "keywords": ["signal", "signals", "reactive"],
44
36
  "license": "MIT",
45
37
  "main": "./dist/index.cjs",
46
38
  "module": "./dist/index.js",
@@ -55,10 +47,10 @@
55
47
  "clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo",
56
48
  "rollup": "npx rollup -c",
57
49
  "test": "npx vitest --coverage",
58
- "types": "npx tsc && npx dts-bundle-generator --config ./dts.config.cts",
50
+ "types": "npx tsc && npx dts-bundle-generator --config ./dts.config.cts --silent",
59
51
  "watch": "npx vite build --watch"
60
52
  },
61
53
  "type": "module",
62
54
  "types": "./types/index.d.ts",
63
- "version": "0.4.0"
55
+ "version": "0.6.0"
64
56
  }
package/src/batch.ts ADDED
@@ -0,0 +1,33 @@
1
+ import {dirtyEffects, runEffect} from './effect';
2
+
3
+ export function flushEffects(): void {
4
+ while (batchDepth === 0 && dirtyEffects.size > 0) {
5
+ const effects = [...dirtyEffects];
6
+
7
+ dirtyEffects.clear();
8
+
9
+ for (const effect of effects) {
10
+ runEffect(effect);
11
+ }
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Start batching effects _(use `stopBatch` to flush and run batched effects)_
17
+ */
18
+ export function startBatch(): void {
19
+ batchDepth += 1;
20
+ }
21
+
22
+ /**
23
+ * Stop batching effects and flush _(run)_ them
24
+ */
25
+ export function stopBatch(): void {
26
+ if (batchDepth > 0) {
27
+ batchDepth -= 1;
28
+ }
29
+
30
+ flushEffects();
31
+ }
32
+
33
+ export let batchDepth = 0;
package/src/computed.ts CHANGED
@@ -1,23 +1,33 @@
1
- import {type Effect, activeEffect, effect, runEffect} from './effect';
2
- import {dirtyEffects} from './signal';
1
+ import {batchDepth} from './batch';
2
+ import {
3
+ type Effect,
4
+ activeEffect,
5
+ dirtyEffects,
6
+ effect,
7
+ runEffect,
8
+ } from './effect';
3
9
 
4
10
  type ComputedState<Value> = {
5
- dirty: boolean;
6
11
  computeds: Set<Computed<unknown>>;
12
+ dirty: boolean;
7
13
  effect: Effect;
8
14
  effects: Set<Effect>;
9
15
  value: Value;
10
16
  };
11
17
 
18
+ export type InternalComputed = {
19
+ readonly state: ComputedState<unknown>;
20
+ };
21
+
12
22
  export let activeComputed: Computed<unknown> | undefined;
13
23
 
14
24
  export class Computed<Value> {
15
- readonly state: ComputedState<Value>;
25
+ private readonly state: ComputedState<Value>;
16
26
 
17
27
  constructor(callback: () => Value) {
18
28
  this.state = {
19
- dirty: true,
20
29
  computeds: new Set(),
30
+ dirty: true,
21
31
  effect: undefined as never,
22
32
  effects: new Set(),
23
33
  value: undefined as never,
@@ -52,6 +62,9 @@ export class Computed<Value> {
52
62
  });
53
63
  }
54
64
 
65
+ /**
66
+ * Get the value
67
+ */
55
68
  get(): Value {
56
69
  if (activeComputed != null && activeComputed !== this) {
57
70
  this.state.computeds.add(activeComputed);
@@ -62,13 +75,20 @@ export class Computed<Value> {
62
75
  }
63
76
 
64
77
  if (this.state.dirty) {
65
- runEffect(this.state.effect);
78
+ if (batchDepth === 0) {
79
+ runEffect(this.state.effect);
80
+ } else {
81
+ dirtyEffects.add(this.state.effect);
82
+ }
66
83
  }
67
84
 
68
85
  return this.state.value;
69
86
  }
70
87
  }
71
88
 
89
+ /**
90
+ * Create a computed value
91
+ */
72
92
  export function computed<Value>(callback: () => Value): Computed<Value> {
73
93
  return new Computed(callback);
74
94
  }
package/src/effect.ts CHANGED
@@ -4,10 +4,12 @@ type EffectState = {
4
4
  callback: GenericCallback;
5
5
  };
6
6
 
7
- export let activeEffect: Effect | undefined;
7
+ type InternalEffect = {
8
+ state: EffectState;
9
+ };
8
10
 
9
11
  export class Effect {
10
- readonly state: EffectState;
12
+ private readonly state: EffectState;
11
13
 
12
14
  constructor(callback: GenericCallback) {
13
15
  this.state = {
@@ -24,12 +26,19 @@ export function runEffect(effect: Effect): void {
24
26
  activeEffect = effect;
25
27
 
26
28
  try {
27
- effect.state.callback();
29
+ (effect as unknown as InternalEffect).state.callback();
28
30
  } finally {
29
31
  activeEffect = previousEffect;
30
32
  }
31
33
  }
32
34
 
35
+ /**
36
+ * Create an effect
37
+ */
33
38
  export function effect(callback: GenericCallback): Effect {
34
39
  return new Effect(callback);
35
40
  }
41
+
42
+ export let activeEffect: Effect | undefined;
43
+
44
+ export const dirtyEffects = new Set<Effect>();
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export {startBatch, stopBatch} from './batch';
1
2
  export {computed, type Computed} from './computed';
2
3
  export {effect, type Effect} from './effect';
3
4
  export {signal, type Signal} from './signal';
package/src/signal.ts CHANGED
@@ -1,5 +1,6 @@
1
- import {type Computed, activeComputed} from './computed';
2
- import {type Effect, activeEffect, runEffect} from './effect';
1
+ import {batchDepth, flushEffects} from './batch';
2
+ import {type Computed, type InternalComputed, activeComputed} from './computed';
3
+ import {type Effect, activeEffect, dirtyEffects} from './effect';
3
4
 
4
5
  type SignalState<Value> = {
5
6
  computeds: Set<Computed<unknown>>;
@@ -7,10 +8,8 @@ type SignalState<Value> = {
7
8
  value: Value;
8
9
  };
9
10
 
10
- export const dirtyEffects = new Set<Effect>();
11
-
12
11
  export class Signal<Value> {
13
- readonly state: SignalState<Value>;
12
+ private readonly state: SignalState<Value>;
14
13
 
15
14
  constructor(value: Value) {
16
15
  this.state = {
@@ -20,6 +19,9 @@ export class Signal<Value> {
20
19
  };
21
20
  }
22
21
 
22
+ /**
23
+ * Get the value
24
+ */
23
25
  get(): Value {
24
26
  if (activeComputed != null) {
25
27
  this.state.computeds.add(activeComputed);
@@ -32,6 +34,9 @@ export class Signal<Value> {
32
34
  return this.state.value;
33
35
  }
34
36
 
37
+ /**
38
+ * Set the value
39
+ */
35
40
  set(value: Value): void {
36
41
  if (Object.is(this.state.value, value)) {
37
42
  return;
@@ -39,27 +44,24 @@ export class Signal<Value> {
39
44
 
40
45
  this.state.value = value;
41
46
 
42
- for (const computed of this.state.computeds) {
43
- computed.state.dirty = true;
47
+ for (const comp of this.state.computeds) {
48
+ (comp as unknown as InternalComputed).state.dirty = true;
44
49
 
45
- dirtyEffects.add(computed.state.effect);
50
+ dirtyEffects.add((comp as unknown as InternalComputed).state.effect);
46
51
  }
47
52
 
48
53
  for (const effect of this.state.effects) {
49
54
  dirtyEffects.add(effect);
50
55
  }
51
56
 
52
- while (dirtyEffects.size > 0) {
53
- const effects = [...dirtyEffects];
54
-
55
- dirtyEffects.clear();
56
-
57
- for (const effect of effects) {
58
- runEffect(effect);
59
- }
57
+ if (batchDepth === 0) {
58
+ flushEffects();
60
59
  }
61
60
  }
62
61
 
62
+ /**
63
+ * Update the value
64
+ */
63
65
  update(callback: (value: Value) => Value): void {
64
66
  this.set(callback(this.state.value));
65
67
  }
@@ -0,0 +1,14 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ export declare function flushEffects(): void;
4
+ /**
5
+ * Start batching effects _(use `stopBatch` to flush and run batched effects)_
6
+ */
7
+ export declare function startBatch(): void;
8
+ /**
9
+ * Stop batching effects and flush _(run)_ them
10
+ */
11
+ export declare function stopBatch(): void;
12
+ export declare let batchDepth: number;
13
+
14
+ export {};
@@ -0,0 +1,10 @@
1
+ export declare function flushEffects(): void;
2
+ /**
3
+ * Start batching effects _(use `stopBatch` to flush and run batched effects)_
4
+ */
5
+ export declare function startBatch(): void;
6
+ /**
7
+ * Stop batching effects and flush _(run)_ them
8
+ */
9
+ export declare function stopBatch(): void;
10
+ export declare let batchDepth: number;
@@ -1,26 +1,32 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
3
  export type GenericCallback = (...args: any[]) => any;
4
- export type EffectState = {
5
- callback: GenericCallback;
6
- };
7
4
  declare class Effect {
8
- readonly state: EffectState;
5
+ private readonly state;
9
6
  constructor(callback: GenericCallback);
10
7
  }
11
8
  export type ComputedState<Value> = {
12
- dirty: boolean;
13
9
  computeds: Set<Computed<unknown>>;
10
+ dirty: boolean;
14
11
  effect: Effect;
15
12
  effects: Set<Effect>;
16
13
  value: Value;
17
14
  };
15
+ export type InternalComputed = {
16
+ readonly state: ComputedState<unknown>;
17
+ };
18
18
  export declare let activeComputed: Computed<unknown> | undefined;
19
19
  export declare class Computed<Value> {
20
- readonly state: ComputedState<Value>;
20
+ private readonly state;
21
21
  constructor(callback: () => Value);
22
+ /**
23
+ * Get the value
24
+ */
22
25
  get(): Value;
23
26
  }
27
+ /**
28
+ * Create a computed value
29
+ */
24
30
  export declare function computed<Value>(callback: () => Value): Computed<Value>;
25
31
 
26
32
  export {};
@@ -1,16 +1,25 @@
1
1
  import { type Effect } from './effect';
2
2
  type ComputedState<Value> = {
3
- dirty: boolean;
4
3
  computeds: Set<Computed<unknown>>;
4
+ dirty: boolean;
5
5
  effect: Effect;
6
6
  effects: Set<Effect>;
7
7
  value: Value;
8
8
  };
9
+ export type InternalComputed = {
10
+ readonly state: ComputedState<unknown>;
11
+ };
9
12
  export declare let activeComputed: Computed<unknown> | undefined;
10
13
  export declare class Computed<Value> {
11
- readonly state: ComputedState<Value>;
14
+ private readonly state;
12
15
  constructor(callback: () => Value);
16
+ /**
17
+ * Get the value
18
+ */
13
19
  get(): Value;
14
20
  }
21
+ /**
22
+ * Create a computed value
23
+ */
15
24
  export declare function computed<Value>(callback: () => Value): Computed<Value>;
16
25
  export {};
@@ -1,15 +1,16 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
3
  export type GenericCallback = (...args: any[]) => any;
4
- export type EffectState = {
5
- callback: GenericCallback;
6
- };
7
- export declare let activeEffect: Effect | undefined;
8
4
  export declare class Effect {
9
- readonly state: EffectState;
5
+ private readonly state;
10
6
  constructor(callback: GenericCallback);
11
7
  }
12
8
  export declare function runEffect(effect: Effect): void;
9
+ /**
10
+ * Create an effect
11
+ */
13
12
  export declare function effect(callback: GenericCallback): Effect;
13
+ export declare let activeEffect: Effect | undefined;
14
+ export declare const dirtyEffects: Set<Effect>;
14
15
 
15
16
  export {};
package/types/effect.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { GenericCallback } from '@oscarpalmer/atoms/models';
2
- type EffectState = {
3
- callback: GenericCallback;
4
- };
5
- export declare let activeEffect: Effect | undefined;
6
2
  export declare class Effect {
7
- readonly state: EffectState;
3
+ private readonly state;
8
4
  constructor(callback: GenericCallback);
9
5
  }
10
6
  export declare function runEffect(effect: Effect): void;
7
+ /**
8
+ * Create an effect
9
+ */
11
10
  export declare function effect(callback: GenericCallback): Effect;
12
- export {};
11
+ export declare let activeEffect: Effect | undefined;
12
+ export declare const dirtyEffects: Set<Effect>;
package/types/index.d.cts CHANGED
@@ -1,39 +1,50 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
+ export declare class Signal<Value> {
4
+ private readonly state;
5
+ constructor(value: Value);
6
+ /**
7
+ * Get the value
8
+ */
9
+ get(): Value;
10
+ /**
11
+ * Set the value
12
+ */
13
+ set(value: Value): void;
14
+ /**
15
+ * Update the value
16
+ */
17
+ update(callback: (value: Value) => Value): void;
18
+ }
19
+ export declare function signal<Value>(value: Value): Signal<Value>;
20
+ /**
21
+ * Start batching effects _(use `stopBatch` to flush and run batched effects)_
22
+ */
23
+ export declare function startBatch(): void;
24
+ /**
25
+ * Stop batching effects and flush _(run)_ them
26
+ */
27
+ export declare function stopBatch(): void;
3
28
  export type GenericCallback = (...args: any[]) => any;
4
- export type EffectState = {
5
- callback: GenericCallback;
6
- };
7
29
  export declare class Effect {
8
- readonly state: EffectState;
30
+ private readonly state;
9
31
  constructor(callback: GenericCallback);
10
32
  }
33
+ /**
34
+ * Create an effect
35
+ */
11
36
  export declare function effect(callback: GenericCallback): Effect;
12
- export type ComputedState<Value> = {
13
- dirty: boolean;
14
- computeds: Set<Computed<unknown>>;
15
- effect: Effect;
16
- effects: Set<Effect>;
17
- value: Value;
18
- };
19
37
  export declare class Computed<Value> {
20
- readonly state: ComputedState<Value>;
38
+ private readonly state;
21
39
  constructor(callback: () => Value);
40
+ /**
41
+ * Get the value
42
+ */
22
43
  get(): Value;
23
44
  }
45
+ /**
46
+ * Create a computed value
47
+ */
24
48
  export declare function computed<Value>(callback: () => Value): Computed<Value>;
25
- export type SignalState<Value> = {
26
- computeds: Set<Computed<unknown>>;
27
- effects: Set<Effect>;
28
- value: Value;
29
- };
30
- export declare class Signal<Value> {
31
- readonly state: SignalState<Value>;
32
- constructor(value: Value);
33
- get(): Value;
34
- set(value: Value): void;
35
- update(callback: (value: Value) => Value): void;
36
- }
37
- export declare function signal<Value>(value: Value): Signal<Value>;
38
49
 
39
50
  export {};
package/types/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { startBatch, stopBatch } from './batch';
1
2
  export { computed, type Computed } from './computed';
2
3
  export { effect, type Effect } from './effect';
3
4
  export { signal, type Signal } from './signal';
@@ -1,36 +1,19 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- export type GenericCallback = (...args: any[]) => any;
4
- export type EffectState = {
5
- callback: GenericCallback;
6
- };
7
- declare class Effect {
8
- readonly state: EffectState;
9
- constructor(callback: GenericCallback);
10
- }
11
- export type ComputedState<Value> = {
12
- dirty: boolean;
13
- computeds: Set<Computed<unknown>>;
14
- effect: Effect;
15
- effects: Set<Effect>;
16
- value: Value;
17
- };
18
- declare class Computed<Value> {
19
- readonly state: ComputedState<Value>;
20
- constructor(callback: () => Value);
21
- get(): Value;
22
- }
23
- export type SignalState<Value> = {
24
- computeds: Set<Computed<unknown>>;
25
- effects: Set<Effect>;
26
- value: Value;
27
- };
28
- export declare const dirtyEffects: Set<Effect>;
29
3
  export declare class Signal<Value> {
30
- readonly state: SignalState<Value>;
4
+ private readonly state;
31
5
  constructor(value: Value);
6
+ /**
7
+ * Get the value
8
+ */
32
9
  get(): Value;
10
+ /**
11
+ * Set the value
12
+ */
33
13
  set(value: Value): void;
14
+ /**
15
+ * Update the value
16
+ */
34
17
  update(callback: (value: Value) => Value): void;
35
18
  }
36
19
  export declare function signal<Value>(value: Value): Signal<Value>;
package/types/signal.d.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { type Computed } from './computed';
2
- import { type Effect } from './effect';
3
- type SignalState<Value> = {
4
- computeds: Set<Computed<unknown>>;
5
- effects: Set<Effect>;
6
- value: Value;
7
- };
8
- export declare const dirtyEffects: Set<Effect>;
9
1
  export declare class Signal<Value> {
10
- readonly state: SignalState<Value>;
2
+ private readonly state;
11
3
  constructor(value: Value);
4
+ /**
5
+ * Get the value
6
+ */
12
7
  get(): Value;
8
+ /**
9
+ * Set the value
10
+ */
13
11
  set(value: Value): void;
12
+ /**
13
+ * Update the value
14
+ */
14
15
  update(callback: (value: Value) => Value): void;
15
16
  }
16
17
  export declare function signal<Value>(value: Value): Signal<Value>;
17
- export {};
package/dist/mora.iife.js DELETED
@@ -1,129 +0,0 @@
1
- var Mora = (function (exports) {
2
- 'use strict';
3
-
4
- let activeEffect;
5
- class Effect {
6
- state;
7
- constructor(callback) {
8
- this.state = {
9
- callback,
10
- };
11
- runEffect(this);
12
- }
13
- }
14
- function runEffect(effect) {
15
- const previousEffect = activeEffect;
16
- activeEffect = effect;
17
- try {
18
- effect.state.callback();
19
- }
20
- finally {
21
- activeEffect = previousEffect;
22
- }
23
- }
24
- function effect(callback) {
25
- return new Effect(callback);
26
- }
27
-
28
- const dirtyEffects = new Set();
29
- class Signal {
30
- state;
31
- constructor(value) {
32
- this.state = {
33
- value,
34
- computeds: new Set(),
35
- effects: new Set(),
36
- };
37
- }
38
- get() {
39
- if (activeComputed != null) {
40
- this.state.computeds.add(activeComputed);
41
- }
42
- if (activeEffect != null) {
43
- this.state.effects.add(activeEffect);
44
- }
45
- return this.state.value;
46
- }
47
- set(value) {
48
- if (Object.is(this.state.value, value)) {
49
- return;
50
- }
51
- this.state.value = value;
52
- for (const computed of this.state.computeds) {
53
- computed.state.dirty = true;
54
- dirtyEffects.add(computed.state.effect);
55
- }
56
- for (const effect of this.state.effects) {
57
- dirtyEffects.add(effect);
58
- }
59
- while (dirtyEffects.size > 0) {
60
- const effects = [...dirtyEffects];
61
- dirtyEffects.clear();
62
- for (const effect of effects) {
63
- runEffect(effect);
64
- }
65
- }
66
- }
67
- update(callback) {
68
- this.set(callback(this.state.value));
69
- }
70
- }
71
- function signal(value) {
72
- return new Signal(value);
73
- }
74
-
75
- let activeComputed;
76
- class Computed {
77
- state;
78
- constructor(callback) {
79
- this.state = {
80
- dirty: true,
81
- computeds: new Set(),
82
- effect: undefined,
83
- effects: new Set(),
84
- value: undefined,
85
- };
86
- this.state.effect = effect(() => {
87
- if (this.state.dirty) {
88
- const previousComputed = activeComputed;
89
- activeComputed = this;
90
- const value = callback();
91
- activeComputed = previousComputed;
92
- if (!Object.is(this.state.value, value)) {
93
- this.state.value = value;
94
- for (const comp of this.state.computeds) {
95
- comp.state.dirty = true;
96
- dirtyEffects.add(comp.state.effect);
97
- }
98
- for (const effect of this.state.effects) {
99
- dirtyEffects.add(effect);
100
- }
101
- }
102
- this.state.dirty = false;
103
- }
104
- });
105
- }
106
- get() {
107
- if (activeComputed != null && activeComputed !== this) {
108
- this.state.computeds.add(activeComputed);
109
- }
110
- if (activeEffect != null && activeEffect !== this.state.effect) {
111
- this.state.effects.add(activeEffect);
112
- }
113
- if (this.state.dirty) {
114
- runEffect(this.state.effect);
115
- }
116
- return this.state.value;
117
- }
118
- }
119
- function computed(callback) {
120
- return new Computed(callback);
121
- }
122
-
123
- exports.computed = computed;
124
- exports.effect = effect;
125
- exports.signal = signal;
126
-
127
- return exports;
128
-
129
- })({});