@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.
Files changed (45) hide show
  1. package/dist/@types/base/config.d.ts +3 -0
  2. package/dist/@types/base/createActions.d.ts +1 -1
  3. package/dist/@types/base/createAtom.d.ts +9 -13
  4. package/dist/@types/base/createDerived.d.ts +4 -4
  5. package/dist/@types/base/createSelector.d.ts +13 -12
  6. package/dist/@types/base/createSlice.d.ts +35 -0
  7. package/dist/@types/base/index.d.ts +2 -0
  8. package/dist/@types/effects/EffectDispatcher.d.ts +11 -11
  9. package/dist/@types/effects/createEffect.d.ts +14 -5
  10. package/dist/@types/effects/index.d.ts +1 -1
  11. package/dist/@types/effects/indexedDb.d.ts +1 -1
  12. package/dist/@types/effects/localStorage.d.ts +1 -1
  13. package/dist/@types/utils/Queue.d.ts +8 -0
  14. package/dist/cjs/base/config.js +1 -0
  15. package/dist/cjs/base/createActions.js +1 -1
  16. package/dist/cjs/base/createAtom.js +32 -10
  17. package/dist/cjs/base/createDerived.js +2 -2
  18. package/dist/cjs/base/createSlice.js +38 -0
  19. package/dist/cjs/base/index.js +2 -0
  20. package/dist/cjs/effects/EffectDispatcher.js +38 -28
  21. package/dist/cjs/effects/createEffect.js +1 -1
  22. package/dist/cjs/effects/indexedDb.js +11 -12
  23. package/dist/cjs/effects/localStorage.js +9 -7
  24. package/dist/cjs/effects/migration.js +2 -2
  25. package/dist/cjs/utils/Queue.js +27 -0
  26. package/dist/mjs/base/Stateful.js +4 -3
  27. package/dist/mjs/base/config.js +1 -0
  28. package/dist/mjs/base/createActions.js +1 -1
  29. package/dist/mjs/base/createAtom.js +37 -10
  30. package/dist/mjs/base/createDerived.js +9 -7
  31. package/dist/mjs/base/createSlice.js +34 -0
  32. package/dist/mjs/base/index.js +2 -0
  33. package/dist/mjs/effects/EffectDispatcher.js +40 -28
  34. package/dist/mjs/effects/createEffect.js +1 -1
  35. package/dist/mjs/effects/indexedDb.js +8 -9
  36. package/dist/mjs/effects/localStorage.js +9 -7
  37. package/dist/mjs/effects/migration.js +2 -2
  38. package/dist/mjs/utils/Expiration.js +9 -7
  39. package/dist/mjs/utils/LocalStorage.js +2 -0
  40. package/dist/mjs/utils/Queue.js +21 -0
  41. package/dist/mjs/utils/Store.js +2 -0
  42. package/package.json +3 -3
  43. package/dist/@types/utils/Scheduler.d.ts +0 -7
  44. package/dist/cjs/utils/Scheduler.js +0 -27
  45. 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 atoms value.
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 { SetStateAction } from "@yaasl/utils";
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, R extends Reducers<Value> = Reducers<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, R extends Reducers<Value> = Reducers<Value>> extends Stateful<Value> {
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
- /** Actions that can be used to set the atom's value. */
21
- readonly actions: Actions<Value, R>;
22
- constructor({ defaultValue, name, effects, reducers, }: AtomConfig<Value, R>);
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: SetStateAction<Value>): void;
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, R extends Reducers<Value> = Reducers<Value>>(config: AtomConfig<Value, R>) => Atom<Value, R>;
35
+ export declare const createAtom: <Value>(config: AtomConfig<Value>) => Atom<Value>;
@@ -1,4 +1,4 @@
1
- import { SetStateAction } from "@yaasl/utils";
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: SetStateAction<V>) => void;
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, import("./createActions").Reducers<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
  *
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: SetStateAction<Value>): void;
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 PathableState = Record<string | number, unknown>;
3
- type DeepKeys<State> = State extends PathableState ? {
4
- [K in keyof State]: `${Exclude<K, symbol>}${"" | `.${DeepKeys<State[K]>}`}`;
5
- }[keyof State] : never;
6
- type DeepValue<State, Path> = State extends PathableState ? Path extends `${infer Current}.${infer Next}` ? DeepValue<State[Current], Next> : State[Path & string] : never;
7
- export declare class PathSelector<State, Path extends DeepKeys<State>> extends Stateful<DeepValue<State, Path>> {
8
- constructor(atom: Stateful<State>, path: Path);
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<Atoms extends readonly unknown[], States extends unknown[] = []> = Atoms extends [Stateful<infer Value>, ...infer Rest] ? InferValuesFromAtoms<Rest, [...States, Value]> : States;
11
- export declare class CombinerSelector<State, Atoms extends [Stateful<any>, ...Stateful<any>[]]> extends Stateful<State> {
12
- constructor(atoms: Atoms, combiner: (...res: InferValuesFromAtoms<Atoms>) => State);
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
- <State, Path extends DeepKeys<State>>(atom: Stateful<State>, path: Path): PathSelector<State, Path>;
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
- <Atoms extends [Stateful<any>, ...Stateful<any>[]], CombinedState>(states: Atoms, combiner: (...res: InferValuesFromAtoms<Atoms>) => CombinedState): CombinerSelector<CombinedState, Atoms>;
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,5 +1,7 @@
1
1
  export * from "./config";
2
+ export * from "./createActions";
2
3
  export * from "./createAtom";
3
4
  export * from "./createDerived";
4
5
  export * from "./createSelector";
6
+ export * from "./createSlice";
5
7
  export * from "./Stateful";
@@ -1,15 +1,15 @@
1
- import { EffectAtomCallback } from "./createEffect";
1
+ import { ActionType, EffectAtomCallback } from "./createEffect";
2
2
  import { Atom } from "../base/createAtom";
3
- interface EffectDispatcherConstructor {
4
- atom: Atom<any>;
5
- effects: EffectAtomCallback<any>[];
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 scheduler;
11
- constructor({ atom, effects }: EffectDispatcherConstructor);
12
- private subscribeSetters;
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
- init?: (payload: EffectPayload<Options, AtomValue>) => Promise<any> | void;
10
- didInit?: (payload: EffectPayload<Options, AtomValue>) => Promise<any> | void;
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 `set` function is called.
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, any>;
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, any>;
28
+ export declare const localStorage: (...[optionsArg]: [] | [undefined] | [LocalStorageOptions]) => import("./createEffect").EffectAtomCallback<LocalStorageOptions | undefined, unknown>;
@@ -0,0 +1,8 @@
1
+ type Task<Value> = (prev: Value) => PromiseLike<Value> | Value;
2
+ export declare class Queue<T = void> {
3
+ private last;
4
+ private queue;
5
+ push(...tasks: Task<T>[]): this;
6
+ run(prev: T): PromiseLike<T>;
7
+ }
8
+ export {};
@@ -8,4 +8,5 @@ exports.CONFIG = void 0;
8
8
  **/
9
9
  exports.CONFIG = {
10
10
  name: undefined,
11
+ globalEffects: [],
11
12
  };
@@ -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 atoms value.
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 createActions_1 = require("./createActions");
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, reducers = {}, }) {
10
+ constructor({ defaultValue, name = `atom-${++key}`, effects: localEffects = [], }) {
10
11
  super(defaultValue);
11
12
  this.name = name;
12
13
  this.defaultValue = defaultValue;
13
- this.actions = (0, createActions_1.createActions)(this, reducers);
14
- if (!effects || effects.length === 0) {
15
- this.didInit = true;
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 { didInit } = new EffectDispatcher_1.EffectDispatcher({ atom: this, effects });
19
- this.setDidInit(didInit);
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 value = next instanceof Function ? next(this.get()) : next;
28
- super.update(value);
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 instanceof Function ? next(this.get()) : 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 instanceof Function ? next(atom.get()) : 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;
@@ -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 Scheduler_1 = require("../utils/Scheduler");
6
- class EffectDispatcher {
7
- constructor({ atom, effects }) {
8
- this.didInit = false;
9
- this.effects = [];
10
- this.scheduler = new Scheduler_1.Scheduler();
11
- this.effects = effects.map(create => create(atom));
12
- this.callEffectAction("init", atom);
13
- this.subscribeSetters(atom);
14
- this.callEffectAction("didInit", atom);
15
- const { queue } = this.scheduler;
16
- if (!(0, utils_1.isPromiseLike)(queue)) {
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
- this.didInit = queue.then(() => {
21
- this.didInit = true;
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
- subscribeSetters(atom) {
25
- atom.subscribe(value => this.callEffectAction("set", atom, () => value));
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
- callEffectAction(action, atom,
28
- /* Must be a function to make sure it is using the latest value when used in promise */
29
- getValue = () => atom.get()) {
30
- const tasks = this.effects.map(({ actions, options }) => {
31
- const actionFn = actions[action];
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 `set` function is called.
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 _a;
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((_a = base_1.CONFIG.name) !== null && _a !== void 0 ? _a : "yaasl");
34
+ atomDb = new Store_1.Store((_c = base_1.CONFIG.name) !== null && _c !== void 0 ? _c : "yaasl");
35
35
  }
36
- return atomDb.get(key).then((value) => __awaiter(void 0, void 0, void 0, function* () {
37
- if (value !== undefined) {
38
- atom.set(value);
39
- }
40
- else {
41
- yield (atomDb === null || atomDb === void 0 ? void 0 : atomDb.set(key, atom.defaultValue));
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: ({ atom }) => {
25
+ init: ({ set }) => {
26
26
  const existing = storage.get();
27
- if (existing === null) {
28
- storage.set(atom.defaultValue);
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
- atom.set(existing);
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
- atom.set(data);
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
  *
@@ -5,4 +5,5 @@
5
5
  **/
6
6
  export const CONFIG = {
7
7
  name: undefined,
8
+ globalEffects: [],
8
9
  };
@@ -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 atoms value.
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 { createActions } from "./createActions";
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
- constructor({ defaultValue, name = `atom-${++key}`, effects, reducers = {}, }) {
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
- this.actions = createActions(this, reducers);
11
- if (!effects || effects.length === 0) {
12
- this.didInit = true;
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 { didInit } = new EffectDispatcher({ atom: this, effects });
16
- this.setDidInit(didInit);
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 value = next instanceof Function ? next(this.get()) : next;
25
- super.update(value);
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 instanceof Function ? next(this.get()) : next;
51
+ const value = updater(next, this.get());
50
52
  this.setter({
51
53
  value,
52
54
  set: (atom, next) => {
53
- const value = next instanceof Function ? next(atom.get()) : 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
+ };
@@ -1,5 +1,7 @@
1
1
  export * from "./config";
2
+ export * from "./createActions";
2
3
  export * from "./createAtom";
3
4
  export * from "./createDerived";
4
5
  export * from "./createSelector";
6
+ export * from "./createSlice";
5
7
  export * from "./Stateful";
@@ -1,33 +1,45 @@
1
- import { isPromiseLike } from "@yaasl/utils";
2
- import { Scheduler } from "../utils/Scheduler";
3
- export class EffectDispatcher {
4
- constructor({ atom, effects }) {
5
- this.didInit = false;
6
- this.effects = [];
7
- this.scheduler = new Scheduler();
8
- this.effects = effects.map(create => create(atom));
9
- this.callEffectAction("init", atom);
10
- this.subscribeSetters(atom);
11
- this.callEffectAction("didInit", atom);
12
- const { queue } = this.scheduler;
13
- if (!isPromiseLike(queue)) {
14
- this.didInit = true;
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
- this.didInit = queue.then(() => {
18
- this.didInit = true;
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
- subscribeSetters(atom) {
22
- atom.subscribe(value => this.callEffectAction("set", atom, () => value));
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
- callEffectAction(action, atom,
25
- /* Must be a function to make sure it is using the latest value when used in promise */
26
- getValue = () => atom.get()) {
27
- const tasks = this.effects.map(({ actions, options }) => {
28
- const actionFn = actions[action];
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 `set` function is called.
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
- return atomDb.get(key).then(async (value) => {
23
- if (value !== undefined) {
24
- atom.set(value);
25
- }
26
- else {
27
- await atomDb?.set(key, atom.defaultValue);
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: ({ atom }) => {
22
+ init: ({ set }) => {
23
23
  const existing = storage.get();
24
- if (existing === null) {
25
- storage.set(atom.defaultValue);
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
- atom.set(existing);
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
- atom.set(data);
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
+ }
@@ -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.9.2",
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 es2020 -t es2020",
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.9.2"
41
+ "@yaasl/utils": "0.10.0-alpha.1"
42
42
  },
43
43
  "eslintConfig": {
44
44
  "extends": [
@@ -1,7 +0,0 @@
1
- type Task = () => PromiseLike<unknown> | unknown;
2
- export declare class Scheduler {
3
- queue: PromiseLike<void> | null;
4
- run(...tasks: Task[] | Task[][]): PromiseLike<void>;
5
- private execute;
6
- }
7
- export {};
@@ -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
- }