@yaasl/core 0.10.1 → 0.11.0-alpha.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.
@@ -0,0 +1,13 @@
1
+ import type { Callback, Stateful } from "./Stateful";
2
+ export declare class Destroyable {
3
+ isDestroyed: boolean;
4
+ private dependents;
5
+ private unsubscribers;
6
+ /** Make this atom unusable and remove all references.
7
+ **/
8
+ destroy(): void;
9
+ /** Subscribe to another atom and automatically destroy the atom instance, if the parent is destroyed */
10
+ protected subscribeTo<T>(parent: Stateful<T>, callback: Callback<T>): void;
11
+ protected addDependent(dependent: Destroyable): void;
12
+ protected removeDependent(dependent: Destroyable): void;
13
+ }
@@ -1,5 +1,6 @@
1
- type Callback<Value> = (value: Value, previous: Value) => void;
2
- export declare class Stateful<Value = unknown> {
1
+ import { Destroyable } from "./Destroyable";
2
+ export type Callback<Value> = (value: Value, previous: Value) => void;
3
+ export declare class Stateful<Value = unknown> extends Destroyable {
3
4
  protected value: Value;
4
5
  /** Promise that resolves when the states initialization was finished. */
5
6
  didInit: PromiseLike<void> | boolean;
@@ -17,8 +18,10 @@ export declare class Stateful<Value = unknown> {
17
18
  * @returns A callback to unsubscribe the passed callback.
18
19
  */
19
20
  subscribe(callback: Callback<Value>): () => boolean;
21
+ /** Make this atom unusable and remove all references.
22
+ **/
23
+ destroy(): void;
20
24
  private emit;
21
25
  protected update(value: Value): void;
22
26
  protected setDidInit(didInit: boolean | PromiseLike<void>): void;
23
27
  }
24
- export {};
@@ -1,4 +1,4 @@
1
- import { EffectAtomCallback } from "../effects/createEffect";
1
+ import type { EffectAtomCallback } from "../effects/createEffect";
2
2
  interface Config {
3
3
  /** Global name to make internal keys unique
4
4
  * among UIs on the same domain.
@@ -1,5 +1,5 @@
1
- import { Atom } from "./createAtom";
2
- import { SettableDerive } from "./createDerived";
1
+ import type { Atom } from "./createAtom";
2
+ import type { SettableDerive } from "./createDerived";
3
3
  type Reducer<State> = (state: State, ...payloadArgs: any[]) => State;
4
4
  export type Reducers<State> = Record<string, Reducer<State>>;
5
5
  type Payload<R extends Reducer<any>> = Parameters<R> extends [
@@ -1,6 +1,6 @@
1
1
  import { Updater } from "@yaasl/utils";
2
2
  import { Stateful } from "./Stateful";
3
- import { EffectAtomCallback } from "../effects/createEffect";
3
+ import type { EffectAtomCallback } from "../effects/createEffect";
4
4
  export interface AtomConfig<Value> {
5
5
  /** Value that will be used initially. */
6
6
  defaultValue: Value;
@@ -1,5 +1,5 @@
1
1
  import { Updater } from "@yaasl/utils";
2
- import { Atom } from "./createAtom";
2
+ import type { Atom } from "./createAtom";
3
3
  import { Stateful } from "./Stateful";
4
4
  type GetterFn<Value> = (context: {
5
5
  get: <V>(dep: Stateful<V>) => V;
@@ -1,5 +1,5 @@
1
- import { ActionType, EffectAtomCallback } from "./createEffect";
2
- import { Atom } from "../base/createAtom";
1
+ import type { ActionType, EffectAtomCallback } from "./createEffect";
2
+ import type { Atom } from "../base/createAtom";
3
3
  export declare class EffectActions<Value> {
4
4
  private readonly atom;
5
5
  private options;
@@ -1,5 +1,5 @@
1
- import { Updater, Dispatch } from "@yaasl/utils";
2
- import { Atom } from "../base";
1
+ import type { Updater, Dispatch } from "@yaasl/utils";
2
+ import type { Atom } from "../base";
3
3
  export type ActionType = "init" | "didInit" | "set";
4
4
  export interface EffectPayload<Options = undefined, AtomValue = any> {
5
5
  /** Current value of the atom */
@@ -14,4 +14,4 @@ export interface ExpirationOptions {
14
14
  *
15
15
  * @returns The effect to be used on atoms.
16
16
  **/
17
- export declare const expiration: (__0_0: ExpirationOptions) => import("./createEffect").EffectAtomCallback<ExpirationOptions, any>;
17
+ export declare const expiration: (optionsArg: ExpirationOptions) => import("./createEffect").EffectAtomCallback<ExpirationOptions, any>;
@@ -25,7 +25,7 @@ export interface MigrationOptions {
25
25
  *
26
26
  * @returns The effect to be used on atoms.
27
27
  **/
28
- export declare const migration: (__0_0: MigrationOptions) => import("./createEffect").EffectAtomCallback<MigrationOptions, unknown>;
28
+ export declare const migration: (optionsArg: MigrationOptions) => import("./createEffect").EffectAtomCallback<MigrationOptions, unknown>;
29
29
  /** Helper to create a step for the migration effect.
30
30
  *
31
31
  * @param migration Migration step to create.
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Destroyable = void 0;
4
+ const utils_1 = require("@yaasl/utils");
5
+ class Destroyable {
6
+ constructor() {
7
+ this.isDestroyed = false;
8
+ this.dependents = new Set();
9
+ this.unsubscribers = new Set();
10
+ }
11
+ /** Make this atom unusable and remove all references.
12
+ **/
13
+ destroy() {
14
+ this.dependents.forEach(dependent => dependent.destroy());
15
+ this.unsubscribers.forEach(unsubscribe => unsubscribe());
16
+ this.dependents.clear();
17
+ this.unsubscribers.clear();
18
+ this.isDestroyed = true;
19
+ const throwOnCall = () => {
20
+ const name = "name" in this ? this.name : undefined;
21
+ throw new Error((0, utils_1.consoleMessage)("The methods of a destroyed atom cannot be called.", {
22
+ scope: name,
23
+ }));
24
+ };
25
+ Object.assign(this, {
26
+ set: throwOnCall,
27
+ get: throwOnCall,
28
+ subscribe: throwOnCall,
29
+ });
30
+ }
31
+ /** Subscribe to another atom and automatically destroy the atom instance, if the parent is destroyed */
32
+ subscribeTo(parent, callback) {
33
+ parent.addDependent(this);
34
+ const unsubscribe = parent.subscribe(callback);
35
+ this.unsubscribers.add(() => {
36
+ unsubscribe();
37
+ parent.removeDependent(this);
38
+ });
39
+ }
40
+ addDependent(dependent) {
41
+ this.dependents.add(dependent);
42
+ }
43
+ removeDependent(dependent) {
44
+ this.dependents.delete(dependent);
45
+ }
46
+ }
47
+ exports.Destroyable = Destroyable;
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Stateful = void 0;
4
- class Stateful {
4
+ const Destroyable_1 = require("./Destroyable");
5
+ class Stateful extends Destroyable_1.Destroyable {
5
6
  constructor(value) {
7
+ super();
6
8
  this.value = value;
7
9
  /** Promise that resolves when the states initialization was finished. */
8
10
  this.didInit = false;
@@ -25,6 +27,13 @@ class Stateful {
25
27
  this.listeners.add(callback);
26
28
  return () => this.listeners.delete(callback);
27
29
  }
30
+ /** Make this atom unusable and remove all references.
31
+ **/
32
+ destroy() {
33
+ super.destroy();
34
+ this.value = null;
35
+ this.listeners = new Set();
36
+ }
28
37
  emit(value, previous) {
29
38
  this.listeners.forEach(listener => listener(value, previous));
30
39
  }
@@ -45,7 +45,10 @@ class Atom extends Stateful_1.Stateful {
45
45
  * new value based off the previous value.
46
46
  */
47
47
  set(next) {
48
- const newState = (0, utils_1.updater)(next, this.get());
48
+ const oldState = this.get();
49
+ const newState = (0, utils_1.updater)(next, oldState);
50
+ if (oldState === newState)
51
+ return;
49
52
  void this.effects
50
53
  .dispatch("set", newState)
51
54
  .then(value => super.update(value));
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createDerived = exports.SettableDerive = exports.Derive = void 0;
3
+ exports.SettableDerive = exports.Derive = void 0;
4
+ exports.createDerived = createDerived;
4
5
  const utils_1 = require("@yaasl/utils");
5
6
  const Stateful_1 = require("./Stateful");
6
7
  const allDidInit = (atoms) => {
@@ -20,7 +21,7 @@ class Derive extends Stateful_1.Stateful {
20
21
  }
21
22
  addGetDependency(dependency) {
22
23
  if (!this.getterDependencies.has(dependency)) {
23
- dependency.subscribe(() => this.deriveUpdate());
24
+ this.subscribeTo(dependency, () => this.deriveUpdate());
24
25
  this.getterDependencies.add(dependency);
25
26
  }
26
27
  return dependency.get();
@@ -50,9 +51,12 @@ class SettableDerive extends Derive {
50
51
  * new value based off the previous value.
51
52
  */
52
53
  set(next) {
53
- const value = (0, utils_1.updater)(next, this.get());
54
+ const oldState = this.get();
55
+ const newState = (0, utils_1.updater)(next, oldState);
56
+ if (oldState === newState)
57
+ return;
54
58
  this.setter({
55
- value,
59
+ value: newState,
56
60
  set: (atom, next) => {
57
61
  const value = (0, utils_1.updater)(next, atom.get());
58
62
  atom.set(value);
@@ -81,4 +85,3 @@ function createDerived(getter, setter) {
81
85
  return new Derive(getter);
82
86
  }
83
87
  }
84
- exports.createDerived = createDerived;
@@ -9,7 +9,7 @@ const selectPath = (state, path) => path
9
9
  class PathSelector extends Stateful_1.Stateful {
10
10
  constructor(atom, path) {
11
11
  super(selectPath(atom.get(), path));
12
- atom.subscribe(state => this.update(selectPath(state, path)));
12
+ this.subscribeTo(atom, state => this.update(selectPath(state, path)));
13
13
  this.setDidInit(atom.didInit);
14
14
  }
15
15
  }
@@ -28,7 +28,7 @@ class CombinerSelector extends Stateful_1.Stateful {
28
28
  return combiner(...values);
29
29
  };
30
30
  super(selectState());
31
- atoms.forEach(atom => atom.subscribe(() => this.update(selectState())));
31
+ atoms.forEach(atom => this.subscribeTo(atom, () => this.update(selectState())));
32
32
  this.setDidInit(allDidInit(atoms));
33
33
  }
34
34
  }
@@ -28,10 +28,10 @@ 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: (_b) => __awaiter(void 0, [_b], void 0, function* ({ atom, set }) {
32
- var _c;
31
+ init: (_a) => __awaiter(void 0, [_a], void 0, function* ({ atom, set }) {
32
+ var _b;
33
33
  if (!atomDb) {
34
- atomDb = new Store_1.Store((_c = base_1.CONFIG.name) !== null && _c !== void 0 ? _c : "yaasl");
34
+ atomDb = new Store_1.Store((_b = base_1.CONFIG.name) !== null && _b !== void 0 ? _b : "yaasl");
35
35
  }
36
36
  const existing = yield atomDb.get(key);
37
37
  if (existing != null) {
@@ -0,0 +1,41 @@
1
+ import { consoleMessage } from "@yaasl/utils";
2
+ export class Destroyable {
3
+ isDestroyed = false;
4
+ dependents = new Set();
5
+ unsubscribers = new Set();
6
+ /** Make this atom unusable and remove all references.
7
+ **/
8
+ destroy() {
9
+ this.dependents.forEach(dependent => dependent.destroy());
10
+ this.unsubscribers.forEach(unsubscribe => unsubscribe());
11
+ this.dependents.clear();
12
+ this.unsubscribers.clear();
13
+ this.isDestroyed = true;
14
+ const throwOnCall = () => {
15
+ const name = "name" in this ? this.name : undefined;
16
+ throw new Error(consoleMessage("The methods of a destroyed atom cannot be called.", {
17
+ scope: name,
18
+ }));
19
+ };
20
+ Object.assign(this, {
21
+ set: throwOnCall,
22
+ get: throwOnCall,
23
+ subscribe: throwOnCall,
24
+ });
25
+ }
26
+ /** Subscribe to another atom and automatically destroy the atom instance, if the parent is destroyed */
27
+ subscribeTo(parent, callback) {
28
+ parent.addDependent(this);
29
+ const unsubscribe = parent.subscribe(callback);
30
+ this.unsubscribers.add(() => {
31
+ unsubscribe();
32
+ parent.removeDependent(this);
33
+ });
34
+ }
35
+ addDependent(dependent) {
36
+ this.dependents.add(dependent);
37
+ }
38
+ removeDependent(dependent) {
39
+ this.dependents.delete(dependent);
40
+ }
41
+ }
@@ -1,9 +1,11 @@
1
- export class Stateful {
1
+ import { Destroyable } from "./Destroyable";
2
+ export class Stateful extends Destroyable {
2
3
  value;
3
4
  /** Promise that resolves when the states initialization was finished. */
4
5
  didInit = false;
5
6
  listeners = new Set();
6
7
  constructor(value) {
8
+ super();
7
9
  this.value = value;
8
10
  }
9
11
  /** Read the value of state.
@@ -23,6 +25,13 @@ export class Stateful {
23
25
  this.listeners.add(callback);
24
26
  return () => this.listeners.delete(callback);
25
27
  }
28
+ /** Make this atom unusable and remove all references.
29
+ **/
30
+ destroy() {
31
+ super.destroy();
32
+ this.value = null;
33
+ this.listeners = new Set();
34
+ }
26
35
  emit(value, previous) {
27
36
  this.listeners.forEach(listener => listener(value, previous));
28
37
  }
@@ -47,7 +47,10 @@ export class Atom extends Stateful {
47
47
  * new value based off the previous value.
48
48
  */
49
49
  set(next) {
50
- const newState = updater(next, this.get());
50
+ const oldState = this.get();
51
+ const newState = updater(next, oldState);
52
+ if (oldState === newState)
53
+ return;
51
54
  void this.effects
52
55
  .dispatch("set", newState)
53
56
  .then(value => super.update(value));
@@ -18,7 +18,7 @@ export class Derive extends Stateful {
18
18
  }
19
19
  addGetDependency(dependency) {
20
20
  if (!this.getterDependencies.has(dependency)) {
21
- dependency.subscribe(() => this.deriveUpdate());
21
+ this.subscribeTo(dependency, () => this.deriveUpdate());
22
22
  this.getterDependencies.add(dependency);
23
23
  }
24
24
  return dependency.get();
@@ -48,9 +48,12 @@ export class SettableDerive extends Derive {
48
48
  * new value based off the previous value.
49
49
  */
50
50
  set(next) {
51
- const value = updater(next, this.get());
51
+ const oldState = this.get();
52
+ const newState = updater(next, oldState);
53
+ if (oldState === newState)
54
+ return;
52
55
  this.setter({
53
- value,
56
+ value: newState,
54
57
  set: (atom, next) => {
55
58
  const value = updater(next, atom.get());
56
59
  atom.set(value);
@@ -6,7 +6,7 @@ const selectPath = (state, path) => path
6
6
  export class PathSelector extends Stateful {
7
7
  constructor(atom, path) {
8
8
  super(selectPath(atom.get(), path));
9
- atom.subscribe(state => this.update(selectPath(state, path)));
9
+ this.subscribeTo(atom, state => this.update(selectPath(state, path)));
10
10
  this.setDidInit(atom.didInit);
11
11
  }
12
12
  }
@@ -24,7 +24,7 @@ export class CombinerSelector extends Stateful {
24
24
  return combiner(...values);
25
25
  };
26
26
  super(selectState());
27
- atoms.forEach(atom => atom.subscribe(() => this.update(selectState())));
27
+ atoms.forEach(atom => this.subscribeTo(atom, () => this.update(selectState())));
28
28
  this.setDidInit(allDidInit(atoms));
29
29
  }
30
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaasl/core",
3
- "version": "0.10.1",
3
+ "version": "0.11.0-alpha.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.10.1"
41
+ "@yaasl/utils": "0.11.0-alpha.0"
42
42
  },
43
43
  "eslintConfig": {
44
44
  "extends": [