juststore 1.2.0 → 1.2.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/README.md +3 -2
- package/dist/form.js +35 -19
- package/package.json +1 -1
- package/dist/src/atom.d.ts +0 -45
- package/dist/src/atom.js +0 -141
- package/dist/src/form.d.ts +0 -97
- package/dist/src/form.js +0 -176
- package/dist/src/impl.d.ts +0 -128
- package/dist/src/impl.js +0 -644
- package/dist/src/index.d.ts +0 -10
- package/dist/src/index.js +0 -7
- package/dist/src/kv_store.d.ts +0 -29
- package/dist/src/kv_store.js +0 -127
- package/dist/src/local_storage.d.ts +0 -7
- package/dist/src/local_storage.js +0 -43
- package/dist/src/memory.d.ts +0 -54
- package/dist/src/memory.js +0 -55
- package/dist/src/mixed_state.d.ts +0 -20
- package/dist/src/mixed_state.js +0 -45
- package/dist/src/node.d.ts +0 -41
- package/dist/src/node.js +0 -374
- package/dist/src/path.d.ts +0 -136
- package/dist/src/path.js +0 -26
- package/dist/src/root.d.ts +0 -23
- package/dist/src/root.js +0 -81
- package/dist/src/stable_keys.d.ts +0 -4
- package/dist/src/stable_keys.js +0 -31
- package/dist/src/store.d.ts +0 -42
- package/dist/src/store.js +0 -40
- package/dist/src/types.d.ts +0 -143
- package/dist/src/types.js +0 -1
- package/dist/src/utils.d.ts +0 -72
- package/dist/src/utils.js +0 -76
package/dist/src/node.js
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
/** biome-ignore-all lint/suspicious/noExplicitAny: <T is not known at this point> */
|
|
2
|
-
/** biome-ignore-all lint/suspicious/noAssignInExpressions: <getter methods are cached> */
|
|
3
|
-
import { getStableKeys } from "./impl";
|
|
4
|
-
export { createNode, createRootNode };
|
|
5
|
-
/**
|
|
6
|
-
* Creates the root proxy node for dynamic path access.
|
|
7
|
-
*
|
|
8
|
-
* This is an internal function that wraps a store API in a Proxy, enabling
|
|
9
|
-
* property-chain syntax like `store.user.profile.name.use()`.
|
|
10
|
-
*
|
|
11
|
-
* @param storeApi - The underlying store API with path-based methods
|
|
12
|
-
* @param initialPath - Starting path segment (default: empty string for root)
|
|
13
|
-
* @returns A proxy that intercepts property access and returns nested proxies or state methods
|
|
14
|
-
*/
|
|
15
|
-
function createRootNode(storeApi, initialPath = "") {
|
|
16
|
-
const proxyCache = new Map();
|
|
17
|
-
return createNode(storeApi, initialPath, proxyCache);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Creates a proxy node for a specific path in the store.
|
|
21
|
-
*
|
|
22
|
-
* The proxy intercepts property access to provide state methods (use, set, value, etc.)
|
|
23
|
-
* and recursively creates child proxies for nested paths. Supports derived state
|
|
24
|
-
* transformations via the `from` and `to` parameters.
|
|
25
|
-
*
|
|
26
|
-
* @param storeApi - The underlying store API
|
|
27
|
-
* @param path - Dot-separated path to this node (e.g., "user.profile.name")
|
|
28
|
-
* @param cache - Shared cache to avoid recreating proxies for the same path
|
|
29
|
-
* @param extensions - Optional custom getters/setters (used by form handling)
|
|
30
|
-
* @param from - Transform function applied when reading values (for derived state)
|
|
31
|
-
* @param to - Transform function applied when writing values (for derived state)
|
|
32
|
-
* @returns A proxy implementing the State interface for the given path
|
|
33
|
-
*/
|
|
34
|
-
function createNode(storeApi, path, cache, extensions, from = unchanged, to = unchanged) {
|
|
35
|
-
const isDerived = from !== unchanged || to !== unchanged;
|
|
36
|
-
const fieldName = path.split(".").pop();
|
|
37
|
-
if (!isDerived && cache.has(path)) {
|
|
38
|
-
return cache.get(path);
|
|
39
|
-
}
|
|
40
|
-
const proxy = new Proxy({}, {
|
|
41
|
-
get(_target, prop) {
|
|
42
|
-
if (prop === "field") {
|
|
43
|
-
return fieldName;
|
|
44
|
-
}
|
|
45
|
-
if (prop === "use") {
|
|
46
|
-
return (_target._use ??= () => from(storeApi.use(path)));
|
|
47
|
-
}
|
|
48
|
-
if (prop === "useDebounce") {
|
|
49
|
-
return (_target._useDebounce ??= (delay) => from(storeApi.useDebounce(path, delay)));
|
|
50
|
-
}
|
|
51
|
-
if (prop === "useState") {
|
|
52
|
-
return (_target._useState ??= () => {
|
|
53
|
-
const value = storeApi.use(path);
|
|
54
|
-
return [from(value), (next) => storeApi.set(path, to(next))];
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
if (prop === "value") {
|
|
58
|
-
return from(storeApi.value(path));
|
|
59
|
-
}
|
|
60
|
-
if (prop === "set") {
|
|
61
|
-
return (_target._set ??= (value, skipUpdate) => storeApi.set(path, to(value), skipUpdate));
|
|
62
|
-
}
|
|
63
|
-
if (prop === "reset") {
|
|
64
|
-
return (_target._reset ??= () => storeApi.reset(path));
|
|
65
|
-
}
|
|
66
|
-
if (prop === "subscribe") {
|
|
67
|
-
return (_target._subscribe ??= (listener) => storeApi.subscribe(path, (value) => listener(to(value))));
|
|
68
|
-
}
|
|
69
|
-
if (prop === "useCompute") {
|
|
70
|
-
return (_target._useCompute ??= (fn) => {
|
|
71
|
-
return storeApi.useCompute(path, (value) => fn(from(value)));
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
if (prop === "derived") {
|
|
75
|
-
if (isDerived) {
|
|
76
|
-
throw new Error(`Derived method cannot be called on a derived node: ${path}`);
|
|
77
|
-
}
|
|
78
|
-
return (_target._derived ??= ({ from, to, }) => createNode(storeApi, path, cache, extensions, from, to));
|
|
79
|
-
}
|
|
80
|
-
if (prop === "notify") {
|
|
81
|
-
return (_target._notify ??= () => storeApi.notify(path));
|
|
82
|
-
}
|
|
83
|
-
if (prop === "ensureArray") {
|
|
84
|
-
return (_target._ensureArray ??= () => {
|
|
85
|
-
const cacheKey = `${path}.__juststore_ensureArray`;
|
|
86
|
-
if (!isDerived && cache.has(cacheKey)) {
|
|
87
|
-
return cache.get(cacheKey);
|
|
88
|
-
}
|
|
89
|
-
const node = createNode(storeApi, path, cache, extensions, (value) => ensureArray(value, from), unchanged);
|
|
90
|
-
if (!isDerived) {
|
|
91
|
-
cache.set(cacheKey, node);
|
|
92
|
-
}
|
|
93
|
-
return node;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
if (prop === "ensureObject") {
|
|
97
|
-
return (_target._ensureObject ??= () => {
|
|
98
|
-
const cacheKey = `${path}.__juststore_ensureObject`;
|
|
99
|
-
if (!isDerived && cache.has(cacheKey)) {
|
|
100
|
-
return cache.get(cacheKey);
|
|
101
|
-
}
|
|
102
|
-
const node = createNode(storeApi, path, cache, extensions, (value) => ensureObject(value, from), to);
|
|
103
|
-
if (!isDerived) {
|
|
104
|
-
cache.set(cacheKey, node);
|
|
105
|
-
}
|
|
106
|
-
return node;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
if (prop === "withDefault") {
|
|
110
|
-
return (_target._withDefault ??= (defaultValue) => createNode(storeApi, path, cache, extensions, (value) => withDefault(value, defaultValue, from), to));
|
|
111
|
-
}
|
|
112
|
-
if (isObjectMethod(prop)) {
|
|
113
|
-
const derivedValue = from(storeApi.value(path));
|
|
114
|
-
if (derivedValue !== undefined && typeof derivedValue !== "object") {
|
|
115
|
-
throw new Error(`Expected object at path ${path}, got ${typeof derivedValue}`);
|
|
116
|
-
}
|
|
117
|
-
if (prop === "rename") {
|
|
118
|
-
return (_target._rename ??= (oldKey, newKey) => storeApi.rename(path, oldKey, newKey));
|
|
119
|
-
}
|
|
120
|
-
if (prop === "keys") {
|
|
121
|
-
const cacheKey = `${path}.__juststore_keys`;
|
|
122
|
-
if (!isDerived && cache.has(cacheKey)) {
|
|
123
|
-
return cache.get(cacheKey);
|
|
124
|
-
}
|
|
125
|
-
const keysNode = createKeysNode(storeApi, path, (value) => ensureObject(value, from));
|
|
126
|
-
if (!isDerived) {
|
|
127
|
-
cache.set(cacheKey, keysNode);
|
|
128
|
-
}
|
|
129
|
-
return keysNode;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (isArrayMethod(prop)) {
|
|
133
|
-
if (prop === "useLength") {
|
|
134
|
-
return (_target._useLength ??= () => storeApi.useCompute(path, (value) => {
|
|
135
|
-
const arr = from(value);
|
|
136
|
-
return Array.isArray(arr) ? arr.length : 0;
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
const derivedValue = from(storeApi.value(path));
|
|
140
|
-
if (derivedValue !== undefined && !Array.isArray(derivedValue)) {
|
|
141
|
-
throw new Error(`Expected array at path ${path}, got ${typeof derivedValue}`);
|
|
142
|
-
}
|
|
143
|
-
const currentArray = derivedValue ? [...derivedValue] : [];
|
|
144
|
-
if (prop === "at") {
|
|
145
|
-
return (_target._at ??= (index) => {
|
|
146
|
-
const nextPath = path ? `${path}.${index}` : String(index);
|
|
147
|
-
return createNode(storeApi, nextPath, cache, extensions);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
if (prop === "length") {
|
|
151
|
-
return currentArray.length;
|
|
152
|
-
}
|
|
153
|
-
// Array mutation methods
|
|
154
|
-
if (prop === "push") {
|
|
155
|
-
return (_target._push ??= (...items) => {
|
|
156
|
-
// We need to fetch the current array at call time, not bind time
|
|
157
|
-
const arr = from(storeApi.value(path)) ?? [];
|
|
158
|
-
const newArray = [...arr, ...items];
|
|
159
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
160
|
-
return newArray.length;
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
if (prop === "pop") {
|
|
164
|
-
return (_target._pop ??= () => {
|
|
165
|
-
const arr = from(storeApi.value(path)) ?? [];
|
|
166
|
-
if (arr.length === 0)
|
|
167
|
-
return undefined;
|
|
168
|
-
const newArray = arr.slice(0, -1);
|
|
169
|
-
const poppedItem = arr[arr.length - 1];
|
|
170
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
171
|
-
return poppedItem;
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
if (prop === "shift") {
|
|
175
|
-
return (_target._shift ??= () => {
|
|
176
|
-
const arr = from(storeApi.value(path)) ?? [];
|
|
177
|
-
if (arr.length === 0)
|
|
178
|
-
return undefined;
|
|
179
|
-
const newArray = arr.slice(1);
|
|
180
|
-
const shiftedItem = arr[0];
|
|
181
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
182
|
-
return shiftedItem;
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
if (prop === "unshift") {
|
|
186
|
-
return (_target._unshift ??= (...items) => {
|
|
187
|
-
const arr = from(storeApi.value(path)) ?? [];
|
|
188
|
-
const newArray = [...items, ...arr];
|
|
189
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
190
|
-
return newArray.length;
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
if (prop === "splice") {
|
|
194
|
-
return (_target._splice ??= (start, deleteCount, ...items) => {
|
|
195
|
-
const arr = from(storeApi.value(path)) ?? [];
|
|
196
|
-
const newArray = [...arr];
|
|
197
|
-
const deletedItems = newArray.splice(start, deleteCount ?? 0, ...items);
|
|
198
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
199
|
-
return deletedItems;
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
if (prop === "reverse") {
|
|
203
|
-
return (_target._reverse ??= () => {
|
|
204
|
-
const arr = from(storeApi.value(path));
|
|
205
|
-
if (!Array.isArray(arr))
|
|
206
|
-
return [];
|
|
207
|
-
const newArray = [...arr];
|
|
208
|
-
newArray.reverse();
|
|
209
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
210
|
-
return newArray;
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
if (prop === "sort") {
|
|
214
|
-
return (_target._sort ??= (compareFn) => {
|
|
215
|
-
const arr = from(storeApi.value(path));
|
|
216
|
-
if (!Array.isArray(arr))
|
|
217
|
-
return [];
|
|
218
|
-
const newArray = [...arr];
|
|
219
|
-
newArray.sort(compareFn);
|
|
220
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
221
|
-
return newArray;
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (prop === "fill") {
|
|
225
|
-
return (_target._fill ??= (value, start, end) => {
|
|
226
|
-
const arr = from(storeApi.value(path));
|
|
227
|
-
if (!Array.isArray(arr))
|
|
228
|
-
return [];
|
|
229
|
-
const newArray = [...arr];
|
|
230
|
-
newArray.fill(value, start, end);
|
|
231
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
232
|
-
return newArray;
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
if (prop === "copyWithin") {
|
|
236
|
-
return (_target._copyWithin ??= (target, start, end) => {
|
|
237
|
-
const arr = from(storeApi.value(path));
|
|
238
|
-
if (!Array.isArray(arr))
|
|
239
|
-
return [];
|
|
240
|
-
const newArray = [...arr];
|
|
241
|
-
newArray.copyWithin(target, start, end);
|
|
242
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
243
|
-
return newArray;
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
if (prop === "sortedInsert") {
|
|
247
|
-
return (_target._sortedInsert ??= (cmp, ...items) => {
|
|
248
|
-
const arr = from(storeApi.value(path));
|
|
249
|
-
if (!Array.isArray(arr))
|
|
250
|
-
return [];
|
|
251
|
-
if (typeof cmp !== "function")
|
|
252
|
-
return arr.length;
|
|
253
|
-
const newArray = [...arr];
|
|
254
|
-
for (const item of items) {
|
|
255
|
-
let left = 0;
|
|
256
|
-
let right = newArray.length;
|
|
257
|
-
// Binary search to find insertion point
|
|
258
|
-
while (left < right) {
|
|
259
|
-
const mid = (left + right) >>> 1;
|
|
260
|
-
if (cmp(newArray[mid], item) <= 0) {
|
|
261
|
-
left = mid + 1;
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
right = mid;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// Insert at the found position
|
|
268
|
-
newArray.splice(left, 0, item);
|
|
269
|
-
}
|
|
270
|
-
storeApi.set(path, isDerived ? newArray.map(to) : newArray);
|
|
271
|
-
return newArray.length;
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (extensions?.[prop]?.get) {
|
|
276
|
-
return extensions[prop]?.get(path);
|
|
277
|
-
}
|
|
278
|
-
if (typeof prop === "string" || typeof prop === "number") {
|
|
279
|
-
const nextPath = path ? `${path}.${prop}` : String(prop);
|
|
280
|
-
return createNode(storeApi, nextPath, cache, extensions);
|
|
281
|
-
}
|
|
282
|
-
return undefined;
|
|
283
|
-
},
|
|
284
|
-
set(_target, prop, value) {
|
|
285
|
-
if (extensions?.[prop]?.set) {
|
|
286
|
-
return extensions[prop]?.set(path, value);
|
|
287
|
-
}
|
|
288
|
-
if (typeof prop === "string" || typeof prop === "number") {
|
|
289
|
-
const nextPath = path ? `${path}.${prop}` : String(prop);
|
|
290
|
-
storeApi.set(nextPath, to(value));
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
return false;
|
|
294
|
-
},
|
|
295
|
-
});
|
|
296
|
-
if (!isDerived) {
|
|
297
|
-
cache.set(path, proxy);
|
|
298
|
-
}
|
|
299
|
-
return proxy;
|
|
300
|
-
}
|
|
301
|
-
function isArrayMethod(prop) {
|
|
302
|
-
return (prop === "at" ||
|
|
303
|
-
prop === "length" ||
|
|
304
|
-
prop === "useLength" ||
|
|
305
|
-
prop === "push" ||
|
|
306
|
-
prop === "pop" ||
|
|
307
|
-
prop === "shift" ||
|
|
308
|
-
prop === "unshift" ||
|
|
309
|
-
prop === "splice" ||
|
|
310
|
-
prop === "reverse" ||
|
|
311
|
-
prop === "sort" ||
|
|
312
|
-
prop === "fill" ||
|
|
313
|
-
prop === "copyWithin" ||
|
|
314
|
-
prop === "sortedInsert");
|
|
315
|
-
}
|
|
316
|
-
function isObjectMethod(prop) {
|
|
317
|
-
return prop === "rename" || prop === "keys";
|
|
318
|
-
}
|
|
319
|
-
function unchanged(value) {
|
|
320
|
-
return value;
|
|
321
|
-
}
|
|
322
|
-
const EMPTY_ARRAY = [];
|
|
323
|
-
const EMPTY_OBJECT = {};
|
|
324
|
-
function createKeysNode(storeApi, path, getObjectValue) {
|
|
325
|
-
const signalPath = path ? `${path}.__juststore_keys` : "__juststore_keys";
|
|
326
|
-
const computeKeys = () => {
|
|
327
|
-
return getStableKeys(getObjectValue(storeApi.value(path)));
|
|
328
|
-
};
|
|
329
|
-
return new Proxy({}, {
|
|
330
|
-
get(_target, prop) {
|
|
331
|
-
if (prop === "use") {
|
|
332
|
-
return (_target._use ??= () => storeApi.useCompute(signalPath, computeKeys));
|
|
333
|
-
}
|
|
334
|
-
if (prop === "value")
|
|
335
|
-
return computeKeys();
|
|
336
|
-
if (prop === "useCompute") {
|
|
337
|
-
return (_target._useCompute ??= (fn) => {
|
|
338
|
-
return storeApi.useCompute(signalPath, () => fn(computeKeys()));
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
if (prop === "Render") {
|
|
342
|
-
return (_target._Render ??= ({ children, }) => children(storeApi.useCompute(signalPath, computeKeys), () => { }));
|
|
343
|
-
}
|
|
344
|
-
if (prop === "Show") {
|
|
345
|
-
return (_target._Show ??= ({ children, on, }) => {
|
|
346
|
-
const show = storeApi.useCompute(signalPath, () => on(computeKeys()), [on]);
|
|
347
|
-
return show ? children : null;
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
return undefined;
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
function ensureArray(value, from) {
|
|
355
|
-
if (value === undefined || value === null)
|
|
356
|
-
return EMPTY_ARRAY;
|
|
357
|
-
const array = from(value);
|
|
358
|
-
if (Array.isArray(array))
|
|
359
|
-
return array;
|
|
360
|
-
return EMPTY_ARRAY;
|
|
361
|
-
}
|
|
362
|
-
function ensureObject(value, from) {
|
|
363
|
-
if (value === undefined || value === null)
|
|
364
|
-
return EMPTY_OBJECT;
|
|
365
|
-
const obj = from(value);
|
|
366
|
-
if (obj && typeof obj === "object" && !Array.isArray(obj))
|
|
367
|
-
return obj;
|
|
368
|
-
return EMPTY_OBJECT;
|
|
369
|
-
}
|
|
370
|
-
function withDefault(value, defaultValue, from) {
|
|
371
|
-
if (value === undefined || value === null)
|
|
372
|
-
return defaultValue; // defaultValue should've already matched the type
|
|
373
|
-
return from(value);
|
|
374
|
-
}
|
package/dist/src/path.d.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/** biome-ignore-all lint/suspicious/noExplicitAny: <Intended> */
|
|
2
|
-
export type Primitive = null | undefined | string | number | boolean | symbol | bigint;
|
|
3
|
-
export type BrowserNativeObject = Date | FileList | File;
|
|
4
|
-
/**
|
|
5
|
-
* Type which given a tuple type returns its own keys, i.e. only its indices.
|
|
6
|
-
* @typeParam T - tuple type
|
|
7
|
-
* @example
|
|
8
|
-
* ```
|
|
9
|
-
* TupleKeys<[number, string]> = '0' | '1'
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
export type TupleKeys<T extends ReadonlyArray<any>> = Exclude<keyof T, keyof any[]>;
|
|
13
|
-
/**
|
|
14
|
-
* Helper type for recursively constructing paths through a type.
|
|
15
|
-
* This actually constructs the strings and recurses into nested
|
|
16
|
-
* object types.
|
|
17
|
-
*
|
|
18
|
-
* See {@link ArrayPath}
|
|
19
|
-
*/
|
|
20
|
-
type ArrayPathImpl<K extends string | number, V, TraversedTypes> = V extends Primitive | BrowserNativeObject ? IsAny<V> extends true ? string : never : V extends ReadonlyArray<infer U> ? U extends Primitive | BrowserNativeObject ? IsAny<V> extends true ? string : never : true extends AnyIsEqual<TraversedTypes, V> ? never : `${K}` | `${K}.${ArrayPathInternal<V, TraversedTypes | V>}` : true extends AnyIsEqual<TraversedTypes, V> ? never : `${K}.${ArrayPathInternal<V, TraversedTypes | V>}`;
|
|
21
|
-
/**
|
|
22
|
-
* Checks whether the type is any
|
|
23
|
-
* See {@link https://stackoverflow.com/a/49928360/3406963}
|
|
24
|
-
* @typeParam T - type which may be any
|
|
25
|
-
* ```
|
|
26
|
-
* IsAny<any> = true
|
|
27
|
-
* IsAny<string> = false
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
31
|
-
/**
|
|
32
|
-
* Checks whether T1 can be exactly (mutually) assigned to T2
|
|
33
|
-
* @typeParam T1 - type to check
|
|
34
|
-
* @typeParam T2 - type to check against
|
|
35
|
-
* ```
|
|
36
|
-
* IsEqual<string, string> = true
|
|
37
|
-
* IsEqual<'foo', 'foo'> = true
|
|
38
|
-
* IsEqual<string, number> = false
|
|
39
|
-
* IsEqual<string, number> = false
|
|
40
|
-
* IsEqual<string, 'foo'> = false
|
|
41
|
-
* IsEqual<'foo', string> = false
|
|
42
|
-
* IsEqual<'foo' | 'bar', 'foo'> = boolean // 'foo' is assignable, but 'bar' is not (true | false) -> boolean
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
export type IsEqual<T1, T2> = T1 extends T2 ? (<G>() => G extends T1 ? 1 : 2) extends <G>() => G extends T2 ? 1 : 2 ? true : false : false;
|
|
46
|
-
/**
|
|
47
|
-
* Helper function to break apart T1 and check if any are equal to T2
|
|
48
|
-
*
|
|
49
|
-
* See {@link IsEqual}
|
|
50
|
-
*/
|
|
51
|
-
type AnyIsEqual<T1, T2> = T1 extends T2 ? IsEqual<T1, T2> extends true ? true : never : never;
|
|
52
|
-
/**
|
|
53
|
-
* Type to query whether an array type T is a tuple type.
|
|
54
|
-
* @typeParam T - type which may be an array or tuple
|
|
55
|
-
* @example
|
|
56
|
-
* ```
|
|
57
|
-
* IsTuple<[number]> = true
|
|
58
|
-
* IsTuple<number[]> = false
|
|
59
|
-
* ```
|
|
60
|
-
*/
|
|
61
|
-
export type IsTuple<T extends ReadonlyArray<any>> = number extends T["length"] ? false : true;
|
|
62
|
-
/**
|
|
63
|
-
* Type which can be used to index an array or tuple type.
|
|
64
|
-
*/
|
|
65
|
-
export type ArrayKey = number;
|
|
66
|
-
/**
|
|
67
|
-
* Helper type for recursively constructing paths through a type.
|
|
68
|
-
* This obscures the internal type param TraversedTypes from exported contract.
|
|
69
|
-
*
|
|
70
|
-
* See {@link ArrayPath}
|
|
71
|
-
*/
|
|
72
|
-
type ArrayPathInternal<T, TraversedTypes = T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
|
|
73
|
-
[K in TupleKeys<T>]-?: ArrayPathImpl<K & string, T[K], TraversedTypes>;
|
|
74
|
-
}[TupleKeys<T>] : ArrayPathImpl<ArrayKey, V, TraversedTypes> : {
|
|
75
|
-
[K in keyof T]-?: ArrayPathImpl<K & string, T[K], TraversedTypes>;
|
|
76
|
-
}[keyof T];
|
|
77
|
-
/**
|
|
78
|
-
* Type which eagerly collects all paths through a type which point to an array
|
|
79
|
-
* type.
|
|
80
|
-
* @typeParam T - type which should be introspected.
|
|
81
|
-
* @example
|
|
82
|
-
* ```
|
|
83
|
-
* Path<{foo: {bar: string[], baz: number[]}}> = 'foo.bar' | 'foo.baz'
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
|
-
export type ArrayPath<T> = T extends any ? ArrayPathInternal<T> : never;
|
|
87
|
-
/**
|
|
88
|
-
* Type which eagerly collects all paths through a type
|
|
89
|
-
* @typeParam T - type which should be introspected
|
|
90
|
-
* @example
|
|
91
|
-
* ```
|
|
92
|
-
* Path<{foo: {bar: string}}> = 'foo' | 'foo.bar'
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
export type Path<T> = T extends any ? PathInternal<T> : never;
|
|
96
|
-
/**
|
|
97
|
-
* See {@link Path}
|
|
98
|
-
*/
|
|
99
|
-
export type FieldPath<TFieldValues extends FieldValues> = Path<TFieldValues>;
|
|
100
|
-
/**
|
|
101
|
-
* Helper type for recursively constructing paths through a type.
|
|
102
|
-
* This actually constructs the strings and recurses into nested
|
|
103
|
-
* object types.
|
|
104
|
-
*
|
|
105
|
-
* See {@link Path}
|
|
106
|
-
*/
|
|
107
|
-
type PathImpl<K extends string | number, V, TraversedTypes> = V extends Primitive | BrowserNativeObject ? `${K}` : true extends AnyIsEqual<TraversedTypes, V> ? `${K}` : `${K}` | `${K}.${PathInternal<V, TraversedTypes | V>}`;
|
|
108
|
-
/**
|
|
109
|
-
* Helper type for recursively constructing paths through a type.
|
|
110
|
-
* This obscures the internal type param TraversedTypes from exported contract.
|
|
111
|
-
*
|
|
112
|
-
* See {@link Path}
|
|
113
|
-
*/
|
|
114
|
-
type PathInternal<T, TraversedTypes = T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
|
|
115
|
-
[K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes>;
|
|
116
|
-
}[TupleKeys<T>] : PathImpl<ArrayKey, V, TraversedTypes> : {
|
|
117
|
-
[K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes>;
|
|
118
|
-
}[keyof T];
|
|
119
|
-
/**
|
|
120
|
-
* Type to evaluate the type which the given path points to.
|
|
121
|
-
* @typeParam T - deeply nested type which is indexed by the path
|
|
122
|
-
* @typeParam P - path into the deeply nested type
|
|
123
|
-
* @example
|
|
124
|
-
* ```
|
|
125
|
-
* PathValue<{foo: {bar: string}}, 'foo.bar'> = string
|
|
126
|
-
* PathValue<[number, string], '1'> = string
|
|
127
|
-
* ```
|
|
128
|
-
*/
|
|
129
|
-
export type PathValue<T, P extends Path<T> | ArrayPath<T>> = PathValueImpl<T, P>;
|
|
130
|
-
type PathValueImpl<T, P extends string> = T extends any ? P extends `${infer K}.${infer R}` ? K extends keyof T ? undefined extends T[K] ? PathValueImpl<T[K], R> | undefined : PathValueImpl<T[K], R> : K extends `${ArrayKey}` ? T extends ReadonlyArray<infer V> ? PathValueImpl<V, R> : never : never : P extends keyof T ? T[P] : P extends `${ArrayKey}` ? T extends ReadonlyArray<infer V> ? V : undefined extends T ? undefined : never : never : never;
|
|
131
|
-
/**
|
|
132
|
-
* See {@link PathValue}
|
|
133
|
-
*/
|
|
134
|
-
export type FieldPathValue<TFieldValues extends FieldValues, TFieldPath extends FieldPath<TFieldValues>> = Exclude<PathValue<TFieldValues, TFieldPath>, Function>;
|
|
135
|
-
export type FieldValues = Record<string, any>;
|
|
136
|
-
export {};
|
package/dist/src/path.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copied and modified from https://github.com/react-hook-form/react-hook-form
|
|
3
|
-
|
|
4
|
-
MIT License
|
|
5
|
-
|
|
6
|
-
Copyright (c) 2019-present Beier(Bill) Luo
|
|
7
|
-
|
|
8
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
in the Software without restriction, including without limitation the rights
|
|
11
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
furnished to do so, subject to the following conditions:
|
|
14
|
-
|
|
15
|
-
The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
copies or substantial portions of the Software.
|
|
17
|
-
|
|
18
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
SOFTWARE.
|
|
25
|
-
*/
|
|
26
|
-
export {};
|
package/dist/src/root.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { FieldValues } from "./path";
|
|
2
|
-
import type { StoreRoot } from "./types";
|
|
3
|
-
export { createStoreRoot, type StoreOptions };
|
|
4
|
-
/**
|
|
5
|
-
* Configuration options for store creation.
|
|
6
|
-
*/
|
|
7
|
-
type StoreOptions = {
|
|
8
|
-
/** When true, the store only uses memory and does not persist to localStorage */
|
|
9
|
-
memoryOnly?: boolean;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Creates the core store API with path-based methods.
|
|
13
|
-
*
|
|
14
|
-
* Uses a Proxy pattern for lazy initialization and caching of methods,
|
|
15
|
-
* similar to the atom implementation. Methods are only created when first accessed
|
|
16
|
-
* and then cached for subsequent use.
|
|
17
|
-
*
|
|
18
|
-
* @param namespace - Unique identifier for the store
|
|
19
|
-
* @param defaultValue - Initial state merged with any persisted data
|
|
20
|
-
* @param options - Configuration options
|
|
21
|
-
* @returns A proxy object providing both path-based and dynamic property access to the store
|
|
22
|
-
*/
|
|
23
|
-
declare function createStoreRoot<T extends FieldValues>(namespace: string, defaultValue: T, options?: StoreOptions): StoreRoot<T>;
|
package/dist/src/root.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
import { getNestedValue, getSnapshot, isRecord, joinPath, notifyListeners, produce, rename, setLeaf, subscribe, useCompute, useDebounce, useObject, } from "./impl";
|
|
3
|
-
import { createRootNode } from "./node";
|
|
4
|
-
export { createStoreRoot };
|
|
5
|
-
/**
|
|
6
|
-
* Creates the core store API with path-based methods.
|
|
7
|
-
*
|
|
8
|
-
* Uses a Proxy pattern for lazy initialization and caching of methods,
|
|
9
|
-
* similar to the atom implementation. Methods are only created when first accessed
|
|
10
|
-
* and then cached for subsequent use.
|
|
11
|
-
*
|
|
12
|
-
* @param namespace - Unique identifier for the store
|
|
13
|
-
* @param defaultValue - Initial state merged with any persisted data
|
|
14
|
-
* @param options - Configuration options
|
|
15
|
-
* @returns A proxy object providing both path-based and dynamic property access to the store
|
|
16
|
-
*/
|
|
17
|
-
function createStoreRoot(namespace, defaultValue, options = {}) {
|
|
18
|
-
"use memo";
|
|
19
|
-
const memoryOnly = options?.memoryOnly ?? false;
|
|
20
|
-
// merge with default value and save in memory only
|
|
21
|
-
produce(namespace, mergeWithDefaults(defaultValue, getSnapshot(namespace, memoryOnly)), true, true);
|
|
22
|
-
const storeApi = {
|
|
23
|
-
state: (path) => createRootNode(storeApi, path),
|
|
24
|
-
use: (path) => useObject(namespace, path, memoryOnly),
|
|
25
|
-
useDebounce: (path, delay) => useDebounce(namespace, path, delay, memoryOnly),
|
|
26
|
-
set: (path, value, skipUpdate = false) => {
|
|
27
|
-
if (typeof value !== "function") {
|
|
28
|
-
return setLeaf(namespace, path, value, skipUpdate, memoryOnly);
|
|
29
|
-
}
|
|
30
|
-
const currentValue = storeApi.value(path);
|
|
31
|
-
const newValue = value(currentValue);
|
|
32
|
-
return setLeaf(namespace, path, newValue, skipUpdate, memoryOnly);
|
|
33
|
-
},
|
|
34
|
-
value: (path) => getSnapshot(joinPath(namespace, path), memoryOnly),
|
|
35
|
-
reset: (path) => {
|
|
36
|
-
return produce(joinPath(namespace, path), getNestedValue(defaultValue, path), false, memoryOnly);
|
|
37
|
-
},
|
|
38
|
-
rename: (path, oldKey, newKey) => rename(joinPath(namespace, path), oldKey, newKey, memoryOnly),
|
|
39
|
-
subscribe: (path, listener) => {
|
|
40
|
-
const fullPath = joinPath(namespace, path);
|
|
41
|
-
const unsubscribe = subscribe(fullPath, () => listener(getSnapshot(fullPath, memoryOnly)));
|
|
42
|
-
return unsubscribe;
|
|
43
|
-
},
|
|
44
|
-
useCompute: (path, fn, deps) => {
|
|
45
|
-
return useCompute(namespace, path, fn, deps, memoryOnly);
|
|
46
|
-
},
|
|
47
|
-
notify: (path) => {
|
|
48
|
-
const value = getNestedValue(getSnapshot(namespace, memoryOnly), path);
|
|
49
|
-
return notifyListeners(joinPath(namespace, path), value, value, {
|
|
50
|
-
skipRoot: true,
|
|
51
|
-
skipChildren: true,
|
|
52
|
-
forceNotify: true,
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
useState: (path) => {
|
|
56
|
-
const setValue = useCallback((value) => {
|
|
57
|
-
storeApi.set(path, value, false);
|
|
58
|
-
}, [path]);
|
|
59
|
-
return [
|
|
60
|
-
useObject(namespace, path, memoryOnly),
|
|
61
|
-
setValue,
|
|
62
|
-
];
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
return storeApi;
|
|
66
|
-
}
|
|
67
|
-
function mergeWithDefaults(defaultValue, existingValue) {
|
|
68
|
-
if (existingValue === undefined) {
|
|
69
|
-
return defaultValue;
|
|
70
|
-
}
|
|
71
|
-
if (!isRecord(defaultValue) || !isRecord(existingValue)) {
|
|
72
|
-
return existingValue;
|
|
73
|
-
}
|
|
74
|
-
const defaults = defaultValue;
|
|
75
|
-
const existing = existingValue;
|
|
76
|
-
const merged = { ...existing };
|
|
77
|
-
for (const key of Object.keys(defaults)) {
|
|
78
|
-
merged[key] = mergeWithDefaults(defaults[key], existing[key]);
|
|
79
|
-
}
|
|
80
|
-
return merged;
|
|
81
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { getExternalKeyOrder, getStableKeys, setExternalKeyOrder };
|
|
2
|
-
declare function getExternalKeyOrder(target: object): string[] | undefined;
|
|
3
|
-
declare function setExternalKeyOrder(target: object, keys: string[]): void;
|
|
4
|
-
declare function getStableKeys(value: unknown): string[];
|
package/dist/src/stable_keys.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { isRecord } from "./impl";
|
|
2
|
-
export { getExternalKeyOrder, getStableKeys, setExternalKeyOrder };
|
|
3
|
-
const externalKeyOrder = new WeakMap();
|
|
4
|
-
function getExternalKeyOrder(target) {
|
|
5
|
-
return externalKeyOrder.get(target);
|
|
6
|
-
}
|
|
7
|
-
function setExternalKeyOrder(target, keys) {
|
|
8
|
-
externalKeyOrder.set(target, keys);
|
|
9
|
-
}
|
|
10
|
-
function getStableKeys(value) {
|
|
11
|
-
if (!isRecord(value))
|
|
12
|
-
return [];
|
|
13
|
-
const target = value;
|
|
14
|
-
const existing = externalKeyOrder.get(target);
|
|
15
|
-
if (existing) {
|
|
16
|
-
const next = existing.filter((k) => Object.hasOwn(target, k));
|
|
17
|
-
const nextSet = new Set(next);
|
|
18
|
-
for (const k of Object.keys(target)) {
|
|
19
|
-
if (nextSet.has(k))
|
|
20
|
-
continue;
|
|
21
|
-
next.push(k);
|
|
22
|
-
}
|
|
23
|
-
if (next.length !== existing.length) {
|
|
24
|
-
setExternalKeyOrder(target, next);
|
|
25
|
-
}
|
|
26
|
-
return next;
|
|
27
|
-
}
|
|
28
|
-
const keys = Object.keys(target);
|
|
29
|
-
setExternalKeyOrder(target, keys);
|
|
30
|
-
return keys;
|
|
31
|
-
}
|