@exodus/atoms 5.7.3 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/lib/countdown-lock.d.ts +5 -0
- package/lib/countdown-lock.js +24 -0
- package/lib/effects/wait-until.d.ts +11 -0
- package/lib/effects/wait-until.js +28 -0
- package/lib/enforce-rules.d.ts +8 -0
- package/lib/enforce-rules.js +56 -0
- package/lib/enhancers/block-until.d.ts +12 -0
- package/lib/enhancers/block-until.js +20 -0
- package/lib/enhancers/combine.d.ts +8 -0
- package/lib/enhancers/combine.js +47 -0
- package/lib/enhancers/compute.d.ts +7 -0
- package/lib/enhancers/compute.js +28 -0
- package/lib/enhancers/dedupe.d.ts +3 -0
- package/lib/enhancers/dedupe.js +29 -0
- package/lib/enhancers/difference.d.ts +9 -0
- package/lib/enhancers/difference.js +16 -0
- package/lib/enhancers/optimistic-notifier.d.ts +3 -0
- package/lib/enhancers/optimistic-notifier.js +48 -0
- package/lib/enhancers/read-only.d.ts +3 -0
- package/lib/enhancers/read-only.js +10 -0
- package/lib/enhancers/swallow-observer-errors.d.ts +12 -0
- package/lib/enhancers/swallow-observer-errors.js +18 -0
- package/lib/enhancers/timeout-observers.d.ts +7 -0
- package/lib/enhancers/timeout-observers.js +20 -0
- package/lib/enhancers/warn-on-same-value-set.d.ts +8 -0
- package/lib/enhancers/warn-on-same-value-set.js +30 -0
- package/lib/enhancers/with-serialization.d.ts +8 -0
- package/lib/enhancers/with-serialization.js +19 -0
- package/lib/event-emitter.d.ts +11 -0
- package/lib/event-emitter.js +15 -0
- package/lib/factories/keystore.d.ts +14 -0
- package/lib/factories/keystore.js +23 -0
- package/lib/factories/memory.d.ts +6 -0
- package/lib/factories/memory.js +35 -0
- package/lib/factories/observer.d.ts +13 -0
- package/lib/factories/observer.js +28 -0
- package/lib/factories/remote-config.d.ts +13 -0
- package/lib/factories/remote-config.js +34 -0
- package/lib/factories/sequenced-keystore.d.ts +15 -0
- package/lib/factories/sequenced-keystore.js +71 -0
- package/lib/factories/storage.d.ts +12 -0
- package/lib/factories/storage.js +46 -0
- package/lib/index.d.ts +22 -0
- package/{src → lib}/index.js +21 -24
- package/lib/simple-observer.d.ts +8 -0
- package/lib/simple-observer.js +18 -0
- package/lib/utils/guards.d.ts +2 -0
- package/lib/utils/guards.js +3 -0
- package/lib/utils/types.d.ts +24 -0
- package/lib/utils/types.js +1 -0
- package/package.json +10 -4
- package/src/countdown-lock.js +0 -29
- package/src/effects/wait-until.js +0 -29
- package/src/enforce-rules.js +0 -74
- package/src/enhancers/block-until.js +0 -23
- package/src/enhancers/combine.js +0 -59
- package/src/enhancers/compute.js +0 -31
- package/src/enhancers/dedupe.js +0 -31
- package/src/enhancers/difference.js +0 -20
- package/src/enhancers/optimistic-notifier.js +0 -60
- package/src/enhancers/read-only.js +0 -12
- package/src/enhancers/swallow-observer-errors.js +0 -22
- package/src/enhancers/timeout-observers.js +0 -27
- package/src/enhancers/warn-on-same-value-set.js +0 -49
- package/src/enhancers/with-serialization.js +0 -23
- package/src/event-emitter.js +0 -19
- package/src/factories/keystore.js +0 -40
- package/src/factories/memory.js +0 -46
- package/src/factories/observer.js +0 -33
- package/src/factories/remote-config.js +0 -50
- package/src/factories/sequenced-keystore.js +0 -108
- package/src/factories/storage.js +0 -60
- package/src/simple-observer.js +0 -23
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import EventEmitter from 'events/';
|
|
2
|
+
import fromEventEmitter from '../event-emitter';
|
|
3
|
+
import pDefer from 'p-defer';
|
|
4
|
+
const createAtomMock = (options = {}) => {
|
|
5
|
+
const { defaultValue } = options;
|
|
6
|
+
let latestValue = defaultValue;
|
|
7
|
+
const emitter = new EventEmitter();
|
|
8
|
+
emitter.setMaxListeners(Number.POSITIVE_INFINITY);
|
|
9
|
+
const initialized = pDefer();
|
|
10
|
+
const get = async () => {
|
|
11
|
+
if (!('defaultValue' in options)) {
|
|
12
|
+
await initialized.promise;
|
|
13
|
+
}
|
|
14
|
+
return latestValue;
|
|
15
|
+
};
|
|
16
|
+
const set = async (data) => {
|
|
17
|
+
latestValue = data;
|
|
18
|
+
if (data !== undefined) {
|
|
19
|
+
initialized.resolve();
|
|
20
|
+
initialized.resolved = true;
|
|
21
|
+
}
|
|
22
|
+
if (initialized.resolved) {
|
|
23
|
+
emitter.emit('data', data);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return fromEventEmitter({
|
|
27
|
+
...options,
|
|
28
|
+
getInitialized: () => initialized.resolved,
|
|
29
|
+
emitter,
|
|
30
|
+
event: 'data',
|
|
31
|
+
get,
|
|
32
|
+
set,
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
export default createAtomMock;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Atom, Port } from '../utils/types';
|
|
2
|
+
type Params<T> = {
|
|
3
|
+
atom: Atom<T>;
|
|
4
|
+
port: Port<T>;
|
|
5
|
+
event: string;
|
|
6
|
+
immediateRegister?: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const createAtomObserver: <T>({ port, atom, event, immediateRegister }: Params<T>) => {
|
|
9
|
+
register: () => void;
|
|
10
|
+
unregister: () => void;
|
|
11
|
+
start: () => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
export default createAtomObserver;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const createAtomObserver = ({ port, atom, event, immediateRegister = true }) => {
|
|
2
|
+
let emitting = false;
|
|
3
|
+
let unobserve;
|
|
4
|
+
const register = () => {
|
|
5
|
+
if (unobserve)
|
|
6
|
+
return;
|
|
7
|
+
unobserve = atom.observe((value) => {
|
|
8
|
+
if (emitting)
|
|
9
|
+
port.emit(event, value);
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
const unregister = () => {
|
|
13
|
+
unobserve?.();
|
|
14
|
+
};
|
|
15
|
+
const start = async () => {
|
|
16
|
+
emitting = true;
|
|
17
|
+
port.emit(event, await atom.get());
|
|
18
|
+
};
|
|
19
|
+
if (immediateRegister) {
|
|
20
|
+
register();
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
register,
|
|
24
|
+
unregister,
|
|
25
|
+
start,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export default createAtomObserver;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RemoteConfigType } from '@exodus/remote-config';
|
|
2
|
+
type FactoryParams = {
|
|
3
|
+
remoteConfig: RemoteConfigType;
|
|
4
|
+
};
|
|
5
|
+
type Params<T> = {
|
|
6
|
+
defaultValue?: T;
|
|
7
|
+
} & ({
|
|
8
|
+
path: string;
|
|
9
|
+
} | {
|
|
10
|
+
selector: (config: object) => T;
|
|
11
|
+
});
|
|
12
|
+
declare const createRemoteConfigAtomFactory: ({ remoteConfig }: FactoryParams) => <T>({ defaultValue, ...params }: Params<T>) => import("../utils/types").Atom<T>;
|
|
13
|
+
export default createRemoteConfigAtomFactory;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { get as getValueAtPath, isEqual } from 'lodash';
|
|
2
|
+
import createSimpleObserver from '../simple-observer';
|
|
3
|
+
import enforceObservableRules from '../enforce-rules';
|
|
4
|
+
const createRemoteConfigAtomFactory = ({ remoteConfig }) => ({ defaultValue, ...params }) => {
|
|
5
|
+
if ('path' in params && 'selector' in params) {
|
|
6
|
+
throw new Error('Provide either a path or a selector to get data from remote config - not both.');
|
|
7
|
+
}
|
|
8
|
+
const getValue = (value) => 'path' in params ? getValueAtPath(value, params.path) : params.selector(value);
|
|
9
|
+
const { notify, observe } = createSimpleObserver();
|
|
10
|
+
const get = async () => {
|
|
11
|
+
const data = await remoteConfig.getAll();
|
|
12
|
+
return getValue(data);
|
|
13
|
+
};
|
|
14
|
+
const set = async () => {
|
|
15
|
+
throw new Error('remoteConfig is read-only');
|
|
16
|
+
};
|
|
17
|
+
remoteConfig.on('sync', async ({ current }) => {
|
|
18
|
+
const data = getValue(current);
|
|
19
|
+
return notify(data);
|
|
20
|
+
});
|
|
21
|
+
const observeDistinct = (callback) => observe(function (value) {
|
|
22
|
+
if (isEqual(this.lastValue, value))
|
|
23
|
+
return;
|
|
24
|
+
this.lastValue = value;
|
|
25
|
+
callback(value);
|
|
26
|
+
}.bind({}));
|
|
27
|
+
return enforceObservableRules({
|
|
28
|
+
get,
|
|
29
|
+
set,
|
|
30
|
+
observe: observeDistinct,
|
|
31
|
+
defaultValue,
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
export default createRemoteConfigAtomFactory;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Keystore, KeystoreValue } from '../utils/types';
|
|
2
|
+
type Params = {
|
|
3
|
+
keystore: Keystore;
|
|
4
|
+
config: {
|
|
5
|
+
key: string;
|
|
6
|
+
getOpts?: object;
|
|
7
|
+
setOpts?: object;
|
|
8
|
+
deleteOpts?: object;
|
|
9
|
+
separator?: string;
|
|
10
|
+
isSoleWriter?: boolean;
|
|
11
|
+
defaultValue?: KeystoreValue[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
declare const createSequencedKeystoreAtom: ({ keystore, config: { key, separator, defaultValue, isSoleWriter, getOpts, setOpts, deleteOpts, }, }: Params) => import("../utils/types").Atom<KeystoreValue[] | undefined>;
|
|
15
|
+
export default createSequencedKeystoreAtom;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert';
|
|
2
|
+
import enforceObservableRules from '../enforce-rules';
|
|
3
|
+
import createSimpleObserver from '../simple-observer';
|
|
4
|
+
const getRange = (offset, length) => Array.from({ length }, (_, i) => i + offset);
|
|
5
|
+
const createSequencedKeystoreAtom = ({ keystore, config: { key, separator = '.', defaultValue = [], isSoleWriter, getOpts, setOpts, deleteOpts, }, }) => {
|
|
6
|
+
assert(key, 'sequence keystore atom: key missing');
|
|
7
|
+
assert(typeof key === 'string', 'sequence keystore atom: key must be a string');
|
|
8
|
+
assert(Array.isArray(defaultValue), 'sequence keystore atom: default value must be an array');
|
|
9
|
+
const { notify, observe } = createSimpleObserver({
|
|
10
|
+
enable: isSoleWriter,
|
|
11
|
+
});
|
|
12
|
+
let cache;
|
|
13
|
+
const getKey = (index) => key.endsWith(separator) ? `${key}${index}` : `${key}${separator}${index}`;
|
|
14
|
+
const getByIndex = async (index) => {
|
|
15
|
+
const key = getKey(index);
|
|
16
|
+
const value = await keystore.getSecret(key, getOpts);
|
|
17
|
+
if (value)
|
|
18
|
+
return { key, value };
|
|
19
|
+
};
|
|
20
|
+
const list = async () => {
|
|
21
|
+
let result = [];
|
|
22
|
+
let batch = [];
|
|
23
|
+
let offset = 0;
|
|
24
|
+
const batchSize = 5;
|
|
25
|
+
do {
|
|
26
|
+
const range = getRange(offset, batchSize);
|
|
27
|
+
batch = await Promise.all(range.map(getByIndex));
|
|
28
|
+
batch = batch.filter(Boolean);
|
|
29
|
+
result = [...result, ...batch];
|
|
30
|
+
offset += batchSize;
|
|
31
|
+
} while (batch.length > 0);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
const clear = async () => {
|
|
35
|
+
const items = await list();
|
|
36
|
+
await Promise.all(items.map((item) => keystore.deleteSecret(item.key, deleteOpts)));
|
|
37
|
+
};
|
|
38
|
+
const _set = async (value) => {
|
|
39
|
+
assert(Array.isArray(value), 'sequence keystore atom: set value must be an array');
|
|
40
|
+
await Promise.all(value.map((value, index) => {
|
|
41
|
+
return keystore.setSecret(getKey(index), value, setOpts);
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
const set = async (value) => {
|
|
45
|
+
await clear();
|
|
46
|
+
if (value !== undefined) {
|
|
47
|
+
await _set(value);
|
|
48
|
+
}
|
|
49
|
+
if (isSoleWriter) {
|
|
50
|
+
cache = value;
|
|
51
|
+
await notify(value);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const get = async () => {
|
|
55
|
+
if (cache)
|
|
56
|
+
return cache;
|
|
57
|
+
const items = await list();
|
|
58
|
+
const value = items.map((item) => item.value);
|
|
59
|
+
if (isSoleWriter) {
|
|
60
|
+
cache = value;
|
|
61
|
+
}
|
|
62
|
+
return items.length === 0 ? undefined : value;
|
|
63
|
+
};
|
|
64
|
+
return enforceObservableRules({
|
|
65
|
+
get,
|
|
66
|
+
set,
|
|
67
|
+
observe,
|
|
68
|
+
defaultValue,
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
export default createSequencedKeystoreAtom;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Atom } from '../utils/types';
|
|
2
|
+
import { Storage } from '@exodus/storage-interface';
|
|
3
|
+
type FactoryParams<T> = {
|
|
4
|
+
storage: Storage<T>;
|
|
5
|
+
};
|
|
6
|
+
type Params<T> = {
|
|
7
|
+
key: string;
|
|
8
|
+
defaultValue?: T;
|
|
9
|
+
isSoleWriter?: boolean;
|
|
10
|
+
};
|
|
11
|
+
declare const createStorageAtomFactory: <T>({ storage }: FactoryParams<T>) => ({ key, defaultValue, isSoleWriter }: Params<T>) => Atom<T | undefined>;
|
|
12
|
+
export default createStorageAtomFactory;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import createSimpleObserver from '../simple-observer';
|
|
2
|
+
import enforceObservableRules from '../enforce-rules';
|
|
3
|
+
import pDefer from 'p-defer';
|
|
4
|
+
const createStorageAtomFactory = ({ storage }) => ({ key, defaultValue, isSoleWriter }) => {
|
|
5
|
+
const { notify, observe } = createSimpleObserver({ enable: isSoleWriter });
|
|
6
|
+
let cached;
|
|
7
|
+
let writePromiseDefer;
|
|
8
|
+
const set = async (value) => {
|
|
9
|
+
if (value === undefined) {
|
|
10
|
+
writePromiseDefer = pDefer();
|
|
11
|
+
await storage.delete(key);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
writePromiseDefer = pDefer();
|
|
15
|
+
await storage.set(key, value);
|
|
16
|
+
}
|
|
17
|
+
writePromiseDefer.resolve();
|
|
18
|
+
if (isSoleWriter) {
|
|
19
|
+
cached = value;
|
|
20
|
+
await notify(value);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const get = async () => {
|
|
24
|
+
if (cached) {
|
|
25
|
+
return cached;
|
|
26
|
+
}
|
|
27
|
+
if (writePromiseDefer) {
|
|
28
|
+
await writePromiseDefer.promise;
|
|
29
|
+
}
|
|
30
|
+
if (cached) {
|
|
31
|
+
return cached;
|
|
32
|
+
}
|
|
33
|
+
const value = await storage.get(key);
|
|
34
|
+
if (isSoleWriter) {
|
|
35
|
+
cached = value;
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
};
|
|
39
|
+
return enforceObservableRules({
|
|
40
|
+
get,
|
|
41
|
+
set,
|
|
42
|
+
observe,
|
|
43
|
+
defaultValue,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
export default createStorageAtomFactory;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { default as createAtomMock } from './factories/memory';
|
|
2
|
+
export { default as createInMemoryAtom } from './factories/memory';
|
|
3
|
+
export { default as createStorageAtomFactory } from './factories/storage';
|
|
4
|
+
export { default as createRemoteConfigAtomFactory } from './factories/remote-config';
|
|
5
|
+
export { default as createKeystoreAtom } from './factories/keystore';
|
|
6
|
+
export { default as createSequencedKeystoreAtom } from './factories/sequenced-keystore';
|
|
7
|
+
export { default as createAtomObserver } from './factories/observer';
|
|
8
|
+
export { default as compute } from './enhancers/compute';
|
|
9
|
+
export { default as blockUntil } from './enhancers/block-until';
|
|
10
|
+
export { default as difference } from './enhancers/difference';
|
|
11
|
+
export { default as withSerialization } from './enhancers/with-serialization';
|
|
12
|
+
export { default as combine } from './enhancers/combine';
|
|
13
|
+
export { default as readOnly } from './enhancers/read-only';
|
|
14
|
+
export { default as dedupe } from './enhancers/dedupe';
|
|
15
|
+
export { default as warnOnSameValueSet } from './enhancers/warn-on-same-value-set';
|
|
16
|
+
export { default as swallowObserverErrors } from './enhancers/swallow-observer-errors';
|
|
17
|
+
export { default as timeoutObservers } from './enhancers/timeout-observers';
|
|
18
|
+
export { default as optimisticNotifier } from './enhancers/optimistic-notifier';
|
|
19
|
+
export { default as waitUntil } from './effects/wait-until';
|
|
20
|
+
export { default as enforceObservableRules } from './enforce-rules';
|
|
21
|
+
export { default as fromEventEmitter } from './event-emitter';
|
|
22
|
+
export { Atom, ReadonlyAtom, Listener, Unsubscribe } from './utils/types';
|
package/{src → lib}/index.js
RENAMED
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
export { default as createAtomMock } from './factories/memory'
|
|
2
|
-
export { default as createInMemoryAtom } from './factories/memory'
|
|
3
|
-
export { default as createStorageAtomFactory } from './factories/storage'
|
|
4
|
-
export { default as createRemoteConfigAtomFactory } from './factories/remote-config'
|
|
5
|
-
export { default as createKeystoreAtom } from './factories/keystore'
|
|
6
|
-
export { default as createSequencedKeystoreAtom } from './factories/sequenced-keystore'
|
|
7
|
-
export { default as createAtomObserver } from './factories/observer'
|
|
8
|
-
|
|
9
|
-
export { default as
|
|
10
|
-
export { default as
|
|
11
|
-
export { default as
|
|
12
|
-
export { default as
|
|
13
|
-
export { default as
|
|
14
|
-
export { default as
|
|
15
|
-
export { default as
|
|
16
|
-
export { default as
|
|
17
|
-
export { default as
|
|
18
|
-
export { default as
|
|
19
|
-
export { default as
|
|
20
|
-
|
|
21
|
-
export { default as
|
|
22
|
-
|
|
23
|
-
export { default as enforceObservableRules } from './enforce-rules'
|
|
24
|
-
export { default as fromEventEmitter } from './event-emitter'
|
|
1
|
+
export { default as createAtomMock } from './factories/memory';
|
|
2
|
+
export { default as createInMemoryAtom } from './factories/memory';
|
|
3
|
+
export { default as createStorageAtomFactory } from './factories/storage';
|
|
4
|
+
export { default as createRemoteConfigAtomFactory } from './factories/remote-config';
|
|
5
|
+
export { default as createKeystoreAtom } from './factories/keystore';
|
|
6
|
+
export { default as createSequencedKeystoreAtom } from './factories/sequenced-keystore';
|
|
7
|
+
export { default as createAtomObserver } from './factories/observer';
|
|
8
|
+
export { default as compute } from './enhancers/compute';
|
|
9
|
+
export { default as blockUntil } from './enhancers/block-until';
|
|
10
|
+
export { default as difference } from './enhancers/difference';
|
|
11
|
+
export { default as withSerialization } from './enhancers/with-serialization';
|
|
12
|
+
export { default as combine } from './enhancers/combine';
|
|
13
|
+
export { default as readOnly } from './enhancers/read-only';
|
|
14
|
+
export { default as dedupe } from './enhancers/dedupe';
|
|
15
|
+
export { default as warnOnSameValueSet } from './enhancers/warn-on-same-value-set';
|
|
16
|
+
export { default as swallowObserverErrors } from './enhancers/swallow-observer-errors';
|
|
17
|
+
export { default as timeoutObservers } from './enhancers/timeout-observers';
|
|
18
|
+
export { default as optimisticNotifier } from './enhancers/optimistic-notifier';
|
|
19
|
+
export { default as waitUntil } from './effects/wait-until';
|
|
20
|
+
export { default as enforceObservableRules } from './enforce-rules';
|
|
21
|
+
export { default as fromEventEmitter } from './event-emitter';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Listener } from './utils/types';
|
|
2
|
+
declare const createSimpleObserver: <T>({ enable }?: {
|
|
3
|
+
enable?: boolean | undefined;
|
|
4
|
+
}) => {
|
|
5
|
+
observe: (listener: Listener<T>) => () => void;
|
|
6
|
+
notify: (value: T) => Promise<void[]>;
|
|
7
|
+
};
|
|
8
|
+
export default createSimpleObserver;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const createSimpleObserver = ({ enable = true } = {}) => {
|
|
2
|
+
let listeners = [];
|
|
3
|
+
const notify = async (value) => Promise.all(listeners.map((listener) => listener(value)));
|
|
4
|
+
const observe = (listener) => {
|
|
5
|
+
if (!enable) {
|
|
6
|
+
throw new Error('observe method is not supported');
|
|
7
|
+
}
|
|
8
|
+
listeners.push(listener);
|
|
9
|
+
return () => {
|
|
10
|
+
listeners = listeners.filter((fn) => fn !== listener);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
observe,
|
|
15
|
+
notify,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export default createSimpleObserver;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Unsubscribe = () => void;
|
|
2
|
+
export type Listener<T> = (value: T) => Promise<void> | void;
|
|
3
|
+
export interface EventEmitter {
|
|
4
|
+
on<T>(event: string, listener: Listener<T>): void;
|
|
5
|
+
removeListener<T>(event: string, listener: Listener<T>): void;
|
|
6
|
+
}
|
|
7
|
+
export type Setter<T> = (prev: T) => T | Promise<T>;
|
|
8
|
+
export interface Atom<T> {
|
|
9
|
+
get(): Promise<T>;
|
|
10
|
+
set(value: T): Promise<void>;
|
|
11
|
+
set(setter: Setter<T>): Promise<void>;
|
|
12
|
+
observe(listener: Listener<T>): Unsubscribe;
|
|
13
|
+
}
|
|
14
|
+
export type ReadonlyAtom<T> = Omit<Atom<T>, 'set'>;
|
|
15
|
+
export type KeystoreValue = string | number;
|
|
16
|
+
export interface Port<T> {
|
|
17
|
+
emit(event: string, value: T): void;
|
|
18
|
+
}
|
|
19
|
+
export interface Keystore {
|
|
20
|
+
getSecret(key: string, options?: object): Promise<KeystoreValue>;
|
|
21
|
+
setSecret(key: string, value: KeystoreValue, options?: object): Promise<void>;
|
|
22
|
+
deleteSecret(key: string, options?: object): Promise<string>;
|
|
23
|
+
}
|
|
24
|
+
export type Logger = Pick<Console, 'trace' | 'debug' | 'error' | 'info' | 'log' | 'warn'>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/atoms",
|
|
3
|
-
"version": "
|
|
4
|
-
"main": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"main": "lib/index.js",
|
|
5
5
|
"description": "Abstraction for encapsulating a piece of data behind a simple unified interface: get, set, observe",
|
|
6
6
|
"author": "Exodus Movement Inc.",
|
|
7
7
|
"scripts": {
|
|
8
|
+
"build": "yarn run -T tsc --build tsconfig.build.json",
|
|
9
|
+
"clean": "yarn run -T tsc --build --clean",
|
|
8
10
|
"test": "jest",
|
|
9
11
|
"lint": "eslint . --ignore-path ../../.gitignore",
|
|
10
12
|
"lint:fix": "yarn lint --fix"
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
|
-
"
|
|
15
|
+
"lib",
|
|
14
16
|
"CHANGELOG.md"
|
|
15
17
|
],
|
|
16
18
|
"repository": {
|
|
@@ -23,6 +25,7 @@
|
|
|
23
25
|
"url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Aatoms"
|
|
24
26
|
},
|
|
25
27
|
"dependencies": {
|
|
28
|
+
"@exodus/storage-interface": "^1.0.0",
|
|
26
29
|
"events": "^3.3.0",
|
|
27
30
|
"lodash": "^4.17.21",
|
|
28
31
|
"make-concurrent": ">=4 <6",
|
|
@@ -32,10 +35,13 @@
|
|
|
32
35
|
},
|
|
33
36
|
"devDependencies": {
|
|
34
37
|
"@exodus/atom-tests": "^1.0.0",
|
|
38
|
+
"@exodus/remote-config": "^2.3.0",
|
|
35
39
|
"@exodus/storage-memory": "^2.1.1",
|
|
40
|
+
"@types/lodash": "^4.14.200",
|
|
41
|
+
"@types/minimalistic-assert": "^1.0.2",
|
|
36
42
|
"delay": "^5.0.0",
|
|
37
43
|
"eslint": "^8.44.0",
|
|
38
44
|
"jest": "^29.1.2"
|
|
39
45
|
},
|
|
40
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "127d91779b91ad17cb1dadbfccb7eb7559ecbf23"
|
|
41
47
|
}
|
package/src/countdown-lock.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import pDefer from 'p-defer'
|
|
2
|
-
|
|
3
|
-
const createCountdownLock = (keys) => {
|
|
4
|
-
if (!Array.isArray(keys)) throw new Error('lock keys must be an array')
|
|
5
|
-
|
|
6
|
-
for (const key of keys) {
|
|
7
|
-
if (typeof key !== 'string') throw new Error(`lock keys must be all strings. Invalid ${key}`)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const deferred = pDefer()
|
|
11
|
-
|
|
12
|
-
const unlockedKeys = []
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
promise: deferred.promise,
|
|
16
|
-
unlock: (key) => {
|
|
17
|
-
if (!unlockedKeys.includes(key)) unlockedKeys.push(key)
|
|
18
|
-
|
|
19
|
-
if (unlockedKeys.length === keys.length) {
|
|
20
|
-
deferred.resolve()
|
|
21
|
-
return true
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return false
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default createCountdownLock
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const waitUntil = ({ atom, predicate, rejectAfter }) => {
|
|
2
|
-
let unobserve
|
|
3
|
-
let timeout
|
|
4
|
-
const promise = new Promise((resolve, reject) => {
|
|
5
|
-
unobserve = atom.observe((v) => {
|
|
6
|
-
if (predicate(v)) {
|
|
7
|
-
clearTimeout(timeout)
|
|
8
|
-
unobserve()
|
|
9
|
-
resolve(v)
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
if (rejectAfter) {
|
|
14
|
-
timeout = setTimeout(() => {
|
|
15
|
-
unobserve()
|
|
16
|
-
reject(new Error('rejected by timeout'))
|
|
17
|
-
}, rejectAfter)
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
promise.unobserve = () => {
|
|
22
|
-
unobserve()
|
|
23
|
-
clearTimeout(timeout)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return promise
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default waitUntil
|
package/src/enforce-rules.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import makeConcurrent from 'make-concurrent'
|
|
2
|
-
import proxyFreeze from 'proxy-freeze'
|
|
3
|
-
|
|
4
|
-
const withChangeDetection = (listener) => {
|
|
5
|
-
let currentValue
|
|
6
|
-
let called = false
|
|
7
|
-
|
|
8
|
-
return async (value) => {
|
|
9
|
-
if (called && value === currentValue) return
|
|
10
|
-
|
|
11
|
-
called = true
|
|
12
|
-
currentValue = value
|
|
13
|
-
|
|
14
|
-
await listener(currentValue)
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const enforceObservableRules = ({ defaultValue, getInitialized = () => true, logger, ...atom }) => {
|
|
19
|
-
// ensure observers get called in series
|
|
20
|
-
const enqueue = makeConcurrent((fn) => fn(), { concurrency: 1 })
|
|
21
|
-
|
|
22
|
-
const postProcessValue = (value = defaultValue) =>
|
|
23
|
-
value && typeof value === 'object' ? proxyFreeze(value) : value
|
|
24
|
-
|
|
25
|
-
const get = () => atom.get().then(postProcessValue)
|
|
26
|
-
|
|
27
|
-
const observe = (listener) => {
|
|
28
|
-
let called = false
|
|
29
|
-
let valueEmittedFromGet
|
|
30
|
-
listener = withChangeDetection(listener)
|
|
31
|
-
|
|
32
|
-
const publishSerially = (value) => {
|
|
33
|
-
called = true
|
|
34
|
-
return enqueue(() => listener(value))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// note: call observe() first to give it a chance to throw if it's not supported
|
|
38
|
-
// if the subscription already fired once, ignore first get
|
|
39
|
-
get().then((value) => {
|
|
40
|
-
if (!called) {
|
|
41
|
-
valueEmittedFromGet = value
|
|
42
|
-
publishSerially(value)
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
return atom.observe((value) => {
|
|
46
|
-
if (valueEmittedFromGet && value === valueEmittedFromGet) {
|
|
47
|
-
valueEmittedFromGet = undefined // ignore changes from observe only for first call
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return publishSerially(postProcessValue(value))
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const set = makeConcurrent(async (value) => {
|
|
56
|
-
// support a function a la React's setState(oldState => newState)
|
|
57
|
-
if (typeof value === 'function') {
|
|
58
|
-
const current = getInitialized() ? await get() : defaultValue
|
|
59
|
-
value = await value(current)
|
|
60
|
-
|
|
61
|
-
if (current === value) return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
await atom.set(value)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
get,
|
|
69
|
-
set,
|
|
70
|
-
observe,
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export default enforceObservableRules
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export default function blockUntil({ atom, unblock }) {
|
|
2
|
-
const get = async () => unblock().then(atom.get)
|
|
3
|
-
|
|
4
|
-
return {
|
|
5
|
-
...atom,
|
|
6
|
-
get,
|
|
7
|
-
observe: (callback) => {
|
|
8
|
-
let isSubscribed = true
|
|
9
|
-
let unsubscribe = () => {}
|
|
10
|
-
|
|
11
|
-
unblock().then(() => {
|
|
12
|
-
if (isSubscribed) {
|
|
13
|
-
unsubscribe = atom.observe(callback)
|
|
14
|
-
}
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
return () => {
|
|
18
|
-
isSubscribed = false
|
|
19
|
-
unsubscribe()
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
}
|