@oscarpalmer/mora 0.13.0 → 0.15.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.
Files changed (62) hide show
  1. package/dist/batch.cjs +2 -2
  2. package/dist/batch.js +1 -1
  3. package/dist/effect.cjs +2 -1
  4. package/dist/effect.js +2 -1
  5. package/dist/helpers/emit.cjs +42 -0
  6. package/dist/helpers/emit.js +42 -0
  7. package/dist/helpers/is.cjs +34 -0
  8. package/dist/helpers/is.js +34 -0
  9. package/dist/helpers/value.cjs +22 -0
  10. package/dist/helpers/value.js +22 -0
  11. package/dist/index.cjs +12 -11
  12. package/dist/index.js +5 -4
  13. package/dist/mora.full.js +200 -140
  14. package/dist/value/array.cjs +161 -0
  15. package/dist/{array.js → value/array.js} +62 -31
  16. package/dist/{computed.cjs → value/computed.cjs} +6 -5
  17. package/dist/{computed.js → value/computed.js} +4 -3
  18. package/dist/{reactive.cjs → value/reactive.cjs} +1 -1
  19. package/dist/{reactive.js → value/reactive.js} +1 -1
  20. package/dist/value/signal.cjs +33 -0
  21. package/dist/{signal.js → value/signal.js} +3 -2
  22. package/package.json +1 -1
  23. package/src/batch.ts +1 -1
  24. package/src/effect.ts +12 -11
  25. package/src/helpers/emit.ts +52 -0
  26. package/src/helpers/is.ts +56 -0
  27. package/src/helpers/value.ts +27 -0
  28. package/src/index.ts +11 -5
  29. package/src/subscription.ts +1 -1
  30. package/src/{array.ts → value/array.ts} +75 -41
  31. package/src/{computed.ts → value/computed.ts} +4 -3
  32. package/src/{reactive.ts → value/reactive.ts} +3 -3
  33. package/src/{signal.ts → value/signal.ts} +3 -2
  34. package/types/batch.d.cts +9 -9
  35. package/types/helpers/emit.d.cts +63 -0
  36. package/types/helpers/emit.d.ts +3 -0
  37. package/types/{is.d.cts → helpers/is.d.cts} +85 -9
  38. package/types/helpers/is.d.ts +26 -0
  39. package/types/{value.d.cts → helpers/value.d.cts} +9 -10
  40. package/types/{value.d.ts → helpers/value.d.ts} +1 -2
  41. package/types/index.d.cts +74 -38
  42. package/types/index.d.ts +5 -5
  43. package/types/subscription.d.cts +12 -12
  44. package/types/subscription.d.ts +1 -1
  45. package/types/{array.d.cts → value/array.d.cts} +41 -9
  46. package/types/value/array.d.ts +74 -0
  47. package/types/{computed.d.ts → value/computed.d.ts} +1 -1
  48. package/types/{reactive.d.ts → value/reactive.d.ts} +2 -2
  49. package/dist/array.cjs +0 -130
  50. package/dist/is.cjs +0 -21
  51. package/dist/is.js +0 -21
  52. package/dist/signal.cjs +0 -32
  53. package/dist/value.cjs +0 -38
  54. package/dist/value.js +0 -38
  55. package/src/is.ts +0 -39
  56. package/src/value.ts +0 -47
  57. package/types/array.d.ts +0 -41
  58. package/types/is.d.ts +0 -17
  59. package/types/{computed.d.cts → value/computed.d.cts} +9 -9
  60. package/types/{reactive.d.cts → value/reactive.d.cts} +9 -9
  61. package/types/{signal.d.cts → value/signal.d.cts} +9 -9
  62. /package/types/{signal.d.ts → value/signal.d.ts} +0 -0
package/dist/mora.full.js CHANGED
@@ -1,3 +1,143 @@
1
+ /**
2
+ * Is the value a reactive array?
3
+ */
4
+ function isArray(value) {
5
+ return isMora(value, arrayName);
6
+ }
7
+ /**
8
+ * Is the value a computed signal?
9
+ */
10
+ function isComputed(value) {
11
+ return isMora(value, computedName);
12
+ }
13
+ /**
14
+ * Is the value an effect?
15
+ */
16
+ function isEffect(value) {
17
+ return isMora(value, effectName);
18
+ }
19
+ function isMora(value, name) {
20
+ return (typeof value === 'object' &&
21
+ value != null &&
22
+ '$mora' in value &&
23
+ (typeof name === 'string'
24
+ ? value.$mora === name
25
+ : name.has(value.$mora)));
26
+ }
27
+ function isReactive(value) {
28
+ return isMora(value, reactiveNames);
29
+ }
30
+ /**
31
+ * Is the value a signal?
32
+ */
33
+ function isSignal(value) {
34
+ return isMora(value, signalName);
35
+ }
36
+ const arrayName = 'array';
37
+ const computedName = 'computed';
38
+ const effectName = 'effect';
39
+ const signalName = 'signal';
40
+ const reactiveNames = new Set([arrayName, computedName, signalName]);
41
+
42
+ class Effect {
43
+ constructor(callback) {
44
+ Object.defineProperty(this, '$mora', {
45
+ value: effectName,
46
+ });
47
+ this.state = {
48
+ callback,
49
+ };
50
+ runEffect(this);
51
+ }
52
+ }
53
+ function runEffect(effect) {
54
+ const previousEffect = activeEffect;
55
+ activeEffect = effect;
56
+ try {
57
+ effect.state.callback();
58
+ }
59
+ finally {
60
+ activeEffect = previousEffect;
61
+ }
62
+ }
63
+ /**
64
+ * Create an effect
65
+ */
66
+ function effect(callback) {
67
+ return new Effect(callback);
68
+ }
69
+ let activeEffect;
70
+
71
+ function flushEffects() {
72
+ while (batchDepth === 0 && batchedHandlers.size > 0) {
73
+ const handlers = [...batchedHandlers];
74
+ batchedHandlers.clear();
75
+ for (const handler of handlers) {
76
+ if (isEffect(handler)) {
77
+ runEffect(handler);
78
+ }
79
+ else {
80
+ handler.callback(handler.state.value);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ /**
86
+ * Start batching effects _(use `stopBatch` to flush and run batched effects)_
87
+ */
88
+ function startBatch() {
89
+ batchDepth += 1;
90
+ }
91
+ /**
92
+ * Stop batching effects and flush _(run)_ them
93
+ */
94
+ function stopBatch() {
95
+ if (batchDepth > 0) {
96
+ batchDepth -= 1;
97
+ }
98
+ flushEffects();
99
+ }
100
+ const batchedHandlers = new Set();
101
+ let batchDepth = 0;
102
+
103
+ function emitArrayChanges(first, second) {
104
+ let { length } = first;
105
+ if (length !== second.length) {
106
+ return true;
107
+ }
108
+ let offset = 0;
109
+ if (length >= 100) {
110
+ offset = Math.round(length / 10);
111
+ offset = offset > 25 ? 25 : offset;
112
+ for (let index = 0; index < offset; index += 1) {
113
+ if (!Object.is(first[index], second[index])) {
114
+ return true;
115
+ }
116
+ }
117
+ }
118
+ length -= offset;
119
+ for (let index = offset; index < length; index += 1) {
120
+ if (!Object.is(first[index], second[index])) {
121
+ return true;
122
+ }
123
+ }
124
+ return false;
125
+ }
126
+ function emitValue(state) {
127
+ for (const computed of state.computeds) {
128
+ computed.effect.dirty = true;
129
+ }
130
+ for (const effect of state.effects) {
131
+ batchedHandlers.add(effect);
132
+ }
133
+ for (const [, subscription] of state.subscriptions) {
134
+ batchedHandlers.add(subscription);
135
+ }
136
+ if (batchDepth === 0) {
137
+ flushEffects();
138
+ }
139
+ }
140
+
1
141
  class Subscription {
2
142
  state;
3
143
  callback;
@@ -71,95 +211,6 @@ class Reactive {
71
211
  }
72
212
  }
73
213
 
74
- class Effect {
75
- constructor(callback) {
76
- Object.defineProperty(this, '$mora', {
77
- value: 'effect',
78
- });
79
- this.state = {
80
- callback,
81
- };
82
- runEffect(this);
83
- }
84
- }
85
- function runEffect(effect) {
86
- const previousEffect = activeEffect;
87
- activeEffect = effect;
88
- try {
89
- effect.state.callback();
90
- }
91
- finally {
92
- activeEffect = previousEffect;
93
- }
94
- }
95
- /**
96
- * Create an effect
97
- */
98
- function effect(callback) {
99
- return new Effect(callback);
100
- }
101
- let activeEffect;
102
-
103
- /**
104
- * Is the value a computed signal?
105
- */
106
- function isComputed(value) {
107
- return isMora(value, 'computed');
108
- }
109
- /**
110
- * Is the value an effect?
111
- */
112
- function isEffect(value) {
113
- return isMora(value, 'effect');
114
- }
115
- function isMora(value, name) {
116
- return (typeof value === 'object' &&
117
- value != null &&
118
- '$mora' in value &&
119
- value.$mora === name);
120
- }
121
- function isReactive(value) {
122
- return isComputed(value) || isSignal(value);
123
- }
124
- /**
125
- * Is the value a signal?
126
- */
127
- function isSignal(value) {
128
- return isMora(value, 'signal');
129
- }
130
-
131
- function flushEffects() {
132
- while (batchDepth === 0 && batchedHandlers.size > 0) {
133
- const handlers = [...batchedHandlers];
134
- batchedHandlers.clear();
135
- for (const handler of handlers) {
136
- if (isEffect(handler)) {
137
- runEffect(handler);
138
- }
139
- else {
140
- handler.callback(handler.state.value);
141
- }
142
- }
143
- }
144
- }
145
- /**
146
- * Start batching effects _(use `stopBatch` to flush and run batched effects)_
147
- */
148
- function startBatch() {
149
- batchDepth += 1;
150
- }
151
- /**
152
- * Stop batching effects and flush _(run)_ them
153
- */
154
- function stopBatch() {
155
- if (batchDepth > 0) {
156
- batchDepth -= 1;
157
- }
158
- flushEffects();
159
- }
160
- const batchedHandlers = new Set();
161
- let batchDepth = 0;
162
-
163
214
  let activeComputed;
164
215
  class Computed extends Reactive {
165
216
  effect = {
@@ -167,7 +218,7 @@ class Computed extends Reactive {
167
218
  instance: undefined,
168
219
  };
169
220
  constructor(callback) {
170
- super('computed', undefined);
221
+ super(computedName, undefined);
171
222
  this.effect.instance = effect(() => {
172
223
  if (this.effect.dirty) {
173
224
  const previousComputed = activeComputed;
@@ -213,20 +264,6 @@ function computed(callback) {
213
264
  return new Computed(callback);
214
265
  }
215
266
 
216
- function emitValue(state) {
217
- for (const computed of state.computeds) {
218
- computed.effect.dirty = true;
219
- }
220
- for (const effect of state.effects) {
221
- batchedHandlers.add(effect);
222
- }
223
- for (const [, subscription] of state.subscriptions) {
224
- batchedHandlers.add(subscription);
225
- }
226
- if (batchDepth === 0) {
227
- flushEffects();
228
- }
229
- }
230
267
  function getValue(state) {
231
268
  if (activeComputed != null) {
232
269
  state.computeds.add(activeComputed);
@@ -237,16 +274,15 @@ function getValue(state) {
237
274
  return state.value;
238
275
  }
239
276
  function setValue$1(state, value) {
240
- if (Object.is(state.value, value)) {
241
- return;
277
+ if (!Object.is(state.value, value)) {
278
+ state.value = value;
279
+ emitValue(state);
242
280
  }
243
- state.value = value;
244
- emitValue(state);
245
281
  }
246
282
 
247
283
  class Signal extends Reactive {
248
284
  constructor(value) {
249
- super('signal', value);
285
+ super(signalName, value);
250
286
  }
251
287
  /**
252
288
  * @inheritdoc
@@ -293,7 +329,7 @@ class ReactiveArray extends Reactive {
293
329
  }
294
330
  }
295
331
  constructor(value) {
296
- super('array', new Proxy(value, {
332
+ super(arrayName, new Proxy(value, {
297
333
  get: (target, property) => updateMethods.has(property)
298
334
  ? updateArray(property, target, this.state, this.#size)
299
335
  : Reflect.get(target, property),
@@ -301,15 +337,45 @@ class ReactiveArray extends Reactive {
301
337
  }));
302
338
  this.#size.set(value.length);
303
339
  }
340
+ /**
341
+ * Get an indexed item
342
+ */
343
+ at(index) {
344
+ return this.get().at(index);
345
+ }
304
346
  /**
305
347
  * @inheritdoc
306
348
  */
307
349
  get() {
308
350
  return getValue(this.state);
309
351
  }
352
+ /**
353
+ * Create a computed, filtered array
354
+ */
355
+ filter(callback) {
356
+ return computed(() => this.get().filter(callback));
357
+ }
358
+ /**
359
+ * Create a computed, mapped array
360
+ */
361
+ map(callback) {
362
+ return computed(() => this.get().map(callback));
363
+ }
310
364
  peek(length) {
311
365
  return length === true ? this.#size.peek() : [...this.state.value];
312
366
  }
367
+ /**
368
+ * Remove and return the last item of the array
369
+ */
370
+ pop() {
371
+ return this.state.value.pop();
372
+ }
373
+ /**
374
+ * Add items to the end of the array
375
+ */
376
+ push(...items) {
377
+ return this.state.value.push(...items);
378
+ }
313
379
  set(first, second) {
314
380
  if (Array.isArray(first)) {
315
381
  this.state.value.splice(0, this.state.value.length, ...first);
@@ -321,6 +387,24 @@ class ReactiveArray extends Reactive {
321
387
  this.state.value[first] = second;
322
388
  }
323
389
  }
390
+ /**
391
+ * Remove and return the first item of the array
392
+ */
393
+ shift() {
394
+ return this.state.value.shift();
395
+ }
396
+ /**
397
+ * Remove and return items from the array _(and optionally add new items)_
398
+ */
399
+ splice(from, to, ...items) {
400
+ return this.state.value.splice(from, to ?? this.state.value.length, ...items);
401
+ }
402
+ /**
403
+ * Add items to the beginning of the array
404
+ */
405
+ unshift(...items) {
406
+ return this.state.value.unshift(...items);
407
+ }
324
408
  }
325
409
  /**
326
410
  * Create a reactive array
@@ -328,29 +412,6 @@ class ReactiveArray extends Reactive {
328
412
  function array(value) {
329
413
  return new ReactiveArray(Array.isArray(value) ? value : []);
330
414
  }
331
- function emit(first, second) {
332
- let { length } = first;
333
- if (length !== second.length) {
334
- return true;
335
- }
336
- let offset = 0;
337
- if (length >= 100) {
338
- offset = Math.round(length / 10);
339
- offset = offset > 25 ? 25 : offset;
340
- for (let index = 0; index < offset; index += 1) {
341
- if (!Object.is(first[index], second[index])) {
342
- return true;
343
- }
344
- }
345
- }
346
- length -= offset;
347
- for (let index = offset; index < length; index += 1) {
348
- if (!Object.is(first[index], second[index])) {
349
- return true;
350
- }
351
- }
352
- return false;
353
- }
354
415
  function setValue(target, property, value, state, length) {
355
416
  const isIndex = !Number.isNaN(Number(property));
356
417
  const isLength = property === 'length';
@@ -358,12 +419,11 @@ function setValue(target, property, value, state, length) {
358
419
  return Reflect.set(target, property, value);
359
420
  }
360
421
  const previous = Reflect.get(target, property);
361
- if (Object.is(previous, value)) {
362
- return true;
422
+ if (!Object.is(previous, value)) {
423
+ Reflect.set(target, property, value);
424
+ emitValue(state);
425
+ length.set(target.length);
363
426
  }
364
- Reflect.set(target, property, value);
365
- emitValue(state);
366
- length.set(target.length);
367
427
  return true;
368
428
  }
369
429
  function updateArray(type, array, state, length) {
@@ -374,7 +434,7 @@ function updateArray(type, array, state, length) {
374
434
  const result = array[type](...args);
375
435
  if (affectsLength
376
436
  ? array.length !== previousLength
377
- : emit(previousArray, array)) {
437
+ : emitArrayChanges(previousArray, array)) {
378
438
  emitValue(state);
379
439
  length.set(array.length);
380
440
  }
@@ -396,4 +456,4 @@ const updateMethods = new Set([
396
456
  'splice',
397
457
  ]);
398
458
 
399
- export { array, computed, effect, isComputed, isEffect, isReactive, isSignal, signal, startBatch, stopBatch };
459
+ export { array, computed, effect, isArray, isComputed, isEffect, isReactive, isSignal, signal, startBatch, stopBatch };
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
6
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
7
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
8
+ var _size;
9
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
10
+ const helpers_emit = require("../helpers/emit.cjs");
11
+ const helpers_is = require("../helpers/is.cjs");
12
+ const helpers_value = require("../helpers/value.cjs");
13
+ const value_computed = require("./computed.cjs");
14
+ const value_reactive = require("./reactive.cjs");
15
+ const value_signal = require("./signal.cjs");
16
+ class ReactiveArray extends value_reactive.Reactive {
17
+ constructor(value) {
18
+ super(
19
+ helpers_is.arrayName,
20
+ new Proxy(value, {
21
+ get: (target, property) => updateMethods.has(property) ? updateArray(property, target, this.state, __privateGet(this, _size)) : Reflect.get(target, property),
22
+ set: (target, property, value2) => setValue(target, property, value2, this.state, __privateGet(this, _size))
23
+ })
24
+ );
25
+ __privateAdd(this, _size, value_signal.signal(0));
26
+ __privateGet(this, _size).set(value.length);
27
+ }
28
+ /**
29
+ * The length of the array
30
+ */
31
+ get length() {
32
+ return __privateGet(this, _size).get();
33
+ }
34
+ /**
35
+ * Set the length of the array
36
+ */
37
+ set length(value) {
38
+ if (typeof value === "number" && value >= 0 && value !== this.state.value.length) {
39
+ this.get().length = value;
40
+ }
41
+ }
42
+ /**
43
+ * Get an indexed item
44
+ */
45
+ at(index) {
46
+ return this.get().at(index);
47
+ }
48
+ /**
49
+ * @inheritdoc
50
+ */
51
+ get() {
52
+ return helpers_value.getValue(this.state);
53
+ }
54
+ /**
55
+ * Create a computed, filtered array
56
+ */
57
+ filter(callback) {
58
+ return value_computed.computed(() => this.get().filter(callback));
59
+ }
60
+ /**
61
+ * Create a computed, mapped array
62
+ */
63
+ map(callback) {
64
+ return value_computed.computed(() => this.get().map(callback));
65
+ }
66
+ peek(length) {
67
+ return length === true ? __privateGet(this, _size).peek() : [...this.state.value];
68
+ }
69
+ /**
70
+ * Remove and return the last item of the array
71
+ */
72
+ pop() {
73
+ return this.state.value.pop();
74
+ }
75
+ /**
76
+ * Add items to the end of the array
77
+ */
78
+ push(...items) {
79
+ return this.state.value.push(...items);
80
+ }
81
+ set(first, second) {
82
+ if (Array.isArray(first)) {
83
+ this.state.value.splice(0, this.state.value.length, ...first);
84
+ } else if (first === "length") {
85
+ this.length = second;
86
+ } else if (typeof first === "number") {
87
+ this.state.value[first] = second;
88
+ }
89
+ }
90
+ /**
91
+ * Remove and return the first item of the array
92
+ */
93
+ shift() {
94
+ return this.state.value.shift();
95
+ }
96
+ /**
97
+ * Remove and return items from the array _(and optionally add new items)_
98
+ */
99
+ splice(from, to, ...items) {
100
+ return this.state.value.splice(
101
+ from,
102
+ to ?? this.state.value.length,
103
+ ...items
104
+ );
105
+ }
106
+ /**
107
+ * Add items to the beginning of the array
108
+ */
109
+ unshift(...items) {
110
+ return this.state.value.unshift(...items);
111
+ }
112
+ }
113
+ _size = new WeakMap();
114
+ function array(value) {
115
+ return new ReactiveArray(Array.isArray(value) ? value : []);
116
+ }
117
+ function setValue(target, property, value, state, length) {
118
+ const isIndex = !Number.isNaN(Number(property));
119
+ const isLength = property === "length";
120
+ if (!isIndex && !isLength) {
121
+ return Reflect.set(target, property, value);
122
+ }
123
+ const previous = Reflect.get(target, property);
124
+ if (!Object.is(previous, value)) {
125
+ Reflect.set(target, property, value);
126
+ helpers_emit.emitValue(state);
127
+ length.set(target.length);
128
+ }
129
+ return true;
130
+ }
131
+ function updateArray(type, array2, state, length) {
132
+ const affectsLength = lengthAffectingMethods.has(type);
133
+ const previousArray = affectsLength ? [] : [...array2];
134
+ const previousLength = array2.length;
135
+ return (...args) => {
136
+ const result = array2[type](
137
+ ...args
138
+ );
139
+ if (affectsLength ? array2.length !== previousLength : helpers_emit.emitArrayChanges(previousArray, array2)) {
140
+ helpers_emit.emitValue(state);
141
+ length.set(array2.length);
142
+ }
143
+ return result;
144
+ };
145
+ }
146
+ const lengthAffectingMethods = /* @__PURE__ */ new Set([
147
+ "pop",
148
+ "push",
149
+ "shift",
150
+ "unshift"
151
+ ]);
152
+ const updateMethods = /* @__PURE__ */ new Set([
153
+ ...lengthAffectingMethods,
154
+ "copyWithin",
155
+ "fill",
156
+ "reverse",
157
+ "sort",
158
+ "splice"
159
+ ]);
160
+ exports.ReactiveArray = ReactiveArray;
161
+ exports.array = array;