@weasel-js/prefs 1.10.0 → 1.10.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 +100 -0
- package/dist/{chunk-5KB3WBJZ.js → chunk-ASSX3JHY.js} +363 -46
- package/dist/chunk-ASSX3JHY.js.map +1 -0
- package/dist/index.d.ts +99 -3
- package/dist/index.js +172 -37
- package/dist/index.js.map +1 -1
- package/dist/{open-BEY-aOGe.d.ts → open-C83pU7Va.d.ts} +112 -10
- package/dist/react.d.ts +8 -3
- package/dist/react.js +11 -3
- package/dist/react.js.map +1 -1
- package/package.json +5 -4
- package/dist/chunk-5KB3WBJZ.js.map +0 -1
|
@@ -164,6 +164,33 @@ interface PrefList extends PrefBase<'list', readonly unknown[]> {
|
|
|
164
164
|
maxItems?: number;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
/**
|
|
168
|
+
* A leaf's value split by another leaf: one member for each option of the
|
|
169
|
+
* `enum` leaf at `$by`. A member may itself be a locus, by a second selector.
|
|
170
|
+
* The alternatives are folded by weight when the value is read, and a plain
|
|
171
|
+
* setting of the selector gives one of them all of it.
|
|
172
|
+
*/
|
|
173
|
+
interface PrefLocus {
|
|
174
|
+
/** The full path of the `enum` leaf whose options key `$of`. */
|
|
175
|
+
$by: string;
|
|
176
|
+
$of: Record<string, unknown>;
|
|
177
|
+
}
|
|
178
|
+
/** A value for each selector a read is made under: its path, then the option chosen. */
|
|
179
|
+
type PrefSelection = Readonly<Record<string, string>>;
|
|
180
|
+
/** A weight for each option of each selector, by the selector's path. An option left out weighs nothing. */
|
|
181
|
+
type PrefWeights = Readonly<Record<string, Readonly<Record<string, number>>>>;
|
|
182
|
+
/** One alternative of a split value, and its share of the whole. */
|
|
183
|
+
interface PrefWeighted {
|
|
184
|
+
value: unknown;
|
|
185
|
+
weight: number;
|
|
186
|
+
}
|
|
187
|
+
/** Whether `value` is a {@link PrefLocus}: an object of exactly `$by` and `$of`. */
|
|
188
|
+
declare function isPrefLocus(value: unknown): value is PrefLocus;
|
|
189
|
+
/** The one member `selection` names. A selector it leaves out, or an option the locus holds nothing for, reads as `absent`. */
|
|
190
|
+
declare function memberAt(stored: unknown, selection: PrefSelection, absent: unknown): unknown;
|
|
191
|
+
/** The path of every selector `stored` is split by, outermost first. */
|
|
192
|
+
declare function locusSelectors(stored: unknown): Set<string>;
|
|
193
|
+
|
|
167
194
|
/**
|
|
168
195
|
* A leaf whose value is an object keyed by strings nobody declared, every
|
|
169
196
|
* value described by `item`: the keyed counterpart of a {@link PrefList}.
|
|
@@ -215,7 +242,8 @@ interface PrefLinearUnit extends PrefUnitText {
|
|
|
215
242
|
/** The stored value a display of 0 stands for. Absent is 0. */
|
|
216
243
|
offset?: number;
|
|
217
244
|
/** Decimal places the shown value is rounded to. Absent, the value shows unrounded and formats to two places.
|
|
218
|
-
* What is typed is stored unrounded either way
|
|
245
|
+
* What is typed is stored unrounded either way: a decimal typed at a decimal `scale` is stored as the
|
|
246
|
+
* decimal the two multiply to, without the dust of multiplying them in binary. */
|
|
219
247
|
precision?: number;
|
|
220
248
|
}
|
|
221
249
|
/** A display unit as code, for a conversion that is not linear. It cannot be written to JSON or edited as data. */
|
|
@@ -288,6 +316,11 @@ interface PrefBase<K extends string, Value> {
|
|
|
288
316
|
* (weasel-ui `PrefsForm` honors this for leaves whose control brings
|
|
289
317
|
* its own chrome). */
|
|
290
318
|
block?: boolean;
|
|
319
|
+
/** Draw the control in the heading of the group or section holding the
|
|
320
|
+
* leaf, in place of a row of its own. Only a built-in `boolean` or `enum`
|
|
321
|
+
* fits there (`prefFitsHeading`); any other leaf stays a row. Honored by
|
|
322
|
+
* weasel-ui `PrefsForm` and labkit's `ControlPanel`. */
|
|
323
|
+
heading?: boolean;
|
|
291
324
|
/** Glyph naming this leaf in a host UI's icon set (weasel-ui resolves it
|
|
292
325
|
* against `ICON_PATHS`). A plain string because this package ships no
|
|
293
326
|
* icon set and cannot depend on one. Read where a leaf's `name` has nowhere to go — a
|
|
@@ -314,6 +347,19 @@ interface PrefBase<K extends string, Value> {
|
|
|
314
347
|
* schema editor shows such a leaf as one closed thing and prints the name
|
|
315
348
|
* in place of its literal. */
|
|
316
349
|
type?: string;
|
|
350
|
+
/** The `enum` leaves this one's value may be split by, each by the full
|
|
351
|
+
* path its value is read at: the value may then be held once per option
|
|
352
|
+
* of any of them (a {@link PrefLocus}), which the store folds into the one
|
|
353
|
+
* value read. Whether it is split is the stored value's to say. The
|
|
354
|
+
* order is the order a value split by several is stored in, the first
|
|
355
|
+
* outermost. Each must be a plain `enum`: not `clearable`, with no
|
|
356
|
+
* `encoding`, and not split itself. */
|
|
357
|
+
splitBy?: readonly string[];
|
|
358
|
+
/** What the leaf holds while nothing is stored for it, where that is a
|
|
359
|
+
* split value: a default that differs by option. Split only by entries of
|
|
360
|
+
* `splitBy`. `default` stays what a member it leaves out reads as, and
|
|
361
|
+
* what the leaf's value type is read from. */
|
|
362
|
+
defaultLocus?: PrefLocus;
|
|
317
363
|
}
|
|
318
364
|
/** How a schema-driven UI should present a number pref. */
|
|
319
365
|
type PrefNumberControl = 'input' | 'slider';
|
|
@@ -515,6 +561,13 @@ type PrefLeaf = BuiltinPref | PrefCustom;
|
|
|
515
561
|
* together: one key for their values to nest under, one node to move.
|
|
516
562
|
*/
|
|
517
563
|
type PrefGroupAs = 'page' | 'tab' | 'panel' | 'section' | 'fragment' | 'bar';
|
|
564
|
+
/**
|
|
565
|
+
* What a group or a section drawn `as: 'bar'` does with cells its line has no
|
|
566
|
+
* room for: `scroll` keeps them all on it, and `menu` opens the ones past its
|
|
567
|
+
* end from a button there. A bar set to `menu` takes all the room its host
|
|
568
|
+
* has along the line, which is why it is asked for and not the default.
|
|
569
|
+
*/
|
|
570
|
+
type PrefBarOverflow = 'scroll' | 'menu';
|
|
518
571
|
/** How a {@link PrefSection} is drawn. */
|
|
519
572
|
type PrefSectionAs = Exclude<PrefGroupAs, 'page'>;
|
|
520
573
|
/**
|
|
@@ -534,6 +587,10 @@ interface PrefGroup {
|
|
|
534
587
|
/** How the group is drawn. Unset, its depth decides: a top-level group is a
|
|
535
588
|
* `page`, and one inside another is a `section`. */
|
|
536
589
|
as?: PrefGroupAs;
|
|
590
|
+
/** Drawn `as: 'bar'`: what happens to the cells its line has no room for.
|
|
591
|
+
* Unset: `scroll`. A surface that takes drops keeps every cell on the line
|
|
592
|
+
* whatever this says, so each is there to drop beside. */
|
|
593
|
+
overflow?: PrefBarOverflow;
|
|
537
594
|
/** Drawn, with everything under it, only while this holds. */
|
|
538
595
|
showIf?: PrefCondition;
|
|
539
596
|
/** Everything under it is disabled while this does not hold. */
|
|
@@ -557,6 +614,8 @@ interface PrefSection {
|
|
|
557
614
|
* over its rows. */
|
|
558
615
|
as?: PrefSectionAs;
|
|
559
616
|
/** As a {@link PrefGroup}'s. */
|
|
617
|
+
overflow?: PrefBarOverflow;
|
|
618
|
+
/** As a {@link PrefGroup}'s. */
|
|
560
619
|
showIf?: PrefCondition;
|
|
561
620
|
/** As a {@link PrefGroup}'s. */
|
|
562
621
|
enableIf?: PrefCondition;
|
|
@@ -565,6 +624,15 @@ interface PrefSection {
|
|
|
565
624
|
/** Whether a top-level group gets a page of its own, and not a place on the page of the root's own leaves. */
|
|
566
625
|
declare function prefGroupIsPage(group: PrefGroup): boolean;
|
|
567
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Folds a split value's alternatives into the one value read. `members` holds
|
|
629
|
+
* two or more, each weighing more than nothing, and their weights need not
|
|
630
|
+
* sum to 1.
|
|
631
|
+
*/
|
|
632
|
+
type PrefBlend = (members: readonly PrefWeighted[], leaf: PrefLeaf) => unknown;
|
|
633
|
+
/** The blends of the built-in kinds that have one. Every other kind takes its heaviest member. */
|
|
634
|
+
declare const PREF_BLENDS: Readonly<Record<string, PrefBlend>>;
|
|
635
|
+
|
|
568
636
|
type Join<A extends string, B extends string> = A extends '' ? B : `${A}.${B}`;
|
|
569
637
|
/** Every value's dotted path in a `PrefGroup` schema, where a group's key is a
|
|
570
638
|
* segment; an `object` leaf is one path, its fields are not, and an `action`,
|
|
@@ -581,10 +649,12 @@ type PrefPath<G, Prefix extends string = ''> = G extends {
|
|
|
581
649
|
children: Record<string, unknown>;
|
|
582
650
|
} ? PrefPath<C[K], Join<Prefix, K>> : never;
|
|
583
651
|
}[keyof C & string] : never;
|
|
584
|
-
/** The leaf at `P`.
|
|
652
|
+
/** The leaf at `P`. `unknown` under a group whose keys are not literal, as a
|
|
653
|
+
* schema built at run time has them: nothing says which leaf is there, so
|
|
654
|
+
* its value reads as `unknown`. */
|
|
585
655
|
type PrefAtPath<G, P extends string> = G extends {
|
|
586
656
|
children: infer C;
|
|
587
|
-
} ? P extends `${infer H}.${infer Rest}` ? H extends keyof C ? C[H] extends {
|
|
657
|
+
} ? string extends keyof C ? unknown : P extends `${infer H}.${infer Rest}` ? H extends keyof C ? C[H] extends {
|
|
588
658
|
kind: string;
|
|
589
659
|
} ? never : PrefAtPath<C[H], Rest> : never : P extends keyof C ? C[P] extends {
|
|
590
660
|
kind: string;
|
|
@@ -665,21 +735,49 @@ interface PrefChange {
|
|
|
665
735
|
/** Values for one schema, held in memory and persisted behind. */
|
|
666
736
|
interface PrefsStore<S extends PrefGroup> {
|
|
667
737
|
readonly schema: S;
|
|
668
|
-
/**
|
|
669
|
-
|
|
670
|
-
|
|
738
|
+
/**
|
|
739
|
+
* The leaf's value, repaired against the schema; its default when unset. A
|
|
740
|
+
* split value is folded: under the selectors' own values it is the member
|
|
741
|
+
* they name, and under `weights` it is the blend its kind makes of the
|
|
742
|
+
* members that weigh something. A selector `weights` leaves out keeps its
|
|
743
|
+
* own value.
|
|
744
|
+
*/
|
|
745
|
+
get<P extends PrefPath<S>>(path: P, read?: {
|
|
746
|
+
weights?: PrefWeights;
|
|
747
|
+
}): PrefValueAt<S, P>;
|
|
748
|
+
/** On a split leaf, writes the member the selectors' own values name, or
|
|
749
|
+
* with `at` the one named with each selector there set to the option given.
|
|
750
|
+
* A {@link PrefLocus} given as the value replaces the whole of what is held. */
|
|
751
|
+
set<P extends PrefPath<S>>(path: P, value: PrefValueAt<S, P> | PrefLocus, write?: {
|
|
752
|
+
at?: PrefSelection;
|
|
753
|
+
}): void;
|
|
754
|
+
/** Hold the leaf's value once per option of the selector at `by`, one of
|
|
755
|
+
* the leaf's `splitBy`, each option starting as the value it had. */
|
|
756
|
+
split(path: PrefPath<S>, by: string): void;
|
|
757
|
+
/** Stop splitting the leaf's value by the selector at `by`, keeping the
|
|
758
|
+
* member that selector's own value names. */
|
|
759
|
+
join(path: PrefPath<S>, by: string): void;
|
|
760
|
+
/** What the leaf holds where its value is split, each member repaired. */
|
|
761
|
+
locus(path: PrefPath<S>): PrefLocus | undefined;
|
|
762
|
+
/** What every split leaf holds, by its path. The same map until something changes. */
|
|
763
|
+
loci(): ReadonlyMap<string, PrefLocus>;
|
|
671
764
|
/** Unset every leaf at or under `path`; with no path, all. */
|
|
672
765
|
reset(path?: string): void;
|
|
673
766
|
/** Whether the leaf has a value of its own rather than following its default. */
|
|
674
767
|
isSet(path: PrefPath<S>): boolean;
|
|
675
|
-
/** Every leaf's value as a nested tree. The same object until something
|
|
676
|
-
|
|
768
|
+
/** Every leaf's value as a nested tree. The same object until something
|
|
769
|
+
* changes. With `at`, the tree as it would read with each selector named
|
|
770
|
+
* there set to the option given, built for the call. */
|
|
771
|
+
values(read?: {
|
|
772
|
+
at?: PrefSelection;
|
|
773
|
+
}): Record<string, unknown>;
|
|
677
774
|
/** The leaves following their default. The same set until something changes. */
|
|
678
775
|
unset(): ReadonlySet<string>;
|
|
679
776
|
/** Every stored record as a nested tree, unrepaired, including records no
|
|
680
777
|
* leaf describes. The same object until something changes. */
|
|
681
778
|
stored(): Record<string, unknown>;
|
|
682
|
-
/** `changes` is empty when only records no leaf describes changed.
|
|
779
|
+
/** `changes` is empty when only records no leaf describes changed. A
|
|
780
|
+
* selector changing is a change to every leaf split by it. */
|
|
683
781
|
subscribe(listener: (changes: PrefChange[]) => void): () => void;
|
|
684
782
|
/** False while the store cannot persist: storage has not been read yet, or
|
|
685
783
|
* was written by a newer schema. Changes still apply for this session, and
|
|
@@ -712,6 +810,10 @@ interface PrefsOptions {
|
|
|
712
810
|
/** Validators for app-defined kinds, keyed by `kind`. One given for a
|
|
713
811
|
* built-in kind replaces that kind's own rule. */
|
|
714
812
|
validators?: Readonly<Record<string, PrefValidator>>;
|
|
813
|
+
/** How a split value's alternatives fold into one for app-defined kinds,
|
|
814
|
+
* keyed by `kind`. One given for a built-in kind replaces that kind's
|
|
815
|
+
* own. A kind with none takes its heaviest alternative. */
|
|
816
|
+
blends?: Readonly<Record<string, PrefBlend>>;
|
|
715
817
|
}
|
|
716
818
|
/** Load every stored value for `schema`, migrate it, and return the store. */
|
|
717
819
|
declare function openPrefs<S extends PrefGroup>(schema: S, options: PrefsOptions): Promise<PrefsStore<S>>;
|
|
@@ -721,4 +823,4 @@ declare function openPrefsSync<S extends PrefGroup>(schema: S, options: PrefsOpt
|
|
|
721
823
|
storage: SyncStorageAdapter;
|
|
722
824
|
}): PrefsStore<S>;
|
|
723
825
|
|
|
724
|
-
export {
|
|
826
|
+
export { type PrefWeighted as $, type PrefBooleanEncoding as A, type BuiltinPref as B, type PrefChange as C, type PrefConditionValue as D, type PrefCustom as E, type PrefEnum as F, type PrefEnumControl as G, type PrefEnumEncoding as H, type PrefField as I, type PrefFunctionUnit as J, type PrefGroupAs as K, type PrefLabel as L, type PrefLinearUnit as M, type PrefNumberControl as N, type PrefNumberUnit as O, type PrefLeaf as P, type PrefPaint as Q, type PrefPair as R, type PrefPath as S, type PrefSectionAs as T, type PrefSeparator as U, type PrefStringControl as V, type PrefTest as W, type PrefUnion as X, type PrefValidator as Y, type PrefValueAt as Z, type PrefValueOf as _, type PrefGroup as a, type PrefWeights as a0, type PrefsMigration as a1, type PrefsOptions as a2, type PrefsStore as a3, type ResolvedPrefUnit as a4, isBuiltinPref as a5, isPrefCondition as a6, isPrefLinearUnit as a7, isPrefLocus as a8, mapPrefConditionPaths as a9, openPrefs as aa, openPrefsSync as ab, pairRowsOf as ac, prefAliasName as ad, prefAliasTarget as ae, prefAliasedLeaf as af, prefConditionHolds as ag, prefConditionPaths as ah, prefConditionReadsItem as ai, prefGroupIsPage as aj, prefIsEnabled as ak, prefIsShown as al, memberAt as am, locusSelectors as an, prefUnit as ao, prefVariantDefault as ap, prefVariantOf as aq, repairPrefValue as ar, resolvePrefUnit as as, type PrefSection as b, type PrefNumber as c, type PrefList as d, type PrefMap as e, type PrefCondition as f, type PrefConditionReader as g, type PrefBoolean as h, type PrefString as i, type PrefColor as j, type PrefEnumBase as k, type PrefSelection as l, type PrefLocus as m, type PrefAlias as n, type PrefObject as o, type PrefKind as p, PREF_BLENDS as q, PREF_KINDS as r, type PrefAction as s, type PrefActionContext as t, type PrefActionControl as u, type PrefAtPath as v, type PrefBarOverflow as w, type PrefBase as x, type PrefBlend as y, type PrefBooleanControl as z };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as PrefGroup,
|
|
1
|
+
import { a as PrefGroup, S as PrefPath, a3 as PrefsStore, Z as PrefValueAt, m as PrefLocus, l as PrefSelection, a2 as PrefsOptions } from './open-C83pU7Va.js';
|
|
2
2
|
import { SyncStorageAdapter } from '@weasel-js/storage';
|
|
3
3
|
import '@weasel-js/quantity';
|
|
4
4
|
|
|
@@ -8,14 +8,19 @@ declare function usePref<S extends PrefGroup, P extends PrefPath<S>>(store: Pref
|
|
|
8
8
|
(next: PrefValueAt<S, P> | ((prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)) => void
|
|
9
9
|
];
|
|
10
10
|
/** The whole of `store` as React state, shaped for `PrefsForm`: `values`,
|
|
11
|
-
* `onChange={set}`, `auto={unset}`, and `reset` for `onAutoChange
|
|
12
|
-
*
|
|
11
|
+
* `onChange={set}`, `auto={unset}`, and `reset` for `onAutoChange`; for its
|
|
12
|
+
* split values, `loci`, `onSplit={split}`, `onJoin={join}`, and
|
|
13
|
+
* `onChangeAt={setAt}`. `stored` is `store.stored()`. */
|
|
13
14
|
declare function usePrefsValues<S extends PrefGroup>(store: PrefsStore<S>): {
|
|
14
15
|
values: Record<string, unknown>;
|
|
15
16
|
set: (path: string, value: unknown) => void;
|
|
16
17
|
unset: ReadonlySet<string>;
|
|
17
18
|
stored: Record<string, unknown>;
|
|
18
19
|
reset: (path?: string) => void;
|
|
20
|
+
loci: ReadonlyMap<string, PrefLocus>;
|
|
21
|
+
split: (path: string, by: string) => void;
|
|
22
|
+
join: (path: string, by: string) => void;
|
|
23
|
+
setAt: (path: string, value: unknown, at: PrefSelection) => void;
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
type SyncOptions = PrefsOptions & {
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { openPrefsCacheSync, createPrefsStore } from './chunk-
|
|
1
|
+
import { openPrefsCacheSync, createPrefsStore } from './chunk-ASSX3JHY.js';
|
|
2
2
|
import { useCallback, useSyncExternalStore } from 'react';
|
|
3
3
|
|
|
4
4
|
function usePref(store, path) {
|
|
@@ -21,12 +21,19 @@ function usePrefsValues(store) {
|
|
|
21
21
|
const values = useSyncExternalStore(subscribe, store.values, store.values);
|
|
22
22
|
const unset = useSyncExternalStore(subscribe, store.unset, store.unset);
|
|
23
23
|
const stored = useSyncExternalStore(subscribe, store.stored, store.stored);
|
|
24
|
+
const loci = useSyncExternalStore(subscribe, store.loci, store.loci);
|
|
24
25
|
const set = useCallback(
|
|
25
26
|
(path, value) => store.set(path, value),
|
|
26
27
|
[store]
|
|
27
28
|
);
|
|
28
29
|
const reset = useCallback((path) => store.reset(path), [store]);
|
|
29
|
-
|
|
30
|
+
const split = useCallback((path, by) => store.split(path, by), [store]);
|
|
31
|
+
const join = useCallback((path, by) => store.join(path, by), [store]);
|
|
32
|
+
const setAt = useCallback(
|
|
33
|
+
(path, value, at) => store.set(path, value, { at }),
|
|
34
|
+
[store]
|
|
35
|
+
);
|
|
36
|
+
return { values, set, unset, stored, reset, loci, split, join, setAt };
|
|
30
37
|
}
|
|
31
38
|
var shared = /* @__PURE__ */ new WeakMap();
|
|
32
39
|
function sharedAt(storage, prefix) {
|
|
@@ -74,7 +81,8 @@ function acquire(schema, options) {
|
|
|
74
81
|
lent(at.open.cache),
|
|
75
82
|
options.validators,
|
|
76
83
|
void 0,
|
|
77
|
-
options.migrations?.length ?? 0
|
|
84
|
+
options.migrations?.length ?? 0,
|
|
85
|
+
options.blends
|
|
78
86
|
);
|
|
79
87
|
if (entry.holders === 0) closeUnheldSoon(storage, prefix, schema, entry);
|
|
80
88
|
}
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts","../src/usePrefsStore.ts"],"names":["useCallback","useSyncExternalStore"],"mappings":"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","file":"react.js","sourcesContent":["import { useCallback, useSyncExternalStore } from 'react';\nimport type { PrefPath, PrefValueAt } from './paths';\nimport type { PrefGroup } from './groups';\nimport type { PrefsStore } from './store';\n\n/** One leaf of `store`, as React state. */\nexport function usePref<S extends PrefGroup, P extends PrefPath<S>>(\n store: PrefsStore<S>,\n path: P,\n): [\n PrefValueAt<S, P>,\n (next: PrefValueAt<S, P> | ((prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)) => void,\n] {\n const subscribe = useCallback((onChange: () => void) => store.subscribe(onChange), [store]);\n const getSnapshot = () => store.get(path);\n const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n const set = useCallback(\n (next: PrefValueAt<S, P> | ((prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)) => {\n store.set(\n path,\n typeof next === 'function'\n ? (next as (prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)(store.get(path))\n : next,\n );\n },\n [store, path],\n );\n return [value, set];\n}\n\n/** The whole of `store` as React state, shaped for `PrefsForm`: `values`,\n * `onChange={set}`, `auto={unset}`, and `reset` for `onAutoChange`. `stored`\n * is `store.stored()`. */\nexport function usePrefsValues<S extends PrefGroup>(store: PrefsStore<S>): {\n values: Record<string, unknown>;\n set: (path: string, value: unknown) => void;\n unset: ReadonlySet<string>;\n stored: Record<string, unknown>;\n reset: (path?: string) => void;\n} {\n const subscribe = useCallback((onChange: () => void) => store.subscribe(onChange), [store]);\n const values = useSyncExternalStore(subscribe, store.values, store.values);\n const unset = useSyncExternalStore(subscribe, store.unset, store.unset);\n const stored = useSyncExternalStore(subscribe, store.stored, store.stored);\n const set = useCallback(\n (path: string, value: unknown) => store.set(path as PrefPath<S>, value as never),\n [store],\n );\n const reset = useCallback((path?: string) => store.reset(path), [store]);\n return { values, set, unset, stored, reset };\n}\n","import type { OwnedRecordCache, StorageAdapter, SyncStorageAdapter } from '@weasel-js/storage';\nimport { useCallback, useSyncExternalStore } from 'react';\nimport type { PrefGroup } from './groups';\nimport { openPrefsCacheSync, type PrefsOptions } from './open';\nimport { createPrefsStore, type PrefsStore } from './store';\n\n/** The store open for one schema, and how many mounted components hold it. */\ninterface Held {\n store: PrefsStore<PrefGroup> | null;\n holders: number;\n}\n\n/** The one cache open on a prefix, and a store over it for each schema in use. */\ninterface Shared {\n open: { cache: OwnedRecordCache; stop: () => void } | null;\n bySchema: Map<PrefGroup, Held>;\n}\n\ntype SyncOptions = PrefsOptions & { storage: SyncStorageAdapter };\n\nconst shared = new WeakMap<StorageAdapter, Map<string, Shared>>();\n\nfunction sharedAt(storage: StorageAdapter, prefix: string): Shared {\n let byPrefix = shared.get(storage);\n if (!byPrefix) shared.set(storage, (byPrefix = new Map()));\n let entry = byPrefix.get(prefix);\n if (!entry) byPrefix.set(prefix, (entry = { open: null, bySchema: new Map() }));\n return entry;\n}\n\nfunction heldAt(at: Shared, schema: PrefGroup): Held {\n let entry = at.bySchema.get(schema);\n if (!entry) at.bySchema.set(schema, (entry = { store: null, holders: 0 }));\n return entry;\n}\n\n/** `cache` for one store among several: closing it sends queued writes and\n * leaves the cache open for the others. */\nconst lent = (cache: OwnedRecordCache): OwnedRecordCache =>\n Object.create(cache, {\n close: {\n value: async (): Promise<void> => {\n await cache.flush();\n },\n },\n }) as OwnedRecordCache;\n\n/** Close a store nothing holds, and the cache once no store is over it, a\n * microtask on: after the remount StrictMode and fast refresh make in the\n * same tick, which holds it again. */\nfunction closeUnheldSoon(storage: StorageAdapter, prefix: string, schema: PrefGroup, entry: Held): void {\n queueMicrotask(() => {\n if (entry.holders > 0) return;\n void entry.store?.close();\n entry.store = null;\n const byPrefix = shared.get(storage);\n const at = byPrefix?.get(prefix);\n if (!at || !byPrefix) return;\n if (at.bySchema.get(schema) === entry) at.bySchema.delete(schema);\n if (at.bySchema.size > 0) return;\n at.open?.stop();\n // `close` sends queued writes before its first await, and a sync adapter's\n // write lands in that call, so a cache opened next reads them.\n void at.open?.cache.close();\n byPrefix.delete(prefix);\n });\n}\n\nfunction acquire<S extends PrefGroup>(schema: S, options: SyncOptions): PrefsStore<S> {\n const { storage, prefix } = options;\n const at = sharedAt(storage, prefix);\n const entry = heldAt(at, schema);\n if (!entry.store) {\n at.open ??= openPrefsCacheSync(options);\n entry.store = createPrefsStore(\n schema,\n lent(at.open.cache),\n options.validators,\n undefined,\n options.migrations?.length ?? 0,\n ) as unknown as PrefsStore<PrefGroup>;\n // Opened by a render that may never mount.\n if (entry.holders === 0) closeUnheldSoon(storage, prefix, schema, entry);\n }\n return entry.store as unknown as PrefsStore<S>;\n}\n\n/**\n * A store for `schema`, open while the component is mounted: opened during\n * the first render, so that render has values, and closed, with its queued\n * writes sent, when the component unmounts.\n *\n * Components naming the same `storage`, `prefix`, and `schema` share one\n * store, which closes when the last of them unmounts. Components naming the\n * same `storage` and `prefix` with different schemas each get a store of\n * their own over the same records, so a write through one is read by the\n * others at once, and each hears it as a `local` change. They must pass the\n * same `migrations`: the records are migrated once, when the first opens.\n *\n * `storage` and `schema` are compared by identity and `prefix` by value, so\n * keep the first two out of the render body. A change to any of the three\n * closes the store and returns the one for the new three. The `options`\n * object itself may be new every render: `migrations` and `validators` are\n * read when a store opens, and a change to either alone opens nothing.\n */\nexport function usePrefsStore<S extends PrefGroup>(schema: S, options: SyncOptions): PrefsStore<S> {\n const { storage, prefix } = options;\n const hold = useCallback(() => {\n const entry = heldAt(sharedAt(storage, prefix), schema);\n entry.holders++;\n return () => {\n if (--entry.holders === 0) closeUnheldSoon(storage, prefix, schema, entry);\n };\n }, [storage, prefix, schema]);\n // Read again once held, so a store closed between render and mount is\n // replaced before anything uses it.\n const current = () => acquire(schema, options);\n return useSyncExternalStore(hold, current, current);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts","../src/usePrefsStore.ts"],"names":["useCallback","useSyncExternalStore"],"mappings":"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","file":"react.js","sourcesContent":["import { useCallback, useSyncExternalStore } from 'react';\nimport type { PrefPath, PrefValueAt } from './paths';\nimport type { PrefGroup } from './groups';\nimport type { PrefLocus, PrefSelection } from './locus';\nimport type { PrefsStore } from './store';\n\n/** One leaf of `store`, as React state. */\nexport function usePref<S extends PrefGroup, P extends PrefPath<S>>(\n store: PrefsStore<S>,\n path: P,\n): [\n PrefValueAt<S, P>,\n (next: PrefValueAt<S, P> | ((prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)) => void,\n] {\n const subscribe = useCallback((onChange: () => void) => store.subscribe(onChange), [store]);\n const getSnapshot = () => store.get(path);\n const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n const set = useCallback(\n (next: PrefValueAt<S, P> | ((prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)) => {\n store.set(\n path,\n typeof next === 'function'\n ? (next as (prev: PrefValueAt<S, P>) => PrefValueAt<S, P>)(store.get(path))\n : next,\n );\n },\n [store, path],\n );\n return [value, set];\n}\n\n/** The whole of `store` as React state, shaped for `PrefsForm`: `values`,\n * `onChange={set}`, `auto={unset}`, and `reset` for `onAutoChange`; for its\n * split values, `loci`, `onSplit={split}`, `onJoin={join}`, and\n * `onChangeAt={setAt}`. `stored` is `store.stored()`. */\nexport function usePrefsValues<S extends PrefGroup>(store: PrefsStore<S>): {\n values: Record<string, unknown>;\n set: (path: string, value: unknown) => void;\n unset: ReadonlySet<string>;\n stored: Record<string, unknown>;\n reset: (path?: string) => void;\n loci: ReadonlyMap<string, PrefLocus>;\n split: (path: string, by: string) => void;\n join: (path: string, by: string) => void;\n setAt: (path: string, value: unknown, at: PrefSelection) => void;\n} {\n const subscribe = useCallback((onChange: () => void) => store.subscribe(onChange), [store]);\n const values = useSyncExternalStore(subscribe, store.values, store.values);\n const unset = useSyncExternalStore(subscribe, store.unset, store.unset);\n const stored = useSyncExternalStore(subscribe, store.stored, store.stored);\n const loci = useSyncExternalStore(subscribe, store.loci, store.loci);\n const set = useCallback(\n (path: string, value: unknown) => store.set(path as PrefPath<S>, value as never),\n [store],\n );\n const reset = useCallback((path?: string) => store.reset(path), [store]);\n const split = useCallback((path: string, by: string) => store.split(path as PrefPath<S>, by), [store]);\n const join = useCallback((path: string, by: string) => store.join(path as PrefPath<S>, by), [store]);\n const setAt = useCallback(\n (path: string, value: unknown, at: PrefSelection) => store.set(path as PrefPath<S>, value as never, { at }),\n [store],\n );\n return { values, set, unset, stored, reset, loci, split, join, setAt };\n}\n","import type { OwnedRecordCache, StorageAdapter, SyncStorageAdapter } from '@weasel-js/storage';\nimport { useCallback, useSyncExternalStore } from 'react';\nimport type { PrefGroup } from './groups';\nimport { openPrefsCacheSync, type PrefsOptions } from './open';\nimport { createPrefsStore, type PrefsStore } from './store';\n\n/** The store open for one schema, and how many mounted components hold it. */\ninterface Held {\n store: PrefsStore<PrefGroup> | null;\n holders: number;\n}\n\n/** The one cache open on a prefix, and a store over it for each schema in use. */\ninterface Shared {\n open: { cache: OwnedRecordCache; stop: () => void } | null;\n bySchema: Map<PrefGroup, Held>;\n}\n\ntype SyncOptions = PrefsOptions & { storage: SyncStorageAdapter };\n\nconst shared = new WeakMap<StorageAdapter, Map<string, Shared>>();\n\nfunction sharedAt(storage: StorageAdapter, prefix: string): Shared {\n let byPrefix = shared.get(storage);\n if (!byPrefix) shared.set(storage, (byPrefix = new Map()));\n let entry = byPrefix.get(prefix);\n if (!entry) byPrefix.set(prefix, (entry = { open: null, bySchema: new Map() }));\n return entry;\n}\n\nfunction heldAt(at: Shared, schema: PrefGroup): Held {\n let entry = at.bySchema.get(schema);\n if (!entry) at.bySchema.set(schema, (entry = { store: null, holders: 0 }));\n return entry;\n}\n\n/** `cache` for one store among several: closing it sends queued writes and\n * leaves the cache open for the others. */\nconst lent = (cache: OwnedRecordCache): OwnedRecordCache =>\n Object.create(cache, {\n close: {\n value: async (): Promise<void> => {\n await cache.flush();\n },\n },\n }) as OwnedRecordCache;\n\n/** Close a store nothing holds, and the cache once no store is over it, a\n * microtask on: after the remount StrictMode and fast refresh make in the\n * same tick, which holds it again. */\nfunction closeUnheldSoon(storage: StorageAdapter, prefix: string, schema: PrefGroup, entry: Held): void {\n queueMicrotask(() => {\n if (entry.holders > 0) return;\n void entry.store?.close();\n entry.store = null;\n const byPrefix = shared.get(storage);\n const at = byPrefix?.get(prefix);\n if (!at || !byPrefix) return;\n if (at.bySchema.get(schema) === entry) at.bySchema.delete(schema);\n if (at.bySchema.size > 0) return;\n at.open?.stop();\n // `close` sends queued writes before its first await, and a sync adapter's\n // write lands in that call, so a cache opened next reads them.\n void at.open?.cache.close();\n byPrefix.delete(prefix);\n });\n}\n\nfunction acquire<S extends PrefGroup>(schema: S, options: SyncOptions): PrefsStore<S> {\n const { storage, prefix } = options;\n const at = sharedAt(storage, prefix);\n const entry = heldAt(at, schema);\n if (!entry.store) {\n at.open ??= openPrefsCacheSync(options);\n entry.store = createPrefsStore(\n schema,\n lent(at.open.cache),\n options.validators,\n undefined,\n options.migrations?.length ?? 0,\n options.blends,\n ) as unknown as PrefsStore<PrefGroup>;\n // Opened by a render that may never mount.\n if (entry.holders === 0) closeUnheldSoon(storage, prefix, schema, entry);\n }\n return entry.store as unknown as PrefsStore<S>;\n}\n\n/**\n * A store for `schema`, open while the component is mounted: opened during\n * the first render, so that render has values, and closed, with its queued\n * writes sent, when the component unmounts.\n *\n * Components naming the same `storage`, `prefix`, and `schema` share one\n * store, which closes when the last of them unmounts. Components naming the\n * same `storage` and `prefix` with different schemas each get a store of\n * their own over the same records, so a write through one is read by the\n * others at once, and each hears it as a `local` change. They must pass the\n * same `migrations`: the records are migrated once, when the first opens.\n *\n * `storage` and `schema` are compared by identity and `prefix` by value, so\n * keep the first two out of the render body. A change to any of the three\n * closes the store and returns the one for the new three. The `options`\n * object itself may be new every render: `migrations` and `validators` are\n * read when a store opens, and a change to either alone opens nothing.\n */\nexport function usePrefsStore<S extends PrefGroup>(schema: S, options: SyncOptions): PrefsStore<S> {\n const { storage, prefix } = options;\n const hold = useCallback(() => {\n const entry = heldAt(sharedAt(storage, prefix), schema);\n entry.holders++;\n return () => {\n if (--entry.holders === 0) closeUnheldSoon(storage, prefix, schema, entry);\n };\n }, [storage, prefix, schema]);\n // Read again once held, so a store closed between render and mount is\n // replaced before anything uses it.\n const current = () => acquire(schema, options);\n return useSyncExternalStore(hold, current, current);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/prefs",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.1",
|
|
4
4
|
"description": "A preferences schema, a store that loads, validates, migrates and persists values against it, and React hooks over the store.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,9 +41,10 @@
|
|
|
41
41
|
"build": "tsup"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@weasel-js/
|
|
45
|
-
"@weasel-js/
|
|
46
|
-
"@weasel-js/
|
|
44
|
+
"@weasel-js/paint": "1.10.1",
|
|
45
|
+
"@weasel-js/quantity": "1.10.1",
|
|
46
|
+
"@weasel-js/registry": "1.10.1",
|
|
47
|
+
"@weasel-js/storage": "1.10.1"
|
|
47
48
|
},
|
|
48
49
|
"peerDependencies": {
|
|
49
50
|
"react": ">=18"
|