Skip to content

Commit 7cc4482

Browse files
committed
mostly working except for rejections
1 parent 6af4016 commit 7cc4482

File tree

4 files changed

+334
-223
lines changed

4 files changed

+334
-223
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog #
22

3+
notes:
4+
5+
- state store now always included
6+
- all mapping load functions are rebounced by default
7+
- if an async store loses all subscriptions and then gains one the mapping load function will always evaluate even if the inputs have not changed
8+
39
## 1.0.17 (2023-6-20)
410

511
- *BREAKING CHANGE* chore: rearrange dependencies to minimize installed package size
@@ -8,7 +14,7 @@
814

915
## 1.0.16 (2023-6-20)
1016

11-
- fix: moduleResoltion: NoodeNext support
17+
- fix: moduleResoltion: NodeNext support
1218
- feat: allow for custom storage types for persisted stores
1319

1420
## 1.0.15 (2023-2-27)

src/async-stores/index.ts

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
reloadAll,
2323
loadAll,
2424
rebounce,
25+
getAll,
2526
} from '../utils/index.js';
2627
import { flagStoreCreated, getStoreTestingMode, logError } from '../config.js';
2728

@@ -66,7 +67,9 @@ export const asyncWritable = <S extends Stores, T>(
6667
options: AsyncStoreOptions<T> = {}
6768
): WritableLoadable<T> => {
6869
flagStoreCreated();
69-
const { reloadable, trackState, initial } = options;
70+
const { reloadable, initial, debug } = options;
71+
72+
const debuggy = debug ? console.log : undefined;
7073

7174
const rebouncedMappingLoad = rebounce(mappingLoadFunction);
7275

@@ -95,16 +98,22 @@ export const asyncWritable = <S extends Stores, T>(
9598
const mappingLoadThenSet = async (setStoreValue) => {
9699
if (get(loadState).isSettled) {
97100
setCurrentLoadPromise();
101+
debuggy?.('setting RELOADING');
98102
setState('RELOADING');
99103
}
100104

101105
try {
102106
const finalValue = await rebouncedMappingLoad(parentValues);
107+
debuggy?.('setting value');
103108
setStoreValue(finalValue);
109+
if (!get(loadState).isWriting) {
110+
debuggy?.('setting LOADED');
111+
setState('LOADED');
112+
}
104113
resolveCurrentLoad(finalValue);
105-
setState('LOADED');
106114
} catch (e) {
107115
if (e.name !== 'AbortError') {
116+
logError(e);
108117
setState('ERROR');
109118
rejectCurrentLoad(e);
110119
}
@@ -113,28 +122,34 @@ export const asyncWritable = <S extends Stores, T>(
113122

114123
const onFirstSubscription: StartStopNotifier<T> = (setStoreValue) => {
115124
setCurrentLoadPromise();
125+
parentValues = getAll(stores);
116126

117127
const initialLoad = async () => {
128+
debuggy?.('initial load called');
118129
try {
119130
parentValues = await loadAll(stores);
131+
debuggy?.('setting ready');
120132
ready = true;
121133
changeReceived = false;
122134
mappingLoadThenSet(setStoreValue);
123135
} catch (error) {
136+
console.log('wtf is happening', error);
124137
rejectCurrentLoad(error);
125138
}
126139
};
127140
initialLoad();
128141

129142
const parentUnsubscribers = getStoresArray(stores).map((store, i) =>
130143
store.subscribe((value) => {
144+
debuggy?.('received value', value);
131145
changeReceived = true;
132146
if (Array.isArray(stores)) {
133147
parentValues[i] = value;
134148
} else {
135-
parentValues = value as any;
149+
parentValues = value as StoresValues<S>;
136150
}
137151
if (ready) {
152+
debuggy?.('proceeding because ready');
138153
mappingLoadThenSet(setStoreValue);
139154
}
140155
})
@@ -143,41 +158,93 @@ export const asyncWritable = <S extends Stores, T>(
143158
// called on losing last subscriber
144159
return () => {
145160
parentUnsubscribers.map((unsubscriber) => unsubscriber());
161+
ready = false;
146162
};
147163
};
148164

149165
const thisStore = writable(initial, onFirstSubscription);
150166

167+
const setStoreValueThenWrite = async (
168+
updater: Updater<T>,
169+
persist?: boolean
170+
) => {
171+
setState('WRITING');
172+
let oldValue: T;
173+
try {
174+
oldValue = await currentLoadPromise;
175+
} catch {
176+
oldValue = get(thisStore);
177+
}
178+
179+
setCurrentLoadPromise();
180+
let newValue = updater(oldValue);
181+
thisStore.set(newValue);
182+
183+
if (mappingWriteFunction && persist) {
184+
try {
185+
const writeResponse = (await mappingWriteFunction(
186+
newValue,
187+
parentValues,
188+
oldValue
189+
)) as T;
190+
191+
if (writeResponse !== undefined) {
192+
thisStore.set(writeResponse);
193+
newValue = writeResponse;
194+
}
195+
} catch (error) {
196+
logError(error);
197+
debuggy?.('setting ERROR');
198+
setState('ERROR');
199+
rejectCurrentLoad(error);
200+
throw error;
201+
}
202+
}
203+
setState('LOADED');
204+
resolveCurrentLoad(newValue);
205+
};
206+
207+
// required properties
151208
const subscribe = thisStore.subscribe;
152-
const load = async () => {
209+
const load = () => {
153210
const dummyUnsubscribe = thisStore.subscribe(() => {
154211
/* no-op */
155212
});
156-
try {
157-
const result = await currentLoadPromise;
158-
dummyUnsubscribe();
159-
return result;
160-
} catch (error) {
161-
dummyUnsubscribe();
162-
throw error;
163-
}
213+
currentLoadPromise
214+
.catch(() => {
215+
/* no-op */
216+
})
217+
.finally(dummyUnsubscribe);
218+
return currentLoadPromise;
164219
};
165220
const reload = async (visitedMap?: VisitedMap) => {
166221
ready = false;
167222
changeReceived = false;
168223
setCurrentLoadPromise();
224+
debuggy?.('setting RELOADING from reload');
169225
setState('RELOADING');
170226

171227
const visitMap = visitedMap ?? new WeakMap();
172-
await reloadAll(stores, visitMap);
173-
ready = true;
174-
if (changeReceived || reloadable) {
175-
mappingLoadThenSet(thisStore.set);
176-
} else {
177-
resolveCurrentLoad(get(thisStore));
228+
try {
229+
await reloadAll(stores, visitMap);
230+
ready = true;
231+
if (changeReceived || reloadable) {
232+
mappingLoadThenSet(thisStore.set);
233+
} else {
234+
resolveCurrentLoad(get(thisStore));
235+
setState('LOADED');
236+
}
237+
} catch (error) {
238+
debuggy?.('caught error during reload');
239+
setState('ERROR');
240+
rejectCurrentLoad(error);
178241
}
179242
return currentLoadPromise;
180243
};
244+
const set = (newValue: T, persist = true) =>
245+
setStoreValueThenWrite(() => newValue, persist);
246+
const update = (updater: Updater<T>, persist = true) =>
247+
setStoreValueThenWrite(updater, persist);
181248

182249
return {
183250
get store() {
@@ -186,10 +253,12 @@ export const asyncWritable = <S extends Stores, T>(
186253
subscribe,
187254
load,
188255
reload,
189-
set: () => Promise.resolve(),
190-
update: () => Promise.resolve(),
256+
set,
257+
update,
258+
state: { subscribe: loadState.subscribe },
191259
};
192260
};
261+
193262
/**
194263
* Generate a Loadable store that is considered 'loaded' after resolving asynchronous behavior.
195264
* This asynchronous behavior may be derived from the value of parent Loadable or non Loadable stores.

src/async-stores/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type WritableLoadable<T> = Loadable<T> & AsyncWritable<T>;
3737
export interface AsyncStoreOptions<T> {
3838
reloadable?: true;
3939
trackState?: true;
40+
debug?: true;
4041
initial?: T;
4142
}
4243
export declare type StoresArray =

0 commit comments

Comments
 (0)