atom.io 0.2.0 → 0.3.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.
@@ -0,0 +1,68 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // ../src/react/index.ts
20
+ var react_exports = {};
21
+ __export(react_exports, {
22
+ composeStoreHooks: () => composeStoreHooks
23
+ });
24
+ module.exports = __toCommonJS(react_exports);
25
+ var import_atom = require("atom.io");
26
+ var composeStoreHooks = ({
27
+ useState,
28
+ useEffect,
29
+ store = import_atom.__INTERNAL__.IMPLICIT.STORE
30
+ }) => {
31
+ function useI(token) {
32
+ const updateState = (next) => (0, import_atom.setState)(token, next, store);
33
+ return updateState;
34
+ }
35
+ function useO(token) {
36
+ const state = import_atom.__INTERNAL__.withdraw(token, store);
37
+ const initialValue = import_atom.__INTERNAL__.getState__INTERNAL(state, store);
38
+ const [current, dispatch] = useState(initialValue);
39
+ useEffect(() => {
40
+ const unsubscribe = (0, import_atom.subscribe)(
41
+ token,
42
+ ({ newValue, oldValue }) => {
43
+ if (oldValue !== newValue) {
44
+ dispatch(newValue);
45
+ }
46
+ },
47
+ store
48
+ );
49
+ return unsubscribe;
50
+ }, []);
51
+ return current;
52
+ }
53
+ function useIO(token) {
54
+ return [useO(token), useI(token)];
55
+ }
56
+ function useStore(token) {
57
+ if (token.type === `readonly_selector`) {
58
+ return useO(token);
59
+ }
60
+ return useIO(token);
61
+ }
62
+ return { useI, useO, useIO, useStore };
63
+ };
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ composeStoreHooks
67
+ });
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.ts"],"sourcesContent":["import type Preact from \"preact/hooks\"\n\nimport type React from \"react\"\n\nimport { subscribe, setState, __INTERNAL__ } from \"atom.io\"\nimport type { ReadonlyValueToken, StateToken } from \"atom.io\"\n\nimport type { Modifier } from \"~/packages/anvl/src/function\"\n\nexport type AtomStoreReactConfig = {\n useState: typeof Preact.useState | typeof React.useState\n useEffect: typeof Preact.useEffect | typeof React.useEffect\n store?: __INTERNAL__.Store\n}\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\nexport const composeStoreHooks = ({\n useState,\n useEffect,\n store = __INTERNAL__.IMPLICIT.STORE,\n}: AtomStoreReactConfig) => {\n function useI<T>(token: StateToken<T>): (next: Modifier<T> | T) => void {\n const updateState = (next: Modifier<T> | T) => setState(token, next, store)\n return updateState\n }\n\n function useO<T>(token: ReadonlyValueToken<T> | StateToken<T>): T {\n const state = __INTERNAL__.withdraw(token, store)\n const initialValue = __INTERNAL__.getState__INTERNAL(state, store)\n const [current, dispatch] = useState(initialValue)\n useEffect(() => {\n const unsubscribe = subscribe(\n token,\n ({ newValue, oldValue }) => {\n if (oldValue !== newValue) {\n dispatch(newValue)\n }\n },\n store\n )\n return unsubscribe\n }, [])\n\n return current\n }\n\n function useIO<T>(token: StateToken<T>): [T, (next: Modifier<T> | T) => void] {\n return [useO(token), useI(token)]\n }\n\n function useStore<T>(\n token: StateToken<T>\n ): [T, (next: Modifier<T> | T) => void]\n function useStore<T>(token: ReadonlyValueToken<T>): T\n function useStore<T>(\n token: ReadonlyValueToken<T> | StateToken<T>\n ): T | [T, (next: Modifier<T> | T) => void] {\n if (token.type === `readonly_selector`) {\n return useO(token)\n }\n return useIO(token)\n }\n return { useI, useO, useIO, useStore }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,kBAAkD;AAY3C,IAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,EACA,QAAQ,yBAAa,SAAS;AAChC,MAA4B;AAC1B,WAAS,KAAQ,OAAuD;AACtE,UAAM,cAAc,CAAC,aAA0B,sBAAS,OAAO,MAAM,KAAK;AAC1E,WAAO;AAAA,EACT;AAEA,WAAS,KAAQ,OAAiD;AAChE,UAAM,QAAQ,yBAAa,SAAS,OAAO,KAAK;AAChD,UAAM,eAAe,yBAAa,mBAAmB,OAAO,KAAK;AACjE,UAAM,CAAC,SAAS,QAAQ,IAAI,SAAS,YAAY;AACjD,cAAU,MAAM;AACd,YAAM,kBAAc;AAAA,QAClB;AAAA,QACA,CAAC,EAAE,UAAU,SAAS,MAAM;AAC1B,cAAI,aAAa,UAAU;AACzB,qBAAS,QAAQ;AAAA,UACnB;AAAA,QACF;AAAA,QACA;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAEL,WAAO;AAAA,EACT;AAEA,WAAS,MAAS,OAA4D;AAC5E,WAAO,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC;AAAA,EAClC;AAMA,WAAS,SACP,OAC0C;AAC1C,QAAI,MAAM,SAAS,qBAAqB;AACtC,aAAO,KAAK,KAAK;AAAA,IACnB;AACA,WAAO,MAAM,KAAK;AAAA,EACpB;AACA,SAAO,EAAE,MAAM,MAAM,OAAO,SAAS;AACvC;","names":[]}
@@ -0,0 +1,44 @@
1
+ // ../src/react/index.ts
2
+ import { subscribe, setState, __INTERNAL__ } from "atom.io";
3
+ var composeStoreHooks = ({
4
+ useState,
5
+ useEffect,
6
+ store = __INTERNAL__.IMPLICIT.STORE
7
+ }) => {
8
+ function useI(token) {
9
+ const updateState = (next) => setState(token, next, store);
10
+ return updateState;
11
+ }
12
+ function useO(token) {
13
+ const state = __INTERNAL__.withdraw(token, store);
14
+ const initialValue = __INTERNAL__.getState__INTERNAL(state, store);
15
+ const [current, dispatch] = useState(initialValue);
16
+ useEffect(() => {
17
+ const unsubscribe = subscribe(
18
+ token,
19
+ ({ newValue, oldValue }) => {
20
+ if (oldValue !== newValue) {
21
+ dispatch(newValue);
22
+ }
23
+ },
24
+ store
25
+ );
26
+ return unsubscribe;
27
+ }, []);
28
+ return current;
29
+ }
30
+ function useIO(token) {
31
+ return [useO(token), useI(token)];
32
+ }
33
+ function useStore(token) {
34
+ if (token.type === `readonly_selector`) {
35
+ return useO(token);
36
+ }
37
+ return useIO(token);
38
+ }
39
+ return { useI, useO, useIO, useStore };
40
+ };
41
+ export {
42
+ composeStoreHooks
43
+ };
44
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.ts"],"sourcesContent":["import type Preact from \"preact/hooks\"\n\nimport type React from \"react\"\n\nimport { subscribe, setState, __INTERNAL__ } from \"atom.io\"\nimport type { ReadonlyValueToken, StateToken } from \"atom.io\"\n\nimport type { Modifier } from \"~/packages/anvl/src/function\"\n\nexport type AtomStoreReactConfig = {\n useState: typeof Preact.useState | typeof React.useState\n useEffect: typeof Preact.useEffect | typeof React.useEffect\n store?: __INTERNAL__.Store\n}\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\nexport const composeStoreHooks = ({\n useState,\n useEffect,\n store = __INTERNAL__.IMPLICIT.STORE,\n}: AtomStoreReactConfig) => {\n function useI<T>(token: StateToken<T>): (next: Modifier<T> | T) => void {\n const updateState = (next: Modifier<T> | T) => setState(token, next, store)\n return updateState\n }\n\n function useO<T>(token: ReadonlyValueToken<T> | StateToken<T>): T {\n const state = __INTERNAL__.withdraw(token, store)\n const initialValue = __INTERNAL__.getState__INTERNAL(state, store)\n const [current, dispatch] = useState(initialValue)\n useEffect(() => {\n const unsubscribe = subscribe(\n token,\n ({ newValue, oldValue }) => {\n if (oldValue !== newValue) {\n dispatch(newValue)\n }\n },\n store\n )\n return unsubscribe\n }, [])\n\n return current\n }\n\n function useIO<T>(token: StateToken<T>): [T, (next: Modifier<T> | T) => void] {\n return [useO(token), useI(token)]\n }\n\n function useStore<T>(\n token: StateToken<T>\n ): [T, (next: Modifier<T> | T) => void]\n function useStore<T>(token: ReadonlyValueToken<T>): T\n function useStore<T>(\n token: ReadonlyValueToken<T> | StateToken<T>\n ): T | [T, (next: Modifier<T> | T) => void] {\n if (token.type === `readonly_selector`) {\n return useO(token)\n }\n return useIO(token)\n }\n return { useI, useO, useIO, useStore }\n}\n"],"mappings":";AAIA,SAAS,WAAW,UAAU,oBAAoB;AAY3C,IAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,EACA,QAAQ,aAAa,SAAS;AAChC,MAA4B;AAC1B,WAAS,KAAQ,OAAuD;AACtE,UAAM,cAAc,CAAC,SAA0B,SAAS,OAAO,MAAM,KAAK;AAC1E,WAAO;AAAA,EACT;AAEA,WAAS,KAAQ,OAAiD;AAChE,UAAM,QAAQ,aAAa,SAAS,OAAO,KAAK;AAChD,UAAM,eAAe,aAAa,mBAAmB,OAAO,KAAK;AACjE,UAAM,CAAC,SAAS,QAAQ,IAAI,SAAS,YAAY;AACjD,cAAU,MAAM;AACd,YAAM,cAAc;AAAA,QAClB;AAAA,QACA,CAAC,EAAE,UAAU,SAAS,MAAM;AAC1B,cAAI,aAAa,UAAU;AACzB,qBAAS,QAAQ;AAAA,UACnB;AAAA,QACF;AAAA,QACA;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAEL,WAAO;AAAA,EACT;AAEA,WAAS,MAAS,OAA4D;AAC5E,WAAO,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC;AAAA,EAClC;AAMA,WAAS,SACP,OAC0C;AAC1C,QAAI,MAAM,SAAS,qBAAqB;AACtC,aAAO,KAAK,KAAK;AAAA,IACnB;AACA,WAAO,MAAM,KAAK;AAAA,EACpB;AACA,SAAO,EAAE,MAAM,MAAM,OAAO,SAAS;AACvC;","names":[]}
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "name": "atom.io-react",
3
- "main": "../dist/react/index.js",
4
- "types": "../dist/react/index.d.ts",
5
- "module": "../dist/react/index.mjs"
3
+ "private": true,
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "module": "dist/index.mjs",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "browser": "./dist/index.mjs",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ }
6
15
  }
package/src/atom.ts CHANGED
@@ -1,14 +1,9 @@
1
- import HAMT from "hamt_plus"
2
- import * as Rx from "rxjs"
1
+ import type * as Rx from "rxjs"
3
2
 
4
3
  import type { Serializable } from "~/packages/anvl/src/json"
5
- import { stringifyJson } from "~/packages/anvl/src/json"
6
4
 
7
- import type { AtomToken, ObserveState } from "."
8
- import { subscribe, setState } from "."
9
- import { deposit } from "./internal"
10
- import type { Store } from "./internal/store"
11
- import { IMPLICIT } from "./internal/store"
5
+ import type { AtomToken } from "."
6
+ import { atomFamily__INTERNAL, atom__INTERNAL } from "./internal"
12
7
 
13
8
  export type Effectors<T> = {
14
9
  setSelf: <V extends T>(next: V | ((oldValue: T) => V)) => void
@@ -23,28 +18,8 @@ export type AtomOptions<T> = {
23
18
  effects?: AtomEffect<T>[]
24
19
  }
25
20
 
26
- export const atom = <T>(
27
- options: AtomOptions<T>,
28
- store: Store = IMPLICIT.STORE
29
- ): AtomToken<T> => {
30
- if (HAMT.has(options.key, store.atoms)) {
31
- store.config.logger?.error?.(
32
- `Key "${options.key}" already exists in the store.`
33
- )
34
- return deposit(store.atoms.get(options.key))
35
- }
36
- const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
37
- const newAtom = { ...options, subject }
38
- const initialValue =
39
- options.default instanceof Function ? options.default() : options.default
40
- store.atoms = HAMT.set(options.key, newAtom, store.atoms)
41
- store.atomsAreDefault = HAMT.set(options.key, true, store.atomsAreDefault)
42
- store.valueMap = HAMT.set(options.key, initialValue, store.valueMap)
43
- const token = deposit(newAtom)
44
- const setSelf = (next) => setState(token, next, store)
45
- const onSet = (observe: ObserveState<T>) => subscribe(token, observe, store)
46
- options.effects?.forEach((effect) => effect({ setSelf, onSet }))
47
- return token
21
+ export function atom<T>(options: AtomOptions<T>): AtomToken<T> {
22
+ return atom__INTERNAL<T>(options)
48
23
  }
49
24
 
50
25
  export type AtomFamilyOptions<T, K extends Serializable> = {
@@ -53,26 +28,16 @@ export type AtomFamilyOptions<T, K extends Serializable> = {
53
28
  effects?: (key: K) => AtomEffect<T>[]
54
29
  }
55
30
 
56
- export const atomFamily =
57
- <T, K extends Serializable>(
58
- options: AtomFamilyOptions<T, K>,
59
- store: Store = IMPLICIT.STORE
60
- ) =>
61
- (key: K): AtomToken<T> => {
62
- const fullKey = `${options.key}__${stringifyJson(key)}`
63
- const existing = store.atoms.get(fullKey)
64
- if (existing) {
65
- return deposit(existing)
66
- }
67
- return atom<T>(
68
- {
69
- key: fullKey,
70
- default:
71
- options.default instanceof Function
72
- ? options.default(key)
73
- : options.default,
74
- effects: options.effects?.(key),
75
- },
76
- store
77
- )
78
- }
31
+ export type AtomFamily<T, K extends Serializable = Serializable> = ((
32
+ key: K
33
+ ) => AtomToken<T>) & {
34
+ key: string
35
+ type: `atom_family`
36
+ subject: Rx.Subject<AtomToken<T>>
37
+ }
38
+
39
+ export function atomFamily<T, K extends Serializable>(
40
+ options: AtomFamilyOptions<T, K>
41
+ ): AtomFamily<T, K> {
42
+ return atomFamily__INTERNAL<T, K>(options)
43
+ }
package/src/index.ts CHANGED
@@ -1,36 +1,51 @@
1
1
  import {
2
2
  IMPLICIT,
3
- configure,
4
- finishAction,
3
+ closeOperation,
4
+ openOperation,
5
5
  getState__INTERNAL,
6
6
  setState__INTERNAL,
7
7
  isAtomDefault,
8
8
  isSelectorDefault,
9
- startAction,
10
- subscribeToRootAtoms,
11
9
  withdraw,
12
10
  } from "./internal"
13
11
  import * as __INTERNAL__ from "./internal"
14
12
  import type { Store } from "./internal/store"
15
13
 
16
14
  export * from "./atom"
15
+ export * from "./logger"
17
16
  export * from "./selector"
17
+ export * from "./subscribe"
18
+ export * from "./timeline"
18
19
  export * from "./transaction"
19
- export { __INTERNAL__, configure }
20
+ export { __INTERNAL__ }
21
+ export type { Serializable } from "~/packages/anvl/src/json"
20
22
 
21
23
  export type AtomToken<_> = {
22
24
  key: string
23
25
  type: `atom`
26
+ family?: FamilyMetadata
24
27
  }
25
28
  export type SelectorToken<_> = {
26
29
  key: string
27
30
  type: `selector`
31
+ family?: FamilyMetadata
28
32
  }
29
33
  export type StateToken<T> = AtomToken<T> | SelectorToken<T>
30
34
 
31
35
  export type ReadonlyValueToken<_> = {
32
36
  key: string
33
37
  type: `readonly_selector`
38
+ family?: FamilyMetadata
39
+ }
40
+
41
+ export type FamilyMetadata = {
42
+ key: string
43
+ subKey: string
44
+ }
45
+
46
+ export type TransactionToken<_> = {
47
+ key: string
48
+ type: `transaction`
34
49
  }
35
50
 
36
51
  export const getState = <T>(
@@ -46,10 +61,17 @@ export const setState = <T, New extends T>(
46
61
  value: New | ((oldValue: T) => New),
47
62
  store: Store = IMPLICIT.STORE
48
63
  ): void => {
49
- startAction(store)
64
+ try {
65
+ openOperation(token, store)
66
+ } catch (thrown) {
67
+ if (!(typeof thrown === `symbol`)) {
68
+ throw thrown
69
+ }
70
+ return
71
+ }
50
72
  const state = withdraw(token, store)
51
73
  setState__INTERNAL(state, value, store)
52
- finishAction(store)
74
+ closeOperation(store)
53
75
  }
54
76
 
55
77
  export const isDefault = (
@@ -59,33 +81,3 @@ export const isDefault = (
59
81
  token.type === `atom`
60
82
  ? isAtomDefault(token.key, store)
61
83
  : isSelectorDefault(token.key, store)
62
-
63
- export type ObserveState<T> = (change: { newValue: T; oldValue: T }) => void
64
-
65
- export const subscribe = <T>(
66
- token: ReadonlyValueToken<T> | StateToken<T>,
67
- observe: ObserveState<T>,
68
- store: Store = IMPLICIT.STORE
69
- ): (() => void) => {
70
- const state = withdraw<T>(token, store)
71
- const subscription = state.subject.subscribe(observe)
72
- store.config.logger?.info(`👀 subscribe to "${state.key}"`)
73
- const dependencySubscriptions = subscribeToRootAtoms(state, store)
74
- const unsubscribe =
75
- dependencySubscriptions === null
76
- ? () => {
77
- store.config.logger?.info(`🙈 unsubscribe from "${state.key}"`)
78
- subscription.unsubscribe()
79
- }
80
- : () => {
81
- store.config.logger?.info(
82
- `🙈 unsubscribe from "${state.key}" and its dependencies`
83
- )
84
- subscription.unsubscribe()
85
- for (const dependencySubscription of dependencySubscriptions) {
86
- dependencySubscription.unsubscribe()
87
- }
88
- }
89
-
90
- return unsubscribe
91
- }
@@ -0,0 +1,50 @@
1
+ import * as Rx from "rxjs"
2
+
3
+ import { deposit } from "./get"
4
+ import { markAtomAsDefault } from "./is-default"
5
+ import { cacheValue, hasKeyBeenUsed, storeAtom } from "./operation"
6
+ import type { Store } from "./store"
7
+ import { IMPLICIT } from "./store"
8
+ import { target } from "./transaction-internal"
9
+ import type { AtomToken, FamilyMetadata, UpdateHandler } from ".."
10
+ import { setState, subscribe } from ".."
11
+ import type { AtomOptions } from "../atom"
12
+
13
+ export type Atom<T> = {
14
+ key: string
15
+ type: `atom`
16
+ family?: FamilyMetadata
17
+ subject: Rx.Subject<{ newValue: T; oldValue: T }>
18
+ default: T
19
+ }
20
+
21
+ export function atom__INTERNAL<T>(
22
+ options: AtomOptions<T>,
23
+ family?: FamilyMetadata,
24
+ store: Store = IMPLICIT.STORE
25
+ ): AtomToken<T> {
26
+ const core = target(store)
27
+ if (hasKeyBeenUsed(options.key, store)) {
28
+ store.config.logger?.error?.(
29
+ `Key "${options.key}" already exists in the store.`
30
+ )
31
+ return deposit(core.atoms.get(options.key))
32
+ }
33
+ const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
34
+ const newAtom = {
35
+ ...options,
36
+ subject,
37
+ type: `atom`,
38
+ ...(family && { family }),
39
+ } as const
40
+ const initialValue =
41
+ options.default instanceof Function ? options.default() : options.default
42
+ storeAtom(newAtom, store)
43
+ markAtomAsDefault(options.key, store)
44
+ cacheValue(options.key, initialValue, store)
45
+ const token = deposit(newAtom)
46
+ const setSelf = (next) => setState(token, next, store)
47
+ const onSet = (handle: UpdateHandler<T>) => subscribe(token, handle, store)
48
+ options.effects?.forEach((effect) => effect({ setSelf, onSet }))
49
+ return token
50
+ }
@@ -0,0 +1,142 @@
1
+ import * as Rx from "rxjs"
2
+
3
+ import type { Serializable } from "~/packages/anvl/src/json"
4
+ import { stringifyJson } from "~/packages/anvl/src/json"
5
+
6
+ import type { Store } from "."
7
+ import {
8
+ atom__INTERNAL,
9
+ withdraw,
10
+ selector__INTERNAL,
11
+ target,
12
+ deposit,
13
+ IMPLICIT,
14
+ } from "."
15
+ import type {
16
+ AtomFamily,
17
+ AtomFamilyOptions,
18
+ AtomToken,
19
+ FamilyMetadata,
20
+ ReadonlySelectorFamily,
21
+ ReadonlySelectorFamilyOptions,
22
+ ReadonlyValueToken,
23
+ SelectorFamily,
24
+ SelectorFamilyOptions,
25
+ SelectorToken,
26
+ } from ".."
27
+
28
+ export function atomFamily__INTERNAL<T, K extends Serializable>(
29
+ options: AtomFamilyOptions<T, K>,
30
+ store: Store = IMPLICIT.STORE
31
+ ): AtomFamily<T, K> {
32
+ const subject = new Rx.Subject<AtomToken<T>>()
33
+ return Object.assign(
34
+ (key: K): AtomToken<T> => {
35
+ const subKey = stringifyJson(key)
36
+ const family: FamilyMetadata = { key: options.key, subKey }
37
+ const fullKey = `${options.key}(${subKey})`
38
+ const existing = withdraw({ key: fullKey, type: `atom` }, store)
39
+ const token = existing
40
+ ? deposit(existing)
41
+ : atom__INTERNAL<T>(
42
+ {
43
+ key: fullKey,
44
+ default:
45
+ options.default instanceof Function
46
+ ? options.default(key)
47
+ : options.default,
48
+ effects: options.effects?.(key),
49
+ },
50
+ family,
51
+ store
52
+ )
53
+ subject.next(token)
54
+ return token
55
+ },
56
+ {
57
+ key: options.key,
58
+ type: `atom_family`,
59
+ subject,
60
+ } as const
61
+ )
62
+ }
63
+
64
+ export function readonlySelectorFamily__INTERNAL<T, K extends Serializable>(
65
+ options: SelectorFamilyOptions<T, K>,
66
+ store?: Store
67
+ ): ReadonlySelectorFamily<T, K> {
68
+ const core = target(store)
69
+ const subject = new Rx.Subject<ReadonlyValueToken<T>>()
70
+ return Object.assign(
71
+ (key: K): ReadonlyValueToken<T> => {
72
+ const subKey = stringifyJson(key)
73
+ const family: FamilyMetadata = { key: options.key, subKey }
74
+ const fullKey = `${options.key}(${subKey})`
75
+ const existing = core.readonlySelectors.get(fullKey)
76
+ if (existing) {
77
+ return deposit(existing)
78
+ }
79
+ return selector__INTERNAL<T>(
80
+ {
81
+ key: fullKey,
82
+ get: options.get(key),
83
+ },
84
+ family,
85
+ store
86
+ ) as ReadonlyValueToken<T>
87
+ },
88
+ {
89
+ key: options.key,
90
+ type: `readonly_selector_family`,
91
+ subject,
92
+ } as const
93
+ ) as ReadonlySelectorFamily<T, K>
94
+ }
95
+
96
+ export function selectorFamily__INTERNAL<T, K extends Serializable>(
97
+ options: SelectorFamilyOptions<T, K>,
98
+ store?: Store
99
+ ): SelectorFamily<T, K>
100
+ export function selectorFamily__INTERNAL<T, K extends Serializable>(
101
+ options: ReadonlySelectorFamilyOptions<T, K>,
102
+ store?: Store
103
+ ): ReadonlySelectorFamily<T, K>
104
+ export function selectorFamily__INTERNAL<T, K extends Serializable>(
105
+ options: ReadonlySelectorFamilyOptions<T, K> | SelectorFamilyOptions<T, K>,
106
+ store: Store = IMPLICIT.STORE
107
+ ): ReadonlySelectorFamily<T, K> | SelectorFamily<T, K> {
108
+ const isReadonly = !(`set` in options)
109
+
110
+ if (isReadonly) {
111
+ return readonlySelectorFamily__INTERNAL(options as any, store)
112
+ }
113
+ const core = target(store)
114
+ const subject = new Rx.Subject<SelectorToken<T>>()
115
+
116
+ return Object.assign(
117
+ (key: K): SelectorToken<T> => {
118
+ const subKey = stringifyJson(key)
119
+ const family: FamilyMetadata = { key: options.key, subKey }
120
+ const fullKey = `${options.key}(${subKey})`
121
+ const existing = core.selectors.get(fullKey)
122
+ if (existing) {
123
+ return deposit(existing)
124
+ }
125
+ const token = selector__INTERNAL<T>(
126
+ {
127
+ key: fullKey,
128
+ get: options.get(key),
129
+ set: options.set(key),
130
+ },
131
+ family,
132
+ store
133
+ )
134
+ subject.next(token)
135
+ return token
136
+ },
137
+ {
138
+ key: options.key,
139
+ type: `selector_family`,
140
+ } as const
141
+ ) as SelectorFamily<T, K>
142
+ }
@@ -1,36 +1,18 @@
1
- import { pipe } from "fp-ts/function"
2
1
  import HAMT from "hamt_plus"
3
2
 
4
- import type { Atom, ReadonlySelector, Selector } from "."
5
- import type { Store } from "./store"
6
- import { IMPLICIT } from "./store"
3
+ import type { Atom, ReadonlySelector, Selector, Store } from "."
4
+ import { target, isValueCached, readCachedValue, IMPLICIT } from "."
7
5
  import type {
8
6
  AtomToken,
9
7
  ReadonlyValueToken,
10
8
  SelectorToken,
11
9
  StateToken,
10
+ Transaction,
11
+ TransactionToken,
12
+ ƒn,
12
13
  } from ".."
13
14
 
14
- export const getCachedState = <T>(
15
- state: Atom<T> | ReadonlySelector<T> | Selector<T>,
16
- store: Store = IMPLICIT.STORE
17
- ): T => {
18
- const path = []
19
- if (`default` in state) {
20
- const atomKey = state.key
21
- store.selectorAtoms = pipe(store.selectorAtoms, (oldValue) => {
22
- let newValue = oldValue
23
- for (const selectorKey of path) {
24
- newValue = newValue.set(selectorKey, atomKey)
25
- }
26
- return newValue
27
- })
28
- }
29
- const value = HAMT.get(state.key, store.valueMap)
30
- return value
31
- }
32
-
33
- export const getSelectorState = <T>(
15
+ export const computeSelectorState = <T>(
34
16
  selector: ReadonlySelector<T> | Selector<T>
35
17
  ): T => selector.get()
36
18
 
@@ -38,9 +20,10 @@ export function lookup(
38
20
  key: string,
39
21
  store: Store
40
22
  ): AtomToken<unknown> | ReadonlyValueToken<unknown> | SelectorToken<unknown> {
41
- const type = HAMT.has(key, store.atoms)
23
+ const core = target(store)
24
+ const type = HAMT.has(key, core.atoms)
42
25
  ? `atom`
43
- : HAMT.has(key, store.selectors)
26
+ : HAMT.has(key, core.selectors)
44
27
  ? `selector`
45
28
  : `readonly_selector`
46
29
  return { key, type }
@@ -56,18 +39,28 @@ export function withdraw<T>(
56
39
  token: ReadonlyValueToken<T>,
57
40
  store: Store
58
41
  ): ReadonlySelector<T>
42
+ export function withdraw<T>(
43
+ token: TransactionToken<T>,
44
+ store: Store
45
+ ): Transaction<T extends ƒn ? T : never>
59
46
  export function withdraw<T>(
60
47
  token: ReadonlyValueToken<T> | StateToken<T>,
61
48
  store: Store
62
49
  ): Atom<T> | ReadonlySelector<T> | Selector<T>
63
50
  export function withdraw<T>(
64
- token: ReadonlyValueToken<T> | StateToken<T>,
51
+ token: ReadonlyValueToken<T> | StateToken<T> | TransactionToken<T>,
65
52
  store: Store
66
- ): Atom<T> | ReadonlySelector<T> | Selector<T> {
53
+ ):
54
+ | Atom<T>
55
+ | ReadonlySelector<T>
56
+ | Selector<T>
57
+ | Transaction<T extends ƒn ? T : never> {
58
+ const core = target(store)
67
59
  return (
68
- HAMT.get(token.key, store.atoms) ??
69
- HAMT.get(token.key, store.selectors) ??
70
- HAMT.get(token.key, store.readonlySelectors)
60
+ HAMT.get(token.key, core.atoms) ??
61
+ HAMT.get(token.key, core.selectors) ??
62
+ HAMT.get(token.key, core.readonlySelectors) ??
63
+ HAMT.get(token.key, core.transactions)
71
64
  )
72
65
  }
73
66
 
@@ -75,32 +68,37 @@ export function deposit<T>(state: Atom<T>): AtomToken<T>
75
68
  export function deposit<T>(state: Selector<T>): SelectorToken<T>
76
69
  export function deposit<T>(state: Atom<T> | Selector<T>): StateToken<T>
77
70
  export function deposit<T>(state: ReadonlySelector<T>): ReadonlyValueToken<T>
71
+ export function deposit<T>(
72
+ state: Transaction<T extends ƒn ? T : never>
73
+ ): TransactionToken<T>
78
74
  export function deposit<T>(
79
75
  state: Atom<T> | ReadonlySelector<T> | Selector<T>
80
76
  ): ReadonlyValueToken<T> | StateToken<T>
81
77
  export function deposit<T>(
82
- state: Atom<T> | ReadonlySelector<T> | Selector<T>
83
- ): ReadonlyValueToken<T> | StateToken<T> {
84
- if (`get` in state) {
85
- if (`set` in state) {
86
- return { key: state.key, type: `selector` }
87
- }
88
- return { key: state.key, type: `readonly_selector` }
78
+ state:
79
+ | Atom<T>
80
+ | ReadonlySelector<T>
81
+ | Selector<T>
82
+ | Transaction<T extends ƒn ? T : never>
83
+ ): ReadonlyValueToken<T> | StateToken<T> | TransactionToken<T> {
84
+ return {
85
+ key: state.key,
86
+ type: state.type,
87
+ ...(`family` in state && { family: state.family }),
89
88
  }
90
- return { key: state.key, type: `atom` }
91
89
  }
92
90
 
93
91
  export const getState__INTERNAL = <T>(
94
92
  state: Atom<T> | ReadonlySelector<T> | Selector<T>,
95
93
  store: Store = IMPLICIT.STORE
96
94
  ): T => {
97
- if (HAMT.has(state.key, store.valueMap)) {
95
+ if (isValueCached(state.key, store)) {
98
96
  store.config.logger?.info(`>> read "${state.key}"`)
99
- return getCachedState(state, store)
97
+ return readCachedValue(state.key, store)
100
98
  }
101
- if (`get` in state) {
99
+ if (state.type !== `atom`) {
102
100
  store.config.logger?.info(`-> calc "${state.key}"`)
103
- return getSelectorState(state)
101
+ return computeSelectorState(state)
104
102
  }
105
103
  store.config.logger?.error(
106
104
  `Attempted to get atom "${state.key}", which was never initialized in store "${store.config.name}".`