atom.io 0.1.0 → 0.2.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,132 @@
1
+ import HAMT from "hamt_plus"
2
+ import * as Rx from "rxjs"
3
+
4
+ import { become } from "~/packages/anvl/src/function"
5
+ import type { Serializable } from "~/packages/anvl/src/json"
6
+ import { stringifyJson } from "~/packages/anvl/src/json"
7
+
8
+ import type { ReadonlyValueToken, SelectorToken } from "."
9
+ import type { Selector, Store } from "./internal"
10
+ import { IMPLICIT, markDone, deposit, registerSelector } from "./internal"
11
+ import type { ReadonlyTransactors, Transactors } from "./transaction"
12
+
13
+ export type SelectorOptions<T> = {
14
+ key: string
15
+ get: (readonlyTransactors: ReadonlyTransactors) => T
16
+ set: (transactors: Transactors, newValue: T) => void
17
+ }
18
+ export type ReadonlySelectorOptions<T> = Omit<SelectorOptions<T>, `set`>
19
+
20
+ export function selector<T>(
21
+ options: SelectorOptions<T>,
22
+ store?: Store
23
+ ): SelectorToken<T>
24
+ export function selector<T>(
25
+ options: ReadonlySelectorOptions<T>,
26
+ store?: Store
27
+ ): ReadonlyValueToken<T>
28
+ export function selector<T>(
29
+ options: ReadonlySelectorOptions<T> | SelectorOptions<T>,
30
+ store: Store = IMPLICIT.STORE
31
+ ): ReadonlyValueToken<T> | SelectorToken<T> {
32
+ if (HAMT.has(options.key, store.selectors)) {
33
+ throw new Error(`Key "${options.key}" already exists in the store.`)
34
+ }
35
+
36
+ const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
37
+
38
+ const { get, set } = registerSelector(options.key, store)
39
+ const getSelf = () => {
40
+ const value = options.get({ get })
41
+ store.valueMap = HAMT.set(options.key, value, store.valueMap)
42
+ return value
43
+ }
44
+
45
+ if (!(`set` in options)) {
46
+ const readonlySelector = {
47
+ ...options,
48
+ subject,
49
+ get: getSelf,
50
+ }
51
+ store.readonlySelectors = HAMT.set(
52
+ options.key,
53
+ readonlySelector,
54
+ store.readonlySelectors
55
+ )
56
+ const initialValue = getSelf()
57
+ store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
58
+ return { ...readonlySelector, type: `readonly_selector` }
59
+ }
60
+
61
+ const setSelf = (next: T | ((oldValue: T) => T)): void => {
62
+ store.config.logger?.info(` <- "${options.key}" became`, next)
63
+ const oldValue = getSelf()
64
+ const newValue = become(next)(oldValue)
65
+ store.valueMap = HAMT.set(options.key, newValue, store.valueMap)
66
+ markDone(options.key, store)
67
+ subject.next({ newValue, oldValue })
68
+ options.set({ get, set }, newValue)
69
+ }
70
+
71
+ const mySelector: Selector<T> = {
72
+ ...options,
73
+ subject,
74
+ get: getSelf,
75
+ set: setSelf,
76
+ }
77
+ store.selectors = HAMT.set(options.key, mySelector, store.selectors)
78
+ const initialValue = getSelf()
79
+ store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
80
+ return { ...mySelector, type: `selector` }
81
+ }
82
+
83
+ export type SelectorFamilyOptions<T, K extends Serializable> = {
84
+ key: string
85
+ get: (key: K) => (readonlyTransactors: ReadonlyTransactors) => T
86
+ set: (key: K) => (transactors: Transactors, newValue: T) => void
87
+ }
88
+ export type ReadonlySelectorFamilyOptions<T, K extends Serializable> = Omit<
89
+ SelectorFamilyOptions<T, K>,
90
+ `set`
91
+ >
92
+
93
+ export function selectorFamily<T, K extends Serializable>(
94
+ options: SelectorFamilyOptions<T, K>,
95
+ store?: Store
96
+ ): (key: K) => SelectorToken<T>
97
+ export function selectorFamily<T, K extends Serializable>(
98
+ options: ReadonlySelectorFamilyOptions<T, K>,
99
+ store?: Store
100
+ ): (key: K) => ReadonlyValueToken<T>
101
+ export function selectorFamily<T, K extends Serializable>(
102
+ options: ReadonlySelectorFamilyOptions<T, K> | SelectorFamilyOptions<T, K>,
103
+ store: Store = IMPLICIT.STORE
104
+ ): (key: K) => ReadonlyValueToken<T> | SelectorToken<T> {
105
+ return (key: K): ReadonlyValueToken<T> | SelectorToken<T> => {
106
+ const fullKey = `${options.key}__${stringifyJson(key)}`
107
+ const existing =
108
+ store.selectors.get(fullKey) ?? store.readonlySelectors.get(fullKey)
109
+ if (existing) {
110
+ return deposit(existing)
111
+ }
112
+ const readonlySelectorOptions: ReadonlySelectorOptions<T> = {
113
+ key: fullKey,
114
+ get: options.get(key),
115
+ }
116
+ if (!(`set` in options)) {
117
+ return selector<T>(
118
+ {
119
+ ...readonlySelectorOptions,
120
+ },
121
+ store
122
+ )
123
+ }
124
+ return selector<T>(
125
+ {
126
+ ...readonlySelectorOptions,
127
+ set: options.set(key),
128
+ },
129
+ store
130
+ )
131
+ }
132
+ }
@@ -0,0 +1,53 @@
1
+ import type { ReadonlyValueToken, StateToken } from "."
2
+ import { getState, setState } from "."
3
+ import type { Store } from "./internal"
4
+ import { IMPLICIT } from "./internal"
5
+ import {
6
+ abortTransaction,
7
+ finishTransaction,
8
+ startTransaction,
9
+ } from "./internal/transaction-internal"
10
+
11
+ export type ƒn = (...parameters: any[]) => any
12
+
13
+ export type Transactors = {
14
+ get: <S>(state: ReadonlyValueToken<S> | StateToken<S>) => S
15
+ set: <S>(state: StateToken<S>, newValue: S | ((oldValue: S) => S)) => void
16
+ }
17
+ export type ReadonlyTransactors = Pick<Transactors, `get`>
18
+
19
+ export type Action<ƒ extends ƒn> = (
20
+ transactors: Transactors,
21
+ ...parameters: Parameters<ƒ>
22
+ ) => ReturnType<ƒ>
23
+
24
+ export type TransactionOptions<ƒ extends ƒn> = {
25
+ key: string
26
+ do: Action<ƒ>
27
+ }
28
+
29
+ export const transaction = <ƒ extends ƒn>(
30
+ options: TransactionOptions<ƒ>,
31
+ store: Store = IMPLICIT.STORE
32
+ ): ((...parameters: Parameters<ƒ>) => ReturnType<ƒ>) & { key: string } =>
33
+ Object.assign(
34
+ (...parameters: Parameters<ƒ>) => {
35
+ startTransaction(store)
36
+ try {
37
+ const result = options.do(
38
+ {
39
+ get: (token) => getState(token, store),
40
+ set: (token, value) => setState(token, value, store),
41
+ },
42
+ ...parameters
43
+ )
44
+ finishTransaction(store)
45
+ return result
46
+ } catch (thrown) {
47
+ abortTransaction(store)
48
+ store.config.logger?.error(`Transaction ${options.key} failed`, thrown)
49
+ throw thrown
50
+ }
51
+ },
52
+ { key: options.key }
53
+ )