@yaasl/core 0.8.0-alpha.4 → 0.9.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/@types/base/createActions.d.ts +31 -0
- package/dist/@types/base/createAtom.d.ts +42 -0
- package/dist/@types/base/{derive.d.ts → createDerived.d.ts} +4 -4
- package/dist/@types/base/{select.d.ts → createSelector.d.ts} +3 -3
- package/dist/@types/base/index.d.ts +3 -3
- package/dist/@types/effects/EffectDispatcher.d.ts +15 -0
- package/dist/@types/effects/createEffect.d.ts +39 -0
- package/dist/@types/{middleware → effects}/expiration.d.ts +3 -3
- package/dist/@types/{middleware → effects}/index.d.ts +2 -2
- package/dist/@types/{middleware → effects}/indexedDb.d.ts +2 -2
- package/dist/@types/{middleware → effects}/localStorage.d.ts +2 -2
- package/dist/@types/{middleware → effects}/migration.d.ts +19 -3
- package/dist/@types/index.d.ts +1 -1
- package/dist/cjs/base/createActions.js +28 -0
- package/dist/cjs/base/createAtom.js +48 -0
- package/dist/cjs/base/{derive.js → createDerived.js} +3 -3
- package/dist/cjs/base/{select.js → createSelector.js} +6 -6
- package/dist/cjs/base/index.js +3 -3
- package/dist/cjs/{middleware/MiddlewareDispatcher.js → effects/EffectDispatcher.js} +11 -11
- package/dist/cjs/effects/createEffect.js +27 -0
- package/dist/cjs/{middleware → effects}/expiration.js +4 -4
- package/dist/cjs/{middleware → effects}/index.js +3 -3
- package/dist/cjs/{middleware → effects}/indexedDb.js +3 -3
- package/dist/cjs/{middleware → effects}/localStorage.js +3 -3
- package/dist/cjs/{middleware → effects}/migration.js +18 -2
- package/dist/cjs/index.js +1 -1
- package/dist/mjs/base/createActions.js +24 -0
- package/dist/mjs/base/createAtom.js +43 -0
- package/dist/mjs/base/{derive.js → createDerived.js} +1 -1
- package/dist/mjs/base/{select.js → createSelector.js} +3 -3
- package/dist/mjs/base/index.js +3 -3
- package/dist/mjs/{middleware/MiddlewareDispatcher.js → effects/EffectDispatcher.js} +9 -9
- package/dist/mjs/effects/createEffect.js +23 -0
- package/dist/mjs/{middleware → effects}/expiration.js +4 -4
- package/dist/mjs/{middleware → effects}/index.js +1 -1
- package/dist/mjs/{middleware → effects}/indexedDb.js +3 -3
- package/dist/mjs/{middleware → effects}/localStorage.js +3 -3
- package/dist/mjs/{middleware → effects}/migration.js +18 -2
- package/dist/mjs/index.js +1 -1
- package/package.json +2 -2
- package/dist/@types/base/atom.d.ts +0 -31
- package/dist/@types/middleware/MiddlewareDispatcher.d.ts +0 -15
- package/dist/@types/middleware/middleware.d.ts +0 -31
- package/dist/cjs/base/atom.js +0 -39
- package/dist/cjs/middleware/middleware.js +0 -19
- package/dist/mjs/base/atom.js +0 -34
- package/dist/mjs/middleware/middleware.js +0 -15
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Atom } from "./createAtom";
|
|
2
|
+
import { SettableDerive } from "./createDerived";
|
|
3
|
+
type Reducer<State> = (state: State, ...payloadArgs: any[]) => State;
|
|
4
|
+
export type Reducers<State> = Record<string, Reducer<State>>;
|
|
5
|
+
type Payload<R extends Reducer<any>> = Parameters<R> extends [
|
|
6
|
+
any,
|
|
7
|
+
...infer PayloadArgs
|
|
8
|
+
] ? PayloadArgs : [];
|
|
9
|
+
export type Actions<State, R extends Reducers<State>> = {
|
|
10
|
+
[K in keyof R]: (...payloadArgs: Payload<R[K]>) => void;
|
|
11
|
+
};
|
|
12
|
+
/** Create actions to change the state of an atom.
|
|
13
|
+
*
|
|
14
|
+
* @param atom Atom to be used.
|
|
15
|
+
* @param reducers Reducers for custom actions to set the atoms value.
|
|
16
|
+
*
|
|
17
|
+
* @returns Actions to change the state of the atom.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const counter = createAtom({ defaultValue: 0 })
|
|
21
|
+
* const actions = createActions(counter, {
|
|
22
|
+
* increment: (state) => state + 1,
|
|
23
|
+
* decrement: (state) => state - 1,
|
|
24
|
+
* add: (state, value: number) => state + value,
|
|
25
|
+
* subtract: (state, value: number) => state - value,
|
|
26
|
+
* })
|
|
27
|
+
* actions.increment()
|
|
28
|
+
* actions.add(5)
|
|
29
|
+
**/
|
|
30
|
+
export declare const createActions: <State, R extends Reducers<State>>(atom: Atom<State> | SettableDerive<State>, reducers: R) => Actions<State, R>;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SetStateAction } from "@yaasl/utils";
|
|
2
|
+
import { Actions, Reducers } from "./createActions";
|
|
3
|
+
import { Stateful } from "./Stateful";
|
|
4
|
+
import { EffectAtomCallback } from "../effects/createEffect";
|
|
5
|
+
export interface AtomConfig<Value, R extends Reducers<Value> = {}> {
|
|
6
|
+
/** Value that will be used initially. */
|
|
7
|
+
defaultValue: Value;
|
|
8
|
+
/** Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}". */
|
|
9
|
+
name?: string;
|
|
10
|
+
/** 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;
|
|
14
|
+
}
|
|
15
|
+
export declare class Atom<Value = unknown, R extends Reducers<Value> = {}> extends Stateful<Value> {
|
|
16
|
+
readonly defaultValue: Value;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly actions: Actions<Value, R>;
|
|
19
|
+
constructor({ defaultValue, name, effects, reducers, }: AtomConfig<Value, R>);
|
|
20
|
+
/** Set the value of the atom.
|
|
21
|
+
*
|
|
22
|
+
* @param next New value or function to create the
|
|
23
|
+
* new value based off the previous value.
|
|
24
|
+
*/
|
|
25
|
+
set(next: SetStateAction<Value>): void;
|
|
26
|
+
}
|
|
27
|
+
/** Creates an atom store.
|
|
28
|
+
*
|
|
29
|
+
* @param config.defaultValue Value that will be used initially.
|
|
30
|
+
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
31
|
+
* @param config.effects Effects that will be applied on the atom.
|
|
32
|
+
* @param config.reducers Reducers for custom actions to set the atom's value.
|
|
33
|
+
*
|
|
34
|
+
* @returns An atom instance.
|
|
35
|
+
* - `result.get`: Read the value of state.
|
|
36
|
+
* - `result.subscribe`: Subscribe to value changes.
|
|
37
|
+
* - `result.set`: Set the value of the atom.
|
|
38
|
+
* - `result.actions`: All actions that were created with reducers.
|
|
39
|
+
* - `result.didInit`: State of the atom's effects initialization process.
|
|
40
|
+
* Will be a promise if the initialization is pending and `true` if finished.
|
|
41
|
+
**/
|
|
42
|
+
export declare const createAtom: <Value, R extends Reducers<Value> = {}>(config: AtomConfig<Value, R>) => Atom<Value, R>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SetStateAction } from "@yaasl/utils";
|
|
2
|
-
import { Atom } from "./
|
|
2
|
+
import { Atom } from "./createAtom";
|
|
3
3
|
import { Stateful } from "./Stateful";
|
|
4
4
|
type GetterFn<Value> = (context: {
|
|
5
5
|
get: <V>(dep: Stateful<V>) => V;
|
|
@@ -17,7 +17,7 @@ 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> | SettableDerive<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
|
*
|
|
@@ -40,6 +40,6 @@ export declare class SettableDerive<Value = unknown> extends Derive<Value> {
|
|
|
40
40
|
*
|
|
41
41
|
* @returns A derive instance.
|
|
42
42
|
**/
|
|
43
|
-
export declare function
|
|
44
|
-
export declare function
|
|
43
|
+
export declare function createDerived<Value>(getter: GetterFn<Value>): Derive<Value>;
|
|
44
|
+
export declare function createDerived<Value>(getter: GetterFn<Value>, setter: SetterFn<Value>): SettableDerive<Value>;
|
|
45
45
|
export {};
|
|
@@ -4,7 +4,7 @@ type DeepKeys<T> = T extends object ? {
|
|
|
4
4
|
[K in keyof T]: `${Exclude<K, symbol>}${"" | `.${DeepKeys<T[K]>}`}`;
|
|
5
5
|
}[keyof T] : never;
|
|
6
6
|
type DeepValue<T, Path> = T extends Record<string | number, unknown> ? Path extends `${infer Current}.${infer Next}` ? DeepValue<T[Current], Next> : T[Path & string] : never;
|
|
7
|
-
export declare class
|
|
7
|
+
export declare class Selector<Path extends DeepKeys<State>, State extends Selectable> extends Stateful<DeepValue<State, Path>> {
|
|
8
8
|
protected readonly parent: Stateful<State>;
|
|
9
9
|
protected readonly path: Path;
|
|
10
10
|
constructor(parent: Stateful<State>, path: Path);
|
|
@@ -14,7 +14,7 @@ export declare class Select<Path extends DeepKeys<State>, State extends Selectab
|
|
|
14
14
|
* @param parent The parent element to select a value from. The internal state must be an object.
|
|
15
15
|
* @param path The path to the value you want to select.
|
|
16
16
|
*
|
|
17
|
-
* @returns A
|
|
17
|
+
* @returns A selector instance.
|
|
18
18
|
**/
|
|
19
|
-
export declare const
|
|
19
|
+
export declare const createSelector: <Path extends DeepKeys<State>, State extends Selectable>(parent: Stateful<State>, path: Path) => Selector<Path, State>;
|
|
20
20
|
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EffectAtomCallback } from "./createEffect";
|
|
2
|
+
import { Atom } from "../base/createAtom";
|
|
3
|
+
interface EffectDispatcherConstructor {
|
|
4
|
+
atom: Atom<any>;
|
|
5
|
+
effects: EffectAtomCallback<any>[];
|
|
6
|
+
}
|
|
7
|
+
export declare class EffectDispatcher {
|
|
8
|
+
didInit: PromiseLike<void> | boolean;
|
|
9
|
+
private effects;
|
|
10
|
+
private scheduler;
|
|
11
|
+
constructor({ atom, effects }: EffectDispatcherConstructor);
|
|
12
|
+
private subscribeSetters;
|
|
13
|
+
private callEffectAction;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Atom } from "../base";
|
|
2
|
+
export type ActionType = "init" | "didInit" | "set";
|
|
3
|
+
interface EffectPayload<Options, AtomValue> {
|
|
4
|
+
value: AtomValue;
|
|
5
|
+
atom: Atom<AtomValue>;
|
|
6
|
+
options: Options;
|
|
7
|
+
}
|
|
8
|
+
interface EffectActions<Options, AtomValue> {
|
|
9
|
+
init?: (payload: EffectPayload<Options, AtomValue>) => Promise<any> | void;
|
|
10
|
+
didInit?: (payload: EffectPayload<Options, AtomValue>) => Promise<any> | void;
|
|
11
|
+
set?: (payload: EffectPayload<Options, AtomValue>) => void;
|
|
12
|
+
}
|
|
13
|
+
interface EffectSetupProps<Options, AtomValue> {
|
|
14
|
+
atom: Atom<AtomValue>;
|
|
15
|
+
options: Options;
|
|
16
|
+
}
|
|
17
|
+
export type EffectAtomCallback<Options = unknown, AtomValue = unknown> = (atom: Atom<AtomValue>) => Effect<Options, AtomValue>;
|
|
18
|
+
type EffectSetup<Options, AtomValue> = EffectActions<Options, AtomValue> | ((props: EffectSetupProps<Options, AtomValue>) => EffectActions<Options, AtomValue>);
|
|
19
|
+
export interface Effect<Options = unknown, AtomValue = unknown> {
|
|
20
|
+
options: Options;
|
|
21
|
+
actions: EffectActions<Options, AtomValue>;
|
|
22
|
+
}
|
|
23
|
+
/** Create effects to be used in combination with atoms.
|
|
24
|
+
*
|
|
25
|
+
* Effects can be used to interact with an atom by using the following lifecycle actions:
|
|
26
|
+
*
|
|
27
|
+
* - `init`: Action to be called when the atom is created, but before subscribing to `set` events.
|
|
28
|
+
* May return a promise that can be awaited by using `atom.didInit`.
|
|
29
|
+
* - `didInit`: Action to be called when the atom is created, but after subscribing to `set` events.
|
|
30
|
+
* May return a promise that can be awaited by using `atom.didInit`.
|
|
31
|
+
* - `set`: Action to be called when the atom's `set` function is called.
|
|
32
|
+
*
|
|
33
|
+
* @param setup Effect actions or function to create effect actions.
|
|
34
|
+
* Effect actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
35
|
+
*
|
|
36
|
+
* @returns The effect to be used on atoms.
|
|
37
|
+
**/
|
|
38
|
+
export declare const createEffect: <Options = undefined, AtomValue = any>(setup: EffectSetup<Options, AtomValue>) => (...[optionsArg]: Options extends undefined ? [Options] | [] : [Options]) => EffectAtomCallback<Options, AtomValue>;
|
|
39
|
+
export {};
|
|
@@ -4,7 +4,7 @@ export interface ExpirationOptions {
|
|
|
4
4
|
/** Milliseconds in which the value expires. Will be ignored if expiresAt is set. */
|
|
5
5
|
expiresIn?: number | (() => number);
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/** Effect to make an atom value expirable and reset to its defaulValue.
|
|
8
8
|
*
|
|
9
9
|
* __Note:__ When using `expiresAt`, a function returning the date should be prefered since using a static date might end in an infinite loop.
|
|
10
10
|
*
|
|
@@ -12,6 +12,6 @@ export interface ExpirationOptions {
|
|
|
12
12
|
* @param options.expiresAt Date at which the value expires
|
|
13
13
|
* @param options.expiresIn Milliseconds in which the value expires. Will be ignored if expiresAt is set.
|
|
14
14
|
*
|
|
15
|
-
* @returns The
|
|
15
|
+
* @returns The effect to be used on atoms.
|
|
16
16
|
**/
|
|
17
|
-
export declare const expiration: (__0_0: ExpirationOptions) => import("./
|
|
17
|
+
export declare const expiration: (__0_0: ExpirationOptions) => import("./createEffect").EffectAtomCallback<ExpirationOptions, any>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
1
|
+
export { createEffect } from "./createEffect";
|
|
2
|
+
export type { Effect } from "./createEffect";
|
|
3
3
|
export { localStorage } from "./localStorage";
|
|
4
4
|
export type { LocalStorageOptions, LocalStorageParser } from "./localStorage";
|
|
5
5
|
export { indexedDb } from "./indexedDb";
|
|
@@ -10,6 +10,6 @@ export interface IndexedDbOptions {
|
|
|
10
10
|
* @param {IndexedDbOptions | undefined} options
|
|
11
11
|
* @param options.key Use your own store key. Will be `atom.name` by default.
|
|
12
12
|
*
|
|
13
|
-
* @returns The
|
|
13
|
+
* @returns The effect to be used on atoms.
|
|
14
14
|
**/
|
|
15
|
-
export declare const indexedDb: (...[optionsArg]: [] | [undefined] | [IndexedDbOptions]) => import("./
|
|
15
|
+
export declare const indexedDb: (...[optionsArg]: [] | [undefined] | [IndexedDbOptions]) => import("./createEffect").EffectAtomCallback<IndexedDbOptions | undefined, any>;
|
|
@@ -23,6 +23,6 @@ export interface LocalStorageOptions {
|
|
|
23
23
|
* @param options.noTabSync Disable the synchronization of values over browser tabs.
|
|
24
24
|
* @param options.parser Custom functions to stringify and parse values. Defaults to JSON.stringify and JSON.parse. Use this when handling complex datatypes like Maps or Sets.
|
|
25
25
|
*
|
|
26
|
-
* @returns The
|
|
26
|
+
* @returns The effect to be used on atoms.
|
|
27
27
|
**/
|
|
28
|
-
export declare const localStorage: (...[optionsArg]: [] | [undefined] | [LocalStorageOptions]) => import("./
|
|
28
|
+
export declare const localStorage: (...[optionsArg]: [] | [undefined] | [LocalStorageOptions]) => import("./createEffect").EffectAtomCallback<LocalStorageOptions | undefined, any>;
|
|
@@ -9,11 +9,27 @@ export interface MigrationStep<Version extends string = string, PreviousVersion
|
|
|
9
9
|
validate?: (data: unknown) => data is OldData;
|
|
10
10
|
}
|
|
11
11
|
export interface MigrationOptions {
|
|
12
|
-
/**
|
|
12
|
+
/** An array of migration steps to perform for outdated values.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* __Note:__ One step must have a `previous` version set to null as entry point.
|
|
15
15
|
**/
|
|
16
16
|
steps: MigrationStep[];
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
/** Effect to migrate the persisted value of an atom to a newer version.
|
|
19
|
+
* You can use the `createMigrationStep` helper to create migration steps.
|
|
20
|
+
*
|
|
21
|
+
* @param {MigrationOptions} options
|
|
22
|
+
* @param options.steps An array of migration steps to perform for outdated values.
|
|
23
|
+
*
|
|
24
|
+
* __Note:__ One step must have a `previous` version set to null as entry point.
|
|
25
|
+
*
|
|
26
|
+
* @returns The effect to be used on atoms.
|
|
27
|
+
**/
|
|
28
|
+
export declare const migration: (__0_0: MigrationOptions) => import("./createEffect").EffectAtomCallback<MigrationOptions, unknown>;
|
|
29
|
+
/** Helper to create a step for the migration effect.
|
|
30
|
+
*
|
|
31
|
+
* @param migration Migration step to create.
|
|
32
|
+
*
|
|
33
|
+
* @returns The migration step.
|
|
34
|
+
**/
|
|
19
35
|
export declare const createMigrationStep: <Version extends string, PreviousVersion extends string | null, OldData, NewData>(migration: MigrationStep<Version, PreviousVersion, OldData, NewData>) => MigrationStep<Version, PreviousVersion, OldData, NewData>;
|
package/dist/@types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./base";
|
|
2
|
-
export * from "./
|
|
2
|
+
export * from "./effects";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createActions = void 0;
|
|
4
|
+
/** Create actions to change the state of an atom.
|
|
5
|
+
*
|
|
6
|
+
* @param atom Atom to be used.
|
|
7
|
+
* @param reducers Reducers for custom actions to set the atoms value.
|
|
8
|
+
*
|
|
9
|
+
* @returns Actions to change the state of the atom.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const counter = createAtom({ defaultValue: 0 })
|
|
13
|
+
* const actions = createActions(counter, {
|
|
14
|
+
* increment: (state) => state + 1,
|
|
15
|
+
* decrement: (state) => state - 1,
|
|
16
|
+
* add: (state, value: number) => state + value,
|
|
17
|
+
* subtract: (state, value: number) => state - value,
|
|
18
|
+
* })
|
|
19
|
+
* actions.increment()
|
|
20
|
+
* actions.add(5)
|
|
21
|
+
**/
|
|
22
|
+
const createActions = (atom, reducers) => Object.entries(reducers).reduce((result, [key, reducerFn]) => {
|
|
23
|
+
result[key] = (...payloadArgs) => {
|
|
24
|
+
atom.set((state) => reducerFn(state, ...payloadArgs));
|
|
25
|
+
};
|
|
26
|
+
return result;
|
|
27
|
+
}, {});
|
|
28
|
+
exports.createActions = createActions;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAtom = exports.Atom = void 0;
|
|
4
|
+
const createActions_1 = require("./createActions");
|
|
5
|
+
const Stateful_1 = require("./Stateful");
|
|
6
|
+
const EffectDispatcher_1 = require("../effects/EffectDispatcher");
|
|
7
|
+
let key = 0;
|
|
8
|
+
class Atom extends Stateful_1.Stateful {
|
|
9
|
+
constructor({ defaultValue, name = `atom-${++key}`, effects, reducers = {}, }) {
|
|
10
|
+
super(defaultValue);
|
|
11
|
+
this.name = name;
|
|
12
|
+
this.defaultValue = defaultValue;
|
|
13
|
+
this.actions = (0, createActions_1.createActions)(this, reducers);
|
|
14
|
+
if (!effects || effects.length === 0) {
|
|
15
|
+
this.didInit = true;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const { didInit } = new EffectDispatcher_1.EffectDispatcher({ atom: this, effects });
|
|
19
|
+
this.setDidInit(didInit);
|
|
20
|
+
}
|
|
21
|
+
/** Set the value of the atom.
|
|
22
|
+
*
|
|
23
|
+
* @param next New value or function to create the
|
|
24
|
+
* new value based off the previous value.
|
|
25
|
+
*/
|
|
26
|
+
set(next) {
|
|
27
|
+
const value = next instanceof Function ? next(this.get()) : next;
|
|
28
|
+
super.update(value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.Atom = Atom;
|
|
32
|
+
/** Creates an atom store.
|
|
33
|
+
*
|
|
34
|
+
* @param config.defaultValue Value that will be used initially.
|
|
35
|
+
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
36
|
+
* @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
|
+
*
|
|
39
|
+
* @returns An atom instance.
|
|
40
|
+
* - `result.get`: Read the value of state.
|
|
41
|
+
* - `result.subscribe`: Subscribe to value changes.
|
|
42
|
+
* - `result.set`: Set the value of the atom.
|
|
43
|
+
* - `result.actions`: All actions that were created with reducers.
|
|
44
|
+
* - `result.didInit`: State of the atom's effects initialization process.
|
|
45
|
+
* Will be a promise if the initialization is pending and `true` if finished.
|
|
46
|
+
**/
|
|
47
|
+
const createAtom = (config) => new Atom(config);
|
|
48
|
+
exports.createAtom = createAtom;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createDerived = exports.SettableDerive = exports.Derive = void 0;
|
|
4
4
|
const utils_1 = require("@yaasl/utils");
|
|
5
5
|
const Stateful_1 = require("./Stateful");
|
|
6
6
|
const allDidInit = (atoms) => {
|
|
@@ -73,7 +73,7 @@ class SettableDerive extends Derive {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
exports.SettableDerive = SettableDerive;
|
|
76
|
-
function
|
|
76
|
+
function createDerived(getter, setter) {
|
|
77
77
|
if (setter) {
|
|
78
78
|
return new SettableDerive(getter, setter);
|
|
79
79
|
}
|
|
@@ -81,4 +81,4 @@ function derive(getter, setter) {
|
|
|
81
81
|
return new Derive(getter);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
exports.
|
|
84
|
+
exports.createDerived = createDerived;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createSelector = exports.Selector = void 0;
|
|
4
4
|
const Stateful_1 = require("./Stateful");
|
|
5
5
|
const selectValue = (state, path) => path
|
|
6
6
|
.split(".")
|
|
7
7
|
.reduce((result, key) => result[key], state);
|
|
8
|
-
class
|
|
8
|
+
class Selector extends Stateful_1.Stateful {
|
|
9
9
|
constructor(parent, path) {
|
|
10
10
|
super(selectValue(parent.get(), path));
|
|
11
11
|
this.parent = parent;
|
|
@@ -14,13 +14,13 @@ class Select extends Stateful_1.Stateful {
|
|
|
14
14
|
this.setDidInit(parent.didInit);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
exports.
|
|
17
|
+
exports.Selector = Selector;
|
|
18
18
|
/** Creates a value, selected from any stateful value.
|
|
19
19
|
*
|
|
20
20
|
* @param parent The parent element to select a value from. The internal state must be an object.
|
|
21
21
|
* @param path The path to the value you want to select.
|
|
22
22
|
*
|
|
23
|
-
* @returns A
|
|
23
|
+
* @returns A selector instance.
|
|
24
24
|
**/
|
|
25
|
-
const
|
|
26
|
-
exports.
|
|
25
|
+
const createSelector = (parent, path) => new Selector(parent, path);
|
|
26
|
+
exports.createSelector = createSelector;
|
package/dist/cjs/base/index.js
CHANGED
|
@@ -14,8 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
18
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./createAtom"), exports);
|
|
18
|
+
__exportStar(require("./createDerived"), exports);
|
|
19
19
|
__exportStar(require("./config"), exports);
|
|
20
|
-
__exportStar(require("./
|
|
20
|
+
__exportStar(require("./createSelector"), exports);
|
|
21
21
|
__exportStar(require("./Stateful"), exports);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.EffectDispatcher = void 0;
|
|
4
4
|
const utils_1 = require("@yaasl/utils");
|
|
5
5
|
const Scheduler_1 = require("../utils/Scheduler");
|
|
6
|
-
class
|
|
7
|
-
constructor({ atom,
|
|
6
|
+
class EffectDispatcher {
|
|
7
|
+
constructor({ atom, effects }) {
|
|
8
8
|
this.didInit = false;
|
|
9
|
-
this.
|
|
9
|
+
this.effects = [];
|
|
10
10
|
this.scheduler = new Scheduler_1.Scheduler();
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
11
|
+
this.effects = effects.map(create => create(atom));
|
|
12
|
+
this.callEffectAction("init", atom);
|
|
13
13
|
this.subscribeSetters(atom);
|
|
14
|
-
this.
|
|
14
|
+
this.callEffectAction("didInit", atom);
|
|
15
15
|
const { queue } = this.scheduler;
|
|
16
16
|
if (!(0, utils_1.isPromiseLike)(queue)) {
|
|
17
17
|
this.didInit = true;
|
|
@@ -22,16 +22,16 @@ class MiddlewareDispatcher {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
subscribeSetters(atom) {
|
|
25
|
-
atom.subscribe(value => this.
|
|
25
|
+
atom.subscribe(value => this.callEffectAction("set", atom, () => value));
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
callEffectAction(action, atom,
|
|
28
28
|
/* Must be a function to make sure it is using the latest value when used in promise */
|
|
29
29
|
getValue = () => atom.get()) {
|
|
30
|
-
const tasks = this.
|
|
30
|
+
const tasks = this.effects.map(({ actions, options }) => {
|
|
31
31
|
const actionFn = actions[action];
|
|
32
32
|
return () => actionFn === null || actionFn === void 0 ? void 0 : actionFn({ value: getValue(), atom, options });
|
|
33
33
|
});
|
|
34
34
|
void this.scheduler.run(tasks);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
exports.
|
|
37
|
+
exports.EffectDispatcher = EffectDispatcher;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEffect = void 0;
|
|
4
|
+
/** Create effects to be used in combination with atoms.
|
|
5
|
+
*
|
|
6
|
+
* Effects can be used to interact with an atom by using the following lifecycle actions:
|
|
7
|
+
*
|
|
8
|
+
* - `init`: Action to be called when the atom is created, but before subscribing to `set` events.
|
|
9
|
+
* May return a promise that can be awaited by using `atom.didInit`.
|
|
10
|
+
* - `didInit`: Action to be called when the atom is created, but after subscribing to `set` events.
|
|
11
|
+
* May return a promise that can be awaited by using `atom.didInit`.
|
|
12
|
+
* - `set`: Action to be called when the atom's `set` function is called.
|
|
13
|
+
*
|
|
14
|
+
* @param setup Effect actions or function to create effect actions.
|
|
15
|
+
* Effect actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
16
|
+
*
|
|
17
|
+
* @returns The effect to be used on atoms.
|
|
18
|
+
**/
|
|
19
|
+
const createEffect = (setup) => (...[optionsArg]) => (atom) => {
|
|
20
|
+
const options = optionsArg;
|
|
21
|
+
const actions = setup instanceof Function ? setup({ options, atom }) : setup;
|
|
22
|
+
return {
|
|
23
|
+
options,
|
|
24
|
+
actions,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.createEffect = createEffect;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.expiration = void 0;
|
|
4
|
-
const
|
|
4
|
+
const createEffect_1 = require("./createEffect");
|
|
5
5
|
const base_1 = require("../base");
|
|
6
6
|
const Expiration_1 = require("../utils/Expiration");
|
|
7
7
|
const STORAGE = window.localStorage;
|
|
@@ -13,7 +13,7 @@ const syncOverBrowserTabs = (observingKey, onChange) => window.addEventListener(
|
|
|
13
13
|
return;
|
|
14
14
|
onChange(newValue);
|
|
15
15
|
});
|
|
16
|
-
/**
|
|
16
|
+
/** Effect to make an atom value expirable and reset to its defaulValue.
|
|
17
17
|
*
|
|
18
18
|
* __Note:__ When using `expiresAt`, a function returning the date should be prefered since using a static date might end in an infinite loop.
|
|
19
19
|
*
|
|
@@ -21,9 +21,9 @@ const syncOverBrowserTabs = (observingKey, onChange) => window.addEventListener(
|
|
|
21
21
|
* @param options.expiresAt Date at which the value expires
|
|
22
22
|
* @param options.expiresIn Milliseconds in which the value expires. Will be ignored if expiresAt is set.
|
|
23
23
|
*
|
|
24
|
-
* @returns The
|
|
24
|
+
* @returns The effect to be used on atoms.
|
|
25
25
|
**/
|
|
26
|
-
exports.expiration = (0,
|
|
26
|
+
exports.expiration = (0, createEffect_1.createEffect)(({ atom, options = {} }) => {
|
|
27
27
|
var _a;
|
|
28
28
|
const hasExpiration = Boolean((_a = options.expiresAt) !== null && _a !== void 0 ? _a : options.expiresIn);
|
|
29
29
|
if (!hasExpiration)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createMigrationStep = exports.migration = exports.expiration = exports.indexedDb = exports.localStorage = exports.
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "
|
|
3
|
+
exports.createMigrationStep = exports.migration = exports.expiration = exports.indexedDb = exports.localStorage = exports.createEffect = void 0;
|
|
4
|
+
var createEffect_1 = require("./createEffect");
|
|
5
|
+
Object.defineProperty(exports, "createEffect", { enumerable: true, get: function () { return createEffect_1.createEffect; } });
|
|
6
6
|
var localStorage_1 = require("./localStorage");
|
|
7
7
|
Object.defineProperty(exports, "localStorage", { enumerable: true, get: function () { return localStorage_1.localStorage; } });
|
|
8
8
|
var indexedDb_1 = require("./indexedDb");
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.indexedDb = void 0;
|
|
13
|
-
const
|
|
13
|
+
const createEffect_1 = require("./createEffect");
|
|
14
14
|
const base_1 = require("../base");
|
|
15
15
|
const Store_1 = require("../utils/Store");
|
|
16
16
|
let atomDb = null;
|
|
@@ -22,9 +22,9 @@ let atomDb = null;
|
|
|
22
22
|
* @param {IndexedDbOptions | undefined} options
|
|
23
23
|
* @param options.key Use your own store key. Will be `atom.name` by default.
|
|
24
24
|
*
|
|
25
|
-
* @returns The
|
|
25
|
+
* @returns The effect to be used on atoms.
|
|
26
26
|
**/
|
|
27
|
-
exports.indexedDb = (0,
|
|
27
|
+
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 {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.localStorage = void 0;
|
|
4
|
-
const
|
|
4
|
+
const createEffect_1 = require("./createEffect");
|
|
5
5
|
const base_1 = require("../base");
|
|
6
6
|
const LocalStorage_1 = require("../utils/LocalStorage");
|
|
7
7
|
/** Middleware to save and load atom values to the local storage.
|
|
@@ -12,9 +12,9 @@ const LocalStorage_1 = require("../utils/LocalStorage");
|
|
|
12
12
|
* @param options.noTabSync Disable the synchronization of values over browser tabs.
|
|
13
13
|
* @param options.parser Custom functions to stringify and parse values. Defaults to JSON.stringify and JSON.parse. Use this when handling complex datatypes like Maps or Sets.
|
|
14
14
|
*
|
|
15
|
-
* @returns The
|
|
15
|
+
* @returns The effect to be used on atoms.
|
|
16
16
|
**/
|
|
17
|
-
exports.localStorage = (0,
|
|
17
|
+
exports.localStorage = (0, createEffect_1.createEffect)(({ atom, options = {} }) => {
|
|
18
18
|
const internalKey = base_1.CONFIG.name ? `${base_1.CONFIG.name}/${atom.name}` : atom.name;
|
|
19
19
|
const { key = internalKey, parser, noTabSync } = options;
|
|
20
20
|
const storage = new LocalStorage_1.LocalStorage(key, {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createMigrationStep = exports.migration = void 0;
|
|
4
4
|
const utils_1 = require("@yaasl/utils");
|
|
5
|
-
const
|
|
5
|
+
const createEffect_1 = require("./createEffect");
|
|
6
6
|
const base_1 = require("../base");
|
|
7
7
|
const sortMigrations = (migrations) => {
|
|
8
8
|
const first = migrations.find(migration => migration.previous === null);
|
|
@@ -58,7 +58,17 @@ const performMigration = (atom, version, migrations) => {
|
|
|
58
58
|
return migrateVersion(atom, data, migration);
|
|
59
59
|
}, currentState);
|
|
60
60
|
};
|
|
61
|
-
|
|
61
|
+
/** Effect to migrate the persisted value of an atom to a newer version.
|
|
62
|
+
* You can use the `createMigrationStep` helper to create migration steps.
|
|
63
|
+
*
|
|
64
|
+
* @param {MigrationOptions} options
|
|
65
|
+
* @param options.steps An array of migration steps to perform for outdated values.
|
|
66
|
+
*
|
|
67
|
+
* __Note:__ One step must have a `previous` version set to null as entry point.
|
|
68
|
+
*
|
|
69
|
+
* @returns The effect to be used on atoms.
|
|
70
|
+
**/
|
|
71
|
+
exports.migration = (0, createEffect_1.createEffect)({
|
|
62
72
|
didInit: ({ atom, options }) => {
|
|
63
73
|
var _a, _b;
|
|
64
74
|
const steps = sortMigrations(options.steps);
|
|
@@ -87,5 +97,11 @@ exports.migration = (0, middleware_1.middleware)({
|
|
|
87
97
|
atom.set(data);
|
|
88
98
|
},
|
|
89
99
|
});
|
|
100
|
+
/** Helper to create a step for the migration effect.
|
|
101
|
+
*
|
|
102
|
+
* @param migration Migration step to create.
|
|
103
|
+
*
|
|
104
|
+
* @returns The migration step.
|
|
105
|
+
**/
|
|
90
106
|
const createMigrationStep = (migration) => migration;
|
|
91
107
|
exports.createMigrationStep = createMigrationStep;
|
package/dist/cjs/index.js
CHANGED
|
@@ -15,4 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./base"), exports);
|
|
18
|
-
__exportStar(require("./
|
|
18
|
+
__exportStar(require("./effects"), exports);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Create actions to change the state of an atom.
|
|
2
|
+
*
|
|
3
|
+
* @param atom Atom to be used.
|
|
4
|
+
* @param reducers Reducers for custom actions to set the atoms value.
|
|
5
|
+
*
|
|
6
|
+
* @returns Actions to change the state of the atom.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const counter = createAtom({ defaultValue: 0 })
|
|
10
|
+
* const actions = createActions(counter, {
|
|
11
|
+
* increment: (state) => state + 1,
|
|
12
|
+
* decrement: (state) => state - 1,
|
|
13
|
+
* add: (state, value: number) => state + value,
|
|
14
|
+
* subtract: (state, value: number) => state - value,
|
|
15
|
+
* })
|
|
16
|
+
* actions.increment()
|
|
17
|
+
* actions.add(5)
|
|
18
|
+
**/
|
|
19
|
+
export const createActions = (atom, reducers) => Object.entries(reducers).reduce((result, [key, reducerFn]) => {
|
|
20
|
+
result[key] = (...payloadArgs) => {
|
|
21
|
+
atom.set((state) => reducerFn(state, ...payloadArgs));
|
|
22
|
+
};
|
|
23
|
+
return result;
|
|
24
|
+
}, {});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createActions } from "./createActions";
|
|
2
|
+
import { Stateful } from "./Stateful";
|
|
3
|
+
import { EffectDispatcher } from "../effects/EffectDispatcher";
|
|
4
|
+
let key = 0;
|
|
5
|
+
export class Atom extends Stateful {
|
|
6
|
+
constructor({ defaultValue, name = `atom-${++key}`, effects, reducers = {}, }) {
|
|
7
|
+
super(defaultValue);
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.defaultValue = defaultValue;
|
|
10
|
+
this.actions = createActions(this, reducers);
|
|
11
|
+
if (!effects || effects.length === 0) {
|
|
12
|
+
this.didInit = true;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const { didInit } = new EffectDispatcher({ atom: this, effects });
|
|
16
|
+
this.setDidInit(didInit);
|
|
17
|
+
}
|
|
18
|
+
/** Set the value of the atom.
|
|
19
|
+
*
|
|
20
|
+
* @param next New value or function to create the
|
|
21
|
+
* new value based off the previous value.
|
|
22
|
+
*/
|
|
23
|
+
set(next) {
|
|
24
|
+
const value = next instanceof Function ? next(this.get()) : next;
|
|
25
|
+
super.update(value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Creates an atom store.
|
|
29
|
+
*
|
|
30
|
+
* @param config.defaultValue Value that will be used initially.
|
|
31
|
+
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
32
|
+
* @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
|
+
*
|
|
35
|
+
* @returns An atom instance.
|
|
36
|
+
* - `result.get`: Read the value of state.
|
|
37
|
+
* - `result.subscribe`: Subscribe to value changes.
|
|
38
|
+
* - `result.set`: Set the value of the atom.
|
|
39
|
+
* - `result.actions`: All actions that were created with reducers.
|
|
40
|
+
* - `result.didInit`: State of the atom's effects initialization process.
|
|
41
|
+
* Will be a promise if the initialization is pending and `true` if finished.
|
|
42
|
+
**/
|
|
43
|
+
export const createAtom = (config) => new Atom(config);
|
|
@@ -68,7 +68,7 @@ export class SettableDerive extends Derive {
|
|
|
68
68
|
return Array.from(setterDependencies).every(dependency => getterDependencies.has(dependency));
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
export function
|
|
71
|
+
export function createDerived(getter, setter) {
|
|
72
72
|
if (setter) {
|
|
73
73
|
return new SettableDerive(getter, setter);
|
|
74
74
|
}
|
|
@@ -2,7 +2,7 @@ import { Stateful } from "./Stateful";
|
|
|
2
2
|
const selectValue = (state, path) => path
|
|
3
3
|
.split(".")
|
|
4
4
|
.reduce((result, key) => result[key], state);
|
|
5
|
-
export class
|
|
5
|
+
export class Selector extends Stateful {
|
|
6
6
|
constructor(parent, path) {
|
|
7
7
|
super(selectValue(parent.get(), path));
|
|
8
8
|
this.parent = parent;
|
|
@@ -16,6 +16,6 @@ export class Select extends Stateful {
|
|
|
16
16
|
* @param parent The parent element to select a value from. The internal state must be an object.
|
|
17
17
|
* @param path The path to the value you want to select.
|
|
18
18
|
*
|
|
19
|
-
* @returns A
|
|
19
|
+
* @returns A selector instance.
|
|
20
20
|
**/
|
|
21
|
-
export const
|
|
21
|
+
export const createSelector = (parent, path) => new Selector(parent, path);
|
package/dist/mjs/base/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { isPromiseLike } from "@yaasl/utils";
|
|
2
2
|
import { Scheduler } from "../utils/Scheduler";
|
|
3
|
-
export class
|
|
4
|
-
constructor({ atom,
|
|
3
|
+
export class EffectDispatcher {
|
|
4
|
+
constructor({ atom, effects }) {
|
|
5
5
|
this.didInit = false;
|
|
6
|
-
this.
|
|
6
|
+
this.effects = [];
|
|
7
7
|
this.scheduler = new Scheduler();
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
8
|
+
this.effects = effects.map(create => create(atom));
|
|
9
|
+
this.callEffectAction("init", atom);
|
|
10
10
|
this.subscribeSetters(atom);
|
|
11
|
-
this.
|
|
11
|
+
this.callEffectAction("didInit", atom);
|
|
12
12
|
const { queue } = this.scheduler;
|
|
13
13
|
if (!isPromiseLike(queue)) {
|
|
14
14
|
this.didInit = true;
|
|
@@ -19,12 +19,12 @@ export class MiddlewareDispatcher {
|
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
subscribeSetters(atom) {
|
|
22
|
-
atom.subscribe(value => this.
|
|
22
|
+
atom.subscribe(value => this.callEffectAction("set", atom, () => value));
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
callEffectAction(action, atom,
|
|
25
25
|
/* Must be a function to make sure it is using the latest value when used in promise */
|
|
26
26
|
getValue = () => atom.get()) {
|
|
27
|
-
const tasks = this.
|
|
27
|
+
const tasks = this.effects.map(({ actions, options }) => {
|
|
28
28
|
const actionFn = actions[action];
|
|
29
29
|
return () => actionFn?.({ value: getValue(), atom, options });
|
|
30
30
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Create effects to be used in combination with atoms.
|
|
2
|
+
*
|
|
3
|
+
* Effects can be used to interact with an atom by using the following lifecycle actions:
|
|
4
|
+
*
|
|
5
|
+
* - `init`: Action to be called when the atom is created, but before subscribing to `set` events.
|
|
6
|
+
* May return a promise that can be awaited by using `atom.didInit`.
|
|
7
|
+
* - `didInit`: Action to be called when the atom is created, but after subscribing to `set` events.
|
|
8
|
+
* May return a promise that can be awaited by using `atom.didInit`.
|
|
9
|
+
* - `set`: Action to be called when the atom's `set` function is called.
|
|
10
|
+
*
|
|
11
|
+
* @param setup Effect actions or function to create effect actions.
|
|
12
|
+
* Effect actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
13
|
+
*
|
|
14
|
+
* @returns The effect to be used on atoms.
|
|
15
|
+
**/
|
|
16
|
+
export const createEffect = (setup) => (...[optionsArg]) => (atom) => {
|
|
17
|
+
const options = optionsArg;
|
|
18
|
+
const actions = setup instanceof Function ? setup({ options, atom }) : setup;
|
|
19
|
+
return {
|
|
20
|
+
options,
|
|
21
|
+
actions,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEffect } from "./createEffect";
|
|
2
2
|
import { CONFIG } from "../base";
|
|
3
3
|
import { Expiration } from "../utils/Expiration";
|
|
4
4
|
const STORAGE = window.localStorage;
|
|
@@ -10,7 +10,7 @@ const syncOverBrowserTabs = (observingKey, onChange) => window.addEventListener(
|
|
|
10
10
|
return;
|
|
11
11
|
onChange(newValue);
|
|
12
12
|
});
|
|
13
|
-
/**
|
|
13
|
+
/** Effect to make an atom value expirable and reset to its defaulValue.
|
|
14
14
|
*
|
|
15
15
|
* __Note:__ When using `expiresAt`, a function returning the date should be prefered since using a static date might end in an infinite loop.
|
|
16
16
|
*
|
|
@@ -18,9 +18,9 @@ const syncOverBrowserTabs = (observingKey, onChange) => window.addEventListener(
|
|
|
18
18
|
* @param options.expiresAt Date at which the value expires
|
|
19
19
|
* @param options.expiresIn Milliseconds in which the value expires. Will be ignored if expiresAt is set.
|
|
20
20
|
*
|
|
21
|
-
* @returns The
|
|
21
|
+
* @returns The effect to be used on atoms.
|
|
22
22
|
**/
|
|
23
|
-
export const expiration =
|
|
23
|
+
export const expiration = createEffect(({ atom, options = {} }) => {
|
|
24
24
|
const hasExpiration = Boolean(options.expiresAt ?? options.expiresIn);
|
|
25
25
|
if (!hasExpiration)
|
|
26
26
|
return {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEffect } from "./createEffect";
|
|
2
2
|
import { CONFIG } from "../base";
|
|
3
3
|
import { Store } from "../utils/Store";
|
|
4
4
|
let atomDb = null;
|
|
@@ -10,9 +10,9 @@ let atomDb = null;
|
|
|
10
10
|
* @param {IndexedDbOptions | undefined} options
|
|
11
11
|
* @param options.key Use your own store key. Will be `atom.name` by default.
|
|
12
12
|
*
|
|
13
|
-
* @returns The
|
|
13
|
+
* @returns The effect to be used on atoms.
|
|
14
14
|
**/
|
|
15
|
-
export const indexedDb =
|
|
15
|
+
export const indexedDb = createEffect(({ atom, options }) => {
|
|
16
16
|
const key = options?.key ?? atom.name;
|
|
17
17
|
return {
|
|
18
18
|
init: ({ atom }) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEffect } from "./createEffect";
|
|
2
2
|
import { CONFIG } from "../base";
|
|
3
3
|
import { LocalStorage } from "../utils/LocalStorage";
|
|
4
4
|
/** Middleware to save and load atom values to the local storage.
|
|
@@ -9,9 +9,9 @@ import { LocalStorage } from "../utils/LocalStorage";
|
|
|
9
9
|
* @param options.noTabSync Disable the synchronization of values over browser tabs.
|
|
10
10
|
* @param options.parser Custom functions to stringify and parse values. Defaults to JSON.stringify and JSON.parse. Use this when handling complex datatypes like Maps or Sets.
|
|
11
11
|
*
|
|
12
|
-
* @returns The
|
|
12
|
+
* @returns The effect to be used on atoms.
|
|
13
13
|
**/
|
|
14
|
-
export const localStorage =
|
|
14
|
+
export const localStorage = createEffect(({ atom, options = {} }) => {
|
|
15
15
|
const internalKey = CONFIG.name ? `${CONFIG.name}/${atom.name}` : atom.name;
|
|
16
16
|
const { key = internalKey, parser, noTabSync } = options;
|
|
17
17
|
const storage = new LocalStorage(key, {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { log } from "@yaasl/utils";
|
|
2
|
-
import {
|
|
2
|
+
import { createEffect } from "./createEffect";
|
|
3
3
|
import { CONFIG } from "../base";
|
|
4
4
|
const sortMigrations = (migrations) => {
|
|
5
5
|
const first = migrations.find(migration => migration.previous === null);
|
|
@@ -55,7 +55,17 @@ const performMigration = (atom, version, migrations) => {
|
|
|
55
55
|
return migrateVersion(atom, data, migration);
|
|
56
56
|
}, currentState);
|
|
57
57
|
};
|
|
58
|
-
|
|
58
|
+
/** Effect to migrate the persisted value of an atom to a newer version.
|
|
59
|
+
* You can use the `createMigrationStep` helper to create migration steps.
|
|
60
|
+
*
|
|
61
|
+
* @param {MigrationOptions} options
|
|
62
|
+
* @param options.steps An array of migration steps to perform for outdated values.
|
|
63
|
+
*
|
|
64
|
+
* __Note:__ One step must have a `previous` version set to null as entry point.
|
|
65
|
+
*
|
|
66
|
+
* @returns The effect to be used on atoms.
|
|
67
|
+
**/
|
|
68
|
+
export const migration = createEffect({
|
|
59
69
|
didInit: ({ atom, options }) => {
|
|
60
70
|
const steps = sortMigrations(options.steps);
|
|
61
71
|
const currentVersion = getVersion(atom);
|
|
@@ -83,4 +93,10 @@ export const migration = middleware({
|
|
|
83
93
|
atom.set(data);
|
|
84
94
|
},
|
|
85
95
|
});
|
|
96
|
+
/** Helper to create a step for the migration effect.
|
|
97
|
+
*
|
|
98
|
+
* @param migration Migration step to create.
|
|
99
|
+
*
|
|
100
|
+
* @returns The migration step.
|
|
101
|
+
**/
|
|
86
102
|
export const createMigrationStep = (migration) => migration;
|
package/dist/mjs/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./base";
|
|
2
|
-
export * from "./
|
|
2
|
+
export * from "./effects";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaasl/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "yet another atomic store library (vanilla-js)",
|
|
5
5
|
"author": "PrettyCoffee",
|
|
6
6
|
"license": "MIT",
|
|
@@ -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.9.0"
|
|
42
42
|
},
|
|
43
43
|
"eslintConfig": {
|
|
44
44
|
"extends": [
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { SetStateAction } from "@yaasl/utils";
|
|
2
|
-
import { Stateful } from "./Stateful";
|
|
3
|
-
import { MiddlewareAtomCallback } from "../middleware/middleware";
|
|
4
|
-
export interface AtomConfig<Value> {
|
|
5
|
-
/** Value that will be returned if the atom is not defined in the store */
|
|
6
|
-
defaultValue: Value;
|
|
7
|
-
/** Name of the atom. Must be unique among all atoms. */
|
|
8
|
-
name?: string;
|
|
9
|
-
/** Middleware that will be applied on the atom */
|
|
10
|
-
middleware?: MiddlewareAtomCallback<any>[];
|
|
11
|
-
}
|
|
12
|
-
export declare class Atom<Value = unknown> extends Stateful<Value> {
|
|
13
|
-
readonly defaultValue: Value;
|
|
14
|
-
readonly name: string;
|
|
15
|
-
constructor({ defaultValue, name, middleware, }: AtomConfig<Value>);
|
|
16
|
-
/** Set the value of the atom.
|
|
17
|
-
*
|
|
18
|
-
* @param next New value or function to create the
|
|
19
|
-
* new value based off the previous value.
|
|
20
|
-
*/
|
|
21
|
-
set(next: SetStateAction<Value>): void;
|
|
22
|
-
}
|
|
23
|
-
/** Creates an atom store.
|
|
24
|
-
*
|
|
25
|
-
* @param config.defaultValue Value that will be used initially.
|
|
26
|
-
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
27
|
-
* @param config.middleware Middleware that will be applied on the atom.
|
|
28
|
-
*
|
|
29
|
-
* @returns An atom instance.
|
|
30
|
-
**/
|
|
31
|
-
export declare const atom: <Value>(config: AtomConfig<Value>) => Atom<Value>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { MiddlewareAtomCallback } from "./middleware";
|
|
2
|
-
import { Atom } from "../base/atom";
|
|
3
|
-
interface MiddlewareDispatcherConstructor {
|
|
4
|
-
atom: Atom<any>;
|
|
5
|
-
middleware: MiddlewareAtomCallback<any>[];
|
|
6
|
-
}
|
|
7
|
-
export declare class MiddlewareDispatcher {
|
|
8
|
-
didInit: PromiseLike<void> | boolean;
|
|
9
|
-
private middleware;
|
|
10
|
-
private scheduler;
|
|
11
|
-
constructor({ atom, middleware }: MiddlewareDispatcherConstructor);
|
|
12
|
-
private subscribeSetters;
|
|
13
|
-
private callMiddlewareAction;
|
|
14
|
-
}
|
|
15
|
-
export {};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Atom } from "../base";
|
|
2
|
-
export type ActionType = "init" | "didInit" | "set";
|
|
3
|
-
interface MiddlewarePayload<Options> {
|
|
4
|
-
value: unknown;
|
|
5
|
-
atom: Atom;
|
|
6
|
-
options: Options;
|
|
7
|
-
}
|
|
8
|
-
export interface MiddlewareActions<Options> {
|
|
9
|
-
init?: (payload: MiddlewarePayload<Options>) => Promise<any> | void;
|
|
10
|
-
didInit?: (payload: MiddlewarePayload<Options>) => Promise<any> | void;
|
|
11
|
-
set?: (payload: MiddlewarePayload<Options>) => void;
|
|
12
|
-
}
|
|
13
|
-
interface MiddlewareSetupProps<Options> {
|
|
14
|
-
atom: Atom;
|
|
15
|
-
options: Options;
|
|
16
|
-
}
|
|
17
|
-
export type MiddlewareAtomCallback<Options> = (atom: Atom<any>) => Middleware<Options>;
|
|
18
|
-
export type MiddlewareSetup<Options> = MiddlewareActions<Options> | ((props: MiddlewareSetupProps<Options>) => MiddlewareActions<Options>);
|
|
19
|
-
export interface Middleware<Options = unknown> {
|
|
20
|
-
options: Options;
|
|
21
|
-
actions: MiddlewareActions<Options>;
|
|
22
|
-
}
|
|
23
|
-
/** Create middlewares to be used in combination with atoms.
|
|
24
|
-
*
|
|
25
|
-
* @param setup Middleware actions or function to create middleware actions.
|
|
26
|
-
* Middleware actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
27
|
-
*
|
|
28
|
-
* @returns A middleware function to be used in atoms.
|
|
29
|
-
**/
|
|
30
|
-
export declare const middleware: <Options = undefined>(setup: MiddlewareSetup<Options>) => (...[optionsArg]: Options extends undefined ? [Options] | [] : [Options]) => MiddlewareAtomCallback<Options>;
|
|
31
|
-
export {};
|
package/dist/cjs/base/atom.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.atom = exports.Atom = void 0;
|
|
4
|
-
const Stateful_1 = require("./Stateful");
|
|
5
|
-
const MiddlewareDispatcher_1 = require("../middleware/MiddlewareDispatcher");
|
|
6
|
-
let key = 0;
|
|
7
|
-
class Atom extends Stateful_1.Stateful {
|
|
8
|
-
constructor({ defaultValue, name = `atom-${++key}`, middleware, }) {
|
|
9
|
-
super(defaultValue);
|
|
10
|
-
this.name = name;
|
|
11
|
-
this.defaultValue = defaultValue;
|
|
12
|
-
if (!middleware || middleware.length === 0) {
|
|
13
|
-
this.didInit = true;
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const { didInit } = new MiddlewareDispatcher_1.MiddlewareDispatcher({ atom: this, middleware });
|
|
17
|
-
this.setDidInit(didInit);
|
|
18
|
-
}
|
|
19
|
-
/** Set the value of the atom.
|
|
20
|
-
*
|
|
21
|
-
* @param next New value or function to create the
|
|
22
|
-
* new value based off the previous value.
|
|
23
|
-
*/
|
|
24
|
-
set(next) {
|
|
25
|
-
const value = next instanceof Function ? next(this.get()) : next;
|
|
26
|
-
super.update(value);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
exports.Atom = Atom;
|
|
30
|
-
/** Creates an atom store.
|
|
31
|
-
*
|
|
32
|
-
* @param config.defaultValue Value that will be used initially.
|
|
33
|
-
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
34
|
-
* @param config.middleware Middleware that will be applied on the atom.
|
|
35
|
-
*
|
|
36
|
-
* @returns An atom instance.
|
|
37
|
-
**/
|
|
38
|
-
const atom = (config) => new Atom(config);
|
|
39
|
-
exports.atom = atom;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.middleware = void 0;
|
|
4
|
-
/** Create middlewares to be used in combination with atoms.
|
|
5
|
-
*
|
|
6
|
-
* @param setup Middleware actions or function to create middleware actions.
|
|
7
|
-
* Middleware actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
8
|
-
*
|
|
9
|
-
* @returns A middleware function to be used in atoms.
|
|
10
|
-
**/
|
|
11
|
-
const middleware = (setup) => (...[optionsArg]) => (atom) => {
|
|
12
|
-
const options = optionsArg;
|
|
13
|
-
const actions = setup instanceof Function ? setup({ options, atom }) : setup;
|
|
14
|
-
return {
|
|
15
|
-
options,
|
|
16
|
-
actions,
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
exports.middleware = middleware;
|
package/dist/mjs/base/atom.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Stateful } from "./Stateful";
|
|
2
|
-
import { MiddlewareDispatcher } from "../middleware/MiddlewareDispatcher";
|
|
3
|
-
let key = 0;
|
|
4
|
-
export class Atom extends Stateful {
|
|
5
|
-
constructor({ defaultValue, name = `atom-${++key}`, middleware, }) {
|
|
6
|
-
super(defaultValue);
|
|
7
|
-
this.name = name;
|
|
8
|
-
this.defaultValue = defaultValue;
|
|
9
|
-
if (!middleware || middleware.length === 0) {
|
|
10
|
-
this.didInit = true;
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
const { didInit } = new MiddlewareDispatcher({ atom: this, middleware });
|
|
14
|
-
this.setDidInit(didInit);
|
|
15
|
-
}
|
|
16
|
-
/** Set the value of the atom.
|
|
17
|
-
*
|
|
18
|
-
* @param next New value or function to create the
|
|
19
|
-
* new value based off the previous value.
|
|
20
|
-
*/
|
|
21
|
-
set(next) {
|
|
22
|
-
const value = next instanceof Function ? next(this.get()) : next;
|
|
23
|
-
super.update(value);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/** Creates an atom store.
|
|
27
|
-
*
|
|
28
|
-
* @param config.defaultValue Value that will be used initially.
|
|
29
|
-
* @param config.name Name of the atom. Must be unique among all atoms. Defaults to "atom-{number}".
|
|
30
|
-
* @param config.middleware Middleware that will be applied on the atom.
|
|
31
|
-
*
|
|
32
|
-
* @returns An atom instance.
|
|
33
|
-
**/
|
|
34
|
-
export const atom = (config) => new Atom(config);
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** Create middlewares to be used in combination with atoms.
|
|
2
|
-
*
|
|
3
|
-
* @param setup Middleware actions or function to create middleware actions.
|
|
4
|
-
* Middleware actions are fired in the atom lifecycle, alongside to the subscriptions.
|
|
5
|
-
*
|
|
6
|
-
* @returns A middleware function to be used in atoms.
|
|
7
|
-
**/
|
|
8
|
-
export const middleware = (setup) => (...[optionsArg]) => (atom) => {
|
|
9
|
-
const options = optionsArg;
|
|
10
|
-
const actions = setup instanceof Function ? setup({ options, atom }) : setup;
|
|
11
|
-
return {
|
|
12
|
-
options,
|
|
13
|
-
actions,
|
|
14
|
-
};
|
|
15
|
-
};
|