@oscarpalmer/mora 0.21.0 → 0.23.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.js +11 -25
- package/dist/constants.js +34 -0
- package/dist/effect.js +18 -28
- package/dist/helpers/is.js +9 -26
- package/dist/helpers/proxy.js +36 -48
- package/dist/helpers/value.js +21 -48
- package/dist/index.js +3 -16
- package/dist/models.js +0 -0
- package/dist/mora.full.js +218 -130
- package/dist/subscription.js +22 -28
- package/dist/value/array.js +97 -182
- package/dist/value/computed.js +33 -58
- package/dist/value/reactive.js +29 -53
- package/dist/value/signal.js +21 -33
- package/dist/value/store.js +37 -60
- package/package.json +17 -24
- package/src/batch.ts +11 -13
- package/src/constants.ts +49 -0
- package/src/effect.ts +11 -16
- package/src/helpers/is.ts +41 -19
- package/src/helpers/proxy.ts +34 -38
- package/src/helpers/value.ts +19 -14
- package/src/index.ts +1 -1
- package/src/models.ts +66 -0
- package/src/subscription.ts +14 -16
- package/src/value/array.ts +87 -45
- package/src/value/computed.ts +29 -39
- package/src/value/reactive.ts +9 -24
- package/src/value/signal.ts +9 -3
- package/src/value/store.ts +40 -9
- package/types/batch.d.ts +3 -5
- package/types/constants.d.ts +14 -0
- package/types/effect.d.ts +2 -1
- package/types/helpers/is.d.ts +23 -10
- package/types/helpers/proxy.d.ts +2 -3
- package/types/helpers/value.d.ts +1 -1
- package/types/index.d.ts +1 -0
- package/types/models.d.ts +56 -0
- package/types/subscription.d.ts +3 -6
- package/types/value/array.d.ts +48 -7
- package/types/value/computed.d.ts +2 -11
- package/types/value/reactive.d.ts +8 -17
- package/types/value/signal.d.ts +7 -1
- package/types/value/store.d.ts +28 -4
- package/dist/batch.cjs +0 -32
- package/dist/effect.cjs +0 -30
- package/dist/helpers/is.cjs +0 -40
- package/dist/helpers/proxy.cjs +0 -56
- package/dist/helpers/value.cjs +0 -54
- package/dist/index.cjs +0 -21
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/is.cjs +0 -17
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/is.js +0 -17
- package/dist/subscription.cjs +0 -32
- package/dist/value/array.cjs +0 -187
- package/dist/value/computed.cjs +0 -60
- package/dist/value/reactive.cjs +0 -55
- package/dist/value/signal.cjs +0 -36
- package/dist/value/store.cjs +0 -63
- package/types/batch.d.cts +0 -79
- package/types/effect.d.cts +0 -16
- package/types/helpers/is.d.cts +0 -259
- package/types/helpers/proxy.d.cts +0 -244
- package/types/helpers/value.d.cts +0 -71
- package/types/index.d.cts +0 -281
- package/types/subscription.d.cts +0 -71
- package/types/value/array.d.cts +0 -161
- package/types/value/computed.d.cts +0 -81
- package/types/value/reactive.d.cts +0 -68
- package/types/value/signal.d.cts +0 -87
- package/types/value/store.d.cts +0 -136
package/package.json
CHANGED
|
@@ -4,39 +4,33 @@
|
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.
|
|
7
|
+
"@oscarpalmer/atoms": "^0.114"
|
|
8
8
|
},
|
|
9
9
|
"description": "Signals and stuff…",
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"@biomejs/biome": "^
|
|
11
|
+
"@biomejs/biome": "^2.3",
|
|
12
12
|
"@rollup/plugin-node-resolve": "^16",
|
|
13
|
-
"@rollup/plugin-typescript": "^12.
|
|
14
|
-
"@types/node": "^24",
|
|
15
|
-
"@vitest/coverage-istanbul": "^
|
|
16
|
-
"dts-bundle-generator": "^9.5",
|
|
13
|
+
"@rollup/plugin-typescript": "^12.3",
|
|
14
|
+
"@types/node": "^24.9",
|
|
15
|
+
"@vitest/coverage-istanbul": "^4",
|
|
17
16
|
"glob": "^11",
|
|
18
|
-
"jsdom": "^
|
|
17
|
+
"jsdom": "^27.1",
|
|
18
|
+
"rollup": "^4.52",
|
|
19
19
|
"tslib": "^2.8",
|
|
20
|
-
"typescript": "^5.
|
|
21
|
-
"vite": "
|
|
22
|
-
"vitest": "^
|
|
20
|
+
"typescript": "^5.9",
|
|
21
|
+
"vite": "npm:rolldown-vite@latest",
|
|
22
|
+
"vitest": "^4"
|
|
23
23
|
},
|
|
24
24
|
"exports": {
|
|
25
|
+
"./package.json": "./package.json",
|
|
25
26
|
".": {
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
"default": "./dist/index.js"
|
|
29
|
-
},
|
|
30
|
-
"require": {
|
|
31
|
-
"types": "./types/index.d.cts",
|
|
32
|
-
"default": "./dist/index.cjs"
|
|
33
|
-
}
|
|
27
|
+
"types": "./types/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
34
29
|
}
|
|
35
30
|
},
|
|
36
31
|
"files": ["dist", "src", "types"],
|
|
37
32
|
"keywords": ["signal", "signals", "reactive"],
|
|
38
33
|
"license": "MIT",
|
|
39
|
-
"main": "./dist/index.cjs",
|
|
40
34
|
"module": "./dist/index.js",
|
|
41
35
|
"name": "@oscarpalmer/mora",
|
|
42
36
|
"repository": {
|
|
@@ -44,15 +38,14 @@
|
|
|
44
38
|
"url": "git+https://github.com/oscarpalmer/mora.git"
|
|
45
39
|
},
|
|
46
40
|
"scripts": {
|
|
47
|
-
"build": "npm run clean &&
|
|
48
|
-
"build:js": "npx vite build",
|
|
41
|
+
"build": "npm run clean && npx vite build && npm run rollup:build && npx tsc",
|
|
49
42
|
"clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo",
|
|
50
|
-
"rollup": "npx rollup -c",
|
|
43
|
+
"rollup:build": "npx rollup -c",
|
|
44
|
+
"rollup:watch": "npx rollup -c --watch",
|
|
51
45
|
"test": "npx vitest --coverage",
|
|
52
|
-
"types": "npx tsc && npx dts-bundle-generator --config ./dts.config.cts --silent",
|
|
53
46
|
"watch": "npx vite build --watch"
|
|
54
47
|
},
|
|
55
48
|
"type": "module",
|
|
56
49
|
"types": "./types/index.d.ts",
|
|
57
|
-
"version": "0.
|
|
50
|
+
"version": "0.23.0"
|
|
58
51
|
}
|
package/src/batch.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {BATCH} from './constants';
|
|
2
|
+
import {runEffect} from './effect';
|
|
2
3
|
import {isEffect} from './helpers/is';
|
|
3
|
-
import type {Subscription} from './subscription';
|
|
4
4
|
|
|
5
5
|
export function flushHandlers(): void {
|
|
6
|
-
while (
|
|
7
|
-
const handlers = [...
|
|
6
|
+
while (BATCH.depth === 0 && BATCH.handlers.size > 0) {
|
|
7
|
+
const handlers = [...BATCH.handlers];
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
BATCH.handlers.clear();
|
|
10
10
|
|
|
11
11
|
for (const handler of handlers) {
|
|
12
12
|
if (isEffect(handler)) {
|
|
@@ -19,23 +19,21 @@ export function flushHandlers(): void {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Start batching effects
|
|
22
|
+
* Start batching effects
|
|
23
|
+
*
|
|
24
|
+
* _(Use {@link stopBatch} to flush and run batched effects)_
|
|
23
25
|
*/
|
|
24
26
|
export function startBatch(): void {
|
|
25
|
-
|
|
27
|
+
BATCH.depth += 1;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Stop batching effects and flush _(run)_ them
|
|
30
32
|
*/
|
|
31
33
|
export function stopBatch(): void {
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
+
if (BATCH.depth > 0) {
|
|
35
|
+
BATCH.depth -= 1;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
flushHandlers();
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
export const batchedHandlers = new Set<Effect | Subscription>();
|
|
40
|
-
|
|
41
|
-
export let batchDepth = 0;
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type {Effect} from './effect';
|
|
2
|
+
import type {Active, Batch} from './models';
|
|
3
|
+
import type {Subscription} from './subscription';
|
|
4
|
+
|
|
5
|
+
export const ACTIVE: Active = {};
|
|
6
|
+
|
|
7
|
+
export const ARRAY_THRESHOLD = 100;
|
|
8
|
+
|
|
9
|
+
export const ARRAY_OFFSET = 25;
|
|
10
|
+
|
|
11
|
+
export const ARRAY_PEEK = 10;
|
|
12
|
+
|
|
13
|
+
export const BATCH: Batch = {
|
|
14
|
+
depth: 0,
|
|
15
|
+
handlers: new Set<Effect | Subscription>(),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const METHODS_AFFECTING_LENGTH: Set<string> = new Set<string>([
|
|
19
|
+
'pop',
|
|
20
|
+
'push',
|
|
21
|
+
'shift',
|
|
22
|
+
'unshift',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export const METHODS_UPDATE: Set<string> = new Set<string>([
|
|
26
|
+
...METHODS_AFFECTING_LENGTH,
|
|
27
|
+
'copyWithin',
|
|
28
|
+
'fill',
|
|
29
|
+
'reverse',
|
|
30
|
+
'sort',
|
|
31
|
+
'splice',
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export const NAME_ARRAY = 'array';
|
|
35
|
+
|
|
36
|
+
export const NAME_COMPUTED = 'computed';
|
|
37
|
+
|
|
38
|
+
export const NAME_EFFECT = 'effect';
|
|
39
|
+
|
|
40
|
+
export const NAME_SIGNAL = 'signal';
|
|
41
|
+
|
|
42
|
+
export const NAME_STORE = 'store';
|
|
43
|
+
|
|
44
|
+
export const NAMES: Set<string> = new Set([
|
|
45
|
+
NAME_ARRAY,
|
|
46
|
+
NAME_COMPUTED,
|
|
47
|
+
NAME_SIGNAL,
|
|
48
|
+
NAME_STORE,
|
|
49
|
+
]);
|
package/src/effect.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
type EffectState = {
|
|
5
|
-
callback: GenericCallback;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
type InternalEffect = {
|
|
9
|
-
state: EffectState;
|
|
10
|
-
};
|
|
2
|
+
import {ACTIVE, NAME_EFFECT} from './constants';
|
|
3
|
+
import type {EffectState, InternalEffect} from './models';
|
|
11
4
|
|
|
12
5
|
export class Effect {
|
|
13
6
|
private declare readonly $mora: string;
|
|
@@ -16,7 +9,7 @@ export class Effect {
|
|
|
16
9
|
|
|
17
10
|
constructor(callback: GenericCallback) {
|
|
18
11
|
Object.defineProperty(this, '$mora', {
|
|
19
|
-
value:
|
|
12
|
+
value: NAME_EFFECT,
|
|
20
13
|
});
|
|
21
14
|
|
|
22
15
|
this.state = {
|
|
@@ -28,22 +21,24 @@ export class Effect {
|
|
|
28
21
|
}
|
|
29
22
|
|
|
30
23
|
export function runEffect(effect: Effect): void {
|
|
31
|
-
const previousEffect =
|
|
24
|
+
const previousEffect = ACTIVE.effect;
|
|
32
25
|
|
|
33
|
-
|
|
26
|
+
ACTIVE.effect = effect;
|
|
34
27
|
|
|
35
28
|
try {
|
|
36
29
|
(effect as unknown as InternalEffect).state.callback();
|
|
37
30
|
} finally {
|
|
38
|
-
|
|
31
|
+
ACTIVE.effect = previousEffect;
|
|
39
32
|
}
|
|
40
33
|
}
|
|
41
34
|
|
|
42
35
|
/**
|
|
43
36
|
* Create an effect
|
|
37
|
+
* @param callback Callback for handling signal effects
|
|
38
|
+
* @returns Effect
|
|
44
39
|
*/
|
|
45
40
|
export function effect(callback: GenericCallback): Effect {
|
|
46
|
-
return
|
|
41
|
+
return typeof callback === 'function'
|
|
42
|
+
? new Effect(callback)
|
|
43
|
+
: (undefined as never);
|
|
47
44
|
}
|
|
48
|
-
|
|
49
|
-
export let activeEffect: Effect | undefined;
|
package/src/helpers/is.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {
|
|
3
|
+
NAME_ARRAY,
|
|
4
|
+
NAME_COMPUTED,
|
|
5
|
+
NAME_EFFECT,
|
|
6
|
+
NAME_SIGNAL,
|
|
7
|
+
NAME_STORE,
|
|
8
|
+
NAMES,
|
|
9
|
+
} from '../constants';
|
|
2
10
|
import type {Effect} from '../effect';
|
|
3
11
|
import type {ReactiveArray} from '../value/array';
|
|
4
12
|
import type {Computed} from '../value/computed';
|
|
@@ -8,23 +16,29 @@ import type {Store} from '../value/store';
|
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* Is the value a reactive array?
|
|
19
|
+
* @param value Value to check
|
|
20
|
+
* @returns True if value is a {@link ReactiveArray}
|
|
11
21
|
*/
|
|
12
|
-
export function isArray(value: unknown): value is ReactiveArray<
|
|
13
|
-
return isMora<ReactiveArray<
|
|
22
|
+
export function isArray<Item>(value: unknown): value is ReactiveArray<Item> {
|
|
23
|
+
return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
/**
|
|
17
27
|
* Is the value a computed signal?
|
|
28
|
+
* @param value Value to check
|
|
29
|
+
* @returns True if value is a {@link Computed}
|
|
18
30
|
*/
|
|
19
|
-
export function isComputed(value: unknown): value is Computed<
|
|
20
|
-
return isMora<Computed<
|
|
31
|
+
export function isComputed<Value>(value: unknown): value is Computed<Value> {
|
|
32
|
+
return isMora<Computed<Value>>(value, NAME_COMPUTED);
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
/**
|
|
24
36
|
* Is the value an effect?
|
|
37
|
+
* @param value Value to check
|
|
38
|
+
* @returns True if value is an {@link Effect}
|
|
25
39
|
*/
|
|
26
40
|
export function isEffect(value: unknown): value is Effect {
|
|
27
|
-
return isMora<Effect>(value,
|
|
41
|
+
return isMora<Effect>(value, NAME_EFFECT);
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
function isMora<T>(value: unknown, name: string | Set<string>): value is T {
|
|
@@ -38,25 +52,33 @@ function isMora<T>(value: unknown, name: string | Set<string>): value is T {
|
|
|
38
52
|
);
|
|
39
53
|
}
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Is the value reactive?
|
|
57
|
+
* @param value Value to check
|
|
58
|
+
* @returns True if value is a {@link Reactive}
|
|
59
|
+
*/
|
|
60
|
+
export function isReactive<Value, Equal = Value>(
|
|
61
|
+
value: unknown,
|
|
62
|
+
): value is Reactive<Value, Equal> {
|
|
63
|
+
return isMora<Reactive<Value, Equal>>(value, NAMES);
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
/**
|
|
46
67
|
* Is the value a signal?
|
|
68
|
+
* @param value Value to check
|
|
69
|
+
* @returns True if value is a {@link Signal}
|
|
47
70
|
*/
|
|
48
|
-
export function isSignal(value: unknown): value is Signal<
|
|
49
|
-
return isMora<Signal<
|
|
71
|
+
export function isSignal<Value>(value: unknown): value is Signal<Value> {
|
|
72
|
+
return isMora<Signal<Value>>(value, NAME_SIGNAL);
|
|
50
73
|
}
|
|
51
74
|
|
|
52
|
-
|
|
53
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Is the value a reactive store?
|
|
77
|
+
* @param value Value to check
|
|
78
|
+
* @returns True if value is a {@link Store}
|
|
79
|
+
*/
|
|
80
|
+
export function isStore<Value extends PlainObject>(
|
|
81
|
+
value: unknown,
|
|
82
|
+
): value is Store<Value> {
|
|
83
|
+
return isMora<Store<Value>>(value, NAME_STORE);
|
|
54
84
|
}
|
|
55
|
-
|
|
56
|
-
export const arrayName = 'array';
|
|
57
|
-
export const computedName = 'computed';
|
|
58
|
-
export const effectName = 'effect';
|
|
59
|
-
export const signalName = 'signal';
|
|
60
|
-
export const storeName = 'store';
|
|
61
|
-
|
|
62
|
-
const reactiveNames = new Set([arrayName, computedName, signalName, storeName]);
|
package/src/helpers/proxy.ts
CHANGED
|
@@ -4,50 +4,49 @@ import type {
|
|
|
4
4
|
PlainObject,
|
|
5
5
|
} from '@oscarpalmer/atoms/models';
|
|
6
6
|
import {startBatch, stopBatch} from '../batch';
|
|
7
|
+
import type {SetValueInProxyParameters} from '../models';
|
|
7
8
|
import type {ReactiveArray} from '../value/array';
|
|
8
9
|
import {type Computed, computed} from '../value/computed';
|
|
9
|
-
import type {ReactiveState} from '../value/reactive';
|
|
10
|
-
import type {Signal} from '../value/signal';
|
|
11
10
|
import type {Store} from '../value/store';
|
|
12
11
|
import {emitValue} from './value';
|
|
13
12
|
|
|
14
13
|
export function getReactiveValueInProxy<Value>(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
array: ReactiveArray<Value>,
|
|
15
|
+
mapped: Map<Key, Computed<unknown>>,
|
|
16
|
+
index: number,
|
|
17
|
+
isArray: true,
|
|
18
|
+
): Computed<unknown>;
|
|
20
19
|
|
|
21
20
|
export function getReactiveValueInProxy<Value extends PlainObject>(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
store: Store<Value>,
|
|
22
|
+
mapped: Map<Key, Computed<unknown>>,
|
|
23
|
+
key: Key,
|
|
24
|
+
isArray: false,
|
|
25
|
+
): Computed<unknown>;
|
|
27
26
|
|
|
28
27
|
export function getReactiveValueInProxy(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
mapped.set(key, item);
|
|
46
|
-
}
|
|
28
|
+
reactive: ReactiveArray<unknown> | Store<PlainObject>,
|
|
29
|
+
mapped: Map<Key, Computed<unknown>>,
|
|
30
|
+
key: Key,
|
|
31
|
+
isArray: boolean,
|
|
32
|
+
): Computed<unknown> {
|
|
33
|
+
let item = mapped.get(key);
|
|
34
|
+
|
|
35
|
+
if (item == null) {
|
|
36
|
+
item = computed(() => {
|
|
37
|
+
const value = reactive.get();
|
|
38
|
+
|
|
39
|
+
return isArray
|
|
40
|
+
? (value as unknown[]).at(key as number)
|
|
41
|
+
: (value as PlainObject)[key];
|
|
42
|
+
}) as Computed<unknown>;
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
mapped.set(key, item);
|
|
49
45
|
}
|
|
50
46
|
|
|
47
|
+
return item;
|
|
48
|
+
}
|
|
49
|
+
|
|
51
50
|
export function setProxyValue(
|
|
52
51
|
proxy: ArrayOrPlainObject,
|
|
53
52
|
value: ArrayOrPlainObject,
|
|
@@ -83,18 +82,15 @@ export function setProxyValue(
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
|
|
86
|
-
|
|
87
|
-
property: PropertyKey,
|
|
88
|
-
value: unknown,
|
|
89
|
-
state: ReactiveState<Value, Equal>,
|
|
90
|
-
isArray: boolean,
|
|
91
|
-
length?: Signal<number>,
|
|
85
|
+
parameters: SetValueInProxyParameters<Value, Equal>,
|
|
92
86
|
): boolean {
|
|
87
|
+
const {isArray, length, property, state, target, value} = parameters;
|
|
88
|
+
|
|
93
89
|
if (isArray) {
|
|
94
90
|
const isIndex = !Number.isNaN(Number(property));
|
|
95
91
|
const isLength = property === 'length';
|
|
96
92
|
|
|
97
|
-
if (!isIndex
|
|
93
|
+
if (!(isIndex || isLength)) {
|
|
98
94
|
return Reflect.set(target, property, value);
|
|
99
95
|
}
|
|
100
96
|
}
|
package/src/helpers/value.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {flushHandlers} from '../batch';
|
|
2
|
+
import {
|
|
3
|
+
ACTIVE,
|
|
4
|
+
ARRAY_OFFSET,
|
|
5
|
+
ARRAY_PEEK,
|
|
6
|
+
ARRAY_THRESHOLD,
|
|
7
|
+
BATCH,
|
|
8
|
+
} from '../constants';
|
|
9
|
+
import type {InternalComputed, ReactiveState} from '../models';
|
|
5
10
|
|
|
6
11
|
export function emitValue<Value>(state: ReactiveState<Value, never>): void {
|
|
7
12
|
for (const computed of state.computeds) {
|
|
@@ -9,14 +14,14 @@ export function emitValue<Value>(state: ReactiveState<Value, never>): void {
|
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
for (const effect of state.effects) {
|
|
12
|
-
|
|
17
|
+
BATCH.handlers.add(effect);
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
for (const [, subscription] of state.subscriptions) {
|
|
16
|
-
|
|
21
|
+
BATCH.handlers.add(subscription);
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
if (
|
|
24
|
+
if (BATCH.depth === 0) {
|
|
20
25
|
flushHandlers();
|
|
21
26
|
}
|
|
22
27
|
}
|
|
@@ -34,9 +39,9 @@ export function equalArrays<Value>(
|
|
|
34
39
|
|
|
35
40
|
let offset = 0;
|
|
36
41
|
|
|
37
|
-
if (length >=
|
|
38
|
-
offset = Math.round(length /
|
|
39
|
-
offset = offset >
|
|
42
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
43
|
+
offset = Math.round(length / ARRAY_PEEK);
|
|
44
|
+
offset = offset > ARRAY_OFFSET ? ARRAY_OFFSET : offset;
|
|
40
45
|
|
|
41
46
|
for (let index = 0; index < offset; index += 1) {
|
|
42
47
|
if (!state.equal(first[index], second[index])) {
|
|
@@ -57,12 +62,12 @@ export function equalArrays<Value>(
|
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export function getValue<Value>(state: ReactiveState<Value, never>): Value {
|
|
60
|
-
if (
|
|
61
|
-
state.computeds.add(
|
|
65
|
+
if (ACTIVE.computed != null) {
|
|
66
|
+
state.computeds.add(ACTIVE.computed);
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
if (
|
|
65
|
-
state.effects.add(
|
|
69
|
+
if (ACTIVE.effect != null) {
|
|
70
|
+
state.effects.add(ACTIVE.effect);
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
return state.value;
|
package/src/index.ts
CHANGED
|
@@ -7,9 +7,9 @@ export {
|
|
|
7
7
|
isReactive,
|
|
8
8
|
isSignal,
|
|
9
9
|
} from './helpers/is';
|
|
10
|
+
export type {Unsubscribe} from './models';
|
|
10
11
|
export {array, type ReactiveArray} from './value/array';
|
|
11
12
|
export {computed, type Computed} from './value/computed';
|
|
12
13
|
export type {Reactive} from './value/reactive';
|
|
13
14
|
export {signal, type Signal} from './value/signal';
|
|
14
15
|
export {store, type Store} from './value/store';
|
|
15
|
-
|
package/src/models.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import type {Effect} from './effect';
|
|
3
|
+
import type {Subscription} from './subscription';
|
|
4
|
+
import type {Computed} from './value/computed';
|
|
5
|
+
import type {Signal} from './value/signal';
|
|
6
|
+
|
|
7
|
+
export type Active = {
|
|
8
|
+
computed?: Computed<unknown>;
|
|
9
|
+
effect?: Effect;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Batch = {
|
|
13
|
+
depth: number;
|
|
14
|
+
handlers: Set<Effect | Subscription>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ComputedEffect = {
|
|
18
|
+
dirty: boolean;
|
|
19
|
+
instance: Effect;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type EffectState = {
|
|
23
|
+
callback: GenericCallback;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type InternalComputed = {
|
|
27
|
+
readonly effect: ComputedEffect;
|
|
28
|
+
readonly state: ReactiveState<unknown, unknown>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type InternalEffect = {
|
|
32
|
+
state: EffectState;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type ReactiveOptions<Value> = {
|
|
36
|
+
/**
|
|
37
|
+
* Method for comparing values for equality
|
|
38
|
+
* @param first First value
|
|
39
|
+
* @param second Second value
|
|
40
|
+
* @returns Are the values equal?
|
|
41
|
+
* @default Object.is
|
|
42
|
+
*/
|
|
43
|
+
equal?: (first: Value, second: Value) => boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type ReactiveState<Value, Equal> = {
|
|
47
|
+
computeds: Set<Computed<unknown>>;
|
|
48
|
+
effects: Set<Effect>;
|
|
49
|
+
equal: (first: Equal, second: Equal) => boolean;
|
|
50
|
+
subscriptions: Map<GenericCallback, Subscription>;
|
|
51
|
+
value: Value;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type SetValueInProxyParameters<Value, Equal> = {
|
|
55
|
+
target: Value;
|
|
56
|
+
property: PropertyKey;
|
|
57
|
+
value: unknown;
|
|
58
|
+
state: ReactiveState<Value, Equal>;
|
|
59
|
+
isArray: boolean;
|
|
60
|
+
length?: Signal<number>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Unsubscribe from changes
|
|
65
|
+
*/
|
|
66
|
+
export type Unsubscribe = () => void;
|
package/src/subscription.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
2
|
-
import type {ReactiveState} from './
|
|
2
|
+
import type {ReactiveState, Unsubscribe} from './models';
|
|
3
3
|
|
|
4
4
|
export class Subscription {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
) {
|
|
5
|
+
callback: GenericCallback;
|
|
6
|
+
state: ReactiveState<unknown, never>;
|
|
7
|
+
|
|
8
|
+
constructor(state: ReactiveState<unknown, never>, callback: GenericCallback) {
|
|
9
|
+
this.state = state;
|
|
10
|
+
this.callback = callback;
|
|
11
|
+
|
|
9
12
|
callback(state.value);
|
|
10
13
|
}
|
|
11
|
-
}
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
destroy(): void {
|
|
16
|
+
this.callback = noop;
|
|
17
|
+
this.state = undefined as never;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
export function noop(): void {}
|
|
19
22
|
|
|
@@ -36,12 +39,7 @@ export function unsubscribe<Value>(
|
|
|
36
39
|
state: ReactiveState<Value, never>,
|
|
37
40
|
callback: (value: Value) => void,
|
|
38
41
|
): void {
|
|
39
|
-
|
|
42
|
+
state.subscriptions.get(callback)?.destroy();
|
|
40
43
|
|
|
41
44
|
state.subscriptions.delete(callback);
|
|
42
|
-
|
|
43
|
-
if (subscription != null) {
|
|
44
|
-
subscription.callback = noop;
|
|
45
|
-
subscription.state = undefined as never;
|
|
46
|
-
}
|
|
47
45
|
}
|