@yaasl/core 0.9.2 → 0.10.0-alpha.1
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/@types/base/config.d.ts +3 -0
- package/dist/@types/base/createActions.d.ts +1 -1
- package/dist/@types/base/createAtom.d.ts +9 -13
- package/dist/@types/base/createDerived.d.ts +4 -4
- package/dist/@types/base/createSelector.d.ts +13 -12
- package/dist/@types/base/createSlice.d.ts +35 -0
- package/dist/@types/base/index.d.ts +2 -0
- package/dist/@types/effects/EffectDispatcher.d.ts +11 -11
- package/dist/@types/effects/createEffect.d.ts +14 -5
- package/dist/@types/effects/index.d.ts +1 -1
- package/dist/@types/effects/indexedDb.d.ts +1 -1
- package/dist/@types/effects/localStorage.d.ts +1 -1
- package/dist/@types/utils/Queue.d.ts +8 -0
- package/dist/cjs/base/config.js +1 -0
- package/dist/cjs/base/createActions.js +1 -1
- package/dist/cjs/base/createAtom.js +32 -10
- package/dist/cjs/base/createDerived.js +2 -2
- package/dist/cjs/base/createSlice.js +38 -0
- package/dist/cjs/base/index.js +2 -0
- package/dist/cjs/effects/EffectDispatcher.js +38 -28
- package/dist/cjs/effects/createEffect.js +1 -1
- package/dist/cjs/effects/indexedDb.js +11 -12
- package/dist/cjs/effects/localStorage.js +9 -7
- package/dist/cjs/effects/migration.js +2 -2
- package/dist/cjs/utils/Queue.js +27 -0
- package/dist/mjs/base/Stateful.js +4 -3
- package/dist/mjs/base/config.js +1 -0
- package/dist/mjs/base/createActions.js +1 -1
- package/dist/mjs/base/createAtom.js +37 -10
- package/dist/mjs/base/createDerived.js +9 -7
- package/dist/mjs/base/createSlice.js +34 -0
- package/dist/mjs/base/index.js +2 -0
- package/dist/mjs/effects/EffectDispatcher.js +40 -28
- package/dist/mjs/effects/createEffect.js +1 -1
- package/dist/mjs/effects/indexedDb.js +8 -9
- package/dist/mjs/effects/localStorage.js +9 -7
- package/dist/mjs/effects/migration.js +2 -2
- package/dist/mjs/utils/Expiration.js +9 -7
- package/dist/mjs/utils/LocalStorage.js +2 -0
- package/dist/mjs/utils/Queue.js +21 -0
- package/dist/mjs/utils/Store.js +2 -0
- package/package.json +3 -3
- package/dist/@types/utils/Scheduler.d.ts +0 -7
- package/dist/cjs/utils/Scheduler.js +0 -27
- package/dist/mjs/utils/Scheduler.js +0 -23
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EffectAtomCallback } from "../effects/createEffect";
|
|
1
2
|
interface Config {
|
|
2
3
|
/** Global name to make internal keys unique
|
|
3
4
|
* among UIs on the same domain.
|
|
@@ -6,6 +7,8 @@ interface Config {
|
|
|
6
7
|
* "{config-name}/{atom-name}")
|
|
7
8
|
**/
|
|
8
9
|
name?: string;
|
|
10
|
+
/** Global effects to apply on all atoms. (e.g. reduxDevtools) */
|
|
11
|
+
globalEffects: EffectAtomCallback<any>[];
|
|
9
12
|
}
|
|
10
13
|
/** Global configuration object to change internal behavior of yaasl.
|
|
11
14
|
*
|
|
@@ -12,7 +12,7 @@ export type Actions<State, R extends Reducers<State>> = {
|
|
|
12
12
|
/** Create actions to change the state of an atom.
|
|
13
13
|
*
|
|
14
14
|
* @param atom Atom to be used.
|
|
15
|
-
* @param reducers Reducers for custom actions to set the
|
|
15
|
+
* @param reducers Reducers for custom actions to set the atom's value.
|
|
16
16
|
*
|
|
17
17
|
* @returns Actions to change the state of the atom.
|
|
18
18
|
*
|
|
@@ -1,39 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Actions, Reducers } from "./createActions";
|
|
1
|
+
import { Updater } from "@yaasl/utils";
|
|
3
2
|
import { Stateful } from "./Stateful";
|
|
4
3
|
import { EffectAtomCallback } from "../effects/createEffect";
|
|
5
|
-
export interface AtomConfig<Value
|
|
4
|
+
export interface AtomConfig<Value> {
|
|
6
5
|
/** Value that will be used initially. */
|
|
7
6
|
defaultValue: Value;
|
|
8
7
|
/** Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}". */
|
|
9
8
|
name?: string;
|
|
10
9
|
/** Effects that will be applied on the atom. */
|
|
11
|
-
effects?: EffectAtomCallback<any>[];
|
|
12
|
-
/** Reducers for custom actions to set the atom's value. */
|
|
13
|
-
reducers?: R;
|
|
10
|
+
effects?: EffectAtomCallback<any, any>[];
|
|
14
11
|
}
|
|
15
|
-
export declare class Atom<Value = unknown
|
|
12
|
+
export declare class Atom<Value = unknown> extends Stateful<Value> {
|
|
16
13
|
/** Default value of the atom. */
|
|
17
14
|
readonly defaultValue: Value;
|
|
18
15
|
/** Identifier of the atom. */
|
|
19
16
|
readonly name: string;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
private effects;
|
|
18
|
+
constructor({ defaultValue, name, effects: localEffects, }: AtomConfig<Value>);
|
|
19
|
+
private initEffects;
|
|
23
20
|
/** Set the value of the atom.
|
|
24
21
|
*
|
|
25
22
|
* @param next New value or function to create the
|
|
26
23
|
* new value based off the previous value.
|
|
27
24
|
*/
|
|
28
|
-
set(next:
|
|
25
|
+
set(next: Updater<Value>): void;
|
|
29
26
|
}
|
|
30
27
|
/** Creates an atom store.
|
|
31
28
|
*
|
|
32
29
|
* @param config.defaultValue Value that will be used initially.
|
|
33
30
|
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
34
31
|
* @param config.effects Effects that will be applied on the atom.
|
|
35
|
-
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
36
32
|
*
|
|
37
33
|
* @returns An atom instance.
|
|
38
34
|
**/
|
|
39
|
-
export declare const createAtom: <Value
|
|
35
|
+
export declare const createAtom: <Value>(config: AtomConfig<Value>) => Atom<Value>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Updater } from "@yaasl/utils";
|
|
2
2
|
import { Atom } from "./createAtom";
|
|
3
3
|
import { Stateful } from "./Stateful";
|
|
4
4
|
type GetterFn<Value> = (context: {
|
|
@@ -6,7 +6,7 @@ type GetterFn<Value> = (context: {
|
|
|
6
6
|
}) => Value;
|
|
7
7
|
type SetterFn<Value> = (context: {
|
|
8
8
|
value: Value;
|
|
9
|
-
set: <V>(dep: Atom<V> | SettableDerive<V>, next:
|
|
9
|
+
set: <V>(dep: Atom<V> | SettableDerive<V>, next: Updater<V>) => void;
|
|
10
10
|
}) => void;
|
|
11
11
|
export declare class Derive<Value> extends Stateful<Value> {
|
|
12
12
|
private readonly getter;
|
|
@@ -17,14 +17,14 @@ export declare class Derive<Value> extends Stateful<Value> {
|
|
|
17
17
|
}
|
|
18
18
|
export declare class SettableDerive<Value = unknown> extends Derive<Value> {
|
|
19
19
|
private readonly setter;
|
|
20
|
-
protected readonly setterDependencies: Set<Atom<any
|
|
20
|
+
protected readonly setterDependencies: Set<Atom<any> | SettableDerive<any>>;
|
|
21
21
|
constructor(getter: GetterFn<Value>, setter: SetterFn<Value>);
|
|
22
22
|
/** Set the value of the derived atom.
|
|
23
23
|
*
|
|
24
24
|
* @param next New value or function to create the
|
|
25
25
|
* new value based off the previous value.
|
|
26
26
|
*/
|
|
27
|
-
set(next:
|
|
27
|
+
set(next: Updater<Value>): void;
|
|
28
28
|
private addSetDependency;
|
|
29
29
|
private compareDependencies;
|
|
30
30
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import { Prettify } from "@yaasl/utils";
|
|
1
2
|
import { Stateful } from "./Stateful";
|
|
2
|
-
type
|
|
3
|
-
type
|
|
4
|
-
[K in keyof
|
|
5
|
-
}[keyof
|
|
6
|
-
type
|
|
7
|
-
export declare class PathSelector<
|
|
8
|
-
constructor(atom: Stateful<
|
|
3
|
+
type PathableValue = Record<string | number, unknown>;
|
|
4
|
+
export type ObjPath<Obj> = Prettify<Obj> extends PathableValue ? {
|
|
5
|
+
[K in keyof Obj]: `${Exclude<K, symbol>}${"" | `.${ObjPath<Obj[K]>}`}`;
|
|
6
|
+
}[keyof Obj] : never;
|
|
7
|
+
type ObjPathValue<State, Path> = State extends PathableValue ? Path extends `${infer Current}.${infer Next}` ? ObjPathValue<State[Current], Next> : State[Path & string] : never;
|
|
8
|
+
export declare class PathSelector<ParentValue, Path extends ObjPath<ParentValue>> extends Stateful<ObjPathValue<ParentValue, Path>> {
|
|
9
|
+
constructor(atom: Stateful<ParentValue>, path: Path);
|
|
9
10
|
}
|
|
10
|
-
type InferValuesFromAtoms<
|
|
11
|
-
export declare class CombinerSelector<
|
|
12
|
-
constructor(atoms:
|
|
11
|
+
type InferValuesFromAtoms<ParentAtoms extends readonly unknown[], ParentValues extends unknown[] = []> = ParentAtoms extends [Stateful<infer Value>, ...infer Rest] ? InferValuesFromAtoms<Rest, [...ParentValues, Value]> : ParentValues;
|
|
12
|
+
export declare class CombinerSelector<ParentAtoms extends [Stateful<any>, ...Stateful<any>[]], CombinedValue> extends Stateful<CombinedValue> {
|
|
13
|
+
constructor(atoms: ParentAtoms, combiner: (...res: InferValuesFromAtoms<ParentAtoms>) => CombinedValue);
|
|
13
14
|
}
|
|
14
15
|
interface CreateSelectorOverloads {
|
|
15
16
|
/** Creates a value, selected from one atom with an object value by using a key path.
|
|
@@ -19,7 +20,7 @@ interface CreateSelectorOverloads {
|
|
|
19
20
|
*
|
|
20
21
|
* @returns A PathSelector instance.
|
|
21
22
|
**/
|
|
22
|
-
<
|
|
23
|
+
<ParentValue, Path extends ObjPath<ParentValue>>(atom: Stateful<ParentValue>, path: Path): PathSelector<ParentValue, Path>;
|
|
23
24
|
/** Creates a value, selected from one atom with an object value by using a key path.
|
|
24
25
|
*
|
|
25
26
|
* @param atoms Atoms you need to combine to receive the new value.
|
|
@@ -27,7 +28,7 @@ interface CreateSelectorOverloads {
|
|
|
27
28
|
*
|
|
28
29
|
* @returns A CombinerSelector instance.
|
|
29
30
|
**/
|
|
30
|
-
<
|
|
31
|
+
<ParentAtoms extends [Stateful<any>, ...Stateful<any>[]], CombinedValue>(states: ParentAtoms, combiner: (...res: InferValuesFromAtoms<ParentAtoms>) => CombinedValue): CombinerSelector<ParentAtoms, CombinedValue>;
|
|
31
32
|
}
|
|
32
33
|
export declare const createSelector: CreateSelectorOverloads;
|
|
33
34
|
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Actions, Reducers } from "./createActions";
|
|
2
|
+
import { Atom, AtomConfig } from "./createAtom";
|
|
3
|
+
import { CombinerSelector, ObjPath, PathSelector } from "./createSelector";
|
|
4
|
+
interface ReducersProp<State, R extends Reducers<State> | undefined> {
|
|
5
|
+
/** Reducers for custom actions to set the atom's value. */
|
|
6
|
+
reducers?: R;
|
|
7
|
+
}
|
|
8
|
+
type Selectors<State> = Record<string, ObjPath<State> | ((state: State) => any)>;
|
|
9
|
+
type ConditionalActions<State, R> = keyof R extends never ? {} : R extends Reducers<State> ? {
|
|
10
|
+
/** Actions that can be used to set the atom's value. */
|
|
11
|
+
actions: Actions<State, R>;
|
|
12
|
+
} : {};
|
|
13
|
+
interface SelectorsProp<State, S extends Selectors<State> | undefined> {
|
|
14
|
+
/** Selectors to create values from the atom. */
|
|
15
|
+
selectors?: S;
|
|
16
|
+
}
|
|
17
|
+
type GetSelector<State, S extends ObjPath<State> | ((state: State) => any)> = S extends ObjPath<State> ? PathSelector<State, S> : S extends (state: State) => any ? CombinerSelector<[Atom<State>], ReturnType<S>> : never;
|
|
18
|
+
type ConditionalSelectors<State, S> = keyof S extends never ? {} : S extends Selectors<State> ? {
|
|
19
|
+
/** Selectors to create new values based on the atom's value. */
|
|
20
|
+
selectors: {
|
|
21
|
+
[K in keyof S]: GetSelector<State, S[K]>;
|
|
22
|
+
};
|
|
23
|
+
} : {};
|
|
24
|
+
/** Creates a slice with actions and selectors.
|
|
25
|
+
*
|
|
26
|
+
* @param config.defaultValue Value that will be used initially.
|
|
27
|
+
* @param config.name Name of the atom.
|
|
28
|
+
* @param config.effects Effects that will be applied on the atom.
|
|
29
|
+
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
30
|
+
* @param config.selectors Path or combiner selectors to use the atom's values to create new ones.
|
|
31
|
+
*
|
|
32
|
+
* @returns An atom instance with actions and selectors.
|
|
33
|
+
**/
|
|
34
|
+
export declare const createSlice: <State, R extends Reducers<State> | undefined, S extends Selectors<State> | undefined>(config: AtomConfig<State> & ReducersProp<State, R> & SelectorsProp<State, S>) => Atom<State> & ConditionalActions<State, R> & ConditionalSelectors<State, S>;
|
|
35
|
+
export {};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { EffectAtomCallback } from "./createEffect";
|
|
1
|
+
import { ActionType, EffectAtomCallback } from "./createEffect";
|
|
2
2
|
import { Atom } from "../base/createAtom";
|
|
3
|
-
|
|
4
|
-
atom
|
|
5
|
-
|
|
3
|
+
export declare class EffectActions<Value> {
|
|
4
|
+
private readonly atom;
|
|
5
|
+
private options;
|
|
6
|
+
private actions;
|
|
7
|
+
constructor(atom: Atom<Value>, effectCreator: EffectAtomCallback<unknown, Value>);
|
|
8
|
+
createAction(action: ActionType): ((prev: Value) => Value | PromiseLike<Value>) | undefined;
|
|
6
9
|
}
|
|
7
|
-
export declare class EffectDispatcher {
|
|
8
|
-
didInit: PromiseLike<void> | boolean;
|
|
10
|
+
export declare class EffectDispatcher<Value> {
|
|
9
11
|
private effects;
|
|
10
|
-
private
|
|
11
|
-
constructor(
|
|
12
|
-
|
|
13
|
-
private callEffectAction;
|
|
12
|
+
private queue;
|
|
13
|
+
constructor(atom: Atom<Value>, effects: EffectAtomCallback<unknown, Value>[]);
|
|
14
|
+
dispatch(action: ActionType, startValue: Value): PromiseLike<Value>;
|
|
14
15
|
}
|
|
15
|
-
export {};
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
+
import { Updater, Dispatch } from "@yaasl/utils";
|
|
1
2
|
import { Atom } from "../base";
|
|
2
3
|
export type ActionType = "init" | "didInit" | "set";
|
|
3
|
-
interface EffectPayload<Options, AtomValue> {
|
|
4
|
+
export interface EffectPayload<Options = undefined, AtomValue = any> {
|
|
5
|
+
/** Current value of the atom */
|
|
4
6
|
value: AtomValue;
|
|
7
|
+
/** Function to set the value of the atom */
|
|
8
|
+
set: Dispatch<Updater<AtomValue>>;
|
|
9
|
+
/** The atom which the effect is applied on */
|
|
5
10
|
atom: Atom<AtomValue>;
|
|
11
|
+
/** Options passed to the effect */
|
|
6
12
|
options: Options;
|
|
7
13
|
}
|
|
8
|
-
interface EffectActions<Options, AtomValue> {
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
export interface EffectActions<Options, AtomValue> {
|
|
15
|
+
/** Action to be called when the atom is created */
|
|
16
|
+
init?: (payload: EffectPayload<Options, AtomValue>) => PromiseLike<any> | void;
|
|
17
|
+
/** Action to be called after the init phase */
|
|
18
|
+
didInit?: (payload: EffectPayload<Options, AtomValue>) => PromiseLike<any> | void;
|
|
19
|
+
/** Action to be called when the atom's value is set */
|
|
11
20
|
set?: (payload: EffectPayload<Options, AtomValue>) => void;
|
|
12
21
|
}
|
|
13
22
|
interface EffectSetupProps<Options, AtomValue> {
|
|
@@ -28,7 +37,7 @@ export interface Effect<Options = unknown, AtomValue = unknown> {
|
|
|
28
37
|
* May return a promise that can be awaited by using `atom.didInit`.
|
|
29
38
|
* - `didInit`: Action to be called when the atom is created, but after subscribing to `set` events.
|
|
30
39
|
* May return a promise that can be awaited by using `atom.didInit`.
|
|
31
|
-
* - `set`: Action to be called when the atom's
|
|
40
|
+
* - `set`: Action to be called when the atom's value is set.
|
|
32
41
|
*
|
|
33
42
|
* @param setup Effect actions or function to create effect actions.
|
|
34
43
|
* Effect actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createEffect } from "./createEffect";
|
|
2
|
-
export type { Effect } from "./createEffect";
|
|
2
|
+
export type { Effect, EffectPayload } from "./createEffect";
|
|
3
3
|
export { localStorage } from "./localStorage";
|
|
4
4
|
export type { LocalStorageOptions, LocalStorageParser } from "./localStorage";
|
|
5
5
|
export { indexedDb } from "./indexedDb";
|
|
@@ -12,4 +12,4 @@ export interface IndexedDbOptions {
|
|
|
12
12
|
*
|
|
13
13
|
* @returns The effect to be used on atoms.
|
|
14
14
|
**/
|
|
15
|
-
export declare const indexedDb: (...[optionsArg]: [] | [undefined] | [IndexedDbOptions]) => import("./createEffect").EffectAtomCallback<IndexedDbOptions | undefined,
|
|
15
|
+
export declare const indexedDb: (...[optionsArg]: [] | [undefined] | [IndexedDbOptions]) => import("./createEffect").EffectAtomCallback<IndexedDbOptions | undefined, unknown>;
|
|
@@ -25,4 +25,4 @@ export interface LocalStorageOptions {
|
|
|
25
25
|
*
|
|
26
26
|
* @returns The effect to be used on atoms.
|
|
27
27
|
**/
|
|
28
|
-
export declare const localStorage: (...[optionsArg]: [] | [undefined] | [LocalStorageOptions]) => import("./createEffect").EffectAtomCallback<LocalStorageOptions | undefined,
|
|
28
|
+
export declare const localStorage: (...[optionsArg]: [] | [undefined] | [LocalStorageOptions]) => import("./createEffect").EffectAtomCallback<LocalStorageOptions | undefined, unknown>;
|
package/dist/cjs/base/config.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.createActions = void 0;
|
|
|
4
4
|
/** Create actions to change the state of an atom.
|
|
5
5
|
*
|
|
6
6
|
* @param atom Atom to be used.
|
|
7
|
-
* @param reducers Reducers for custom actions to set the
|
|
7
|
+
* @param reducers Reducers for custom actions to set the atom's value.
|
|
8
8
|
*
|
|
9
9
|
* @returns Actions to change the state of the atom.
|
|
10
10
|
*
|
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createAtom = exports.Atom = void 0;
|
|
4
|
-
const
|
|
4
|
+
const utils_1 = require("@yaasl/utils");
|
|
5
|
+
const config_1 = require("./config");
|
|
5
6
|
const Stateful_1 = require("./Stateful");
|
|
6
7
|
const EffectDispatcher_1 = require("../effects/EffectDispatcher");
|
|
7
8
|
let key = 0;
|
|
8
9
|
class Atom extends Stateful_1.Stateful {
|
|
9
|
-
constructor({ defaultValue, name = `atom-${++key}`, effects
|
|
10
|
+
constructor({ defaultValue, name = `atom-${++key}`, effects: localEffects = [], }) {
|
|
10
11
|
super(defaultValue);
|
|
11
12
|
this.name = name;
|
|
12
13
|
this.defaultValue = defaultValue;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const effects = [
|
|
15
|
+
...config_1.CONFIG.globalEffects,
|
|
16
|
+
...localEffects,
|
|
17
|
+
];
|
|
18
|
+
this.effects = new EffectDispatcher_1.EffectDispatcher(this, effects);
|
|
19
|
+
if (effects.length === 0) {
|
|
20
|
+
this.setDidInit(true);
|
|
16
21
|
return;
|
|
17
22
|
}
|
|
18
|
-
const
|
|
19
|
-
|
|
23
|
+
const result = this.initEffects();
|
|
24
|
+
if (result instanceof utils_1.Thenable) {
|
|
25
|
+
this.setDidInit(true);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.setDidInit(result.then(utils_1.toVoid));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
initEffects() {
|
|
32
|
+
const updateValue = (value) => {
|
|
33
|
+
super.update(value);
|
|
34
|
+
return value;
|
|
35
|
+
};
|
|
36
|
+
return new utils_1.Thenable(this.defaultValue)
|
|
37
|
+
.then(value => this.effects.dispatch("init", value))
|
|
38
|
+
.then(updateValue)
|
|
39
|
+
.then(value => this.effects.dispatch("didInit", value))
|
|
40
|
+
.then(updateValue);
|
|
20
41
|
}
|
|
21
42
|
/** Set the value of the atom.
|
|
22
43
|
*
|
|
@@ -24,8 +45,10 @@ class Atom extends Stateful_1.Stateful {
|
|
|
24
45
|
* new value based off the previous value.
|
|
25
46
|
*/
|
|
26
47
|
set(next) {
|
|
27
|
-
const
|
|
28
|
-
|
|
48
|
+
const newState = (0, utils_1.updater)(next, this.get());
|
|
49
|
+
void this.effects
|
|
50
|
+
.dispatch("set", newState)
|
|
51
|
+
.then(value => super.update(value));
|
|
29
52
|
}
|
|
30
53
|
}
|
|
31
54
|
exports.Atom = Atom;
|
|
@@ -34,7 +57,6 @@ exports.Atom = Atom;
|
|
|
34
57
|
* @param config.defaultValue Value that will be used initially.
|
|
35
58
|
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
36
59
|
* @param config.effects Effects that will be applied on the atom.
|
|
37
|
-
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
38
60
|
*
|
|
39
61
|
* @returns An atom instance.
|
|
40
62
|
**/
|
|
@@ -50,11 +50,11 @@ class SettableDerive extends Derive {
|
|
|
50
50
|
* new value based off the previous value.
|
|
51
51
|
*/
|
|
52
52
|
set(next) {
|
|
53
|
-
const value = next
|
|
53
|
+
const value = (0, utils_1.updater)(next, this.get());
|
|
54
54
|
this.setter({
|
|
55
55
|
value,
|
|
56
56
|
set: (atom, next) => {
|
|
57
|
-
const value = next
|
|
57
|
+
const value = (0, utils_1.updater)(next, atom.get());
|
|
58
58
|
atom.set(value);
|
|
59
59
|
},
|
|
60
60
|
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSlice = void 0;
|
|
4
|
+
const createActions_1 = require("./createActions");
|
|
5
|
+
const createAtom_1 = require("./createAtom");
|
|
6
|
+
const createSelector_1 = require("./createSelector");
|
|
7
|
+
const isEmpty = (obj) => !obj || Object.keys(obj).length === 0;
|
|
8
|
+
const createSelectors = (atom, selectors) => Object.fromEntries(Object.entries(selectors).map(([key, selector]) => [
|
|
9
|
+
key,
|
|
10
|
+
typeof selector === "string"
|
|
11
|
+
? (0, createSelector_1.createSelector)(atom, selector)
|
|
12
|
+
: (0, createSelector_1.createSelector)([atom], selector),
|
|
13
|
+
]));
|
|
14
|
+
/** Creates a slice with actions and selectors.
|
|
15
|
+
*
|
|
16
|
+
* @param config.defaultValue Value that will be used initially.
|
|
17
|
+
* @param config.name Name of the atom.
|
|
18
|
+
* @param config.effects Effects that will be applied on the atom.
|
|
19
|
+
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
20
|
+
* @param config.selectors Path or combiner selectors to use the atom's values to create new ones.
|
|
21
|
+
*
|
|
22
|
+
* @returns An atom instance with actions and selectors.
|
|
23
|
+
**/
|
|
24
|
+
const createSlice = (config) => {
|
|
25
|
+
const atom = new createAtom_1.Atom(config);
|
|
26
|
+
const actionsProp = isEmpty(config.reducers)
|
|
27
|
+
? {}
|
|
28
|
+
: {
|
|
29
|
+
actions: (0, createActions_1.createActions)(atom, config.reducers),
|
|
30
|
+
};
|
|
31
|
+
const selectorsProp = isEmpty(config.selectors)
|
|
32
|
+
? {}
|
|
33
|
+
: {
|
|
34
|
+
selectors: createSelectors(atom, config.selectors),
|
|
35
|
+
};
|
|
36
|
+
return Object.assign(atom, actionsProp, selectorsProp);
|
|
37
|
+
};
|
|
38
|
+
exports.createSlice = createSlice;
|
package/dist/cjs/base/index.js
CHANGED
|
@@ -15,7 +15,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./config"), exports);
|
|
18
|
+
__exportStar(require("./createActions"), exports);
|
|
18
19
|
__exportStar(require("./createAtom"), exports);
|
|
19
20
|
__exportStar(require("./createDerived"), exports);
|
|
20
21
|
__exportStar(require("./createSelector"), exports);
|
|
22
|
+
__exportStar(require("./createSlice"), exports);
|
|
21
23
|
__exportStar(require("./Stateful"), exports);
|
|
@@ -1,37 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EffectDispatcher = void 0;
|
|
3
|
+
exports.EffectDispatcher = exports.EffectActions = void 0;
|
|
4
4
|
const utils_1 = require("@yaasl/utils");
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
17
|
-
this.didInit = true;
|
|
5
|
+
const Queue_1 = require("../utils/Queue");
|
|
6
|
+
const isTruthy = (value) => !!value;
|
|
7
|
+
class EffectActions {
|
|
8
|
+
constructor(atom, effectCreator) {
|
|
9
|
+
this.atom = atom;
|
|
10
|
+
const { actions, options } = effectCreator(atom);
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.actions = actions;
|
|
13
|
+
}
|
|
14
|
+
createAction(action) {
|
|
15
|
+
const actionFn = this.actions[action];
|
|
16
|
+
if (!actionFn)
|
|
18
17
|
return;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
return (prev) => {
|
|
19
|
+
const nextValue = { current: prev };
|
|
20
|
+
const set = (next) => (nextValue.current = (0, utils_1.updater)(next, nextValue.current));
|
|
21
|
+
const result = actionFn({
|
|
22
|
+
value: prev,
|
|
23
|
+
set,
|
|
24
|
+
atom: this.atom,
|
|
25
|
+
options: this.options,
|
|
26
|
+
});
|
|
27
|
+
return (0, utils_1.isPromiseLike)(result)
|
|
28
|
+
? result.then(() => nextValue.current)
|
|
29
|
+
: nextValue.current;
|
|
30
|
+
};
|
|
23
31
|
}
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
}
|
|
33
|
+
exports.EffectActions = EffectActions;
|
|
34
|
+
class EffectDispatcher {
|
|
35
|
+
constructor(atom, effects) {
|
|
36
|
+
this.effects = [];
|
|
37
|
+
this.queue = new Queue_1.Queue();
|
|
38
|
+
this.effects = effects.map(create => new EffectActions(atom, create));
|
|
26
39
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return () => actionFn === null || actionFn === void 0 ? void 0 : actionFn({ value: getValue(), atom, options });
|
|
33
|
-
});
|
|
34
|
-
void this.scheduler.run(tasks);
|
|
40
|
+
dispatch(action, startValue) {
|
|
41
|
+
const tasks = this.effects
|
|
42
|
+
.map(effect => effect.createAction(action))
|
|
43
|
+
.filter(isTruthy);
|
|
44
|
+
return this.queue.push(...tasks).run(startValue);
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
exports.EffectDispatcher = EffectDispatcher;
|
|
@@ -9,7 +9,7 @@ exports.createEffect = void 0;
|
|
|
9
9
|
* May return a promise that can be awaited by using `atom.didInit`.
|
|
10
10
|
* - `didInit`: Action to be called when the atom is created, but after subscribing to `set` events.
|
|
11
11
|
* May return a promise that can be awaited by using `atom.didInit`.
|
|
12
|
-
* - `set`: Action to be called when the atom's
|
|
12
|
+
* - `set`: Action to be called when the atom's value is set.
|
|
13
13
|
*
|
|
14
14
|
* @param setup Effect actions or function to create effect actions.
|
|
15
15
|
* Effect actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
@@ -28,20 +28,19 @@ exports.indexedDb = (0, createEffect_1.createEffect)(({ atom, options }) => {
|
|
|
28
28
|
var _a;
|
|
29
29
|
const key = (_a = options === null || options === void 0 ? void 0 : options.key) !== null && _a !== void 0 ? _a : atom.name;
|
|
30
30
|
return {
|
|
31
|
-
init: ({ atom })
|
|
32
|
-
var
|
|
31
|
+
init: (_b) => __awaiter(void 0, [_b], void 0, function* ({ atom, set }) {
|
|
32
|
+
var _c;
|
|
33
33
|
if (!atomDb) {
|
|
34
|
-
atomDb = new Store_1.Store((
|
|
34
|
+
atomDb = new Store_1.Store((_c = base_1.CONFIG.name) !== null && _c !== void 0 ? _c : "yaasl");
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
36
|
+
const existing = yield atomDb.get(key);
|
|
37
|
+
if (existing != null) {
|
|
38
|
+
set(existing);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
yield atomDb.set(key, atom.defaultValue);
|
|
42
|
+
}
|
|
43
|
+
}),
|
|
45
44
|
set: ({ value }) => {
|
|
46
45
|
void (atomDb === null || atomDb === void 0 ? void 0 : atomDb.set(key, value));
|
|
47
46
|
},
|
|
@@ -22,17 +22,19 @@ exports.localStorage = (0, createEffect_1.createEffect)(({ atom, options = {} })
|
|
|
22
22
|
onTabSync: noTabSync ? undefined : value => atom.set(value),
|
|
23
23
|
});
|
|
24
24
|
return {
|
|
25
|
-
init: ({
|
|
25
|
+
init: ({ set }) => {
|
|
26
26
|
const existing = storage.get();
|
|
27
|
-
if (existing
|
|
28
|
-
|
|
27
|
+
if (existing != null) {
|
|
28
|
+
set(existing);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
set: ({ value, atom }) => {
|
|
32
|
+
if (value === atom.defaultValue) {
|
|
33
|
+
storage.remove();
|
|
29
34
|
}
|
|
30
35
|
else {
|
|
31
|
-
|
|
36
|
+
storage.set(value);
|
|
32
37
|
}
|
|
33
38
|
},
|
|
34
|
-
set: ({ value }) => {
|
|
35
|
-
storage.set(value);
|
|
36
|
-
},
|
|
37
39
|
};
|
|
38
40
|
});
|
|
@@ -69,7 +69,7 @@ const performMigration = (atom, version, migrations) => {
|
|
|
69
69
|
* @returns The effect to be used on atoms.
|
|
70
70
|
**/
|
|
71
71
|
exports.migration = (0, createEffect_1.createEffect)({
|
|
72
|
-
didInit: ({ atom, options }) => {
|
|
72
|
+
didInit: ({ atom, options, set }) => {
|
|
73
73
|
var _a, _b;
|
|
74
74
|
const steps = sortMigrations(options.steps);
|
|
75
75
|
const currentVersion = getVersion(atom);
|
|
@@ -94,7 +94,7 @@ exports.migration = (0, createEffect_1.createEffect)({
|
|
|
94
94
|
if (version == null)
|
|
95
95
|
return;
|
|
96
96
|
setVersion(atom, version);
|
|
97
|
-
|
|
97
|
+
set(data);
|
|
98
98
|
},
|
|
99
99
|
});
|
|
100
100
|
/** Helper to create a step for the migration effect.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Queue = void 0;
|
|
4
|
+
const utils_1 = require("@yaasl/utils");
|
|
5
|
+
class Queue {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.last = null;
|
|
8
|
+
this.queue = [];
|
|
9
|
+
}
|
|
10
|
+
push(...tasks) {
|
|
11
|
+
this.queue.push(...tasks);
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
run(prev) {
|
|
15
|
+
const init = this.last ? this.last.then(() => prev) : new utils_1.Thenable(prev);
|
|
16
|
+
const result = this.queue.reduce((result, next) => result.then(prev => next(prev)), init);
|
|
17
|
+
this.queue = [];
|
|
18
|
+
this.last = result.then(value => {
|
|
19
|
+
if (this.last === result) {
|
|
20
|
+
this.last = null;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
});
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.Queue = Queue;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export class Stateful {
|
|
2
|
+
value;
|
|
3
|
+
/** Promise that resolves when the states initialization was finished. */
|
|
4
|
+
didInit = false;
|
|
5
|
+
listeners = new Set();
|
|
2
6
|
constructor(value) {
|
|
3
7
|
this.value = value;
|
|
4
|
-
/** Promise that resolves when the states initialization was finished. */
|
|
5
|
-
this.didInit = false;
|
|
6
|
-
this.listeners = new Set();
|
|
7
8
|
}
|
|
8
9
|
/** Read the value of state.
|
|
9
10
|
*
|
package/dist/mjs/base/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Create actions to change the state of an atom.
|
|
2
2
|
*
|
|
3
3
|
* @param atom Atom to be used.
|
|
4
|
-
* @param reducers Reducers for custom actions to set the
|
|
4
|
+
* @param reducers Reducers for custom actions to set the atom's value.
|
|
5
5
|
*
|
|
6
6
|
* @returns Actions to change the state of the atom.
|
|
7
7
|
*
|
|
@@ -1,19 +1,45 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { updater, Thenable, toVoid } from "@yaasl/utils";
|
|
2
|
+
import { CONFIG } from "./config";
|
|
2
3
|
import { Stateful } from "./Stateful";
|
|
3
4
|
import { EffectDispatcher } from "../effects/EffectDispatcher";
|
|
4
5
|
let key = 0;
|
|
5
6
|
export class Atom extends Stateful {
|
|
6
|
-
|
|
7
|
+
/** Default value of the atom. */
|
|
8
|
+
defaultValue;
|
|
9
|
+
/** Identifier of the atom. */
|
|
10
|
+
name;
|
|
11
|
+
effects;
|
|
12
|
+
constructor({ defaultValue, name = `atom-${++key}`, effects: localEffects = [], }) {
|
|
7
13
|
super(defaultValue);
|
|
8
14
|
this.name = name;
|
|
9
15
|
this.defaultValue = defaultValue;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
const effects = [
|
|
17
|
+
...CONFIG.globalEffects,
|
|
18
|
+
...localEffects,
|
|
19
|
+
];
|
|
20
|
+
this.effects = new EffectDispatcher(this, effects);
|
|
21
|
+
if (effects.length === 0) {
|
|
22
|
+
this.setDidInit(true);
|
|
13
23
|
return;
|
|
14
24
|
}
|
|
15
|
-
const
|
|
16
|
-
|
|
25
|
+
const result = this.initEffects();
|
|
26
|
+
if (result instanceof Thenable) {
|
|
27
|
+
this.setDidInit(true);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
this.setDidInit(result.then(toVoid));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
initEffects() {
|
|
34
|
+
const updateValue = (value) => {
|
|
35
|
+
super.update(value);
|
|
36
|
+
return value;
|
|
37
|
+
};
|
|
38
|
+
return new Thenable(this.defaultValue)
|
|
39
|
+
.then(value => this.effects.dispatch("init", value))
|
|
40
|
+
.then(updateValue)
|
|
41
|
+
.then(value => this.effects.dispatch("didInit", value))
|
|
42
|
+
.then(updateValue);
|
|
17
43
|
}
|
|
18
44
|
/** Set the value of the atom.
|
|
19
45
|
*
|
|
@@ -21,8 +47,10 @@ export class Atom extends Stateful {
|
|
|
21
47
|
* new value based off the previous value.
|
|
22
48
|
*/
|
|
23
49
|
set(next) {
|
|
24
|
-
const
|
|
25
|
-
|
|
50
|
+
const newState = updater(next, this.get());
|
|
51
|
+
void this.effects
|
|
52
|
+
.dispatch("set", newState)
|
|
53
|
+
.then(value => super.update(value));
|
|
26
54
|
}
|
|
27
55
|
}
|
|
28
56
|
/** Creates an atom store.
|
|
@@ -30,7 +58,6 @@ export class Atom extends Stateful {
|
|
|
30
58
|
* @param config.defaultValue Value that will be used initially.
|
|
31
59
|
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
32
60
|
* @param config.effects Effects that will be applied on the atom.
|
|
33
|
-
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
34
61
|
*
|
|
35
62
|
* @returns An atom instance.
|
|
36
63
|
**/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { consoleMessage, toVoid } from "@yaasl/utils";
|
|
1
|
+
import { consoleMessage, toVoid, updater } from "@yaasl/utils";
|
|
2
2
|
import { Stateful } from "./Stateful";
|
|
3
3
|
const allDidInit = (atoms) => {
|
|
4
4
|
const inits = atoms
|
|
@@ -7,11 +7,12 @@ const allDidInit = (atoms) => {
|
|
|
7
7
|
return inits.length === 0 ? true : Promise.all(inits).then(toVoid);
|
|
8
8
|
};
|
|
9
9
|
export class Derive extends Stateful {
|
|
10
|
+
getter;
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
getterDependencies = new Set();
|
|
10
13
|
constructor(getter) {
|
|
11
14
|
super(undefined);
|
|
12
15
|
this.getter = getter;
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
-
this.getterDependencies = new Set();
|
|
15
16
|
this.value = getter({ get: dep => this.addGetDependency(dep) });
|
|
16
17
|
this.setDidInit(allDidInit(Array.from(this.getterDependencies)));
|
|
17
18
|
}
|
|
@@ -27,11 +28,12 @@ export class Derive extends Stateful {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
export class SettableDerive extends Derive {
|
|
31
|
+
setter;
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
setterDependencies = new Set();
|
|
30
34
|
constructor(getter, setter) {
|
|
31
35
|
super(getter);
|
|
32
36
|
this.setter = setter;
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
this.setterDependencies = new Set();
|
|
35
37
|
setter({
|
|
36
38
|
value: this.get(),
|
|
37
39
|
set: dep => this.addSetDependency(dep),
|
|
@@ -46,11 +48,11 @@ export class SettableDerive extends Derive {
|
|
|
46
48
|
* new value based off the previous value.
|
|
47
49
|
*/
|
|
48
50
|
set(next) {
|
|
49
|
-
const value = next
|
|
51
|
+
const value = updater(next, this.get());
|
|
50
52
|
this.setter({
|
|
51
53
|
value,
|
|
52
54
|
set: (atom, next) => {
|
|
53
|
-
const value = next
|
|
55
|
+
const value = updater(next, atom.get());
|
|
54
56
|
atom.set(value);
|
|
55
57
|
},
|
|
56
58
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createActions } from "./createActions";
|
|
2
|
+
import { Atom } from "./createAtom";
|
|
3
|
+
import { createSelector, } from "./createSelector";
|
|
4
|
+
const isEmpty = (obj) => !obj || Object.keys(obj).length === 0;
|
|
5
|
+
const createSelectors = (atom, selectors) => Object.fromEntries(Object.entries(selectors).map(([key, selector]) => [
|
|
6
|
+
key,
|
|
7
|
+
typeof selector === "string"
|
|
8
|
+
? createSelector(atom, selector)
|
|
9
|
+
: createSelector([atom], selector),
|
|
10
|
+
]));
|
|
11
|
+
/** Creates a slice with actions and selectors.
|
|
12
|
+
*
|
|
13
|
+
* @param config.defaultValue Value that will be used initially.
|
|
14
|
+
* @param config.name Name of the atom.
|
|
15
|
+
* @param config.effects Effects that will be applied on the atom.
|
|
16
|
+
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
17
|
+
* @param config.selectors Path or combiner selectors to use the atom's values to create new ones.
|
|
18
|
+
*
|
|
19
|
+
* @returns An atom instance with actions and selectors.
|
|
20
|
+
**/
|
|
21
|
+
export const createSlice = (config) => {
|
|
22
|
+
const atom = new Atom(config);
|
|
23
|
+
const actionsProp = isEmpty(config.reducers)
|
|
24
|
+
? {}
|
|
25
|
+
: {
|
|
26
|
+
actions: createActions(atom, config.reducers),
|
|
27
|
+
};
|
|
28
|
+
const selectorsProp = isEmpty(config.selectors)
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
selectors: createSelectors(atom, config.selectors),
|
|
32
|
+
};
|
|
33
|
+
return Object.assign(atom, actionsProp, selectorsProp);
|
|
34
|
+
};
|
package/dist/mjs/base/index.js
CHANGED
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
import { isPromiseLike } from "@yaasl/utils";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
this.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { isPromiseLike, updater } from "@yaasl/utils";
|
|
2
|
+
import { Queue } from "../utils/Queue";
|
|
3
|
+
const isTruthy = (value) => !!value;
|
|
4
|
+
export class EffectActions {
|
|
5
|
+
atom;
|
|
6
|
+
options;
|
|
7
|
+
actions;
|
|
8
|
+
constructor(atom, effectCreator) {
|
|
9
|
+
this.atom = atom;
|
|
10
|
+
const { actions, options } = effectCreator(atom);
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.actions = actions;
|
|
13
|
+
}
|
|
14
|
+
createAction(action) {
|
|
15
|
+
const actionFn = this.actions[action];
|
|
16
|
+
if (!actionFn)
|
|
15
17
|
return;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
return (prev) => {
|
|
19
|
+
const nextValue = { current: prev };
|
|
20
|
+
const set = (next) => (nextValue.current = updater(next, nextValue.current));
|
|
21
|
+
const result = actionFn({
|
|
22
|
+
value: prev,
|
|
23
|
+
set,
|
|
24
|
+
atom: this.atom,
|
|
25
|
+
options: this.options,
|
|
26
|
+
});
|
|
27
|
+
return isPromiseLike(result)
|
|
28
|
+
? result.then(() => nextValue.current)
|
|
29
|
+
: nextValue.current;
|
|
30
|
+
};
|
|
20
31
|
}
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
}
|
|
33
|
+
export class EffectDispatcher {
|
|
34
|
+
effects = [];
|
|
35
|
+
queue = new Queue();
|
|
36
|
+
constructor(atom, effects) {
|
|
37
|
+
this.effects = effects.map(create => new EffectActions(atom, create));
|
|
23
38
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return () => actionFn?.({ value: getValue(), atom, options });
|
|
30
|
-
});
|
|
31
|
-
void this.scheduler.run(tasks);
|
|
39
|
+
dispatch(action, startValue) {
|
|
40
|
+
const tasks = this.effects
|
|
41
|
+
.map(effect => effect.createAction(action))
|
|
42
|
+
.filter(isTruthy);
|
|
43
|
+
return this.queue.push(...tasks).run(startValue);
|
|
32
44
|
}
|
|
33
45
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* May return a promise that can be awaited by using `atom.didInit`.
|
|
7
7
|
* - `didInit`: Action to be called when the atom is created, but after subscribing to `set` events.
|
|
8
8
|
* May return a promise that can be awaited by using `atom.didInit`.
|
|
9
|
-
* - `set`: Action to be called when the atom's
|
|
9
|
+
* - `set`: Action to be called when the atom's value is set.
|
|
10
10
|
*
|
|
11
11
|
* @param setup Effect actions or function to create effect actions.
|
|
12
12
|
* Effect actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
@@ -15,18 +15,17 @@ let atomDb = null;
|
|
|
15
15
|
export const indexedDb = createEffect(({ atom, options }) => {
|
|
16
16
|
const key = options?.key ?? atom.name;
|
|
17
17
|
return {
|
|
18
|
-
init: ({ atom }) => {
|
|
18
|
+
init: async ({ atom, set }) => {
|
|
19
19
|
if (!atomDb) {
|
|
20
20
|
atomDb = new Store(CONFIG.name ?? "yaasl");
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
22
|
+
const existing = await atomDb.get(key);
|
|
23
|
+
if (existing != null) {
|
|
24
|
+
set(existing);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
await atomDb.set(key, atom.defaultValue);
|
|
28
|
+
}
|
|
30
29
|
},
|
|
31
30
|
set: ({ value }) => {
|
|
32
31
|
void atomDb?.set(key, value);
|
|
@@ -19,17 +19,19 @@ export const localStorage = createEffect(({ atom, options = {} }) => {
|
|
|
19
19
|
onTabSync: noTabSync ? undefined : value => atom.set(value),
|
|
20
20
|
});
|
|
21
21
|
return {
|
|
22
|
-
init: ({
|
|
22
|
+
init: ({ set }) => {
|
|
23
23
|
const existing = storage.get();
|
|
24
|
-
if (existing
|
|
25
|
-
|
|
24
|
+
if (existing != null) {
|
|
25
|
+
set(existing);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
set: ({ value, atom }) => {
|
|
29
|
+
if (value === atom.defaultValue) {
|
|
30
|
+
storage.remove();
|
|
26
31
|
}
|
|
27
32
|
else {
|
|
28
|
-
|
|
33
|
+
storage.set(value);
|
|
29
34
|
}
|
|
30
35
|
},
|
|
31
|
-
set: ({ value }) => {
|
|
32
|
-
storage.set(value);
|
|
33
|
-
},
|
|
34
36
|
};
|
|
35
37
|
});
|
|
@@ -66,7 +66,7 @@ const performMigration = (atom, version, migrations) => {
|
|
|
66
66
|
* @returns The effect to be used on atoms.
|
|
67
67
|
**/
|
|
68
68
|
export const migration = createEffect({
|
|
69
|
-
didInit: ({ atom, options }) => {
|
|
69
|
+
didInit: ({ atom, options, set }) => {
|
|
70
70
|
const steps = sortMigrations(options.steps);
|
|
71
71
|
const currentVersion = getVersion(atom);
|
|
72
72
|
const isLatestVersion = currentVersion === steps.at(-1)?.version;
|
|
@@ -90,7 +90,7 @@ export const migration = createEffect({
|
|
|
90
90
|
if (version == null)
|
|
91
91
|
return;
|
|
92
92
|
setVersion(atom, version);
|
|
93
|
-
|
|
93
|
+
set(data);
|
|
94
94
|
},
|
|
95
95
|
});
|
|
96
96
|
/** Helper to create a step for the migration effect.
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const STORAGE = window.localStorage;
|
|
2
2
|
export class Expiration {
|
|
3
|
+
key;
|
|
4
|
+
getExpiration;
|
|
5
|
+
timeout = null;
|
|
3
6
|
constructor({ key, expiresAt, expiresIn }) {
|
|
4
|
-
this.timeout = null;
|
|
5
|
-
this.dispatchExpiration = (expiresIn, onExpire) => {
|
|
6
|
-
this.timeout = setTimeout(() => {
|
|
7
|
-
onExpire();
|
|
8
|
-
this.remove();
|
|
9
|
-
}, expiresIn);
|
|
10
|
-
};
|
|
11
7
|
this.timeout = null;
|
|
12
8
|
this.key = key;
|
|
13
9
|
if (expiresAt) {
|
|
@@ -30,6 +26,12 @@ export class Expiration {
|
|
|
30
26
|
this.timeout = null;
|
|
31
27
|
}
|
|
32
28
|
}
|
|
29
|
+
dispatchExpiration = (expiresIn, onExpire) => {
|
|
30
|
+
this.timeout = setTimeout(() => {
|
|
31
|
+
onExpire();
|
|
32
|
+
this.remove();
|
|
33
|
+
}, expiresIn);
|
|
34
|
+
};
|
|
33
35
|
init(onExpire) {
|
|
34
36
|
if (!this.getExpiration)
|
|
35
37
|
return;
|
|
@@ -10,6 +10,8 @@ const syncOverBrowserTabs = (observingKey, onTabSync) => window.addEventListener
|
|
|
10
10
|
onTabSync(newValue);
|
|
11
11
|
});
|
|
12
12
|
export class LocalStorage {
|
|
13
|
+
key;
|
|
14
|
+
parser;
|
|
13
15
|
constructor(key, options = {}) {
|
|
14
16
|
this.key = key;
|
|
15
17
|
this.parser = options.parser ?? defaultParser;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Thenable } from "@yaasl/utils";
|
|
2
|
+
export class Queue {
|
|
3
|
+
last = null;
|
|
4
|
+
queue = [];
|
|
5
|
+
push(...tasks) {
|
|
6
|
+
this.queue.push(...tasks);
|
|
7
|
+
return this;
|
|
8
|
+
}
|
|
9
|
+
run(prev) {
|
|
10
|
+
const init = this.last ? this.last.then(() => prev) : new Thenable(prev);
|
|
11
|
+
const result = this.queue.reduce((result, next) => result.then(prev => next(prev)), init);
|
|
12
|
+
this.queue = [];
|
|
13
|
+
this.last = result.then(value => {
|
|
14
|
+
if (this.last === result) {
|
|
15
|
+
this.last = null;
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
});
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/mjs/utils/Store.js
CHANGED
|
@@ -3,6 +3,8 @@ const promisifyRequest = (request) => new Promise((resolve, reject) => {
|
|
|
3
3
|
request.onerror = () => reject(request.error);
|
|
4
4
|
});
|
|
5
5
|
export class Store {
|
|
6
|
+
name;
|
|
7
|
+
database;
|
|
6
8
|
constructor(name) {
|
|
7
9
|
this.name = name;
|
|
8
10
|
const openRequest = indexedDB.open(`${name}-database`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaasl/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-alpha.1",
|
|
4
4
|
"description": "yet another atomic store library (vanilla-js)",
|
|
5
5
|
"author": "PrettyCoffee",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"build": "run-s clean compile",
|
|
29
29
|
"clean": "rimraf ./dist",
|
|
30
30
|
"compile": "run-p compile:mjs compile:cjs compile:types",
|
|
31
|
-
"compile:mjs": "tsc -p ./tsconfig.node.json --outDir ./dist/mjs -m
|
|
31
|
+
"compile:mjs": "tsc -p ./tsconfig.node.json --outDir ./dist/mjs -m esnext -t esnext",
|
|
32
32
|
"compile:cjs": "tsc -p ./tsconfig.node.json --outDir ./dist/cjs -m commonjs -t es2015",
|
|
33
33
|
"compile:types": "tsc -p ./tsconfig.node.json --outDir ./dist/@types --declaration --emitDeclarationOnly",
|
|
34
34
|
"test": "vitest run",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"validate": "run-s lint test build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@yaasl/utils": "0.
|
|
41
|
+
"@yaasl/utils": "0.10.0-alpha.1"
|
|
42
42
|
},
|
|
43
43
|
"eslintConfig": {
|
|
44
44
|
"extends": [
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Scheduler = void 0;
|
|
4
|
-
const utils_1 = require("@yaasl/utils");
|
|
5
|
-
class Scheduler {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.queue = null;
|
|
8
|
-
}
|
|
9
|
-
run(...tasks) {
|
|
10
|
-
const run = () => this.execute(tasks.flat()).then(utils_1.toVoid);
|
|
11
|
-
const promise = this.queue ? this.queue.then(run) : run();
|
|
12
|
-
this.queue = promise;
|
|
13
|
-
return promise.then(() => {
|
|
14
|
-
if (this.queue !== promise)
|
|
15
|
-
return;
|
|
16
|
-
this.queue = null;
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
execute(tasks) {
|
|
20
|
-
const result = tasks.map(task => {
|
|
21
|
-
const result = task();
|
|
22
|
-
return (0, utils_1.isPromiseLike)(result) ? result : new utils_1.Thenable(result);
|
|
23
|
-
});
|
|
24
|
-
return utils_1.Thenable.all(result);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
exports.Scheduler = Scheduler;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { toVoid, isPromiseLike, Thenable } from "@yaasl/utils";
|
|
2
|
-
export class Scheduler {
|
|
3
|
-
constructor() {
|
|
4
|
-
this.queue = null;
|
|
5
|
-
}
|
|
6
|
-
run(...tasks) {
|
|
7
|
-
const run = () => this.execute(tasks.flat()).then(toVoid);
|
|
8
|
-
const promise = this.queue ? this.queue.then(run) : run();
|
|
9
|
-
this.queue = promise;
|
|
10
|
-
return promise.then(() => {
|
|
11
|
-
if (this.queue !== promise)
|
|
12
|
-
return;
|
|
13
|
-
this.queue = null;
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
execute(tasks) {
|
|
17
|
-
const result = tasks.map(task => {
|
|
18
|
-
const result = task();
|
|
19
|
-
return isPromiseLike(result) ? result : new Thenable(result);
|
|
20
|
-
});
|
|
21
|
-
return Thenable.all(result);
|
|
22
|
-
}
|
|
23
|
-
}
|