@createstore/createstore 1.0.0 → 1.1.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.
package/dist/cjs/createStore.cjs
CHANGED
|
@@ -29,6 +29,15 @@ __export(createStore_exports, {
|
|
|
29
29
|
module.exports = __toCommonJS(createStore_exports);
|
|
30
30
|
var import_react_bindings = require("./react-bindings.cjs");
|
|
31
31
|
const __SnapshotContext = import_react_bindings.RSC ? null : (0, import_react_bindings.createContext)(null);
|
|
32
|
+
let leavingPage = false;
|
|
33
|
+
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
34
|
+
window.addEventListener("pagehide", () => {
|
|
35
|
+
leavingPage = true;
|
|
36
|
+
});
|
|
37
|
+
window.addEventListener("pageshow", () => {
|
|
38
|
+
leavingPage = false;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
32
41
|
const storesByName = /* @__PURE__ */ new Map();
|
|
33
42
|
const MERGE = /* @__PURE__ */ Symbol("createStore.merge");
|
|
34
43
|
const QUIET = /* @__PURE__ */ Symbol("createStore.quiet");
|
|
@@ -163,12 +172,48 @@ function defineEvents() {
|
|
|
163
172
|
return void 0;
|
|
164
173
|
}
|
|
165
174
|
function createStore(initialState, options = {}) {
|
|
175
|
+
checkStoreOptions(options);
|
|
166
176
|
if ("queries" in options)
|
|
167
177
|
throw new Error(
|
|
168
178
|
`[${options.name ?? "store"}] the queries option was replaced: declare each query in the state with query(fetch, options), e.g. { product: query((id, signal) => api.product(id, signal)) }, and read it with useGlobalState('product', id).`
|
|
169
179
|
);
|
|
170
180
|
return withScopes(initialState, options, makeBase(initialState, options));
|
|
171
181
|
}
|
|
182
|
+
const STORE_OPTIONS = /* @__PURE__ */ new Set([
|
|
183
|
+
"name",
|
|
184
|
+
"persist",
|
|
185
|
+
"syncTabs",
|
|
186
|
+
"history",
|
|
187
|
+
"devtools",
|
|
188
|
+
"shared",
|
|
189
|
+
"ssr",
|
|
190
|
+
"logger",
|
|
191
|
+
"onChange",
|
|
192
|
+
"offline",
|
|
193
|
+
"validate",
|
|
194
|
+
"events",
|
|
195
|
+
"computed",
|
|
196
|
+
"keyActions",
|
|
197
|
+
"actions",
|
|
198
|
+
"queries"
|
|
199
|
+
]);
|
|
200
|
+
const warnedOptions = /* @__PURE__ */ new Set();
|
|
201
|
+
function checkStoreOptions(opts) {
|
|
202
|
+
const options = opts;
|
|
203
|
+
const warn = (message) => {
|
|
204
|
+
if (warnedOptions.has(message)) return;
|
|
205
|
+
warnedOptions.add(message);
|
|
206
|
+
console.warn(message);
|
|
207
|
+
};
|
|
208
|
+
const id = options.name ?? "store";
|
|
209
|
+
for (const k of Object.keys(options))
|
|
210
|
+
if (!STORE_OPTIONS.has(k))
|
|
211
|
+
warn(`[${id}] createStore() has no option "${k}", so it is ignored. Options: ${[...STORE_OPTIONS].filter((o) => o !== "queries").join(", ")}.`);
|
|
212
|
+
if (!options.name && (options.persist || options.syncTabs || options.offline || options.shared))
|
|
213
|
+
warn(
|
|
214
|
+
`[store] persist, syncTabs, offline and shared use the store's name to tell stores apart. Without { name: '...' }, every unnamed store uses "store", so two of them would overwrite each other's saved data and mix up their tabs. Give this one a name.`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
172
217
|
const warnedSharedOnServer = /* @__PURE__ */ new Set();
|
|
173
218
|
const warnedShape = /* @__PURE__ */ new Set();
|
|
174
219
|
const SHAPE = /* @__PURE__ */ Symbol("createStore.shape");
|
|
@@ -1025,6 +1070,8 @@ function build(initialState, options, sharedDefinition) {
|
|
|
1025
1070
|
}
|
|
1026
1071
|
};
|
|
1027
1072
|
let hydrated = !persistStorage;
|
|
1073
|
+
let warnedUnsavable = false;
|
|
1074
|
+
const earlyWrites = /* @__PURE__ */ new Set();
|
|
1028
1075
|
let resolveHydrated;
|
|
1029
1076
|
const whenHydrated = new Promise((r) => resolveHydrated = r);
|
|
1030
1077
|
let storageIsAsync = false;
|
|
@@ -1051,7 +1098,26 @@ function build(initialState, options, sharedDefinition) {
|
|
|
1051
1098
|
saveTimer = void 0;
|
|
1052
1099
|
if (!persistStorage || !hydrated) return;
|
|
1053
1100
|
const data = Object.fromEntries(persistedKeys.map((k) => [k, state[k]]));
|
|
1054
|
-
|
|
1101
|
+
let text;
|
|
1102
|
+
try {
|
|
1103
|
+
text = JSON.stringify(storageIsAsync ? { $version: persistVersion, $saved: Date.now(), state: data } : { $version: persistVersion, state: data });
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
if (!warnedUnsavable) {
|
|
1106
|
+
warnedUnsavable = true;
|
|
1107
|
+
const bad = persistedKeys.find((k) => {
|
|
1108
|
+
try {
|
|
1109
|
+
JSON.stringify(state[k]);
|
|
1110
|
+
return false;
|
|
1111
|
+
} catch {
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
console.warn(
|
|
1116
|
+
`[${name}] couldn't save${bad ? ` "${bad}"` : ""}: ${error.message}. Saved state is JSON, so keep persisted keys to strings, numbers, booleans, null, arrays and plain objects, or leave this key out of saving with persist: { exclude: [...] }. Nothing is saved until it is fixed.`
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1055
1121
|
lastSavedText = text;
|
|
1056
1122
|
quietly(() => {
|
|
1057
1123
|
const written = persistStorage.setItem(persistKey, text);
|
|
@@ -1156,9 +1222,12 @@ function build(initialState, options, sharedDefinition) {
|
|
|
1156
1222
|
}
|
|
1157
1223
|
if (rescued != null && isSavedState(rescued) && savedAt(rescued) > savedAt(stored)) text = rescued;
|
|
1158
1224
|
const values = trusted(pick(readSaved(text)), "storage");
|
|
1225
|
+
for (const k of earlyWrites) delete values[k];
|
|
1226
|
+
const keptEarly = earlyWrites.size > 0;
|
|
1227
|
+
earlyWrites.clear();
|
|
1159
1228
|
hydrated = true;
|
|
1160
1229
|
if (!destroyed) apply(values, "storage", "loaded from storage", false);
|
|
1161
|
-
if (!destroyed && text === rescued && rescued != null) saveNow();
|
|
1230
|
+
if (!destroyed && (keptEarly || text === rescued && rescued != null)) saveNow();
|
|
1162
1231
|
finishHydration();
|
|
1163
1232
|
},
|
|
1164
1233
|
(error) => {
|
|
@@ -1263,6 +1332,7 @@ function build(initialState, options, sharedDefinition) {
|
|
|
1263
1332
|
updateHistoryInfo();
|
|
1264
1333
|
}
|
|
1265
1334
|
function batch(fn) {
|
|
1335
|
+
if (typeof fn !== "function") throw new Error(`[${name}] batch() takes a function that makes the changes: store.batch(() => { store.setState('a', 1); store.setState('b', 2); }).`);
|
|
1266
1336
|
batchDepth++;
|
|
1267
1337
|
if (batchDepth === 1 && historyLimit) batchChanges = /* @__PURE__ */ Object.create(null);
|
|
1268
1338
|
let result;
|
|
@@ -1355,6 +1425,7 @@ function build(initialState, options, sharedDefinition) {
|
|
|
1355
1425
|
(k) => isStateKey(k) && !Object.is(state[k], values[k])
|
|
1356
1426
|
);
|
|
1357
1427
|
if (keys.length === 0) return;
|
|
1428
|
+
if (!hydrated && source === "local") for (const k of keys) earlyWrites.add(k);
|
|
1358
1429
|
const prev = {};
|
|
1359
1430
|
for (const k of keys) {
|
|
1360
1431
|
prev[k] = state[k];
|
|
@@ -1590,15 +1661,6 @@ function build(initialState, options, sharedDefinition) {
|
|
|
1590
1661
|
ownPromises.add(promise);
|
|
1591
1662
|
return promise;
|
|
1592
1663
|
}
|
|
1593
|
-
let leavingPage = false;
|
|
1594
|
-
if (isBrowser && typeof window.addEventListener === "function") {
|
|
1595
|
-
window.addEventListener("pagehide", () => {
|
|
1596
|
-
leavingPage = true;
|
|
1597
|
-
});
|
|
1598
|
-
window.addEventListener("pageshow", () => {
|
|
1599
|
-
leavingPage = false;
|
|
1600
|
-
});
|
|
1601
|
-
}
|
|
1602
1664
|
const SERVER_LOADING = Object.freeze({
|
|
1603
1665
|
state: "loading",
|
|
1604
1666
|
loading: true,
|
|
@@ -2100,11 +2162,15 @@ function build(initialState, options, sharedDefinition) {
|
|
|
2100
2162
|
}
|
|
2101
2163
|
const k = cacheKey(q, params);
|
|
2102
2164
|
const prev = cache2.get(k)?.data;
|
|
2165
|
+
outdateRequest(k);
|
|
2103
2166
|
setEntry(k, {
|
|
2104
2167
|
data: typeof updater === "function" ? updater(prev) : updater,
|
|
2105
2168
|
status: "success"
|
|
2106
2169
|
});
|
|
2107
2170
|
}
|
|
2171
|
+
function outdateRequest(k) {
|
|
2172
|
+
if (inflight.has(k)) versions.set(k, (versions.get(k) ?? 0) + 1);
|
|
2173
|
+
}
|
|
2108
2174
|
function abort(q) {
|
|
2109
2175
|
if (isQuery(q)) inlineAborts.get(q)?.abort();
|
|
2110
2176
|
for (const [k, c] of [...namedAborts])
|
|
@@ -2242,6 +2308,8 @@ function build(initialState, options, sharedDefinition) {
|
|
|
2242
2308
|
};
|
|
2243
2309
|
const api = Object.assign(Object.create(setterProtoOf(def, API_NAMES, "apiProto")), apiMembers);
|
|
2244
2310
|
Object.defineProperty(api, SETTER_OF, { value: (k) => setterFor(k) });
|
|
2311
|
+
if (options.actions != null && typeof options.actions !== "function")
|
|
2312
|
+
throw new Error(`[${name}] actions must be a function that receives the store's api and returns your actions: createStore(state, { actions: (api) => ({ add(x) { api.setState('items', (i) => [...i, x]); } }) }).`);
|
|
2245
2313
|
const actions = options.actions?.(api) ?? {};
|
|
2246
2314
|
const keyActionDefs = options.keyActions ?? {};
|
|
2247
2315
|
const ssrCfg = options.ssr;
|
|
@@ -2636,6 +2704,7 @@ function build(initialState, options, sharedDefinition) {
|
|
|
2636
2704
|
setter = (value, serverCall) => {
|
|
2637
2705
|
const prev = cache2.get(k)?.data;
|
|
2638
2706
|
const next = typeof value === "function" ? value(prev) : value;
|
|
2707
|
+
outdateRequest(k);
|
|
2639
2708
|
setEntry(k, { data: next, status: "success" });
|
|
2640
2709
|
if (!serverCall) return Promise.resolve();
|
|
2641
2710
|
const done = Promise.resolve().then(serverCall).then(
|